From 72e41775408fa4ea0f47808515f07d2751fcc42e Mon Sep 17 00:00:00 2001 From: Nikos E Date: Mon, 26 Sep 2016 13:27:11 +0100 Subject: [PATCH 001/170] Initial commit. Code from the old work transfered. --- ...oodWithLinearModelForMeanAndListModeData.h | 2 +- ...orMeanAndListModeDataWithProjMatrixByBin.h | 25 ++- ...MeanAndListModeDataWithProjMatrixByBin.cxx | 211 ++++++++++++++++-- 3 files changed, 216 insertions(+), 22 deletions(-) diff --git a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeData.h b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeData.h index edaf2c36fe..9e87806828 100644 --- a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeData.h +++ b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeData.h @@ -115,7 +115,7 @@ public PoissonLogLikelihoodWithLinearModelForMean //! Listmode pointer shared_ptr list_mode_data_sptr; - int current_frame_num; + unsigned int current_frame_num; //! sets any default values /*! Has to be called by set_defaults in the leaf-class */ diff --git a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h index f425c4803a..a1c503b17e 100644 --- a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h +++ b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h @@ -2,7 +2,7 @@ // /* Copyright (C) 2003- 2011, Hammersmith Imanet Ltd - + Copyright (C) 2016, UCL This file is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or @@ -21,6 +21,7 @@ \brief Declaration of class stir::PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin + \author Nikos Efthimiou \author Kris Thielemans \author Sanida Mustafovic @@ -81,6 +82,8 @@ typedef RegisteredParsingObject&); + protected: virtual double actual_compute_objective_function_without_penalty(const TargetT& current_estimate, @@ -110,6 +113,26 @@ typedef RegisteredParsingObject additive_proj_data_sptr; + + //! + //! \brief normalisation_sptr + //! \author Nikos Efthimiou + //! \details The normalization sinogram. I am going to need it, if I want to be able + //! to run listmode reconstruction without any dependencies on sinogram based functions. + shared_ptr normalisation_sptr; + + //! + //! \brief num_events_to_store + //! \author Nikos Efthimiou + //! \details This is part of some functionality I transfer from lm_to_projdata. + //! The total number of events to be *STORED* not *PROCESSED*. + int num_events_to_store; + + //! + //! \brief do_time_frame + //! \author Nikos Efthimiou + //! \details Reconstruct based on time frames? + bool do_time_frame; std::string additive_projection_data_filename ; //! ProjDataInfo diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx index 397a1228f3..1ce4ea72a8 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx @@ -1,6 +1,6 @@ /* Copyright (C) 2003- 2011, Hammersmith Imanet Ltd - Copyright (C) 2014, University College London + Copyright (C) 2014, 2016, University College London This file is part of STIR. This file is free software; you can redistribute it and/or modify @@ -20,6 +20,7 @@ \brief Implementation of class stir::PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin + \author Nikos Efthimiou \author Kris Thielemans \author Sanida Mustafovic */ @@ -35,6 +36,17 @@ #include "stir/info.h" #include +#include "stir/recon_buildblock/TrivialBinNormalisation.h" +#include "stir/Viewgram.h" +#include "stir/RelatedViewgrams.h" + +#include "stir/recon_array_functions.h" + +#include +#include +#include +#include "stir/stream.h" + #ifdef STIR_MPI #include "stir/recon_buildblock/distributed_functions.h" #endif @@ -56,6 +68,14 @@ PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin() this->set_defaults(); } +template +void +PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin:: +set_normalisation_sptr(const shared_ptr& arg) +{ + this->normalisation_sptr = arg; +} + template void PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin:: @@ -65,7 +85,10 @@ set_defaults() this->additive_proj_data_sptr.reset(); this->additive_projection_data_filename ="0"; this->max_ring_difference_num_to_process =-1; - this->PM_sptr.reset(new ProjMatrixByBinUsingRayTracing()); + this->PM_sptr.reset(new ProjMatrixByBinUsingRayTracing()); + + this->normalisation_sptr.reset(new TrivialBinNormalisation); + this->do_time_frame = false; } template @@ -80,25 +103,92 @@ initialise_keymap() this->parser.add_parsing_key("Matrix type", &this->PM_sptr); this->parser.add_key("additive sinogram",&this->additive_projection_data_filename); - + this->parser.add_key("num_events_to_store",&this->num_events_to_store); + this->parser.add_parsing_key("Bin Normalisation type", &this->normalisation_sptr); + } template int PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin:: set_num_subsets(const int new_num_subsets) { - if (new_num_subsets!=1) - warning("PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin currently only supports 1 subset"); - this->num_subsets = 1; +// if (new_num_subsets!=1) +// warning("PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin currently only supports 1 subset"); +// this->num_subsets = 1; return this->num_subsets; } template bool PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin:: -actual_subsets_are_approximately_balanced(std::string&) const +actual_subsets_are_approximately_balanced(std::string& warning_message) const { - return true; + assert(this->num_subsets>0); + const DataSymmetriesForBins& symmetries = + *this->PM_sptr->get_symmetries_ptr(); + + Array<1,int> num_bins_in_subset(this->num_subsets); + num_bins_in_subset.fill(0); + + + for (int subset_num=0; subset_numnum_subsets; ++subset_num) + { + for (int segment_num = -this->max_ring_difference_num_to_process; + segment_num <= this->max_ring_difference_num_to_process; ++segment_num) + { + for (int axial_num = this->proj_data_info_cyl_uncompressed_ptr->get_min_axial_pos_num(segment_num); + axial_num < this->proj_data_info_cyl_uncompressed_ptr->get_max_axial_pos_num(segment_num); + axial_num ++) + { + // For debugging. + // std::cout <proj_data_info_cyl_uncompressed_ptr->get_min_tangential_pos_num(); + tang_num < this->proj_data_info_cyl_uncompressed_ptr->get_max_tangential_pos_num(); + tang_num ++ ) + { + for(int view_num = this->proj_data_info_cyl_uncompressed_ptr->get_min_view_num() + subset_num; + view_num <= this->proj_data_info_cyl_uncompressed_ptr->get_max_view_num(); + view_num += this->num_subsets) + { + const Bin tmp_bin(segment_num, + view_num, + axial_num, + tang_num, 1); + + if (!this->PM_sptr->get_symmetries_ptr()->is_basic(tmp_bin) ) + continue; + + num_bins_in_subset[subset_num] += + symmetries.num_related_bins(tmp_bin); + + } + } + } + } + } + + for (int subset_num=1; subset_numnum_subsets; ++subset_num) + { + if(num_bins_in_subset[subset_num] != num_bins_in_subset[0]) + { + std::stringstream str(warning_message); + str <<"Number of subsets is such that subsets will be very unbalanced.\n" + << "Number of Bins in each subset would be:\n" + << num_bins_in_subset + << "\nEither reduce the number of symmetries used by the projector, or\n" + "change the number of subsets. It usually should be a divisor of\n" + << this->proj_data_info_cyl_uncompressed_ptr->get_num_views() + << "/4 (or if that's not an integer, a divisor of " + << this->proj_data_info_cyl_uncompressed_ptr->get_num_views() + << "/2 or " + << this->proj_data_info_cyl_uncompressed_ptr->get_num_views() + << ").\n"; + warning_message = str.str(); + return false; + } + } + return true; } template @@ -107,14 +197,38 @@ PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin const& target_sptr) { #ifdef STIR_MPI - //broadcast objective_function (100=PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin) - distributed::send_int_value(100, -1); + //broadcast objective_function (100=PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin) + distributed::send_int_value(100, -1); #endif - - - // set projector to be used for the calculations - this->PM_sptr->set_up(this->proj_data_info_cyl_uncompressed_ptr->create_shared_clone(),target_sptr); - return Succeeded::yes; + + + // set projector to be used for the calculations + this->PM_sptr->set_up(this->proj_data_info_cyl_uncompressed_ptr->create_shared_clone(),target_sptr); + + if (is_null_ptr(this->normalisation_sptr)) + { + warning("Invalid normalisation object"); + return Succeeded::no; + } + + if (this->normalisation_sptr->set_up( + this->proj_data_info_cyl_uncompressed_ptr->create_shared_clone()) == Succeeded::no) + return Succeeded::no; + + if (this->current_frame_num<=0) + { + warning("frame_num should be >= 1"); + return Succeeded::no; + } + + if (this->current_frame_num > this->frame_defs.get_num_frames()) + { + warning("frame_num is %d, but should be less than the number of frames %d.", + this->current_frame_num, this->frame_defs.get_num_frames()); + return Succeeded::no; + } + + return Succeeded::yes; } @@ -201,20 +315,46 @@ compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, const int subset_num) { + assert(subset_num>=0); + assert(subset_numnum_subsets); + + ProjDataInfoCylindricalNoArcCorr* proj_data_no_arc_ptr = + dynamic_cast ( this->proj_data_info_cyl_uncompressed_ptr.get()); + const double start_time = this->frame_defs.get_start_time(this->current_frame_num); const double end_time = this->frame_defs.get_end_time(this->current_frame_num); + + long num_stored_events = 0; + const float max_quotient = 10000.F; + //go to the beginning of this frame // list_mode_data_sptr->set_get_position(start_time); // TODO implement function that will do this for a random time this->list_mode_data_sptr->reset(); double current_time = 0.; ProjMatrixElemsForOneBin proj_matrix_row; + shared_ptr record_sptr = this->list_mode_data_sptr->get_empty_record_sptr(); CListRecord& record = *record_sptr; // int count_of_events=0; //int in_the_range =0; - while (this->list_mode_data_sptr->get_next_record(record) == Succeeded::yes) + + if (num_events_to_store <= 0 ) + do_time_frame = true; + else + do_time_frame = false; + + int more_events = 0 ; + + more_events = do_time_frame? 1 : (num_events_to_store); + + while (more_events)//this->list_mode_data_sptr->get_next_record(record) == Succeeded::yes) { + if (this->list_mode_data_sptr->get_next_record(record) == Succeeded::no) + { + info("End of file!"); + break; //get out of while loop + } //count_of_events++; if(record.is_time()) { @@ -226,15 +366,31 @@ compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, } if (current_time < start_time) continue; + if (record.is_event() && record.event().is_prompt()) { Bin measured_bin; + measured_bin.set_bin_value(1.0f); + record.event().get_bin(measured_bin, *proj_data_info_cyl_uncompressed_ptr); - if (measured_bin.get_bin_value() <= 0) - continue; + // If more than 1 subsets, check if the current bin belongs to + // the current. + if (this->num_subsets > 1) + { + Bin basic_bin = measured_bin; + if (!this->PM_sptr->get_symmetries_ptr()->is_basic(measured_bin) ) + this->PM_sptr->get_symmetries_ptr()->find_basic_bin(basic_bin); + + if (subset_num != static_cast(basic_bin.view_num() % this->num_subsets)) + { + continue; + } + } + this->PM_sptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, measured_bin); //in_the_range++; Bin fwd_bin; + fwd_bin.set_bin_value(0.0f); proj_matrix_row.forward_project(fwd_bin,current_estimate); // additive sinogram if (!is_null_ptr(this->additive_proj_data_sptr)) @@ -243,7 +399,22 @@ compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, float value= fwd_bin.get_bin_value()+add_value; fwd_bin.set_bin_value(value); } - float measured_div_fwd = measured_bin.get_bin_value()/fwd_bin.get_bin_value(); + float measured_div_fwd = 0.0f; + + if(!do_time_frame) + more_events -=1 ; + + num_stored_events += 1; + + if (num_stored_events%100000L==0) + info( boost::format("Proccessed events: %1% ") % num_stored_events); + + if ( measured_bin.get_bin_value() <= max_quotient *fwd_bin.get_bin_value()) + measured_div_fwd = 1.0f /fwd_bin.get_bin_value(); + else + continue; + +// float measured_div_fwd = measured_bin.get_bin_value()/fwd_bin.get_bin_value(); measured_bin.set_bin_value(measured_div_fwd); proj_matrix_row.back_project(gradient, measured_bin); @@ -251,7 +422,7 @@ compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, } // cerr << " The number_of_events " << count_of_events << " "; //cerr << " The number of events proecessed " << in_the_range << " "; - + info(boost::format("Number of used events: %1%") % num_stored_events); } # ifdef _MSC_VER From 25a8067b23ca5d2b281ea8324241f7e2f87b0e4f Mon Sep 17 00:00:00 2001 From: Nikos E Date: Mon, 3 Oct 2016 14:52:38 +0100 Subject: [PATCH 002/170] Some functions added in the listmode objective function. --- ...oodWithLinearModelForMeanAndListModeData.h | 13 ++ ...orMeanAndListModeDataWithProjMatrixByBin.h | 33 +--- ...dWithLinearModelForMeanAndListModeData.cxx | 14 +- ...MeanAndListModeDataWithProjMatrixByBin.cxx | 144 +++++++++++++----- 4 files changed, 136 insertions(+), 68 deletions(-) diff --git a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeData.h b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeData.h index 9e87806828..69b6e076a3 100644 --- a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeData.h +++ b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeData.h @@ -116,6 +116,19 @@ public PoissonLogLikelihoodWithLinearModelForMean shared_ptr list_mode_data_sptr; unsigned int current_frame_num; + + //! + //! \brief num_events_to_store + //! \author Nikos Efthimiou + //! \details This is part of some functionality I transfer from lm_to_projdata. + //! The total number of events to be *STORED* not *PROCESSED*. + int num_events_to_store; + + //! + //! \brief do_time_frame + //! \author Nikos Efthimiou + //! \details Reconstruct based on time frames? + bool do_time_frame; //! sets any default values /*! Has to be called by set_defaults in the leaf-class */ diff --git a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h index a1c503b17e..16b82a5f90 100644 --- a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h +++ b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h @@ -82,8 +82,6 @@ typedef RegisteredParsingObject&); - protected: virtual double actual_compute_objective_function_without_penalty(const TargetT& current_estimate, @@ -96,12 +94,8 @@ typedef RegisteredParsingObject const& target_sptr); - // TODO virtual void - add_subset_sensitivity(TargetT& sensitivity, const int subset_num) const - { - error("add_subset_sensitivity not implemented yet"); - } + add_subset_sensitivity(TargetT& sensitivity, const int subset_num) const; //! Maximum ring difference to take into account /*! \todo Might be removed */ @@ -109,30 +103,9 @@ typedef RegisteredParsingObject PM_sptr; - //shared_ptr projector_by_bin_pair; //! points to the additive projection data - shared_ptr additive_proj_data_sptr; - - //! - //! \brief normalisation_sptr - //! \author Nikos Efthimiou - //! \details The normalization sinogram. I am going to need it, if I want to be able - //! to run listmode reconstruction without any dependencies on sinogram based functions. - shared_ptr normalisation_sptr; - - //! - //! \brief num_events_to_store - //! \author Nikos Efthimiou - //! \details This is part of some functionality I transfer from lm_to_projdata. - //! The total number of events to be *STORED* not *PROCESSED*. - int num_events_to_store; - - //! - //! \brief do_time_frame - //! \author Nikos Efthimiou - //! \details Reconstruct based on time frames? - bool do_time_frame; + shared_ptr additive_proj_data_sptr; std::string additive_projection_data_filename ; //! ProjDataInfo @@ -147,6 +120,8 @@ typedef RegisteredParsingObjectlist_mode_filename =""; this->frame_defs_filename =""; this->list_mode_data_sptr.reset(); - this->current_frame_num =0; + this->current_frame_num = 1; this->output_image_size_xy=-1; this->output_image_size_z=-1; @@ -81,7 +81,7 @@ initialise_keymap() this->parser.add_key("time frame definition filename", &this->frame_defs_filename); // SM TODO -- later do not parse this->parser.add_key("time frame number", &this->current_frame_num); - + this->parser.add_parsing_key("Bin Normalisation type", &this->normalisation_sptr); } template @@ -93,6 +93,14 @@ PoissonLogLikelihoodWithLinearModelForMeanAndListModeData::post_process if (this->list_mode_filename.length() == 0) { warning("You need to specify an input file\n"); return true; } + + do_time_frame = num_events_to_store<=0; + + if (do_time_frame && frame_defs_filename.size()==0) + { + warning("Have to specify either 'frame_definition_filename' or 'num_events_to_store'\n"); + return true; + } this->list_mode_data_sptr= read_from_file(this->list_mode_filename); @@ -115,7 +123,7 @@ PoissonLogLikelihoodWithLinearModelForMeanAndListModeData::post_process { warning("output image size z must be positive (or -1 as default)\n"); return true; } - return false; + return false; } template diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx index 1ce4ea72a8..8a6e60ace0 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx @@ -68,14 +68,6 @@ PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin() this->set_defaults(); } -template -void -PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin:: -set_normalisation_sptr(const shared_ptr& arg) -{ - this->normalisation_sptr = arg; -} - template void PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin:: @@ -104,17 +96,13 @@ initialise_keymap() this->parser.add_key("additive sinogram",&this->additive_projection_data_filename); this->parser.add_key("num_events_to_store",&this->num_events_to_store); - this->parser.add_parsing_key("Bin Normalisation type", &this->normalisation_sptr); - } template int PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin:: set_num_subsets(const int new_num_subsets) { -// if (new_num_subsets!=1) -// warning("PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin currently only supports 1 subset"); -// this->num_subsets = 1; + this->num_subsets = new_num_subsets; return this->num_subsets; } @@ -264,29 +252,109 @@ PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBinget_num_rings()-1; } - - if (this->additive_projection_data_filename != "0") - { + + if (this->additive_projection_data_filename != "0") + { info(boost::format("Reading additive projdata data '%1%'") % additive_projection_data_filename ); - shared_ptr temp_additive_proj_data_sptr = - ProjData::read_from_file(this->additive_projection_data_filename); + shared_ptr temp_additive_proj_data_sptr = + ProjData::read_from_file(this->additive_projection_data_filename); this->additive_proj_data_sptr.reset(new ProjDataInMemory(* temp_additive_proj_data_sptr)); - } - + } + this->proj_data_info_cyl_uncompressed_ptr.reset( dynamic_cast( - ProjDataInfo::ProjDataInfoCTI(scanner_sptr, + ProjDataInfo::ProjDataInfoCTI(scanner_sptr, 1, this->max_ring_difference_num_to_process, scanner_sptr->get_num_detectors_per_ring()/2, - scanner_sptr->get_default_num_arccorrected_bins(), - false))); + scanner_sptr->get_max_num_non_arccorrected_bins(), + false))); + + if ( this->normalisation_sptr->set_up(proj_data_info_cyl_uncompressed_ptr) + != Succeeded::yes) + { +warning("PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin: " + "set-up of pre-normalisation failed\n"); +return true; + } + + + return false; } - +template +void +PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin:: +add_subset_sensitivity(TargetT& sensitivity, const int subset_num) const +{ + std::cout << "!Nere " <max_ring_difference_num_to_process; + const int max_segment_num = this->max_ring_difference_num_to_process; + + std::cout << min_segment_num<< " "<< max_segment_num <proj_data_info_cyl_uncompressed_ptr->get_min_axial_pos_num(segment_num); + axial_num < this->proj_data_info_cyl_uncompressed_ptr->get_max_axial_pos_num(segment_num); + axial_num ++) + { + // For debugging. + std::cout <proj_data_info_cyl_uncompressed_ptr->get_min_tangential_pos_num(); + tang_num < this->proj_data_info_cyl_uncompressed_ptr->get_max_tangential_pos_num(); + tang_num ++ ) + { + + for(int view_num = this->proj_data_info_cyl_uncompressed_ptr->get_min_view_num() + subset_num; + view_num <= this->proj_data_info_cyl_uncompressed_ptr->get_max_view_num(); + view_num += this->num_subsets) + { + Bin tmp_bin(segment_num, + view_num, + axial_num, + tang_num, 1); + + if (!this->PM_sptr->get_symmetries_ptr()->is_basic(tmp_bin) ) + continue; + + this->add_projmatrix_to_sensitivity(sensitivity, tmp_bin); + } + } + } + } +} + +template +void +PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin:: +add_projmatrix_to_sensitivity(TargetT& sensitivity, Bin & this_basic_bin) const +{ + std::vector r_bins; + ProjMatrixElemsForOneBin probabilities; + + this->PM_sptr->get_symmetries_ptr()->get_related_bins(r_bins, this_basic_bin); + + for (unsigned int i = 0; i < r_bins.size(); i++) + r_bins[i].set_bin_value(1.0); + + + // find efficiencies + { + const double start_frame = this->frame_defs.get_start_time(this->current_frame_num); + const double end_frame = this->frame_defs.get_end_time(this->current_frame_num); +// this->normalisation_sptr->undo(r_bins, this->PM_sptr->get_symmetries_ptr(), +// start_frame,end_frame); + } + + this->PM_sptr->get_proj_matrix_elems_for_one_bin(probabilities, this_basic_bin); +// probabilities.back_project(sensitivity, r_bins, this->PM_sptr->get_symmetries_ptr()); +} template TargetT * @@ -336,20 +404,24 @@ compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, shared_ptr record_sptr = this->list_mode_data_sptr->get_empty_record_sptr(); CListRecord& record = *record_sptr; - // int count_of_events=0; - //int in_the_range =0; - - if (num_events_to_store <= 0 ) - do_time_frame = true; - else - do_time_frame = false; - - int more_events = 0 ; - more_events = do_time_frame? 1 : (num_events_to_store); +long long int more_events = 0 ; + if (this->num_events_to_store <= - 1) + more_events = this->list_mode_data_sptr->get_total_number_of_events(); + else if (this->num_events_to_store == 0 ) + { + this->do_time_frame = true; + more_events = 1; + } + else + { + this->do_time_frame = false; + more_events = this->num_events_to_store; + } while (more_events)//this->list_mode_data_sptr->get_next_record(record) == Succeeded::yes) { + if (this->list_mode_data_sptr->get_next_record(record) == Succeeded::no) { info("End of file!"); @@ -360,7 +432,7 @@ compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, { current_time = record.time().get_time_in_secs(); } - if (current_time >= end_time) + if (this->do_time_frame && current_time >= end_time) { break; // get out of while loop } @@ -401,7 +473,7 @@ compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, } float measured_div_fwd = 0.0f; - if(!do_time_frame) + if(!this->do_time_frame) more_events -=1 ; num_stored_events += 1; From fc6b0b8f8769d5c2753c5e0de67632195c7e1e5f Mon Sep 17 00:00:00 2001 From: Nikos E Date: Sun, 9 Oct 2016 02:03:58 +0100 Subject: [PATCH 003/170] LM reconstruction is working and is able to calculate the sensitivity image and I am working to be able to apply the normalisation factors. I had problems with the RelatedBins class, therefore I created several fucntion which take as input a vector and a DataSymmetriesFromBin, which essentially is the same thing ( at least for this application). --- src/buildblock/ProjDataFromStream.cxx | 177 ++++++++++++++++++ src/include/stir/Bin.h | 6 + src/include/stir/Bin.inl | 23 +++ src/include/stir/ProjData.h | 1 + src/include/stir/ProjDataFromStream.h | 36 ++++ .../stir/recon_buildblock/BinNormalisation.h | 13 +- .../BinNormalisationFromECAT7.h | 2 +- ...oodWithLinearModelForMeanAndListModeData.h | 2 +- .../stir/recon_buildblock/ProjMatrixByBin.h | 5 +- .../stir/recon_buildblock/ProjMatrixByBin.inl | 12 +- .../ProjMatrixElemsForOneBin.h | 10 +- src/listmode_buildblock/LmToProjData.cxx | 6 +- src/recon_buildblock/BinNormalisation.cxx | 27 +++ ...dWithLinearModelForMeanAndListModeData.cxx | 14 +- ...MeanAndListModeDataWithProjMatrixByBin.cxx | 42 ++--- src/recon_buildblock/ProjMatrixByBin.cxx | 2 +- .../ProjMatrixByBinFromFile.cxx | 4 +- .../ProjMatrixByBinSPECTUB.cxx | 20 +- .../ProjMatrixByBinUsingInterpolation.cxx | 2 +- .../ProjMatrixByBinUsingRayTracing.cxx | 2 +- .../ProjMatrixElemsForOneBin.cxx | 24 +++ 21 files changed, 369 insertions(+), 61 deletions(-) diff --git a/src/buildblock/ProjDataFromStream.cxx b/src/buildblock/ProjDataFromStream.cxx index 8ada90b67e..8ecbf9f0d5 100644 --- a/src/buildblock/ProjDataFromStream.cxx +++ b/src/buildblock/ProjDataFromStream.cxx @@ -194,6 +194,96 @@ ProjDataFromStream::get_viewgram(const int view_num, const int segment_num, return viewgram; } +float +ProjDataFromStream::get_bin_value(const Bin& this_bin) const +{ + return get_bin_value(this_bin.segment_num(), + this_bin.axial_pos_num(), + this_bin.view_num(), + this_bin.tangential_pos_num()); +} + + +float +ProjDataFromStream::get_bin_value(const int segment_num, + const int axial_pos_num, + const int view_num, + const int tang_pos_num) const +{ + if (sino_stream == 0) + { + error("ProjDataFromStream::get_viewgram: stream ptr is 0\n"); + } + if (! *sino_stream) + { + error("ProjDataFromStream::get_viewgram: error in stream state before reading\n"); + } + + vector offsets = get_offsets_bin(segment_num, axial_pos_num, view_num, tang_pos_num); + + 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_viewgram: error after seekg\n"); + } + + Array< 1, float> value; + value.resize(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"); + if(scale != 1.f) + error("ProjDataFromStream: error reading data: scale factor returned by read_data should be 1\n"); + +// 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) +// error("ProjDataFromStream: error reading data\n"); +// if(scale != 1) +// error("ProjDataFromStream: error reading data: scale factor returned by read_data should be 1\n"); +// // 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) +// error("ProjDataFromStream: error reading data\n"); +// if(scale != 1) +// error("ProjDataFromStream: error reading data: scale factor returned by read_data should be 1\n"); +// } + + value *= 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; + + // viewgram.grow( + // IndexRange2D(get_min_axial_pos_num(segment_num), + // get_max_axial_pos_num(segment_num), + + // get_min_tangential_pos_num(), + // new_max_tangential_pos)); + // } + + return value[0]; +} + + vector ProjDataFromStream::get_offsets(const int view_num, const int segment_num) const @@ -371,6 +461,93 @@ ProjDataFromStream::set_viewgram(const Viewgram& v) } +std::vector +ProjDataFromStream::get_offsets_bin(const int segment_num, + const int ax_pos_num, + const int view_num, + const int tang_pos_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()); + + // Now we are just in front of the correct segment + if (get_storage_order() == Segment_AxialPos_View_TangPos) + { + // skip axial positions + const streamoff 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(); + + // sinogram location + + //find view + const streamoff view_offset = + (view_num - get_min_view_num()) + * get_num_tangential_poss() + * on_disk_data_type.size_in_bytes(); + + // find tang pos + const streamoff tang_offset = + (tang_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; + } + else //if (get_storage_order() == Segment_View_AxialPos_TangPos) + { + + // Skip views + const streamoff 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(); + + + // find axial pos + const streamoff ax_pos_offset = + (ax_pos_num - get_min_axial_pos_num(segment_num)) * + get_num_tangential_poss()* + on_disk_data_type.size_in_bytes(); + + // find tang pos + const streamoff tang_offset = + (tang_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; + } +} // get offsets for the sino data diff --git a/src/include/stir/Bin.h b/src/include/stir/Bin.h index d766257696..0233bf18cc 100644 --- a/src/include/stir/Bin.h +++ b/src/include/stir/Bin.h @@ -79,6 +79,12 @@ class Bin //! accumulate voxel's contribution during forward projection inline Bin& operator+=(const float dx); + //! multiply bin values during normalisation -- apply() + inline Bin& operator*=(const float dx); + //! divide bin values during normalisation -- undo() + //! \todo It is zero division proof in a similar way to divide<,,>(), though I am + //! not sure if it should be. + inline Bin& operator/=(const float dx); //! comparison operators inline bool operator==(const Bin&) const; diff --git a/src/include/stir/Bin.inl b/src/include/stir/Bin.inl index c4607bade6..014f08c821 100644 --- a/src/include/stir/Bin.inl +++ b/src/include/stir/Bin.inl @@ -32,6 +32,8 @@ START_NAMESPACE_STIR +const float SMALL_NUM = 0.000001F; + Bin::Bin() {} @@ -120,4 +122,25 @@ Bin::operator!=(const Bin& bin2) const return !(*this==bin2); } +Bin& +Bin::operator*=(const float dx) +{ + bin_value*=dx; + return *this; +} + +Bin& +Bin::operator/=(const float dx) +{ + float small_value= + bin_value * SMALL_NUM; + + if (std::fabs(dx) < small_value) + bin_value = 0.0f; + else + bin_value /= dx; + + return *this; +} + END_NAMESPACE_STIR diff --git a/src/include/stir/ProjData.h b/src/include/stir/ProjData.h index db213ea41d..a0ba177e9d 100644 --- a/src/include/stir/ProjData.h +++ b/src/include/stir/ProjData.h @@ -36,6 +36,7 @@ #include "stir/Succeeded.h" #include "stir/SegmentBySinogram.h" #include "stir/SegmentByView.h" +#include "stir/Bin.h" //#include #include "stir/IO/ExamData.h" diff --git a/src/include/stir/ProjDataFromStream.h b/src/include/stir/ProjDataFromStream.h index 94f7288200..a333521ba3 100644 --- a/src/include/stir/ProjDataFromStream.h +++ b/src/include/stir/ProjDataFromStream.h @@ -138,6 +138,28 @@ class ProjDataFromStream : public ProjData //! Get scale factor float get_scale_factor() const; + //! + //! \brief get_bin_value + //! \param segment_num + //! \param axial_pos_num + //! \param view_num + //! \param tang_pos_num + //! \return + //! \author Nikos Efthimiou + //! \details This function return the value of a single bin stored in a sinogram in the + //! disk. + float get_bin_value(const int segment_num, + const int axial_pos_num, + const int view_num, + const int tang_pos_num) const; + + //! + //! \brief get_bin_value + //! \param this_bin + //! \return + //! \author Nikos Efthimiou + //! \details Overloaded + float get_bin_value(const Bin& this_bin) const; protected: //! the stream with the data @@ -171,6 +193,20 @@ class ProjDataFromStream : public ProjData //! Calculate offsets for sinogram data std::vector get_offsets_sino(const int ax_pos_num, const int segment_num) const; + //! + //! \brief get_offsets_bin + //! \param segment_num + //! \param axial_pos_num + //! \param view_num + //! \param tang_pos_num + //! \return + //! \author Nikos Efthimiou + //! \details I could make use of get_offsets and get_offset_sino to extract the final offset of the + //! bin, but it would be another one burden in an already slow procedure. + std::vector get_offsets_bin(const int segment_num, + const int ax_pos_num, + const int view_num, + const int tang_pos_num) const; }; diff --git a/src/include/stir/recon_buildblock/BinNormalisation.h b/src/include/stir/recon_buildblock/BinNormalisation.h index 2f3b35436a..1e4530aa0f 100644 --- a/src/include/stir/recon_buildblock/BinNormalisation.h +++ b/src/include/stir/recon_buildblock/BinNormalisation.h @@ -33,6 +33,7 @@ #include "stir/RegisteredObject.h" #include "stir/Bin.h" #include "stir/shared_ptr.h" +#include "DataSymmetriesForBins.h" START_NAMESPACE_STIR @@ -125,7 +126,17 @@ class BinNormalisation : public RegisteredObject The default value for the symmetries means that TrivialDataSymmetriesForBins will be used. */ void undo(ProjData&,const double start_time, const double end_time, - shared_ptr = shared_ptr()) const; + shared_ptr = shared_ptr()) const; + + //! This is the a bin-wise overload for the undo function. + //! \todo RelatedBins should be used, instead of a std::vector. + void apply(std::vector& bins, + const double start_time, const double end_time) const; + + //! This is the a bin-wise overload for the apply function. + //! \todo RelatedBins should be used, instead of a std::vector. + void undo(std::vector& bins, + const double start_time, const double end_time) const; }; diff --git a/src/include/stir/recon_buildblock/BinNormalisationFromECAT7.h b/src/include/stir/recon_buildblock/BinNormalisationFromECAT7.h index 32c0ddfd2a..3d1f63477c 100644 --- a/src/include/stir/recon_buildblock/BinNormalisationFromECAT7.h +++ b/src/include/stir/recon_buildblock/BinNormalisationFromECAT7.h @@ -95,7 +95,7 @@ class BinNormalisationFromECAT7 : BinNormalisationFromECAT7(const std::string& filename); virtual Succeeded set_up(const shared_ptr&); - float get_bin_efficiency(const Bin& bin, const double start_time, const double end_time) const; + virtual float get_bin_efficiency(const Bin& bin, const double start_time, const double end_time) const; bool use_detector_efficiencies() const; bool use_dead_time() const; diff --git a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeData.h b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeData.h index 69b6e076a3..41a051045c 100644 --- a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeData.h +++ b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeData.h @@ -122,7 +122,7 @@ public PoissonLogLikelihoodWithLinearModelForMean //! \author Nikos Efthimiou //! \details This is part of some functionality I transfer from lm_to_projdata. //! The total number of events to be *STORED* not *PROCESSED*. - int num_events_to_store; + unsigned long int num_events_to_store; //! //! \brief do_time_frame diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBin.h b/src/include/stir/recon_buildblock/ProjMatrixByBin.h index c9e0a51efd..684a03dbcc 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBin.h +++ b/src/include/stir/recon_buildblock/ProjMatrixByBin.h @@ -108,7 +108,8 @@ class ProjMatrixByBin : //! get a pointer to an object encoding all symmetries that are used by this ProjMatrixByBin inline const DataSymmetriesForBins* get_symmetries_ptr() const; - + //! get a shared_ptr to an object encoding all symmetries that are used by this ProjMatrixByBin + inline const shared_ptr get_symmetries_sptr() const; //! The main method for getting a row of the matrix. /*! @@ -155,7 +156,7 @@ class ProjMatrixByBin : protected: - shared_ptr symmetries_ptr; + shared_ptr symmetries_sptr; //! default ctor (calls set_defaults()) /*! Note that due to the C++ definition (and some good reasons), diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl index 4540c34467..e288d8a5cf 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl +++ b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl @@ -37,7 +37,13 @@ START_NAMESPACE_STIR const DataSymmetriesForBins* ProjMatrixByBin:: get_symmetries_ptr() const { - return symmetries_ptr.get(); + return symmetries_sptr.get(); +} + +const shared_ptr +ProjMatrixByBin:: get_symmetries_sptr() const +{ + return symmetries_sptr; } inline void @@ -56,7 +62,7 @@ get_proj_matrix_elems_for_one_bin( // find basic bin Bin basic_bin = bin; std::auto_ptr symm_ptr = - symmetries_ptr->find_symmetry_operation_from_basic_bin(basic_bin); + symmetries_sptr->find_symmetry_operation_from_basic_bin(basic_bin); probabilities.set_bin(basic_bin); // check if basic bin is in cache @@ -84,7 +90,7 @@ get_proj_matrix_elems_for_one_bin( // find basic bin Bin basic_bin = bin; std::auto_ptr symm_ptr = - symmetries_ptr->find_symmetry_operation_from_basic_bin(basic_bin); + symmetries_sptr->find_symmetry_operation_from_basic_bin(basic_bin); probabilities.set_bin(basic_bin); // check if basic bin is in cache diff --git a/src/include/stir/recon_buildblock/ProjMatrixElemsForOneBin.h b/src/include/stir/recon_buildblock/ProjMatrixElemsForOneBin.h index a869a966c9..ce59cb8879 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixElemsForOneBin.h +++ b/src/include/stir/recon_buildblock/ProjMatrixElemsForOneBin.h @@ -37,8 +37,10 @@ #include "stir/recon_buildblock/ProjMatrixElemsForOneBinValue.h" +#include "stir/recon_buildblock/DataSymmetriesForBins.h" #include "stir/Bin.h" #include +#include START_NAMESPACE_STIR @@ -197,7 +199,13 @@ class ProjMatrixElemsForOneBin const DiscretisedDensity<3,float>&) const; //! back project related bins void back_project(DiscretisedDensity<3,float>&, - const RelatedBins&) const; + const RelatedBins&) const; + //! Alternative to back project related bins. + //! \warning N.E. I use this function just because I cannot get the + //! RelatedBins class to work for me. + void back_project(DiscretisedDensity<3,float>&, + const std::vector&, + const shared_ptr&); //! forward project related bins void forward_project(RelatedBins&, const DiscretisedDensity<3,float>&) const; diff --git a/src/listmode_buildblock/LmToProjData.cxx b/src/listmode_buildblock/LmToProjData.cxx index d79313cd8a..2113b26737 100644 --- a/src/listmode_buildblock/LmToProjData.cxx +++ b/src/listmode_buildblock/LmToProjData.cxx @@ -587,8 +587,8 @@ process_data() // ('allowed' independent on the fact of we have its segment in memory or not) // When do_time_frame=true, the number of events is irrelevant, so we // just set more_events to 1, and never change it - long more_events = - do_time_frame? 1 : static_cast(num_events_to_store); + unsigned long int more_events = + do_time_frame? 1 : num_events_to_store; if (start_segment_index != proj_data_ptr->get_min_segment_num()) { @@ -666,7 +666,7 @@ process_data() continue; if (!do_time_frame) - more_events-= event_increment; + more_events -= event_increment; // now check if we have its segment in memory if (bin.segment_num() >= start_segment_index && bin.segment_num()<=end_segment_index) diff --git a/src/recon_buildblock/BinNormalisation.cxx b/src/recon_buildblock/BinNormalisation.cxx index 94a974161c..6f2cc590bd 100644 --- a/src/recon_buildblock/BinNormalisation.cxx +++ b/src/recon_buildblock/BinNormalisation.cxx @@ -174,6 +174,33 @@ undo(ProjData& proj_data,const double start_time, const double end_time, } } +void +BinNormalisation:: +apply(std::vector& bins, + const double start_time, const double end_time) const +{ + + for (std::vector::iterator iter = bins.begin(); iter !=bins.end(); ++iter) + *iter *= this->get_bin_efficiency(*iter, start_time, end_time); + + // TODO: DELETE THE FOLLOWING LINES +// std::vector normalization_values = norm_proj_data_ptr->get_related_bin_values(bins, symmetries); +// assert(bins.size() == normalization_values.size()); + +// for (unsigned int i = 0; i < bins.size(); i++) +// { +// bins[i].set_bin_value(bins.at(i).get_bin_value() / normalization_values.at(i)); +// } +} + +void +BinNormalisation:: +undo(std::vector& bins, + const double start_time, const double end_time) const +{ + for (std::vector::iterator iter = bins.begin(); iter !=bins.end(); ++iter) + *iter /= this->get_bin_efficiency(*iter, start_time, end_time); +} END_NAMESPACE_STIR diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeData.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeData.cxx index 1b3f4a9aa0..794e21fe18 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeData.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeData.cxx @@ -54,6 +54,7 @@ set_defaults() this->frame_defs_filename =""; this->list_mode_data_sptr.reset(); this->current_frame_num = 1; + this->num_events_to_store = 0; this->output_image_size_xy=-1; this->output_image_size_z=-1; @@ -94,13 +95,10 @@ PoissonLogLikelihoodWithLinearModelForMeanAndListModeData::post_process if (this->list_mode_filename.length() == 0) { warning("You need to specify an input file\n"); return true; } - do_time_frame = num_events_to_store<=0; - - if (do_time_frame && frame_defs_filename.size()==0) - { - warning("Have to specify either 'frame_definition_filename' or 'num_events_to_store'\n"); - return true; - } + // handle time frame definitions etc + // If num_events_to_store == 0 && frame_definition_filename.size == 0 + if(num_events_to_store==0 && frame_defs_filename.size() == 0) + do_time_frame = true; this->list_mode_data_sptr= read_from_file(this->list_mode_filename); @@ -110,7 +108,7 @@ PoissonLogLikelihoodWithLinearModelForMeanAndListModeData::post_process else { // make a single frame starting from 0. End value will be ignored. - vector > frame_times(1, pair(0,1)); + vector > frame_times(1, pair(0,0)); this->frame_defs = TimeFrameDefinitions(frame_times); } // image stuff diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx index 8a6e60ace0..16c4d3dcb9 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx @@ -318,7 +318,7 @@ add_subset_sensitivity(TargetT& sensitivity, const int subset_num) const Bin tmp_bin(segment_num, view_num, axial_num, - tang_num, 1); + tang_num, 1.f); if (!this->PM_sptr->get_symmetries_ptr()->is_basic(tmp_bin) ) continue; @@ -336,24 +336,28 @@ PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin r_bins; - ProjMatrixElemsForOneBin probabilities; + ProjMatrixElemsForOneBin elem_row; this->PM_sptr->get_symmetries_ptr()->get_related_bins(r_bins, this_basic_bin); + if (r_bins.size() == 0 ) + error("Something went wrong with the symmetries. Abort."); + for (unsigned int i = 0; i < r_bins.size(); i++) - r_bins[i].set_bin_value(1.0); + r_bins[i].set_bin_value(1.0f); // find efficiencies { const double start_frame = this->frame_defs.get_start_time(this->current_frame_num); const double end_frame = this->frame_defs.get_end_time(this->current_frame_num); -// this->normalisation_sptr->undo(r_bins, this->PM_sptr->get_symmetries_ptr(), -// start_frame,end_frame); + this->normalisation_sptr->undo(r_bins, start_frame,end_frame); } - this->PM_sptr->get_proj_matrix_elems_for_one_bin(probabilities, this_basic_bin); -// probabilities.back_project(sensitivity, r_bins, this->PM_sptr->get_symmetries_ptr()); + this->PM_sptr->get_proj_matrix_elems_for_one_bin(elem_row, this_basic_bin); + //N.E: I had problems with RelatedBins thats why I use a std::vector and + // symmetries. + elem_row.back_project(sensitivity, r_bins, this->PM_sptr->get_symmetries_sptr()); } template @@ -405,19 +409,8 @@ compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, shared_ptr record_sptr = this->list_mode_data_sptr->get_empty_record_sptr(); CListRecord& record = *record_sptr; -long long int more_events = 0 ; - if (this->num_events_to_store <= - 1) - more_events = this->list_mode_data_sptr->get_total_number_of_events(); - else if (this->num_events_to_store == 0 ) - { - this->do_time_frame = true; - more_events = 1; - } - else - { - this->do_time_frame = false; - more_events = this->num_events_to_store; - } + unsigned long int more_events = + this->do_time_frame? 1 : this->num_events_to_store; while (more_events)//this->list_mode_data_sptr->get_next_record(record) == Succeeded::yes) { @@ -427,8 +420,8 @@ long long int more_events = 0 ; info("End of file!"); break; //get out of while loop } - //count_of_events++; - if(record.is_time()) + + if(record.is_time() && end_time > 0.01) { current_time = record.time().get_time_in_secs(); } @@ -486,14 +479,11 @@ long long int more_events = 0 ; else continue; -// float measured_div_fwd = measured_bin.get_bin_value()/fwd_bin.get_bin_value(); measured_bin.set_bin_value(measured_div_fwd); proj_matrix_row.back_project(gradient, measured_bin); } - } - // cerr << " The number_of_events " << count_of_events << " "; - //cerr << " The number of events proecessed " << in_the_range << " "; + } info(boost::format("Number of used events: %1%") % num_stored_events); } diff --git a/src/recon_buildblock/ProjMatrixByBin.cxx b/src/recon_buildblock/ProjMatrixByBin.cxx index 8ba26c6443..4aa5993fc2 100644 --- a/src/recon_buildblock/ProjMatrixByBin.cxx +++ b/src/recon_buildblock/ProjMatrixByBin.cxx @@ -202,7 +202,7 @@ get_cached_proj_matrix_elems_for_one_bin( { // Check that this is a 'basic' coordinate Bin bin_copy = bin; - assert ( symmetries_ptr->find_basic_bin(bin_copy) == 0); + assert ( symmetries_sptr->find_basic_bin(bin_copy) == 0); } #endif diff --git a/src/recon_buildblock/ProjMatrixByBinFromFile.cxx b/src/recon_buildblock/ProjMatrixByBinFromFile.cxx index f104e13e70..fec274a423 100644 --- a/src/recon_buildblock/ProjMatrixByBinFromFile.cxx +++ b/src/recon_buildblock/ProjMatrixByBinFromFile.cxx @@ -161,7 +161,7 @@ ProjMatrixByBinFromFile::post_processing() if (this->symmetries_type == standardise_interfile_keyword("PET_CartesianGrid")) { - symmetries_ptr.reset( + symmetries_sptr.reset( new DataSymmetriesForBins_PET_CartesianGrid(proj_data_info_ptr, density_info_sptr, do_symmetry_90degrees_min_phi, @@ -172,7 +172,7 @@ ProjMatrixByBinFromFile::post_processing() } else if (this->symmetries_type == "none") { - symmetries_ptr.reset(new TrivialDataSymmetriesForBins(proj_data_info_ptr)); + symmetries_sptr.reset(new TrivialDataSymmetriesForBins(proj_data_info_ptr)); } else { diff --git a/src/recon_buildblock/ProjMatrixByBinSPECTUB.cxx b/src/recon_buildblock/ProjMatrixByBinSPECTUB.cxx index 0f8acdbfa4..1dd5948193 100644 --- a/src/recon_buildblock/ProjMatrixByBinSPECTUB.cxx +++ b/src/recon_buildblock/ProjMatrixByBinSPECTUB.cxx @@ -189,7 +189,7 @@ set_up( } this->proj_data_info_ptr=proj_data_info_ptr_v; - symmetries_ptr.reset( + symmetries_sptr.reset( new TrivialDataSymmetriesForBins(proj_data_info_ptr_v)); this->densel_range = image_info_ptr->get_index_range(); @@ -205,7 +205,7 @@ set_up( //... fill prj structure from projection data info prj.Nbin = this->proj_data_info_ptr->get_num_tangential_poss(); - prj.szcm = this->proj_data_info_ptr->get_scanner_ptr()->get_default_bin_size()/10.; + prj.szcm = this->proj_data_info_ptr->get_scanner_ptr()->get_default_bin_size()/10.f; prj.Nang = this->proj_data_info_ptr->get_num_views(); @@ -213,24 +213,24 @@ set_up( vol.Ncol = image_info_ptr->get_x_size(); // Image: number of columns vol.Nrow = image_info_ptr->get_y_size(); // Image: number of rows vol.Nsli = image_info_ptr->get_z_size(); // Image: and projections: number of slices - vol.szcm = image_info_ptr->get_voxel_size().x()/10.; // Image: voxel size (cm) - vol.thcm = image_info_ptr->get_voxel_size().z()/10.; // Image: slice thickness (cm) + vol.szcm = image_info_ptr->get_voxel_size().x()/10.f; // Image: voxel size (cm) + vol.thcm = image_info_ptr->get_voxel_size().z()/10.f; // Image: slice thickness (cm) //..... geometrical and other derived parameters of the volume structure............... vol.Npix = vol.Ncol * vol.Nrow; vol.Nvox = vol.Npix * vol.Nsli; - vol.Ncold2 = (float)vol.Ncol / (float)2.; // half of the matrix Nvox (integer or semi-integer) - vol.Nrowd2 = (float)vol.Nrow / (float)2.; // half of the matrix NbOS (integer or semi-integer) - vol.Nslid2 = (float)vol.Nsli / (float)2.; // half of the number of slices (integer or semi-integer) + vol.Ncold2 = (float)vol.Ncol / (float)2.f; // half of the matrix Nvox (integer or semi-integer) + vol.Nrowd2 = (float)vol.Nrow / (float)2.f; // half of the matrix NbOS (integer or semi-integer) + vol.Nslid2 = (float)vol.Nsli / (float)2.f; // half of the number of slices (integer or semi-integer) vol.Xcmd2 = vol.Ncold2 * vol.szcm; // Half of the size of the image volume, dimension x (cm); vol.Ycmd2 = vol.Nrowd2 * vol.szcm; // Half of the size of the image volume, dimension y (cm); vol.Zcmd2 = vol.Nslid2 * vol.thcm; // Half of the size of the image volume, dimension z (cm); - vol.x0 = ( (float)0.5 - vol.Ncold2) * vol.szcm; // coordinate x of the first voxel - vol.y0 = ( (float)0.5 - vol.Nrowd2) * vol.szcm; // coordinate y of the first voxel - vol.z0 = ( (float)0.5 - vol.Nslid2) * vol.thcm; // coordinate z of the first voxel + vol.x0 = ( (float)0.5f - vol.Ncold2) * vol.szcm; // coordinate x of the first voxel + vol.y0 = ( (float)0.5f - vol.Nrowd2) * vol.szcm; // coordinate y of the first voxel + vol.z0 = ( (float)0.5f - vol.Nslid2) * vol.thcm; // coordinate z of the first voxel vol.first_sl = 0; // Image: first slice to take into account (no weight bellow) vol.last_sl = vol.Nsli; // Image: last slice to take into account (no weights above) diff --git a/src/recon_buildblock/ProjMatrixByBinUsingInterpolation.cxx b/src/recon_buildblock/ProjMatrixByBinUsingInterpolation.cxx index 95000eab59..fe4bf6e3b6 100644 --- a/src/recon_buildblock/ProjMatrixByBinUsingInterpolation.cxx +++ b/src/recon_buildblock/ProjMatrixByBinUsingInterpolation.cxx @@ -129,7 +129,7 @@ set_up( (densel_range.get_max_index() + densel_range.get_min_index())*voxel_size.z()/2.F; origin.z() -= z_to_middle; - symmetries_ptr.reset( + symmetries_sptr.reset( new DataSymmetriesForBins_PET_CartesianGrid(proj_data_info_ptr, density_info_ptr, do_symmetry_90degrees_min_phi, diff --git a/src/recon_buildblock/ProjMatrixByBinUsingRayTracing.cxx b/src/recon_buildblock/ProjMatrixByBinUsingRayTracing.cxx index 0b6f56c538..8560fc0f42 100644 --- a/src/recon_buildblock/ProjMatrixByBinUsingRayTracing.cxx +++ b/src/recon_buildblock/ProjMatrixByBinUsingRayTracing.cxx @@ -284,7 +284,7 @@ set_up( origin = image_info_ptr->get_origin(); image_info_ptr->get_regular_range(min_index, max_index); - symmetries_ptr.reset( + symmetries_sptr.reset( new DataSymmetriesForBins_PET_CartesianGrid(proj_data_info_ptr, density_info_ptr, do_symmetry_90degrees_min_phi, diff --git a/src/recon_buildblock/ProjMatrixElemsForOneBin.cxx b/src/recon_buildblock/ProjMatrixElemsForOneBin.cxx index 98b5b7a42e..dcb90e063f 100644 --- a/src/recon_buildblock/ProjMatrixElemsForOneBin.cxx +++ b/src/recon_buildblock/ProjMatrixElemsForOneBin.cxx @@ -415,6 +415,30 @@ back_project(DiscretisedDensity<3,float>& density, } } +void +ProjMatrixElemsForOneBin:: +back_project(DiscretisedDensity<3,float>& density, + const std::vector& r_bins, + const shared_ptr& symmetries) +{ + + ProjMatrixElemsForOneBin row_copy; + + for (std::vector::const_iterator r_bins_iterator =r_bins.begin(); + r_bins_iterator != r_bins.end(); ++ r_bins_iterator) + { + row_copy = *this; + + Bin symmetric_bin = *r_bins_iterator; + // KT 21/02/2002 added check on 0 + if (symmetric_bin.get_bin_value() == 0.f) + return; + auto_ptr symm_ptr = + symmetries->find_symmetry_operation_from_basic_bin(symmetric_bin); + symm_ptr->transform_proj_matrix_elems_for_one_bin(row_copy); + row_copy.back_project(density,symmetric_bin); + } +} void ProjMatrixElemsForOneBin:: From 4141403181d1b584cd2ebfaf16c4b67fc614e800 Mon Sep 17 00:00:00 2001 From: Nikos E Date: Mon, 10 Oct 2016 16:05:21 +0100 Subject: [PATCH 004/170] The listmode reconsturction can calculate the sensitiviity image with normalisation Currently I am using only the new bin-wise normalisation. After the tests run I will try the old viewgram-wise method. And report the results. --- src/buildblock/ProjData.cxx | 19 ++++++++++++ src/buildblock/ProjDataFromStream.cxx | 4 +-- src/buildblock/ProjDataInMemory.cxx | 7 +++-- src/include/stir/Bin.inl | 7 +---- src/include/stir/ProjData.h | 8 ++++- src/include/stir/ProjDataGEAdvance.h | 13 +++++++- .../stir/recon_buildblock/BinNormalisation.h | 13 ++++---- .../BinNormalisationFromAttenuationImage.h | 4 +++ .../BinNormalisationFromECAT8.h | 4 +++ .../BinNormalisationFromProjData.h | 3 ++ .../ChainedBinNormalisation.h | 9 ++++++ .../TrivialBinNormalisation.h | 11 +++++++ src/recon_buildblock/BinNormalisation.cxx | 30 ++++++++++--------- .../BinNormalisationFromAttenuationImage.cxx | 4 +++ .../BinNormalisationFromECAT8.cxx | 5 ++++ .../BinNormalisationFromProjData.cxx | 7 +++++ .../ChainedBinNormalisation.cxx | 29 ++++++++++++++++++ 17 files changed, 145 insertions(+), 32 deletions(-) diff --git a/src/buildblock/ProjData.cxx b/src/buildblock/ProjData.cxx index 33b5e706d9..e665b09a6a 100644 --- a/src/buildblock/ProjData.cxx +++ b/src/buildblock/ProjData.cxx @@ -289,6 +289,25 @@ ProjData::get_related_viewgrams(const ViewSegmentNumbers& view_segmnet_num, 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->segment_num(), + *r_bins_iterator->axial_pos_num(), + *r_bins_iterator->view_num(), + *r_bins_iterator->tangential_pos_num())); + } + + return values; +} + Succeeded ProjData::set_related_viewgrams( const RelatedViewgrams& viewgrams) diff --git a/src/buildblock/ProjDataFromStream.cxx b/src/buildblock/ProjDataFromStream.cxx index 8ecbf9f0d5..8020a24aac 100644 --- a/src/buildblock/ProjDataFromStream.cxx +++ b/src/buildblock/ProjDataFromStream.cxx @@ -231,8 +231,8 @@ ProjDataFromStream::get_bin_value(const int segment_num, error("ProjDataFromStream::get_viewgram: error after seekg\n"); } - Array< 1, float> value; - value.resize(1); + Array< 1, float> value(1); +// value.resize(1); float scale = float(1); // Now the storage order is not more important. Just read. diff --git a/src/buildblock/ProjDataInMemory.cxx b/src/buildblock/ProjDataInMemory.cxx index 934bde9487..0154f51185 100644 --- a/src/buildblock/ProjDataInMemory.cxx +++ b/src/buildblock/ProjDataInMemory.cxx @@ -160,10 +160,11 @@ write_to_file(const string& output_filename) const float ProjDataInMemory::get_bin_value(Bin& bin) { - Viewgram viewgram = get_viewgram(bin.view_num(),bin.segment_num()); + Viewgram viewgram = get_viewgram(bin.view_num(),bin.segment_num()); - return viewgram[bin.axial_pos_num()][bin.tangential_pos_num()]; - + return viewgram[bin.axial_pos_num()][bin.tangential_pos_num()]; +// return get_bin_value(bin.segment_num(), bin.axial_pos_num(), +// bin.view_num(), bin.tangential_pos_num()); } END_NAMESPACE_STIR diff --git a/src/include/stir/Bin.inl b/src/include/stir/Bin.inl index 014f08c821..7fe9d8ac3b 100644 --- a/src/include/stir/Bin.inl +++ b/src/include/stir/Bin.inl @@ -32,8 +32,6 @@ START_NAMESPACE_STIR -const float SMALL_NUM = 0.000001F; - Bin::Bin() {} @@ -132,10 +130,7 @@ Bin::operator*=(const float dx) Bin& Bin::operator/=(const float dx) { - float small_value= - bin_value * SMALL_NUM; - - if (std::fabs(dx) < small_value) + if (dx == 0.f) bin_value = 0.0f; else bin_value /= dx; diff --git a/src/include/stir/ProjData.h b/src/include/stir/ProjData.h index a0ba177e9d..3701251cb0 100644 --- a/src/include/stir/ProjData.h +++ b/src/include/stir/ProjData.h @@ -152,6 +152,10 @@ class ProjData : public ExamData //! Set sinogram virtual Succeeded set_sinogram(const Sinogram&) = 0; + //! Get Bin value + virtual float get_bin_value(const int view_pos, const int segment_pos, const int axial_pos, const int tang_pos) const = 0; + //! Get Bin value + virtual float get_bin_value(const Bin& this_bin) const = 0; //! Get empty viewgram Viewgram get_empty_viewgram(const int view, const int segment_num, @@ -192,7 +196,9 @@ class ProjData : public ExamData const bool make_num_tangential_poss_odd = false) const; //! Set related viewgrams virtual Succeeded set_related_viewgrams(const RelatedViewgrams& viewgrams); - + //! Get related bin values + //! \todo This function temporaliry has as input a vector instead this should be replaced by RelatedBins. + std::vector get_related_bin_values(const std::vector&) const; //! Get empty related viewgrams, where the symmetries_ptr specifies the symmetries to use RelatedViewgrams diff --git a/src/include/stir/ProjDataGEAdvance.h b/src/include/stir/ProjDataGEAdvance.h index ac4fb0cae8..e6931b40bd 100644 --- a/src/include/stir/ProjDataGEAdvance.h +++ b/src/include/stir/ProjDataGEAdvance.h @@ -73,7 +73,18 @@ class ProjDataGEAdvance : public ProjData Sinogram get_sinogram(const int ax_pos_num, const int sergment_num,const bool make_num_tangential_poss_odd=false) const; Succeeded set_sinogram(const Sinogram& s); - + float get_bin_value(const int segment_num, + const int axial_pos_num, + const int view_num, + const int tang_pos_num) const + { + //Do nothing + } + + float get_bin_value(const Bin& this_bin) const + { + // Do nothing + } private: //the file with the data //This has to be a reference (or pointer) to a stream, diff --git a/src/include/stir/recon_buildblock/BinNormalisation.h b/src/include/stir/recon_buildblock/BinNormalisation.h index 1e4530aa0f..892819e7de 100644 --- a/src/include/stir/recon_buildblock/BinNormalisation.h +++ b/src/include/stir/recon_buildblock/BinNormalisation.h @@ -33,7 +33,6 @@ #include "stir/RegisteredObject.h" #include "stir/Bin.h" #include "stir/shared_ptr.h" -#include "DataSymmetriesForBins.h" START_NAMESPACE_STIR @@ -130,13 +129,17 @@ class BinNormalisation : public RegisteredObject //! This is the a bin-wise overload for the undo function. //! \todo RelatedBins should be used, instead of a std::vector. - void apply(std::vector& bins, - const double start_time, const double end_time) const; + virtual void apply(std::vector&, const double, const double) const; //! This is the a bin-wise overload for the apply function. //! \todo RelatedBins should be used, instead of a std::vector. - void undo(std::vector& bins, - const double start_time, const double end_time) const; + virtual void undo(std::vector&, + const double, const double) const; + +protected: + + virtual + std::vector get_related_bins_values(const std::vector&) const = 0; }; diff --git a/src/include/stir/recon_buildblock/BinNormalisationFromAttenuationImage.h b/src/include/stir/recon_buildblock/BinNormalisationFromAttenuationImage.h index 5f87b986c6..06f4f5f667 100644 --- a/src/include/stir/recon_buildblock/BinNormalisationFromAttenuationImage.h +++ b/src/include/stir/recon_buildblock/BinNormalisationFromAttenuationImage.h @@ -119,6 +119,10 @@ class BinNormalisationFromAttenuationImage : virtual bool post_processing(); std::string attenuation_image_filename; + +protected: + virtual + std::vector get_related_bins_values(const std::vector&) const; }; diff --git a/src/include/stir/recon_buildblock/BinNormalisationFromECAT8.h b/src/include/stir/recon_buildblock/BinNormalisationFromECAT8.h index e3f8bfed34..d45cc30e6f 100644 --- a/src/include/stir/recon_buildblock/BinNormalisationFromECAT8.h +++ b/src/include/stir/recon_buildblock/BinNormalisationFromECAT8.h @@ -135,6 +135,10 @@ class BinNormalisationFromECAT8 : virtual bool post_processing(); string normalisation_ECAT8_filename; + +protected: + virtual + std::vector get_related_bins_values(const std::vector&) const; }; END_NAMESPACE_ECAT diff --git a/src/include/stir/recon_buildblock/BinNormalisationFromProjData.h b/src/include/stir/recon_buildblock/BinNormalisationFromProjData.h index 1f47bbaf42..e28c6289b1 100644 --- a/src/include/stir/recon_buildblock/BinNormalisationFromProjData.h +++ b/src/include/stir/recon_buildblock/BinNormalisationFromProjData.h @@ -107,6 +107,9 @@ class BinNormalisationFromProjData : virtual bool post_processing(); std::string normalisation_projdata_filename; +protected: + virtual + std::vector get_related_bins_values(const std::vector&) const; }; diff --git a/src/include/stir/recon_buildblock/ChainedBinNormalisation.h b/src/include/stir/recon_buildblock/ChainedBinNormalisation.h index 0aae69824d..a8d3c85be2 100644 --- a/src/include/stir/recon_buildblock/ChainedBinNormalisation.h +++ b/src/include/stir/recon_buildblock/ChainedBinNormalisation.h @@ -110,7 +110,16 @@ ChainedBinNormalisation(shared_ptr const& apply_first, virtual void undo(RelatedViewgrams& viewgrams,const double start_time, const double end_time) const; virtual float get_bin_efficiency(const Bin& bin,const double start_time, const double end_time) const; + //! Apply for related bins + virtual void apply(std::vector& bins, + const double start_time, const double end_time) const; + //! Undo for related bins + virtual void undo(std::vector& bins, + const double start_time, const double end_time) const; +protected: +virtual +std::vector get_related_bins_values(const std::vector&) const; private: shared_ptr apply_first; diff --git a/src/include/stir/recon_buildblock/TrivialBinNormalisation.h b/src/include/stir/recon_buildblock/TrivialBinNormalisation.h index 663cc10712..24fded8f6d 100644 --- a/src/include/stir/recon_buildblock/TrivialBinNormalisation.h +++ b/src/include/stir/recon_buildblock/TrivialBinNormalisation.h @@ -48,6 +48,12 @@ class TrivialBinNormalisation : virtual inline void apply(RelatedViewgrams&,const double start_time, const double end_time) const {} virtual inline void undo(RelatedViewgrams&,const double start_time, const double end_time) const {} + + //! This is the a bin-wise overload for the apply function. + virtual void apply(std::vector&, const double, const double) const{} + + //! This is the a bin-wise overload for the undo function. + virtual void undo(std::vector&, const double, const double) const {} virtual inline float get_bin_efficiency(const Bin& bin,const double start_time, const double end_time) const { return 1;} @@ -57,6 +63,11 @@ class TrivialBinNormalisation : virtual inline void set_defaults() {} virtual inline void initialise_keymap() {} +protected: + virtual + std::vector get_related_bins_values(const std::vector& ) const + {} + }; END_NAMESPACE_STIR diff --git a/src/recon_buildblock/BinNormalisation.cxx b/src/recon_buildblock/BinNormalisation.cxx index 6f2cc590bd..9e42426692 100644 --- a/src/recon_buildblock/BinNormalisation.cxx +++ b/src/recon_buildblock/BinNormalisation.cxx @@ -176,30 +176,32 @@ undo(ProjData& proj_data,const double start_time, const double end_time, void BinNormalisation:: -apply(std::vector& bins, +apply(std::vector& r_bins, const double start_time, const double end_time) const { + std::vector normalization_values = this->get_related_bins_values(r_bins); - for (std::vector::iterator iter = bins.begin(); iter !=bins.end(); ++iter) - *iter *= this->get_bin_efficiency(*iter, start_time, end_time); + assert(r_bins.size() == normalization_values.size()); - // TODO: DELETE THE FOLLOWING LINES -// std::vector normalization_values = norm_proj_data_ptr->get_related_bin_values(bins, symmetries); -// assert(bins.size() == normalization_values.size()); - -// for (unsigned int i = 0; i < bins.size(); i++) -// { -// bins[i].set_bin_value(bins.at(i).get_bin_value() / normalization_values.at(i)); -// } + for (unsigned int i = 0; i < r_bins.size(); i++) + { + r_bins[i] *= normalization_values.at(i); + } } void BinNormalisation:: -undo(std::vector& bins, +undo(std::vector& r_bins, const double start_time, const double end_time) const { - for (std::vector::iterator iter = bins.begin(); iter !=bins.end(); ++iter) - *iter /= this->get_bin_efficiency(*iter, start_time, end_time); + std::vector normalization_values = this->get_related_bins_values(r_bins); + + assert(r_bins.size() == normalization_values.size()); + + for (unsigned int i = 0; i < r_bins.size(); i++) + { + r_bins[i] /= normalization_values.at(i); + } } END_NAMESPACE_STIR diff --git a/src/recon_buildblock/BinNormalisationFromAttenuationImage.cxx b/src/recon_buildblock/BinNormalisationFromAttenuationImage.cxx index d842689774..1a75c39c37 100644 --- a/src/recon_buildblock/BinNormalisationFromAttenuationImage.cxx +++ b/src/recon_buildblock/BinNormalisationFromAttenuationImage.cxx @@ -178,6 +178,10 @@ BinNormalisationFromAttenuationImage::get_bin_efficiency(const Bin& bin,const do return 1; } +std::vector BinNormalisationFromAttenuationImage::get_related_bins_values(const std::vector& r_bins) const +{ +error("Not implemented, yet"); +} END_NAMESPACE_STIR diff --git a/src/recon_buildblock/BinNormalisationFromECAT8.cxx b/src/recon_buildblock/BinNormalisationFromECAT8.cxx index fec7de4206..aac73dfe62 100644 --- a/src/recon_buildblock/BinNormalisationFromECAT8.cxx +++ b/src/recon_buildblock/BinNormalisationFromECAT8.cxx @@ -707,6 +707,11 @@ BinNormalisationFromECAT8::get_dead_time_efficiency (const DetectionPosition<>& } +std::vector BinNormalisationFromECAT8::get_related_bins_values(const std::vector& r_bins) const +{ +error("Not implemented, yet"); +} + END_NAMESPACE_ECAT END_NAMESPACE_STIR diff --git a/src/recon_buildblock/BinNormalisationFromProjData.cxx b/src/recon_buildblock/BinNormalisationFromProjData.cxx index 57449e6346..113ceef497 100644 --- a/src/recon_buildblock/BinNormalisationFromProjData.cxx +++ b/src/recon_buildblock/BinNormalisationFromProjData.cxx @@ -171,6 +171,13 @@ BinNormalisationFromProjData::get_bin_efficiency(const Bin& bin,const double sta return 1; } + + +std::vector +BinNormalisationFromProjData::get_related_bins_values(const std::vector& r_bins) const +{ + return this->norm_proj_data_ptr->get_related_bin_values(r_bins); +} END_NAMESPACE_STIR diff --git a/src/recon_buildblock/ChainedBinNormalisation.cxx b/src/recon_buildblock/ChainedBinNormalisation.cxx index 65cb120adb..c73a568a2e 100644 --- a/src/recon_buildblock/ChainedBinNormalisation.cxx +++ b/src/recon_buildblock/ChainedBinNormalisation.cxx @@ -120,6 +120,35 @@ ChainedBinNormalisation:: get_bin_efficiency(const Bin& bin,const double start_t : 1); } +void +ChainedBinNormalisation:: +apply(std::vector& bins, + const double start_time, const double end_time) const +{ + if (!is_null_ptr(apply_first)) + apply_first->apply(bins,start_time,end_time); + if (!is_null_ptr(apply_second)) + apply_second->apply(bins,start_time,end_time); +} + +void +ChainedBinNormalisation:: +undo(std::vector& bins, + const double start_time, const double end_time) const +{ + if (!is_null_ptr(apply_first)) + apply_first->undo(bins,start_time,end_time); + if (!is_null_ptr(apply_second)) + apply_second->undo(bins,start_time,end_time); +} +std::vector +ChainedBinNormalisation:: +get_related_bins_values(const std::vector& r_bins) const +{ + error("Not implemented, yet"); + //Something like BINSnormA * BINSnormB +} + END_NAMESPACE_STIR From 1dfe186ae08931bfff401d86feae5b7920a6f698 Mon Sep 17 00:00:00 2001 From: Nikos E Date: Mon, 10 Oct 2016 16:11:49 +0100 Subject: [PATCH 005/170] Corrected an error. --- src/buildblock/ProjData.cxx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/buildblock/ProjData.cxx b/src/buildblock/ProjData.cxx index e665b09a6a..8d411b68ac 100644 --- a/src/buildblock/ProjData.cxx +++ b/src/buildblock/ProjData.cxx @@ -299,10 +299,10 @@ ProjData::get_related_bin_values(const std::vector& r_bins) const 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->segment_num(), - *r_bins_iterator->axial_pos_num(), - *r_bins_iterator->view_num(), - *r_bins_iterator->tangential_pos_num())); + values.push_back(this->get_bin_value((*r_bins_iterator).segment_num(), + (*r_bins_iterator).axial_pos_num(), + (*r_bins_iterator).view_num(), + (*r_bins_iterator).tangential_pos_num())); } return values; From 00d26eb27261d5b9fdc086455c26b1d0521edf63 Mon Sep 17 00:00:00 2001 From: Nikos E Date: Mon, 10 Oct 2016 18:36:34 +0100 Subject: [PATCH 006/170] Added functions to lm objective function to compute the sensitivity image as the sinogram objective function. --- ...orMeanAndListModeDataWithProjMatrixByBin.h | 10 +- ...MeanAndListModeDataWithProjMatrixByBin.cxx | 134 ++++++++++++++---- 2 files changed, 112 insertions(+), 32 deletions(-) diff --git a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h index 16b82a5f90..6ecbd2ce3f 100644 --- a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h +++ b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h @@ -34,7 +34,7 @@ #include "stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeData.h" #include "stir/recon_buildblock/ProjMatrixByBin.h" #include "stir/ProjDataInMemory.h" - +#include "stir/recon_buildblock/ProjectorByBinPair.h" #include "stir/ExamInfo.h" START_NAMESPACE_STIR @@ -103,7 +103,10 @@ typedef RegisteredParsingObject PM_sptr; - + + //! Stores the projectors that are used for the computations + shared_ptr projector_pair_ptr; + //! points to the additive projection data shared_ptr additive_proj_data_sptr; @@ -122,6 +125,9 @@ typedef RegisteredParsingObject @@ -47,6 +47,11 @@ #include #include "stir/stream.h" +#include "stir/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.h" +#include "stir/recon_buildblock/BackProjectorByBinUsingProjMatrixByBin.h" +#include "stir/recon_buildblock/ProjMatrixByBinUsingRayTracing.h" +#include "stir/recon_buildblock/ProjectorByBinPairUsingSeparateProjectors.h" + #ifdef STIR_MPI #include "stir/recon_buildblock/distributed_functions.h" #endif @@ -193,6 +198,14 @@ set_up_before_sensitivity(shared_ptr const& target_sptr) // set projector to be used for the calculations this->PM_sptr->set_up(this->proj_data_info_cyl_uncompressed_ptr->create_shared_clone(),target_sptr); + shared_ptr forward_projector_ptr(new ForwardProjectorByBinUsingProjMatrixByBin(this->PM_sptr)); + shared_ptr back_projector_ptr(new BackProjectorByBinUsingProjMatrixByBin(this->PM_sptr)); + + this->projector_pair_ptr->set_up(this->proj_data_info_cyl_uncompressed_ptr->create_shared_clone(),target_sptr); + + this->projector_pair_ptr.reset( + new ProjectorByBinPairUsingSeparateProjectors(forward_projector_ptr, back_projector_ptr)); + if (is_null_ptr(this->normalisation_sptr)) { warning("Invalid normalisation object"); @@ -246,6 +259,7 @@ PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin scanner_sptr(new Scanner(*this->list_mode_data_sptr->get_scanner_ptr())); + if (this->max_ring_difference_num_to_process == -1) { this->max_ring_difference_num_to_process = @@ -290,46 +304,106 @@ void PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin:: add_subset_sensitivity(TargetT& sensitivity, const int subset_num) const { - std::cout << "!Nere " <max_ring_difference_num_to_process; +// const int max_segment_num = this->max_ring_difference_num_to_process; + +// std::cout << min_segment_num<< " "<< max_segment_num <proj_data_info_cyl_uncompressed_ptr->get_min_axial_pos_num(segment_num); +// axial_num < this->proj_data_info_cyl_uncompressed_ptr->get_max_axial_pos_num(segment_num); +// axial_num ++) +// { +// // For debugging. +// std::cout <proj_data_info_cyl_uncompressed_ptr->get_min_tangential_pos_num(); +// tang_num < this->proj_data_info_cyl_uncompressed_ptr->get_max_tangential_pos_num(); +// tang_num ++ ) +// { + +// for(int view_num = this->proj_data_info_cyl_uncompressed_ptr->get_min_view_num() + subset_num; +// view_num <= this->proj_data_info_cyl_uncompressed_ptr->get_max_view_num(); +// view_num += this->num_subsets) +// { +// Bin tmp_bin(segment_num, +// view_num, +// axial_num, +// tang_num, 1.f); + +// if (!this->PM_sptr->get_symmetries_ptr()->is_basic(tmp_bin) ) +// continue; + +// this->add_projmatrix_to_sensitivity(sensitivity, tmp_bin); +// } +// } +// } +// } + + + const int min_segment_num = -this->max_ring_difference_num_to_process; const int max_segment_num = this->max_ring_difference_num_to_process; - std::cout << min_segment_num<< " "<< max_segment_num <proj_data_info_cyl_uncompressed_ptr->get_min_axial_pos_num(segment_num); - axial_num < this->proj_data_info_cyl_uncompressed_ptr->get_max_axial_pos_num(segment_num); - axial_num ++) - { - // For debugging. - std::cout <proj_data_info_cyl_uncompressed_ptr->get_min_tangential_pos_num(); - tang_num < this->proj_data_info_cyl_uncompressed_ptr->get_max_tangential_pos_num(); - tang_num ++ ) - { + for (int view = this->proj_data_info_cyl_uncompressed_ptr->get_min_view_num() + subset_num; + view <= this->proj_data_info_cyl_uncompressed_ptr->get_max_view_num(); + view += this->num_subsets) + { + const ViewSegmentNumbers view_segment_num(view, segment_num); - for(int view_num = this->proj_data_info_cyl_uncompressed_ptr->get_min_view_num() + subset_num; - view_num <= this->proj_data_info_cyl_uncompressed_ptr->get_max_view_num(); - view_num += this->num_subsets) - { - Bin tmp_bin(segment_num, - view_num, - axial_num, - tang_num, 1.f); + if (! this->projector_pair_ptr->get_symmetries_used()->is_basic(view_segment_num)) + continue; + this->add_view_seg_to_sensitivity(sensitivity, view_segment_num); + } + // cerr<PM_sptr->get_symmetries_ptr()->is_basic(tmp_bin) ) - continue; +template +void +PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin:: +add_view_seg_to_sensitivity(TargetT& sensitivity, const ViewSegmentNumbers& view_seg_nums) const +{ + shared_ptr symmetries_used + (this->projector_pair_ptr->get_symmetries_used()->clone()); + + RelatedViewgrams viewgrams = + this->proj_data_info_cyl_uncompressed_ptr->get_empty_related_viewgrams(view_seg_nums,symmetries_used); + + viewgrams.fill(1.F); + // find efficiencies + { + const double start_frame = this->frame_defs.get_start_time(this->current_frame_num); + const double end_frame = this->frame_defs.get_end_time(this->current_frame_num); + this->normalisation_sptr->undo(viewgrams,start_frame,end_frame); + } + // backproject + { + const int range_to_zero = + view_seg_nums.segment_num() == 0 + ? 1 : 0; + const int min_ax_pos_num = + viewgrams.get_min_axial_pos_num() + range_to_zero; + const int max_ax_pos_num = + viewgrams.get_max_axial_pos_num() - range_to_zero; + + this->projector_pair_ptr->get_back_projector_sptr()-> + back_project(sensitivity, viewgrams, + min_ax_pos_num, max_ax_pos_num); + } - this->add_projmatrix_to_sensitivity(sensitivity, tmp_bin); - } - } - } - } } + template void PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin:: From 5dfa94f302598253f88cd94632b099375d9a5903 Mon Sep 17 00:00:00 2001 From: Nikos E Date: Thu, 13 Oct 2016 10:17:12 +0100 Subject: [PATCH 007/170] Everything is working. But I would like to remove the bin-wise functions. --- src/include/stir/listmode/CListModeDataECAT.h | 7 +++++++ src/include/stir/listmode/CListModeDataECAT8_32bit.h | 2 +- .../recon_buildblock/BinNormalisationFromECAT7.h | 3 +++ src/recon_buildblock/BinNormalisationFromECAT7.cxx | 5 ++++- ...odelForMeanAndListModeDataWithProjMatrixByBin.cxx | 12 +++++++++--- 5 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/include/stir/listmode/CListModeDataECAT.h b/src/include/stir/listmode/CListModeDataECAT.h index 1990aaf5ea..9eaebd41b6 100644 --- a/src/include/stir/listmode/CListModeDataECAT.h +++ b/src/include/stir/listmode/CListModeDataECAT.h @@ -89,6 +89,13 @@ class CListModeDataECAT : public CListModeData /*! \todo this might depend on the acquisition parameters */ virtual bool has_delayeds() const { return true; } + virtual inline + unsigned long int + get_total_number_of_events() const + { + error("Not implemented yet. Abort."); + } + private: std::string listmode_filename_prefix; mutable unsigned int current_lm_file; diff --git a/src/include/stir/listmode/CListModeDataECAT8_32bit.h b/src/include/stir/listmode/CListModeDataECAT8_32bit.h index 7feae1741d..d36a44bc96 100644 --- a/src/include/stir/listmode/CListModeDataECAT8_32bit.h +++ b/src/include/stir/listmode/CListModeDataECAT8_32bit.h @@ -81,7 +81,7 @@ class CListModeDataECAT8_32bit : public CListModeData get_total_number_of_events() const { error("Not implemented yet. Abort."); - }; + } private: typedef CListRecordECAT8_32bit CListRecordT; diff --git a/src/include/stir/recon_buildblock/BinNormalisationFromECAT7.h b/src/include/stir/recon_buildblock/BinNormalisationFromECAT7.h index 3d1f63477c..8f0cfa5a80 100644 --- a/src/include/stir/recon_buildblock/BinNormalisationFromECAT7.h +++ b/src/include/stir/recon_buildblock/BinNormalisationFromECAT7.h @@ -136,6 +136,9 @@ class BinNormalisationFromECAT7 : virtual bool post_processing(); std::string normalisation_ECAT7_filename; +protected: + virtual + std::vector get_related_bins_values(const std::vector&) const; }; END_NAMESPACE_ECAT7 diff --git a/src/recon_buildblock/BinNormalisationFromECAT7.cxx b/src/recon_buildblock/BinNormalisationFromECAT7.cxx index a08d1b2f14..dc57f94514 100644 --- a/src/recon_buildblock/BinNormalisationFromECAT7.cxx +++ b/src/recon_buildblock/BinNormalisationFromECAT7.cxx @@ -653,7 +653,10 @@ BinNormalisationFromECAT7::get_dead_time_efficiency (const DetectionPosition<>& } - +std::vector BinNormalisationFromECAT7::get_related_bins_values(const std::vector& r_bins) const +{ +error("Not implemented, yet"); +} END_NAMESPACE_ECAT7 END_NAMESPACE_ECAT diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx index 22d3decaec..1d0b83b1ec 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx @@ -35,7 +35,7 @@ #include "stir/Viewgram.h" #include "stir/info.h" #include - +#include "stir/HighResWallClockTimer.h" #include "stir/recon_buildblock/TrivialBinNormalisation.h" #include "stir/Viewgram.h" #include "stir/RelatedViewgrams.h" @@ -201,11 +201,12 @@ set_up_before_sensitivity(shared_ptr const& target_sptr) shared_ptr forward_projector_ptr(new ForwardProjectorByBinUsingProjMatrixByBin(this->PM_sptr)); shared_ptr back_projector_ptr(new BackProjectorByBinUsingProjMatrixByBin(this->PM_sptr)); - this->projector_pair_ptr->set_up(this->proj_data_info_cyl_uncompressed_ptr->create_shared_clone(),target_sptr); - this->projector_pair_ptr.reset( new ProjectorByBinPairUsingSeparateProjectors(forward_projector_ptr, back_projector_ptr)); + this->projector_pair_ptr->set_up(this->proj_data_info_cyl_uncompressed_ptr->create_shared_clone(),target_sptr); + + if (is_null_ptr(this->normalisation_sptr)) { warning("Invalid normalisation object"); @@ -304,6 +305,9 @@ void PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin:: add_subset_sensitivity(TargetT& sensitivity, const int subset_num) const { + HighResWallClockTimer t; + t.reset(); + t.start(); // std::cout << "!Nere " <max_ring_difference_num_to_process; // const int max_segment_num = this->max_ring_difference_num_to_process; @@ -366,6 +370,8 @@ add_subset_sensitivity(TargetT& sensitivity, const int subset_num) const } // cerr< From f1e60e6d81cac114dd429e8429627caefab26390 Mon Sep 17 00:00:00 2001 From: Nikos E Date: Thu, 13 Oct 2016 10:38:38 +0100 Subject: [PATCH 008/170] Removed all the Bin-wise functions. My tests showed that the results are not exactly the same as in the viewgram-wise calculations. So I retreat it for now. --- .../stir/recon_buildblock/BinNormalisation.h | 16 +--- .../BinNormalisationFromAttenuationImage.h | 4 - .../BinNormalisationFromECAT7.h | 5 +- .../BinNormalisationFromECAT8.h | 4 - .../BinNormalisationFromProjData.h | 3 - .../ChainedBinNormalisation.h | 9 --- ...orMeanAndListModeDataWithProjMatrixByBin.h | 3 +- .../ProjMatrixElemsForOneBin.h | 10 +-- .../TrivialBinNormalisation.h | 11 --- src/recon_buildblock/BinNormalisation.cxx | 29 ------- .../BinNormalisationFromAttenuationImage.cxx | 4 - .../BinNormalisationFromECAT7.cxx | 5 +- .../BinNormalisationFromECAT8.cxx | 5 -- .../BinNormalisationFromProjData.cxx | 7 -- .../ChainedBinNormalisation.cxx | 29 ------- ...MeanAndListModeDataWithProjMatrixByBin.cxx | 79 ------------------- .../ProjMatrixElemsForOneBin.cxx | 24 ------ 17 files changed, 5 insertions(+), 242 deletions(-) diff --git a/src/include/stir/recon_buildblock/BinNormalisation.h b/src/include/stir/recon_buildblock/BinNormalisation.h index 892819e7de..2f3b35436a 100644 --- a/src/include/stir/recon_buildblock/BinNormalisation.h +++ b/src/include/stir/recon_buildblock/BinNormalisation.h @@ -125,21 +125,7 @@ class BinNormalisation : public RegisteredObject The default value for the symmetries means that TrivialDataSymmetriesForBins will be used. */ void undo(ProjData&,const double start_time, const double end_time, - shared_ptr = shared_ptr()) const; - - //! This is the a bin-wise overload for the undo function. - //! \todo RelatedBins should be used, instead of a std::vector. - virtual void apply(std::vector&, const double, const double) const; - - //! This is the a bin-wise overload for the apply function. - //! \todo RelatedBins should be used, instead of a std::vector. - virtual void undo(std::vector&, - const double, const double) const; - -protected: - - virtual - std::vector get_related_bins_values(const std::vector&) const = 0; + shared_ptr = shared_ptr()) const; }; diff --git a/src/include/stir/recon_buildblock/BinNormalisationFromAttenuationImage.h b/src/include/stir/recon_buildblock/BinNormalisationFromAttenuationImage.h index 06f4f5f667..5f87b986c6 100644 --- a/src/include/stir/recon_buildblock/BinNormalisationFromAttenuationImage.h +++ b/src/include/stir/recon_buildblock/BinNormalisationFromAttenuationImage.h @@ -119,10 +119,6 @@ class BinNormalisationFromAttenuationImage : virtual bool post_processing(); std::string attenuation_image_filename; - -protected: - virtual - std::vector get_related_bins_values(const std::vector&) const; }; diff --git a/src/include/stir/recon_buildblock/BinNormalisationFromECAT7.h b/src/include/stir/recon_buildblock/BinNormalisationFromECAT7.h index 8f0cfa5a80..32c0ddfd2a 100644 --- a/src/include/stir/recon_buildblock/BinNormalisationFromECAT7.h +++ b/src/include/stir/recon_buildblock/BinNormalisationFromECAT7.h @@ -95,7 +95,7 @@ class BinNormalisationFromECAT7 : BinNormalisationFromECAT7(const std::string& filename); virtual Succeeded set_up(const shared_ptr&); - virtual float get_bin_efficiency(const Bin& bin, const double start_time, const double end_time) const; + float get_bin_efficiency(const Bin& bin, const double start_time, const double end_time) const; bool use_detector_efficiencies() const; bool use_dead_time() const; @@ -136,9 +136,6 @@ class BinNormalisationFromECAT7 : virtual bool post_processing(); std::string normalisation_ECAT7_filename; -protected: - virtual - std::vector get_related_bins_values(const std::vector&) const; }; END_NAMESPACE_ECAT7 diff --git a/src/include/stir/recon_buildblock/BinNormalisationFromECAT8.h b/src/include/stir/recon_buildblock/BinNormalisationFromECAT8.h index d45cc30e6f..e3f8bfed34 100644 --- a/src/include/stir/recon_buildblock/BinNormalisationFromECAT8.h +++ b/src/include/stir/recon_buildblock/BinNormalisationFromECAT8.h @@ -135,10 +135,6 @@ class BinNormalisationFromECAT8 : virtual bool post_processing(); string normalisation_ECAT8_filename; - -protected: - virtual - std::vector get_related_bins_values(const std::vector&) const; }; END_NAMESPACE_ECAT diff --git a/src/include/stir/recon_buildblock/BinNormalisationFromProjData.h b/src/include/stir/recon_buildblock/BinNormalisationFromProjData.h index e28c6289b1..1f47bbaf42 100644 --- a/src/include/stir/recon_buildblock/BinNormalisationFromProjData.h +++ b/src/include/stir/recon_buildblock/BinNormalisationFromProjData.h @@ -107,9 +107,6 @@ class BinNormalisationFromProjData : virtual bool post_processing(); std::string normalisation_projdata_filename; -protected: - virtual - std::vector get_related_bins_values(const std::vector&) const; }; diff --git a/src/include/stir/recon_buildblock/ChainedBinNormalisation.h b/src/include/stir/recon_buildblock/ChainedBinNormalisation.h index a8d3c85be2..0aae69824d 100644 --- a/src/include/stir/recon_buildblock/ChainedBinNormalisation.h +++ b/src/include/stir/recon_buildblock/ChainedBinNormalisation.h @@ -110,16 +110,7 @@ ChainedBinNormalisation(shared_ptr const& apply_first, virtual void undo(RelatedViewgrams& viewgrams,const double start_time, const double end_time) const; virtual float get_bin_efficiency(const Bin& bin,const double start_time, const double end_time) const; - //! Apply for related bins - virtual void apply(std::vector& bins, - const double start_time, const double end_time) const; - //! Undo for related bins - virtual void undo(std::vector& bins, - const double start_time, const double end_time) const; -protected: -virtual -std::vector get_related_bins_values(const std::vector&) const; private: shared_ptr apply_first; diff --git a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h index 6ecbd2ce3f..91174db804 100644 --- a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h +++ b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h @@ -2,6 +2,7 @@ // /* Copyright (C) 2003- 2011, Hammersmith Imanet Ltd + Copyright (C) 2015, Univ. of Leeds Copyright (C) 2016, UCL This file is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -124,8 +125,6 @@ typedef RegisteredParsingObject -#include START_NAMESPACE_STIR @@ -199,13 +197,7 @@ class ProjMatrixElemsForOneBin const DiscretisedDensity<3,float>&) const; //! back project related bins void back_project(DiscretisedDensity<3,float>&, - const RelatedBins&) const; - //! Alternative to back project related bins. - //! \warning N.E. I use this function just because I cannot get the - //! RelatedBins class to work for me. - void back_project(DiscretisedDensity<3,float>&, - const std::vector&, - const shared_ptr&); + const RelatedBins&) const; //! forward project related bins void forward_project(RelatedBins&, const DiscretisedDensity<3,float>&) const; diff --git a/src/include/stir/recon_buildblock/TrivialBinNormalisation.h b/src/include/stir/recon_buildblock/TrivialBinNormalisation.h index 24fded8f6d..663cc10712 100644 --- a/src/include/stir/recon_buildblock/TrivialBinNormalisation.h +++ b/src/include/stir/recon_buildblock/TrivialBinNormalisation.h @@ -48,12 +48,6 @@ class TrivialBinNormalisation : virtual inline void apply(RelatedViewgrams&,const double start_time, const double end_time) const {} virtual inline void undo(RelatedViewgrams&,const double start_time, const double end_time) const {} - - //! This is the a bin-wise overload for the apply function. - virtual void apply(std::vector&, const double, const double) const{} - - //! This is the a bin-wise overload for the undo function. - virtual void undo(std::vector&, const double, const double) const {} virtual inline float get_bin_efficiency(const Bin& bin,const double start_time, const double end_time) const { return 1;} @@ -63,11 +57,6 @@ class TrivialBinNormalisation : virtual inline void set_defaults() {} virtual inline void initialise_keymap() {} -protected: - virtual - std::vector get_related_bins_values(const std::vector& ) const - {} - }; END_NAMESPACE_STIR diff --git a/src/recon_buildblock/BinNormalisation.cxx b/src/recon_buildblock/BinNormalisation.cxx index 9e42426692..94a974161c 100644 --- a/src/recon_buildblock/BinNormalisation.cxx +++ b/src/recon_buildblock/BinNormalisation.cxx @@ -174,35 +174,6 @@ undo(ProjData& proj_data,const double start_time, const double end_time, } } -void -BinNormalisation:: -apply(std::vector& r_bins, - const double start_time, const double end_time) const -{ - std::vector normalization_values = this->get_related_bins_values(r_bins); - - assert(r_bins.size() == normalization_values.size()); - - for (unsigned int i = 0; i < r_bins.size(); i++) - { - r_bins[i] *= normalization_values.at(i); - } -} - -void -BinNormalisation:: -undo(std::vector& r_bins, - const double start_time, const double end_time) const -{ - std::vector normalization_values = this->get_related_bins_values(r_bins); - - assert(r_bins.size() == normalization_values.size()); - - for (unsigned int i = 0; i < r_bins.size(); i++) - { - r_bins[i] /= normalization_values.at(i); - } -} END_NAMESPACE_STIR diff --git a/src/recon_buildblock/BinNormalisationFromAttenuationImage.cxx b/src/recon_buildblock/BinNormalisationFromAttenuationImage.cxx index 1a75c39c37..d842689774 100644 --- a/src/recon_buildblock/BinNormalisationFromAttenuationImage.cxx +++ b/src/recon_buildblock/BinNormalisationFromAttenuationImage.cxx @@ -178,10 +178,6 @@ BinNormalisationFromAttenuationImage::get_bin_efficiency(const Bin& bin,const do return 1; } -std::vector BinNormalisationFromAttenuationImage::get_related_bins_values(const std::vector& r_bins) const -{ -error("Not implemented, yet"); -} END_NAMESPACE_STIR diff --git a/src/recon_buildblock/BinNormalisationFromECAT7.cxx b/src/recon_buildblock/BinNormalisationFromECAT7.cxx index dc57f94514..a08d1b2f14 100644 --- a/src/recon_buildblock/BinNormalisationFromECAT7.cxx +++ b/src/recon_buildblock/BinNormalisationFromECAT7.cxx @@ -653,10 +653,7 @@ BinNormalisationFromECAT7::get_dead_time_efficiency (const DetectionPosition<>& } -std::vector BinNormalisationFromECAT7::get_related_bins_values(const std::vector& r_bins) const -{ -error("Not implemented, yet"); -} + END_NAMESPACE_ECAT7 END_NAMESPACE_ECAT diff --git a/src/recon_buildblock/BinNormalisationFromECAT8.cxx b/src/recon_buildblock/BinNormalisationFromECAT8.cxx index aac73dfe62..fec7de4206 100644 --- a/src/recon_buildblock/BinNormalisationFromECAT8.cxx +++ b/src/recon_buildblock/BinNormalisationFromECAT8.cxx @@ -707,11 +707,6 @@ BinNormalisationFromECAT8::get_dead_time_efficiency (const DetectionPosition<>& } -std::vector BinNormalisationFromECAT8::get_related_bins_values(const std::vector& r_bins) const -{ -error("Not implemented, yet"); -} - END_NAMESPACE_ECAT END_NAMESPACE_STIR diff --git a/src/recon_buildblock/BinNormalisationFromProjData.cxx b/src/recon_buildblock/BinNormalisationFromProjData.cxx index 113ceef497..57449e6346 100644 --- a/src/recon_buildblock/BinNormalisationFromProjData.cxx +++ b/src/recon_buildblock/BinNormalisationFromProjData.cxx @@ -171,13 +171,6 @@ BinNormalisationFromProjData::get_bin_efficiency(const Bin& bin,const double sta return 1; } - - -std::vector -BinNormalisationFromProjData::get_related_bins_values(const std::vector& r_bins) const -{ - return this->norm_proj_data_ptr->get_related_bin_values(r_bins); -} END_NAMESPACE_STIR diff --git a/src/recon_buildblock/ChainedBinNormalisation.cxx b/src/recon_buildblock/ChainedBinNormalisation.cxx index c73a568a2e..65cb120adb 100644 --- a/src/recon_buildblock/ChainedBinNormalisation.cxx +++ b/src/recon_buildblock/ChainedBinNormalisation.cxx @@ -120,35 +120,6 @@ ChainedBinNormalisation:: get_bin_efficiency(const Bin& bin,const double start_t : 1); } -void -ChainedBinNormalisation:: -apply(std::vector& bins, - const double start_time, const double end_time) const -{ - if (!is_null_ptr(apply_first)) - apply_first->apply(bins,start_time,end_time); - if (!is_null_ptr(apply_second)) - apply_second->apply(bins,start_time,end_time); -} - -void -ChainedBinNormalisation:: -undo(std::vector& bins, - const double start_time, const double end_time) const -{ - if (!is_null_ptr(apply_first)) - apply_first->undo(bins,start_time,end_time); - if (!is_null_ptr(apply_second)) - apply_second->undo(bins,start_time,end_time); -} -std::vector -ChainedBinNormalisation:: -get_related_bins_values(const std::vector& r_bins) const -{ - error("Not implemented, yet"); - //Something like BINSnormA * BINSnormB -} - END_NAMESPACE_STIR diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx index 1d0b83b1ec..f938408c7f 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx @@ -305,49 +305,6 @@ void PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin:: add_subset_sensitivity(TargetT& sensitivity, const int subset_num) const { - HighResWallClockTimer t; - t.reset(); - t.start(); -// std::cout << "!Nere " <max_ring_difference_num_to_process; -// const int max_segment_num = this->max_ring_difference_num_to_process; - -// std::cout << min_segment_num<< " "<< max_segment_num <proj_data_info_cyl_uncompressed_ptr->get_min_axial_pos_num(segment_num); -// axial_num < this->proj_data_info_cyl_uncompressed_ptr->get_max_axial_pos_num(segment_num); -// axial_num ++) -// { -// // For debugging. -// std::cout <proj_data_info_cyl_uncompressed_ptr->get_min_tangential_pos_num(); -// tang_num < this->proj_data_info_cyl_uncompressed_ptr->get_max_tangential_pos_num(); -// tang_num ++ ) -// { - -// for(int view_num = this->proj_data_info_cyl_uncompressed_ptr->get_min_view_num() + subset_num; -// view_num <= this->proj_data_info_cyl_uncompressed_ptr->get_max_view_num(); -// view_num += this->num_subsets) -// { -// Bin tmp_bin(segment_num, -// view_num, -// axial_num, -// tang_num, 1.f); - -// if (!this->PM_sptr->get_symmetries_ptr()->is_basic(tmp_bin) ) -// continue; - -// this->add_projmatrix_to_sensitivity(sensitivity, tmp_bin); -// } -// } -// } -// } - - const int min_segment_num = -this->max_ring_difference_num_to_process; const int max_segment_num = this->max_ring_difference_num_to_process; @@ -355,9 +312,6 @@ add_subset_sensitivity(TargetT& sensitivity, const int subset_num) const // warning: has to be same as subset scheme used as in distributable_computation for (int segment_num = min_segment_num; segment_num <= max_segment_num; ++segment_num) { - //CPUTimer timer; - //timer.start(); - for (int view = this->proj_data_info_cyl_uncompressed_ptr->get_min_view_num() + subset_num; view <= this->proj_data_info_cyl_uncompressed_ptr->get_max_view_num(); view += this->num_subsets) @@ -370,8 +324,6 @@ add_subset_sensitivity(TargetT& sensitivity, const int subset_num) const } // cerr< @@ -409,37 +361,6 @@ add_view_seg_to_sensitivity(TargetT& sensitivity, const ViewSegmentNumbers& view } - -template -void -PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin:: -add_projmatrix_to_sensitivity(TargetT& sensitivity, Bin & this_basic_bin) const -{ - std::vector r_bins; - ProjMatrixElemsForOneBin elem_row; - - this->PM_sptr->get_symmetries_ptr()->get_related_bins(r_bins, this_basic_bin); - - if (r_bins.size() == 0 ) - error("Something went wrong with the symmetries. Abort."); - - for (unsigned int i = 0; i < r_bins.size(); i++) - r_bins[i].set_bin_value(1.0f); - - - // find efficiencies - { - const double start_frame = this->frame_defs.get_start_time(this->current_frame_num); - const double end_frame = this->frame_defs.get_end_time(this->current_frame_num); - this->normalisation_sptr->undo(r_bins, start_frame,end_frame); - } - - this->PM_sptr->get_proj_matrix_elems_for_one_bin(elem_row, this_basic_bin); - //N.E: I had problems with RelatedBins thats why I use a std::vector and - // symmetries. - elem_row.back_project(sensitivity, r_bins, this->PM_sptr->get_symmetries_sptr()); -} - template TargetT * PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin:: diff --git a/src/recon_buildblock/ProjMatrixElemsForOneBin.cxx b/src/recon_buildblock/ProjMatrixElemsForOneBin.cxx index dcb90e063f..98b5b7a42e 100644 --- a/src/recon_buildblock/ProjMatrixElemsForOneBin.cxx +++ b/src/recon_buildblock/ProjMatrixElemsForOneBin.cxx @@ -415,30 +415,6 @@ back_project(DiscretisedDensity<3,float>& density, } } -void -ProjMatrixElemsForOneBin:: -back_project(DiscretisedDensity<3,float>& density, - const std::vector& r_bins, - const shared_ptr& symmetries) -{ - - ProjMatrixElemsForOneBin row_copy; - - for (std::vector::const_iterator r_bins_iterator =r_bins.begin(); - r_bins_iterator != r_bins.end(); ++ r_bins_iterator) - { - row_copy = *this; - - Bin symmetric_bin = *r_bins_iterator; - // KT 21/02/2002 added check on 0 - if (symmetric_bin.get_bin_value() == 0.f) - return; - auto_ptr symm_ptr = - symmetries->find_symmetry_operation_from_basic_bin(symmetric_bin); - symm_ptr->transform_proj_matrix_elems_for_one_bin(row_copy); - row_copy.back_project(density,symmetric_bin); - } -} void ProjMatrixElemsForOneBin:: From 80982364b812e87968986227c7fb2cacbd06cb42 Mon Sep 17 00:00:00 2001 From: Nikos E Date: Thu, 13 Oct 2016 10:43:32 +0100 Subject: [PATCH 009/170] I removed few lines which would conflict with the branch which addresses issue#54. --- src/include/stir/listmode/CListModeDataECAT.h | 7 ------- src/include/stir/listmode/CListModeDataECAT8_32bit.h | 12 ++++++------ 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/src/include/stir/listmode/CListModeDataECAT.h b/src/include/stir/listmode/CListModeDataECAT.h index 9eaebd41b6..1990aaf5ea 100644 --- a/src/include/stir/listmode/CListModeDataECAT.h +++ b/src/include/stir/listmode/CListModeDataECAT.h @@ -89,13 +89,6 @@ class CListModeDataECAT : public CListModeData /*! \todo this might depend on the acquisition parameters */ virtual bool has_delayeds() const { return true; } - virtual inline - unsigned long int - get_total_number_of_events() const - { - error("Not implemented yet. Abort."); - } - private: std::string listmode_filename_prefix; mutable unsigned int current_lm_file; diff --git a/src/include/stir/listmode/CListModeDataECAT8_32bit.h b/src/include/stir/listmode/CListModeDataECAT8_32bit.h index d36a44bc96..6a6fa9e805 100644 --- a/src/include/stir/listmode/CListModeDataECAT8_32bit.h +++ b/src/include/stir/listmode/CListModeDataECAT8_32bit.h @@ -18,7 +18,7 @@ \file \ingroup listmode \brief Declaration of class stir::ecat::CListModeDataECAT8_32bit - + \author Kris Thielemans */ @@ -40,7 +40,7 @@ namespace ecat { //! A class that reads the listmode data for Siemens scanners /*! \ingroup listmode - This file format is currently used by the Siemens Biograph PET/CT and mMR scanners. + This file format is currently used by the Siemens Biograph PET/CT and mMR scanners. There's an Interfile-like header and a binary file with the actual list mode data. The name of the binary file is given by the value of the "name of data file" keyword in the header. @@ -57,13 +57,13 @@ class CListModeDataECAT8_32bit : public CListModeData virtual std::string get_name() const; - virtual + virtual shared_ptr get_empty_record_sptr() const; - virtual + virtual Succeeded get_next_record(CListRecord& record) const; - virtual + virtual Succeeded reset(); virtual @@ -81,7 +81,7 @@ class CListModeDataECAT8_32bit : public CListModeData get_total_number_of_events() const { error("Not implemented yet. Abort."); - } + }; private: typedef CListRecordECAT8_32bit CListRecordT; From f10b815b11522b10c3cfe878739aad4656fffb02 Mon Sep 17 00:00:00 2001 From: Nikos E Date: Thu, 13 Oct 2016 17:15:04 +0100 Subject: [PATCH 010/170] Added tests for lm reconstruction and a couple of fixes [x]. Crash when segment of measured bin was larger than the max segment in par file. FIXED [x]. Zero stored counts if either total_number_of_events or time_frames were not specified. --- .travis.yml | 1 + recon_test_pack/OSMAPOSL_test_lmf.par | 36 ++++++++++ recon_test_pack/OSMAPOSL_test_proj.par | 35 +++++++++ recon_test_pack/PET_ACQ_small.l.hdr.STIR | 67 ++++++++++++++++++ recon_test_pack/Siemens_mMR_seg2.hs | 47 ++++++++++++ recon_test_pack/Siemens_mMR_seg2.s | 0 ...jdata_from_ROOT.par => lm_to_projdata.par} | 4 +- recon_test_pack/run_root_GATE.sh | 3 +- recon_test_pack/small_listmode_file.l | Bin 0 -> 1019264 bytes recon_test_pack/total_mult.hs | 47 ++++++++++++ ...dWithLinearModelForMeanAndListModeData.cxx | 2 +- ...MeanAndListModeDataWithProjMatrixByBin.cxx | 12 ++-- 12 files changed, 244 insertions(+), 10 deletions(-) create mode 100755 recon_test_pack/OSMAPOSL_test_lmf.par create mode 100755 recon_test_pack/OSMAPOSL_test_proj.par create mode 100644 recon_test_pack/PET_ACQ_small.l.hdr.STIR create mode 100644 recon_test_pack/Siemens_mMR_seg2.hs create mode 100644 recon_test_pack/Siemens_mMR_seg2.s rename recon_test_pack/{lm_to_projdata_from_ROOT.par => lm_to_projdata.par} (94%) create mode 100755 recon_test_pack/small_listmode_file.l create mode 100644 recon_test_pack/total_mult.hs diff --git a/.travis.yml b/.travis.yml index 82b524b94c..de4b9c84c8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -50,6 +50,7 @@ script: - export PATH=$PATH:$TRAVIS_BUILD_DIR/install/usr/local/bin - cd $TRAVIS_BUILD_DIR/recon_test_pack - ./run_test_simulate_and_recon.sh + - ./run_test_listmode_recon.sh - ./run_test_simulate_and_recon_with_motion.sh - ./run_scatter_tests.sh - ./run_tests.sh --nointbp diff --git a/recon_test_pack/OSMAPOSL_test_lmf.par b/recon_test_pack/OSMAPOSL_test_lmf.par new file mode 100755 index 0000000000..725b4a44fc --- /dev/null +++ b/recon_test_pack/OSMAPOSL_test_lmf.par @@ -0,0 +1,36 @@ +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:= total_mult.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 + +end PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin Parameters:= +enforce initial positivity condition:= 1 +number of subsets:= 1 +number of subiterations:= 2 +save estimates at subiteration intervals:= 1 +output filename prefix := my_ouput_t_lm_pr_seg2 +END := diff --git a/recon_test_pack/OSMAPOSL_test_proj.par b/recon_test_pack/OSMAPOSL_test_proj.par new file mode 100755 index 0000000000..a30596a108 --- /dev/null +++ b/recon_test_pack/OSMAPOSL_test_proj.par @@ -0,0 +1,35 @@ +OSMAPOSLParameters := +objective function type:= PoissonLogLikelihoodWithLinearModelForMeanAndProjData +PoissonLogLikelihoodWithLinearModelForMeanAndProjData Parameters:= + input file := my_sinogram_f1g1d0b0.hs + maximum absolute segment number 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:= total_mult.hs + End Bin Normalisation From ProjData:= + + recompute sensitivity := 1 + use subset sensitivities:= 0 + sensitivity filename:= my_sens_t_proj_seg2.hv + zoom := 1 + +end PoissonLogLikelihoodWithLinearModelForMeanAndProjData Parameters:= +enforce initial positivity condition:= 1 +number of subsets:= 1 +number of subiterations:= 2 +save estimates at subiteration intervals:= 1 +output filename prefix := my_output_t_proj_seg2 +END := diff --git a/recon_test_pack/PET_ACQ_small.l.hdr.STIR b/recon_test_pack/PET_ACQ_small.l.hdr.STIR new file mode 100644 index 0000000000..b1b711f626 --- /dev/null +++ b/recon_test_pack/PET_ACQ_small.l.hdr.STIR @@ -0,0 +1,67 @@ +!INTERFILE := +!originating system :=2008 +%SMS-MI header name space :=PETLINK bin address +%SMS-MI version number :=3.4 +!GENERAL DATA := +!data offset in bytes :=0 +name of data file := small_listmode_file.l +!GENERAL IMAGE DATA := +!type of data := PET +number of bytes per pixel := 4 +!type of data :=PET +%study date (yyyy:mm:dd) :=2014:06:24 +%study time (hh:mm:ss GMT+00:00):=15:45:07 +isotope name :=F-18 +isotope gamma halflife (sec) :=6586.2 +isotope branching factor :=0.97 +radiopharmaceutical :=Fluorodeoxyglucose +relative time of tracer injection (sec) :=0 +tracer activity at time of injection (Bq) :=6e+007 +injected volume (ml):=0 +%tracer injection date (yyyy:mm:dd):=2014:06:24 +%tracer injection time (hh:mm:ss GMT+00:00):=15:14:00 +%patient orientation :=HFS +PET data type :=Emission +data format :=CoincidenceList +horizontal bed translation :=stepped +start horizontal bed position (mm) :=0 +end horizontal bed position (mm) :=0 +start vertical bed position (mm) :=0 +%bed zero offset (mm) :=0 +number of energy windows :=1 +%energy window lower level (keV) [1]:=430 +%energy window upper level (keV) [1]:=610 +!PET STUDY (Emission data) := +PET scanner type :=cylindrical +transaxial FOV diameter (cm) :=59.6 +number of rings :=64 +distance between rings (cm) :=0.40625 +gantry tilt angle (degrees) :=0 +gantry crystal radius (cm) :=32.8 +bin size (cm) :=0.20445 +septa state :=none +%number of TOF time bins:=1 +%TOF mashing factor:=1 +!IMAGE DATA DESCRIPTION := +%preset type :=time +%preset value :=3600 +%preset unit:=seconds +number of time frames:=1 +!image duration (sec)[1] :=3600 +%total listmode word counts :=1210129934 +%COINCIDENCE LIST DATA := +%LM event and tag words format (bits) :=32 +%timing tagwords interval (msec) :=1 +%singles polling method :=instantaneous +%singles polling interval (sec) :=2 +%singles scale factor :=8 +%total number of singles blocks :=224 +%axial compression :=1 +%maximum ring difference :=60 +%number of projections :=344 +%number of views :=252 +%number of segments :=11 +%segment table :={127,115, 115, 93, 93 ,71, 71, 49, 49, 27, 27} +%time_sync :=24123339 +%comment:=PET/CT gantry offset during PET acquisition was x=0.000000mm, y=0.000000mm, z=0.000000mm +!END OF INTERFILE := diff --git a/recon_test_pack/Siemens_mMR_seg2.hs b/recon_test_pack/Siemens_mMR_seg2.hs new file mode 100644 index 0000000000..f419f744ec --- /dev/null +++ b/recon_test_pack/Siemens_mMR_seg2.hs @@ -0,0 +1,47 @@ +!INTERFILE := +!imaging modality := PT +name of data file := Siemens_mMR_seg2.s +originating system := Siemens mMR +!version of keys := STIR3.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 := 4 +matrix axis label [4] := segment +!matrix size [4] := 5 +matrix axis label [3] := view +!matrix size [3] := 252 +matrix axis label [2] := axial coordinate +!matrix size [2] := { 62,63,64,63,62} +matrix axis label [1] := tangential coordinate +!matrix size [1] := 344 +minimum ring difference per segment := { -2,-1,0,1,2} +maximum ring difference per segment := { -2,-1,0,1,2} +Scanner parameters:= +Scanner type := Siemens mMR +Number of rings := 64 +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 := 2 +Number of crystals per block in axial direction := 8 +Number of crystals per block in transaxial direction := 9 +Number of detector layers := 1 +Number of crystals per singles unit in axial direction := 16 +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 +!END OF INTERFILE := diff --git a/recon_test_pack/Siemens_mMR_seg2.s b/recon_test_pack/Siemens_mMR_seg2.s new file mode 100644 index 0000000000..e69de29bb2 diff --git a/recon_test_pack/lm_to_projdata_from_ROOT.par b/recon_test_pack/lm_to_projdata.par similarity index 94% rename from recon_test_pack/lm_to_projdata_from_ROOT.par rename to recon_test_pack/lm_to_projdata.par index 792cb38be2..d55fd474ae 100755 --- a/recon_test_pack/lm_to_projdata_from_ROOT.par +++ b/recon_test_pack/lm_to_projdata.par @@ -1,11 +1,11 @@ lm_to_projdata Parameters:= - input file := root_header.hroot + input file := ${INPUT} output filename prefix := ${OUT_PROJDATA_FILE} ; parameters that determine the sizes etc of the output - template_projdata := template_for_ROOT_scanner.hs + template_projdata := ${TEMPLATE} ; the next can be used to use a smaller number of segments than given ; in the template maximum absolute segment number to process := -1 diff --git a/recon_test_pack/run_root_GATE.sh b/recon_test_pack/run_root_GATE.sh index 5ffc0f2320..2c0cf8a7c1 100755 --- a/recon_test_pack/run_root_GATE.sh +++ b/recon_test_pack/run_root_GATE.sh @@ -72,7 +72,8 @@ rm -f my_*v my_*s my_*S INSTALL_DIR=$1 ThereWereErrors=0 export INPUT_ROOT_FILE=test_PET_GATE.root - +export INPUT=root_header.hroot +export TEMPLATE=template_for_ROOT_scanner.hs echo ------------- Converting ROOT files to ProjData file ------------- echo Making ProjData for all events diff --git a/recon_test_pack/small_listmode_file.l b/recon_test_pack/small_listmode_file.l new file mode 100755 index 0000000000000000000000000000000000000000..a0ad2449e5dc2221a4037e14be452364c40ad1e2 GIT binary patch literal 1019264 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
sQjbz1fB8?>Qjsy1Z!ShK(Dy@ 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

}4~6YLV2M5~;S*5m0y{gl=GJ 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≷N{?9^I?&^A6Q&QVAbA#E5Dm&c=f|WWy3O{l2 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

    Mx#dIL0^dNEI9h`y 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 ? zzbiHce@Cj-jqwo!R@p50qF|4)z<-*I$E{=@|-hO+2ZPJ1<2e>)wb`E;;kcocx7* z+~xVrDP!av_2+^Qupw&pI*(j@aFK@9=R2rQz9RPS!$&B}=MFYt)r2MA*(m=;at1xV zWRQ)w%>&mNs7b2a4#cLWoH^lkmC_}5&f%&{mGR`tc#XGQ2ex)$o~X-N0#~w};_>IH zdoI*I2j>9q=@wT7RwKFv@=23c*S~5@{_V`A8cp+nEweu$ods8jvVu-{Oq@|$Ru&2C zgOaIP9}h{DmKBnC`miuG@R97kWy<)2m9FSN;F2Q3(d3k6U!m0PHewTJC(*0=rI2pl zMdh0hx>-)!eBksCKmLfr2O@M^nMlf585xo;V^7NGLpnu$P>W8c=uMysRP3oi|2Xg5%B zm4PKuM0lgh-A3F#_$p6J;}X;POCYqJ&=N#v`6;YA)yh5TP>&PiJEW>~b`c-5tYvmD z`-FF9S&C-PH}fv3%#sXV#N*G+ng<^vY=x6W+U$>4nnXj$Y%phsJpTkXi#<0t3|(L1 zN#9%3ip$Ec0k7;a5S?8Rti7s$C8xL8GMgR!Ve4}hsu+d!9V4;K&G}V`m)_bQT3#pPKoqlcHt8-Bd4(yy|wH=a?^yaB)M?@iuv?s ze$7%p8J5TzR$*+GeIuK9?0^oM)Q2BFYk&?L?|faiaQN~oCcwo=IEEZYFQ+>ScFDxzKOd`5m*0DeVy6=6^}AxVobo@>SMBuC#by{# zTXz-m%8!v4sI|)POyLvzY3q^4Qbd zR}g1Hm6kd30;>7(g|=vM5ypSaLyHZ{l+66gL`xqpk!Uxo;}3OR<@H~^%6ELW4Utye z%O8trV}9-mL9qwjkXVw=997+potubabFDXt9-R%Mk;;%%(I0L-+wf$+BlLh$(yxN^paE>{lc&i8QToo!o6?OL6UU5m?QE~v~# z_0K=$y=!g5hqV_ofRqL`Yi=E=5XWkXwbt|wDOc`cNmX3?WxOb={R`k9Z!Z!A+~BR< zZ6frKeXV@8+@HPVc3AM&kOo&1b;QA?UBHK#`Ap$=0hNEJm6BR)$7)K;lNQA-=#n)o zJ2op^wyyL79=mshoHjRMdA{iyEq}|2Jl9b`P-m{VyTBUTvzIGq=#A$bDXU~@ zuO~mz#&jJ4J}No|GEd%%80B z(nKW!SjT%j7L7j-_P)G=6(`Jta+eija)YMm`6D_^cHcPDJ||vvW7S{Y?__NZo*5w; z!=_o#=c{IovmUeLNVj%Y*d>sxScD859mC%Kok9L9d8?!|vJyDTp7){53|v`Y3FR)af)zfx6E~IAls3)p5`|xE68y`#1>AK~ z6}ouX@}Dl9#!k$Wp>_O1Si|J2%qs6QWLDb-VU}?fm|OXfT>b(lI?P7+X{L3A4ynxp z*O(Ey_J=tAN^+q8Xcgz-uFLWRozc{;nI5%s^-8pXtBz#dS_j4AUT?8lM+4l`->BAA ziD6PCq#eSp9mInm7pc3P-+IzRNhoHg_}wh zK;}QJkY$ftg^fQ9gl@;h+{1s0kVj`W^XT~$Fl{;?oiBTovoxWEy&ScQ-<-V|Xo;6dx5|+{WTb*yIPh@ufhhWVY$N}IRXJ;W$CMN~>%eoKw4iSrFLD<6 zeWGLXDcP30I9BdrL=^OwQ%|a*C9n8~BQLG(1s|L)kSm5)QoYVc_=WGp`o|BW7@jr& zUKsYnPhMPz;j`DmUKIyX{D3SLSonmQ%$1{B6o!aXjfe3sL8Vx!{!?z#8ExK-@>8tn zojG+^%a)g2$)RNQ=85>qjm3fvo3=vSx%H2eW<)U$o7lTDr%T;}crQ*H(A1WkHt0!@m!q(@(aLJzuv& zVXEc=k(V}oaNb8;Zq9v8Z`UT=c+Fbs>Zk|x>Qn(F{Ir|c+T6!Ba+c7G_JxD|EkXj( zegJ%6FP324P6#NO@tDW}7MYz#tjr6%2Qh20E1aEabz*jg(A%7}H zpoEPD!oEX)iLcg2Npy=qeeLOwSm~Eawf%k5yt21Dm_2rboaxCZ(!)s)H&$2!N&*u& zaEIil>DaSj6&fPnqHJAi($j)J6sO-_BfKyrrt^8V=vMN>-1%1n6I};6)sP~kZ&E^k$ zPZm-M4&JQB=`rlgEfe9q-Z!EH2KnUC$@|=UqDRb5{b1~seH%`#w}HVwLD;^jd{FSf zOMqAml6k4c*oGu=CtCkFmDgCqJ^%T#`q}HTGS7+=SVqGHB&2XCb=Dee*7CE6#UdT# z(%e?)wh~TDO87(8u>!#{`x!>EUlFjoJy#g2Ji~5x^}q~jlpy(yn=!i>4^DCBXM#>< zz?Xhc*jImwM0@iI_eneQyRZL?gm_RO@dGNbiJ_WoVCLO?AAaDs zi$Ic-8Fx#X3omRu6;FP6o~XUYQNQ!`7J?)9E2G= zFXk+X(iDxrAw;HQFmJzk08sws8qie2BKMUY2u)%e`FLNuQbbQTUA0RH^_~18+(Pg% z|2R`_a#pua)+clDl6eL&X)FLLaZAhxd`w?ro3n_6?OPicOQ=s)qMDKi=s0dR`U!M-c8!Pr=8)Zy)|`(4`NBO`XGoWShQLr) z8hl_~Aw|U!TweDT>aJ}e-#6(for2#-?vnxVuLTY0`E7I2vcYH$=XoxWf`sr_iD%|@ zJLj?n-|dKK`wpq|R2$)>KL&;As8WF11S|7whH<$Rh!Xp%k-)hIBANMz*xCKzlu^xF zRZso~eh1Ocr8lywl@1^HW655`S)Y$k^Nx1tpot=;+J`{j=>)ky>ILM3ViQr7mkWII zpaxSocQ>`DyO({bwG?T--vg0*LK*6M6XSk*9rXC%V>(Ka0ZYrp>{RM0zjC)oX6FM_ zo>ae~P~YJ?;5VSlDG!X4>n5b7ZPowd&WUc8k3b@L6Ut>eMQv@IL#D%6wb3OuHBCyP zt4>35ZG<@^O6&*rj=p5OAN`^J<^@1zkAk#K7bVJt49I(q>`O%sx5m->MfJE>t++4B zUrPkl?x&6yodjKET}AAYui!RYU6J#eT0#G#Z&WX*S!&W~6WUkZ&e?llitrc8DohH# zg8z9DSk&lE+O{o9xz=(oKQVPs<8bwMwYzfG{G8$r>QXN!qk6u0ua(*NVdSKXk~vg&aMeOpv@W+S7xBaC*iT!k+}RB za>HrC-LnIng2_8J|DAbDm)G*;O#*`ahNYn$?uj zi?!7Bx>m4H|D43*(cf&k)?IGto&!9GttG<9cgp0bvJp>sk%Q?ZwNPH(pWx;t6mcRV zg>1M|$!xqa7hM+asi7e8UbwX>15cahAvxR99jiaBs5Wcxmy%&xgP>eXis*T;lUb7g z8g>t>!PI}_c=Ek-nDuFE?usWOq&IRg>=NO^kAqg>b8oe>!^RPtXKO4iM!fyO%2zGbSoqU+yyYDxMdVjdQb&E+?a*A9dd&dQZx}m zpnz24&4XV&aMq}fdy1^vn2eut*FZ|*Hw)WnC*dCB6oIoKfc^fVP}~nH2liEt(8{M} zAx3v4E-9D9wRT-aKb|*Anl}3Aycsz{1&t|UJF_B)tO`PO)V4@n-51f+Z+eS0c1M#* zZ$5AWf{)_OdYhmPH*PV9`PNK31JKP6m8c?TK7IR)9rj%)i!>qY;JvxCk(Igj_}{!| z+!;Ziy}j6aw_>`)c^f^~@E$(4%bJFgJfZSYgdY4Oq801)sRwCASa!=6h0>)p zm|f-#-p|7kSYzyRlD`eX|8sFh7Xmg^s$(JGJVN0!jd`?*-&IA=y~nv(F2z_*)oife zO&=fMV+HY!YynmOY=<{Fp5|A3L}5n_Tx1#oOL;qMI+RZ;Xvl}DZW17#T$PvaZqXV( z0rVo%4NQz)jrPbDC$P=P716zO9VidUVY%v#^4`nLIaf@&snD|$@Q;-EH+!B4~UaY&1I%u;{ZV z9}1EQixPb$XX9>k5%HXAC+AX(Y#liv&MtS^T2Vq!3iK&Yid@~)BV?Mks&D-=&Ak8N zFZ|P;4pm9MmQNdAr#SL*k>KsNpVH$J8xY4~J7V`gA0Xb}2tK%jV4aVCWbmnNqJ^ct z^yQX?A~oj>$lp?nA0xe=dv<>{ki2fEp#GTvcfV^vbgy|q_{zMKTH7-Y^IBnKB4D?y zS;+_V;+%d}u8E>1A*61yVEl(xCVojM$tWO>Nrzj?!$~LAP6{;g7GaN9P;w5*k8ov_;%~#O#BI_Jp0FAU|P26a5nk`iq531@!vFB+Qq{Df1U%0p`AwUE)U4z_O69`5S$ zhZ38`GpPUz;o`(h)=kRLA01BuVp@Youh z_Gu|oXp20Ds;;O<^i*}&8RvUQ&eD8f^7nVqtGSNY5ycaPO}R*foj;D<-KvOmUK25$ z(uX*v1uht>70howc?M0%^`tpsS7nTP7){97D^;oQk)1lZNp$r2a=y&!^ISP#9W{_6 zP1n3Tt@%W+96Wiy7$)IV%xLleVVcn@S5e(8VE*O9&Pz0bB~E+!y4SvuarK7WKjxQ( zi^n24yK}P8eGjz|uxt-6HLi$B^l0Lb*ywp0Ch|e5GLGIeT92pieL=qQScX@fic|IQ zl|yAG%dnUd5f@p17hLMRk3Pj9g!sG&I(0>x$PY~7w#;)-Y;`CDdN{7siKr0jEt3h| zd-H=18HELI3R{W8eRW#8Zt<$x)zK2ki!+7AcKw1y6_vt4=V(?&?hggVhq0{TE#APo z*@R@SuR^ZtTA}O#MTKVBPxL*%S1OefVmwlaH_=tN71KG}x+`HbFw;}csW{!u-OJq#@4vZF;oQk1oIF2*BQ;{EAcc$X#e|8> zqFHC9@J1h4z-?mosS1fRX$@Sx)E+?nP$$Z1kd+&>dCa}cZKvbc%>wSaKVX8+gu}M_ zuGIdK$LOzDLG(O>Av$WTiJNxvjPyerEw=k-wj86}N`$}378c+9MxKo?6veA<622UL z%uQBk#9HsnMIO$0aKAJA2$9}#Hf51IZ<}8Swd=@dQ4DtkZm`z|y{yu)k|;}Px}ulr zE49bV``6%UnTYbyXkFp_R~cmE>~@X+F8xuef6*fp=B>bP(DhnLO_hp|nE&AN&5tpq zgZ4;yrZVuvdzREinX7D0>1STq=x3<^U#y1mJ_}J*Mk9Xuha7dCbrRUCjv#5(YtaX@ zx&&4?t;h+#BI=)(J8^c2Iw+gAK=gPJVwdZF;7ZKa7hYN@r?5~wgO6<+#a#dq|_$W8C7)XQ2}nHlon$ZrHZ%xtuSDcS_#6$4dQd&j5VxKV+NBw&4TE z)g=u~6z~eEecYO(%~G467s(#8OV}K1YvAM)bGgMI;x#A58L{)r zS<+lph3R^30Ve5Z512Y1sV+x(=IxyW;jDc?Z3OvKa@MO~?^S zU>wX%Yu^f-KGvuiQoa;5^&S^ln5to0@)n^9t*7bsmDiE^Zay-i{njY%VlCKWst?9K z4;F4cbCC@B7f+{f4$FFfI6%D*nigb_Tm^p}+d*7Rye0YP?hcJyi9N)`yPN23tzuGT zXqugMbOVX#cc71|=HgD553+NXr;=gTZA3=Do_KHcP`(BSnYvClI<fwBpu{rz~ML*01ljKx9pYE6B-+&`!y0382dnO4)(r`8emE|e@xaGw!bZQXI8GI|e z^lL8fcAYYk_qPj_419=?3X{A?yMxi(IjQ`agWiH!mgS;}g-#qL{Xtyg$Wrz{opB)O z_+{dOqCex@paYz)Il!7LHuE3GxY5^BPKt<$0BvD&C2hC&JaB04cjT&=q0=G$$BMH# z4{c0|LRKFS<~@!*4X7pg36l$NtKnnu-0O!{l8Z!hbW);>fc2Yt0WTz<>c4yySabQD zq%WAtY(MWJH;p$a{=D)8L1z~NS34X;J+EBFv*8L_x3!%4PkNL3OPgr^8rLTBz*3UDzd|}020G-1eG@((7FG` zQLAg6L3#HE4Ps2Zy<{uFdy|mZ{ZL0`M>y~!ZVRce!v~3S&2OCR(-76Q^8xq$qI7EC z=6vk#(P2gn>E-@D^$-(#3j5XrxDUr-Guw`TpIMQfyZtWugI3? z{8L)r)Wn20YBFV`-I`ED2b|mEEsf}XCgoxSc&~h2p_vx(xB2|f)HP8lu%=6kIKApL z{@?NzA|NT4qoXs5o1yv8)U#n)2l|4{CGuol3u@4Y9uYa@E`i_Q*sSHN{!REiMb&fT zP%f)&S_Tals56!PXFT)ZgPflaWqI?jH=+}dGRe=C;`fH3E%Vsuk@n$^JNQ775>**Y zNSj!8!~5qj$XwM@xb*!J#!2}+sA3hYxb(g$Q!jQK3Ccdv<2Mg;GgxoHtmB(-IOnUD zNdP50Z8ZsO(@>{mqn7h;=Xo(@XPycxj0}VkUZ+JD3MT0KN>_G!_;T)v`S0+xDcVf* zrv$;IWII31UUcBKpGzN1@x2-LO?aFt;W7E$7wcM{MxmN-*!DwC3OB z%i!;g`m`-`8<-Ux&g-~3ip1Y<6h75otNQV^k8({>G!OKBB{J&Q5^T)rPwh@BJo5iUOV4|WL>zjD{HSoi%UD0y{1_}O z?v9}?P5iiaR~Mo_3v&eTMO(-dr8@|0aj-~jjL%e9cd6gz7QucQ_lS~H_fQRD$^3MK&XX=Kf$*@MNplY4Z;tHN&tkpX2c8IhzQ;p@_RvrH>ZK zzaYofO5(51B%-8k4xY8+54CRdQS|jqUGR2{1?r{$lBnIDie=VS3+H{x(9GL(hVHcA zL9LbL;V*p4Ree@i!;N$)x=4GzFxo^-E>X3P^8V8Uy^*+&U;5*P3-qes-rpslg6SCN zSVkJN;r>E&o8yeo-|{~h8#x_J!eJrzTwNdBwlhREm%asl*E}O%*{&?r$Uai2i$B1x ztz1eCT1=sQNEIHq_Lg7c7pM_Xyp@U)O2Ttx8zmlv?j*}J9&@t1_Vc=wT;V-u=7|0i zyk#RMc2gC%W(&_r>jL`uOR@MQC1K6z17iNKr%WI;O5X@Q$j#ha!nInTBT#e9ho8g% zaN z+{edXvgSvVX=86G>aI^8V0LM~#FbKYF4Xc&_2FnfxFcwse&P-=iSRmdL9-9`a!V{7 zZvUHrX7VYCCM)#IQh(l#-G1z2aX;Yget{1RLf?|-?pX(qYh+^T2R#J2 z>&`%$e_hbgMrE0h`#EYRsxRRd_Y$Gom#0MhlZEut@13HFWIsAb={4ueFBNLVg4vMt zm8Fb@;G4k1cYu<;kR+&Y8WnPRdth74k(srHgZaA$V&996=*>F4HU zX~w_-{~twX9#_NL#^LsTUqnf^R0<^_S>~KMb7tSCQi+sGN|G%lLJFZ!QnV|wCxo(v zY|&nRMVod@L_*{}@4xfs%;)pm_uS9@y{=Sr9f!cS?7$mc*EtqV;Pf~jcuvkoI8SXF za3U;7^sP#ZTIqEK-b&1%M#C?HT5~aBXx$4&!Cr?M`Z$85yu@|zDv?CJ?jf&oCqP5^ z93pNQGZW=~)}f!N=OC$uS*#TrN!(33PG9%=&d9oB4ES^z`EUIsb(5HhSb!N%h3@<# zb{S&TkG%un*?-QX22{O4j=>Zv`|&&>+`pc5%h@3=YLI1qmsYTE8t3qFTXm_*Xj>?z zFo{2PemiH$UZ6uX#;K%TR^Z%wqcmpbToOZ(nY2dZLH^|-9dPmWWUS)NM7S-KWL8W} zPzE)|^T-x!61`IgpFN_^eUN#Mm$tcsCshvuv+^d=rDIq0tPRx})3i>FJCnZSs;iR# z)yxCf+NIH=-AlSzr`8R?+It5G&9`cT^TlLn$=fM}oa-rcZtoKr3(sHf^X_f>HYF@x zHM>c8ywnD`*;c`l9u@d@mtuA-HIAfWstKiIDx`{D4R!I938d3~STea~K2(#Jr;`;? z#>#K`Pc8Xq9Gf=vFh9fDNBXzpi@w;S1Bgz~BnO~{bcahh%4^0eh8(&GEs#^k-6zzj z9D`7Bo%%S@52Jq8ZzG&rLjK z1seJW3M8Mh*c~5n8Ak<@P3&yD($k71~!kUEiL<*GdsQ5k`+OS zsPc%;L!T5VA1J{M&2{)a1JBvp)E#Q{K11zgw~HIXW4QuQo?W4B#&|6L!{%%W(Oat( ziC3fp%%;_*^jF0#)IIla(j#U0=sG1EI4sW``mVp4ZxUw--451J#gZNv*!wO+o*3EnGeAve+$mmI@u1P5=4)Rc=AzDIN&Dgnjh$qtz}} zizd$;4}B6|OXv3au=2s(xXIicB+*Eo=$bu^s}8u1%*?rIP>CKR4qST+G#Ah1=7ws> zKifK$@}B#GS@86U&L8VgBA%5hBF>y`B8FeeVRf@@w3e%>GPyUligtYtK|Br5b0xD|$gGJ=h1MV| z7@ISj0B${HHNigAmES^4_>UJ3eOXGF9+)T5-~AR1v_5EvUr*DW@@O3HRH8trlgjLg zrH#5v&!o|RWi==CYro*0ycvI(w;eg_WGhG)L_%>+CCY~DX0uP^!q{ctppilN7#-hI zthmEif{#7cz%ztmFp=N{=O`;q<+W@Q&xwjMU|-zwehy{bO!{`|UpJ z@azj(uJRUtrdEwb3NB0i8t+l<6@}uSsENRmgfqa?^#?UGs~?C|!ftRQ^#RyGxjbkp zt|wnUxPyLekK@c;9%8FA=Bkthm9rgT%EZ#&UECddh4@*Z7o)Ns;UBcT6z!Kjz!mCy z$g0+J_)#4j>=M!lT#>EjZ^ZJX{o+G_$n_UqnKM^^xM4sz5NkvQ&YLb4D$}W&_%xDO*h!2>2lBpq!rkzXa(JOE+AER+bPG( z!HCa-XDY4zQaI({4t&}BKq?^OJy7w=n6r3WDSx{|8?O#D0E5>R;b+b~Lkq_j(#<7i z^om&xy2_dZ9mk+Ga7^ggjo@OUq z{=n7zoJrr*`XYHzW=CCZX0S%pP{>JokD2E14lP#HrF}A{OLmwW7Or~7eK77u zJZ-~&+3y7Vxf9W4yG!|yc|o$>9xZU&(-=0!RF3Sabz_I(`msf;g7x-%OsBtF8uKNv z0lfO7IU8S~55237};MY$nR@mU3c@iZB!Z; zcx9NW2skTwP&S(%vX5ege}Bc^Z=aJndtm9eW25vpg>R7Ax~KSzJ-Ng&^CYdwYpm!i z=aj(0PwTbCnp?#AHBTw@)=^-y@>@uQI|3J>awK3cbBee)02Zr`^4GhXrI)i4amCes z>?-{VJ-tOX0?CTdsfN0)727BAI-ybQBSYEUkn>bhVP?2kLE)9rlf5UIH?y~)`gay; zy$&*9?jPBKjN3gG_-j6@G`u@TN_tjGb*XNtafxewhms}s2Bvslux%bfp^SC7P1VN{R$S9rVg7}`P+>R&-6C-ys$_f$m`=)v$t>*=Z*$Y6?@!lSOa&(ONnY;(J zetw4V`d+j=b~F;`oTASqM#kV3lTJu%;^ok{lV`F@#5Ll^=AUrkur2B0Yoa!GSOEqt zC1ADjOtMXBy99{3q#Y%A8fMvcNSj>`ismHvVhY|<#mZ-uxu-K9NiRBmQ)nw+PupDe zhx=}aA+^9-vAIhp-2WpT+_gOvNzAXs4uT`#%epC&b9V!kBYKXbQp0#){JZrAE&sZq zr?<`u_a5AYhELW=;@;+o52jwm_WL}C6Km7?n6xOk<;68Z-TanrTmN~%LEeFqQtI5q z`(l2XW)tvtbS-+>FBUI6ecVtBx1q-^tPr>uEqwa=Q1NJ#2m9g5PRt>9ySmf(%K|hQ zhh%pP8mr%rVyAC#`V6Wndt?d!?CMLA zk%EhKWif=e4fZ1Di(S}YwbkU9td?kxMnL7b9UX2JCcWC1kFEwf;qOW>aG>!4ZZLz^ zOe%5F>FoKBRC>6EZnH_oTg2~q1+#F)tc26>w`E$I7yQTZOQA~jC!1nKKhx5f?ZXz3 zuL46KBN5FT-V3NyYLxe#d`PmmID+{lmkXl&#-I#1^jGW=5Bb8WZ)7Fh=*`<~% zi5ce(QO_!l!HQn-jA6@Z>e6}@(z)~$nybA`)CIaoXSg*2z~+gpr|%+eru`vBA@2^g zNhuY!F$)vLNzAa@IjhN&YfEL}zvCFO;T-E197QUumI^zMeAY5O9cTECPk`qxPhs?3 zCxN%OLSUz%k?WPw$uEtlpM}U1D)mN)n@F;kiR*%TDs*?IA*e$Cdb%5+HY$ zxqiHc)7aU|S8eTuM@{xIzL9ns?-nQ<1bleNZhCS7?9y*%W4}S{k`w9>C_e+(>2pPA z-qUvCeO4r4x=|G#`m#jgRjx&QR%SBdy(-MP@}#8`9Vkw{>;y^=ImA-R7~ z)PcicWxQTO0J7ZXHy+*{t*}k^HIoCo6ANSJh>}`QaVPhwiT1zyP5zp^O(eRZ!Ijx& zgOTT-N(#(wA=6&RgB!z^&_i2WK;@0AA;oRQN|fqX=I!NdF6q%2^v5t>u(Vi(mCz&9 z<9xR0y*vqR!)wi!1*a+rGD+NAch=mosi57&)0j1skd2&KK7l&O?O8T6#3 zhR}zECWGPZXh?B!mE2bMWGdb^hwMtdMdk(?aX;=)L%@&QaGlO8__)Ax%sm^~+MgLH zULl)eyer%%{j_Pac3Q_RAw2LWyM56FI;F7zJG0&r*fjnaX}I<)9ejl1BXt#omsd85 zhV_r)DOpN_$A@<0$HOecT8(4+Ct)0V1$F(SE zpwqEi^*UmscqOGwH=N0GQeakz$3Z?>p3stOitx|cVrZrBVqtq)xyXOPPaJc90{>W{ zNp8CNfiESa*fF;zL&HPWlFi>Iz)$3UF?r5r$ab|ZZf~}tYkk*aiSc1IqsYd6k_~S5 z!sOyb_;w9}()k^W+Z*IT~4|yz*!V{e3DYi#7Jqv>ImL%;xS?bn*xQHjcBSzJ7_zfGZtcx zPBcs`(`KtuT7Z*3wK>&^S#odO-bwzvUy4UPQvhB&95+b)Wy_nS^&st2Iin+1bwD52 z%lkrGg_kzCrsw*2Acfx{WAHiv#YJ<;J2~@#qP6oSi5Yv4jG7JP<{zdA*3iN3=+@v( zHs4eqUn@_2JmDht!SRjJ#JcRpY@FKjg@JcS!Z{Cc`M> zcEs*v5k51)7PXEw711kM1-IB8Je#u1)3b}r7Bx(No|vZis3SDiLb?EdU#kFs9lh#r*zVeunX%_wjO<^ zEB>QKwkqG%JNj!DdftK;L@6^gk}|u{!ZQ%_aE^?oU9&|n51(W3QEP?(-_VCcAW$>Y zI#+U4BM8eo|C~Y}i#eC&kC{l_efa7->lkENA6#-F6FSn+#_31>24}Zz)qk{{MReZz zAS-qLQ{4H&2Me6*jc8~^WAcg1!TQ+Cw2Nybqw9HE(QdOlyT#}R?Ovx&?H-f)F7Zg| zcSk+W#JEP31Xf7V4f|*ZtqFvG#8iI#gVkV@3*tzf0QdO0uOV>EC%5%-e6q}Jr_sCfF^ z4$h}-HoNPmifj6X>AL@9wJo}2Jip9uJ>+?zO7h(66SBACJFMh68`!yA#@NzV*1SFT zfO;|~mG3=m%hIYEaFs$I1ABb}pBuLlC*LoSpt5OGa#jpdXK*f;B z@>yYD5dYLV(k6NyUim12yWhATn(r|Suxb!sxq-jp+3JDfu_=G?7tbxUFTeEC+H*dE z(P~mrS=f46rQBu;lYU;+(A3lcaCj9ZsmREJx3tfe>gZZyXiYpDv3xmu)8Y;j=CF$u zyV~KP~3My@b6qlDn72m?A)W6K+`kmq~3Orag|vnCRS=>?*2*{tT-&q z5+*3@8`nSsw)?3XL|J;^Oc zm6m>A4-VG#utz?%5X)Qb*qE_cc8PldLnzFlUXB|@7Hw9gDi)du>sN{C1eN(}F=Mki zGCCg#H=55V*9D^RkqDz7q3@vRwT}$I%7ap^lQldQo4Q36`kyMzk6_G8vl-6Ef^v2$7VJN>2@LX_+ zZT_yV(--|m+J@Y~t>aVaRe}Cw*Z3FoiA;t~?C@pW23xUA1vlZYZ8-l`^iO@k;BVnb ztG|qj@LChj-3Z(1Z4v$r|K!Z4Yyx6K{izumhc~ih19f*RG7ls43m5qg9zeUo~m$MjT>I>#< zVuR2!%N1ufPBgfQ%op7RkLKLq>HE`>Ek+>#D*40sKD?#p@ox^elzD~rt}*0o@j%4iuoph4 z9?bWiiWamJ(kOcHy=38XRyd%u0jjaJ5p_*FN@bZf03QE3wS467f+q)(bXFbRt?*>a zV_M@jC_q;E{q>4y9faVdThEPw8c6Q{$7`5L+6yY_*c zXx=O8ikbnMbm=TI&Cvzx-z=q8RqB%$5AMX5$19NBj<0yd(l*r4Gu1#-;UW~7sK&)O z{~{BHLxe&+klzvVipcIsl&t;{BL4gB9b~Cj%^$sXo!<5?n#TT^=nu4L3G3H=QkZCv zNG$y~#$-KmA>JKG72GHNQGT&?ndrVU2~V5cD*E=MNBeix94_J01UM!sPdh#C4JL0^ zOwRlB13X^tjhr=?bG>Y)hXe?jtbmTrq*ixl_x46o}*A5%G0&go1_oIcd(d8C2zIO4ElK8 zMm+J-Vo_H>0zAV+0lD#cnr58*9?AOrL)5IcBF1bb*m{}jy>f^z`=SDmsd>D~z+tc;O*3yk{ngK_xD zp>B9_#z!1^k|3-*TgI5rolC5cOXB9W5a7;t8sdRLJ?MUc2Tashh%S}OILum7>g6F> zR&vl?{yyv?Dl=VArXLIvd}p<>iQio`7M=RX&KsYt)-d*%n{zV<)!kqLyki##jl#$H zE$ZLt`#BE4^lj#<#_1RFk?%VoT)BacccAFC6;`5C$K)tS$C-fHvq#jYB4uG-*AdB_ zQ~hXGia+x12212`nFR!B0~j1>A=uU3RARS05)N(}<64U*Asfnl)wUKikzXHMK}=h# zq7@>MO!^STwiI6z#;uDYx8FQNW_@s{yVxxXIamKuj}GX;v+nM~ukM{K>g+rw`RCDx z<0sPLz_FF$+MW#}{RdB(oy{k;jN!ZDTJ4Q^>v)j6th3{t5i&&)|vE)KNl4A&OG7PE}caD(d~s)DzfDI)}KQ!7p{W_=on7N|1Ul_RbJC# z$~U3E_$qWSYyi=<_Yr7zJ9Ip27EzUw3^?juAxrjfr1o)HUs^l|-4nABy5Wt0$$KA( zQovgD!+^WW^ua(R?7@06`O|W~YkLTPWp_AnVZ|eI?lgO~t#42BQ+-$R1@RQtcO@MU zDN`iAITfPll|OWQY&`$?>@LcwqDD0Gz)Nn+^a=EtPqMtkqc+ik-{GPQ+IG5LQ!i_J z-*F&j?2$e1@nAIQj7uU(Qk znN5h`)Tg674!~xImXeYL74$u;kIXLaM--Fas~Z29B5!fGjgn8C z!Fwd6P^FdYAh)Vo`p}B!RG`{i=GTs7bQ!aNOO~IfrB#XGI%j9HF^-|)6SI|(fJH5m z84K@gKGG7?1c^MBz#U%VpwLa*~Pogn_?sU%7D{NJdXQ-|2HdKZ2{ zaV4t9+Gwn(3jl4-UPLM%Y!EjuQ-i(ioOK>{xnfVd^3fc}7`9E}7y+5vPzQYddEMAn z>hF?Ol-ox@qBqxr{P6fP!oJ&OpnPk&;P^2{VXDO>uwb<_7rQZ(Don8C&a|uIe`;Kr z>e+IHc*k^(wx2Dw^6ugvTCb6gM#gZ9&V>qccD~$Pzb<&O$P<}>&H?Voe6Fp<4Mb3( zxzO}$p6F(`Ew>d{Cb9T^=ve7PY<`3tUv_(7%CXyOF0|i1RvPGGD{lY3lt0{&OEhn| zZ^#e4<>tGwfbZ} z;~AdaZ+#3heUi+~w%a1e9Xk)l$UMt;M#hQ9nhZtV2R^XHm6Y(?VTxwLV3xu5+GfSB zDg)NlmsdSxvy>Qk6i=uxZUE~4F4w+Rw3Hh{i;*_?1#xYfsUWd27p>R7%6pAPl5Pg4 zMDntFl#b_PNxR+koi&Ya6aIez%TM|K|hF zPCd)+y`#ndHS6H6uA`|bro(JhkCw!&;U1|peII9l+lhQijfj^Mo^xONwj$lXhs5ur zD^x!yZPX}Iauw|U+Hh0mSP1fGeh8WmKLcmoOp#CCMLG|uLizFiiIRd%oKBh;SGZ+c z$yhA=Op^0G{rEIU%J_{OlKRsx%nMf|cA8^j$w{-W#k zBNRO_nOF@U!6Un$L*qNP^6nAaMD@RQ@PKR;@O!zbbl$O*{PNsPM6>3j3U6P>UfvW$ zcrQ|S9c#EtfASiS4R7dS1BTP+oEi7HvCc14!OLar=E0@>)U|1Xv30UU?s_QdwCDw1 zNC$Dj;41j20Y=6Tt_6PlvcPMkzW5f+Q2y1UOlaRd8v;&OR*N%yE_j};AjX%}E3QC1 zh1YaE1mp&ZtsCXJ50lH8fZwi+&!oRVTigxdb8084!}dzl@Apy9w=G8Z@0t%Rz#p-@ zzc!(R)An*U4GpwYry`s2P0{s2QXZU;cwf9MHU*9~380Ek{nkN#PC@VsR;nqsvGluZ zUaVc@Ml$S_17kI6jC{V$;w#iw@Q)_0;(o53sTm98vUQyTT)0Y|O}1zQZdz(m_x@Ah zZ|<%W4K7g!!=gI)b1PSZCt3>mg!ClzMsttUBH^a=%IobmhylYYhzOF2P520 zFj80+-%ToIKGK;cIuCxC_YYG_9gmJvZXrJS=}6z#Jyi%@JYVYI-N+ACyYnNJ4ifdV z`T`Mi1nRYThZU~sp{Mqg@LRXW;_~Kx%AcQB8wM;c5{JA{(j)ab@~_?(gQ9{NfW6F{ z?q+TbpHu%LCf89f*FaG~5}hacFM=x!mim=wx6Ton|?L%nrLRep7f|o$22}`7k!)>zn*-M+K4l73AU!ps~cM3n`p)Ego-DTEf!+BKE z?hHF{Jy+9q)@0d?b4Kig&a7s>*^tbpRr$6okRJ1@zGWgO0F~4w6bRin1qdQLt$7b_jbz(N# zH_wmP^KD|Acun5DJc9}UpiCyktkt=?A{RZf_>E++C>8E_mCp9t1wivxr{N;`blR}_ z5wBA70(_Wtg1g%a8Z2H!GhVk`b-b*t_*^QDaJc(PNWt9+l=*?x&s5-k`}EK%8@39c z<}Zd`*XXzw*(p*JEp$|(a0!26Ubc|cf<*xJZE#U9M*~K+I4EW zDB$L7A%0whUbknRD52Gs{SLi>45qvU@OA$Pg{H?^hu5w!TIVbF3{<{#|7rZ}$OW|C;&Muftmn0flU3V#Y7 zt{;%}J(aR-+G*}peZN|L$~X-tjScma5NXJF;~$LOE`_Ul`DpOZ_{ zor+8io}~I}X*%5SXbLj^sWmEW*n-_^vF8sOyH0$M-$J_wTPOU^Nr?Cz!Wz82y2s=KrWg;)`Ew!J;#Euy1|y*zLjou!>uw%Jut7cxkBy9dXDM zxL!32NXx!1j;;;hrsdgcwH>)5Qp`yQb@I+)N7^?MTQ2v(c`*x-+c$LCgttA&@)Tta&MJnbIgp?VrQnxS&TG8uruTkk@GU>I1~jp$X{W`G={WPasZTK0!Rm z!q!0TfH}}ImqR$^ly~}DKU}5+MYiKd?v4Fle zq3H*K#^PP{yA^iglN*=N)~R94mc%wn)pyK5E3Xz0m+#XPLn%Q>CCZVwtL43Y-PQr zu>8zxBJ-^`_Ug42shP8eckN#-+FW-}d}6~Fwcb;jl3%*5!27OB%JfZ(aHXZ4?7ki^ zHE~(1QT8!J?FO}53^aZ~uYe!mv>GLFLrD#`Eg9#Ij!Z^w+3y9i%nP9fUb*DbX9HN< z1x#~cpb=kJ>4x1KaN?32FN@DUR8vT>t)>5D$a`ZroxI_w7j$V~uFxtnkvY37`P>swAui#NJw z6o6$67LvE%QSJY%G#Cf?De=4StwPfajBOm5jPoCwMLTkL@#m_pLl~4One?9{R2Wn) zd^uf(mgJseZp+-;F6kT5{^2-aePJ22Vlp9}*K5pgoqe1Vo%;qQb`^rYZf}_z`DsK{ zn~(BI4;Oadx?lm`rN`DUDu9j{pCDTXwy2|jz6tZ97ZLi?EZ~6Nui&&j`$ge{PIw27XX%}+2p zd@AzjcnDRdSU?Uh+ay9PQ@F#|0M4y-7F0M?!OUCnihp}?y-ws%1=dtCfSvJu&p+rB zjx=EFs^QOr!4oNv8R_@nV}? z4NfKZ8aQ%&vaoG=KaUkFQuKjEv~N|p823L4>bN&xT3_>|Ck)q+20k!z`sZcE2_MqH z1EZ?s=W#Pdrfofl{#JER^3#DXIKEJlx7-^u&i@Ez#}0w1o2^hi>Iqvjy%j&@ZLEWz+sue--^|Ay9Zi)w3$^VD(v$&E0H zD)4}73cU)hx)RPV=<5*V<-Fj8a)FuPeHzG(U8=hiJPIt$f z)$oIv>XN`um?V)*ru+hPg@|J>}A$ei5T%wMmg7^CnXk?ENVx8GEShCf;p z-X+H%L+2b~_`fW4pC|}i`g1&Mnb}RhH`i5Pb|pcw=W2j>f6q^?Jik-mVGRp<0=xp= zX0%=VSW1P+@Y)8QiSA=q^OknvQ-TQ-_c5QoGt*hP`C3EfDsq=rg30)c=p<>-+zLEv zX9$?DI!T#M^MqfFbP5JBa~WO7pCmZjP9VQ-0wQPp1Up_%mdNJkWM2GyaROOMc|OXO zu5Euy%+Yor=MGO7qOSHM{$U0@8xu<2El3y28$$83B{_)JoS?crp_V%N4$*D(IT-h5_R0K@$7ZLD!Fq6B#OB5MQe4 zV?^$r%*|YGhyw_x@ReXMiuD9(g!3paVa>~!69i+0P+{e{G{OSQcWLW`yDp<(v@{{mx70IaA z9!=D-P{Y*%vS;@dq*LX;`~f0LiLl!A8|sJ$HRGe^2#y9CDs1}faw%p#Q?s~A3sFey zM-z_U2mTnP8Z0zz5b5MPk0zuxWxM7#404iLuJ zaE1!@+hm%Cs@G8&b8Mf~-S4+#-y&Cu^!{R8;d&_3er+a_9BTzmgKz04-hW1*>2i3b zQ8zeo8;KIa9ISp#wP^TqAHOAL4wT)|gHmhm;I-;gWfG?(Ol?jkl@QRuI4Qnh91Z&= z8jW27x+ID}`Fy{`Im!YccD=(|98t~+AA*f|V`C5RNrT_xVBj@3 zY^&}d{j~&DA!33A4aDo(b=>x>IpgLk2M!&#MixFZ zgpWDIv$|hjV~NcJFg$)c-#qRkx@4eVqb-u-vzrfG5D^I?Z}S7AKHe6x0$c4 z3a&aqu5wp*=&@P%s)*8}5`OjP&!QJ|k4PH6uT!t63TJx{ZqQuEcF25vkBGtkK5oCN z03UhUhP8h=sMhqNi8Z*~i*V}=z#_+Hj$wDQ(-!V#m+W=ZpvPMIZNVuj+3EYGm;W`R zS=TuJg4Hl*W2q^)T%1eJo6EA|U@h0VjW?xU{?_!$v_iy2v0X6nOvWtBB1I;3ld;%X zN2w&K7Jb$(hpVqNY+H6v+b#96|2A`M@U>jJm zlI%=x`0oN9uuM@3Ic*aUS%)B*X}Fa3UJIr2ukjL9J^ogKgnEbGfKJ?-s-$){TIYx{ zKw8CW>E%3%2Ruq#@Gai{8rzH(3uJONxoYJ>*|qINK<>0Q_qhKy#3@gq?mEuUPqXqt z$lTNV_NA)?s}D1PL+e)}ok7bPtIxNYFY~>{iq^35#YgWk^jp0o;`lZ4%Dt`FDuo{K zcxSb6>BLtF^l!J2@1HMhEng-KOkSn$ntWOGykiJi=iLH_jTGW)>$AXZGN!Omz9qfv zRwuk%xfFEyUMIB=vXzT{+RTsGT|?KLxTPb6vGj(3wIaW6ANF36Cf}~LS0YjIL=%B5 z(51Od+ilKJUXknFljx1DmatP-&-TVp}};_t}oE}qm_8MB$N#nPPd|zfO?=;(<-*uzND5&lio#Z(QoP2K7VBl#cRTyC?GIpFmoZI^m zPA>WiI(C8*^m2{l%L8A%N|!2;na2xh)S?7f`LZLDvLFWibZaY@VQE4eJGhW7Z^wfR zRDPmn7gec_cN0bTCRY+V7UAOm7CDk+Nw-YZCt}pQ%nb(9MZkgkFhAj#1@LOr9NF+y z=3=fM6T0cWl9_wD^w#g+t{4BokUww^lme?Fq(K)o*-_dVweC*ko*YgxTJ~lObvMJC z9$b5jI`{Y#wQA=v_|1fMO3$P#sW<8-t`4g@xP#0-E@j>l)r8x)WJ1OUydq?oW?a!i za&C4ag}wJ;W77SMD)d5-!!d4%Pv%Rgdi77p&c4bhsv80ABBqMr<&L1cxgWHwXEQW) zqA@<()r&oTqgfifvrxygCyN=;-Un>{;Viv%^Qti5His&Au>@x`7^rA(BG$QSO4LuZ zQaLuBP@Q1giK-mG3@Z8aNXhr!O(|>J~SdPH|G^z zJrO0}y`759tSkY?ey0d(OF%sQzpp(fDiEGvwy8 z7(7kO8#8=lhkZU?DY~4s1vVWEW}aEd%Tnw&RUSBRKyMov8%3`wlAageM2y#&zz-{$ zgabC7Z1*TA9aDVHbi`fN`JBF(SH06CNxr8m9@+kmR=6f0gI}!4)vn&Ge#9No`>pBd z!NDEKC2J$&=5h<>>D6WUf0a3W+QA6f)oJ<`b^iQOWu zI~gRhV!tAxl~bV}$3LLa)@-J9cOrU5bVTI;nWUSR?80Bk<)SN2+Ow@!+aw&>E!v<< z(|LCyv{@H(=Yb$RShUG0&%XFqxqRN^!!Jrkf}t&fmQr7L_vOAmiOYa=y& zrJ$R)J6g1^u}N}yCWm!gDWJ~wNx5~mS3@t%?Ga+_5<++rs%m=$l(xo{i~st619g+; zQclY>q-|^<6jh|pzF7fquaXmmKRGB-j9fz28g=Xa)OM2Sd|}Y`N+0Oe#wntbOCg}! zx9|LrQ=9I^pNH^5;Ujv#J_ETrK~ux?=3Qk$=Kr4hHJof;Gg&lcT_8O2eGpMPJjT7$ zP$Ux!)37_mH>udy9gL{u81*e6T69ur4N^O*Rp>y@CuWsW#{1G(*q&;dSnA*D>}*^c{Tu zP6;(Xm_fHro59Z*?gyvMok5Pjr3)=_-vhKTy7++yv1G%yxtyXaj}O)LGhpyT=&aL0 zH9z$RCF2bOcIvS!-st~a;xX@%y8q!ESnpYvr2NZPaMV`IHLvF!hNyVT>+S~hby7BYZl)t;x8@nAxX2ZAc>f;Ny0wOX)%8v~b^Lc+ z*VPp9Sh`PKfZnGweNHSNKbW%Uc9R2lUn5kwRIT`p+5@b z*gc~S)z0?7a@yThH_na}URtaCf4F8OAa5UtNj4?irCil^w|^!VedNq&Ju!XZo1 z?$%{|ZrD{kR560S-Ucy^EqkTcUi#36dk*vVVJ!PgZl5Y)G><&re^+cdvKnn!vI=dS zn20T|{y{x<*e@+b3B1g>q?69#&bOHr8Cc|HJx8Ug}0m!8+IhJ%)9FmTg6a(Zd0@LAN3{xlupMwMw{2=k==xh+!6Zf}HLzcwLziYBma zFCXv=7yhJM^EmGM>P)Egu8q1u=6Kp5K?g3KsU>_dnFmbLFhC0vgTMfLN8PHrM~3@8 zSz(V`tQXezfeX3^P_gZvX!ZIA4X9BexJBYVr!m#(9 zr=pxcdw@+LGgNX$<_k?P*YULc7pYh=Pu|f|j!4N4LH)w5p?)e^srt|vbhX(F?7a0` zxwB-wyzRxuVxx@`-YYs4-u6ZvPWkwhoVHJ$(!1EKTM2e<4 zZr5K@kih*82En(fKiP_Jc1*_V{mNHvwhM0+x{y!L&yuG0cSQk*ROxh^0ex$oJ7`Lz z9(Xu-zVuAZe{i(Bml(aMCU;XEs0O1y*?@1PRNVtTUja32U?l zB8yD5SYMxw&@7`uICB3B%1_f#Lb{#cFa1bWI zFzJOHc{&UBXxtBs#`PjqP&T^sTQqB`wnJieqEW}aHPZ0Wlv1MXz&&COFWd866HX@! z0wN*h)v7+bi{~X2K)nA-Q1Ox()aOtLNjiLphcPZf-$f_(_=!!}>;vm*-;NdJ*y%Y! z!qqg+z2PVHrfwbg`EQ(VLFGK+akeA*Wzt4AYuPE8gEo^}*;N8rN-WTtQ-`F-4D_LU zZ<6_nVRORVIgdR2H;Oj(b5yc5W?pD^hsp!$p|gx=3mv*2>43~4@f8zHkA0luaY>OnN|R~VxB(6 z872Y;&aA-}z3`Q-X~zn`-c1McODg!|h_Jru zBRK_IroFcWTnfbcnJKJ5TR_lp)lmmsQxyy#BW!r>=hl?bYD{KUn|JSb*1empsXC|sAOR2!! z-1P}GST@Z;mXUH}omKt-+mEFxcNl&KRc3ezKWrX|E@+l2Z6dbO2X14$vWpvQ;Cn$Z zflHxYODS{z*(F{cbJ?mT;+ zlZ+5ag_6~>8papWj)o`|S!Gr>ZB$yKL@Kmol!nB2|Aza!?|a_$Jb8ce)CQUsVf)^f zGwrUw(dEnSV4kEdPL)Z=&z&^q&FYL~@v$Gg`?ir1`lvm1A$6RM89O1b&}NByCFf}T zoj9Ur`YoL(jd!9u!u`N_aUWF?Ws4aEwNa6WdsOwK3q_M}ugG^6UDi0}ZyJprWVv zhu%Pb$H)ureB(Z7AY&Ev|MZ2 z6zTFufzI(LMAxm%gN9Yr#Xh21e9^M$vMo>73s=pv2X5JCYCPIJ9s8`XfzY=8%PR29 z`Hhmi^AshuR}N($=TFY%>CY}`=gX**J z!gC{P>p^EIx$P>Z?2^J&++9X?m8lXJ$}~t6-SJg-q=C!`1${o4#d<>r*c0zu$u{qeY?UelmKDhHp6Mr} zK01r=l42F+jSRtc?zznTejh?b9grvX9|#ij)(;VG)tSWcE73rpnYDJ3#PvJ7_XBt= zX}!>Yy9sXj%bog{yM%HbtVLcg*N1IIx9Pk1H1X_Q$sXg5ZS-tvrR+T|Inln2D0j^F zF>11gVx#C1y+Lhz?03Nr^h3UZW}5GNcD+(?)D}2RzKu2&`$2Lmb&Ja@O-7+W50ky0~?v@>eEQ`hr4hEu!$fz3v&+Sj_0Q2pzmohMBOnh@>viynNap6iNA01;2KR8uYxt)cjZrzicVZ4OWKdkgX${i198Y&m{md^ckP zDzG=6UxhaMP8FWrc`|10vZvWRo&M=y{ z`W?CmoY)+sJzB}xEtb}T^u z^hp+W4!R>xzZ{ghv%5ptIp~*ShTMEro%SAV>%B|(I~OJ9W{II*RR#pqA6%!tvSc+T zJNbY~sRTfe&^*Q%O%lC1=*YR)*&|QQ{Is+tUeiUcJNes^&%zZmBDgIFzY(d3u+JEjW&wZ|3G zE|TNWn&k|PtpGPua=gx!Fb@(0IIb2l z*A3j)pPWI~&h_LrIR>%^|Flr+V&2o!Q+{YW&vp|(p4dk%xYiO#b2B)sWkktF)XVNPxhSrlHjP=IuYf=OHYP9ivx2_vbeNxS zm#cI<8(;_7v)HrVDBMBS46Lk@cK-!wv-a6>?1|fTxcJ08MkhB~6fc`(e=D6#le0MOdI338Whz zl8uoG!{?f4i+6?9A-l6(G2JEILOwAEmNp=XccJGAfsr~GV(7tLzqnFVq#M9L;yDen zUj9pb{B#HYVD2SMW{IEBbjMq=>)CqFX~i-Q-EYkvoOuS?Uh`c(|BjO=Jtjmm$^utk zESsi&Acz5?U&x3aovh%wy}8bkb}n>S`d*b4v&FQ-!%axWXfP9Zn?|=lqwpr`j?6W? zKxo*08x2J*EoY%siI$sOU+!9Lm2w$;(-V_NJbW*6*{2}y2 zYZGrNuthj6^#d{Ea0d7@tR60uT95EFl|(Xa&BUR@^O48*24%D1SHMI}x!gw}QtVLt ziGQo`Dn4%3OU`$+mtEtQN~kv7RFyM4gY=(Lp>Li?MA1GQ@Q3~yf}2Cnkkn1DVV-Y@ z;;aTSv$k?Sw--9X($&i}?9^u>`)wbQ{{?32wf-Cz#16UwJGw83kN0R2`O?k|CUN1s z?>hz7x`_$;+B+~ODin}aC1Hx=t#afu6Jc%3M$i`C4Egpri->kr=16TX`13Y{j(H^tdz|8ku+RWpVe=j`+_Ihj zVrc_^mt!AU+z0^^8LPo(--HT3{)DJr(toF?S{H)xzGc#dOJ5Q>uWWJqw`ufbf389- zwT7)sJdSN2T1$6=>(JOEYp^Q{<4l3yUMj00RdlLP0IW-QAkA<~?xCNjQc|in7!&I! zaCKkZu?vLoTp#k{FF|+Z;|mGzlV@Yjt4o*HzGrn^mee ziZ@YNIrWs)eGj_)>Sp}LMs;<&DHF{7?mXBdm|(j-m*);&|cw;(P zGV23z=}EFmSkrOFx2qgYJ~0bdeJ0P;eEcWS7uONSD|!i~sTkgu?pwM_on?G=`*>|_ zVJ^LRUK!=PYLIuQ?E^iOC!!LjmJl-cd#SUJ{^0$+LhwNv&KyRvkOR8*;>x>Ws)x4k z;QNf{7dr!co z%qbW62z`x6@T%7=9h1mejE$ni*CxuvBLG* zr?o5C6Q+YWacVlWH@_cL%ozm+LRG~c`6GfCmR~_rbT{1H^a9LYIEpF!XT|r`dKb`Xv3DhVqf^#dV&}vdjB-9mSp|!^>XRw* zLD$O!1?`8qcK#@LLvEQYUr7;+cQX;3)05WSBbzTAx1I-|Noj-LAJP;JJhLEAF4X}4 z;>Sf>Yepmtr8v=4bq3yG`k6rVhhW)~S8@wx+wj#F{l`E2zJ|9Vb4=JU8H^r0&xxzo z7I1FML{dgQ=7?2;NdD!{?ey-u!NBg{6NK!-l|cMH4E^Puu9D*1BNc{xg6`%Qh>wQf z09E2>dWpwX#g!rT;2j59wT{*<(P*3pTL^#6W^2_E1lqlxd-7ayaK4Z8zFMqdxLSFXDL42-iS9eRnr@+m-GErEY`?A z&MC>1tGQDN59tqZ2zes)B&3O7P`}Bu!>bKROJ6wW{C#7aQ3rd39G$02OgyoDNjQK=tihWL0xPm zb?LFmDN?G-rekuKj_YLyB-^dNrmg42x=1D(y%~5q5+W=~v`N~J~=P~|- z4`b0jr7)?rgxZ$BhRxLsWlvPiWt|;n2`VhCiP*cLT8~!c0uE92BG7z>VX=*Q1~9wSiyMm)HB8hhvRIKzL|%*lUWP8sjQ zS<)v0vtRO?^67Wv-hFA{WA48N@B3JhbM_hjf0f6$N4B{n@wN#FPkJhN(C8zkwfCug zKHf)Mcp%xU+Ebv|o81EJTYO(Ses!jJDj*+E&h_A&jRT>0+MM1~7EJpGJj2^Bm9q;R zcOXIDPq~Jy2?!sM2XIoxeRIDC1h?u{B(N+K6U)cakq zif4;|5PAC9d{FN@ zsqF>hNw0p$#Q!4TSAK6<*B&-%ey1gM|Qm0_ z@G16$@n=rs=<(Y$1aE7FSsOv8sXJ+Qco%Rob`CkYe+6NDbT@qVgFCv& z#!oi7ccb9)ylDtL>kWQeu^#!TeL*DkaD_;xCthxl_Xew@PGUd%5Zd+QB_U>ZM+HlY zkl9oz2YO+;;u^Ipz-F~({sU((=35v|9J|OTA7f`^-}UCp&#jn+^nOsljor^HI1>R% z@#jH&&vYY+3U)OYc}-GV@1uk~_iXayDgtwH8(~;1l8~uQA)nmbhK1Xlf#gOGA(ea2 z>dpJQ9xMgE=vZ6@$V-2sL?vf)_bx2;X6Et&^4p3ncG_VO*oIc_Xw zA1?P679gL&n_73p!JiFiYrY2Rv38K$-T#tQmgKtUNBtBV*3A^HIvzx9HrdN<)6C{( z`3?!&!>pjp$CGTa=??AHmZ#vGTimH9k#0otY*XON$IH|T$u8H^3LXB&RjcqIYOTO< z&MAJS+c~5%$4q$OM=%y*l1wX?nR11zGAXsbX5s*21T8f=zCUN5%uL67Y_Rtbj$b(qe6?5y@fWus1vR5wp3_Wn@?jd3s}u*kx>HIAJr^?T ziVffg?rQES=VVk?a}uvcjTdQKvQ1%pp(eQx<%^zbRbmH*O}WLVtLc39KBMMeN--jR zux%;?Tl8Wk-@I2O67I^O`=o@FPw0NuMQTu$$Ndq8bd|uL?3?lFjiCJB>)w=h)(*C7 z>ppRPFiQ9HJ?O+WpAaZ2Of;bLf~V@HDQ*X9k%$&^g-%fbozbS_zHT>j@=&aO>VJ+czNe}-LUmsdG;7g`6t-PQ`Yq4jk zszhx1ULv4xRQ&6{40*`DiN2L>Ov+zZLrL)uImRrM)5&QTBfoEOBeL3Pq0J8^n^~Z) z^?Y+`Q?wB^6mp7u`FEkF>kS@YnC?gQX@0<7UhgMfwpn38^P|vs*DuWM3ov5cx{TVj zVFh58bPn4W@mY0-@)064>Y9$hVMSH&o*Q<(>IjfF?-5Y7!3{VUQ=-`qUSu3*W{P@( zi}?4nk9VEXff}4#iHBd+1%LC?fE8~p(|Fz}`E~0a<_U5gas8l8 zK2MyB7g}0kZddIQ;<*6=pHSzA?f#8@_{Za?y#B@*$GEYzQyiDRKNr|lqrgS&X+iFF zAl#cFJFTBPUShX-hVFYBq?nLN97_#SrFBoK;Z}RY`PifFu!+JhW~No4dU?_TcFEyR zGW^R^bsbe-?%J7`$h+hYVjuzKET$O>X5H4NwGPSXKJb4Cji@f*>-{=RKZst*g_W6+ zQu(&rSi~uz>>CmGR5F(`T$G6YR(>g+FL~=3w-Z2jlOV8nQ3rG@x(1Q#vVwwo17u|U z0{BgDJ@~+8F@NApK6Y-(3VHD40Ase{he+LI9_xfAiaZOyL7D$uMc8$lg?D8(sE4Pi zQ_uhH5dJstj_Z1&LF_OD!S|J`xa|^$@t?w(+^7Fa`9ZfknMaAH+>E%Ja9>g*QWRiJ zz7>a%O-(jXa_Cn4#;!49s$zgQ+?6ZVd4GqJnO4u+wSytw#R*79gAktF{9kI<4gU!L zZfW3;mlfeN6Z*vqn@!eP& zi1$oKEAGd!)7L$u(rO=IZhn|BH|G@Ud~%Aaw{5{*%E*Fg=T^$EzS<%h^*by~zQ&{C zo1Q2c3AK^^TXcjU&mLs6Z{ti+yPeuOUq{5DVlUqyK;nl_TrRRevY1J&+wkA_9XcaB zBIr)?DCfG&oVW|-BTGzYD|qdi4S2MvOX<+7*cq-a1g_}^9ei!cjqV>rZ(0AM&R@}B zEib5n3MpY!_qAZoHvc1vhJB_j9WJucRK3vLzKD4bo+jU2lEZ#}yNO&_HL9*6$$Q$% zW!<}8&g6Y8PLWOM{V4mI=|Gnx77PA7vJ?jE#Z&exJj7XVoRq^SZ=qMhuVJj-kTjqn zC+>}{<@c(t;!@Pw$ZNaP>9<8byjQl5`L1pNPkQ_d{8q4nAM@}HSS4r2n=h&mM9W-f z+>0!sb6(a=FEQ<^+%)of}MRBfsfvmg2 z3EYo)j+gu?fL+xUWj5;`WGiMRQ5_k(i1ezZ*!i=L^7O`=!XGI$0{+&|AgKBSbaP+}wpLN!ZXAf|K)@HW zd-3H%Q&hJ>8M4NzkA0rm0^{$Wvsuf!c{}W#`3KUB#IpmZAw9T0R{B~bOt?BnwOpWv z`L7-(1^1+YeNW`+oVzQj{Ba1A8`{s$5xes2z({U^&lgeaS{;>(3HJ2I@(MNHT`Ay( z=T+wNa5Tx=*s2r~ASL=@;Ey#>*e%Ii+PH zK6L})XHj)(_v}t%3BQBMgy;ZXbHQwU;p?;5x|_cNz0F#}(HqxvlO2D^$yj_7eh>8} zW>ndkIk= zbU{P$Ww(Vy!j|WlN5E-OpSei*=iE2JI8b}K5hT231~LC!tHx_nBUaS2`im-ST?7K$V*7!k2uYu)Ez1bsrnppw%Ah;q#ml2 zGVDq(_s;-zy4E6EX7+5tsboyuy8-U&kkya#T*EZw&&7V93Xt6s`h%P9qt5Kq*sa)+ zwo|@@T}{im>{34vpN5CzE6}bUrLq>fYEZlF5LIH|M%7&z0faj@^Y?@v0H6KV!relf7rWGP9QSnN+I`vrC_h#NsZGxV)&4}}O){R7Ku7?xC zk0N{C+Q}Wg(TR(X5pv%!Lnuz*FsJ zXi@xiaf2`cK|582GIPFgZDo6b!C_PJ9kW?b!p|na;e{hKsrpph-)+lonhKIOUus2H zOgKTG;zNvS{1IRg{U&Ib!aeNBo*l zb83(ls2^GFhjrR4KxVC<0=~-{5@Y{PgTc^rNKPeD*{bsxX-;l~ghzC#hEJ(X#%4Qe zrg1lXMB%YwM!_3AL|c)%0eqAF^w&Wg`LvCfd$f~yu^m-9c)7G4#s8?Y4TcNKw_ zhZ{Mg!K0#90|7bSwpNG`5ya_RLM}JwF%|CgjXN2!n1^l-llf0Klx#m1tEkem3f}S5 zi#;4Kub6s#1!WZTlj6S|fsKk2w8{hG7_8>HoQcys*h;q=kb5vo<7N6{Li=$ueEurS zPfB%Xnmx-X_k}~soA{q-ABjul?S>!d!q7abS56M?HW4zX)1J!c6w8ovCX>Ng@*>gl zrN{ZMe|&_Ow6np(((b}gwH?~)eg}%n3^Dl2;6GqZ))*T6YA&E~JCkU5Dl5Isb(+i# zuNC5q+yAKMFP6&tKX}s{H5^q9j7?S2)j;f?*K%zM^-^0l{Vg(@;vk6pCgJKWN=1}1 zUSa#~3$cw?JK4iublK4JNuq(ojm+A#3K@-g`+4|V9(F44HKOUa6_+XDiBu&y@#>;H zdbL?K(Gnn|&KnWwiZ7I-PIF)5g#B)^^~5xiT|UOm?>mdHFVSZs&iBD=WgDsXk3yi6 zo&xnV%b1^^iqWF>mrN4xA#{RfK&Pdy*i)lGtmvm3>9^wyI8W6;yiwm&LE(2BuS+uX z{W5I!x8NkX!tSG>nQwHya!wj^vy`I~}e;D=#&pU&HhTHyuRGo77ZxTW}kw ze&`7n9BTpU8ykTy|5IRB4D4i5_8w%Cr>V;WhW3o>r{#=_&l7={Pap|Ji+zsVQ=^tv3UqW$c$<_(=p65P7|%EpID2FUQ@-miPaNlkPrJ7Tfd#JYtqw!% z>Q+@&r&w8IzyA+B491lb!t=nn>;3UXLp^eAcLkUJst;+{Y)KzUHV6AZ?GnmXYod4U zr{jL^4*YX{3-LElkOF768GANIEV`Km5M$;l%=&h|9Cc_P{a8|0em`zY1G8#a52s3Q z0j`JK_H<V5Itk~g>1g) zE&f?ri_Y%xBILiqRI`mME*l@xv zu7Ped*1;UlF2mo4Si(WO-|=FbP6%Cc#)TK=A7nFr#sYtf4={&fY_ZZHAF&TRgO3C} z)BW?cP3NY^d0yA+#Q=Hu70zVc5tK~iK%RG62pj1v;NXo^Oo2#8U^hoSm7aTK^y(ja zDO>)dj~1+kp~+hOlX)(BYtF zi7%Djc=?hEoBRYfYBv&dr{)Ps| zD&n4nMx#=4jr=DGb@bbNOEi}iR^#zre{orlWAwjeEY^`0h#R^;r)N+1=A633c)8Z3 zWFU?5?sZ+l`;>2Lf=lJWx6k)+jW5OpYnvYvmSYFVH1!E_KB5FvM}LNXtUn^YE}l0h|kMMnazRIw7s=dl;;kA*8J(Bz*a?I zlyaaEZ%fct^hn(nR`9cipB3H8lz{PEQGk?`IQ*}=T9*mGAuk2;+v&jbZ0Ga$JQd-# zjfLF0@s;$-kVV|#HexjO0$IEmBq5JDs#QFM-j6I1&s?<>uR3~xSbeP*3O-mT zK$flsL~jAKJAE!%u}&l&sFx!e@5HL#jA((qm+TcGQ9V$Q&l-W6BsP0ze@2r{gra3x zPq>bz5@!B3Z{em*?nu)fCpGAF9Ru1-6MswhX5$D8*5`yZXj)B>i)5DIUN=rb_m7Q} z?#>c9=FtE=SguzvZh4OTxWY?ovTPB%wraUBVPgX{J&O?)WZHmdn(hh`UyqAVwQZ1j zz=Ies+jHE)9fZjLgf%v>)qr~cI~U$A{s48Ie$7+Q66rMbR{@(Ie-TGO!45;$PfXL!CXH1y|pE1NSxtV0)gNmH1(!XiH~zsO7(7 z*xI_4;%{-@#ID0>v|7J3uI1Q-O}#KyE31HT7c!qcC-(pwwMr7DHP;CY>NW|vjURxc zM;)OzzXB+`!~j9*qix_{Y%S@TE!m|meZ?p`pMyT>pGXMhLp@$YE;r(F!^*c`3C6oD$Bwxk046Q^b`e2O_F2 z+U{kBqvFTs>-ojUMsV(18<4v56?HTr2!8)xyHf0?Y>3mj%ATFj7Yy7$$@c-cJQcTuG<@_ z{N@|XgC0M@o(yTJW2qEqqqUQrpJFL+GtJ=Q{!09cr#*$^x&c~YF5QsKD+R{O>=2Of z#2zOSmf?@CG?62IJYsZqm*AGp3wp+`Qb9x54&K@-TlSNGykvG?Ab)VS3W zUf$PnYVG_pjO#&CvGTAJozoG`e!AW#Jmw@tUbwlKw`ORb;JaWYaSfq}_(!2Zx z3ofLAC;Cn32yIj1iKYU*D@0dpzO!3;oIB3kIblWq5fzAQ_Gp5Z_unB}p&FopbOScy zc?s{z$IGNF*dub0?-fr!=D<7h>o3_FnLvPTi+KMMw)1^n9R-pS1=Lr83ZWMF1=_Vo zT7O2vF0Rz+A^N>Mh5GO-73;OckX$GdJRw_SV9Hqkkd-p_R0;%|YdO5#&}Ts0|J?6{Klw>?da z`D_!ntn46KchAM<8-)s(SE=~0QXiGQH=LgC_rKPf$cqM;_d4F0DU& zjN8wbkqt~)j2ivdpq!O*g@;b=XMb`yCpHeos69w3b`ekKrn)&AC z?7X4H)bjlon4s-vY47>9)RpCb*@nEe;{SFhW8Z{wz`a)>P!=xF)`@&5bxA(R4NVux z69vMYpY5U(?@c)QEHyw*%NK&^VO;y#EXn)*Mv*ytyCscYQ)^TB5>r7o3KGC2!T3 zlmvQ1b2{_-gs)QG@eqLi;*5#HW>6E$v_R?88p2THB4qruKm6`qA{APHTPV+NrZ@co zRqFghwCgpRa8+Cpf9^pd1rlnd^VU>&&0$aQ#vg!84*H9?d{l7%W%+~5JF*Kd@d$*0 zDp=%MJ0>-L;-WbHK#W4~>xH87hU0*I1Ia{GyJMsBd&tMt;Z)iySQvh94RN!^8QW*D zoM}0ni$rECqNuAHXVc;&IPLRSxaLth*h{1-dOqyYd9ch8to69hjW?7l>8^T&W$HPI zhI~Y5nuUSZdbE_=e5Q_VRDT6efAo^OaN1H>+f)LXdD4LQ2X~FF`3<<(CY5(&;wI|4 z%^Wn2v!MDu1+axFztHxh%SE*fp6pzQ0@Z!{+C<-}R60JUgXf~XLuEO$i+^n3HTg37 zJs4wYjl{98ct*|;Dew1MQs;X@bIJ-~+qPTu*GI2-7}Kix@w+qkIUg5Yd#%sEs~Sf) z5HVy++y?nS>k@Rcub&f{dVJvQGk1v#A}cX%#b)5IO(-}m_Z^D&@p-%7_3*6MFJoPn zIN`bnwQMO7L^5N|ph&={f-li*Bb#F(J95eI0;i`lgICkHYl`7Q5m#22>X;v((LPlnLK zIW=_Cb6@qGvUV_Z_c!_JiEC*3KsNm6`#LTYHYGQdt>aq14xwwj>oJdlNbaE1AGzR? zaMbpf7!1{}VHV^YGu>YMMfz>(+@mO4?XRbVsIz&Bputs#xirmQu=arzkYd&grj2;R zi;#!Z^bQm7_2*B7;+mE0MuW%NT3Urd3|mR>u=^>pZjqSrqVEfydCN*>sb49xfLoO8 zRe*Zw5y6P(qyZ~AMkbj_!l!$#62$BDg3Lq;d^2%2vTBVnuLabUU;bND{;bg!e$?gN z#O1~)<)he4;ziy|LZj3`de_aj&%c|6n@J8urW_|qluuCgXWruBeEiiYk_59wl@{$KLs z+k9+%$4z?ffgZSGSS+>Zh=^DT4`_AZW!TFlvFuHz8)kH#u!%+&k;ipjbk|5c`~2B^ zUU-}pF+*zy{K(P>l1^VlygU6yHSA#pa&_q!p@v;QrMhn=IJhGiIg$TR@H6-k*5Z3x z^tHSj8kqb5Jun}j&ib{(L+;ze`tVn@O~IHx?`ci3ne+JyBVUlqkJ@xP0=E)d2UbId z4{X`Jzo%$%Uk1IRDHvPV3DHWzT5>9CKLvl6V&di~(#5lRz}kBWT=~vs`N~QwdiC=T zSgYbIb+B}l?AIL>hUqT=KBlQCw1h@7*-AlBab+1W_45*JTv5qQ7W5$tq89MVREC6o zdRpjj-v>O#GJq*ndhL{uO!Jo(v5*M2c6Ib_qhgVl} z+=rK8NbmeDI6P>FuU$KW^qVfxJr}%GoxLq0)oK#x+gGZ9jfGXbgqg_?@X>~ygr|MkcPO&>7ym^1H1M;Ctez7t`0CXw??y9jF^`YX7t z5vF%xq5_G&V<}cR6oZ|!SjxTL*29d>oemw_se=J>r0C4!3pAxv$IsV3BA!<3sdo0{ zW}<4=PJY&d7jSIJ6|T1Cy!5zWA9#%akxt690J|mJV1*;`__y>du~f%Rsb4y;_;Ri9 zA&bIlB>L@T`uAWkE*lL3H=sd$*`;mlwS7PFx3*!_-WX@%k(G|3&z3r1>*kr@*rls* z!=>w(#`Zj7lk`RA$Y}y3&UMoc%cr=r@g{V_i3WO_>J@MU>$vL__;tv17-eB3JIc?Y|uR()u$`%gm z^=TP+Rif9Y03iFN8Ejh6PQEFULUl*hwi;JlOhfHW~;(RXm%uMa; z+egVI>tOP>{Xfh)vsUlXbz@`!@mJ=_i$1DS*Na{$SWN~)O5*TtBca0Qcvk$h6&ap< zqEVYSiH;v!f~QDl!Q~htTzym%oOe)PyklRo(AKSney?@{9b9aLtgp#MPRjWSAAix| z)GZAKd`khUzke-vMh`}R)$!m3ZnI>Z8$aUGYh#$4Fl}|Em-(2P>~?;(+IjGSXA7+w z^B;auY5{ImBoAWO|APvas7!ogFwgNUt;A*##(z{S3or*Z3daPHJ1{Gi-Pe#y!0 zT(y%4&(!S*7PwM_SIJL~f$%&*wJ3@V=S8ks$c>jaYA<>0j$W!fj})tS z5khF1mc{((Y-{aC1l@8AJhShr?54JIL#V0E-!y5nf@gfQj0kH~y(BIJuh-1s8@aLH+bjN}3X!Xkl9Cm#v z_`N?;M>fJ9j%p7@{jb-9x&v2)jn7X3@?f$M`L|!RXzmg1Fxh*=i6|cNUtJCGufK|z zdBjx|IBN;?*YOp3ekzXWGEsE@6!?-n9X83x%XH&Alm|&YbU$d&h6Q9!f;Q|`MM&wBAs3F~cQra1=zi7B>3bK(S=`^bTMChyB?|H{y- zd-GEGRl*OC({3a}Rz=7u2v0DEyJPXAnXi>Q1gN~-+%0?s**_~K{fWKUfeyz+M@UgA)J1xR}EuIq<5<-J>^V(wgoeU0|;Jull3Px-I;yGFGH zx_%x?fwp(WV{AC;@-kg%-l=?O&DJR~@#Y3F_Dr3KiEYI$LOgWA^tt5io^%>o5k+*= z6{=>*#fj{BL(0?aloWi{o)YbI_k`x&|A)-nyAG5;X^#np$C>P3+hD0hIrspjPS579 z3vEyA(RPJ$#eCaFENo`6U{mW2cBgC_3}3R7o_d#rf)Ukdbd01KD4m5Vl0{_RK7Z`n zi7Q~)$^pvy$w4rwup4}E+yvwsPtegT7`b3!Ie$r&P;}_!6{L3mD}4Hri-Z_nCMO#4fid059r@FF?uoE2C(M>^6^SJ`CeeizX z!!HrKSvN+gTS^&3)Iv{oa3!aCI&ZrGUpg17@d_6tQO8APgYv|%Fb4R`rQ$Y%QuelK zIRCEpgkW$u4b#tm!04@2bDvkFBd=yU4n9q(MCGkR$Y&uBQMGTEnb2P#Ixx#h{BUL$ z!WDc+9lhr9>vqO46BdO6o7sgBpW6*@zdxV9wps~&bvXu@_~VRU`C5kG&QRoa+y0?1 zOBtXE`(wziv!Tr2f;^@ZSHdpC#>(efSIQ?SB@51LpVjkl&~pa@x3PL^`E-o6JX8A3 z2yt7zP>gOqf~5@%b8_wHF~=}Oum6ptH0&>6dx5k8G8*WC^|AtigT^oxK+qNmW ze7#WmgZ4X||JF!ldyOgdXGNugB)3aX7q~D-FB&lWqh})D{%CS(&VR-Bl>xd?s-9w` z);&#^E-kPd_^dSN+-cmBoy%AGuTp^C3WN)jzNzyS4BQp=er5#WuXx`FPjNwe7xEhB zozQx2lmvgQT1oW+e^IHUi@5%f)#Q`PFci_MXYuR-MNv~F@MVRfdt2Tqwe-1{k?)C* zbfT8+rJv6@&a@k4P#>_1!gHp@K*&igKr<{t{czLQPzlMXjCjANK6+QPf6){~TQU z34SmWRXaJLjG6mZXfEF4Mh4GS0QXLstJoLnfqpaRX%Cy^QCcVU-4oyjQQ3hX)HCrq zk?XP-IM{VdPOP<<2-5JxxYxskl%6#g7n#Qz(&gfH8b^@K(s%NQ6lH*nIWsiBzCqE2 ze_umpeY5o7{%GDWqcM6F*a=tJ@0WWVB`3-p>!Ldnk81i?^SI9LCV(x7;PuytMRy)u zXZFr}&n`0;1MJyMDydtMqaO$1b!qeX5iU=t#6uy3iK2$!tE(aCA4kE%g1y|mX>A&@ zFN;abo)Eyr|12o8=a|B77f8t!QRY5vyF!0zIgY8{@xmepguLmGv!IB!x5C2OH#`f| z+o(%Wz2*gkLi~Pjnx436Ux1|C%>;q` zYQ6cQv{5&}ZnG(0$0Js`Tx14|2Ry_NJBHA!l0CWMvhU*RSx$uNmi3%4y%pEJu%D{h z@R=^p^}}YJZX_QM9N41O zpGRuDsCEc8DcE><%WG|#2AzwWl({?@ggx3bOPKp*3nJyMf__}2Ln<`SRQK1OLO%Ge zCKH}&(w{Fy0u!OH_}_~9s5^iE$f>>=7OYoz$6Sl7q;FQ{Kn2VG0e{>RSnJj9rNa3A0HF@-5Ak}yS|S*W+TGRX6o4*0^tnTXAGR73md16ovDkKOa! zD3(&|z)4k49zWT-p+#C$Q&d9zxS6q%#%tO` z*#Ea$_uq#G{J3QV*k1xGJdl>h45n-p!}hZ5l)ayzRPc=Y1udiFR3vf!-xYg#DouQLccS|s545dFMWai9E;noQB>Pi74gC3I8*^!yiQr$(Z?xdqJ05j`Pb8t~sfY0WAMA+- z4ywH9eswU)AX*{i_B-mQ^mgRGiM>F+wK2MFK>%fKsVdft_vdVq{NUy_aYS3Vl{WBYM22szG?9ZEb2|)MSx1x1sgz#S4 zebK+aUnC!?QV~lFq?j)jJkfWGAcxceo2!; z8U6eX=e*A6e811}y{j--9`#VAA*JY_}WOoWKE&L*W0RU+D z<`yxvw{Fso$wO-H!LPAUdJuR$C{lMw3xI-sEOGCDi?{_3%wQS2x#;foQJlS>o>EEj zPB^{IQS^F#q2$>iMdc|^4``X&GahB%wIO2-DK{eWn+1QG1s=?Ow_L3JyA2F+@8d+!mv(%_wp2+!?0=~vx5s0bYA$-1n3zU<3N~k^m6sGqX;jaTU zx%9apZt2So1nzu--3$A`uDJ4$GXK>_eF+m%2jeWr&WEMKPN_2Z$q^2DXqMAVjNM>{ z#vy#*awIYSqC`zCX4Zw9W`z&h%CH|?u7VD?eTDSIh`@Tq874rUR7yUUE7~%@M-Xs4 z9eyz6$x`umXea(xDCe{r`dn@S1}bepweQx^1NqTZXS^|)Q7?f#{Wr@8TQ5N_z8}Z? z^lTH|$fgL0)hWE@oJKz9;&w1nFOgqlR!`m7y^6hg@~Om$YX$UzPd@a}F9f!T8%EZB zR^%U%5o4|Om&yHvDn*feAqk#44%qTHv#&RJGAFCl@n?@pgco#^c@;DM0tk$tGuEc=`>*^!EJA(=jy`;g%IRkF_iA+!eAAV@r)j4|$2t2%vie>iFW?8;c+m)oMU3f% zeX-p9N282Qo&rTNP7Je{&o8up%bh4g!1G%aL{r0q@XU1`>sT;c;CW8R6TpmfPgO>z@#q# zR(yO)?EFDjMB#oh{qCp{ZY^}gA}(kNLyPCJJBX9i3{MmNFtZJAt9?jd{eHd93ktu5DS~1H#|6^bF?!hgM z_6VE9{OK>)DUH2}h=hXrO+nDiF6d%54Lfkgx%9|A*11<78X9in4~&>A%a0!ANz`{T zHXnz9m%1OJs=u>NLJJL$6SPQRd?o;b*CJr8e;u|hewnaYcRiTWqRmxSH{yiO-44k& zDO0VCxV*gSN#2RNx$uOyh#dZ@3`V}_X8$PMBKsd68W~KA@|-gGW1|1K$mr67hmWRHf#Wu&-ur%9$!SC zG0hWf?u_PhcK?9=h+ba6@!8pZlrM<8$Iyo+|3SmOAg{IT0`2xq4vGhYh^RI4Y*mM{ zs7SAaTXJw*w0PzL-ZdtH^~$d1ufI73{ky=2%ioNU?_}%vG0{Ihz&?xl4x#Twu0 z!U6K{I*CWDbm3L--@sP0J?~S|aXfPMUo>z2Eo6MYCc)EuFYUg4qre{Uk=w5fp2~qH?nf$%O8&G9OQr(I zvl5RaFBFM^K2BA#HJ>j4VskBpAKSyxCquo!+NTZ30r0Z$<+qiTz4szft%Hu(x7vf8 z6OVN`^G-FO=Z^ERros_b_tI26i^>55hbNRwKqH!a&lYEoCfbTZ3Z-QEEW4Re;_TFL$FnVnMSYoMry;CGDdwiN1Ep>67lo5$gV3L zky$hoM19S^N+$}V8Lxwjps2@|cu>MY;7GS6=^3(bf($#>P<)aT|x*QQ<#(>q>F zYW9d?6Z;6hYeukdt9B?;7XwMWWop(}ld8D2Qb4@@(2T0|jVK3OCJFLo-x6`xYWZ8{ zJc5=<9mii3XDG;=zk{G9is(yfhOYk?jaeL@Ae#)kNVj1SFD10$tho#6>_ewzjYrm_ z-!fkjUzfP4f7;cCICuRcdc^hNwLNnLpU>xDE4#Ge$SodFZ;v9I$P{xOX5A9aiw97y z%uT_lthwZrJZ$5 zAde;CR09%)3M2=B|4yHQmF?6K_k+v%U%k|zTl+E8R6HIi3>70$MpGncU7(Q-dx#fB z?c;9==oBf)>TrZ(n{nJu71ND6iEa5V1dZZ0$y~ddCwknpPR#GzNhNyIUY@nW3ITgI zkQsU158hoFg0+Q&NFPgJVBM)F3|F0X`%-rHBf?yDi3sjv2m?hZl)FS+L& z%bETB?o&*rlW&U1_u&zIZE769>qHHf z7$8twTUE!m{@2G?>W@N~PtNYOw7g|#>1ym)qKGVxzb;z;ITL&KRf^^R@Mn}ao)T|y z>k+qJgkxcnzwtSB{UR0LOu^1`7qI?v2|P3GA(3+GF}LUviHH=n@Cl!8Xe`|YuW1ki zx$^UbsTJ9Rh=-+ok!vGapRS_85BCMn-$_M)w0hpLM+|E-nTE)hNGNXFItb=j7Qj`( zo0%>JX}&q}0)v)jlbMfUe(WRYN+dxJg_|317j4qtn|@pO>Xg+ zAUIByccFU}`7=699X?g7xT5hFB5^pGY3MU$daB#_MoFHCgQ!*W^=>ZxDV`<__R1k; z4$pa^Hf`j*S4(+cz8uGHG?mbEZyeY1*f2?78*<<^BpJXejUqu=|0MI#N|!r^>VpZD zmsEE~Mer7v)#JY6dbDHB61gM!FW`@s6;QtKGkn0sTHs{24DY#@&5z!Oq6xr9ezo2X z+21aqNa^+w*wtX1x#U!aXwS}T2DJ}Sr=Kq6Mf&&%Vs1wwy+_kgmt*R*jFubq_*Xkx zJ-cU8&o!V1c5a|U=7`uzdkeWc`%~-*(~s0VJsY!@`OuTllZ~g zC19Ra9-L$Hj%)ARN7`Rq!n_aXA%QqWg=wVludg^jshAjZ6O2&N3+p)wmtaNL6?5aM zZEqy7k9SO{o@!6r_k^bF)gT$%K5q=7Q%}*CJoiY4-07Z$RYfy%Ueyub)oRIryWU#$ z`sb^_4=_;~zmHZ0hCsh0OH*m#! zY3?p)6SfCbLqDHz#@xw$$k}TUammyMyS-VO6lHyf?i3tSFWd1=@m~FeAlRl(&aUu= z)HTF}ym2{-FdK8kMy)JGQKE67*w`4o`o1UnI@67 zZrX+YNTbx-%LTlYq;jmEGlj)UWeY}cg%SHMJ&?;5egba?ToUDc8G)Xi)x&-dTqVbU zoe}TOs+chs3|4UZ@?M%y)KKz&$(? zP6-B|aUR=_fp03(vEc)=d)3mf&}8p!&hMR{@m4WU$Th)+AHLZZYEwUqTxj_U>@xnw zySgEhEfThgo&&kCrM>0sdDlbu(BmvBJ{U=ZKSR*W+pB>e+0Jt3PjmzGrs~-vAp+=-MYZDGKD@4J&3km5>8luQ;8lr^#kwRPM zwN|4^3a&9bNBXW%N<45AiPy-usa;JkrTfNso|U; zlnhq@hss*uJiF(FR?J&!pTQ5%Z`VIO<;wuRzwj+vKK_{J8&=A9xNa(IDeXoOQ9gpF zkLGcVt+bHn`yx=fqLkaKQHe+NuS3SOM4}pt4dTN9E3e#lfKvWOz}L65iH?J+Nb^#4 z*wO4OrPa(1y7q3pOvvNaL4(3w4(%YOr^{XJmd*-TTW^9 z7ag>z+>N?bdO-MP(gavqqJT~R2_!eas^Cb~E#qlE{7r@iYb!)ydXU`NSW2UM68iVt zfL$Uc4&P{o2+8meZkw}`nDfI&372Ar%39KYl z-;kL4zO!KK{c^#Li@J)3U00%2YUi1n77EhTBJmooHY<^|2j~i)px1g8Vnt-^c{=UjP~i3Kbnu^ZBtc}QAw{)Yhy6mF>KH)B*xi&>~>rntxJwE~0oXv%N zF}~8waKF(1thfx(cAtCh*&^ZU6TQNy*coE;BW>`on`WmXtPXd_lYo7sy`KI?1mrYi1X|cmUxBqikuohDhyrGVye$e&qln=ARfb}yY5*g%i4-`F{2&D=d-SK@xB8>s%_ zegSCR!C2H5fsSt*l_ z9w0A9c_2ly+gUaDcqX)Bl&$hj7Yjt>aj_Fd%*Gyhn*FVT7@R)|KTsh_#fnM_)w_x% zRdiy$ik5V$s2g5jH-~_;3z=I9AIZN)2Ey8RviQ|fL)uu?P}ucajr-<^4&gWd4dJ8S ztfujVBEp(I75luLInNr-(!!BDV1nji%EeF_+$GBqMZNn6(I>5hJFfRq>w}jGDqi@= z_E=`3iR@vy8uwO8M&S?j`DYT(s(D&uer{s+8>b4j$mkK?c05g4g`1$WDO0>YTC)T~MJ}I*!F^3zu}g`6QMadC zAVMuyL&YhXvG1Hj5YO|n^*%rGJvsxV-^BuI=~hkj-I5T|wf_=CaUln(K1CT|&wt)z zijubQsG<#;u(OTs-t7iF?0z7p=8}tklU&4Ed`67>tAD9lf`Sx&Fli3f{?`gx5JM7t zyI->J3#N%gUmyA@g%NwNYBBd#u4i7Ac<_`TYzJ0HE$8{`8_8r?WOAN7eJT3Vzf%Zq zAC*ZvlqWhTwiRCW@V%&ZG!L`|7?F0~575f|v(WBj6a6DSj$iBU&u+oj;pTOVg(@p= z!6<&7X=wOLe_J<7>_K*t`_j#Ya~cex=zTkYe_ub-H)n4Jp5E$FGCk=Aqq^xLg=jv* z8}1+%sAN*ol}1VmXTp##k~wnv$3wY=5;QAO62V#!T z4kAvny{zV;laTXBJY(E1=GtGE!LgT+!EOyN2ef9t{l&*$D0lEZ1PymW<$k|*l8sVa zuj#wC9o0T#N?h3LFDTP3A=G7#@X%f3$asJPd@MJC)Hy$mwFLae9=q;VUDUFc;vP!p zzrV=ACB&QMzz_XsmQE<7oU>9``L>#nNlK9VXg!JE9nB@~WYEI>Vm=bheuUzHd^tTZ z*O&V4n}9AKoMc~1&d@#pz_+g*7K^NWs9u6%{JYBKg6v^!R7FK73qrNTv)e}O+L9CE zTK3=3@OMqzhbglTNee$Q{#2}J@f1ne$CU`akBV_qzdn_lN}0mKCL6_ou8Z7!W4`Qj z$_NcHUP%AZJcsNFkYuqVG2#ZDSFy%Vk;vsHO>TASM=G=ODi|vKtF`&UG>%yEk*(Pq zq(g5xA)gzmS?{qFxbI^Jr@cLaF<;Uo61x-5y~ruV+LzsgPONEyo$MjLAov8T4$SU? z*G$p?@QF7OuBskYpDN@BsBne;l9)wyB?3K-rYd$fab9#3aOG=3*r?5rdriw&LREhs zRcfsdJhFa1%LdmZ4;=HM``8*%@U4M8aAH*IV26)-Q&=Xk)+~j6@JpBbM;S)!;lBi@ zrkYgo@q!Ec3=xI&g~TJ;n2;`90O-uomEOO!fSvPD7o}!r~;i++p~8-b=yp zxpBO%7sH^5%vWsR$`G)6-Fc05)D~72U&Bp3d`9u)>RljDRg!$VH$Xk}Vko^re}iZ` z9FM!jk$)y5o7n#73?FfO z!VcPB#CDl(XTLNO%(Dc2@WV$lvGn6V!1d!lB!|x3VwF=rYNZ*?BV{&VkaodkiSya7 zQP1hOf{n3Cu6J&VE9GB!jO*=~;eacZ@%Qt~k(=fwZ^axjk!bn@f)=Z zkn@l4kjhzG@XA+%g8yveq=%)%j zj#5&(QW3n45D$L9n%XPU6U{-?dyB;!?|b$HQ1yTq@7_bmd**Y`ISrBr)5WCLRRw`( zKAs{|-)0C7^vGgXGLNynSrw{YQr@)NmH^m6{+X&>>lcmcPwEW2MxS$imjd?ao-F-k zg_i&l|3xoNOO$!#GD9E8G?zO1a}#<0iZZ0BP${aLx&((m+yz{336!{;w?)J^rihm_ z+Jr�r|+BZD^UDDV@c7FvfNlC{wegm`TkyV$*sF*4NNl_0;(<;4xw@uSV9rqy_39u3&y!&}DV0i62|jp^_6s;DXG;0dILwdX7$FnubciPnQ>tr%{=m+2 z5~(s`E7#w5i2P@1$2qanj8>X3nstAz!Mb9Egk-~S^@6Ao=H|Mq)T^u_-l70wLI{6m zwv1u;;2}$)8>bQgEF2{7fC$E3Zv-Wj7I;^k!O)dW02TQPuE9 ztFX;G9e@Mb?wIkNMt<+x!~B%f3&<>)IQGNkRNnS2V}PDmJMYHWA>bzQ9BQdW+4NO7 z?YQqCGWJY^-ryaKu5i&I|16wgBs5Nd#iijgXIn}s+tkIJdmN80q$Jih2P&P&EMNu zEjPN~PW{PLClEEf1eIQW2^!w9f&1sQHu!OlAG=NE8Ly>Qk4!Q$VlKvY5Q#2Np|Gv5 z#l3z4;L_D8_;~(ars_JwtPjteb%EJ%kF;@@|hU<-LDC4SUe40ei^{7T)kgo;Xe)6 z{AxGRa7r;2?r{~(;&c=1ub#q<7fFckGkeRSpQVEu-MjG3ItEZAZOhC}83Sz#_Toyi zkKwv56zM>&OYJK=#y&O$u(YvOPGjSc;9{T+iD2K6>J4AO0qYc2YHJ~Oy6_QXuvJ#- z$H-ifm);o>?)->6qrP5nB7TVcwR5Xh%dESG7yAWBP;>(?{Mf^KZuh~!zaQ}RJ64le z@oN6Dci}*oCl{1_Wh;t}T+O#xPMIcEJ1G(?_Q<|zx8)(~tWS&N0w?KKkk6Pp? zB|3B{i4Sgcnx*vV3-eA<_0L;D_w3oFA{-fT*o20c}4s zZs9q5j*Z_46xkg^CQ57pA{_hhx#fthae`O!B@U6I_@uJrX&%YmLemnfcmgc+>HAij-KW^2g=}>v`N5%*l@aiMuX59{( z9PgBiwfE=8tWs8%`FI$9gSR4cfC92S=2bOUd~57U2u!ze>ZnLf&Yk>Y`h>r-->O{h-$VRzhvZIYG#~ zJ>&_IBDf5_AlM@F2Aelzi*DlT^ON%2xgzTk9>jn%VThFC)02 z+fN)&J&f&2HlUxL{SF(PwnJs1jj#(x8T9dv1G1zCB|3E{*!lw;dg-#g$eO&Bls;G{ zb>!&)&#u2m_Lc;z9M&;UG|iYY4Vph$+GLmT+8Kb?R1wR!oHQcZ>poGNL@lE2PN{sY zvwdm-&mXbA4dzH!1x)t8ox{xcenRe;JOY_0LOhq#?x0&nKks?*aq{t<8RYVf9L}=H zP~_8M3)A>lS<+b=IrvUlkSy=6 z@ajBGn){FALx<*w$$O}a`umwO^(oxah)irc%C9Q48 zcu_VY#3x}NbvR=W$AQSJ!jKd*=zx?zl%kKf4{NPrLyhl@fR5|GEQqzI%;X zt?8HeFgvGtIqT|(kAcA>lNfxZX&YpjudEdL{f3-InG|X#pA6T>E(Ii)#ltPx{|UvO z$1!&`M|e5%pNS=>uCPbD-tZdc8-QNV%#q-+Ty(CJJQi=gnZAv;0XMd`kl(xWpq!j_ z*!BmI_^yc`c!0(MTJq&(BE(srYo8j;C!JK(B8~R`hIhpZbk{lpokMkGuI*sr=kk5-JNN@|m$M8NBt@_Cn?>#_pBhiEWZ_ zM8`iADos@7!>O4O#19z@)^TNo^oRGByxEumuP;vEo5eR`k?a2PuU93JKgvxA$7kWt zq9IT4`KqI+%AyG&u<|3mFnB$?=Zz0-$bF3_h9%18thQ2Wypttd9g;;!T1sFW?%QCh z@1F_p%=mClmxWO=wYun;k}pE-*-RTea8%0eOC`4;{t27tuO*5kJ(NiPr!G~z>7KTM2)m+qbsAB-K{PBt8W2?4U3D$KW8BA+WC?Nr<@ghMhu1X z+iiqbx@9?MHx<%Y`&|jKbx-Kvz-w%-9YU}l5JvNd4;;X6lA5VJ1n|!*;ePEu*b=Rm zO8ZQQ=|fWCoCEWHl$Jf7VNc)$%^H2CvkPAHO9MUKW)3|6*tX0~}(E{&rq#68I7MR%{MfKE| z&}vG1Fq^^|Zr$r^+_igeO2-lQ`Wtd=LxeH~?qkw7`Ug0#t6sEXNh{2pet{g9RbaOHSrQk!xQ@?Q2h1M#9OTr44$`EPl?6c-X#Q#5qLJGO6uoB{;2 z&a9{7@25uWzjYIG2_`!fQiF}ThdZX|#-!y~q^F1MMa^SMCHOkUMzu>aHo}9Jo5M%0#$}*Sub!j=?);N9 zJ{T>&hgbp~8c)H{T#`NV^e_9Orki6sdJglOltx4bP0T-+9>KDu%P9a^1bq1i6SY-8 zXzf?={8fMge1QY$XgoKy{1nBgt=!97?!+-o`Ssoy~)Qu{CXpFsl+ z{5i?{b*7YFv5SD!2Liz;sdoIzMDkiJnQ^@W=JNIqxtW;N`L4FnDhG|C7v)J{I@(6?!T7^$dsKzIS=Ns%c%_6 zwk_&{Cx1zJ!+`=pFuR4@rssnyw*_I2h70JIkTK~Uu1&b}3qVnFCE93E?;;@8(HFrR0F z7C-w=7GI@_!_Bq$aLIPA{)KjFee*af_CY;)sqwq8O=F7o>1wBson6gZ9{fAY}7X#U^pbJatrsC%39`11k|7V`}29REkoPuvLo`=-vFuQh@V41Ex;F+V39 zJ+2DtPWW;3Q?3hdx*g@jFCE0EH8(LwM!qS;spSivIY8V~N?l{)DK;r~40~y;gbM9zkgo z1iIhdQJg!QO)=*?v7Cp5JOd0>9T9*996dd@98YlgH}w_StvLnor9P6Dg*A8zDhtb3q|MD zsqX3w##K4;jO3)AK*K~{BHHvA-6M2kAL(b~-haegBdV|Q%idmQg!ylI4&QqPoqfH$ zJf)MIu-JG`B0pY5>0K%vx?vn0kqFe7wkTEydoJ>mM3<EcFy>PHQc=wcvSZ+u2s7r^1li>MSGH-o&UA2UT{ zGnwWCA^QgEDCxwna7DBn{B`4QZeqJGr&4ka-KzLi>|Nm!rbJ~#kbgH%JfPyAL?2#8 z94x;pIA5ZP7**b9oiv|t)!SZjR70~QBNG@PQ)ixQj!G|JHQoih=;ngo{WtJ$o4ptL z?->NYJ@Qa^_kAVrMSL4K!XbuND6It4HphW-Y&=--c1kfdXM%Y6<|BDVYYiv3=^rZA zKTqh%&f^C9My9+>_3DR&*>Q^SdZ}_U(S8P+`>6w+4mqq6CjyuU%{5fYqib;DP_L3% z%Lh?W%s==5+(o?ql8>Cte?w$i8$f^J?$G2yGk(*-_rS-hNkaT5Luu~%L|!bb2F{8d zX5E_0*=OHo5T4aa{Kw^JxqC6onQFUW&ibwE;kAo>seeB*VdGzNbnkn6VXUz>O!|y7 zf$!%+2FrQ{ODkW%ckULjQa&a^i$ooCzHTU1+`R^Q_am8n+yc6P^>8PQJ zyp2q-UKgzb{UahjDDiF7vM?wkmiwd3Ov}vARz?p{K$@jq@un5@`S+g{i{LH(^-)0k)C*9Us8Kusl zHYT5Cz?r(23WpZ{7QGb3Xl|UNM=Xl>SKTnnGIUEh#58etf`tt(DCN7Ay55=1dy*&4 z4^g;JD|Xj%?}tXyEhg6JxVR7OSfdEvoNvL|zGo?k>?x-!_v{63*8CA$^kCshQy_K!^t0jGOIpP_oV@9xI74cV>8XMNgn`jF~hJQrGnhwyAA?YIuU0g zPUG7nz2zMQk$CPKb)+%usMb$W1#@HXX?%K>EMu_z7;)_BXTka?XXJ!s0HAU|3~|3m zAuqWsA?JmXuRnvFh?aS5`95!!bZ_HWeN@HK@VkQ8t5#4`&8*XkR)&gNj#Fi=>R3&T zwrI1dHUujD;r7@Xs`=Un2%kgGpr50rq|R&}_1`!z%2;)j`%T`G{H>^mwAjuQEuWNU ze|aq-rFJZ)fx>Lo_eGYdv}g$vZYWN?|HLE8iCeTNwOf?#GtErzDHB$i*YaGQ1uXUR zG5X-tM&UEwKJj7kOH|*zpU}R!hNSN8zmU7$9J%NDn}Lfum#~}x1FlXNgPMS+)ex%` zj=;qXfEP|uE&fd$Lj5>zt?Lw6_W}bi#2lxe_L%YwQ>|IYx2MQ!H4wj=+J#+p(<3LK zTxz|-6JdmY73)p4FgHB3k)YK&$c-vLVasnG4*iWpsFI&)ehv7;;zcoPG?wIm-eLhf$oh#aKME4DRk;q3+EF z@XeV%(P-x(s_5J{JNXi8+V8bl*q_-2S8PbM+9JUI^4QxC83PplHvl2w_IeS2a}*g(!B$rSXR+bSl3<&Jh=S?a}Th^yDWT!c|#r8G4W!wX!WFK z?bLqG)rM^1WR*1dK6y2PE?Z2#bO-ym#W7J`t!p4Kmb)vUJG61Wuv3HLoY zNpqy~IEE1&l*jW@;c=H9?EwX4#!BVPxY>)4dtDg4=Xlc@WU=G z$LaqBM^4WtEjB08{pSvdH@CzAtV@gduhaqIuD~sV&EGCD8dGLm+2%Ctft!Lv`4%ye z+Nv#z?*EDN`Fwl2HN=t)kFe%k&WHw5&pR^5zE}vhwk8q3-HXtyuc|VKrxf9}zevR5 zf+4ryRRH?Ac>=yY@LFI->eFblE+tlFCiqyk8g_o=r+7S<612sMA@&P{k+cunX@#CG zva1q{ps0thM2Chp;bksM0rwj^vKD2^;NImU{`uPrT*6c!utoh zFpV28MBapp>LWiVoGm%SMDO;3GKMM?hgNUq{>7R&N?tMiOV76B%@wb}{DsDR+^UV; z=kf(G$@c`E`g?dm5Xk=gImy5CGer9Q&JoeroN@l&fHXEr+}r|mi{ z_a6Oj*UVd>ae_ZlYOgS0*uu!oT`0J);wiEGoVe`8(&HL))$`zhs2RR$I;@qqFcAD} zA3=WpwF7!+ngm!VJ`+t0NRk2L<9x}iuTpi;CrGt36u@1bdG6<2xpw8nguSdT9lD;9 zq^8y2eNX0d*L*5dEQq#bH@HeN;PL~=>x18rD@RlW++cm?muC%!7nH70p!!x=Xhbtg zjp@pg$|1!O($XyWW97WJ?o^sSvgzP1aI$X`qg}$mD#I!g@ zvoDm+po<%dp&7-)TEXdA?1KB7M5pVL@Qsmi{LFa+aGuh4WJzi{`YQANk9wtJNSi?ar;e8iB^jz#0QiAJ%0-<=!+ABD> zBeR6phr=0bk4WH2r#$TVm(M;E;#A;QB~g6s62|-IZ+ex}9rkwSULx}DJ2Gw2YU)>V z2N`TY$k=385srRmHPkH2Sc&}s)aPYQ3VtguV=GLIpouC&xYaTkGA_%e=Z1@8CuH{s z?N;iGt4RzC<>!V0m*p!7DRJbn5n(rmPe)*5`-m;M7 zL?5R#PtKlglFq>Byd%WYfM0^qj5P50=U{xX^E3ANhi=}L>c?2%-8uC9@p$g*8*4b9 zW_yy((HtO4-isNiFk=mF>{9V~^j4B9O{*~%@;Fg_Txmd_7HJ_me?K9j_a?$+E4kRf z^0hp@uQfRGvz0gK=`L{VE)c#_J3;z|T-BI4GoJ}Md{W?(y9$ZZuofnDUB}8C+xc$? zAK}l(oaynr*&g=2Lx5YA3|X#fBUFvE<7iEE^Q50X;@p~b^V$?1;qU*f#@~=?$ZZq4*qO_{t3fa*<+iT7sYmTmGLaN;8R0BQ!;-T~Ge_H9H<8giDwuKkK19=(n z1+O4>D#=FjmUk65^zmk8P5)Ho!khVMTz5JZFk&oRarv4C;X=c);Xe3}hAh~@_b0xh z_M&QNsU3fTX`E9h@;#s+Mc){)W$#d#1Ayzae=;hY^$)nQ=Q|iO)%w;caknW zdyo%`b)x5WnfQQol`yzyA@c9&Db$p|0e713d%uxrUFU zasQP9;LV3ZVzJ;A>>7Itf1dhISl&DZt$uI@&;VYE#C*7-bo~~-pRY4xuJnVI9xfzq zWT24EjTg|npSGN+0R+3bdJ|^4$x_q3_!=|#>WbjO!PjKX#&$-ES|l!zHNBjnu)+I!J>$-WrVI{1hCWj0qMC$8n)~F!`594pl?ccgDc)&63hGa z2oE`Dg10IUDQeVa*O@IXQFz6kcRbKU>ZJXIVxXCh z2)q7(>hP4sVlURB9XSeW(f#Twr`uIs4}Z!d`;&x7n(}H~Qo$cns!CB(TCB|sht!g1 z`Y%&Q%KlS7G9f{$xZ-jP$9D^p#NG>{HPpf3Tl#RAjfpTjq8(VaK%P37KP?@Uzg&gb zr6L&q-a?*{_S}<#u~$=~#UlR$Pel@~Q#n zm?`X4?lk`7_F8qVpT)$xXZi|#otvpTD4AoH+6lk2)1V2uMIG22j zuFXo^&t1gsYXtX`);N7O&6qR(wUt?Vvqxn6sT+?PyGm--w1aLD-E7nAe1VJ08p6v# z(^VP2r;_odT;!9t%qw#k8CLr|7)nsrvsoZtr#NGAj+DC~4BY=iGD8xo0mkk<~yXyQN)J zQlUj9N?B1^QHh2^h<23F4$=6c@w>l2@4xqP?|Gfi`}2A~pQ#*UTO!32lSM<1#H6qH zdiuc7F+Ba(h;;VCoxH;D=|V$W1<9@1PKbnXEj{i&&eV%T}{B$`i`FZuWo_nn$9Z&g#g4 zvIVWQ+^;~%yY)%JYCjEFCrZpLwMga-x;Kd*&U{Z<>|em}c0bs6e^wDG=Pn`lr)k5M zm7!oF-A=52FZ;`pvrxbEd2-1Clk(!uH~DEB_e7hEy|%cu9E8`mjeV(s5d3F(Lr zHonxLCtn3yzD=i&BvXvsnND%)u62;p?CoNmj(DxuC=pneNJ5Gi#8}hgg~D5BNZoQa zN8gLl!uI#m@YiqM;+uC{(3EScdJm3NBSUX9g=(+|b9u#coe@-a1}hu_)og+w-r_$< zCH}F*UipyL+{34kl?|7;r6;_o`hn}}xk1em?VcEJLDU=W>Es0A)%ts60A9*gZfz2q z%XO0L)r&zL?F=m3s88B+YZ)z?9wU90b^$F>I?b9z&&8jNPGS1Ay!1Z78RTq1AbqkN zpnv>#2^yU}3Er9LD!vx*8Xs_^*eFgF-YPc;%kM9fCU08@^wEtnr!yv=`s5?0(KwkW zD}Uiun;VF23*X}KjWN()JBzqjWsa6vtfj_M+PE8?&BQWSS4lwYWevI4&YJ(NIieR^ zG)ibqis5}z-6Y{Q_qfX)Px!@p%6i$*OFrYDuKV}Sx#GnKQt>IsLF!l8cV@|p7vh@p zCs-9u-`#D;KCFZ*lXGOoUiF-vLy{R@7eKC0nVE<+t zHP~4!f}F>=&eMDGRfcnPR+x&}UZ2(UqA3?aW%!Zylkr}}L(`K37oN~gQ;zYkz`8!_I0I{F7bS(9>wExr`Hp;$H+`LAKJPJ9< z9N$lH|Fbw>e%1r&{rOY6e_jxH==2(J-e?T9^JE^Sa&s-^^E`raxR{RCKe&fZyA>hv zd)ddWtth}tjmy}@dkvvQCvPB{i*tl~SDYt|*LUz&KYr(wx(p@y>M}Q%>Na6OAwlu? zdECeMDQu^pMxL$&gl=B|amKkae$cc;>KV2QQig6z3$rdjM;AWD&m21_iD|h*P5JIa z%v!w%&upG2on@cLvkQI_;Pz4eyGJi0oE=8?wi@%91GbXuJDX5`dl^)-J%um5{zBrA zd6Vj0{)KJSP;u{T2xTjM#Z+u-yZEo-Cvc+r7``+~!9A#3U%KCHJ*Rf3iue?_6uGlU z8FGu%VVJjh)$ymn$V4i`Jn*6d;Qm-$wSr`CHgd}@}enJiP5vi+LYZpI+qyq3_LR!q7@+9KbsoP}&X zypbs(B$>0~i3r+x6#G8Xg`B9ksFi;&h6ndu(mAtk9--X07c@`u#Vz`(fnT08xVYp4 zLdHJ?{ykBIA9Bb@X3=(9Uk+c>gspmntFK?vvQ}+VJLx@D z-`b%IbyWpwfTdfoc`EzRJ)$fG zoaX~7k!fhT@@ma#k*1;@L78Mxm=U||1dAGc*vC&^)lN(+%*UZ$uA)h`QRMBcU0Ar! z0qFUnDsHgl5z!ZDq<{6DgSa}7mpGl~=#c9JnCt0Ls^QZ;(L4JEe9NRB5PNk^Ye|rS z!LGGYItv$gNYze;@UGP33|KIRcjhEECsJcwLY*XBGr z+u#Ryz`BN?{HqTCm46eSY_fXDp1V61~JH8oL_(5~#ppNGlePyH0QVy8&)e%%{zCS>W zj&m1f?|{Eo+L6=u%$BY*zAANEcMX1#GFP}n(-I$ekf=^?yf5i%I>h*`>_a8~znQUX zcE}GM3vtCnH}NAo1)|}}dCGfhI{ENcI=Nx(dg@q#HLzoY4gO5m2W`Lk0W<>_i4v$D z)z2FVOsR5I&m;4={BI8)9XT!KYm`CEdu=va*d5L<-APExgvHVcWh(AZGJ?_Z=P~Ht zMnJ97ApsZ`Hj9&&Tmju?JSE-?_j6gVwV*ktuCWG6$B1X=tZC7-aHv-=kZ|-^4*xS> zh}~JMg+&j!2t##CLE)k;_!*<0Jn$v}7?a(RpIY%v|Iyxfw9b#G7sW&gvgKp+2RvI0 z8gsKG_vb$sLDxK$PWN5X_SiCo%1d`;r(P!5utn2_%Xd`JQbSMBWO)FWD!c+knav=6 zK07VlUOJ!uJ3kQWw2VewmU<$kB^LT^yIiF{PTR;>%^--|OdvK(o)We9juXs2V@}dJ zM|3rH0p2bwMn-4UVlEjT{Kah}1Op$VcC2Ytxw5sLFi!FXb3b~kBtE*q8h_0umOQ!v zHFR#|z6v=7?8czF98=GsKiM^^{gekPIB$KrN0Fqc8FmkpNi7iX*}*0!NfJOb~iDUndHV4)IIv`(WT-y2RVm#Qj-D z2iII;AxV$=$KOxjX(vUDRO%Y1;zuMLVX%PrDNfZ+Ig>1TFRnui)icNvLobxq|3<@RlHd&l`|7y z|60nK>coG1)}86}qM2!Q_M?fcSKBS6gm$N@YSg}IqfaFK*Os8!t#@Q(9Za3OlVX>wQdT4l1_i7H!AfbA000ktlQ*^ zow;`uuSm22iZ&?_wbu)!`#-!xEYGf2s!!5It*32N2>|!Yg780XI$?QVn>^wqF^Bribf*HkY$$71!OYw)I&=>BkGI+fb|;+c+*A z+cX0zTl-XMqS_|8FmDwahG;Pld*e8#@GXq*(uE4!4th{Q<#1cI=!?R_Y!&oI|ufNllog!#ud87TcrPlA6r<+=k_~XXx+r~0xM~V z;|$S9L{3}_K0`q}1>J^7$##U72pMfn*| z**!(=8l_p5d{w_T(qU~w7f;egCLLcOetW?TxKh-v+8>qx`L~}!v)rn=M+aD?NkxBj zYfm3n37K^bi#h!V@ZDU)8y?(2DSE{aUqkA_M;be*jEZ)=#Vrk;tF%Q_lt|FPgtOA5 z6Q1D48`l_@tDfk3dw_A+xRi_9uC4ek$4_d|+knaaH=p{3cXK;7 zhTn6UXCB*b5f==I*lMlQ^qir!fM*Iq@QV{fMcdZ^*=L2s+Fxh+;mL#eoxU8fGkKrn z!-2K(NOvH_Z+^vqjcd?l8MA~Rj)`H@l9keTx*q(@&|$j4Y&K$l(+Yh&7zz(tyyaJP zJBjZT1E6Z`eu2Go5%>JN0wfpWFTI3tu!d71>cH(4rKEu3KS8~!UF03+)WcKI#WhdiTG2A}K^dOV?InuUw2=qj>?e(WaFCnpNu~|_EjIsg8e4s*MzXbiIv(=%Aoh|s zqiY_baGC8YX@tr{oxU3q!Y!au;de?KIn;VVx~-L?|CP3gw!XCm=25$~&7N@R{U=q1 zk8B1pLGBxN-TTd=6R#BjYlA+1zFw&QHMb-D^WH_OLX!+I;nPdehXW7=_WqNsi25dN z|F~b$p1qgPgPXDF@Q=7;iWaiiI$5=_Se=#oQfs=M+;=Q|Tp>-($lVa3IY`m?Er3@>GZr(W0=)G1nunzr%##!mW z{E<@R;LB{>_U2dUdU*<&qcE!EE$HG`xjrV9XLK?5dbOq?16$6&pkRyzq**u*v~pi#C(nc&b+>jopRkPc9uTl z1pA74YM%q5Ozlx~>~O)K7FGjz6osqWbaC7YEkWbCK2i(;pb8qb-YupoM zD&2IU8+?Zeb>uZ}Klag7UpXYLxH27bczp|>Bf197JxfAm*J3m-JiH9U6RP2+h510h#9Y3w;x+5BR@NpwmnmmZ5FAV1|7`!AtzKfM2N-!uIc!&r* zhPC}yuu{}wl$t!?!j#r_0x+_XvoTrDyv)50MBdSc_NDy>uU%aYZ*@^sH+sE|%ef%p zOQucd{5@LGVPjLn-!qgX8I!Eg;H4!XLv@AQw-y1CqJ?^od>4_r z0r!wck#T^$b-kX2Ecc)qS*x^a1vp^oA}M>Yk%6x+hc~7F65!(Q2YEJ z5SN~aP3WwVOl_d`Uz5MdEU&-3`{FdN=Y6pBJN=e)BXal=7@`lVgaP8?#h?V4r(1f` zQ6t7Xo7%Jbp+Udsp)j>53VU?*I36JR1;u<+A$G?_^I4^#_}??rkwsaXN#p&Wafyek z=)Wn(tj(lDcyLS>Xt%x`$RL}E!^Oo=o1s8??0hj5yj@XTH|raJ?AM_D{;r$iPkDZ{ z&1eqkWS6ONs`m)I%p*aX65K-Q98-kPvKg$(ltLibxlrMidpjoDp@m&N(ZaPr-C(7R zyHK=ppKzsW30(NEhPmzCg+)s514jCato+0_N|~C=_AABFZ@%Y(Pgd25)WdwGgJ07L zg_JR5LS(tV{`1Sw4Q@Yv)oVFBUM9MU4)J~lI(0wV-xmi$~&Zj#$EM@=rPv1 zbqx{pG)U3z@l4=ES~!0C!*ym`bd#L>H4Bl>tpvIGXpEMrg^~7qwWmULgJ1BCffabZ z>ssmlEwQ}kH)tyRefQ(0!i~AT$cceabf!ri5W4BMJgnFYXNXp-PrX-;<;#x( zaR;|(Fn$s>#bYH3y1$ZW=Ytmn|E{EAi*0^s8?N`JuP40_U3~UXvQdY{Rs}$cYU1Z& zZ;Q{It~rgqaKpt->Ko);ew^b6G(E^?oppfm<8@Nk;ZZ0;jzW8WK1I3QR#DWSBI%CN zm8@sZ2js}b2~1>T2>i@&9kum4&EA5iXay;%fot;)a$)`k*s$d+LHwO1lIqh*+!db< z%wm&|;G&tb-Oh4fl=M+oi&#m3IjlTSMy6{xE;~cOXNsjgMl;YlL8D-MW)0{O*(B-6 zT+SrV{3Ts{A`eMuFW{Cw43nn(2&50$N8=%7vki}a$s+0&OoU!f=*F{l=!1%Wm*|^K zhHS6pS*>Nuf}sB%7@(h6Lv~r(L-KLR9)@50L%k&Bm9*~L7r1PeFY{F51RahOFg|@j zz`wFb+>52bQp5AJfs~r1bVJ`y#UuC=QR&P`i-oXD2 z3Alo*@1@f{HCT<7KfJf`d+N-SI(p%~D=ehfp?$DCfIR-Oi)t{~N8mc91V3l47^thn z>Xo11cf%9uJ+BhPDJzQ|KXpGTme!JLkVyvQ9$6#t3i+qa%a?c`ed zmoHb!wZF)tdVW2mKdgR%hR7SyNpg0~mWK)K^H-pLQAHGOK$;6*S5Bd)ZU2i|YWdkVF|l#O1&tgZoZc1>a|+8jkgG~H&2-Yhfm}*is$~^aTU`y&C#6Qvd&}M z2EZ_WlIGecH%PmNXVj`%O+xqm8rXaSE_rU z@G6S$OS~+d`fVXt0cI=axv6SRg5DaACB9^vB3|)-O$zXDzzQaHK`IVZs`7mAsC4T* zKkoOJ1H#g*rTB^OV)j93A?u-jjBHU?);>sWK|dbS5SvFXrZzYnGgy427Xz6gtU~XH z^1tgPzz*YB$oH8!sStkz@4V5d;j#aT2L8!U*I;miR{Zz_@vG*`qR9WI^D$*VSqsM` zs=DD29(l4{{o=5h*7(s8p#^_W^mJ3Z*ix23F9wsfqav3upC_yY{z)a2VZEcasnvSG z=Tod)U;iYzNBje!v)?q4h1@1#e|i9Y;?HLEdTI|MnV3P+_pPwb=Vt7a#3aeIsx185 z9Tn+_bn8e5&s-;w$6GDu%73W>uUdi22dH zufP$2jtO{0w`2TvueH!T>3!t!unPT~ThA@3oG*554Oe;AGNLfzx)=M|tHa#5@m4W2 zo07(Dd&YJ`(WuF=C2)%8J>~5_8DcA@N7T~8N#6guq}R0XFflVF1R%bS zE0wkxW81FQ0W}A9DBlU|CWSV~q_?d+z$CvXbfw)iR`Ze~F3?&cJ~$RbIo22h8KD^5 z2&^aX^l`|E#obKB`^AP6SL>sZ!@2BK@poW`3->W}PE786T6+_?HHaw>o1;*p|o0Doe*f%5MPW;1NAsy%lb z701obhqYRIr5Bg9Ne(8p;Y36`IwGtvq=z$0(PMJJEWJ-{Blu)j zhKqTs?Vd6;gzdC5<_AR0l$~6k>fp1lO2W!#NT-P{qU(}N#C{P#q>?|8SPZb5rWbI> z*DuAU*W!4mMW47o=Lea7Wkn`h{R02o$dTS2+Y75DET^u4n}JmH8UOuE7P9%%MYc%2 z8acM?6*6B=le(+)UOG{2ib`M88T|vROIb=dk5P#BlCXtw?AU<@iDg-X*qwhZz=0pHU>Pz}^7wQKyKnAs$?5)k+9&JU(9P3-!3#FK@og)0$XnDFrZ4;y zKTavod3XF_!w1LlJ+QLM*o{Wzhmn<7MEy4T%q^{YZ+=Zi^p-2Lv$R5xy7u$j_aoay zm8u?igrI|be>zKcR((w^2v(!*bR2{mR$o!xcj6Nk_3ysSEg?r3bQfdIkE1Ewq@P;P z*;m3DkIk7gJGJDnY(Z(`PlT>HrPYvZF$}6MjXa-z(^e) zMfB$pS4}?Cshhgc5ZPR~`)Vb>&wmX*%tc6 zrYdKH!58mllE7)riIGHT_ld#z#rWG6Z&E+1Q@m5; zBws1ELwB3H!#kdr(i#UnL=*iwc=g-+#rg#kbbmgXA#^#Z2A*5zMNY#Vgq_O8M7(MY zLyDS2%XBB=xOotHef?zq*4iGWlz*D=b}OD+*ZITnMwSAs2bNoiWtA(BJ{9~^Wa+2$WxAD;;N&p2a>1AJYP`A zNjI(5tBNOp6iGgJn|7A&DzL)F0Wr*J8;VjomC4+;2D+f^9HLwiA-!q%hkl(sRmen5 zMn3swY3a*%NY^?w37vjfp<^qCtQj zo53IIdb2FyY>7E-ZvRkm4cR6&x$*$L0y;8QcgJbf#g#y-#(u(C1)?Rbef-dFe_k}D zPSS_1Cr^jvBWkkQ{Ha+nxc|s2}S*IZxEA?Xv z&Z1<%OjUYpPBA$D>=ev+_9uD&XCvx=9Af0wF3F;Hx%5IVUWoXOYA>f^cM`0Reun+j z3Q`STcLyliqY0QaG>Z;jM8H9>3ABsD5B9?a2mHOi0@gcc618#H8De98J766^Q-zPW zz%w%}fxL)b&3@Bc8iKUDBIHhxEVZnl=bvo^X&d(ocX!D_mZPJr{yc5>kA2-d_HT^+ z@m`;E-|7dhe)5?qch==1&UY&>nVSm^1YG6jMr`EoM2>08&zwO2J(z-SnYwd|-d(Yg>%DYS z%0F38oR64BJ_E`KzY;m+a*HT#X2n-Jp|_g;yX~dgmA7Z4oIy5Z*ztuD$uE+=(+N{u zn(mwS+apHdpQPN`f-R?+q8wJ+fN~z@^g5g!4!I96N51WdVqN1HNfXyXIUbT!&vV+eX_pAFUe|+W!8`PCYoteU|jo;z1kX$c+r>*rmVFmiLh4*6BHt zZ_*TTM@Izdoi97^U$y|7s%NVG)44?5Nv|Nb?J6b?9X~EM{23#n!U%9pwTK9OXG3iZ zs3)?5H><^_hvRX!UHl)TGgRiglYEKmkU_8f7IsESqDb;=oVR_l9!h@<7`6L8^J(%RHr4keJpMcf@(*(nuF~g`i^wMNRZ`0BG^v7a zHzZ2*XD#Ec60R9`cimOj*2rhXdOKN@H#-RBJ&UE6E+h~~_htx_`$Nb;UWqi?8wc?z zZRo5kZ^f@#leOgqdW6(}8*BH)z|gAyyVAOv79kmD%YzODjMhtQ;WYIV@QlJ4c;rh< z@SKU1J#apYQEILR%aaK%Yge))Tcea8Nh)I>yvfCSk9;SuO3h%ot650?*6X6kxpx#t zoh(KCh9^Q5^DZVAc_6CwO~rz{;w5I6uQ6e>svzZV478X&AWZ%1&rX<`%|d?9(Cd4* zVUP39!~N}T5}kbvRXX(*>?Z1ix(5w0DoU3}wl5dNgxkUWVV_Dk5e{Ng#6II9D*?a1z!2V(&TszCVFfEM4vE{o?CfTTrJ)s z@Hu%|lKtrj38z~^zbE{m%<^Z0nr8Lj9PSQ&&Z!tIDsYrcIpIVyeO}x`m&1TT`DW~z z=U)DNhOxl@+YQOxpBsfngI)Yx$7!ga+cY9&%{Q!NViTFWbVLbn*Wy<+grmZHj>PYk zjm(ueBlnkydgSI_eSOOVrRom1o>G$yHesPDO_YUx9GfyS#0%e>v9sb1OWe5>EIhIa z_ahz2rzPItzx#3$Q3`)&!{DU%k z+a!DLY~=EXY6NGKA_-QtN4Rp07Zle4VzG&r_|^7XLA63i{PvUsuUL8ve`JE`ENV#O zbZ=@C-cT}jFIbL0qv$KWrP)rd*?(1gZQODs?e}h>&$P!v!JgS-!>&1;oZ)Z0;@Tz1 zvc?$nj$RA@8c@fR)Rq#0nlRp5bKxVi-cyKCRB^% z@FKArR&>c4u#5aIJXyK|*!;+qSh&Dg;k{%)b>h0?sM<&uUn14f_`IkZIQG2{y=SZm zO7o9Myq2$Kt!KBe{a?>8m25Mm6@jBKgPn-f=83?%_bFoIcagv}n_+T(@H%GD{DnAX z+D!E8!%RYP{xnfr_zYn{HNvhqqb4kL)xsw#&*OjGn=0N^;Dv;33k8J%Ph?MbbLyLR zFgSmWnks+B8{QUqOcJlRT7+os-xQ;7l2rfZ{jio1ZDMSg`Mcl!@6AZ=5bE1&{l2$+{6wK4g)Jj{0Zo32vwt8$%i55N&i2i2BmVQ z`1hBeWh~n_%&lk`h+Kd&M>bF6Vxv#NFE`8=XZM^}9b0F>6o0*gg~+|5cZlM}y!u8a zJ*xZGjw z!5cPPpsgRDh|i%1;PP|ZnI|@uXv#TDNE*?=hMil4>Gf=&JE5oSy(d;^_ls?5Osh};7xg{4{=@kZ>aFaTcLvMugr_3q%+wKFP)q*TgIVps& z>c30J{jmZRW~YkNR8|2x-%Z^Go*sDk2P5phiIKcunpm}?YMba+XPcVQ6c_|o)4+J` zPhex9I=>}J#eJuG8CsOX-ZIFy^)4rmB`s1R-wqBDY=& zrjj*_^)eSyPU6M+_eWh*WzEj8A1yoZ|4w!sqD<7_zD9LS3kW3et z3dcS;Gu!QpiB0b?Z11yl`e3yQyJbcnnVq{u;qY}`4Ve}j+j_PMn+yd*6WB+h=W6>T z(Q=oVtaB6i&0i9k`EwgZCynIci=`{joV?pKaP2dHPD3Kr4g0L#;gy2EoHj$_t%(IP z+3X2=vilI@CBI!5f^H#4&q26J#Zscm-V|DOZo6o2!FPOaa~~l$;1B=)@I)>1X)H8x zW)M*^WQW8a)!-Vg+!y8g{^fTRY-W3QJ`=vL&cz2+j)SE)ZUHN`%4G$NFFf{|05oQL zBE!-qKK0Zd#h6wXE^%BQN-)FN=<}Pz4YCS|W2Lq90Pq|&j!fdW7UzR?Gl;GWe>aY8z{YCPQbMx{oLGn*U;Ta2Wu@qRUCc9hnem49}sr^ zFBKr`^|qxP6CRAIVBen@WEXqsu+la1?9J2wE_|GUP0@7H=hs1nUv;)Zi@|L`QpicH zS)Ip^{oYLFRt5t4yME|8|FzOy{#b-=SIa>qf(G?Lt#Iy6hz9wxavuN8*hSo5W=EB& zZJo8=cwe{pA=eu>jueU9asF2 zs}i*ddnVDb0MucJQ}Cm>?cABXl?17DP8y@`B0l(JkHogpoAJaoQd_k-C?_)!)vJ+qQO&~Z{?T=8329=QoBnWd+oyZ55Oj1MnS z*VArlHs~CwPRV+-`0snE%t!%!I=vGKr!6Ee&06uijP5&|3jMD;W2}< zT``THxki&zxo6@Y*P+PYNI1_{Y;a~)=IT)aZ}TZi=^|RPd6wFqN$VAZ-i=Te5KIqh zWbwNs&iwwlvc0C5BM932K)B!;%l@2$QxR#k*z;GG%I__fvv2EiIfKF1Y|*JNbnaOL ztoIU0Z#NC4fvh>qmGnCN^=J!b%eTv1dM`lRM^A-D#co7dToqsy@Q#<824hNVgXpHz z0M@)Z3taoF8)>X|;f!e;x19?kKv!>){=+Q(QS%5%p7du}>;_jBh7OJ{)GCuutNi{kMS9 zWs!1*`FhZZkqMgKI!LaK_GZ4G^+u{^MM2rGCh%KZeAsmxn;=Iudt|Ln3DT;S!-f~< zv66>Vq?up;sLin~0u;__iDRNBD$E3>;JVjGLkeka@;qws-MHY5J#L;u9IlzyBM-)*~;VPglFp+X*#NlPzCJD$1Y3Qg&cV z-aVmq7ZmV^j@gkjkIn{eyQB(tzgz%Vu6co;FY=QH$LJ7Oe?ShggdP%=aDk)PviFOi+|JA3jenUYeafAV*=q$)JqVC?7>=*OH?akajs z>W|Qu(xz}5p^eRLB!#dNon0X22g{ao%R>WI&M6L&ONk7L*JfG2|N238VN3}`{X;34 zSAo81sVw}@;(=~iaVm=}OM^$A6!4oZWWSgws6k&<|?&F^N{+i1B`@e)y$_hMDhIii!mx zoSgZXc;L?u;=^hmW|%*(r0_zQnXvPqj5RV>I-|OdKV?w}@h?o|FE8;^?^g3eRKE90 zJ>~pSuiM?oU&VV|oaG$#I^$b}Tm3Qp_K@drN^3!J29*B z)mbF?CfjKm9fa_my|8`63Cc;now)mYzGNtTTu-6uJ29TeNtF=*quQXr0O_ zWJ={Z_+x|(uNpH5+MB0xze^J_<6T4i`PK+>Z}fMpdPgCYeW+eC{p(cW?!gD}(yt$} zk(z3Pc`hwZw&h{1lk?~ z5(XLfIfLF^-0|Ud!IZguhH>sA^w~Extnl_3_Qv;EUVLm^a`LG!c&5w-nYRC&hDm0J zc+AI^3OJP|wTE^H1Ey@Jf*z{DZASu-$+BGQ?Y31n^E`|)Dr#wP- zR}-$sEuN`}=q1zxme17a3tpS4w!5yvK+$}{JYPo5j>gzK&sIW8zaFzqb8jgZuzv9E zr-vj!0V%n9isA<@Z-GVDcd(+13_km+fVmc|#Howsi$_p>wZx~ZwDcA(u3KVFGHho>u*U2!08bT#T;+?}aE z1t=9>3X+QUKJMl1Rv#on9ik0lRO1!jvw6tV;(WYCV}*Fl>I=lbO)HQX#dQog9K^|! z(^YlWS^|HAyy^E=C7673hJubtDRg780<^w0Msx!`fL%0QiS+yzj9&d=qe?BI>G2W* zY@%ntvqFkA(l(?~1NWWjjWal%r(Wgcnp+5V`MSM?^^%j6dC9m)^Db~c^%Y3*DrYRm z;tP4i^Bv`Ta3K-$^B-H8Y|TF(Dj^r3dr9kyZ-rhbSFy|FzYC|_p3ZJNrp)DcrAu@) zyVM#Ec=OO&d-S}bh&%SMmI2$w_`t*z;`7HW87r}Yi)i;CD`#6X57lJMZ`jB^X46sn zBbGz%E-UBS24@RbxlRR^*P5};^e)O#JqRr8@Yh^+Af7t+_maj|mv{;My^GiNPNR>E z3wR67d;BF2OO8|e20qN$CaN!-O_nDK>9<=>lk>v` z&nOpc%G#@Q*y=J=7V!wZH*gl-Bp;8W;%8bry+`=)%rp4EXEML<_#}S*pnw1~_wb=> z3>3u{}uOC?CIHGcN^9^t;559IZhISXIcXp8KpEJI)4yv?tPHAeJ1%Yo1l zUF_gHC-hY37Rd!>Hf?7Z&BoQUu-wH1ytal8sBRm_N}eynk65P&nP2zt!dHhmPI(Sg zJi~=lamFFnAzSJF2R~8RW{$`~)f}otw55tpz2cfWj?Gjk16oF|i2|%<@r`45*?U(e z!M?TwGKhYintWxw9IoKUoQ?BmcDa>Ednpy$smvo8auBqShbd=`UU^M=_zXEi)8 z^DVygNiM56pe$YCI*++?ITLF;5vov-76wd6GvO9*TQAgqF#~Zfm`@&Z4#Tf49>-JV zR|xfXr;0A$$PrK11K@(#K+sKYvNZYVPwZM_4D!n^4Scp;MWOhG1LgHOmYbfMphvua z1Fr6yfaxpm(b~B`QF<8of0%fc#Mvp{`XNEq$NTDKTy=(NYMXj67+u5*>B)N2FU{ zf@{^^NGz#5=E9|Y?CEejt)o3=8DwL*$yh7$??1-eFJ+cl55H+XRCO@ z@QGQBaY&|I!|Z*iPk|4&SJy!_BmSPq?bCa)e&8_CzTgn7a0}w{|61bb%?lMXYg8c1 z%pC|Lg}DH46ZC+5lv2^CpZ4XmCpcdVGdN{d0VpE{G9UdOOL`tV=}ns2$T`n}CA+WJ zAY-?4^%xH)T*0SLDpj5c%8wv$PVj6td}cVy8*BiH3hv^;iB`+RR&>I6VXgNV`O0q!a zHLMn$p5M)sdtZP_<92%f`Tu~V`T{s1E?RCPl#Tc860slS^QE?nJjn_DmUP{*d%(p0 zVC=@;G05SrF>YD4mKG~?poQAg3?FNK5?cX^*r83$9QElvm{+OAC@QuM&DEDe`F z{FRNI4Ham!>N7;gdwiMlq8?Owe*iw+`Ht>fk|FMQb}-CO&ma^B2c-|z2Z&<~73gicIJr5Tj@GNqZb=p0T(b;>#@VUVxlw9?jSdd!*+Q!EVAFsJ1zVjy? z)Yf0dU9=369JFyU44C&C(SLo6iaqidmfopl_EaVbYULp@HFuc1`T7XELxv?@-Etp0 zv3N4(bZ(e&FtA-I*<3@@9a5v5~?E&2!oGe;~G)XLL zNoG;aG%BKa6YJ!6#uv^TQ<+s6Ct2c|#E9R>QRes5iDmLd@D5K3oNPBpu5cY>fA`j6{afnsv`tNb z`{NXR+3weR2T&zNqHkVjBHmW7RQTDIP%i$LUCkjp5+{7Ed zc(5+E^Rc;GQYG(#HVGR&o>Dg29r#eWqh?P38Kr%f0?DlAS2Sz-}9c$ig#lSZ||1v=m~)p@?AtO zrwFz0dX1bb{13ddZj6HRG}%S)0?gR`h04hCFWA;I3#B@@{Lp-|2Y*qeCb{hoaKBG3 zAR6!=NbCq1^7tIC&W_Tl; zqxWj9n4w8H&sRp2tDZ9PZ|76gE(6{NT0y2|{1Jc?mU6cy%ZghTMv@J4v+;r!p~{3i z!I)dPDO-?eNvZEvA;~;QFYInI7MTl^6|LJf+(+}-vFZaresrFosAZVjX8ax5d!-b6 zrTI=8yTuYv>nl;LDpYa*vf-z|`CmI7^Rt)ug$63 zqzQB7S7Hs1Z43=s0#r&8ze>M|{1J=hUglE;%e@}zBL|->RaMDtr5`qf*s;Qc_~E)M z+-2TbYFm&gOgxE$=*U~K|5`)DMaF#@ijt!_2Mm~4Z0NY?+ZV!9?ql4^?ay5?mY3D&d|xA>X}AI<)QJvE*t$~$j{KVP~6 zJe|@5hpnjMsm-d~?Zvgww@C$hccah2(MNh^exh!6Ea@BgigM?Ft%?OMW`*KW1jcW% z>p^2ja9wzEAb$9i0WQDik7k|AODP<&O8Qt^OOXCEhnDT=LgzMgO6xw)hUS;sl51A| z#LOMOV&AQ7k>sRtrDwNtu&@GM;aK`*{(4s z&yLb8mqf)~y6ea~$ebrvn$d^u3yAmnXPJQD*<`4(G2`HRmvu6*#+I#oM)cH&z$Te9 zrCQTp0PE>hLiF1hWN=TDHBGY=&#*HDZvSh9_WpIn&v&M=SarDC5trH02@6(ojW!Ns zQ)V$*xhRRc}tf+jo7b+oTkD_HXs3Z*)rL>JQB8g}bTEF}IAI{^vKcDyK^?E)XiO=&w_+Rht z6SXEI@};HOc%Oj~Gk<-DKd?@bu)S6xZAc*WyR83&Q|CUQgC{j9bnku4y}nvTpSY38 z6|YEAejZRQlK-5k+Ezb<7ey{4k1OsHh)_jrrTKrP)#EB`9_)?!S-J?{Umv9+*rWWx z{T9^5sCdwL`hZC5=Unb$#AC8&Atk*3N-QaO7$t;zFHm8!p;Z4zS$xh1dGfzE_sE&j zne&7zVb^Zc<(qxxP|*f%f{3s*@kTLpA@`A6zNUh+T$2wBiT9C?$6;k1Mk@a)f+(_bz(mRW7z+YdLnut$<1kAtk1Z+UcWZ$HbTE#rR#kN%;G4 ztRPny3?AuDMt=^gs6~7mkR*s3`6aW%=|lSw$aLmmu6z1!v6AZ^?c3dFpz$kHXtlvv z*iI#Vz(3rZynXREu^elKm9jlB#fC27xW;DC{q|iBTVP2ZIZ>)JCP@eOBiE5f_j6zWx@Z}vfuKCF?okMLesvh zqN88CSmjyKf{6`&Xo$`v)s!C@NawT&8DIMrG3*M``aO39Bbf$JUD7;B@sxX9Z{Gx{ zN;>GDy}MIbIZ?#F3fwR7R^G(btRSH$_Z5`o79T||js%DoT-XRY76kUd7fQEmzBgY zyX>k(Hw&#mxFbgS`?(`z@GEKNZ>JBvENUD7d6zeTy)B2qXLLf2sdBPI#s>T}#y~fI z`yqJpf+p-m+k}>|#+lKK8pGwL4H0==XNU|1ZR(n14~wh#DGXdNWM=PPfs4)r;FH77 zQ19zY`L-=5Gz(D#3S6vD^C4l(ouPhi#fHyJSX6;%)v5XH?Do(6X{nd)%8An&n@TXW z?ueHBR9z*^M_KCn4p|8-EQml7?hR<_@3r7%QViL=_w)IA(+}vgL3h|kX1|H=g?)UQ zW-5cr`9n+n*NePooE9tOS80DBl)U^!j(BwaYjlHaD4DFdM6CG3g5B`iRb4@4t&YZ= zc5Sst2{rm@r{1~~C1})KU+QaRu<%nvuUuM+Sm>N`R`GA=eKEW#8_BH^5uQ$um`33j zdg+g)+@c|dIx!^8oj;#0pbyE?ReFWg1L8i`yW$J>wagda9P*Y4Yi#8zN}5pRuE~PR zo6j}uPlS@HZ?=NN_7CY#dIEBo+b`I=^aMIA?ZQ7(cI6%~F9JF`!Ya&ki5j$>lxqH8{RgO2J6>04w57goK22w|mLclRuZ6MNcgcnq!K_2kH|fWCLcHqaZK|?FnIX|+ zF=B9vT=i?1uQOO6f4{3jG(%khQQNVFzL?~OPCa}S_U--*9Zs}?qg|efufr_Ax338e z2H&G?sprs{mZ3^dLjAQXD@3IFHHIT~dFo|CtGKOmHr+gXT=nRF3wruOv5chYB=zR? z5u9$lfgQY8Dfxa*Ra+ydl|7X^Pg3G?8#Y;_^ZNZ1lF=q2S z=8)SX-hyL4c2Q~{?uuO4kL&_U8!n27ME3Av%)MbX*cs`|o@Mb)nEK(FQQ}N*r@6)_{of zx=9$ln%cA`VA^=RPy@teJgvow3t4aT7ef__lEM^ z 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%0A&o$=C7VSf$=ZTcxe2~FHq>c3!=+}y6 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::post_process // handle time frame definitions etc // If num_events_to_store == 0 && frame_definition_filename.size == 0 - if(num_events_to_store==0 && frame_defs_filename.size() == 0) + if(this->num_events_to_store==0 && this->frame_defs_filename.size() == 0) do_time_frame = true; this->list_mode_data_sptr= diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx index f938408c7f..01cab96b11 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx @@ -425,13 +425,11 @@ compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, if(record.is_time() && end_time > 0.01) { current_time = record.time().get_time_in_secs(); + if (this->do_time_frame && current_time >= end_time) + break; // get out of while loop + if (current_time < start_time) + continue; } - if (this->do_time_frame && current_time >= end_time) - { - break; // get out of while loop - } - if (current_time < start_time) - continue; if (record.is_event() && record.event().is_prompt()) { @@ -439,6 +437,8 @@ compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, measured_bin.set_bin_value(1.0f); record.event().get_bin(measured_bin, *proj_data_info_cyl_uncompressed_ptr); + if (std::abs(measured_bin.segment_num()) > this->max_ring_difference_num_to_process ) + continue; // If more than 1 subsets, check if the current bin belongs to // the current. if (this->num_subsets > 1) From 16f72e7195614089a0a89eece14e20e548e66678 Mon Sep 17 00:00:00 2001 From: Nikos E Date: Fri, 14 Oct 2016 11:38:46 +0100 Subject: [PATCH 011/170] Minor improvements and code clean out. --- recon_test_pack/OSMAPOSL_test_lmf.par | 4 +-- recon_test_pack/lm_to_projdata.par | 2 +- ...MeanAndListModeDataWithProjMatrixByBin.cxx | 31 ++++++++++++------- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/recon_test_pack/OSMAPOSL_test_lmf.par b/recon_test_pack/OSMAPOSL_test_lmf.par index 725b4a44fc..423b5a6c6c 100755 --- a/recon_test_pack/OSMAPOSL_test_lmf.par +++ b/recon_test_pack/OSMAPOSL_test_lmf.par @@ -30,7 +30,7 @@ PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin Par end PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin Parameters:= enforce initial positivity condition:= 1 number of subsets:= 1 -number of subiterations:= 2 +number of subiterations:= 1 save estimates at subiteration intervals:= 1 -output filename prefix := my_ouput_t_lm_pr_seg2 +output filename prefix := my_output_t_lm_pr_seg2 END := diff --git a/recon_test_pack/lm_to_projdata.par b/recon_test_pack/lm_to_projdata.par index d55fd474ae..1fd25c064d 100755 --- a/recon_test_pack/lm_to_projdata.par +++ b/recon_test_pack/lm_to_projdata.par @@ -18,7 +18,7 @@ output filename prefix := ${OUT_PROJDATA_FILE} ; note that this normally counts the total of prompts-delayeds (see below) ; If you don't define a time frame definition file nor the num_events_to_store ; then the total number of events in the listmode file is used. -;num_events_to_store := 200 +; num_events_to_store := 10 ; parameters relating to prompts and delayeds diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx index 01cab96b11..479afacb25 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx @@ -286,16 +286,16 @@ PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBinget_max_num_non_arccorrected_bins(), false))); + // If Additive is smaller : Error + if ( this->normalisation_sptr->set_up(proj_data_info_cyl_uncompressed_ptr) - != Succeeded::yes) + == Succeeded::no) { warning("PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin: " "set-up of pre-normalisation failed\n"); return true; } - - return false; } @@ -306,8 +306,8 @@ PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBinmax_ring_difference_num_to_process; - const int max_segment_num = this->max_ring_difference_num_to_process; + const int min_segment_num = this->proj_data_info_cyl_uncompressed_ptr->get_min_segment_num(); + const int max_segment_num = this->proj_data_info_cyl_uncompressed_ptr->get_max_segment_num(); // warning: has to be same as subset scheme used as in distributable_computation for (int segment_num = min_segment_num; segment_num <= max_segment_num; ++segment_num) @@ -322,7 +322,6 @@ add_subset_sensitivity(TargetT& sensitivity, const int subset_num) const continue; this->add_view_seg_to_sensitivity(sensitivity, view_segment_num); } - // cerr< this->max_ring_difference_num_to_process ) + record.event().get_bin(measured_bin, *this->proj_data_info_cyl_uncompressed_ptr); + + if (measured_bin.get_bin_value() != 1.0f + || measured_bin.segment_num() < this->proj_data_info_cyl_uncompressed_ptr->get_min_segment_num() + || measured_bin.segment_num() > this->proj_data_info_cyl_uncompressed_ptr->get_max_segment_num() + || measured_bin.tangential_pos_num() < this->proj_data_info_cyl_uncompressed_ptr->get_min_tangential_pos_num() + || measured_bin.tangential_pos_num() > this->proj_data_info_cyl_uncompressed_ptr->get_max_tangential_pos_num() + || measured_bin.axial_pos_num() < this->proj_data_info_cyl_uncompressed_ptr->get_min_axial_pos_num(measured_bin.segment_num()) + || measured_bin.axial_pos_num() > this->proj_data_info_cyl_uncompressed_ptr->get_max_axial_pos_num(measured_bin.segment_num())) + { continue; + } + + measured_bin.set_bin_value(1.0f); // If more than 1 subsets, check if the current bin belongs to // the current. if (this->num_subsets > 1) @@ -472,8 +481,8 @@ compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, num_stored_events += 1; - if (num_stored_events%100000L==0) - info( boost::format("Proccessed events: %1% ") % num_stored_events); + if (num_stored_events%200000L==0) + info( boost::format("Stored Events: %1% ") % num_stored_events); if ( measured_bin.get_bin_value() <= max_quotient *fwd_bin.get_bin_value()) measured_div_fwd = 1.0f /fwd_bin.get_bin_value(); From 1aa85c18e0abf5d9663091c988f37462ee804de8 Mon Sep 17 00:00:00 2001 From: Nikos E Date: Fri, 14 Oct 2016 11:54:25 +0100 Subject: [PATCH 012/170] Corrected bug in the first and last plane. --- recon_test_pack/OSMAPOSL_test_proj.par | 2 +- ...inearModelForMeanAndListModeDataWithProjMatrixByBin.cxx | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/recon_test_pack/OSMAPOSL_test_proj.par b/recon_test_pack/OSMAPOSL_test_proj.par index a30596a108..358cc04a18 100755 --- a/recon_test_pack/OSMAPOSL_test_proj.par +++ b/recon_test_pack/OSMAPOSL_test_proj.par @@ -16,7 +16,7 @@ PoissonLogLikelihoodWithLinearModelForMeanAndProjData Parameters:= End Ray tracing matrix parameters := End Projector Pair Using Matrix Parameters := -Bin Normalisation type := From ProjData + Bin Normalisation type := From ProjData Bin Normalisation From ProjData := normalisation projdata filename:= total_mult.hs End Bin Normalisation From ProjData:= diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx index 479afacb25..d9292a3987 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx @@ -345,13 +345,10 @@ add_view_seg_to_sensitivity(TargetT& sensitivity, const ViewSegmentNumbers& view } // backproject { - const int range_to_zero = - view_seg_nums.segment_num() == 0 - ? 1 : 0; const int min_ax_pos_num = - viewgrams.get_min_axial_pos_num() + range_to_zero; + viewgrams.get_min_axial_pos_num(); const int max_ax_pos_num = - viewgrams.get_max_axial_pos_num() - range_to_zero; + viewgrams.get_max_axial_pos_num(); this->projector_pair_ptr->get_back_projector_sptr()-> back_project(sensitivity, viewgrams, From fd5ef3bd7bccd1babe15907d2fa17e563b5e119a Mon Sep 17 00:00:00 2001 From: Nikos E Date: Fri, 14 Oct 2016 12:58:18 +0100 Subject: [PATCH 013/170] Finished the test for listmode reconstruction. :) --- recon_test_pack/run_test_listmode_recon.sh | 112 +++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100755 recon_test_pack/run_test_listmode_recon.sh diff --git a/recon_test_pack/run_test_listmode_recon.sh b/recon_test_pack/run_test_listmode_recon.sh new file mode 100755 index 0000000000..7466b5abce --- /dev/null +++ b/recon_test_pack/run_test_listmode_recon.sh @@ -0,0 +1,112 @@ +#! /bin/sh +# A script to check to see if reconstruction of simulated data gives the expected result. +# +# Copyright (C) 2011 - 2011-01-14, Hammersmith Imanet Ltd +# Copyright (C) 2011-07-01 - 2011, Kris Thielemans +# Copyright (C) 2014, University College London +# This file is part of STIR. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# This file is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# See STIR/LICENSE.txt for details +# +# Author Nikos Efthimiou +# + +# Scripts should exit with error code when a test fails: +if [ -n "$TRAVIS" ]; then + # The code runs inside Travis + set -e +fi + +echo This script should work with STIR version ">"3.0. If you have +echo a later version, you might have to update your test pack. +echo Please check the web site. +echo + +# +# Options +# +MPIRUN="" + +# +# Parse option arguments (--) +# Note that the -- is required to suppress interpretation of $1 as options +# to expr +# +while test `expr -- "$1" : "--.*"` -gt 0 +do + + if test "$1" = "--mpicmd" + then + MPIRUN="$2" + shift 1 + elif test "$1" = "--help" + then + echo "Usage: `basename $0` [--mpicmd somecmd] [install_dir]" + echo "(where [] means that an argument is optional)" + echo "See README.txt for more info." + exit 1 + else + echo Warning: Unknown option "$1" + echo rerun with --help for more info. + exit 1 + fi + + shift 1 + +done + +if [ $# -eq 1 ]; then + echo "Prepending $1 to your PATH for the duration of this script." + PATH=$1:$PATH +fi + +# first delete any files remaining from a previous run +rm -f my_*v my_*s my_*S + +echo "=== reconstruct listmode data" +OSMAPOSL OSMAPOSL_test_lmf.par +echo "=== " +# create sinograms +echo "=== unlist listmode data (for comparison)" +INPUT=PET_ACQ_small.l.hdr.STIR TEMPLATE=Siemens_mMR_seg2.hs OUT_PROJDATA_FILE=my_sinogram lm_to_projdata lm_to_projdata.par +echo "=== reconstruct projection data for comparison" +OSMAPOSL OSMAPOSL_test_proj.par +echo "=== compare sensitivity images" +if ${INSTALL_DIR}compare_image my_sens_t_proj_seg2.hv my_sens_t_lm_pr_seg2.hv 2>my_sens_comparison_stderr.log; +then +echo ---- This test seems to be ok !; +else +echo There were problems here!; +ThereWereErrors=1; +fi + +echo "=== compare reconstructed images" +if ${INSTALL_DIR}compare_image my_output_t_proj_seg2_1.hv my_output_t_lm_pr_seg2_1.hv 2>my_output_comparison_stderr.log; +then +echo ---- This test seems to be ok !; +else +echo There were problems here!; +ThereWereErrors=1; +fi + +echo +echo '--------------- End of tests -------------' +echo +if test ${ThereWereErrors} = 1 ; +then +echo "Check what went wrong. The *.log files might help you." +else +echo "Everything seems to be fine !" +echo 'You could remove all generated files using "rm -f my_* *.log"' +fi + From cde4c01619c71d9f85b666e77e306c4d67a563c1 Mon Sep 17 00:00:00 2001 From: Nikos E Date: Fri, 14 Oct 2016 14:34:42 +0100 Subject: [PATCH 014/170] Removed large file. From 9aa012737ed8d1311bb706eab5e58b04325782cb Mon Sep 17 00:00:00 2001 From: Nikos E Date: Fri, 14 Oct 2016 15:52:29 +0100 Subject: [PATCH 015/170] Applied Kris's comments: [x]. ProjDataFromStream: Only get_bin_value(Bin) left. [x]. ProjData.h get_bin_value was removed [x]. ProjDataGEAdvance.h get_bin_value() was removed [x]. CListModeDataECAT8_32.h reverted. [x]. ProjMatrixByBinSPECTUB reverted. [x]. ProjData.cxx : get_related_bin_values commented out [x]. --- documentation/release_3.1.htm | 1 + recon_test_pack/OSMAPOSL_test_lmf.par | 9 ++-- recon_test_pack/OSMAPOSL_test_proj.par | 9 ++-- recon_test_pack/total_mult.hs | 47 ------------------- src/buildblock/ProjData.cxx | 27 +++++------ src/buildblock/ProjDataFromStream.cxx | 26 +++++----- src/include/stir/Bin.h | 4 +- src/include/stir/ProjData.h | 14 +++--- src/include/stir/ProjDataFromStream.h | 16 +------ src/include/stir/ProjDataGEAdvance.h | 16 ++----- .../stir/listmode/CListModeDataECAT8_32bit.h | 10 ++-- .../ProjMatrixByBinSPECTUB.cxx | 18 +++---- 12 files changed, 62 insertions(+), 135 deletions(-) delete mode 100644 recon_test_pack/total_mult.hs diff --git a/documentation/release_3.1.htm b/documentation/release_3.1.htm index b2eefdcb2e..ae2633c4fb 100644 --- a/documentation/release_3.1.htm +++ b/documentation/release_3.1.htm @@ -147,6 +147,7 @@

    Known problems

    Minor bug fixes

    • fix HighResWallClockTimer on Linux and Win32 which caused reporting wrong timings in certain situations
    • +
    • In ProjMatrixByBin.h the name of DataSymmetriesForBins is corrected to symmetries_sptr.

    Documentation changes

    diff --git a/recon_test_pack/OSMAPOSL_test_lmf.par b/recon_test_pack/OSMAPOSL_test_lmf.par index 423b5a6c6c..a9d7b103f9 100755 --- a/recon_test_pack/OSMAPOSL_test_lmf.par +++ b/recon_test_pack/OSMAPOSL_test_lmf.par @@ -16,10 +16,11 @@ PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin Par End Ray tracing matrix parameters := End Projector Pair Using Matrix Parameters := - Bin Normalisation type := From ProjData - Bin Normalisation From ProjData := - normalisation projdata filename:= total_mult.hs - End Bin Normalisation From ProjData:= + ; You may download this file separately, from STIR website + ;Bin Normalisation type := From ProjData + ; Bin Normalisation From ProjData := + ; normalisation projdata filename:= total_mult.hs + ;End Bin Normalisation From ProjData:= ;num_events_to_store := 100 recompute sensitivity :=1 diff --git a/recon_test_pack/OSMAPOSL_test_proj.par b/recon_test_pack/OSMAPOSL_test_proj.par index 358cc04a18..1fe49b0c9b 100755 --- a/recon_test_pack/OSMAPOSL_test_proj.par +++ b/recon_test_pack/OSMAPOSL_test_proj.par @@ -16,10 +16,11 @@ PoissonLogLikelihoodWithLinearModelForMeanAndProjData Parameters:= End Ray tracing matrix parameters := End Projector Pair Using Matrix Parameters := - Bin Normalisation type := From ProjData - Bin Normalisation From ProjData := - normalisation projdata filename:= total_mult.hs - End Bin Normalisation From ProjData:= +; You may download this file separately, from STIR website + ;Bin Normalisation type := From ProjData + ; Bin Normalisation From ProjData := + ; normalisation projdata filename:= total_mult.hs + ;End Bin Normalisation From ProjData:= recompute sensitivity := 1 use subset sensitivities:= 0 diff --git a/recon_test_pack/total_mult.hs b/recon_test_pack/total_mult.hs deleted file mode 100644 index 6c03e62dac..0000000000 --- a/recon_test_pack/total_mult.hs +++ /dev/null @@ -1,47 +0,0 @@ -!INTERFILE := -!imaging modality := PT -name of data file := total_mult.s -originating system := Siemens mMR -!version of keys := STIR3.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 := 4 -matrix axis label [4] := segment -!matrix size [4] := 5 -matrix axis label [3] := view -!matrix size [3] := 252 -matrix axis label [2] := axial coordinate -!matrix size [2] := { 62,63,64,63,62} -matrix axis label [1] := tangential coordinate -!matrix size [1] := 344 -minimum ring difference per segment := { -2,-1,0,1,2} -maximum ring difference per segment := { -2,-1,0,1,2} -Scanner parameters:= -Scanner type := Siemens mMR -Number of rings := 64 -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 := 2 -Number of crystals per block in axial direction := 8 -Number of crystals per block in transaxial direction := 9 -Number of detector layers := 1 -Number of crystals per singles unit in axial direction := 16 -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 -!END OF INTERFILE := diff --git a/src/buildblock/ProjData.cxx b/src/buildblock/ProjData.cxx index 8d411b68ac..1eea479fa7 100644 --- a/src/buildblock/ProjData.cxx +++ b/src/buildblock/ProjData.cxx @@ -289,24 +289,21 @@ ProjData::get_related_viewgrams(const ViewSegmentNumbers& view_segmnet_num, return RelatedViewgrams(viewgrams, symmetries_used); } -std::vector -ProjData::get_related_bin_values(const std::vector& r_bins) const -{ +//std::vector +//ProjData::get_related_bin_values(const std::vector& r_bins) const +//{ - std::vector values; - values.reserve(r_bins.size()); +// 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).segment_num(), - (*r_bins_iterator).axial_pos_num(), - (*r_bins_iterator).view_num(), - (*r_bins_iterator).tangential_pos_num())); - } +// 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; -} +// return values; +//} Succeeded diff --git a/src/buildblock/ProjDataFromStream.cxx b/src/buildblock/ProjDataFromStream.cxx index 8020a24aac..c42d34806f 100644 --- a/src/buildblock/ProjDataFromStream.cxx +++ b/src/buildblock/ProjDataFromStream.cxx @@ -194,21 +194,18 @@ ProjDataFromStream::get_viewgram(const int view_num, const int segment_num, return viewgram; } -float -ProjDataFromStream::get_bin_value(const Bin& this_bin) const -{ - return get_bin_value(this_bin.segment_num(), - this_bin.axial_pos_num(), - this_bin.view_num(), - this_bin.tangential_pos_num()); -} +//float +//ProjDataFromStream::get_bin_value() const +//{ +// return get_bin_value(this_bin.segment_num(), +// this_bin.axial_pos_num(), +// this_bin.view_num(), +// this_bin.tangential_pos_num()); +//} float -ProjDataFromStream::get_bin_value(const int segment_num, - const int axial_pos_num, - const int view_num, - const int tang_pos_num) const +ProjDataFromStream::get_bin_value(const Bin& this_bin) const { if (sino_stream == 0) { @@ -219,7 +216,8 @@ ProjDataFromStream::get_bin_value(const int segment_num, error("ProjDataFromStream::get_viewgram: error in stream state before reading\n"); } - vector offsets = get_offsets_bin(segment_num, axial_pos_num, view_num, tang_pos_num); + vector offsets = get_offsets_bin(this_bin.segment_num(), this_bin.axial_pos_num(), + this_bin.view_num(), this_bin.tangential_pos_num()); const streamoff total_offset = offsets[0]; @@ -228,7 +226,7 @@ ProjDataFromStream::get_bin_value(const int segment_num, if (! *sino_stream) { - error("ProjDataFromStream::get_viewgram: error after seekg\n"); + error("ProjDataFromStream::get_bin_value: error after seekg."); } Array< 1, float> value(1); diff --git a/src/include/stir/Bin.h b/src/include/stir/Bin.h index 0233bf18cc..b9e445653e 100644 --- a/src/include/stir/Bin.h +++ b/src/include/stir/Bin.h @@ -79,9 +79,9 @@ class Bin //! accumulate voxel's contribution during forward projection inline Bin& operator+=(const float dx); - //! multiply bin values during normalisation -- apply() + //! multiply bin values inline Bin& operator*=(const float dx); - //! divide bin values during normalisation -- undo() + //! divide bin values //! \todo It is zero division proof in a similar way to divide<,,>(), though I am //! not sure if it should be. inline Bin& operator/=(const float dx); diff --git a/src/include/stir/ProjData.h b/src/include/stir/ProjData.h index 3701251cb0..5f61c0cb4f 100644 --- a/src/include/stir/ProjData.h +++ b/src/include/stir/ProjData.h @@ -36,7 +36,7 @@ #include "stir/Succeeded.h" #include "stir/SegmentBySinogram.h" #include "stir/SegmentByView.h" -#include "stir/Bin.h" + //#include #include "stir/IO/ExamData.h" @@ -152,10 +152,8 @@ class ProjData : public ExamData //! Set sinogram virtual Succeeded set_sinogram(const Sinogram&) = 0; - //! Get Bin value - virtual float get_bin_value(const int view_pos, const int segment_pos, const int axial_pos, const int tang_pos) const = 0; - //! Get Bin value - virtual float get_bin_value(const Bin& this_bin) const = 0; + // //! Get Bin value + //virtual float get_bin_value(const Bin& this_bin) const = 0; //! Get empty viewgram Viewgram get_empty_viewgram(const int view, const int segment_num, @@ -196,9 +194,9 @@ class ProjData : public ExamData const bool make_num_tangential_poss_odd = false) const; //! Set related viewgrams virtual Succeeded set_related_viewgrams(const RelatedViewgrams& viewgrams); - //! Get related bin values - //! \todo This function temporaliry has as input a vector instead this should be replaced by RelatedBins. - std::vector get_related_bin_values(const std::vector&) const; +// //! Get related bin values +// //! \todo This function temporaliry has as input a vector instead this should be replaced by RelatedBins. +// std::vector get_related_bin_values(const std::vector&) const; //! Get empty related viewgrams, where the symmetries_ptr specifies the symmetries to use RelatedViewgrams diff --git a/src/include/stir/ProjDataFromStream.h b/src/include/stir/ProjDataFromStream.h index a333521ba3..d3a4cbf97c 100644 --- a/src/include/stir/ProjDataFromStream.h +++ b/src/include/stir/ProjDataFromStream.h @@ -37,7 +37,7 @@ #include "stir/NumericType.h" #include "stir/ByteOrder.h" #include "stir/shared_ptr.h" - +#include "stir/Bin.h" #include #include @@ -138,20 +138,6 @@ class ProjDataFromStream : public ProjData //! Get scale factor float get_scale_factor() const; - //! - //! \brief get_bin_value - //! \param segment_num - //! \param axial_pos_num - //! \param view_num - //! \param tang_pos_num - //! \return - //! \author Nikos Efthimiou - //! \details This function return the value of a single bin stored in a sinogram in the - //! disk. - float get_bin_value(const int segment_num, - const int axial_pos_num, - const int view_num, - const int tang_pos_num) const; //! //! \brief get_bin_value diff --git a/src/include/stir/ProjDataGEAdvance.h b/src/include/stir/ProjDataGEAdvance.h index e6931b40bd..6901db3806 100644 --- a/src/include/stir/ProjDataGEAdvance.h +++ b/src/include/stir/ProjDataGEAdvance.h @@ -73,18 +73,10 @@ class ProjDataGEAdvance : public ProjData Sinogram get_sinogram(const int ax_pos_num, const int sergment_num,const bool make_num_tangential_poss_odd=false) const; Succeeded set_sinogram(const Sinogram& s); - float get_bin_value(const int segment_num, - const int axial_pos_num, - const int view_num, - const int tang_pos_num) const - { - //Do nothing - } - - float get_bin_value(const Bin& this_bin) const - { - // Do nothing - } +// float get_bin_value(const Bin& this_bin) const +// { +// // Do nothing +// } private: //the file with the data //This has to be a reference (or pointer) to a stream, diff --git a/src/include/stir/listmode/CListModeDataECAT8_32bit.h b/src/include/stir/listmode/CListModeDataECAT8_32bit.h index 6a6fa9e805..7feae1741d 100644 --- a/src/include/stir/listmode/CListModeDataECAT8_32bit.h +++ b/src/include/stir/listmode/CListModeDataECAT8_32bit.h @@ -18,7 +18,7 @@ \file \ingroup listmode \brief Declaration of class stir::ecat::CListModeDataECAT8_32bit - + \author Kris Thielemans */ @@ -40,7 +40,7 @@ namespace ecat { //! A class that reads the listmode data for Siemens scanners /*! \ingroup listmode - This file format is currently used by the Siemens Biograph PET/CT and mMR scanners. + This file format is currently used by the Siemens Biograph PET/CT and mMR scanners. There's an Interfile-like header and a binary file with the actual list mode data. The name of the binary file is given by the value of the "name of data file" keyword in the header. @@ -57,13 +57,13 @@ class CListModeDataECAT8_32bit : public CListModeData virtual std::string get_name() const; - virtual + virtual shared_ptr get_empty_record_sptr() const; - virtual + virtual Succeeded get_next_record(CListRecord& record) const; - virtual + virtual Succeeded reset(); virtual diff --git a/src/recon_buildblock/ProjMatrixByBinSPECTUB.cxx b/src/recon_buildblock/ProjMatrixByBinSPECTUB.cxx index 1dd5948193..833f2b89a5 100644 --- a/src/recon_buildblock/ProjMatrixByBinSPECTUB.cxx +++ b/src/recon_buildblock/ProjMatrixByBinSPECTUB.cxx @@ -205,7 +205,7 @@ set_up( //... fill prj structure from projection data info prj.Nbin = this->proj_data_info_ptr->get_num_tangential_poss(); - prj.szcm = this->proj_data_info_ptr->get_scanner_ptr()->get_default_bin_size()/10.f; + prj.szcm = this->proj_data_info_ptr->get_scanner_ptr()->get_default_bin_size()/10.; prj.Nang = this->proj_data_info_ptr->get_num_views(); @@ -213,24 +213,24 @@ set_up( vol.Ncol = image_info_ptr->get_x_size(); // Image: number of columns vol.Nrow = image_info_ptr->get_y_size(); // Image: number of rows vol.Nsli = image_info_ptr->get_z_size(); // Image: and projections: number of slices - vol.szcm = image_info_ptr->get_voxel_size().x()/10.f; // Image: voxel size (cm) - vol.thcm = image_info_ptr->get_voxel_size().z()/10.f; // Image: slice thickness (cm) + vol.szcm = image_info_ptr->get_voxel_size().x()/10.; // Image: voxel size (cm) + vol.thcm = image_info_ptr->get_voxel_size().z()/10.; // Image: slice thickness (cm) //..... geometrical and other derived parameters of the volume structure............... vol.Npix = vol.Ncol * vol.Nrow; vol.Nvox = vol.Npix * vol.Nsli; - vol.Ncold2 = (float)vol.Ncol / (float)2.f; // half of the matrix Nvox (integer or semi-integer) - vol.Nrowd2 = (float)vol.Nrow / (float)2.f; // half of the matrix NbOS (integer or semi-integer) - vol.Nslid2 = (float)vol.Nsli / (float)2.f; // half of the number of slices (integer or semi-integer) + vol.Ncold2 = (float)vol.Ncol / (float)2.; // half of the matrix Nvox (integer or semi-integer) + vol.Nrowd2 = (float)vol.Nrow / (float)2.; // half of the matrix NbOS (integer or semi-integer) + vol.Nslid2 = (float)vol.Nsli / (float)2.; // half of the number of slices (integer or semi-integer) vol.Xcmd2 = vol.Ncold2 * vol.szcm; // Half of the size of the image volume, dimension x (cm); vol.Ycmd2 = vol.Nrowd2 * vol.szcm; // Half of the size of the image volume, dimension y (cm); vol.Zcmd2 = vol.Nslid2 * vol.thcm; // Half of the size of the image volume, dimension z (cm); - vol.x0 = ( (float)0.5f - vol.Ncold2) * vol.szcm; // coordinate x of the first voxel - vol.y0 = ( (float)0.5f - vol.Nrowd2) * vol.szcm; // coordinate y of the first voxel - vol.z0 = ( (float)0.5f - vol.Nslid2) * vol.thcm; // coordinate z of the first voxel + vol.x0 = ( (float)0.5 - vol.Ncold2) * vol.szcm; // coordinate x of the first voxel + vol.y0 = ( (float)0.5 - vol.Nrowd2) * vol.szcm; // coordinate y of the first voxel + vol.z0 = ( (float)0.5 - vol.Nslid2) * vol.thcm; // coordinate z of the first voxel vol.first_sl = 0; // Image: first slice to take into account (no weight bellow) vol.last_sl = vol.Nsli; // Image: last slice to take into account (no weights above) From fe2cae090e6a9c1eec05bec05ce8b2f512e3c1e1 Mon Sep 17 00:00:00 2001 From: Nikos E Date: Fri, 14 Oct 2016 16:22:41 +0100 Subject: [PATCH 016/170] run_test_listmode would perform two iterations. Not really usefull. --- recon_test_pack/OSMAPOSL_test_proj.par | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/recon_test_pack/OSMAPOSL_test_proj.par b/recon_test_pack/OSMAPOSL_test_proj.par index 1fe49b0c9b..9e49ad1c38 100755 --- a/recon_test_pack/OSMAPOSL_test_proj.par +++ b/recon_test_pack/OSMAPOSL_test_proj.par @@ -30,7 +30,7 @@ PoissonLogLikelihoodWithLinearModelForMeanAndProjData Parameters:= end PoissonLogLikelihoodWithLinearModelForMeanAndProjData Parameters:= enforce initial positivity condition:= 1 number of subsets:= 1 -number of subiterations:= 2 +number of subiterations:= 1 save estimates at subiteration intervals:= 1 output filename prefix := my_output_t_proj_seg2 END := From f6a904518f8ebd30bfe72dca5c3cd6b93cfd925e Mon Sep 17 00:00:00 2001 From: Nikos E Date: Fri, 14 Oct 2016 23:20:44 +0100 Subject: [PATCH 017/170] additive sinogram added in the tests. The test script computes a background sinogram. The lm-objective function checks if the additive projdata are compatible. --- recon_test_pack/OSMAPOSL_test_lmf.par | 2 ++ recon_test_pack/OSMAPOSL_test_proj.par | 1 + recon_test_pack/frame_single.fdef | 2 ++ recon_test_pack/lm_fansums_delayed.par | 10 ++++++ recon_test_pack/run_test_listmode_recon.sh | 16 +++++++++ ...MeanAndListModeDataWithProjMatrixByBin.cxx | 35 +++++++++++++++++-- 6 files changed, 64 insertions(+), 2 deletions(-) create mode 100755 recon_test_pack/frame_single.fdef create mode 100644 recon_test_pack/lm_fansums_delayed.par diff --git a/recon_test_pack/OSMAPOSL_test_lmf.par b/recon_test_pack/OSMAPOSL_test_lmf.par index a9d7b103f9..8c014efddf 100755 --- a/recon_test_pack/OSMAPOSL_test_lmf.par +++ b/recon_test_pack/OSMAPOSL_test_lmf.par @@ -28,6 +28,8 @@ PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin Par 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 diff --git a/recon_test_pack/OSMAPOSL_test_proj.par b/recon_test_pack/OSMAPOSL_test_proj.par index 9e49ad1c38..8c0bb0f774 100755 --- a/recon_test_pack/OSMAPOSL_test_proj.par +++ b/recon_test_pack/OSMAPOSL_test_proj.par @@ -27,6 +27,7 @@ PoissonLogLikelihoodWithLinearModelForMeanAndProjData Parameters:= sensitivity filename:= my_sens_t_proj_seg2.hv zoom := 1 +additive sinogram := my_MLrandoms_f1.hs end PoissonLogLikelihoodWithLinearModelForMeanAndProjData Parameters:= enforce initial positivity condition:= 1 number of subsets:= 1 diff --git a/recon_test_pack/frame_single.fdef b/recon_test_pack/frame_single.fdef new file mode 100755 index 0000000000..bb04dd5a98 --- /dev/null +++ b/recon_test_pack/frame_single.fdef @@ -0,0 +1,2 @@ +0 0 +1 1 diff --git a/recon_test_pack/lm_fansums_delayed.par b/recon_test_pack/lm_fansums_delayed.par new file mode 100644 index 0000000000..5743960dff --- /dev/null +++ b/recon_test_pack/lm_fansums_delayed.par @@ -0,0 +1,10 @@ +lm_fansums Parameters:= +input file := ${INPUT} +frame definition file := frame_single.fdef +output filename prefix := ${OUTPUT} +tangential fan size := -1 +maximum absolute segment number to process := +store 'prompts' := 0 +increment to use for 'delayeds' := 1 +list event coordinates := 0 +end:= diff --git a/recon_test_pack/run_test_listmode_recon.sh b/recon_test_pack/run_test_listmode_recon.sh index 7466b5abce..d9fefac834 100755 --- a/recon_test_pack/run_test_listmode_recon.sh +++ b/recon_test_pack/run_test_listmode_recon.sh @@ -73,6 +73,22 @@ fi # first delete any files remaining from a previous run rm -f my_*v my_*s my_*S +echo "=== calculate backround data" +echo "===== randoms" + +echo "====== create delayed fansums" +INPUT=PET_ACQ_small.l.hdr.STIR OUTPUT=my_fansums_delayed ${INSTALL_DIR}lm_fansums lm_fansums_delayed.par 2>my_fansums.log + +echo "====== estimate singles from fansums" +niters=10 +# Note: the last 2 numbers are specific to the mMR +${INSTALL_DIR}find_ML_singles_from_delayed -f my_MLsingles_f1 my_fansums_delayed_f1.dat $niters 2 343 my_construct_randoms_from_singles.log + +echo "=== simulate normalisation data" + echo "=== reconstruct listmode data" OSMAPOSL OSMAPOSL_test_lmf.par echo "=== " diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx index d9292a3987..01e1c5cd85 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx @@ -286,9 +286,40 @@ PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBinget_max_num_non_arccorrected_bins(), false))); - // If Additive is smaller : Error + if (*(this->additive_proj_data_sptr->get_proj_data_info_sptr()) != *proj_data_info_cyl_uncompressed_ptr) + { + const ProjDataInfo& add_proj = *(this->additive_proj_data_sptr->get_proj_data_info_sptr()); + const ProjDataInfo& proj = *this->proj_data_info_cyl_uncompressed_ptr; + bool ok = + typeid(add_proj) == typeid(proj) && + *add_proj.get_scanner_ptr()== *(proj.get_scanner_ptr()) && + (add_proj.get_min_view_num()==proj.get_min_view_num()) && + (add_proj.get_max_view_num()==proj.get_max_view_num()) && + (add_proj.get_min_tangential_pos_num() ==proj.get_min_tangential_pos_num())&& + (add_proj.get_max_tangential_pos_num() ==proj.get_max_tangential_pos_num()) && + add_proj.get_min_segment_num() <= proj.get_min_segment_num() && + add_proj.get_max_segment_num() >= proj.get_max_segment_num(); + + for (int segment_num=proj.get_min_segment_num(); + ok && segment_num<=proj.get_max_segment_num(); + ++segment_num) + { + ok = + add_proj.get_min_axial_pos_num(segment_num) == proj.get_min_axial_pos_num(segment_num) && + add_proj.get_max_axial_pos_num(segment_num) == proj.get_max_axial_pos_num(segment_num); + } + if (ok) + return false; + else + { + warning(boost::format("Incompatible additive projection data:\nAdditive projdata info:\n%s\nEmission projdata info:\n%s\n--- (end of incompatible projection data info)---\n") + % add_proj.parameter_info() + % proj.parameter_info()); + return true; + } + } - if ( this->normalisation_sptr->set_up(proj_data_info_cyl_uncompressed_ptr) + if( this->normalisation_sptr->set_up(proj_data_info_cyl_uncompressed_ptr) == Succeeded::no) { warning("PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin: " From 01c8aa7cf3e9aadc1c58f9c50d4dccf20c32211c Mon Sep 17 00:00:00 2001 From: Nikos E Date: Fri, 14 Oct 2016 23:24:06 +0100 Subject: [PATCH 018/170] Corrected one small bug --- ...inearModelForMeanAndListModeDataWithProjMatrixByBin.cxx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx index 01e1c5cd85..381e5f6152 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx @@ -308,11 +308,10 @@ PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin Date: Sun, 16 Oct 2016 17:25:11 +0100 Subject: [PATCH 019/170] Tests for listmode reconstruction finished. I added fake normalisation and additive components. In addition, In the run_root_GATE test I changed a bit the way the script detects GATE support, after some complains by users. I hope that is better now. I cleaned up the code in the ProjData FromStream but didn't edit the way get_offsets_bin() works because I see no improvement if it depend or other offset function, or if another offset function depends on that. FInally, I uncomment the use of get_bin_value() in the ProjDataInMemory. --- recon_test_pack/OSMAPOSL_test_lmf.par | 9 ++-- recon_test_pack/OSMAPOSL_test_proj.par | 10 ++-- .../lm_generate_uniform_cylinder.par | 23 +++++++++ recon_test_pack/run_root_GATE.sh | 11 +++-- recon_test_pack/run_test_listmode_recon.sh | 13 +++++ src/buildblock/ProjDataFromStream.cxx | 47 ------------------- src/buildblock/ProjDataInMemory.cxx | 10 ++-- src/include/stir/ProjDataFromStream.h | 20 ++------ src/include/stir/ProjDataInMemory.h | 2 + src/listmode_utilities/lm_to_projdata.cxx | 4 +- 10 files changed, 63 insertions(+), 86 deletions(-) create mode 100644 recon_test_pack/lm_generate_uniform_cylinder.par diff --git a/recon_test_pack/OSMAPOSL_test_lmf.par b/recon_test_pack/OSMAPOSL_test_lmf.par index 8c014efddf..775ae5d047 100755 --- a/recon_test_pack/OSMAPOSL_test_lmf.par +++ b/recon_test_pack/OSMAPOSL_test_lmf.par @@ -16,11 +16,10 @@ PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin Par End Ray tracing matrix parameters := End Projector Pair Using Matrix Parameters := - ; You may download this file separately, from STIR website - ;Bin Normalisation type := From ProjData - ; Bin Normalisation From ProjData := - ; normalisation projdata filename:= total_mult.hs - ;End Bin Normalisation From ProjData:= + 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 diff --git a/recon_test_pack/OSMAPOSL_test_proj.par b/recon_test_pack/OSMAPOSL_test_proj.par index 8c0bb0f774..320642abaf 100755 --- a/recon_test_pack/OSMAPOSL_test_proj.par +++ b/recon_test_pack/OSMAPOSL_test_proj.par @@ -16,11 +16,11 @@ PoissonLogLikelihoodWithLinearModelForMeanAndProjData Parameters:= End Ray tracing matrix parameters := End Projector Pair Using Matrix Parameters := -; You may download this file separately, from STIR website - ;Bin Normalisation type := From ProjData - ; Bin Normalisation From ProjData := - ; normalisation projdata filename:= total_mult.hs - ;End Bin Normalisation From ProjData:= + + Bin Normalisation type := From ProjData + Bin Normalisation From ProjData := + normalisation projdata filename:= my_acfs.hs + End Bin Normalisation From ProjData:= recompute sensitivity := 1 use subset sensitivities:= 0 diff --git a/recon_test_pack/lm_generate_uniform_cylinder.par b/recon_test_pack/lm_generate_uniform_cylinder.par new file mode 100644 index 0000000000..25377d9452 --- /dev/null +++ b/recon_test_pack/lm_generate_uniform_cylinder.par @@ -0,0 +1,23 @@ +generate_image Parameters := +output filename:=my_uniform_cylinder +X output image size (in pixels):=111 +Y output image size (in pixels):=111 +Z output image size (in pixels):=65 +X voxel size (in mm):= 3 +Y voxel size (in mm):= 3 +Z voxel size (in mm) := 0.40625 + + Z number of samples to take per voxel := 1 + Y number of samples to take per voxel := 1 + X number of samples to take per voxel := 1 + +shape type:= ellipsoidal cylinder +Ellipsoidal Cylinder Parameters:= + radius-x (in mm):=100 + radius-y (in mm):=100 + length-z (in mm):=110 + origin (in mm):={70,10,20} + END:= +value :=1 + +END:= diff --git a/recon_test_pack/run_root_GATE.sh b/recon_test_pack/run_root_GATE.sh index 2c0cf8a7c1..8d70cb2b8a 100755 --- a/recon_test_pack/run_root_GATE.sh +++ b/recon_test_pack/run_root_GATE.sh @@ -53,13 +53,17 @@ if test "$1" = "--help" done -if ! ${INSTALL_DIR}lm_to_projdata --input-formats 2>&1 | grep ROOT; then +${INSTALL_DIR}lm_to_projdata --input-formats > my_lm_supported_inputs.log 2>&1 + +if ! grep "ROOT" my_lm_supported_inputs.log > /dev/null +then echo GATE support has not been installed in this system. Aborting. exit 1; fi + echo Executing tests on ROOT files generated by GATE simulations, with cylindrical PET scanners @@ -134,7 +138,4 @@ echo "Check what went wrong. The *.log files might help you." else echo "Everything seems to be fine !" echo 'You could remove all generated files using "rm -f my_* *.log"' -fi - - - +#fi diff --git a/recon_test_pack/run_test_listmode_recon.sh b/recon_test_pack/run_test_listmode_recon.sh index d9fefac834..c5a12564f7 100755 --- a/recon_test_pack/run_test_listmode_recon.sh +++ b/recon_test_pack/run_test_listmode_recon.sh @@ -88,6 +88,19 @@ echo " ===== estimate randoms from singles" ${INSTALL_DIR}construct_randoms_from_singles my_MLrandoms_f1 my_MLsingles_f1 Siemens_mMR_seg2.hs $niters 2>my_construct_randoms_from_singles.log echo "=== simulate normalisation data" +# For normalisation data we are going to use a cylinder in the center, +# with water attenuation values + +echo "=== make fake emission image" +${INSTALL_DIR}generate_image lm_generate_uniform_cylinder.par +echo "=== use that as template for fake attenuation" +${INSTALL_DIR}stir_math --including-first --times-scalar .096 my_atten_image.hv my_uniform_cylinder.hv + +echo "=== create ACFs" +${INSTALL_DIR}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 "=== reconstruct listmode data" OSMAPOSL OSMAPOSL_test_lmf.par diff --git a/src/buildblock/ProjDataFromStream.cxx b/src/buildblock/ProjDataFromStream.cxx index c42d34806f..0e19dee214 100644 --- a/src/buildblock/ProjDataFromStream.cxx +++ b/src/buildblock/ProjDataFromStream.cxx @@ -194,16 +194,6 @@ ProjDataFromStream::get_viewgram(const int view_num, const int segment_num, return viewgram; } -//float -//ProjDataFromStream::get_bin_value() const -//{ -// return get_bin_value(this_bin.segment_num(), -// this_bin.axial_pos_num(), -// this_bin.view_num(), -// this_bin.tangential_pos_num()); -//} - - float ProjDataFromStream::get_bin_value(const Bin& this_bin) const { @@ -230,7 +220,6 @@ ProjDataFromStream::get_bin_value(const Bin& this_bin) const } Array< 1, float> value(1); -// value.resize(1); float scale = float(1); // Now the storage order is not more important. Just read. @@ -240,44 +229,8 @@ ProjDataFromStream::get_bin_value(const Bin& this_bin) const if(scale != 1.f) error("ProjDataFromStream: error reading data: scale factor returned by read_data should be 1\n"); -// 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) -// error("ProjDataFromStream: error reading data\n"); -// if(scale != 1) -// error("ProjDataFromStream: error reading data: scale factor returned by read_data should be 1\n"); -// // 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) -// error("ProjDataFromStream: error reading data\n"); -// if(scale != 1) -// error("ProjDataFromStream: error reading data: scale factor returned by read_data should be 1\n"); -// } - value *= 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; - - // viewgram.grow( - // IndexRange2D(get_min_axial_pos_num(segment_num), - // get_max_axial_pos_num(segment_num), - - // get_min_tangential_pos_num(), - // new_max_tangential_pos)); - // } - return value[0]; } diff --git a/src/buildblock/ProjDataInMemory.cxx b/src/buildblock/ProjDataInMemory.cxx index 0154f51185..358b7e3c2f 100644 --- a/src/buildblock/ProjDataInMemory.cxx +++ b/src/buildblock/ProjDataInMemory.cxx @@ -4,9 +4,11 @@ \ingroup projdata \brief Implementations for non-inline functions of class stir::ProjDataInMemory + \author Nikos Efthimiou \author Kris Thielemans */ /* + * Copyright (C) 2016, UCL Copyright (C) 2002 - 2011-02-23, Hammersmith Imanet Ltd Copyright (C) 2011, Kris Thielemans This file is part of STIR. @@ -160,11 +162,9 @@ write_to_file(const string& output_filename) const float ProjDataInMemory::get_bin_value(Bin& bin) { - Viewgram viewgram = get_viewgram(bin.view_num(),bin.segment_num()); - - return viewgram[bin.axial_pos_num()][bin.tangential_pos_num()]; -// return get_bin_value(bin.segment_num(), bin.axial_pos_num(), -// bin.view_num(), bin.tangential_pos_num()); +// Viewgram viewgram = get_viewgram(bin.view_num(),bin.segment_num()); +// return viewgram[bin.axial_pos_num()][bin.tangential_pos_num()]; + return ProjDataFromStream::get_bin_value(bin); } END_NAMESPACE_STIR diff --git a/src/include/stir/ProjDataFromStream.h b/src/include/stir/ProjDataFromStream.h index d3a4cbf97c..1e374194ba 100644 --- a/src/include/stir/ProjDataFromStream.h +++ b/src/include/stir/ProjDataFromStream.h @@ -6,6 +6,7 @@ \ingroup projdata \brief Declaration of class stir::ProjDataFromStream + \author Nikos Efthimiou \author Sanida Mustafovic \author Kris Thielemans \author Claire Labbe @@ -138,13 +139,7 @@ class ProjDataFromStream : public ProjData //! Get scale factor float get_scale_factor() const; - - //! - //! \brief get_bin_value - //! \param this_bin - //! \return - //! \author Nikos Efthimiou - //! \details Overloaded + //! Get the value of bin. float get_bin_value(const Bin& this_bin) const; protected: @@ -179,16 +174,7 @@ class ProjDataFromStream : public ProjData //! Calculate offsets for sinogram data std::vector get_offsets_sino(const int ax_pos_num, const int segment_num) const; - //! - //! \brief get_offsets_bin - //! \param segment_num - //! \param axial_pos_num - //! \param view_num - //! \param tang_pos_num - //! \return - //! \author Nikos Efthimiou - //! \details I could make use of get_offsets and get_offset_sino to extract the final offset of the - //! bin, but it would be another one burden in an already slow procedure. + //! Calculate the offsets for specific bins. std::vector get_offsets_bin(const int segment_num, const int ax_pos_num, const int view_num, diff --git a/src/include/stir/ProjDataInMemory.h b/src/include/stir/ProjDataInMemory.h index 72fe461d87..408e1ac2a5 100644 --- a/src/include/stir/ProjDataInMemory.h +++ b/src/include/stir/ProjDataInMemory.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2016, UCL Copyright (C) 2002 - 2011-02-23, Hammersmith Imanet Ltd This file is part of STIR. @@ -19,6 +20,7 @@ \ingroup projdata \brief Declaration of class stir::ProjDataInMemory + \author Nikos Efthimiou \author Kris Thielemans */ diff --git a/src/listmode_utilities/lm_to_projdata.cxx b/src/listmode_utilities/lm_to_projdata.cxx index dc741b4642..6c81789320 100644 --- a/src/listmode_utilities/lm_to_projdata.cxx +++ b/src/listmode_utilities/lm_to_projdata.cxx @@ -54,10 +54,10 @@ int main(int argc, char * argv[]) // which listmode files are supported if (strcmp(argv[1], "--input-formats")==0) { - cerr<::default_sptr()-> list_registered_names(cerr); - exit(EXIT_FAILURE); + exit(EXIT_SUCCESS); } LmToProjData application(argc==2 ? argv[1] : 0); application.process_data(); From 23045f32de6c322313159eb3d3b71b293ae39a6f Mon Sep 17 00:00:00 2001 From: Nikos E Date: Sun, 16 Oct 2016 18:33:24 +0100 Subject: [PATCH 020/170] Corrected a small error. Found that the last fi was commented and the test was failling. --- recon_test_pack/run_root_GATE.sh | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/recon_test_pack/run_root_GATE.sh b/recon_test_pack/run_root_GATE.sh index 8d70cb2b8a..aead914642 100755 --- a/recon_test_pack/run_root_GATE.sh +++ b/recon_test_pack/run_root_GATE.sh @@ -59,17 +59,12 @@ if ! grep "ROOT" my_lm_supported_inputs.log > /dev/null then echo GATE support has not been installed in this system. Aborting. exit 1; +else +echo "GATE support detected!" fi - - - echo Executing tests on ROOT files generated by GATE simulations, with cylindrical PET scanners - - - - # first delete any files remaining from a previous run rm -f my_*v my_*s my_*S @@ -138,4 +133,4 @@ echo "Check what went wrong. The *.log files might help you." else echo "Everything seems to be fine !" echo 'You could remove all generated files using "rm -f my_* *.log"' -#fi +fi From 7645e91869bd33895525798b265d7252de01c139 Mon Sep 17 00:00:00 2001 From: Nikos E Date: Sun, 16 Oct 2016 19:24:40 +0100 Subject: [PATCH 021/170] Added explicit exit status. Trying to narrow the error from Travis. --- recon_test_pack/run_root_GATE.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/recon_test_pack/run_root_GATE.sh b/recon_test_pack/run_root_GATE.sh index aead914642..7a87b581c8 100755 --- a/recon_test_pack/run_root_GATE.sh +++ b/recon_test_pack/run_root_GATE.sh @@ -130,7 +130,9 @@ echo if test ${ThereWereErrors} = 1 ; then echo "Check what went wrong. The *.log files might help you." +exit 1 else echo "Everything seems to be fine !" echo 'You could remove all generated files using "rm -f my_* *.log"' +exit 0 fi From 39fa774d6459b44e547d2d0b0f44981130ffc850 Mon Sep 17 00:00:00 2001 From: Nikos E Date: Sun, 16 Oct 2016 20:40:19 +0100 Subject: [PATCH 022/170] minor corrections --- recon_test_pack/run_root_GATE.sh | 33 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/recon_test_pack/run_root_GATE.sh b/recon_test_pack/run_root_GATE.sh index 7a87b581c8..361f19fac9 100755 --- a/recon_test_pack/run_root_GATE.sh +++ b/recon_test_pack/run_root_GATE.sh @@ -63,8 +63,8 @@ else 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, with cylindrical PET scanners" +echo "" # first delete any files remaining from a previous run rm -f my_*v my_*s my_*S @@ -74,18 +74,17 @@ export INPUT_ROOT_FILE=test_PET_GATE.root export INPUT=root_header.hroot export TEMPLATE=template_for_ROOT_scanner.hs -echo ------------- Converting ROOT files to ProjData file ------------- -echo Making ProjData for all events -echo Running ${INSTALL_DIR}lm_to_projdata for all events - +echo "---- Converting ROOT file to ProjData file -----" +echo ">> Running lm_to_projdata for all events" +echo "" export OUT_PROJDATA_FILE=my_proj_from_lm_all_events export EXCLUDE_SCATTERED=0 export EXCLUDE_RANDOM=0 -all_events=$(${INSTALL_DIR}lm_to_projdata ./lm_to_projdata_from_ROOT.par 2>&1 | grep "Number of prompts stored in this time period" | grep -o -E '[0-9]+') +all_events=$(${INSTALL_DIR}lm_to_projdata ./lm_to_projdata.par 2>&1 | 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 Number of prompts stored in this time period: ${all_events} -echo echo Reading all values from ROOT file all_root_num=$(root -l ${INPUT_ROOT_FILE} << EOF | grep "Number of prompts stored in this time period" | grep -o -E '[0-9]+' @@ -94,31 +93,31 @@ Int_t N = eventlist->GetN(); cout<> Running lm_to_projdata *ONLY* for true events" +echo "" export OUT_PROJDATA_FILE=my_proj_from_lm_true_events export EXCLUDE_SCATTERED=1 export EXCLUDE_RANDOM=1 -true_events=$(${INSTALL_DIR}lm_to_projdata ./lm_to_projdata_from_ROOT.par 2>&1 | grep "Number of prompts stored in this time period" | grep -o -E '[0-9]+') +true_events=$(${INSTALL_DIR}lm_to_projdata ./lm_to_projdata.par 2>&1 | grep "Number of prompts stored in this time period" | grep -o -E '[0-9]+') echo Number of prompts stored in this time period: ${true_events} echo echo Reading true values from ROOT file ... -true_root_num=$(root -l ${INPUT_ROOT_FILE} << EOF | grep "Number of trues stored in this time period" | grep -o -E '[0-9]+' +true_root_num=$(root -l ${INPUT_ROOT_FILE} << EOF | grep "Number" | grep -o -E '[0-9]+' Coincidences->Draw(">>eventlist","eventID1 == eventID2 && comptonPhantom1 == 0 && comptonPhantom2 == 0","goff"); Int_t N = eventlist->GetN(); cout< Date: Wed, 19 Oct 2016 17:20:09 +0100 Subject: [PATCH 023/170] BUG fix: If the additive data are not set then don't perform any tests. --- ...MeanAndListModeDataWithProjMatrixByBin.cxx | 59 ++++++++++--------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx index 381e5f6152..1c6bd8d8a3 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx @@ -286,37 +286,38 @@ PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBinget_max_num_non_arccorrected_bins(), false))); - if (*(this->additive_proj_data_sptr->get_proj_data_info_sptr()) != *proj_data_info_cyl_uncompressed_ptr) - { - const ProjDataInfo& add_proj = *(this->additive_proj_data_sptr->get_proj_data_info_sptr()); - const ProjDataInfo& proj = *this->proj_data_info_cyl_uncompressed_ptr; - bool ok = - typeid(add_proj) == typeid(proj) && - *add_proj.get_scanner_ptr()== *(proj.get_scanner_ptr()) && - (add_proj.get_min_view_num()==proj.get_min_view_num()) && - (add_proj.get_max_view_num()==proj.get_max_view_num()) && - (add_proj.get_min_tangential_pos_num() ==proj.get_min_tangential_pos_num())&& - (add_proj.get_max_tangential_pos_num() ==proj.get_max_tangential_pos_num()) && - add_proj.get_min_segment_num() <= proj.get_min_segment_num() && - add_proj.get_max_segment_num() >= proj.get_max_segment_num(); - - for (int segment_num=proj.get_min_segment_num(); - ok && segment_num<=proj.get_max_segment_num(); - ++segment_num) - { - ok = - add_proj.get_min_axial_pos_num(segment_num) == proj.get_min_axial_pos_num(segment_num) && - add_proj.get_max_axial_pos_num(segment_num) == proj.get_max_axial_pos_num(segment_num); - } - if (!ok) + if(!is_null_ptr(this->additive_proj_data_sptr->get_proj_data_info_sptr())) + if (*(this->additive_proj_data_sptr->get_proj_data_info_sptr()) != *proj_data_info_cyl_uncompressed_ptr) { - warning(boost::format("Incompatible additive projection data:\nAdditive projdata info:\n%s\nEmission projdata info:\n%s\n" - "--- (end of incompatible projection data info)---\n") - % add_proj.parameter_info() - % proj.parameter_info()); - return true; + const ProjDataInfo& add_proj = *(this->additive_proj_data_sptr->get_proj_data_info_sptr()); + const ProjDataInfo& proj = *this->proj_data_info_cyl_uncompressed_ptr; + bool ok = + typeid(add_proj) == typeid(proj) && + *add_proj.get_scanner_ptr()== *(proj.get_scanner_ptr()) && + (add_proj.get_min_view_num()==proj.get_min_view_num()) && + (add_proj.get_max_view_num()==proj.get_max_view_num()) && + (add_proj.get_min_tangential_pos_num() ==proj.get_min_tangential_pos_num())&& + (add_proj.get_max_tangential_pos_num() ==proj.get_max_tangential_pos_num()) && + add_proj.get_min_segment_num() <= proj.get_min_segment_num() && + add_proj.get_max_segment_num() >= proj.get_max_segment_num(); + + for (int segment_num=proj.get_min_segment_num(); + ok && segment_num<=proj.get_max_segment_num(); + ++segment_num) + { + ok = + add_proj.get_min_axial_pos_num(segment_num) == proj.get_min_axial_pos_num(segment_num) && + add_proj.get_max_axial_pos_num(segment_num) == proj.get_max_axial_pos_num(segment_num); + } + if (!ok) + { + warning(boost::format("Incompatible additive projection data:\nAdditive projdata info:\n%s\nEmission projdata info:\n%s\n" + "--- (end of incompatible projection data info)---\n") + % add_proj.parameter_info() + % proj.parameter_info()); + return true; + } } - } if( this->normalisation_sptr->set_up(proj_data_info_cyl_uncompressed_ptr) == Succeeded::no) From 971eb5768a1d4b25437c813231ee26733dc08ff9 Mon Sep 17 00:00:00 2001 From: Nikos E Date: Fri, 21 Oct 2016 19:24:10 +0100 Subject: [PATCH 024/170] Listmode data can handle compression (span). The lm objective function takes the pro_data_info from CListModeData. New keywords for CListrModeROOT to set the sizes of the proj_data_info. Better tests on ranges. --- recon_test_pack/root_header.hroot | 6 ++ recon_test_pack/run_test_listmode_recon.sh | 2 +- src/include/stir/listmode/CListModeData.h | 8 +- .../stir/listmode/CListModeDataECAT8_32bit.h | 3 +- src/include/stir/listmode/CListModeDataROOT.h | 7 ++ ...orMeanAndListModeDataWithProjMatrixByBin.h | 2 +- src/listmode_buildblock/CListModeData.cxx | 8 ++ src/listmode_buildblock/CListModeDataROOT.cxx | 22 +++++ ...MeanAndListModeDataWithProjMatrixByBin.cxx | 85 ++++++++++--------- 9 files changed, 97 insertions(+), 46 deletions(-) diff --git a/recon_test_pack/root_header.hroot b/recon_test_pack/root_header.hroot index 8936f059d4..a566b779bd 100644 --- a/recon_test_pack/root_header.hroot +++ b/recon_test_pack/root_header.hroot @@ -9,6 +9,12 @@ Distance between rings (cm) := 0.40625 Default bin size (cm) := 0.208626 Maximum number of non-arc-corrected bins := 344 +; Acquisition specific params +%axial_compression := +%maximum_ring_difference := +%number_of_projections := +%number_of_views := +%number_of_segments := GATE scanner type := GATE_Cylindrical_PET diff --git a/recon_test_pack/run_test_listmode_recon.sh b/recon_test_pack/run_test_listmode_recon.sh index c5a12564f7..23322c6a55 100755 --- a/recon_test_pack/run_test_listmode_recon.sh +++ b/recon_test_pack/run_test_listmode_recon.sh @@ -73,7 +73,7 @@ fi # first delete any files remaining from a previous run rm -f my_*v my_*s my_*S -echo "=== calculate backround data" +echo "=== calculate background data" echo "===== randoms" echo "====== create delayed fansums" diff --git a/src/include/stir/listmode/CListModeData.h b/src/include/stir/listmode/CListModeData.h index 75bad2f7e8..98ccbd9646 100644 --- a/src/include/stir/listmode/CListModeData.h +++ b/src/include/stir/listmode/CListModeData.h @@ -30,7 +30,7 @@ #include "stir/shared_ptr.h" #include #include - +#include "stir/ProjDataInfo.h" #include "stir/IO/ExamData.h" #include "stir/RegisteredParsingObject.h" @@ -226,11 +226,15 @@ class CListModeData : public ExamData //! Returns the total number of events in the listmode file virtual inline unsigned long int get_total_number_of_events() const = 0; + shared_ptr get_proj_data_info_sptr() const; + protected: - //! Has to be set by the derived class + //! Has to be initialised by the derived class shared_ptr scanner_sptr; //! Has to be set by the derived class // shared_ptr exam_info_sptr; + + shared_ptr proj_data_info_sptr; }; END_NAMESPACE_STIR diff --git a/src/include/stir/listmode/CListModeDataECAT8_32bit.h b/src/include/stir/listmode/CListModeDataECAT8_32bit.h index 7feae1741d..3ca76eaec6 100644 --- a/src/include/stir/listmode/CListModeDataECAT8_32bit.h +++ b/src/include/stir/listmode/CListModeDataECAT8_32bit.h @@ -87,7 +87,8 @@ class CListModeDataECAT8_32bit : public CListModeData typedef CListRecordECAT8_32bit CListRecordT; std::string listmode_filename; shared_ptr > current_lm_data_ptr; - shared_ptr proj_data_info_sptr; + //moved at CListModeData +// shared_ptr proj_data_info_sptr; InterfileHeader interfile_parser; // members to store info from the interfile header. // These tell us something about how the listmode is stored. diff --git a/src/include/stir/listmode/CListModeDataROOT.h b/src/include/stir/listmode/CListModeDataROOT.h index 8bc13b34d8..c6dc3ae695 100644 --- a/src/include/stir/listmode/CListModeDataROOT.h +++ b/src/include/stir/listmode/CListModeDataROOT.h @@ -88,11 +88,18 @@ class CListModeDataROOT : public CListModeData float average_depth_of_interaction; float ring_spacing; float bin_size; + // These tell us something about how the listmode is stored. + int axial_compression; + int maximum_ring_difference; + int number_of_projections; + int number_of_views; + int number_of_segments; KeyParser parser; //! Name of input chain which is going to be used. Succeeded open_lm_file(); + }; diff --git a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h index 91174db804..a596588603 100644 --- a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h +++ b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h @@ -113,7 +113,7 @@ typedef RegisteredParsingObject proj_data_info_cyl_uncompressed_ptr; + shared_ptr proj_data_info_cyl_sptr; //! sets any default values /*! Has to be called by set_defaults in the leaf-class */ diff --git a/src/listmode_buildblock/CListModeData.cxx b/src/listmode_buildblock/CListModeData.cxx index 2cb1266c77..94c9991910 100644 --- a/src/listmode_buildblock/CListModeData.cxx +++ b/src/listmode_buildblock/CListModeData.cxx @@ -59,6 +59,14 @@ get_scanner_ptr() const return this->scanner_sptr.get(); } +shared_ptr +CListModeData:: +get_proj_data_info_sptr() const +{ + assert(!is_null_ptr(proj_data_info_sptr)); + return proj_data_info_sptr; +} + #if 0 std::time_t CListModeData:: diff --git a/src/listmode_buildblock/CListModeDataROOT.cxx b/src/listmode_buildblock/CListModeDataROOT.cxx index 8a19eea7d3..4803bb31a5 100644 --- a/src/listmode_buildblock/CListModeDataROOT.cxx +++ b/src/listmode_buildblock/CListModeDataROOT.cxx @@ -43,6 +43,13 @@ CListModeDataROOT(const std::string& listmode_filename) this->parser.add_start_key("ROOT header"); this->parser.add_stop_key("End ROOT header"); + // Set some defaults, in order to know if this parameters have been initialised + axial_compression = -1; + maximum_ring_difference = -1; + number_of_projections = -1; + number_of_views = -1; + number_of_segments = -1; + // Scanner related & Physical dimentions. this->parser.add_key("originating system", &this->originating_system); this->parser.add_key("Number of rings", &this->num_rings); @@ -54,6 +61,14 @@ CListModeDataROOT(const std::string& listmode_filename) this->parser.add_key("Maximum number of non-arc-corrected bins", &this->max_num_non_arccorrected_bins); // end Scanner and physical dimentions. + // Acquisition related + this->parser.add_key("%axial_compression", &axial_compression); + this->parser.add_key("%maximum_ring_difference", &maximum_ring_difference); + this->parser.add_key("%number_of_projections", &number_of_projections); + this->parser.add_key("%number_of_views", &number_of_views); + this->parser.add_key("%number_of_segments", &number_of_segments); + // + // ROOT related this->parser.add_parsing_key("GATE scanner type", &this->current_lm_data_ptr); this->parser.parse(listmode_filename.c_str(), false /* no warnings about unrecognised keywords */); @@ -116,6 +131,13 @@ CListModeDataROOT(const std::string& listmode_filename) if (this->open_lm_file() == Succeeded::no) error("CListModeDataROOT: error opening the first listmode file for filename %s\n", listmode_filename.c_str()); + + this->proj_data_info_sptr.reset(ProjDataInfo::ProjDataInfoCTI(this->scanner_sptr, + std::max(axial_compression, 1), + std::max(maximum_ring_difference, num_rings), + std::max(number_of_views, num_detectors_per_ring/2), + std::max(number_of_projections, max_num_non_arccorrected_bins), + /* arc_correction*/false)); } std::string diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx index 1c6bd8d8a3..f5dd6bc1b8 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx @@ -129,19 +129,19 @@ actual_subsets_are_approximately_balanced(std::string& warning_message) const for (int segment_num = -this->max_ring_difference_num_to_process; segment_num <= this->max_ring_difference_num_to_process; ++segment_num) { - for (int axial_num = this->proj_data_info_cyl_uncompressed_ptr->get_min_axial_pos_num(segment_num); - axial_num < this->proj_data_info_cyl_uncompressed_ptr->get_max_axial_pos_num(segment_num); + for (int axial_num = proj_data_info_cyl_sptr->get_min_axial_pos_num(segment_num); + axial_num < proj_data_info_cyl_sptr->get_max_axial_pos_num(segment_num); axial_num ++) { // For debugging. // std::cout <proj_data_info_cyl_uncompressed_ptr->get_min_tangential_pos_num(); - tang_num < this->proj_data_info_cyl_uncompressed_ptr->get_max_tangential_pos_num(); + for (int tang_num= proj_data_info_cyl_sptr->get_min_tangential_pos_num(); + tang_num < proj_data_info_cyl_sptr->get_max_tangential_pos_num(); tang_num ++ ) { - for(int view_num = this->proj_data_info_cyl_uncompressed_ptr->get_min_view_num() + subset_num; - view_num <= this->proj_data_info_cyl_uncompressed_ptr->get_max_view_num(); + for(int view_num = proj_data_info_cyl_sptr->get_min_view_num() + subset_num; + view_num <= proj_data_info_cyl_sptr->get_max_view_num(); view_num += this->num_subsets) { const Bin tmp_bin(segment_num, @@ -171,11 +171,11 @@ actual_subsets_are_approximately_balanced(std::string& warning_message) const << num_bins_in_subset << "\nEither reduce the number of symmetries used by the projector, or\n" "change the number of subsets. It usually should be a divisor of\n" - << this->proj_data_info_cyl_uncompressed_ptr->get_num_views() + << proj_data_info_cyl_sptr->get_num_views() << "/4 (or if that's not an integer, a divisor of " - << this->proj_data_info_cyl_uncompressed_ptr->get_num_views() + << proj_data_info_cyl_sptr->get_num_views() << "/2 or " - << this->proj_data_info_cyl_uncompressed_ptr->get_num_views() + << proj_data_info_cyl_sptr->get_num_views() << ").\n"; warning_message = str.str(); return false; @@ -196,7 +196,7 @@ set_up_before_sensitivity(shared_ptr const& target_sptr) // set projector to be used for the calculations - this->PM_sptr->set_up(this->proj_data_info_cyl_uncompressed_ptr->create_shared_clone(),target_sptr); + this->PM_sptr->set_up(proj_data_info_cyl_sptr->create_shared_clone(),target_sptr); shared_ptr forward_projector_ptr(new ForwardProjectorByBinUsingProjMatrixByBin(this->PM_sptr)); shared_ptr back_projector_ptr(new BackProjectorByBinUsingProjMatrixByBin(this->PM_sptr)); @@ -204,7 +204,7 @@ set_up_before_sensitivity(shared_ptr const& target_sptr) this->projector_pair_ptr.reset( new ProjectorByBinPairUsingSeparateProjectors(forward_projector_ptr, back_projector_ptr)); - this->projector_pair_ptr->set_up(this->proj_data_info_cyl_uncompressed_ptr->create_shared_clone(),target_sptr); + this->projector_pair_ptr->set_up(proj_data_info_cyl_sptr->create_shared_clone(),target_sptr); if (is_null_ptr(this->normalisation_sptr)) @@ -214,7 +214,7 @@ set_up_before_sensitivity(shared_ptr const& target_sptr) } if (this->normalisation_sptr->set_up( - this->proj_data_info_cyl_uncompressed_ptr->create_shared_clone()) == Succeeded::no) + proj_data_info_cyl_sptr->create_shared_clone()) == Succeeded::no) return Succeeded::no; if (this->current_frame_num<=0) @@ -260,14 +260,12 @@ PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin scanner_sptr(new Scanner(*this->list_mode_data_sptr->get_scanner_ptr())); - if (this->max_ring_difference_num_to_process == -1) { this->max_ring_difference_num_to_process = scanner_sptr->get_num_rings()-1; } - if (this->additive_projection_data_filename != "0") { info(boost::format("Reading additive projdata data '%1%'") @@ -277,20 +275,25 @@ PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBinadditive_proj_data_sptr.reset(new ProjDataInMemory(* temp_additive_proj_data_sptr)); } + proj_data_info_cyl_sptr = this->list_mode_data_sptr->get_proj_data_info_sptr()->create_shared_clone(); - this->proj_data_info_cyl_uncompressed_ptr.reset( - dynamic_cast( - ProjDataInfo::ProjDataInfoCTI(scanner_sptr, - 1, this->max_ring_difference_num_to_process, - scanner_sptr->get_num_detectors_per_ring()/2, - scanner_sptr->get_max_num_non_arccorrected_bins(), - false))); + if (max_ring_difference_num_to_process > proj_data_info_cyl_sptr->get_max_segment_num()) + { + warning("In the parameter file, the 'maximum ring difference' is larger than the number of segments" + "in the emission header. Abort."); + return true; + } + else if (max_ring_difference_num_to_process < proj_data_info_cyl_sptr->get_max_segment_num()) + { + proj_data_info_cyl_sptr->reduce_segment_range(-max_ring_difference_num_to_process, + max_ring_difference_num_to_process); + } if(!is_null_ptr(this->additive_proj_data_sptr->get_proj_data_info_sptr())) - if (*(this->additive_proj_data_sptr->get_proj_data_info_sptr()) != *proj_data_info_cyl_uncompressed_ptr) + if (*(this->additive_proj_data_sptr->get_proj_data_info_sptr()) != *proj_data_info_cyl_sptr) { const ProjDataInfo& add_proj = *(this->additive_proj_data_sptr->get_proj_data_info_sptr()); - const ProjDataInfo& proj = *this->proj_data_info_cyl_uncompressed_ptr; + const ProjDataInfo& proj = *this->proj_data_info_cyl_sptr; bool ok = typeid(add_proj) == typeid(proj) && *add_proj.get_scanner_ptr()== *(proj.get_scanner_ptr()) && @@ -298,7 +301,7 @@ PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin= proj.get_max_segment_num(); for (int segment_num=proj.get_min_segment_num(); @@ -306,8 +309,8 @@ PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin= proj.get_max_axial_pos_num(segment_num); } if (!ok) { @@ -319,7 +322,7 @@ PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBinnormalisation_sptr->set_up(proj_data_info_cyl_uncompressed_ptr) + if( this->normalisation_sptr->set_up(proj_data_info_cyl_sptr) == Succeeded::no) { warning("PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin: " @@ -337,14 +340,14 @@ PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBinproj_data_info_cyl_uncompressed_ptr->get_min_segment_num(); - const int max_segment_num = this->proj_data_info_cyl_uncompressed_ptr->get_max_segment_num(); + const int min_segment_num = proj_data_info_cyl_sptr->get_min_segment_num(); + const int max_segment_num = proj_data_info_cyl_sptr->get_max_segment_num(); // warning: has to be same as subset scheme used as in distributable_computation for (int segment_num = min_segment_num; segment_num <= max_segment_num; ++segment_num) { - for (int view = this->proj_data_info_cyl_uncompressed_ptr->get_min_view_num() + subset_num; - view <= this->proj_data_info_cyl_uncompressed_ptr->get_max_view_num(); + for (int view = proj_data_info_cyl_sptr->get_min_view_num() + subset_num; + view <= proj_data_info_cyl_sptr->get_max_view_num(); view += this->num_subsets) { const ViewSegmentNumbers view_segment_num(view, segment_num); @@ -365,7 +368,7 @@ add_view_seg_to_sensitivity(TargetT& sensitivity, const ViewSegmentNumbers& view (this->projector_pair_ptr->get_symmetries_used()->clone()); RelatedViewgrams viewgrams = - this->proj_data_info_cyl_uncompressed_ptr->get_empty_related_viewgrams(view_seg_nums,symmetries_used); + proj_data_info_cyl_sptr->get_empty_related_viewgrams(view_seg_nums,symmetries_used); viewgrams.fill(1.F); // find efficiencies @@ -395,7 +398,7 @@ construct_target_ptr() const { return - new VoxelsOnCartesianGrid (*this->proj_data_info_cyl_uncompressed_ptr, + new VoxelsOnCartesianGrid (*proj_data_info_cyl_sptr, static_cast(this->zoom), CartesianCoordinate3D(static_cast(this->Zoffset), static_cast(this->Yoffset), @@ -419,7 +422,7 @@ compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, assert(subset_numnum_subsets); ProjDataInfoCylindricalNoArcCorr* proj_data_no_arc_ptr = - dynamic_cast ( this->proj_data_info_cyl_uncompressed_ptr.get()); + dynamic_cast (proj_data_info_cyl_sptr.get()); const double start_time = this->frame_defs.get_start_time(this->current_frame_num); const double end_time = this->frame_defs.get_end_time(this->current_frame_num); @@ -462,15 +465,15 @@ compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, { Bin measured_bin; measured_bin.set_bin_value(1.0f); - record.event().get_bin(measured_bin, *this->proj_data_info_cyl_uncompressed_ptr); + record.event().get_bin(measured_bin, *proj_data_info_cyl_sptr); if (measured_bin.get_bin_value() != 1.0f - || measured_bin.segment_num() < this->proj_data_info_cyl_uncompressed_ptr->get_min_segment_num() - || measured_bin.segment_num() > this->proj_data_info_cyl_uncompressed_ptr->get_max_segment_num() - || measured_bin.tangential_pos_num() < this->proj_data_info_cyl_uncompressed_ptr->get_min_tangential_pos_num() - || measured_bin.tangential_pos_num() > this->proj_data_info_cyl_uncompressed_ptr->get_max_tangential_pos_num() - || measured_bin.axial_pos_num() < this->proj_data_info_cyl_uncompressed_ptr->get_min_axial_pos_num(measured_bin.segment_num()) - || measured_bin.axial_pos_num() > this->proj_data_info_cyl_uncompressed_ptr->get_max_axial_pos_num(measured_bin.segment_num())) + || measured_bin.segment_num() < proj_data_info_cyl_sptr->get_min_segment_num() + || measured_bin.segment_num() > proj_data_info_cyl_sptr->get_max_segment_num() + || measured_bin.tangential_pos_num() < proj_data_info_cyl_sptr->get_min_tangential_pos_num() + || measured_bin.tangential_pos_num() > proj_data_info_cyl_sptr->get_max_tangential_pos_num() + || measured_bin.axial_pos_num() < proj_data_info_cyl_sptr->get_min_axial_pos_num(measured_bin.segment_num()) + || measured_bin.axial_pos_num() > proj_data_info_cyl_sptr->get_max_axial_pos_num(measured_bin.segment_num())) { continue; } From 77ff2c1b76bc1f27e7661ddf44c4a9dd8b01c6dd Mon Sep 17 00:00:00 2001 From: Nikos E Date: Sun, 23 Oct 2016 19:39:07 +0100 Subject: [PATCH 025/170] Minor bug corrections and cosmetics. After some more complains about the grep in the run_root_GATE.sh, I changed to an awk. --- recon_test_pack/run_root_GATE.sh | 51 ++++++++----------- src/IO/InputStreamFromROOTFile.cxx | 4 +- ...putStreamFromROOTFileForCylindricalPET.cxx | 20 ++++---- src/IO/InputStreamFromROOTFileForECATPET.cxx | 12 ++--- src/include/stir/IO/InputStreamFromROOTFile.h | 2 +- src/listmode_buildblock/CListModeDataROOT.cxx | 25 +++------ 6 files changed, 47 insertions(+), 67 deletions(-) diff --git a/recon_test_pack/run_root_GATE.sh b/recon_test_pack/run_root_GATE.sh index 361f19fac9..0f487bdbd9 100755 --- a/recon_test_pack/run_root_GATE.sh +++ b/recon_test_pack/run_root_GATE.sh @@ -80,37 +80,36 @@ echo "" export OUT_PROJDATA_FILE=my_proj_from_lm_all_events export EXCLUDE_SCATTERED=0 export EXCLUDE_RANDOM=0 - -all_events=$(${INSTALL_DIR}lm_to_projdata ./lm_to_projdata.par 2>&1 | grep "Number of prompts stored in this time period" | grep -o -E '[0-9]+') - +${INSTALL_DIR}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} -echo Reading all values from ROOT file +echo "" +echo ">> Running lm_to_projdata *ONLY* for true events" +echo "" +export OUT_PROJDATA_FILE=my_proj_from_lm_true_events +export EXCLUDE_SCATTERED=1 +export EXCLUDE_RANDOM=1 +${INSTALL_DIR}lm_to_projdata ./lm_to_projdata.par 2>"./my_root_log_trues.log" +true_events=$(awk -F ":" '/Number of prompts/ {print $2}' my_root_log_trues.log) +echo "Number of prompts stored in this time period:" ${true_events} + +echo "" +echo "Counting all values from ROOT file ..." all_root_num=$(root -l ${INPUT_ROOT_FILE} << EOF | grep "Number of prompts stored in this time period" | grep -o -E '[0-9]+' Coincidences->Draw(">>eventlist","","goff"); Int_t N = eventlist->GetN(); cout<> Running lm_to_projdata *ONLY* for true events" -echo "" -export OUT_PROJDATA_FILE=my_proj_from_lm_true_events -export EXCLUDE_SCATTERED=1 -export EXCLUDE_RANDOM=1 - -true_events=$(${INSTALL_DIR}lm_to_projdata ./lm_to_projdata.par 2>&1 | grep "Number of prompts stored in this time period" | grep -o -E '[0-9]+') - -echo Number of prompts stored in this time period: ${true_events} -echo -echo Reading true values from ROOT file ... +echo "" +echo "Counting true values from ROOT file ..." true_root_num=$(root -l ${INPUT_ROOT_FILE} << EOF | grep "Number" | grep -o -E '[0-9]+' Coincidences->Draw(">>eventlist","eventID1 == eventID2 && comptonPhantom1 == 0 && comptonPhantom2 == 0","goff"); Int_t N = eventlist->GetN(); @@ -118,20 +117,14 @@ cout<SetBranchAddress("time1", &time1); stream_ptr->SetBranchAddress("time2", &time2); - stream_ptr->SetBranchAddress("eventID1",&eventID1); - stream_ptr->SetBranchAddress("eventID2",&eventID2); + stream_ptr->SetBranchAddress("eventID1",&event1); + stream_ptr->SetBranchAddress("eventID2",&event2); stream_ptr->SetBranchAddress("energy1", &energy1); stream_ptr->SetBranchAddress("energy2", &energy2); stream_ptr->SetBranchAddress("comptonPhantom1", &comptonphantom1); diff --git a/src/IO/InputStreamFromROOTFileForCylindricalPET.cxx b/src/IO/InputStreamFromROOTFileForCylindricalPET.cxx index ba7855dff6..bf23f85833 100644 --- a/src/IO/InputStreamFromROOTFileForCylindricalPET.cxx +++ b/src/IO/InputStreamFromROOTFileForCylindricalPET.cxx @@ -67,22 +67,22 @@ get_next_record(CListRecordROOT& record) return Succeeded::no; - if (stream_ptr->GetEntry(current_position) == 0 ) + if (stream_ptr->GetEntry(static_cast(current_position)) == 0 ) return Succeeded::no; current_position ++ ; - if ( (comptonphantom1 > 0 || comptonphantom2>0) && exclude_scattered ) + if ( (this->comptonphantom1 > 0 || this->comptonphantom2 > 0) && this->exclude_scattered ) continue; - else if ( (eventID1 != eventID2) && exclude_randoms ) + if ( this->event1 != this->event2 && this->exclude_randoms) continue; - else if (energy1 < low_energy_window || - energy1 > up_energy_window || - energy2 < low_energy_window || - energy2 > up_energy_window) + if (this->energy1 < this->low_energy_window || + this->energy1 > this->up_energy_window || + this->energy2 < this->low_energy_window || + this->energy2 > this->up_energy_window) continue; - else - break; + + break; } int ring1 = static_cast(crystalID1/crystal_repeater_y) @@ -116,7 +116,7 @@ get_next_record(CListRecordROOT& record) record.init_from_data(ring1, ring2, crystal1, crystal2, time1, time2, - eventID1, eventID2); + event1, event2); } std::string diff --git a/src/IO/InputStreamFromROOTFileForECATPET.cxx b/src/IO/InputStreamFromROOTFileForECATPET.cxx index 35dc0ee7ba..87efb85d8c 100644 --- a/src/IO/InputStreamFromROOTFileForECATPET.cxx +++ b/src/IO/InputStreamFromROOTFileForECATPET.cxx @@ -68,17 +68,17 @@ get_next_record(CListRecordROOT& record) current_position ++ ; - if ( (comptonphantom1 > 0 && comptonphantom2>0) && exclude_scattered ) + if ( (comptonphantom1 > 0 || comptonphantom2 > 0) && exclude_scattered ) continue; - else if ( (eventID1 != eventID2) && exclude_randoms ) + if ( event1 != event2 && exclude_randoms ) continue; - else if (energy1 < low_energy_window || + if (energy1 < low_energy_window || energy1 > up_energy_window || energy2 < low_energy_window || energy2 > up_energy_window) continue; - else - break; + + break; } int ring1 = static_cast(crystalID1/crystal_repeater_z) @@ -106,7 +106,7 @@ get_next_record(CListRecordROOT& record) record.init_from_data(ring1, ring2, crystal1, crystal2, time1, time2, - eventID1, eventID2); + event1, event2); } std::string diff --git a/src/include/stir/IO/InputStreamFromROOTFile.h b/src/include/stir/IO/InputStreamFromROOTFile.h index 7da8255674..0ff6b63a4b 100644 --- a/src/include/stir/IO/InputStreamFromROOTFile.h +++ b/src/include/stir/IO/InputStreamFromROOTFile.h @@ -145,7 +145,7 @@ class InputStreamFromROOTFile : public RegisteredObject< InputStreamFromROOTFile // Variables to store root information std::string chain_name; - Int_t eventID1, eventID2; + Int_t event1, event2; Double_t time1, time2; Float_t energy1, energy2; Int_t comptonphantom1, comptonphantom2; diff --git a/src/listmode_buildblock/CListModeDataROOT.cxx b/src/listmode_buildblock/CListModeDataROOT.cxx index 4803bb31a5..bfc899fb04 100644 --- a/src/listmode_buildblock/CListModeDataROOT.cxx +++ b/src/listmode_buildblock/CListModeDataROOT.cxx @@ -134,7 +134,7 @@ CListModeDataROOT(const std::string& listmode_filename) this->proj_data_info_sptr.reset(ProjDataInfo::ProjDataInfoCTI(this->scanner_sptr, std::max(axial_compression, 1), - std::max(maximum_ring_difference, num_rings), + std::max(maximum_ring_difference, num_rings-1), std::max(number_of_views, num_detectors_per_ring/2), std::max(number_of_projections, max_num_non_arccorrected_bins), /* arc_correction*/false)); @@ -162,40 +162,27 @@ open_lm_file() info(boost::format("CListModeDataROOT: opening ROOT file %s") % this->current_lm_data_ptr->get_ROOT_filename()); - // Read the 4 bytes to check whether this is a ROOT file, indeed. - // I could rewrite it in a more sofisticated way ... + // Read the 4 bytes to check whether this is a ROOT file std::stringstream ss; - char mem[4]="\0"; + std::string mem(4,' '); std::string sig= "root"; std::ifstream t; - t.open(this->current_lm_data_ptr->get_ROOT_filename().c_str(), std::ios::in |std::ios::binary); + t.open(this->current_lm_data_ptr->get_ROOT_filename().c_str(), std::ios::in); if (t.is_open()) { t.seekg(0, std::ios::beg); - t.read(mem,4); - ss << mem; + t.read(&mem[0],4); - if ( !sig.compare(ss.str()) ) + if (sig.compare(mem) ) { warning("CListModeDataROOT: File '%s is not a ROOT file!!'", this->current_lm_data_ptr->get_ROOT_filename().c_str()); return Succeeded::no; } -// current_lm_data_ptr.reset( -// new InputStreamFromROOTFile(this->input_data_filename, -// this->name_of_input_tchain, -// this->number_of_crystals_x, this->number_of_crystals_y, this->number_of_crystals_z, -// this->number_of_submodules_x, this->number_of_submodules_y, this->number_of_submodules_z, -// this->number_of_modules_x, this->number_of_modules_y, this->number_of_modules_z, -// this->number_of_rsectors, -// this->exclude_scattered, this->exclude_randoms, -// static_cast(this->low_energy_window*0.001f), -// static_cast(this->up_energy_window*0.001f), -// this->offset_dets)); t.close(); return Succeeded::yes; From adb10256a01d6a4288b446bd5c29ee18a2a5d529 Mon Sep 17 00:00:00 2001 From: Nikos E Date: Sun, 23 Oct 2016 22:09:45 +0100 Subject: [PATCH 026/170] Removed a redundant variable. --- src/listmode_buildblock/CListModeDataROOT.cxx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/listmode_buildblock/CListModeDataROOT.cxx b/src/listmode_buildblock/CListModeDataROOT.cxx index bfc899fb04..c60a8639b0 100644 --- a/src/listmode_buildblock/CListModeDataROOT.cxx +++ b/src/listmode_buildblock/CListModeDataROOT.cxx @@ -163,7 +163,6 @@ open_lm_file() this->current_lm_data_ptr->get_ROOT_filename()); // Read the 4 bytes to check whether this is a ROOT file - std::stringstream ss; std::string mem(4,' '); std::string sig= "root"; From b1386232e9c16f5a8664558a7a7a727987eef95f Mon Sep 17 00:00:00 2001 From: Nikos E Date: Sun, 23 Oct 2016 23:23:08 +0100 Subject: [PATCH 027/170] Axial compression for CListmodeDataROOT has been commented out. Use of backquotes in the test script. Other minor imrpovements. --- recon_test_pack/root_header.hroot | 8 --- recon_test_pack/run_root_GATE.sh | 12 ++--- ...putStreamFromROOTFileForCylindricalPET.cxx | 2 +- src/include/stir/listmode/CListModeDataROOT.h | 12 ++--- src/listmode_buildblock/CListModeData.cxx | 2 - src/listmode_buildblock/CListModeDataROOT.cxx | 50 +++++++++++-------- 6 files changed, 42 insertions(+), 44 deletions(-) diff --git a/recon_test_pack/root_header.hroot b/recon_test_pack/root_header.hroot index a566b779bd..3bada70ad8 100644 --- a/recon_test_pack/root_header.hroot +++ b/recon_test_pack/root_header.hroot @@ -9,14 +9,6 @@ Distance between rings (cm) := 0.40625 Default bin size (cm) := 0.208626 Maximum number of non-arc-corrected bins := 344 -; Acquisition specific params -%axial_compression := -%maximum_ring_difference := -%number_of_projections := -%number_of_views := -%number_of_segments := - - 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 0f487bdbd9..4a7d05cbd2 100755 --- a/recon_test_pack/run_root_GATE.sh +++ b/recon_test_pack/run_root_GATE.sh @@ -81,7 +81,7 @@ export OUT_PROJDATA_FILE=my_proj_from_lm_all_events export EXCLUDE_SCATTERED=0 export EXCLUDE_RANDOM=0 ${INSTALL_DIR}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) +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} echo "" @@ -92,16 +92,16 @@ export OUT_PROJDATA_FILE=my_proj_from_lm_true_events export EXCLUDE_SCATTERED=1 export EXCLUDE_RANDOM=1 ${INSTALL_DIR}lm_to_projdata ./lm_to_projdata.par 2>"./my_root_log_trues.log" -true_events=$(awk -F ":" '/Number of prompts/ {print $2}' my_root_log_trues.log) +true_events=`awk -F ":" '/Number of prompts/ {print $2}' my_root_log_trues.log` echo "Number of prompts stored in this time period:" ${true_events} echo "" echo "Counting all values from ROOT file ..." -all_root_num=$(root -l ${INPUT_ROOT_FILE} << EOF | grep "Number of prompts stored in this time period" | grep -o -E '[0-9]+' +all_root_num=`root -l ${INPUT_ROOT_FILE} << EOF | grep "Number of prompts stored in this time period" | grep -o -E '[0-9]+' Coincidences->Draw(">>eventlist","","goff"); Int_t N = eventlist->GetN(); cout<Draw(">>eventlist","eventID1 == eventID2 && comptonPhantom1 == 0 && comptonPhantom2 == 0","goff"); Int_t N = eventlist->GetN(); cout<GetEntry(static_cast(current_position)) == 0 ) + if (stream_ptr->GetEntry(static_cast(current_position)) == 0 ) return Succeeded::no; current_position ++ ; diff --git a/src/include/stir/listmode/CListModeDataROOT.h b/src/include/stir/listmode/CListModeDataROOT.h index c6dc3ae695..7f82670574 100644 --- a/src/include/stir/listmode/CListModeDataROOT.h +++ b/src/include/stir/listmode/CListModeDataROOT.h @@ -88,12 +88,12 @@ class CListModeDataROOT : public CListModeData float average_depth_of_interaction; float ring_spacing; float bin_size; - // These tell us something about how the listmode is stored. - int axial_compression; - int maximum_ring_difference; - int number_of_projections; - int number_of_views; - int number_of_segments; + // Axial compresstion has been commented out, until further testing is done. + // int axial_compression; + // int maximum_ring_difference; + // int number_of_projections; + // int number_of_views; + // int number_of_segments; KeyParser parser; //! Name of input chain which is going to be used. diff --git a/src/listmode_buildblock/CListModeData.cxx b/src/listmode_buildblock/CListModeData.cxx index 94c9991910..184d4f909f 100644 --- a/src/listmode_buildblock/CListModeData.cxx +++ b/src/listmode_buildblock/CListModeData.cxx @@ -55,7 +55,6 @@ const Scanner* CListModeData:: get_scanner_ptr() const { - assert(!is_null_ptr(scanner_sptr)); return this->scanner_sptr.get(); } @@ -63,7 +62,6 @@ shared_ptr CListModeData:: get_proj_data_info_sptr() const { - assert(!is_null_ptr(proj_data_info_sptr)); return proj_data_info_sptr; } diff --git a/src/listmode_buildblock/CListModeDataROOT.cxx b/src/listmode_buildblock/CListModeDataROOT.cxx index c60a8639b0..67d0c9daa4 100644 --- a/src/listmode_buildblock/CListModeDataROOT.cxx +++ b/src/listmode_buildblock/CListModeDataROOT.cxx @@ -32,7 +32,7 @@ #include "stir/error.h" #include #include -#include +#include START_NAMESPACE_STIR @@ -43,12 +43,12 @@ CListModeDataROOT(const std::string& listmode_filename) this->parser.add_start_key("ROOT header"); this->parser.add_stop_key("End ROOT header"); - // Set some defaults, in order to know if this parameters have been initialised - axial_compression = -1; - maximum_ring_difference = -1; - number_of_projections = -1; - number_of_views = -1; - number_of_segments = -1; + // N.E.: Compression on ROOT listmode data is commented out until further testing is done. + // axial_compression = -1; + // maximum_ring_difference = -1; + // number_of_projections = -1; + // number_of_views = -1; + // number_of_segments = -1; // Scanner related & Physical dimentions. this->parser.add_key("originating system", &this->originating_system); @@ -62,18 +62,18 @@ CListModeDataROOT(const std::string& listmode_filename) // end Scanner and physical dimentions. // Acquisition related - this->parser.add_key("%axial_compression", &axial_compression); - this->parser.add_key("%maximum_ring_difference", &maximum_ring_difference); - this->parser.add_key("%number_of_projections", &number_of_projections); - this->parser.add_key("%number_of_views", &number_of_views); - this->parser.add_key("%number_of_segments", &number_of_segments); + // N.E.: Compression on ROOT listmode data has been commented out until further testing is done. + // this->parser.add_key("%axial_compression", &axial_compression); + // this->parser.add_key("%maximum_ring_difference", &maximum_ring_difference); + // this->parser.add_key("%number_of_projections", &number_of_projections); + // this->parser.add_key("%number_of_views", &number_of_views); + // this->parser.add_key("%number_of_segments", &number_of_segments); // // ROOT related this->parser.add_parsing_key("GATE scanner type", &this->current_lm_data_ptr); this->parser.parse(listmode_filename.c_str(), false /* no warnings about unrecognised keywords */); -// this->current_lm_data_ptr->set_up(); // ExamInfo initialisation this->exam_info_sptr.reset(new ExamInfo); @@ -132,11 +132,19 @@ CListModeDataROOT(const std::string& listmode_filename) error("CListModeDataROOT: error opening the first listmode file for filename %s\n", listmode_filename.c_str()); + // N.E.: Compression on ROOT listmode data has been commented out until further testing is done. + // this->proj_data_info_sptr.reset(ProjDataInfo::ProjDataInfoCTI(this->scanner_sptr, + // std::max(axial_compression, 1), + // std::max(maximum_ring_difference, num_rings-1), + // std::max(number_of_views, num_detectors_per_ring/2), + // std::max(number_of_projections, max_num_non_arccorrected_bins), + // /* arc_correction*/false)); + this->proj_data_info_sptr.reset(ProjDataInfo::ProjDataInfoCTI(this->scanner_sptr, - std::max(axial_compression, 1), - std::max(maximum_ring_difference, num_rings-1), - std::max(number_of_views, num_detectors_per_ring/2), - std::max(number_of_projections, max_num_non_arccorrected_bins), + 1, + num_rings-1, + num_detectors_per_ring/2, + max_num_non_arccorrected_bins, /* arc_correction*/false)); } @@ -163,8 +171,8 @@ open_lm_file() this->current_lm_data_ptr->get_ROOT_filename()); // Read the 4 bytes to check whether this is a ROOT file - std::string mem(4,' '); - std::string sig= "root"; + char mem[5]="\0"; + char sig[5]="root"; std::ifstream t; t.open(this->current_lm_data_ptr->get_ROOT_filename().c_str(), std::ios::in); @@ -173,9 +181,9 @@ open_lm_file() { t.seekg(0, std::ios::beg); - t.read(&mem[0],4); + t.read(mem,4); - if (sig.compare(mem) ) + if (strncmp(sig, mem, 4)) { warning("CListModeDataROOT: File '%s is not a ROOT file!!'", this->current_lm_data_ptr->get_ROOT_filename().c_str()); From 3fc9612e97dada0ff2d2c2369fb497be3f92e13a Mon Sep 17 00:00:00 2001 From: Nikos E Date: Mon, 24 Oct 2016 01:00:15 +0100 Subject: [PATCH 028/170] CListModeData::get_proj_data_info_sptr() became pure virtual --- recon_test_pack/run_root_GATE.sh | 15 +++++++++------ src/include/stir/listmode/CListModeData.h | 4 +++- src/include/stir/listmode/CListModeDataECAT.h | 3 +++ .../stir/listmode/CListModeDataECAT8_32bit.h | 3 +++ src/include/stir/listmode/CListModeDataROOT.h | 4 +++- src/listmode_buildblock/CListModeData.cxx | 8 +------- src/listmode_buildblock/CListModeDataECAT.cxx | 8 ++++++++ .../CListModeDataECAT8_32bit.cxx | 8 ++++++++ src/listmode_buildblock/CListModeDataROOT.cxx | 8 ++++++++ 9 files changed, 46 insertions(+), 15 deletions(-) diff --git a/recon_test_pack/run_root_GATE.sh b/recon_test_pack/run_root_GATE.sh index 4a7d05cbd2..76fd022891 100755 --- a/recon_test_pack/run_root_GATE.sh +++ b/recon_test_pack/run_root_GATE.sh @@ -97,11 +97,13 @@ echo "Number of prompts stored in this time period:" ${true_events} echo "" echo "Counting all values from ROOT file ..." -all_root_num=`root -l ${INPUT_ROOT_FILE} << EOF | grep "Number of prompts stored in this time period" | grep -o -E '[0-9]+' +cat>my_root.input<Draw(">>eventlist","","goff"); Int_t N = eventlist->GetN(); -cout<my_root.input<Draw(">>eventlist","eventID1 == eventID2 && comptonPhantom1 == 0 && comptonPhantom2 == 0","goff"); Int_t N = eventlist->GetN(); -cout< get_proj_data_info_sptr() const; + virtual + shared_ptr get_proj_data_info_sptr() const = 0; protected: //! Has to be initialised by the derived class diff --git a/src/include/stir/listmode/CListModeDataECAT.h b/src/include/stir/listmode/CListModeDataECAT.h index 1990aaf5ea..781ea8c2b5 100644 --- a/src/include/stir/listmode/CListModeDataECAT.h +++ b/src/include/stir/listmode/CListModeDataECAT.h @@ -89,6 +89,9 @@ class CListModeDataECAT : public CListModeData /*! \todo this might depend on the acquisition parameters */ virtual bool has_delayeds() const { return true; } + virtual + shared_ptr get_proj_data_info_sptr() const; + private: std::string listmode_filename_prefix; mutable unsigned int current_lm_file; diff --git a/src/include/stir/listmode/CListModeDataECAT8_32bit.h b/src/include/stir/listmode/CListModeDataECAT8_32bit.h index 3ca76eaec6..22394d67d0 100644 --- a/src/include/stir/listmode/CListModeDataECAT8_32bit.h +++ b/src/include/stir/listmode/CListModeDataECAT8_32bit.h @@ -83,6 +83,9 @@ class CListModeDataECAT8_32bit : public CListModeData error("Not implemented yet. Abort."); }; + virtual + shared_ptr get_proj_data_info_sptr() const = 0; + private: typedef CListRecordECAT8_32bit CListRecordT; std::string listmode_filename; diff --git a/src/include/stir/listmode/CListModeDataROOT.h b/src/include/stir/listmode/CListModeDataROOT.h index 7f82670574..104ff24290 100644 --- a/src/include/stir/listmode/CListModeDataROOT.h +++ b/src/include/stir/listmode/CListModeDataROOT.h @@ -70,6 +70,9 @@ class CListModeDataROOT : public CListModeData unsigned long int get_total_number_of_events() const ; + virtual + shared_ptr get_proj_data_info_sptr() const; + private: // typedef CListRecordROOT CListRecordT; @@ -99,7 +102,6 @@ class CListModeDataROOT : public CListModeData //! Name of input chain which is going to be used. Succeeded open_lm_file(); - }; diff --git a/src/listmode_buildblock/CListModeData.cxx b/src/listmode_buildblock/CListModeData.cxx index 184d4f909f..2cb1266c77 100644 --- a/src/listmode_buildblock/CListModeData.cxx +++ b/src/listmode_buildblock/CListModeData.cxx @@ -55,16 +55,10 @@ const Scanner* CListModeData:: get_scanner_ptr() const { + assert(!is_null_ptr(scanner_sptr)); return this->scanner_sptr.get(); } -shared_ptr -CListModeData:: -get_proj_data_info_sptr() const -{ - return proj_data_info_sptr; -} - #if 0 std::time_t CListModeData:: diff --git a/src/listmode_buildblock/CListModeDataECAT.cxx b/src/listmode_buildblock/CListModeDataECAT.cxx index 9ad6fdf837..bf4d101749 100644 --- a/src/listmode_buildblock/CListModeDataECAT.cxx +++ b/src/listmode_buildblock/CListModeDataECAT.cxx @@ -329,6 +329,14 @@ get_num_records() const #endif +template +shared_ptr +CListModeDataECAT:: +get_proj_data_info_sptr() const +{ + assert(!is_null_ptr(proj_data_info_sptr)); + return proj_data_info_sptr; +} // instantiations template class CListModeDataECAT; diff --git a/src/listmode_buildblock/CListModeDataECAT8_32bit.cxx b/src/listmode_buildblock/CListModeDataECAT8_32bit.cxx index 2360926562..d707b76b21 100644 --- a/src/listmode_buildblock/CListModeDataECAT8_32bit.cxx +++ b/src/listmode_buildblock/CListModeDataECAT8_32bit.cxx @@ -164,5 +164,13 @@ set_get_position(const CListModeDataECAT8_32bit::SavedPosition& pos) current_lm_data_ptr->set_get_position(pos); } +shared_ptr +CListModeDataECAT8_32bit:: +get_proj_data_info_sptr() const +{ + assert(!is_null_ptr(proj_data_info_sptr)); + return proj_data_info_sptr; +} + } // namespace ecat END_NAMESPACE_STIR diff --git a/src/listmode_buildblock/CListModeDataROOT.cxx b/src/listmode_buildblock/CListModeDataROOT.cxx index 67d0c9daa4..73945cdecf 100644 --- a/src/listmode_buildblock/CListModeDataROOT.cxx +++ b/src/listmode_buildblock/CListModeDataROOT.cxx @@ -238,5 +238,13 @@ set_get_position(const CListModeDataROOT::SavedPosition& pos) return current_lm_data_ptr->set_get_position(pos); } +shared_ptr +CListModeDataROOT:: +get_proj_data_info_sptr() const +{ + assert(!is_null_ptr(proj_data_info_sptr)); + return proj_data_info_sptr; +} + END_NAMESPACE_STIR From d955723612ee6a53789f25e9359a86d2f348547b Mon Sep 17 00:00:00 2001 From: Nikos E Date: Mon, 24 Oct 2016 08:08:51 +0100 Subject: [PATCH 029/170] minor correction --- src/include/stir/listmode/CListModeDataECAT8_32bit.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/include/stir/listmode/CListModeDataECAT8_32bit.h b/src/include/stir/listmode/CListModeDataECAT8_32bit.h index 22394d67d0..af4d69ece7 100644 --- a/src/include/stir/listmode/CListModeDataECAT8_32bit.h +++ b/src/include/stir/listmode/CListModeDataECAT8_32bit.h @@ -84,7 +84,7 @@ class CListModeDataECAT8_32bit : public CListModeData }; virtual - shared_ptr get_proj_data_info_sptr() const = 0; + shared_ptr get_proj_data_info_sptr() const; private: typedef CListRecordECAT8_32bit CListRecordT; From dcbdf36ac04329cc3152825783f1a9d3bfa6f9c0 Mon Sep 17 00:00:00 2001 From: Nikos E Date: Mon, 24 Oct 2016 11:41:25 +0100 Subject: [PATCH 030/170] One more minor bug found --- ...WithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx index f5dd6bc1b8..f79fb6c043 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx @@ -289,7 +289,7 @@ PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBinadditive_proj_data_sptr->get_proj_data_info_sptr())) + if(!is_null_ptr(this->additive_proj_data_sptr)) if (*(this->additive_proj_data_sptr->get_proj_data_info_sptr()) != *proj_data_info_cyl_sptr) { const ProjDataInfo& add_proj = *(this->additive_proj_data_sptr->get_proj_data_info_sptr()); From f409f8cb7e09bbf858c3fbd79a5711a94ab9f778 Mon Sep 17 00:00:00 2001 From: NikEfth Date: Wed, 21 Dec 2016 18:31:28 +0000 Subject: [PATCH 031/170] First commit with code for TOF-lm reconstruction. This code is transfered from other branches and is cleaned up and commented. --- src/buildblock/ProjDataInfo.cxx | 125 ++++++++++++++++++ src/buildblock/ProjDataInfoCylindrical.cxx | 74 ++++++++++- .../ProjDataInfoCylindricalNoArcCorr.cxx | 37 ++++++ src/include/stir/Bin.h | 30 ++++- src/include/stir/Bin.inl | 37 +++++- src/include/stir/IO/InputStreamFromROOTFile.h | 5 + src/include/stir/ProjData.h | 10 ++ src/include/stir/ProjData.inl | 14 ++ src/include/stir/ProjDataInfo.h | 46 +++++++ 9 files changed, 369 insertions(+), 9 deletions(-) diff --git a/src/buildblock/ProjDataInfo.cxx b/src/buildblock/ProjDataInfo.cxx index ef7cc292f3..4e0b0eb1a2 100644 --- a/src/buildblock/ProjDataInfo.cxx +++ b/src/buildblock/ProjDataInfo.cxx @@ -4,6 +4,7 @@ Copyright (C) 2000 PARAPET partners Copyright (C) 2000 - 2009-05-13, Hammersmith Imanet Ltd Copyright (C) 2011-07-01 - 2011, Kris Thielemans + Copyright (C) 2016, University of Hull This file is part of STIR. This file is free software; you can redistribute it and/or modify @@ -23,6 +24,7 @@ \brief Implementation of non-inline functions of class stir::ProjDataInfo + \author Nikos Efthimiou \author Sanida Mustafovic \author Kris Thielemans \author PARAPET project @@ -56,6 +58,8 @@ #include #endif +#include "stir/info.h" +#include "boost/foreach.hpp" #ifndef STIR_NO_NAMESPACES using std::string; @@ -68,6 +72,24 @@ using std::equal; START_NAMESPACE_STIR +float +ProjDataInfo::get_k(const Bin& bin) const +{ + if (!get_num_timing_poss()%2) + return bin.timing_pos_num() * timing_increament_in_mm; + else + return (bin.timing_pos_num() * timing_increament_in_mm) - timing_increament_in_mm/2.f; +} + +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 @@ -157,6 +179,89 @@ 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 new_num) +{ + if (scanner_ptr->is_tof_ready()) + { + if(tof_mash_factor < 0 || tof_mash_factor > scanner_ptr->get_num_max_of_timing_bins()) + error("ProjDataInfo: TOF mashing factor must be positive and smaller or equal than" + "the scanner's number of max timing bins. Abort."); + tof_mash_factor = new_num; + + timing_increament_in_mm = tof_mash_factor * scanner_ptr->get_size_of_timing_bin() * 0.299792458f; + + // limit to the size of the diameter. + + Bin b; + + float lowest_t = get_min_tangential_pos_num(); + float highest_t = get_max_tangential_pos_num(); + + b.tangential_pos_num() = lowest_t; + float k = get_sampling_in_t(b); + b.tangential_pos_num() = highest_t; + float l = get_sampling_in_t(b); + + float lowest_boundary = lowest_t * k; + float highest_boundary = highest_t * l; + + int num_tof_positions_in_FOV = static_cast(((highest_boundary - lowest_boundary) / (2.f * timing_increament_in_mm))+0.5); + + min_timing_pos_num = -num_tof_positions_in_FOV/2; + max_timing_pos_num = min_timing_pos_num + num_tof_positions_in_FOV; + + // Upper and lower boundaries of the timing poss; + timing_bin_boundaries.grow(min_timing_pos_num, max_timing_pos_num); + + for (int i = min_timing_pos_num; i <= max_timing_pos_num; ++i ) + { + Bin bin; + bin.timing_pos_num() = i; + + float cur_low = get_k(bin); + float cur_high = get_k(bin) + get_sampling_in_k(bin); + + if (cur_low< 0 || cur_high < 0 ) + { + if (cur_low < lowest_boundary && cur_high > lowest_boundary) + { + timing_bin_boundaries[i].low_lim = lowest_boundary; + timing_bin_boundaries[i].high_lim = cur_high; + } + else if (cur_low >= lowest_boundary && cur_high >= lowest_boundary) + { + timing_bin_boundaries[i].low_lim = cur_low; + timing_bin_boundaries[i].high_lim = cur_high; + } + } + else + { + if (cur_high > highest_boundary && cur_low < highest_boundary) + { + timing_bin_boundaries[i].low_lim = cur_low; + timing_bin_boundaries[i].high_lim = highest_boundary; + } + else if (cur_low <= highest_boundary && cur_high <= highest_boundary) + { + timing_bin_boundaries[i].low_lim = cur_low; + timing_bin_boundaries[i].high_lim = cur_high; + } + } + float lowt = (timing_bin_boundaries[i].low_lim / 0.299792458f ) ; + float hight = ( timing_bin_boundaries[i].high_lim/ 0.299792458f); + info(boost::format("Tbin %1%: %2% - %3% mm (%4% - %5% ps) = %6%") %i % timing_bin_boundaries[i].low_lim % timing_bin_boundaries[i].high_lim + % lowt% hight % get_sampling_in_k(bin)); + // Go to natural coordinates - opted out. + // timing_bin_boundaries[i].low_lim *= r_sqrtdPI_gauss_sigma; + // timing_bin_boundaries[i].high_lim *= r_sqrtdPI_gauss_sigma; + } + } + else + error("Not TOF compatible scanner template. Abort."); +} + ProjDataInfo::ProjDataInfo() {} @@ -174,6 +279,26 @@ 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_timing_pos_num = 0; + max_timing_pos_num = 0; + timing_increament_in_mm = 0.f; + tof_mash_factor = 1; // zero? +} + +// 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 diff --git a/src/buildblock/ProjDataInfoCylindrical.cxx b/src/buildblock/ProjDataInfoCylindrical.cxx index f7ed2e159e..808e861b34 100644 --- a/src/buildblock/ProjDataInfoCylindrical.cxx +++ b/src/buildblock/ProjDataInfoCylindrical.cxx @@ -3,6 +3,7 @@ Copyright (C) 2000 - 2009-10-18 Hammersmith Imanet Ltd Copyright (C) 2011, Kris Thielemans Copyright (C) 2013, University College London + Copyright (C) 2016, University of Hull This file is part of STIR. @@ -25,6 +26,7 @@ \brief Non-inline implementations of stir::ProjDataInfoCylindrical + \author Nikos Efthimiou \author Kris Thielemans \author Sanida Mustafovic \author PARAPET project @@ -454,6 +456,15 @@ compute_segment_axial_pos_to_ring_pair(const int segment_num, const int axial_po } } +void +ProjDataInfoCylindrical:: +set_tof_mash_factor(const int new_num) +{ + base_type::set_tof_mash_factor(new_num); + //! \todo N.E. Would be nice to have all the points of the scanner in cache. + //initialise_uncompressed_lor_as_point1point2(); +} + void ProjDataInfoCylindrical:: set_num_axial_poss_per_segment(const VectorWithOffset& num_axial_poss_per_segment) @@ -538,7 +549,68 @@ get_LOR(LORInAxialAndNoArcCorrSinogramCoordinates& lor, asin(s_in_mm/get_ring_radius()), get_ring_radius()); } - + +void +ProjDataInfoCylindrical:: +get_LOR_as_two_points(CartesianCoordinate3D& coord_1, + CartesianCoordinate3D& coord_2, + const Bin& bin) const +{ + const float s_in_mm = get_s(bin); + const float m_in_mm = get_m(bin); + const float tantheta = get_tantheta(bin); + const float phi = get_phi(bin); + /* parametrisation of LOR is + X= s*cphi + a*sphi, + Y= s*sphi - a*cphi, + Z= m - a*tantheta + find now min_a, max_a such that end-points intersect the ring + */ + assert(fabs(s_in_mm) < get_ring_radius()); + // a has to be such that X^2+Y^2 == R^2 + const float max_a = sqrt(square(get_ring_radius()) - square(s_in_mm)); + const float min_a = -max_a; + + coord_1.x() = s_in_mm*cos(phi) + min_a*sin(phi); + coord_1.y() = s_in_mm*sin(phi) - max_a*cos(phi); + coord_1.z() = m_in_mm - max_a*tantheta; + + coord_2.x() = s_in_mm*cos(phi) + max_a*sin(phi); + coord_2.y() = s_in_mm*sin(phi) - min_a*cos(phi); + coord_2.z() = m_in_mm - min_a*tantheta; +} + +void +ProjDataInfoCylindrical:: +get_LOR_as_two_points_alt(CartesianCoordinate3D& coord_1, + CartesianCoordinate3D& coord_2, + const Bin& bin) const +{ + const int num_detectors_per_ring = + get_scanner_ptr()->get_num_detectors_per_ring(); + + float h_scanner_height = ( (get_scanner_ptr()->get_ring_spacing() -1) * get_scanner_ptr()->get_num_rings())/2.F; + + // although code maybe doesn't really need the following, + // asserts in the LOR code will break if these conditions are not satisfied. + assert(0<=det1); + assert(det1 cyl_coords(get_scanner_ptr()->get_inner_ring_radius()); + + cyl_coords.p1().psi() = static_cast((2.*_PI/num_detectors_per_ring)*(det1)); + cyl_coords.p2().psi() = static_cast((2.*_PI/num_detectors_per_ring)*(det2)); + + cyl_coords.p1().z() = Ring_A*get_scanner_ptr()->get_ring_spacing() - h_scanner_height; + cyl_coords.p2().z() = Ring_B*get_scanner_ptr()->get_ring_spacing() - h_scanner_height; + + LORAs2Points lor(cyl_coords); + coord_1 = lor.p1(); + coord_2 = lor.p2(); +} + string ProjDataInfoCylindrical::parameter_info() const { diff --git a/src/buildblock/ProjDataInfoCylindricalNoArcCorr.cxx b/src/buildblock/ProjDataInfoCylindricalNoArcCorr.cxx index cc0afd2010..35754c5242 100644 --- a/src/buildblock/ProjDataInfoCylindricalNoArcCorr.cxx +++ b/src/buildblock/ProjDataInfoCylindricalNoArcCorr.cxx @@ -3,6 +3,7 @@ /* Copyright (C) 2000- 2007-10-08, Hammersmith Imanet Ltd Copyright (C) 2011-07-01 - 2011, Kris Thielemans + Copyright (C) 2016, University of Hull This file is part of STIR. This file is free software; you can redistribute it and/or modify @@ -24,6 +25,7 @@ \brief Implementation of non-inline functions of class stir::ProjDataInfoCylindricalNoArcCorr + \author Nikos Efthimiou \author Kris Thielemans */ @@ -496,6 +498,41 @@ find_cartesian_coordinates_given_scanner_coordinates (CartesianCoordinate3D& coord_1, + CartesianCoordinate3D& coord_2, + const int Ring_A,const int Ring_B, + const int det1, const int det2) const +{ + const int num_detectors_per_ring = + get_scanner_ptr()->get_num_detectors_per_ring(); + +// float h_scanner_height = ( (get_scanner_ptr()->get_ring_spacing() -1) * get_scanner_ptr()->get_num_rings())/2.F; + + // although code maybe doesn't really need the following, + // asserts in the LOR code will break if these conditions are not satisfied. + assert(0<=det1); + assert(det1 cyl_coords(get_scanner_ptr()->get_inner_ring_radius()); + + cyl_coords.p1().psi() = static_cast((2.*_PI/num_detectors_per_ring)*(det1)); + cyl_coords.p2().psi() = static_cast((2.*_PI/num_detectors_per_ring)*(det2)); + +// cyl_coords.p1().z() = Ring_A*get_scanner_ptr()->get_ring_spacing() - h_scanner_height; +// cyl_coords.p2().z() = Ring_B*get_scanner_ptr()->get_ring_spacing() - h_scanner_height; + + LORAs2Points lor(cyl_coords); + coord_1 = lor.p1(); + coord_2 = lor.p2(); +} + + void ProjDataInfoCylindricalNoArcCorr:: find_bin_given_cartesian_coordinates_of_detection(Bin& bin, diff --git a/src/include/stir/Bin.h b/src/include/stir/Bin.h index b9e445653e..3b79c91429 100644 --- a/src/include/stir/Bin.h +++ b/src/include/stir/Bin.h @@ -7,7 +7,7 @@ \brief Declaration of class stir::Bin - + \author Nikos Efthimiou \author Sanida Mustafovic \author Kris Thielemans \author Mustapha Sadki @@ -17,6 +17,7 @@ /* Copyright (C) 2000 PARAPET partners Copyright (C) 2000- 2009, Hammersmith Imanet Ltd + Copyright (C) 2016, University of Hull This file is part of STIR. This file is free software; you can redistribute it and/or modify @@ -43,6 +44,17 @@ START_NAMESPACE_STIR \brief A class for storing coordinates and value of a single projection bin. + The timing position reflect the detection time difference between the two events. + It is a multiple of the delta t of the least significant clock bit. + + \details In order to go around the timing pos which is set only in TOF reconstruction + we set it, by default, to zero. If it is zero in both bins then it is omitted from the + comparison. + \warning Comparison between bin with and without timing position is of high risk! + + \warning Constructors with default values were removed. + + \warning Temporarily the timing_pos_num is not taken into account when comparing two bins. */ class Bin @@ -53,7 +65,16 @@ class Bin //! A constructor : constructs a bin with value (defaulting to 0) inline Bin(int segment_num,int view_num, int axial_pos_num, - int tangential_pos_num,float bin_value=0); + int tangential_pos_num,float bin_value); + + inline Bin(int segment_num, int view_num, int axial_pos_num, + int tangential_pos_num); + + inline Bin(int segment_num, int view_num, int axial_pos_num, + int tangential_pos_num, int timing_pos_num, float bin_value); + + inline Bin(int segment_num, int view_num, int axial_pos_num, + int tangential_pos_num, int timing_pos_num); //!get axial position number inline int axial_pos_num()const; @@ -63,6 +84,8 @@ class Bin inline int tangential_pos_num() const; //! get view number inline int view_num() const; + //! get timing position number + inline int timing_pos_num() const; inline int& axial_pos_num(); inline int& segment_num(); @@ -96,7 +119,8 @@ private : int segment; int view; int axial_pos; - int tangential_pos; + int tangential_pos; + int timing_pos; float bin_value; diff --git a/src/include/stir/Bin.inl b/src/include/stir/Bin.inl index 7fe9d8ac3b..4f0e37bd20 100644 --- a/src/include/stir/Bin.inl +++ b/src/include/stir/Bin.inl @@ -7,6 +7,7 @@ \brief Implementations of inline functions of class stir::Bin + \author Nikos Efthimiou \author Sanida Mustafovic \author Kris Thielemans \author PARAPET project @@ -15,6 +16,7 @@ /* Copyright (C) 2000 PARAPET partners Copyright (C) 2000- 2009, Hammersmith Imanet Ltd + Copyright (C) 2016, University of Hull This file is part of STIR. This file is free software; you can redistribute it and/or modify @@ -32,13 +34,29 @@ START_NAMESPACE_STIR -Bin::Bin() +Bin::Bin():segment(0),view(0), + axial_pos(0),tangential_pos(0),timing_pos(0), bin_value(0.0f) {} -Bin::Bin(int segment_num,int view_num, int axial_pos_num,int tangential_pos_num,float bin_value) - :segment(segment_num),view(view_num), - axial_pos(axial_pos_num),tangential_pos(tangential_pos_num),bin_value(bin_value) +Bin::Bin(int segment_num,int view_num, int axial_pos_num,int tangential_pos_num, float bin_value) + :segment(segment_num),view(view_num), + axial_pos(axial_pos_num),tangential_pos(tangential_pos_num), bin_value(bin_value), timing_pos(0) + {} + +Bin::Bin(int segment_num,int view_num, int axial_pos_num,int tangential_pos_num) + :segment(segment_num),view(view_num), + axial_pos(axial_pos_num),tangential_pos(tangential_pos_num),timing_pos(0), bin_value(0.0f) + {} + +Bin::Bin(int segment_num,int view_num, int axial_pos_num,int tangential_pos_num, int timing_pos_num, float bin_value) + :segment(segment_num),view(view_num), + axial_pos(axial_pos_num),tangential_pos(tangential_pos_num), timing_pos(timing_pos_num), bin_value(bin_value) + {} + +Bin::Bin(int segment_num,int view_num, int axial_pos_num,int tangential_pos_num, int timing_pos_num) + :segment(segment_num),view(view_num), + axial_pos(axial_pos_num),tangential_pos(tangential_pos_num), timing_pos(timing_pos_num), bin_value(0.0f) {} @@ -58,6 +76,10 @@ int Bin::view_num() const { return view;} +int +Bin::timing_pos_num() const +{ return timing_pos; } + int& Bin::axial_pos_num() { return axial_pos;} @@ -74,6 +96,10 @@ int& Bin:: view_num() { return view;} +int& +Bin:: timing_pos_num() +{ return timing_pos;} + #if 0 const ProjDataInfo * Bin::get_proj_data_info_ptr() const @@ -86,7 +112,7 @@ Bin Bin::get_empty_copy() const { - Bin copy(segment_num(),view_num(),axial_pos_num(),tangential_pos_num(),0); + Bin copy(segment_num(),view_num(),axial_pos_num(),tangential_pos_num(),timing_pos_num(), 0.f); return copy; } @@ -111,6 +137,7 @@ Bin::operator==(const Bin& bin2) const return segment == bin2.segment && view == bin2.view && axial_pos == bin2.axial_pos && tangential_pos == bin2.tangential_pos && +// && timing_pos == bin2.timing_pos bin_value == bin2.bin_value; } diff --git a/src/include/stir/IO/InputStreamFromROOTFile.h b/src/include/stir/IO/InputStreamFromROOTFile.h index 0ff6b63a4b..30e2d5efa7 100644 --- a/src/include/stir/IO/InputStreamFromROOTFile.h +++ b/src/include/stir/IO/InputStreamFromROOTFile.h @@ -9,6 +9,7 @@ /* * Copyright (C) 2015, 2016 University of Leeds Copyright (C) 2016, UCL + Copyright (C) 2016, University of Hull This file is part of STIR. This file is free software; you can redistribute it and/or modify @@ -158,6 +159,10 @@ class InputStreamFromROOTFile : public RegisteredObject< InputStreamFromROOTFile int offset_dets; int singles_readout_depth; + + // This member will try to give to the continuous time register in GATE + // data, a finite least significant bit. + double least_significant_clock_bit; }; END_NAMESPACE_STIR diff --git a/src/include/stir/ProjData.h b/src/include/stir/ProjData.h index 5f61c0cb4f..1bf1227f84 100644 --- a/src/include/stir/ProjData.h +++ b/src/include/stir/ProjData.h @@ -70,6 +70,8 @@ class Succeeded;
  1. \c tangential_pos_num : indexes different positions in a direction tangential to the scanner cylinder. (sometimes called 'bin' or 'element') +
  2. axial (i.e. positive vs. negative segment): \a do_symmetry_swap_segment
  3. @@ -87,23 +86,22 @@ class DataSymmetriesForBins_PET_CartesianGrid : public DataSymmetriesForBins The symmetry in phi is automatically reduced for non-square grids or when the number of views is not a multiple of 4. - */ + */ DataSymmetriesForBins_PET_CartesianGrid(const shared_ptr& proj_data_info_ptr, - const shared_ptr >& image_info_ptr, + const shared_ptr>& image_info_ptr, const bool do_symmetry_90degrees_min_phi = true, const bool do_symmetry_180degrees_min_phi = true, - const bool do_symmetry_swap_segment = true, - const bool do_symmetry_swap_s = true, - const bool do_symmetry_shift_z = true); + const bool do_symmetry_swap_segment = true, const bool do_symmetry_swap_s = true, + const bool do_symmetry_shift_z = true); - - virtual + virtual #ifndef STIR_NO_COVARIANT_RETURN_TYPES - DataSymmetriesForBins_PET_CartesianGrid + DataSymmetriesForBins_PET_CartesianGrid #else - DataSymmetriesForViewSegmentNumbers + DataSymmetriesForViewSegmentNumbers #endif - * clone() const; + * + clone() const; //! Check equality virtual bool operator==(const DataSymmetriesForBins_PET_CartesianGrid&) const; @@ -115,38 +113,29 @@ class DataSymmetriesForBins_PET_CartesianGrid : public DataSymmetriesForBins get_basic_bin_index_range() const; #endif - inline void - get_related_bins_factorised(std::vector&, const Bin& b, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) const; - - inline int - num_related_bins(const Bin& b) const; - - inline unique_ptr - find_symmetry_operation_from_basic_bin(Bin&) const; - - inline bool - find_basic_bin(Bin& b) const; - - inline int - num_related_view_segment_numbers(const ViewSegmentNumbers& vs) const; - - inline void - get_related_view_segment_numbers(std::vector& rel_vs, const ViewSegmentNumbers& vs) const; - - inline bool - find_basic_view_segment_numbers(ViewSegmentNumbers& v_s) const; + inline void get_related_bins_factorised(std::vector&, const Bin& b, const int min_axial_pos_num, + const int max_axial_pos_num, const int min_tangential_pos_num, + const int max_tangential_pos_num) const; - //! find out how many image planes there are for every scanner ring - inline float get_num_planes_per_scanner_ring() const; + inline int num_related_bins(const Bin& b) const; + + inline unique_ptr find_symmetry_operation_from_basic_bin(Bin&) const; + + inline bool find_basic_bin(Bin& b) const; + + inline int num_related_view_segment_numbers(const ViewSegmentNumbers& vs) const; + inline void get_related_view_segment_numbers(std::vector& rel_vs, const ViewSegmentNumbers& vs) const; + inline bool find_basic_view_segment_numbers(ViewSegmentNumbers& v_s) const; + + //! find out how many image planes there are for every scanner ring + inline float get_num_planes_per_scanner_ring() const; //! find correspondence between axial_pos_num and image coordinates /*! z = num_planes_per_axial_pos * axial_pos_num + axial_pos_to_z_offset - - compute the offset by matching up the centre of the scanner + + compute the offset by matching up the centre of the scanner in the 2 coordinate systems */ inline float get_num_planes_per_axial_pos(const int segment_num) const; @@ -154,16 +143,11 @@ class DataSymmetriesForBins_PET_CartesianGrid : public DataSymmetriesForBins //! \name Methods to find out which symmetries are used //@{ - inline bool using_symmetry_90degrees_min_phi() const - { return do_symmetry_90degrees_min_phi; } - inline bool using_symmetry_180degrees_min_phi() const - { return do_symmetry_180degrees_min_phi; } - inline bool using_symmetry_swap_segment() const - { return do_symmetry_swap_segment; } - inline bool using_symmetry_swap_s() const - { return do_symmetry_swap_s; } - inline bool using_symmetry_shift_z() const - { return do_symmetry_shift_z; } + inline bool using_symmetry_90degrees_min_phi() const { return do_symmetry_90degrees_min_phi; } + inline bool using_symmetry_180degrees_min_phi() const { return do_symmetry_180degrees_min_phi; } + inline bool using_symmetry_swap_segment() const { return do_symmetry_swap_segment; } + inline bool using_symmetry_swap_s() const { return do_symmetry_swap_s; } + inline bool using_symmetry_shift_z() const { return do_symmetry_shift_z; } //@} private: @@ -172,7 +156,7 @@ class DataSymmetriesForBins_PET_CartesianGrid : public DataSymmetriesForBins bool do_symmetry_swap_segment; bool do_symmetry_swap_s; bool do_symmetry_shift_z; - //const shared_ptr& proj_data_info_ptr; + // const shared_ptr& proj_data_info_ptr; int num_views; int num_planes_per_scanner_ring; //! a list of values for every segment_num @@ -191,30 +175,15 @@ class DataSymmetriesForBins_PET_CartesianGrid : public DataSymmetriesForBins cartesian_grid_info_ptr() const; #endif - virtual bool blindly_equals(const root_type * const) const; - - - inline bool - find_basic_bin(int &segment_num, int &view_num, int &axial_pos_num, int &tangential_pos_num) const; - - - inline int find_transform_z( - const int segment_num, - const int axial_pos_num) const; - - inline SymmetryOperation* - find_sym_op_general_bin( - int s, - int seg, - int view_num, - int axial_pos_num) const; - - inline SymmetryOperation* - find_sym_op_bin0( - int seg, - int view_num, - int axial_pos_num) const; - + virtual bool blindly_equals(const root_type* const) const; + + inline bool find_basic_bin(int& segment_num, int& view_num, int& axial_pos_num, int& tangential_pos_num) const; + + inline int find_transform_z(const int segment_num, const int axial_pos_num) const; + + inline SymmetryOperation* find_sym_op_general_bin(int s, int seg, int view_num, int axial_pos_num) const; + + inline SymmetryOperation* find_sym_op_bin0(int seg, int view_num, int axial_pos_num) const; }; END_NAMESPACE_STIR diff --git a/src/include/stir/recon_buildblock/DataSymmetriesForBins_PET_CartesianGrid.inl b/src/include/stir/recon_buildblock/DataSymmetriesForBins_PET_CartesianGrid.inl index 9478c769bc..158cc588a1 100644 --- a/src/include/stir/recon_buildblock/DataSymmetriesForBins_PET_CartesianGrid.inl +++ b/src/include/stir/recon_buildblock/DataSymmetriesForBins_PET_CartesianGrid.inl @@ -34,7 +34,6 @@ #include "stir/ProjDataInfoCylindrical.h" #include "stir/recon_buildblock/SymmetryOperations_PET_CartesianGrid.h" - START_NAMESPACE_STIR #if 0 @@ -49,113 +48,87 @@ cartesian_grid_info_ptr() const #endif float -DataSymmetriesForBins_PET_CartesianGrid:: -get_num_planes_per_axial_pos(const int segment_num) const -{ +DataSymmetriesForBins_PET_CartesianGrid::get_num_planes_per_axial_pos(const int segment_num) const { return static_cast(num_planes_per_axial_pos[segment_num]); } float -DataSymmetriesForBins_PET_CartesianGrid:: -get_num_planes_per_scanner_ring() const -{ +DataSymmetriesForBins_PET_CartesianGrid::get_num_planes_per_scanner_ring() const { return static_cast(num_planes_per_scanner_ring); } -float -DataSymmetriesForBins_PET_CartesianGrid:: -get_axial_pos_to_z_offset(const int segment_num) const -{ +float +DataSymmetriesForBins_PET_CartesianGrid::get_axial_pos_to_z_offset(const int segment_num) const { return axial_pos_to_z_offset[segment_num]; -} +} int -DataSymmetriesForBins_PET_CartesianGrid:: -find_transform_z( - const int segment_num, - const int axial_pos_num) const -{ - const ProjDataInfoCylindrical* proj_data_info_cyl_ptr = - static_cast(proj_data_info_ptr.get()); +DataSymmetriesForBins_PET_CartesianGrid::find_transform_z(const int segment_num, const int axial_pos_num) const { + const ProjDataInfoCylindrical* proj_data_info_cyl_ptr = static_cast(proj_data_info_ptr.get()); const float delta = proj_data_info_cyl_ptr->get_average_ring_difference(segment_num); - // Find symmetric value in Z by 'mirroring' it around the centre z of the LOR: // Z+Q = 2*centre_of_LOR_in_image_coordinates == transform_z { // first compute it as floating point (although it has to be an int really) - const float transform_z_float = (2*num_planes_per_axial_pos[segment_num]*(axial_pos_num) - + num_planes_per_scanner_ring*delta - + 2*axial_pos_to_z_offset[segment_num]); + const float transform_z_float = (2 * num_planes_per_axial_pos[segment_num] * (axial_pos_num) + + num_planes_per_scanner_ring * delta + 2 * axial_pos_to_z_offset[segment_num]); // now use rounding to be safe int transform_z = (int)floor(transform_z_float + 0.5); - assert(fabs(transform_z-transform_z_float) < 10E-4); + assert(fabs(transform_z - transform_z_float) < 10E-4); return transform_z; } } SymmetryOperation* -DataSymmetriesForBins_PET_CartesianGrid:: -find_sym_op_bin0( - int segment_num, - int view_num, - int axial_pos_num) const -{ +DataSymmetriesForBins_PET_CartesianGrid::find_sym_op_bin0(int segment_num, int view_num, int axial_pos_num) const { // note: if do_symmetry_shift_z==true, then basic axial_pos_num will be 0 - const int transform_z = - find_transform_z(abs(segment_num), - do_symmetry_shift_z ? 0 : axial_pos_num); - - const int z_shift = - do_symmetry_shift_z ? - num_planes_per_axial_pos[segment_num]*axial_pos_num - : 0; - + const int transform_z = find_transform_z(abs(segment_num), do_symmetry_shift_z ? 0 : axial_pos_num); + + const int z_shift = do_symmetry_shift_z ? num_planes_per_axial_pos[segment_num] * axial_pos_num : 0; + const int view180 = num_views; // TODO get rid of next 2 restrictions - assert(!do_symmetry_180degrees_min_phi || view_num>=0); - assert(!do_symmetry_180degrees_min_phi || view_num= 0); + assert(!do_symmetry_180degrees_min_phi || view_num < num_views); #ifndef NDEBUG - // This variable is only used in assert() at the moment, so avoid compiler + // This variable is only used in assert() at the moment, so avoid compiler // warning by defining it only when in debug mode - const int view0 = 0; + const int view0 = 0; #endif - const int view135 = view180/4*3; - const int view90 = view180/2; - const int view45 = view180/4; - - if ( do_symmetry_90degrees_min_phi && view_num > view90 && view_num <= view135) { //(90, 135 ] - if ( !do_symmetry_swap_segment || segment_num >= 0) - return new SymmetryOperation_PET_CartesianGrid_swap_xmy_yx(view180, axial_pos_num, z_shift); - else - return new SymmetryOperation_PET_CartesianGrid_swap_xmy_yx_zq(view180, axial_pos_num, z_shift, transform_z); // seg < 0 - } - else if ( do_symmetry_90degrees_min_phi && view_num > view45 && view_num <= view90 ) { // [ 45, 90] - if ( !do_symmetry_swap_segment || segment_num >= 0) - return new SymmetryOperation_PET_CartesianGrid_swap_xy_yx_zq(view180, axial_pos_num, z_shift, transform_z); + const int view135 = view180 / 4 * 3; + const int view90 = view180 / 2; + const int view45 = view180 / 4; + + if (do_symmetry_90degrees_min_phi && view_num > view90 && view_num <= view135) { //(90, 135 ] + if (!do_symmetry_swap_segment || segment_num >= 0) + return new SymmetryOperation_PET_CartesianGrid_swap_xmy_yx(view180, axial_pos_num, z_shift); else - return new SymmetryOperation_PET_CartesianGrid_swap_xy_yx(view180, axial_pos_num, z_shift); // seg < 0 //KT???????????? different for view90, TODO - } - else if( do_symmetry_180degrees_min_phi && view_num > view90/* && view_num <= view180 */){ // (135, 180) but (90,180) for reduced symmetry case - if( !do_symmetry_swap_segment || segment_num >= 0) - return new SymmetryOperation_PET_CartesianGrid_swap_xmx_zq(view180, axial_pos_num, z_shift, transform_z); - else - return new SymmetryOperation_PET_CartesianGrid_swap_xmx(view180, axial_pos_num, z_shift); // seg < 0 - } - else - { - assert( !do_symmetry_90degrees_min_phi || (view_num >= view0 && view_num <= view45)); - assert( !do_symmetry_180degrees_min_phi || (view_num >= view0 && view_num <= view90)); - if ( do_symmetry_swap_segment && segment_num < 0) - return new SymmetryOperation_PET_CartesianGrid_swap_zq(view180, axial_pos_num, z_shift, transform_z); + return new SymmetryOperation_PET_CartesianGrid_swap_xmy_yx_zq(view180, axial_pos_num, z_shift, transform_z); // seg < 0 + } else if (do_symmetry_90degrees_min_phi && view_num > view45 && view_num <= view90) { // [ 45, 90] + if (!do_symmetry_swap_segment || segment_num >= 0) + return new SymmetryOperation_PET_CartesianGrid_swap_xy_yx_zq(view180, axial_pos_num, z_shift, transform_z); else - { - if (z_shift==0) - return new TrivialSymmetryOperation(); + return new SymmetryOperation_PET_CartesianGrid_swap_xy_yx( + view180, axial_pos_num, z_shift); // seg < 0 //KT???????????? different for view90, TODO + } else if (do_symmetry_180degrees_min_phi && + view_num > view90 /* && view_num <= view180 */) { // (135, 180) but (90,180) for reduced symmetry case + if (!do_symmetry_swap_segment || segment_num >= 0) + return new SymmetryOperation_PET_CartesianGrid_swap_xmx_zq(view180, axial_pos_num, z_shift, transform_z); + else + return new SymmetryOperation_PET_CartesianGrid_swap_xmx(view180, axial_pos_num, z_shift); // seg < 0 + } else { + assert(!do_symmetry_90degrees_min_phi || (view_num >= view0 && view_num <= view45)); + assert(!do_symmetry_180degrees_min_phi || (view_num >= view0 && view_num <= view90)); + if (do_symmetry_swap_segment && segment_num < 0) + return new SymmetryOperation_PET_CartesianGrid_swap_zq(view180, axial_pos_num, z_shift, transform_z); + else { + if (z_shift == 0) + return new TrivialSymmetryOperation(); else return new SymmetryOperation_PET_CartesianGrid_z_shift(axial_pos_num, z_shift); } @@ -163,329 +136,282 @@ find_sym_op_bin0( } // from symmetries -SymmetryOperation* -DataSymmetriesForBins_PET_CartesianGrid:: -find_sym_op_general_bin( - int s, - int segment_num, - int view_num, - int axial_pos_num) const -{ +SymmetryOperation* +DataSymmetriesForBins_PET_CartesianGrid::find_sym_op_general_bin(int s, int segment_num, int view_num, int axial_pos_num) const { // note: if do_symmetry_shift_z==true, then basic axial_pos_num will be 0 - const int transform_z = - find_transform_z(abs(segment_num), - do_symmetry_shift_z ? 0 : axial_pos_num); - - const int z_shift = - do_symmetry_shift_z ? - num_planes_per_axial_pos[segment_num]*axial_pos_num - : 0; - -// TODO get rid of next 2 restrictions - assert(!do_symmetry_180degrees_min_phi || view_num>=0); - assert(!do_symmetry_180degrees_min_phi || view_num= 0); + assert(!do_symmetry_180degrees_min_phi || view_num < num_views); const int view180 = num_views; #ifndef NDEBUG - // This variable is only used in assert() at the moment, so avoid compiler + // This variable is only used in assert() at the moment, so avoid compiler // warning by defining it only when in debug mode - const int view0 = 0; + const int view0 = 0; #endif - const int view135 = view180/4*3; - const int view90 = view180/2; - const int view45 = view180/4; - - - if ( do_symmetry_90degrees_min_phi && view_num > view90 && view_num <= view135) { //(90, 135 ] - if ( !do_symmetry_swap_segment || segment_num > 0) { // pos_plus90 - if ( !do_symmetry_swap_s || s > 0 ) - return new SymmetryOperation_PET_CartesianGrid_swap_xmy_yx(view180, axial_pos_num, z_shift); - else - return new SymmetryOperation_PET_CartesianGrid_swap_xy_ymx_zq(view180, axial_pos_num, z_shift, transform_z); // s < 0 - } - else // neg_plus90 - ///// - if ( segment_num < 0 ) { - if ( !do_symmetry_swap_s || s > 0 ) - return new SymmetryOperation_PET_CartesianGrid_swap_xmy_yx_zq(view180, axial_pos_num, z_shift, transform_z); - else - return new SymmetryOperation_PET_CartesianGrid_swap_xy_ymx(view180, axial_pos_num, z_shift); - } - else { // segment_num == 0 - if ( !do_symmetry_swap_s || s > 0 ) - return new SymmetryOperation_PET_CartesianGrid_swap_xmy_yx(view180, axial_pos_num, z_shift); - else - return new SymmetryOperation_PET_CartesianGrid_swap_xy_ymx(view180, axial_pos_num, z_shift); - } - } - else if ( do_symmetry_90degrees_min_phi && view_num > view45 && view_num <= view90 ) // [ 45, 90] - { - if ( !do_symmetry_swap_segment || segment_num > 0){ - if ( !do_symmetry_swap_s || s > 0 ) - return new SymmetryOperation_PET_CartesianGrid_swap_xy_yx_zq(view180, axial_pos_num, z_shift, transform_z); - else - return new SymmetryOperation_PET_CartesianGrid_swap_xmy_ymx(view180, axial_pos_num, z_shift); + const int view135 = view180 / 4 * 3; + const int view90 = view180 / 2; + const int view45 = view180 / 4; + + if (do_symmetry_90degrees_min_phi && view_num > view90 && view_num <= view135) { //(90, 135 ] + if (!do_symmetry_swap_segment || segment_num > 0) { // pos_plus90 + if (!do_symmetry_swap_s || s > 0) + return new SymmetryOperation_PET_CartesianGrid_swap_xmy_yx(view180, axial_pos_num, z_shift); + else + return new SymmetryOperation_PET_CartesianGrid_swap_xy_ymx_zq(view180, axial_pos_num, z_shift, transform_z); // s < 0 + } else // neg_plus90 + ///// + if (segment_num < 0) { + if (!do_symmetry_swap_s || s > 0) + return new SymmetryOperation_PET_CartesianGrid_swap_xmy_yx_zq(view180, axial_pos_num, z_shift, transform_z); + else + return new SymmetryOperation_PET_CartesianGrid_swap_xy_ymx(view180, axial_pos_num, z_shift); + } else { // segment_num == 0 + if (!do_symmetry_swap_s || s > 0) + return new SymmetryOperation_PET_CartesianGrid_swap_xmy_yx(view180, axial_pos_num, z_shift); + else + return new SymmetryOperation_PET_CartesianGrid_swap_xy_ymx(view180, axial_pos_num, z_shift); } - else if ( segment_num < 0 ) { // {//101 segment_num < 0 - if ( !do_symmetry_swap_s || s > 0 ) - return new SymmetryOperation_PET_CartesianGrid_swap_xy_yx(view180, axial_pos_num, z_shift); - else - return new SymmetryOperation_PET_CartesianGrid_swap_xmy_ymx_zq(view180, axial_pos_num, z_shift, transform_z); - - } - else // segment_num == 0 + } else if (do_symmetry_90degrees_min_phi && view_num > view45 && view_num <= view90) // [ 45, 90] + { + if (!do_symmetry_swap_segment || segment_num > 0) { + if (!do_symmetry_swap_s || s > 0) + return new SymmetryOperation_PET_CartesianGrid_swap_xy_yx_zq(view180, axial_pos_num, z_shift, transform_z); + else + return new SymmetryOperation_PET_CartesianGrid_swap_xmy_ymx(view180, axial_pos_num, z_shift); + } else if (segment_num < 0) { // {//101 segment_num < 0 + if (!do_symmetry_swap_s || s > 0) + return new SymmetryOperation_PET_CartesianGrid_swap_xy_yx(view180, axial_pos_num, z_shift); + else + return new SymmetryOperation_PET_CartesianGrid_swap_xmy_ymx_zq(view180, axial_pos_num, z_shift, transform_z); + + } else // segment_num == 0 { - if ( !do_symmetry_swap_s || s > 0 ) - return new SymmetryOperation_PET_CartesianGrid_swap_xy_yx(view180, axial_pos_num, z_shift); - else - return new SymmetryOperation_PET_CartesianGrid_swap_xmy_ymx(view180, axial_pos_num, z_shift); + if (!do_symmetry_swap_s || s > 0) + return new SymmetryOperation_PET_CartesianGrid_swap_xy_yx(view180, axial_pos_num, z_shift); + else + return new SymmetryOperation_PET_CartesianGrid_swap_xmy_ymx(view180, axial_pos_num, z_shift); } - } - else if( do_symmetry_180degrees_min_phi && view_num > view90/* && view_num <= view180 */) // (135, 180) but (90,180) for reduced symmetry case + } else if (do_symmetry_180degrees_min_phi && + view_num > view90 /* && view_num <= view180 */) // (135, 180) but (90,180) for reduced symmetry case { - if( !do_symmetry_swap_segment || segment_num > 0){ - if ( !do_symmetry_swap_s || s > 0 ) - return new SymmetryOperation_PET_CartesianGrid_swap_xmx_zq(view180, axial_pos_num, z_shift, transform_z); - else - return new SymmetryOperation_PET_CartesianGrid_swap_ymy(view180, axial_pos_num, z_shift); // s <= 0 - } - else //if ( segment_num < 0 ) - {// segment_num <= 0 - if ( !do_symmetry_swap_s || s > 0 ) - return new SymmetryOperation_PET_CartesianGrid_swap_xmx(view180, axial_pos_num, z_shift); - else - return new SymmetryOperation_PET_CartesianGrid_swap_ymy_zq(view180, axial_pos_num, z_shift, transform_z); - }// segment_num == 0 - // /*else{ if ( !do_symmetry_swap_s || s > 0 ) return new SymmetryOperation_PET_CartesianGrid_swap_xmx(); else return new SymmetryOperation_PET_CartesianGrid_swap_ymy(view180, axial_pos_num, z_shift);}*/ - } - else - { - assert( !do_symmetry_90degrees_min_phi || (view_num >= view0 && view_num <= view45)); - assert( !do_symmetry_180degrees_min_phi || (view_num >= view0 && view_num <= view90)); - if ( !do_symmetry_swap_segment || segment_num > 0) - { - if ( do_symmetry_swap_s && s < 0) - return new SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy_zq(view180, axial_pos_num, z_shift, transform_z); + if (!do_symmetry_swap_segment || segment_num > 0) { + if (!do_symmetry_swap_s || s > 0) + return new SymmetryOperation_PET_CartesianGrid_swap_xmx_zq(view180, axial_pos_num, z_shift, transform_z); + else + return new SymmetryOperation_PET_CartesianGrid_swap_ymy(view180, axial_pos_num, z_shift); // s <= 0 + } else // if ( segment_num < 0 ) + { // segment_num <= 0 + if (!do_symmetry_swap_s || s > 0) + return new SymmetryOperation_PET_CartesianGrid_swap_xmx(view180, axial_pos_num, z_shift); else - { - if (z_shift==0) + return new SymmetryOperation_PET_CartesianGrid_swap_ymy_zq(view180, axial_pos_num, z_shift, transform_z); + } // segment_num == 0 + // /*else{ if ( !do_symmetry_swap_s || s > 0 ) return new SymmetryOperation_PET_CartesianGrid_swap_xmx(); else return new + // SymmetryOperation_PET_CartesianGrid_swap_ymy(view180, axial_pos_num, z_shift);}*/ + } else { + assert(!do_symmetry_90degrees_min_phi || (view_num >= view0 && view_num <= view45)); + assert(!do_symmetry_180degrees_min_phi || (view_num >= view0 && view_num <= view90)); + if (!do_symmetry_swap_segment || segment_num > 0) { + if (do_symmetry_swap_s && s < 0) + return new SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy_zq(view180, axial_pos_num, z_shift, transform_z); + else { + if (z_shift == 0) return new TrivialSymmetryOperation(); else return new SymmetryOperation_PET_CartesianGrid_z_shift(axial_pos_num, z_shift); } - } - else - if ( segment_num < 0 ) - { - /*KT if ( s == 0) - return new SymmetryOperation_PET_CartesianGrid_swap_zq(view180, axial_pos_num, z_shift, transform_z); - else*/ - if ( do_symmetry_swap_s && s < 0) - return new SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy(view180, axial_pos_num, z_shift); - else - return new SymmetryOperation_PET_CartesianGrid_swap_zq(view180, axial_pos_num, z_shift, transform_z); // s > 0 - } - else // segment_num = 0 - { - if ( do_symmetry_swap_s && s < 0) return new SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy(view180, axial_pos_num, z_shift); + } else if (segment_num < 0) { + /*KT if ( s == 0) + return new SymmetryOperation_PET_CartesianGrid_swap_zq(view180, axial_pos_num, z_shift, transform_z); + else*/ + if (do_symmetry_swap_s && s < 0) + return new SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy(view180, axial_pos_num, z_shift); + else + return new SymmetryOperation_PET_CartesianGrid_swap_zq(view180, axial_pos_num, z_shift, transform_z); // s > 0 + } else // segment_num = 0 + { + if (do_symmetry_swap_s && s < 0) + return new SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy(view180, axial_pos_num, z_shift); + else { + if (z_shift == 0) + return new TrivialSymmetryOperation(); else - { - if (z_shift==0) - return new TrivialSymmetryOperation(); - else - return new SymmetryOperation_PET_CartesianGrid_z_shift(axial_pos_num, z_shift); - } + return new SymmetryOperation_PET_CartesianGrid_z_shift(axial_pos_num, z_shift); } - } + } + } } +bool +DataSymmetriesForBins_PET_CartesianGrid::find_basic_view_segment_numbers(ViewSegmentNumbers& v_s) const { + bool change = false; + // TODO get rid of next 2 restrictions + assert(!do_symmetry_180degrees_min_phi || v_s.view_num() >= 0); + assert(!do_symmetry_180degrees_min_phi || v_s.view_num() < num_views); + + // const int view0= 0; + const int view90 = num_views >> 1; + const int view45 = view90 >> 1; + const int view135 = view90 + view45; + + if (do_symmetry_swap_segment && v_s.segment_num() < 0) { + v_s.segment_num() = -v_s.segment_num(); + change = true; + } + + if (do_symmetry_90degrees_min_phi) { + // if ( v_s.view_num() == num_views ) v_s.view_num() =0; // KT 30/05/2002 disabled as it should never happen + // else + if (v_s.view_num() >= view135) { + v_s.view_num() = num_views - v_s.view_num(); + return true; + } else if (v_s.view_num() >= view90) { + v_s.view_num() = v_s.view_num() - view90; + return true; + } else if (v_s.view_num() > view45) { + v_s.view_num() = view90 - v_s.view_num(); + return true; + } + } else if (do_symmetry_180degrees_min_phi) { + if (v_s.view_num() > view90) { + v_s.view_num() = num_views - v_s.view_num(); + return true; + } + } -bool -DataSymmetriesForBins_PET_CartesianGrid:: -find_basic_view_segment_numbers(ViewSegmentNumbers& v_s) const -{ - bool change=false; - // TODO get rid of next 2 restrictions - assert(!do_symmetry_180degrees_min_phi || v_s.view_num()>=0); - assert(!do_symmetry_180degrees_min_phi || v_s.view_num()>1; - const int view45 = view90>>1; - const int view135 = view90+view45; - - if ( do_symmetry_swap_segment && v_s.segment_num() < 0 ) { v_s.segment_num() = -v_s.segment_num(); change=true;} - - if (do_symmetry_90degrees_min_phi) - { - //if ( v_s.view_num() == num_views ) v_s.view_num() =0; // KT 30/05/2002 disabled as it should never happen - //else - if (v_s.view_num() >= view135) - { v_s.view_num() = num_views - v_s.view_num(); return true; } - else if (v_s.view_num() >= view90 ) - { v_s.view_num() = v_s.view_num() - view90; return true; } - else if (v_s.view_num() > view45 ) - { v_s.view_num() = view90 - v_s.view_num() ; return true; } - } - else if (do_symmetry_180degrees_min_phi) - { - if (v_s.view_num() > view90 ) - { v_s.view_num() = num_views - v_s.view_num(); return true; } - } - return change; } -bool -DataSymmetriesForBins_PET_CartesianGrid:: -find_basic_bin(int &segment_num, int &view_num, int &axial_pos_num, int &tangential_pos_num) const -{ +bool +DataSymmetriesForBins_PET_CartesianGrid::find_basic_bin(int& segment_num, int& view_num, int& axial_pos_num, + int& tangential_pos_num) const { ViewSegmentNumbers v_s(view_num, segment_num); - bool change=find_basic_view_segment_numbers(v_s); + bool change = find_basic_view_segment_numbers(v_s); view_num = v_s.view_num(); segment_num = v_s.segment_num(); - if ( do_symmetry_swap_s && tangential_pos_num < 0 ) { tangential_pos_num = - tangential_pos_num ; change=true;}; - if ( do_symmetry_shift_z && axial_pos_num != 0 ) { axial_pos_num = 0; change = true; } - + if (do_symmetry_swap_s && tangential_pos_num < 0) { + tangential_pos_num = -tangential_pos_num; + change = true; + }; + if (do_symmetry_shift_z && axial_pos_num != 0) { + axial_pos_num = 0; + change = true; + } + return change; } -bool -DataSymmetriesForBins_PET_CartesianGrid:: -find_basic_bin(Bin& b) const -{ - return - find_basic_bin(b.segment_num(), b.view_num(), b.axial_pos_num(), b.tangential_pos_num()); +bool +DataSymmetriesForBins_PET_CartesianGrid::find_basic_bin(Bin& b) const { + return find_basic_bin(b.segment_num(), b.view_num(), b.axial_pos_num(), b.tangential_pos_num()); } - // TODO, optimise unique_ptr -DataSymmetriesForBins_PET_CartesianGrid:: - find_symmetry_operation_from_basic_bin(Bin& b) const -{ - unique_ptr - sym_op( - (b.tangential_pos_num()==0) ? - find_sym_op_bin0(b.segment_num(), b.view_num(), b.axial_pos_num()) : - find_sym_op_general_bin(b.tangential_pos_num(), b.segment_num(), b.view_num(), b.axial_pos_num()) - ); +DataSymmetriesForBins_PET_CartesianGrid::find_symmetry_operation_from_basic_bin(Bin& b) const { + unique_ptr sym_op( + (b.tangential_pos_num() == 0) + ? find_sym_op_bin0(b.segment_num(), b.view_num(), b.axial_pos_num()) + : find_sym_op_general_bin(b.tangential_pos_num(), b.segment_num(), b.view_num(), b.axial_pos_num())); find_basic_bin(b); return sym_op; } - int -DataSymmetriesForBins_PET_CartesianGrid:: -num_related_view_segment_numbers(const ViewSegmentNumbers& vs) const -{ - int num = do_symmetry_180degrees_min_phi && (vs.view_num() % (num_views/2)) != 0 ? 2 : 1; - if (do_symmetry_90degrees_min_phi && (vs.view_num() % (num_views/2)) != num_views/4) +DataSymmetriesForBins_PET_CartesianGrid::num_related_view_segment_numbers(const ViewSegmentNumbers& vs) const { + int num = do_symmetry_180degrees_min_phi && (vs.view_num() % (num_views / 2)) != 0 ? 2 : 1; + if (do_symmetry_90degrees_min_phi && (vs.view_num() % (num_views / 2)) != num_views / 4) num *= 2; if (do_symmetry_swap_segment && vs.segment_num() != 0) num *= 2; - return num; + return num; } - int -DataSymmetriesForBins_PET_CartesianGrid:: -num_related_bins(const Bin& b) const -{ - int num = do_symmetry_180degrees_min_phi && (b.view_num() % (num_views/2)) != 0 ? 2 : 1; - if (do_symmetry_90degrees_min_phi && (b.view_num() % (num_views/2)) != num_views/4) +DataSymmetriesForBins_PET_CartesianGrid::num_related_bins(const Bin& b) const { + int num = do_symmetry_180degrees_min_phi && (b.view_num() % (num_views / 2)) != 0 ? 2 : 1; + if (do_symmetry_90degrees_min_phi && (b.view_num() % (num_views / 2)) != num_views / 4) num *= 2; if (do_symmetry_swap_segment && b.segment_num() != 0) num *= 2; if (do_symmetry_swap_s && b.tangential_pos_num() != 0) num *= 2; - + if (do_symmetry_shift_z) num *= proj_data_info_ptr->get_num_axial_poss(b.segment_num()); return num; } void -DataSymmetriesForBins_PET_CartesianGrid:: -get_related_bins_factorised(std::vector& ax_tang_poss, const Bin& b, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) const -{ - for (int axial_pos_num=do_symmetry_shift_z?min_axial_pos_num:b.axial_pos_num(); - axial_pos_num <= (do_symmetry_shift_z?max_axial_pos_num:b.axial_pos_num()); - ++axial_pos_num) - { - if (b.tangential_pos_num() >= min_tangential_pos_num && - b.tangential_pos_num() <= max_tangential_pos_num) - ax_tang_poss.push_back(AxTangPosNumbers(axial_pos_num, b.tangential_pos_num())); - if (do_symmetry_swap_s && b.tangential_pos_num()!=0 && - -b.tangential_pos_num() >= min_tangential_pos_num && - -b.tangential_pos_num() <= max_tangential_pos_num) - ax_tang_poss.push_back(AxTangPosNumbers(axial_pos_num, -b.tangential_pos_num())); - } +DataSymmetriesForBins_PET_CartesianGrid::get_related_bins_factorised(std::vector& ax_tang_poss, const Bin& b, + const int min_axial_pos_num, const int max_axial_pos_num, + const int min_tangential_pos_num, + const int max_tangential_pos_num) const { + for (int axial_pos_num = do_symmetry_shift_z ? min_axial_pos_num : b.axial_pos_num(); + axial_pos_num <= (do_symmetry_shift_z ? max_axial_pos_num : b.axial_pos_num()); ++axial_pos_num) { + if (b.tangential_pos_num() >= min_tangential_pos_num && b.tangential_pos_num() <= max_tangential_pos_num) + ax_tang_poss.push_back(AxTangPosNumbers(axial_pos_num, b.tangential_pos_num())); + if (do_symmetry_swap_s && b.tangential_pos_num() != 0 && -b.tangential_pos_num() >= min_tangential_pos_num && + -b.tangential_pos_num() <= max_tangential_pos_num) + ax_tang_poss.push_back(AxTangPosNumbers(axial_pos_num, -b.tangential_pos_num())); + } } - + void -DataSymmetriesForBins_PET_CartesianGrid:: -get_related_view_segment_numbers(std::vector& rel_vs, const ViewSegmentNumbers& vs) const -{ +DataSymmetriesForBins_PET_CartesianGrid::get_related_view_segment_numbers(std::vector& rel_vs, + const ViewSegmentNumbers& vs) const { #ifndef NDEBUG { - ViewSegmentNumbers vstest=vs; - assert(find_basic_view_segment_numbers(vstest)==false); + ViewSegmentNumbers vstest = vs; + assert(find_basic_view_segment_numbers(vstest) == false); } #endif const int segment_num = vs.segment_num(); const int view_num = vs.view_num(); - const bool symz = - do_symmetry_swap_segment && (segment_num != 0); + const bool symz = do_symmetry_swap_segment && (segment_num != 0); rel_vs.reserve(num_related_view_segment_numbers(vs)); rel_vs.resize(0); - rel_vs.push_back(ViewSegmentNumbers(view_num,segment_num)); + rel_vs.push_back(ViewSegmentNumbers(view_num, segment_num)); if (symz) - rel_vs.push_back(ViewSegmentNumbers(view_num,-segment_num)); + rel_vs.push_back(ViewSegmentNumbers(view_num, -segment_num)); - if (do_symmetry_180degrees_min_phi && do_symmetry_90degrees_min_phi && (view_num % (num_views/2)) != num_views/4) - { - const int related_view_num = - view_num < num_views/2 ? - view_num + num_views/2 : - view_num - num_views/2; - rel_vs.push_back(ViewSegmentNumbers( related_view_num,segment_num)); + if (do_symmetry_180degrees_min_phi && do_symmetry_90degrees_min_phi && (view_num % (num_views / 2)) != num_views / 4) { + const int related_view_num = view_num < num_views / 2 ? view_num + num_views / 2 : view_num - num_views / 2; + rel_vs.push_back(ViewSegmentNumbers(related_view_num, segment_num)); if (symz) - rel_vs.push_back(ViewSegmentNumbers( related_view_num,-segment_num)); + rel_vs.push_back(ViewSegmentNumbers(related_view_num, -segment_num)); } - if (do_symmetry_180degrees_min_phi && (view_num % (num_views/2)) != 0) - { - rel_vs.push_back(ViewSegmentNumbers( num_views - view_num,segment_num)); + if (do_symmetry_180degrees_min_phi && (view_num % (num_views / 2)) != 0) { + rel_vs.push_back(ViewSegmentNumbers(num_views - view_num, segment_num)); if (symz) - rel_vs.push_back(ViewSegmentNumbers( num_views - view_num,-segment_num)); + rel_vs.push_back(ViewSegmentNumbers(num_views - view_num, -segment_num)); } - if (do_symmetry_90degrees_min_phi && (view_num % (num_views/4)) != 0) - { + if (do_symmetry_90degrees_min_phi && (view_num % (num_views / 4)) != 0) { // use trick to get related_view_num between 0 and num_views: // use modulo num_views (but add num_views first to ensure positivity) - const int related_view_num = - (num_views/2 - view_num + num_views) % num_views; - rel_vs.push_back(ViewSegmentNumbers( related_view_num,segment_num)); + const int related_view_num = (num_views / 2 - view_num + num_views) % num_views; + rel_vs.push_back(ViewSegmentNumbers(related_view_num, segment_num)); if (symz) - rel_vs.push_back(ViewSegmentNumbers( related_view_num,-segment_num)); + rel_vs.push_back(ViewSegmentNumbers(related_view_num, -segment_num)); } - - assert(rel_vs.size() == - static_cast(num_related_view_segment_numbers(vs))); + assert(rel_vs.size() == static_cast(num_related_view_segment_numbers(vs))); } END_NAMESPACE_STIR diff --git a/src/include/stir/recon_buildblock/DataSymmetriesForDensels.h b/src/include/stir/recon_buildblock/DataSymmetriesForDensels.h index be46afff7f..082e7255f9 100644 --- a/src/include/stir/recon_buildblock/DataSymmetriesForDensels.h +++ b/src/include/stir/recon_buildblock/DataSymmetriesForDensels.h @@ -23,7 +23,7 @@ \brief Declaration of class stir::DataSymmetriesForDensels \author Kris Thielemans - + */ #ifndef __stir_recon_buildblock_DataSymmetriesForDensels_H__ #define __stir_recon_buildblock_DataSymmetriesForDensels_H__ @@ -37,41 +37,35 @@ START_NAMESPACE_STIR -//class Densel; +// class Densel; class SymmetryOperation; - #if 0 class DenselIndexRange; #endif - - /*! \ingroup symmetries \brief A class for encoding/finding symmetries common to the geometry - of the projection data and the discretised density. + of the projection data and the discretised density. This class is mainly (only?) useful for ProjMatrixByDensel classes and their - 'users'. Together with SymmetryOperation, it provides the basic - way to be able to write generic code without knowing which + 'users'. Together with SymmetryOperation, it provides the basic + way to be able to write generic code without knowing which particular symmetries the data have. */ -class DataSymmetriesForDensels -{ +class DataSymmetriesForDensels { public: DataSymmetriesForDensels(); - virtual ~DataSymmetriesForDensels() {}; + virtual ~DataSymmetriesForDensels(){}; - virtual - DataSymmetriesForDensels - * clone() const = 0; + virtual DataSymmetriesForDensels* clone() const = 0; - bool operator ==(const DataSymmetriesForDensels&) const; + bool operator==(const DataSymmetriesForDensels&) const; - bool operator !=(const DataSymmetriesForDensels&) const; + bool operator!=(const DataSymmetriesForDensels&) const; #if 0 TODO! @@ -81,12 +75,11 @@ class DataSymmetriesForDensels #endif //! fills in a vector with all the Densels that are related to 'b' (including itself) - /*! + /*! \warning \c b has to be a 'basic' Densel */ // next return value could be a RelatedDensels ??? - virtual void - get_related_densels(std::vector&, const Densel& b) const = 0; + virtual void get_related_densels(std::vector&, const Densel& b) const = 0; #if 0 //! fills in a vector with all the Densels (within the range) that are related to 'b' @@ -98,36 +91,30 @@ class DataSymmetriesForDensels #endif //! returns the number of Densels related to 'b' - virtual int - num_related_densels(const Densel& b) const; + virtual int num_related_densels(const Densel& b) const; /*! \brief given an arbitrary Densel 'b', find the basic Densel - - sets 'b' to the corresponding 'basic' Densel and returns the symmetry + + sets 'b' to the corresponding 'basic' Densel and returns the symmetry transformation from 'basic' to 'b'. */ - virtual unique_ptr - find_symmetry_operation_from_basic_densel(Densel&) const = 0; + virtual unique_ptr find_symmetry_operation_from_basic_densel(Densel&) const = 0; /*! \brief given an arbitrary Densel 'b', find the basic Densel - + sets 'b' to the corresponding 'basic' Densel and returns true if 'b' is changed (i.e. it was NOT a basic Densel). */ - virtual bool - find_basic_densel(Densel& b) const; + virtual bool find_basic_densel(Densel& b) const; - protected: +protected: typedef DataSymmetriesForDensels root_type; - virtual bool blindly_equals(const root_type * const) const = 0; - + virtual bool blindly_equals(const root_type* const) const = 0; }; END_NAMESPACE_STIR //#include "stir/recon_buildblock/DataSymmetriesForDensels.inl" - #endif - diff --git a/src/include/stir/recon_buildblock/DataSymmetriesForDensels.inl b/src/include/stir/recon_buildblock/DataSymmetriesForDensels.inl index 242fe009b7..670a2767da 100644 --- a/src/include/stir/recon_buildblock/DataSymmetriesForDensels.inl +++ b/src/include/stir/recon_buildblock/DataSymmetriesForDensels.inl @@ -8,7 +8,7 @@ \brief inline implementations for class stir::DataSymmetriesForDensels \author Kris Thielemans - + */ /* Copyright (C) 2001- 2009, Hammersmith Imanet Ltd @@ -31,16 +31,10 @@ START_NAMESPACE_STIR void -DataSymmetriesForDensels:: - get_related_densels(vector& rel_b, const Densel& b) const -{ - get_related_densels(rel_b, b, - proj_data_info_ptr->get_min_axial_pos_num(b.segment_num()), - proj_data_info_ptr->get_max_axial_pos_num(b.segment_num()), - proj_data_info_ptr->get_min_tangential_pos_num(), - proj_data_info_ptr->get_max_tangential_pos_num()); +DataSymmetriesForDensels::get_related_densels(vector& rel_b, const Densel& b) const { + get_related_densels(rel_b, b, proj_data_info_ptr->get_min_axial_pos_num(b.segment_num()), + proj_data_info_ptr->get_max_axial_pos_num(b.segment_num()), + proj_data_info_ptr->get_min_tangential_pos_num(), proj_data_info_ptr->get_max_tangential_pos_num()); } - - END_NAMESPACE_STIR diff --git a/src/include/stir/recon_buildblock/DistributedCachingInformation.h b/src/include/stir/recon_buildblock/DistributedCachingInformation.h index e54c3b1c50..cdba6e8ce7 100644 --- a/src/include/stir/recon_buildblock/DistributedCachingInformation.h +++ b/src/include/stir/recon_buildblock/DistributedCachingInformation.h @@ -22,7 +22,7 @@ /*! \ingroup distributable - + \brief Declaration of class stir::DistributedCachingInformation \author Tobias Beisel @@ -40,39 +40,38 @@ START_NAMESPACE_STIR \ingroup distributable \brief This class implements the logic needed to support caching in a distributed manner. - To enable distributed caching, the master needs to store how the view segment numbers or the - related viewgrams respectively were distributed between the workers. Doing that, the master - is able to send those viegrams to a slave requesting for work, which that specific slave already - handled before and still has cached locally. + To enable distributed caching, the master needs to store how the view segment numbers or the + related viewgrams respectively were distributed between the workers. Doing that, the master + is able to send those viegrams to a slave requesting for work, which that specific slave already + handled before and still has cached locally. Additionally it is possible to only send the view segment number instead of the whole projection data, if the the worker stores the recieved related viewgrams. - + This class stores all needed information to determine a not yet processed view segment number - that will be sent to the requesting slave in such a way that the belonging data most likely - is stored in the slaves cache. - + that will be sent to the requesting slave in such a way that the belonging data most likely + is stored in the slaves cache. + The function \c get_unprocessed_vs_num() can be called by the master, passing the requesting worker and the current subset to determine the next processed view segment number - - The logic is a bit sophisticated, as it has to make sure, that every vs_num is + + The logic is a bit sophisticated, as it has to make sure, that every vs_num is only processed once and that load balancing is forced. T - + Process numbers are expected to be between 0 and \c num_workers - + Whether to use the cache enabled function or not can be set by the parsing parameter \verbatim enable distributed caching := 0 \endverbatim within the parameter specification for the objective function, where 1 activates it and 0 deactivates caching. The default is set to 0. - + */ -class DistributedCachingInformation -{ -public: +class DistributedCachingInformation { +public: //! constructor, calls initialise() explicit DistributedCachingInformation(const int num_processors); - + //! destructor to clean up data structures virtual ~DistributedCachingInformation(); @@ -83,31 +82,30 @@ class DistributedCachingInformation void initialise(); /*! \brief to be called at the beginning of the processing of a set of data * The caching data is kept, such that the cache will be re-used over multiple runs. - */ + */ void initialise_new_subiteration(const std::vector& vs_nums_to_process); - + /*! \brief get the next work-package for a given processor - * \warning this must only be called if there for sure is an unprocessed vs_num left, + * \warning this must only be called if there for sure is an unprocessed vs_num left, * otherwise it will call stir::error(). * \param[out] vs_num will be set accordingly * \param[in] proc the processor for which the View-Segment-Numbers are calculated * \return \c true if the vs_num was not in the cache of the processor * This function updates internal cache values etc. The user can just repeatedly call * the function without worrying about the caching algorithm. - */ + */ bool get_unprocessed_vs_num(ViewSegmentNumbers& vs_num, int proc); - -private: +private: //! Number of processors available int num_workers; //! stores which data that have to be processed in this subiteration - std::vector vs_nums_to_process; + std::vector vs_nums_to_process; + + //! stores the vs_nums in the cache of every processor + std::vector> proc_vs_nums; - //!stores the vs_nums in the cache of every processor - std::vector > proc_vs_nums; - //! marks the vs_num that still need to be processed /*! Has the same length as vs_nums_to_process */ std::vector still_to_process; @@ -117,7 +115,7 @@ class DistributedCachingInformation //! \brief find the first vs_num which has not be processed at all int find_position_of_first_unprocessed() const; - + //! count how many data-sets that are cached remain to be processed by processor \a proc int get_num_remaining_cached_data_to_process(int proc) const; @@ -136,33 +134,31 @@ class DistributedCachingInformation */ bool get_oldest_unprocessed_vs_num(ViewSegmentNumbers& vs_num, int proc) const; - /*! \brief gets a vs_num of the processor, which has the most work left - * \param proc processor that will not be checked (i.e. the one for which + * \param proc processor that will not be checked (i.e. the one for which * we are trying to find some work) - * - * this function is called if a processor requests work and already accomplished + * + * this function is called if a processor requests work and already accomplished * everything in its cache. Allocating work from the slave with most work left * encourages load balancing without having a lot of extra communicatrions. - * That way the probability of requesting already cached work is kept high. + * That way the probability of requesting already cached work is kept high. */ ViewSegmentNumbers get_vs_num_of_proc_with_most_work_left(int proc) const; - + /*! \brief set a ViewSegmentNumbers as already processed * \param vs_num the vs_num to be set processed */ void set_processed(const ViewSegmentNumbers& vs_num); - + /*! \brief reset all data to unprocessed */ void set_all_vs_num_unprocessed(); - + /*! \brief check if a vs_num is still to be processed or not * \param vs_num the view segment number to be checked * Also returns false if the \a vs_num is not in the list to process at all. */ bool is_still_to_be_processed(const ViewSegmentNumbers& vs_num) const; - }; END_NAMESPACE_STIR diff --git a/src/include/stir/recon_buildblock/DistributedWorker.h b/src/include/stir/recon_buildblock/DistributedWorker.h index 8b0c8b9bb0..ca5c6e7c8c 100644 --- a/src/include/stir/recon_buildblock/DistributedWorker.h +++ b/src/include/stir/recon_buildblock/DistributedWorker.h @@ -17,27 +17,26 @@ */ #ifndef __stir_recon_buildblock_DistributedWorker_h__ -#define __stir_recon_buildblock_DistributedWorker_h__ - +# define __stir_recon_buildblock_DistributedWorker_h__ /*! - \file + \file \ingroup recon_buildblock - + \brief declares the stir::DistributedWorker class \author Tobias Beisel \author Kris Thielemans */ -#include "stir/shared_ptr.h" -#include "stir/TimedObject.h" +# include "stir/shared_ptr.h" +# include "stir/TimedObject.h" //#include "stir/ParsingObject.h" -#include "stir/ProjData.h" -#include "stir/recon_buildblock/ProjectorByBinPair.h" -#include "stir/recon_buildblock/distributable.h" -#include -#include +# include "stir/ProjData.h" +# include "stir/recon_buildblock/ProjectorByBinPair.h" +# include "stir/recon_buildblock/distributable.h" +# include +# include START_NAMESPACE_STIR @@ -49,16 +48,16 @@ class ExamInfo; The start() method is an infinite loop waiting for a task from the master. Very few tasks are implemented at the moment: set_up, compute, stop. - + The \c distributable_computation() function does the actual work. It is the slave-part - of stir::distributable_computation() which runs on the master. It is a loop receiving the related - viewgrams and calling an RPC_process_related_viewgrams_type function - with the received values. When an end_iteration_notification is received, it calls the + of stir::distributable_computation() which runs on the master. It is a loop receiving the related + viewgrams and calling an RPC_process_related_viewgrams_type function + with the received values. When an end_iteration_notification is received, it calls the reduction of the output_image. - - In each inner loop the worker first receives the vs_num and the information whether this is a - new viewgram or a previously received viewgram. The latter case only emerges if distributed caching is - enabled. If so, the worker does not have to receive the related viewgrams, but just gets it from + + In each inner loop the worker first receives the vs_num and the information whether this is a + new viewgram or a previously received viewgram. The latter case only emerges if distributed caching is + enabled. If so, the worker does not have to receive the related viewgrams, but just gets it from its saved viewgrams. \todo The log_likelihood_ptr argument to the RPC function is currently always NULL. @@ -67,73 +66,67 @@ class ExamInfo; */ template class DistributedWorker : public TimedObject //, public ParsingObject -{ - private: - +{ +private: double* log_likelihood_ptr; bool zero_seg0_end_planes; shared_ptr proj_pair_sptr; shared_ptr exam_info_sptr; shared_ptr proj_data_info_sptr; shared_ptr target_sptr; - - int image_buffer_size; //to save the image_size + + int image_buffer_size; // to save the image_size // cache variables bool cache_enabled; - shared_ptr proj_data_ptr; + shared_ptr proj_data_ptr; shared_ptr binwise_correction; shared_ptr mult_proj_data_sptr; - int my_rank; //rank of the worker + int my_rank; // rank of the worker - - public: - - //Default constructor +public: + // Default constructor DistributedWorker(); - - //Default destructor - ~DistributedWorker() ; - + + // Default destructor + ~DistributedWorker(); + /*! \brief Infinite loop waiting for tasks from the master */ void start(); - - protected: - + +protected: /*! - \brief sets defaults for this object + \brief sets defaults for this object */ void set_defaults(); /*! \brief Get basic information from the master. - - It sets up all needed objects by communicating with the - master. - + + It sets up all needed objects by communicating with the + master. + The following objects are set up: - bool zero_seg0_end_planes - target_image_sptr - ProjDataInfo pointer - ProjectorByBinPair pointer - ProjDataInMemory to save the received related viewgrams - - Additionally some values for testing are set up. - + + Additionally some values for testing are set up. + */ void setup_distributable_computation(); /*! \brief this does the actual computation corresponding to distributable_computation() */ - void distributable_computation(RPC_process_related_viewgrams_type * RPC_process_related_viewgrams); + void distributable_computation(RPC_process_related_viewgrams_type* RPC_process_related_viewgrams); }; - END_NAMESPACE_STIR #endif // __DistributedWorker_h__ - diff --git a/src/include/stir/recon_buildblock/FilterRootPrior.h b/src/include/stir/recon_buildblock/FilterRootPrior.h index 5b47f1215a..b8582889d8 100644 --- a/src/include/stir/recon_buildblock/FilterRootPrior.h +++ b/src/include/stir/recon_buildblock/FilterRootPrior.h @@ -29,29 +29,29 @@ #ifndef __stir_recon_buildblock_FilterRootPrior_H__ #define __stir_recon_buildblock_FilterRootPrior_H__ - #include "stir/RegisteredParsingObject.h" #include "stir/recon_buildblock/GeneralisedPrior.h" #include "stir/shared_ptr.h" START_NAMESPACE_STIR -template class DataProcessor; +template +class DataProcessor; /*! \ingroup priors \brief - A class in the GeneralisedPrior hierarchy. This implements 'generalised' + A class in the GeneralisedPrior hierarchy. This implements 'generalised' priors a la the Median Root Prior (which was invented by Sakari Alenius). - This class takes an DataProcessor object (i.e. a filter), and computes + This class takes an DataProcessor object (i.e. a filter), and computes the prior 'gradient' as \f[ G_v = \beta ( {\lambda_v \over F_v} - 1) \f] where \f$ \lambda\f$ is the data where to compute the gradient, and \f$F\f$ is the data obtained by filtering \f$\lambda\f$. - However, we need to avoid division by 0, as it might cause a NaN or an + However, we need to avoid division by 0, as it might cause a NaN or an 'infinity'. So, we replace the quotient above by
    if \f$|\lambda_v| < M*|F_v| \f$ then \f${\lambda_v \over F_v}\f$ @@ -65,64 +65,46 @@ template class DataProcessor; of the gradient). For most (interesting) filters, the Hessian will no be symmetric. - The Median Root Prior is obtained by using a MedianImageFilter3D as + The Median Root Prior is obtained by using a MedianImageFilter3D as DataProcessor. */ template -class FilterRootPrior: public - RegisteredParsingObject< - FilterRootPrior, - GeneralisedPrior, - GeneralisedPrior - > -{ - private: - typedef - RegisteredParsingObject< - FilterRootPrior, - GeneralisedPrior, - GeneralisedPrior - > - base_type; +class FilterRootPrior : public RegisteredParsingObject, GeneralisedPrior, GeneralisedPrior> { +private: + typedef RegisteredParsingObject, GeneralisedPrior, GeneralisedPrior> base_type; + public: //! Name which will be used when parsing a GeneralisedPrior object - static const char * const registered_name; + static const char* const registered_name; //! Default constructor (no filter) FilterRootPrior(); //! Constructs it explicitly - FilterRootPrior(shared_ptr >const&, - const float penalization_factor); + FilterRootPrior(shared_ptr> const&, const float penalization_factor); - //! compute the value of the function + //! compute the value of the function /*! \warning Generally there is no function associated to this prior, so we just return 0 and write a warning the first time it's called. */ - virtual double - compute_value(const DataT ¤t_estimate); - + virtual double compute_value(const DataT& current_estimate); + //! compute gradient by applying the filter - void compute_gradient(DataT& prior_gradient, - const DataT ¤t_estimate); + void compute_gradient(DataT& prior_gradient, const DataT& current_estimate); //! Has to be called before using this object virtual Succeeded set_up(shared_ptr const& target_sptr); - -protected: +protected: //! Check that the prior is ready to be used virtual void check(DataT const& current_image_estimate) const; - -private: - shared_ptr > filter_ptr; - virtual void set_defaults(); - virtual void initialise_keymap(); +private: + shared_ptr> filter_ptr; + virtual void set_defaults(); + virtual void initialise_keymap(); }; - END_NAMESPACE_STIR #endif - diff --git a/src/include/stir/recon_buildblock/ForwardProjectorByBin.h b/src/include/stir/recon_buildblock/ForwardProjectorByBin.h index 091d4c4f91..9c41e86456 100644 --- a/src/include/stir/recon_buildblock/ForwardProjectorByBin.h +++ b/src/include/stir/recon_buildblock/ForwardProjectorByBin.h @@ -7,7 +7,8 @@ \file \ingroup projection - \brief Base class for forward projectors which work on 'large' collections of bins: given the whole image, fill in a stir::RelatedViewgrams object. + \brief Base class for forward projectors which work on 'large' collections of bins: given the whole image, fill in a + stir::RelatedViewgrams object. \author Kris Thielemans \author Sanida Mustafovic @@ -43,89 +44,77 @@ START_NAMESPACE_STIR - -template class RelatedViewgrams; -template class DiscretisedDensity; +template +class RelatedViewgrams; +template +class DiscretisedDensity; class ProjDataInfo; class ProjData; class DataSymmetriesForViewSegmentNumbers; -template class DataProcessor; +template +class DataProcessor; /*! \ingroup projection \brief Abstract base class for all forward projectors */ -class ForwardProjectorByBin : - public TimedObject, - public RegisteredObject -{ +class ForwardProjectorByBin : public TimedObject, public RegisteredObject { public: - //! Default constructor calls reset_timers() - //inline - ForwardProjectorByBin(); + // inline + ForwardProjectorByBin(); //! Stores all necessary geometric info - /*! - If necessary, set_up() can be called more than once. + /*! + If necessary, set_up() can be called more than once. - Derived classes can assume that forward_project() will be called - with input corresponding to the arguments of the last call to set_up(). + Derived classes can assume that forward_project() will be called + with input corresponding to the arguments of the last call to set_up(). - \warning there is currently no check on this. - \warning Derived classes have to call set_up from the base class. - */ -virtual void set_up( - const shared_ptr& proj_data_info_ptr, - const shared_ptr >& density_info_sptr // TODO should be Info only - ) =0; + \warning there is currently no check on this. + \warning Derived classes have to call set_up from the base class. + */ + virtual void set_up(const shared_ptr& proj_data_info_ptr, + const shared_ptr>& density_info_sptr // TODO should be Info only + ) = 0; //! Informs on which symmetries the projector handles /*! It should get data related by at least those symmetries. Otherwise, a run-time error will occur (unless the derived class has other behaviour). */ - virtual const DataSymmetriesForViewSegmentNumbers * get_symmetries_used() const = 0; + virtual const DataSymmetriesForViewSegmentNumbers* get_symmetries_used() const = 0; //! project the volume into the whole or a subset of proj_data, optionally zeroing the rest /*! it overwrites the data already present in the projection data. - - The optional arguments can be used to project only a subset of the data. + + The optional arguments can be used to project only a subset of the data. Subsets are determined as per detail::find_basic_vs_nums_in_subset(). However, this usage will likely be phased out at later stage.*/ - void forward_project(ProjData&, - const DiscretisedDensity<3,float>&, - int subset_num = 0, int num_subsets = 1, bool zero = true); + void forward_project(ProjData&, const DiscretisedDensity<3, float>&, int subset_num = 0, int num_subsets = 1, bool zero = true); #ifdef STIR_PROJECTORS_AS_V3 - //! project the volume into the viewgrams - /*! it overwrites the data already present in the viewgram */ - void forward_project(RelatedViewgrams&, - const DiscretisedDensity<3,float>&); - - void forward_project(RelatedViewgrams&, - const DiscretisedDensity<3,float>&, - const int min_axial_pos_num, const int max_axial_pos_num); - - void forward_project(RelatedViewgrams&, - const DiscretisedDensity<3,float>&, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num); + //! project the volume into the viewgrams + /*! it overwrites the data already present in the viewgram */ + void forward_project(RelatedViewgrams&, const DiscretisedDensity<3, float>&); + + void forward_project(RelatedViewgrams&, const DiscretisedDensity<3, float>&, const int min_axial_pos_num, + const int max_axial_pos_num); + + void forward_project(RelatedViewgrams&, const DiscretisedDensity<3, float>&, const int min_axial_pos_num, + const int max_axial_pos_num, const int min_tangential_pos_num, const int max_tangential_pos_num); #endif - //! project the volume into the whole proj_data - /*! it overwrites the data already present in the projection data */ - virtual void forward_project(ProjData&, - int subset_num = 0, int num_subsets = 1, bool zero = true); + //! project the volume into the whole proj_data + /*! it overwrites the data already present in the projection data */ + virtual void forward_project(ProjData&, int subset_num = 0, int num_subsets = 1, bool zero = true); - //! project the volume into the viewgrams - /*! it overwrites the data already present in the viewgram */ - void forward_project(RelatedViewgrams&); + //! project the volume into the viewgrams + /*! it overwrites the data already present in the viewgram */ + void forward_project(RelatedViewgrams&); - void forward_project(RelatedViewgrams&, - const int min_axial_pos_num, const int max_axial_pos_num); + void forward_project(RelatedViewgrams&, const int min_axial_pos_num, const int max_axial_pos_num); - void forward_project(RelatedViewgrams&, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num); + void forward_project(RelatedViewgrams&, const int min_axial_pos_num, const int max_axial_pos_num, + const int min_tangential_pos_num, const int max_tangential_pos_num); #if 0 // disabled as currently not used. needs to be written in the new style anyway //! function mainly used in ListMode reconstruction. @@ -133,24 +122,23 @@ virtual void set_up( void forward_project(Bin&, const DiscretisedDensity<3,float>&); #endif - virtual ~ForwardProjectorByBin(); + virtual ~ForwardProjectorByBin(); - /// Set input - virtual void set_input(const DiscretisedDensity<3,float>&); + /// Set input + virtual void set_input(const DiscretisedDensity<3, float>&); - /// Set data processor to use before forward projection. MUST BE CALLED BEFORE SET_INPUT. - void set_pre_data_processor(shared_ptr > > pre_data_processor_sptr); + /// Set data processor to use before forward projection. MUST BE CALLED BEFORE SET_INPUT. + void set_pre_data_processor(shared_ptr>> pre_data_processor_sptr); protected: //! This virtual function has to be implemented by the derived class. - virtual void actual_forward_project(RelatedViewgrams&, - const DiscretisedDensity<3,float>&, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num); + virtual void actual_forward_project(RelatedViewgrams&, const DiscretisedDensity<3, float>&, const int min_axial_pos_num, + const int max_axial_pos_num, const int min_tangential_pos_num, + const int max_tangential_pos_num); - virtual void actual_forward_project(RelatedViewgrams& viewgrams, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num); + virtual void actual_forward_project(RelatedViewgrams& viewgrams, const int min_axial_pos_num, + const int max_axial_pos_num, const int min_tangential_pos_num, + const int max_tangential_pos_num); #if 0 // disabled as currently not used. needs to be written in the new style anyway //! This virtual function has to be implemented by the derived class. @@ -163,12 +151,12 @@ virtual void set_up( If overriding this function in a derived class, you need to call this one. */ - virtual void check(const ProjDataInfo& proj_data_info, const DiscretisedDensity<3,float>& density_info) const; + virtual void check(const ProjDataInfo& proj_data_info, const DiscretisedDensity<3, float>& density_info) const; bool _already_set_up; //! The density ptr set with set_up() - shared_ptr > _density_sptr; - shared_ptr > > _pre_data_processor_sptr; + shared_ptr> _density_sptr; + shared_ptr>> _pre_data_processor_sptr; virtual void set_defaults(); virtual void initialise_keymap(); diff --git a/src/include/stir/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.h b/src/include/stir/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.h index f385737d03..ee7d5faebe 100644 --- a/src/include/stir/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.h +++ b/src/include/stir/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.h @@ -7,14 +7,14 @@ \file \ingroup projection - + \brief definition of stir::ForwardProjectorByBinUsingProjMatrixByBin - + \author Kris Thielemans \author Sanida Mustafovic \author Mustapha Sadki \author PARAPET project - + */ /* Copyright (C) 2000 PARAPET partners @@ -34,59 +34,48 @@ See STIR/LICENSE.txt for details */ - #include "stir/recon_buildblock/ProjMatrixByBin.h" #include "stir/recon_buildblock/ForwardProjectorByBin.h" #include "stir/RegisteredParsingObject.h" #include "stir/shared_ptr.h" - - START_NAMESPACE_STIR -template class RelatedViewgrams; +template +class RelatedViewgrams; /*! - \brief This implements the ForwardProjectorByBin interface, given any + \brief This implements the ForwardProjectorByBin interface, given any ProjMatrixByBin object \ingroup projection It stores a shared_ptr to a ProjMatrixByBin object, which will be used to get the relevant elements of the projection matrix. */ -class ForwardProjectorByBinUsingProjMatrixByBin: - public RegisteredParsingObject -{ +class ForwardProjectorByBinUsingProjMatrixByBin + : public RegisteredParsingObject { public: - //! Name which will be used when parsing a ForwardProjectorByBin object - static const char * const registered_name; + //! Name which will be used when parsing a ForwardProjectorByBin object + static const char* const registered_name; ForwardProjectorByBinUsingProjMatrixByBin(); - ForwardProjectorByBinUsingProjMatrixByBin( - const shared_ptr& proj_matrix_ptr - ); + ForwardProjectorByBinUsingProjMatrixByBin(const shared_ptr& proj_matrix_ptr); //! Stores all necessary geometric info /*! Note that the density_info_ptr is not stored in this object. It's only used to get some info on sizes etc. - */ - virtual void set_up( - const shared_ptr& proj_data_info_ptr, - const shared_ptr >& density_info_ptr // TODO should be Info only - ); + */ + virtual void set_up(const shared_ptr& proj_data_info_ptr, + const shared_ptr>& density_info_ptr // TODO should be Info only + ); - - const DataSymmetriesForViewSegmentNumbers * get_symmetries_used() const; + const DataSymmetriesForViewSegmentNumbers* get_symmetries_used() const; private: - shared_ptr proj_matrix_ptr; - + shared_ptr proj_matrix_ptr; - void actual_forward_project(RelatedViewgrams&, - const DiscretisedDensity<3,float>& image, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num); + void actual_forward_project(RelatedViewgrams&, const DiscretisedDensity<3, float>& image, const int min_axial_pos_num, + const int max_axial_pos_num, const int min_tangential_pos_num, const int max_tangential_pos_num); #if 0 // disabled as currently not used. needs to be written in the new style anyway void actual_forward_project(Bin&, const DiscretisedDensity<3,float>&); @@ -95,13 +84,8 @@ class ForwardProjectorByBinUsingProjMatrixByBin: virtual void set_defaults(); virtual void initialise_keymap(); virtual bool post_processing(); - }; - END_NAMESPACE_STIR - #endif - - diff --git a/src/include/stir/recon_buildblock/ForwardProjectorByBinUsingRayTracing.h b/src/include/stir/recon_buildblock/ForwardProjectorByBinUsingRayTracing.h index 59c27bdeea..947a2f1ada 100644 --- a/src/include/stir/recon_buildblock/ForwardProjectorByBinUsingRayTracing.h +++ b/src/include/stir/recon_buildblock/ForwardProjectorByBinUsingRayTracing.h @@ -4,12 +4,12 @@ \file \ingroup projection - + \brief Declaration of class stir::ForwardProjectorByBinUsingRayTracing - + \author Kris Thielemans \author PARAPET project - + */ /* Copyright (C) 2000 PARAPET partners @@ -39,29 +39,32 @@ #include "stir/shared_ptr.h" START_NAMESPACE_STIR -template class Viewgram; -template class RelatedViewgrams; -template class VoxelsOnCartesianGrid; -template class Array; +template +class Viewgram; +template +class RelatedViewgrams; +template +class VoxelsOnCartesianGrid; +template +class Array; class ProjDataInfo; class ProjDataInfoCylindrical; - /*! \ingroup projection \brief This class implements forward projection using Siddon's algorithm for ray tracing. That is, it computes length of intersection with the voxels. Currently, the LOIs are divided by voxel_size.x(), unless NEWSCALE is - #defined during compilation time of ForwardProjectorByBinUsingRayTracing_Siddon.cxx. + #defined during compilation time of ForwardProjectorByBinUsingRayTracing_Siddon.cxx. If the z voxel size is exactly twice the sampling in axial direction, multiple LORs are used, to avoid missing voxels. (TODOdoc describe how). - Currently, a FOV is used which is circular, and is slightly 'inside' the + Currently, a FOV is used which is circular, and is slightly 'inside' the image (i.e. the radius is about 1 voxel smaller than the maximum possible). - \warning Current implementation assumes that x,y voxel sizes are at least as + \warning Current implementation assumes that x,y voxel sizes are at least as large as the sampling in tangential direction, and that z voxel size is either equal to or exactly twice the sampling in axial direction of the segments. @@ -71,47 +74,39 @@ class ProjDataInfoCylindrical; \warning The implementation assumes that the \c s -coordinate is antisymmetric in terms of the tangential_pos_num, i.e. \code - proj_data_info_ptr->get_s(Bin(...,tang_pos_num)) == + proj_data_info_ptr->get_s(Bin(...,tang_pos_num)) == - proj_data_info_ptr->get_s(Bin(...,-tang_pos_num)) \endcode */ -class ForwardProjectorByBinUsingRayTracing : - public RegisteredParsingObject -{ +class ForwardProjectorByBinUsingRayTracing + : public RegisteredParsingObject { public: - //! Name which will be used when parsing a ForwardProjectorByBin object - static const char * const registered_name; - + //! Name which will be used when parsing a ForwardProjectorByBin object + static const char* const registered_name; ForwardProjectorByBinUsingRayTracing(); //! Constructor /*! \warning Obsolete */ - ForwardProjectorByBinUsingRayTracing( - const shared_ptr&, - const shared_ptr >&); + ForwardProjectorByBinUsingRayTracing(const shared_ptr&, + const shared_ptr>&); //! Stores all necessary geometric info /*! Note that the density_info_ptr is not stored in this object. It's only used to get some info on sizes etc. - */ - virtual void set_up( - const shared_ptr& proj_data_info_ptr, - const shared_ptr >& density_info_ptr // TODO should be Info only - ); + */ + virtual void set_up(const shared_ptr& proj_data_info_ptr, + const shared_ptr>& density_info_ptr // TODO should be Info only + ); - virtual const DataSymmetriesForViewSegmentNumbers * get_symmetries_used() const; + virtual const DataSymmetriesForViewSegmentNumbers* get_symmetries_used() const; - protected: +protected: //! variable that determines if a cylindrical FOV or the whole image will be handled bool restrict_to_cylindrical_FOV; - private: - void actual_forward_project(RelatedViewgrams&, - const DiscretisedDensity<3,float>&, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num); + void actual_forward_project(RelatedViewgrams&, const DiscretisedDensity<3, float>&, const int min_axial_pos_num, + const int max_axial_pos_num, const int min_tangential_pos_num, const int max_tangential_pos_num); #if 0 // disabled as currently not used. needs to be written in the new style anyway void actual_forward_project(Bin&, const DiscretisedDensity<3,float>&); @@ -124,129 +119,86 @@ class ForwardProjectorByBinUsingRayTracing : Here 0<=view < num_views/4 (= 45 degrees) */ - void - forward_project_all_symmetries( - Viewgram & pos_view, - Viewgram & neg_view, - Viewgram & pos_plus90, - Viewgram & neg_plus90, - Viewgram & pos_min180, - Viewgram & neg_min180, - Viewgram & pos_min90, - Viewgram & neg_min90, - const VoxelsOnCartesianGrid& image, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) const; - + void forward_project_all_symmetries(Viewgram& pos_view, Viewgram& neg_view, Viewgram& pos_plus90, + Viewgram& neg_plus90, Viewgram& pos_min180, Viewgram& neg_min180, + Viewgram& pos_min90, Viewgram& neg_min90, + const VoxelsOnCartesianGrid& image, const int min_axial_pos_num, + const int max_axial_pos_num, const int min_tangential_pos_num, + const int max_tangential_pos_num) const; /* This function projects 4 viewgrams related by symmetry. - It will be used for view=0 or 45 degrees + It will be used for view=0 or 45 degrees (or others if the number of views is not a multiple of 4) Here 0<=view < num_views/2 (= 90 degrees) */ - void - forward_project_view_plus_90_and_delta( - Viewgram & pos_view, - Viewgram & neg_view, - Viewgram & pos_plus90, - Viewgram & neg_plus90, - const VoxelsOnCartesianGrid & image, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) const; + void forward_project_view_plus_90_and_delta(Viewgram& pos_view, Viewgram& neg_view, Viewgram& pos_plus90, + Viewgram& neg_plus90, const VoxelsOnCartesianGrid& image, + const int min_axial_pos_num, const int max_axial_pos_num, + const int min_tangential_pos_num, const int max_tangential_pos_num) const; /* This function projects 4 viewgrams related by symmetry. - It will be used for view=0 or 45 degrees + It will be used for view=0 or 45 degrees (or others if the number of views is not a multiple of 4) Here 0<=view < num_views/2 (= 90 degrees) */ - void - forward_project_view_min_180_and_delta( - Viewgram & pos_view, - Viewgram & neg_view, - Viewgram & pos_min180, - Viewgram & neg_min180, - const VoxelsOnCartesianGrid & image, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) const; + void forward_project_view_min_180_and_delta(Viewgram& pos_view, Viewgram& neg_view, Viewgram& pos_min180, + Viewgram& neg_min180, const VoxelsOnCartesianGrid& image, + const int min_axial_pos_num, const int max_axial_pos_num, + const int min_tangential_pos_num, const int max_tangential_pos_num) const; /* This function projects 4 viewgrams related by symmetry. - It will be used for view=0 or 45 degrees + It will be used for view=0 or 45 degrees (or others if the number of views is not a multiple of 4) Here 0<=view < num_views/2 (= 90 degrees) */ - void - forward_project_delta( - Viewgram & pos_view, - Viewgram & neg_view, - const VoxelsOnCartesianGrid & image, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) const; - - //////////////// 2D - void forward_project_all_symmetries_2D( - Viewgram & pos_view, - Viewgram & pos_plus90, - Viewgram & pos_min180, - Viewgram & pos_min90, - const VoxelsOnCartesianGrid& image, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) const; - void -forward_project_view_plus_90_2D(Viewgram & pos_view, - Viewgram & pos_plus90, - const VoxelsOnCartesianGrid & image, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) const; -void -forward_project_view_min_180_2D(Viewgram & pos_view, - Viewgram & pos_min180, - const VoxelsOnCartesianGrid & image, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) const; -// no symmetries -void -forward_project_view_2D(Viewgram & pos_view, - const VoxelsOnCartesianGrid & image, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) const; -#if defined(_MSC_VER) && _MSC_VER<1310 + void forward_project_delta(Viewgram& pos_view, Viewgram& neg_view, const VoxelsOnCartesianGrid& image, + const int min_axial_pos_num, const int max_axial_pos_num, const int min_tangential_pos_num, + const int max_tangential_pos_num) const; + + //////////////// 2D + void forward_project_all_symmetries_2D(Viewgram& pos_view, Viewgram& pos_plus90, Viewgram& pos_min180, + Viewgram& pos_min90, const VoxelsOnCartesianGrid& image, + const int min_axial_pos_num, const int max_axial_pos_num, + const int min_tangential_pos_num, const int max_tangential_pos_num) const; + void forward_project_view_plus_90_2D(Viewgram& pos_view, Viewgram& pos_plus90, + const VoxelsOnCartesianGrid& image, const int min_axial_pos_num, + const int max_axial_pos_num, const int min_tangential_pos_num, + const int max_tangential_pos_num) const; + void forward_project_view_min_180_2D(Viewgram& pos_view, Viewgram& pos_min180, + const VoxelsOnCartesianGrid& image, const int min_axial_pos_num, + const int max_axial_pos_num, const int min_tangential_pos_num, + const int max_tangential_pos_num) const; + // no symmetries + void forward_project_view_2D(Viewgram& pos_view, const VoxelsOnCartesianGrid& image, const int min_axial_pos_num, + const int max_axial_pos_num, const int min_tangential_pos_num, + const int max_tangential_pos_num) const; +#if defined(_MSC_VER) && _MSC_VER < 1310 /* VC 6.0 (and 7.0 ?) cannot use the normal syntax unfortunately See also http://www.boost.org/more/microsoft_vcpp.html So, we forget about the template in this case. Sigh */ -#define STIR_SIDDON_NO_TEMPLATE +# define STIR_SIDDON_NO_TEMPLATE #endif #ifndef STIR_SIDDON_NO_TEMPLATE - //! The actual implementation of Siddon's algorithm - /*! \return true if the LOR intersected the image, i.e. of Projptr (potentially) changed */ - template - static bool - proj_Siddon(Array<4,float> &Projptr, const VoxelsOnCartesianGrid &, - const shared_ptr proj_data_info_sptr, - const float cphi, const float sphi, const float delta, - const float s_in_mm, - const float R, const int min_ax_pos_num, const int max_ax_pos_num, const float offset, - const int num_planes_per_axial_pos, - const float axial_pos_to_z_offset, - const float norm_factor, - const bool restrict_to_cylindrical_FOV); + //! The actual implementation of Siddon's algorithm + /*! \return true if the LOR intersected the image, i.e. of Projptr (potentially) changed */ + template + static bool proj_Siddon(Array<4, float>& Projptr, const VoxelsOnCartesianGrid&, + const shared_ptr proj_data_info_sptr, const float cphi, const float sphi, + const float delta, const float s_in_mm, const float R, const int min_ax_pos_num, + const int max_ax_pos_num, const float offset, const int num_planes_per_axial_pos, + const float axial_pos_to_z_offset, const float norm_factor, const bool restrict_to_cylindrical_FOV); #else - static bool - proj_Siddon(int symmetry_type, - Array<4,float> &Projptr, const VoxelsOnCartesianGrid &, - const shared_ptr proj_data_info_ptr, - const float cphi, const float sphi, const float delta, - const float s_in_mm, - const float R, const int min_ax_pos_num, const int max_ax_pos_num, const float offset, - const int num_planes_per_axial_pos, - const float axial_pos_to_z_offset, - const float norm_factor, - const bool restrict_to_cylindrical_FOV); + static bool proj_Siddon(int symmetry_type, Array<4, float>& Projptr, const VoxelsOnCartesianGrid&, + const shared_ptr proj_data_info_ptr, const float cphi, const float sphi, + const float delta, const float s_in_mm, const float R, const int min_ax_pos_num, + const int max_ax_pos_num, const float offset, const int num_planes_per_axial_pos, + const float axial_pos_to_z_offset, const float norm_factor, const bool restrict_to_cylindrical_FOV); #endif virtual void set_defaults(); diff --git a/src/include/stir/recon_buildblock/FourierRebinning.h b/src/include/stir/recon_buildblock/FourierRebinning.h index 398d78e45e..37eb438e0d 100644 --- a/src/include/stir/recon_buildblock/FourierRebinning.h +++ b/src/include/stir/recon_buildblock/FourierRebinning.h @@ -1,9 +1,9 @@ // // -/*! - \file - \brief Class for FORE Reconstruction +/*! + \file + \brief Class for FORE Reconstruction \ingroup recon_buildblock \author Claire LABBE \author Kris Thielemans @@ -38,48 +38,47 @@ #include "stir/RegisteredParsingObject.h" #include - START_NAMESPACE_STIR -template class SegmentByView; -template class SegmentBySinogram; -//template class Sinogram; -template class Array; +template +class SegmentByView; +template +class SegmentBySinogram; +// template class Sinogram; +template +class Array; class Succeeded; /* \class PETCount_rebinned - \brief Class for rebinned elements counter + \brief Class for rebinned elements counter \ingroup recon_buildblock */ -class PETCount_rebinned -{ - - public: -//! Total rebinned elements - int total; -//! Total missed rebinned elements - int miss; -//! Total SSRB rebinned elements - int ssrb; +class PETCount_rebinned { + +public: + //! Total rebinned elements + int total; + //! Total missed rebinned elements + int miss; + //! Total SSRB rebinned elements + int ssrb; #ifdef PARALLEL - friend PMessage& operator<<(PMessage&, PETCount_rebinned&); - friend PMessage& operator>>(PMessage&, PETCount_rebinned&); - - PETCount_rebinned & operator+= (const PETCount_rebinned &rebin) - { - total += rebin.total; - miss += rebin.miss; - ssrb += rebin.ssrb; - return *this; - } + friend PMessage& operator<<(PMessage&, PETCount_rebinned&); + friend PMessage& operator>>(PMessage&, PETCount_rebinned&); + + PETCount_rebinned& operator+=(const PETCount_rebinned& rebin) { + total += rebin.total; + miss += rebin.miss; + ssrb += rebin.ssrb; + return *this; + } #endif -// Default constructor by initialising all the elements conter to null - explicit PETCount_rebinned(int total_v=0, int miss_v =0, int ssrb_v = 0) - :total(total_v), miss(miss_v), ssrb(ssrb_v) - {} - - ; + // Default constructor by initialising all the elements conter to null + explicit PETCount_rebinned(int total_v = 0, int miss_v = 0, int ssrb_v = 0) + : total(total_v), miss(miss_v), ssrb(ssrb_v){} + + ; }; /*! @@ -92,140 +91,131 @@ class PETCount_rebinned a) Initialise the 2D Fourier transform of all rebinned sinograms Pr(w,k);
    b) Process sequentially each pair of oblique sinograms pij and pji for i,j (= 0..2*num_rings-2) as:
    - - merge pij and pji to get a sinogram sampled over 2p;
    - - calculates the 2D FFT Pij(w,k) of the merged sinogram;
    - - assign each frequency component (w,k) to the rebinned sinogram of the slice lying closest axially to - z - (tk/w): Pr(w,k) = Pr(w,k) + Pij(w,k), where r is the nearest integer to (i+j) - k(i-j)/wR;
    + - merge pij and pji to get a sinogram sampled over 2p;
    + - calculates the 2D FFT Pij(w,k) of the merged sinogram;
    + - assign each frequency component (w,k) to the rebinned sinogram of the slice lying closest axially to + z - (tk/w): Pr(w,k) = Pr(w,k) + Pij(w,k), where r is the nearest integer to (i+j) - k(i-j)/wR;
    c) Normalise Pr(w,k) for the variable number of contributions to each w, k, r ;
    d) Calculate the 2D inverse FFT of each Pr(w,k) to get the rebinned sinogram Pr(s,f); - As FORE is based on a high frequency approximation, it is necessary to handle separately low and high frequencies. - This is done by subdividing the (w,k) plane into three sub regions defined by two parameters - in Fourier space, w (the continuous frequency corresponding to the radial coordinates s) and k - (the integer Fourier index corresponding to the azimuthal angle f), and by applying in each region + As FORE is based on a high frequency approximation, it is necessary to handle separately low and high frequencies. + This is done by subdividing the (w,k) plane into three sub regions defined by two parameters + in Fourier space, w (the continuous frequency corresponding to the radial coordinates s) and k + (the integer Fourier index corresponding to the azimuthal angle f), and by applying in each region a different method to estimated the rebinned sinogram. - The rebinned data are represented in spatial space with |s|\<=R, 0<=f\=R; |w|\>wlim or |k|\>klim. - Finally, in the low-frequency (region 3), Fourier rebinning is not applicable. - Therefore the rebinned data are estimated using only the oblique sinograms with - a small value of d : dlim. Owing to the small value of d, the axial shift can be + Finally, in the low-frequency (region 3), Fourier rebinning is not applicable. + Therefore the rebinned data are estimated using only the oblique sinograms with + a small value of d : dlim. Owing to the small value of d, the axial shift can be neglected as in the SSRB approximation. */ -class FourierRebinning : public RegisteredParsingObject< - FourierRebinning, - ProjDataRebinning, - ProjDataRebinning> -{ - private: +class FourierRebinning : public RegisteredParsingObject { +private: typedef ProjDataRebinning base_type; - public: + +public: //! Name which will be used when parsing a ProjDataRebinning object - static const char * const registered_name; + static const char* const registered_name; virtual void set_defaults(); - protected: -//! Smallest angular freq. index minimum 2 ( 1 is zero frequency) - int kmin; -//! Smallest transax. freq. index minimum 2 ( 1 is zero frequency) - int wmin; -//! Delta max for small omega limiting delta for SSRB for small freq. - int deltamin; -//! kc index for consistency - int kc; -//! fore_debug_level. Setting it to >0 will produce some debug information - int fore_debug_level; - - public: -//! default constructor calls set_defaults(); - FourierRebinning(); - -//! This method returns the type of the algorithm for the rebinning - std::string method_info() const - { return("FORE"); } - - -//! This method creates a stack of 2D rebinned sinograms from the whole 3D data set (i.e. the ProjData data) and saves it. - Succeeded rebin(); - -//! A set of get and set utility functions to access the rebinning parameters - inline void set_kmin(int km){kmin = km;} - inline void set_wmin(int wm){wmin = wm;} - inline void set_deltamin(int dm){deltamin = dm;} - inline void set_kc(int kcc) {kc = kcc;} - inline void set_fore_debug_level(int fdebug){fore_debug_level = fdebug;} - - inline int get_kmin(){return kmin;} - inline int get_wmin(){return wmin;} - inline int get_deltamin(){return deltamin;} - inline int get_kc() {return kc;} - inline int get_fore_debug_level(){return fore_debug_level;} - - private: - -/*! - \brief Fourier rebinning - - This method takes as input the 3D data set (Array3D) in Fourier space of one sinogram - for a given delta as the data dimension are (1,fft_size,nviews_pow2), the scanner informations - and returns the updated stack of 2D rebinned sinograms still in Fourier space, - the updated weigthing factors as well as the new rebinned elements counter. - - -*/ - void rebinning(Array<3,std::complex > &FT_rebinned_data, Array<3,float> &Weights_for_FT_rebinned_data, - PETCount_rebinned &num_rebinned, const Array<2,std::complex > &FT_current_sinogram, const float z, - const float average_ring_difference_in_segment, const int num_views_pow2, const int num_tang_poss_pow2, - const float half_distance_between_rings, const float sampling_distance_in_s, const float radial_sampling_freq_w, - const float R_field_of_view_mm, const float ratio_ring_spacing_to_ring_radius); - -/*! - \brief This method takes as input the real 3D data set - (in which the number of views have been extended to a number of power of 2) - and returns the rebinned sinograms in Fourier space, their weighting factors - as well as the counter rebinned elements - - \b Rebinning
    - Assign each frequency component (w,k) to the rebinned sinogram of the slice lying closest axially to - z - (tk/w) with t=((ring0 -ring1)*ring_spacing/(2*R) with R=ring_radius, - Pm(w,k) = Pm(w,k) + Pij(w,k) (i=ring0 and j=ring1), and m is the nearest integer to (i+j) -k(i-j)/(Rw)). -*/ - - void do_rebinning(Array<3,std::complex > &FT_rebinned_data, Array<3,float> &Weights_for_FT_rebinned_data, - PETCount_rebinned &count_rebinned, const SegmentBySinogram &segment, const int num_tang_poss_pow2, - const int num_views_pow2, const int num_planes, const float average_ring_difference_in_segment, - const float half_distance_between_rings, const float sampling_distance_in_s, - const float radial_sampling_freq_w, const float R_field_of_view_mm, - const float ratio_ring_spacing_to_ring_radius); - -/*! - This method takes as input the information of the 3D data set (ProjData) - as well as the reconstruction parameters and prints out the informations of reconstruction - parameters implemented in FORE as well as the CPU and relative timing of differents process - (i.e I/O, rebinning, backprojection, matrix handling). - fore_debug_level must be set to a integer value > 0 -*/ - void do_log_file(); - -//! This is a function to display the current counter of all rebinned elements - void do_display_count(PETCount_rebinned &num_rebinned_total); - - -//! This is a function to adjust the number of views of a segment to the next power of 2 - void do_adjust_nb_views_to_pow2(SegmentBySinogram &segment) ; - -//! This function checks if the steering and input paramters for FORE are inside the possible range of parameters - Succeeded fore_check_parameters(int num_tang_poss_pow2, int num_views_pow2, int max_segment_num_to_process); - - - protected: - virtual bool post_processing(); +protected: + //! Smallest angular freq. index minimum 2 ( 1 is zero frequency) + int kmin; + //! Smallest transax. freq. index minimum 2 ( 1 is zero frequency) + int wmin; + //! Delta max for small omega limiting delta for SSRB for small freq. + int deltamin; + //! kc index for consistency + int kc; + //! fore_debug_level. Setting it to >0 will produce some debug information + int fore_debug_level; + +public: + //! default constructor calls set_defaults(); + FourierRebinning(); + + //! This method returns the type of the algorithm for the rebinning + std::string method_info() const { return ("FORE"); } + + //! This method creates a stack of 2D rebinned sinograms from the whole 3D data set (i.e. the ProjData data) and saves it. + Succeeded rebin(); + + //! A set of get and set utility functions to access the rebinning parameters + inline void set_kmin(int km) { kmin = km; } + inline void set_wmin(int wm) { wmin = wm; } + inline void set_deltamin(int dm) { deltamin = dm; } + inline void set_kc(int kcc) { kc = kcc; } + inline void set_fore_debug_level(int fdebug) { fore_debug_level = fdebug; } + + inline int get_kmin() { return kmin; } + inline int get_wmin() { return wmin; } + inline int get_deltamin() { return deltamin; } + inline int get_kc() { return kc; } + inline int get_fore_debug_level() { return fore_debug_level; } + +private: + /*! + \brief Fourier rebinning + + This method takes as input the 3D data set (Array3D) in Fourier space of one sinogram + for a given delta as the data dimension are (1,fft_size,nviews_pow2), the scanner informations + and returns the updated stack of 2D rebinned sinograms still in Fourier space, + the updated weigthing factors as well as the new rebinned elements counter. + + + */ + void rebinning(Array<3, std::complex>& FT_rebinned_data, Array<3, float>& Weights_for_FT_rebinned_data, + PETCount_rebinned& num_rebinned, const Array<2, std::complex>& FT_current_sinogram, const float z, + const float average_ring_difference_in_segment, const int num_views_pow2, const int num_tang_poss_pow2, + const float half_distance_between_rings, const float sampling_distance_in_s, const float radial_sampling_freq_w, + const float R_field_of_view_mm, const float ratio_ring_spacing_to_ring_radius); + + /*! + \brief This method takes as input the real 3D data set + (in which the number of views have been extended to a number of power of 2) + and returns the rebinned sinograms in Fourier space, their weighting factors + as well as the counter rebinned elements + + \b Rebinning
    + Assign each frequency component (w,k) to the rebinned sinogram of the slice lying closest axially to + z - (tk/w) with t=((ring0 -ring1)*ring_spacing/(2*R) with R=ring_radius, + Pm(w,k) = Pm(w,k) + Pij(w,k) (i=ring0 and j=ring1), and m is the nearest integer to (i+j) -k(i-j)/(Rw)). + */ + + void do_rebinning(Array<3, std::complex>& FT_rebinned_data, Array<3, float>& Weights_for_FT_rebinned_data, + PETCount_rebinned& count_rebinned, const SegmentBySinogram& segment, const int num_tang_poss_pow2, + const int num_views_pow2, const int num_planes, const float average_ring_difference_in_segment, + const float half_distance_between_rings, const float sampling_distance_in_s, + const float radial_sampling_freq_w, const float R_field_of_view_mm, + const float ratio_ring_spacing_to_ring_radius); + + /*! + This method takes as input the information of the 3D data set (ProjData) + as well as the reconstruction parameters and prints out the informations of reconstruction + parameters implemented in FORE as well as the CPU and relative timing of differents process + (i.e I/O, rebinning, backprojection, matrix handling). + fore_debug_level must be set to a integer value > 0 + */ + void do_log_file(); + + //! This is a function to display the current counter of all rebinned elements + void do_display_count(PETCount_rebinned& num_rebinned_total); + + //! This is a function to adjust the number of views of a segment to the next power of 2 + void do_adjust_nb_views_to_pow2(SegmentBySinogram& segment); + + //! This function checks if the steering and input paramters for FORE are inside the possible range of parameters + Succeeded fore_check_parameters(int num_tang_poss_pow2, int num_views_pow2, int max_segment_num_to_process); + +protected: + virtual bool post_processing(); virtual void initialise_keymap(); - }; END_NAMESPACE_STIR diff --git a/src/include/stir/recon_buildblock/GeneralisedObjectiveFunction.h b/src/include/stir/recon_buildblock/GeneralisedObjectiveFunction.h index 7292367e31..1c4f3fdf00 100644 --- a/src/include/stir/recon_buildblock/GeneralisedObjectiveFunction.h +++ b/src/include/stir/recon_buildblock/GeneralisedObjectiveFunction.h @@ -31,7 +31,6 @@ #ifndef __stir_recon_buildblock_GeneralisedObjectiveFunction_H__ #define __stir_recon_buildblock_GeneralisedObjectiveFunction_H__ - #include "stir/RegisteredObject.h" #include "stir/ParsingObject.h" #include "stir/shared_ptr.h" @@ -44,20 +43,19 @@ START_NAMESPACE_STIR - class Succeeded; /*! \ingroup GeneralisedObjectiveFunction \brief A base class for 'generalised' objective functions, i.e. objective - functions for which at least a 'gradient' is defined. + functions for which at least a 'gradient' is defined. - Some iterative algorithms use an 'objective function' only in a - loose sense. They might for instance allow generalisations + Some iterative algorithms use an 'objective function' only in a + loose sense. They might for instance allow generalisations which no longer optimise a function. For example in the case of PoissonLogLikelihoodWithLinearModelForMeanAndProjData - with non-matching forward and back projectors, the 'gradient' + with non-matching forward and back projectors, the 'gradient' that is computed is generally not the gradient of the log-likelihood that corresponds to the forward projector. However, one hopes that it still points towards the optimum. @@ -70,7 +68,7 @@ class Succeeded; In tomography, we often use subsets, where the objective function is written as a sum of sub-objective functions. This class has some - subset functionality. When using subsets, the + subset functionality. When using subsets, the penalty will be distributed evenly over all subsets. While this increases the computational cost, it makes the subsets more 'balanced' which is best for most algorithms. @@ -89,97 +87,76 @@ class Succeeded; \endverbatim */ template -class GeneralisedObjectiveFunction: - public RegisteredObject > -{ +class GeneralisedObjectiveFunction : public RegisteredObject> { public: - - //GeneralisedObjectiveFunction(); - - virtual ~GeneralisedObjectiveFunction(); + // GeneralisedObjectiveFunction(); + virtual ~GeneralisedObjectiveFunction(); //! Creates a suitable target as determined by the parameters - virtual TargetT * - construct_target_ptr() const = 0; + virtual TargetT* construct_target_ptr() const = 0; //! Has to be called before using this object - virtual Succeeded - set_up(shared_ptr const& target_sptr); + virtual Succeeded set_up(shared_ptr const& target_sptr); //! This should compute the sub-gradient of the objective function at the \a current_estimate /*! The subgradient is the gradient of the objective function restricted to the subset specified. What this means depends on how this function is implemented later on in the hierarchy. - Computed as the difference of + Computed as the difference of compute_sub_gradient_without_penalty - and + and get_prior_ptr()->compute_gradient()/num_subsets. \warning Any data in \a gradient will be overwritten. */ - virtual void - compute_sub_gradient(TargetT& gradient, - const TargetT ¤t_estimate, - const int subset_num); + virtual void compute_sub_gradient(TargetT& gradient, const TargetT& current_estimate, const int subset_num); //! This should compute the sub-gradient of the unregularised objective function at the \a current_estimate - /*! + /*! \warning The derived class should overwrite any data in \a gradient. */ - virtual void - compute_sub_gradient_without_penalty(TargetT& gradient, - const TargetT ¤t_estimate, - const int subset_num) =0; + virtual void compute_sub_gradient_without_penalty(TargetT& gradient, const TargetT& current_estimate, const int subset_num) = 0; //! Compute the value of the unregularised sub-objective function at the \a current_estimate /*! Implemented in terms of actual_compute_objective_function_without_penalty. */ - virtual double - compute_objective_function_without_penalty(const TargetT& current_estimate, - const int subset_num); + virtual double compute_objective_function_without_penalty(const TargetT& current_estimate, const int subset_num); //! Compute the value of the unregularised objective function at the \a current_estimate /*! Computed by summing over all subsets. */ - virtual double - compute_objective_function_without_penalty(const TargetT& current_estimate); + virtual double compute_objective_function_without_penalty(const TargetT& current_estimate); //! Compute the value of the sub-penalty at the \a current_estimate /*! As each subset contains the same penalty, this function returns - the same as + the same as \code - compute_penalty(current_estimate)/num_subsets + compute_penalty(current_estimate)/num_subsets \endcode Implemented in terms of GeneralisedPrior::compute_value. \see compute_objective_function(const TargetT&) for sign conventions. */ - double - compute_penalty(const TargetT& current_estimate, - const int subset_num); + double compute_penalty(const TargetT& current_estimate, const int subset_num); //! Compute the value of the penalty at the \a current_estimate /*! Implemented in terms of GeneralisedPrior::compute_value. */ - double - compute_penalty(const TargetT& current_estimate); + double compute_penalty(const TargetT& current_estimate); //! Compute the value of the sub-objective function at the \a current_estimate - /*! Computed as the difference of + /*! Computed as the difference of compute_objective_function_without_penalty - and + and compute_penalty. - */ - double - compute_objective_function(const TargetT& current_estimate, - const int subset_num); + */ + double compute_objective_function(const TargetT& current_estimate, const int subset_num); //! Compute the value of the objective function at the \a current_estimate - /*! Computed as the difference of + /*! Computed as the difference of compute_objective_function_without_penalty - and + and compute_penalty. - */ - double - compute_objective_function(const TargetT& current_estimate); + */ + double compute_objective_function(const TargetT& current_estimate); //! Fill any elements that we cannot estimate with a fixed value /*! In many cases, it is easier to use a larger target than what we can @@ -195,88 +172,62 @@ class GeneralisedObjectiveFunction: \todo The type of the value should really be derived from e.g. TargetT::full_iterator. */ - virtual void - fill_nonidentifiable_target_parameters(TargetT& target, const float value ) const - {} + virtual void fill_nonidentifiable_target_parameters(TargetT& target, const float value) const {} //! \name multiplication with approximate (sub)Hessian /*! \brief Functions that multiply the approximate (sub)Hessian with a \'vector\'. - + All these functions add their result to any existing data in \a output. They all call actual_add_multiplication_with_approximate_sub_Hessian_without_penalty. */ //@{ - Succeeded - add_multiplication_with_approximate_sub_Hessian_without_penalty(TargetT& output, - const TargetT& input, - const int subset_num) const; - Succeeded - add_multiplication_with_approximate_sub_Hessian(TargetT& output, - const TargetT& input, - const int subset_num) const; - Succeeded - add_multiplication_with_approximate_Hessian_without_penalty(TargetT& output, - const TargetT& input) const; - Succeeded - add_multiplication_with_approximate_Hessian(TargetT& output, - const TargetT& input) const; + Succeeded add_multiplication_with_approximate_sub_Hessian_without_penalty(TargetT& output, const TargetT& input, + const int subset_num) const; + Succeeded add_multiplication_with_approximate_sub_Hessian(TargetT& output, const TargetT& input, const int subset_num) const; + Succeeded add_multiplication_with_approximate_Hessian_without_penalty(TargetT& output, const TargetT& input) const; + Succeeded add_multiplication_with_approximate_Hessian(TargetT& output, const TargetT& input) const; //@} - //! \name multiplication (sub)Hessian times input - /*! \brief Functions that multiply the True (sub)Hessian with a \'vector\'. - - All these functions add their result to any existing data in \a output. - - They all call actual_accumulate_sub_Hessian_times_input_without_penalty. - */ - //@{ - Succeeded - accumulate_Hessian_times_input(TargetT& output, - const TargetT& current_image_estimate, - const TargetT& input) const; - - Succeeded - accumulate_Hessian_times_input_without_penalty(TargetT& output, - const TargetT& current_image_estimate, - const TargetT& input) const; + //! \name multiplication (sub)Hessian times input + /*! \brief Functions that multiply the True (sub)Hessian with a \'vector\'. + All these functions add their result to any existing data in \a output. - Succeeded - accumulate_sub_Hessian_times_input(TargetT& output, - const TargetT& current_image_estimate, - const TargetT& input, - const int subset_num) const; - Succeeded - accumulate_sub_Hessian_times_input_without_penalty(TargetT& output, - const TargetT& current_image_estimate, - const TargetT& input, - const int subset_num) const; + They all call actual_accumulate_sub_Hessian_times_input_without_penalty. + */ + //@{ + Succeeded accumulate_Hessian_times_input(TargetT& output, const TargetT& current_image_estimate, const TargetT& input) const; - //@} + Succeeded accumulate_Hessian_times_input_without_penalty(TargetT& output, const TargetT& current_image_estimate, + const TargetT& input) const; + Succeeded accumulate_sub_Hessian_times_input(TargetT& output, const TargetT& current_image_estimate, const TargetT& input, + const int subset_num) const; + Succeeded accumulate_sub_Hessian_times_input_without_penalty(TargetT& output, const TargetT& current_image_estimate, + const TargetT& input, const int subset_num) const; + //@} //! Construct a string with info on the value of objective function with and without penalty - std::string - get_objective_function_values_report(const TargetT& current_estimate); + std::string get_objective_function_values_report(const TargetT& current_estimate); //! Return the number of subsets in-use int get_num_subsets() const; - -// //! Virtual get normalisation, it will be defined by the derived class -// virtual const shared_ptr & -// get_normalisation_sptr() const =0; - - virtual std::unique_ptr get_exam_info_uptr_for_target() const - { auto exam_info_uptr=unique_ptr(new ExamInfo(*(this->get_input_data().get_exam_info_sptr()))); - return exam_info_uptr; + + // //! Virtual get normalisation, it will be defined by the derived class + // virtual const shared_ptr & + // get_normalisation_sptr() const =0; + + virtual std::unique_ptr get_exam_info_uptr_for_target() const { + auto exam_info_uptr = unique_ptr(new ExamInfo(*(this->get_input_data().get_exam_info_sptr()))); + return exam_info_uptr; } //! Return the status of TOF bool get_tof_status() const; - //! Attempts to change the number of subsets. + //! Attempts to change the number of subsets. /*! \return The number of subsets that will be used later, which is not guaranteed to be what you asked for. */ virtual int set_num_subsets(const int num_subsets) = 0; @@ -287,12 +238,12 @@ class GeneralisedObjectiveFunction: This function tests if this is approximately true, such that a reconstruction algorithm can either adapt or abort. - + Implemented in terms of actual_subsets_are_approximately_balanced(std::string&). */ bool subsets_are_approximately_balanced() const; //! Checks of the current subset scheme is approximately balanced and constructs a warning message - /*! + /*! \see subsets_are_approximately_balanced() \param warning_message A string variable. If the subsets are not (approx.) balanced, this function will append @@ -305,23 +256,21 @@ class GeneralisedObjectiveFunction: //! Read-only access to the prior /*! \todo It would be nicer to not return a pointer. - */ - GeneralisedPrior * const - get_prior_ptr() const; + */ + GeneralisedPrior* const get_prior_ptr() const; - shared_ptr > - get_prior_sptr(); + shared_ptr> get_prior_sptr(); //! Change the prior /*! \warning You should call set_up() again after using this function. */ - void set_prior_sptr(const shared_ptr >&); + void set_prior_sptr(const shared_ptr>&); //! \brief set_input_data //! \author Nikos Efthimiou //! \details It can be used to set the data to be reconstructed //! within some other code, as opposed to via parsing. - virtual void set_input_data(const shared_ptr< ExamData > &) = 0; + virtual void set_input_data(const shared_ptr&) = 0; //! \brief get input data /*! Will throw an exception if it wasn't set first */ @@ -332,7 +281,7 @@ class GeneralisedObjectiveFunction: //! \details In the case the reconstruction process is called from another //! piece of code, the user should be able to set any additive sinogram //! - virtual void set_additive_proj_data_sptr(const shared_ptr&) = 0; + virtual void set_additive_proj_data_sptr(const shared_ptr&) = 0; //! \brief set_normalisation_sptr //! \author Nikos Efthimiou @@ -346,7 +295,7 @@ class GeneralisedObjectiveFunction: //! If set TOF information will be taken into account. bool use_tof; - shared_ptr > prior_sptr; + shared_ptr> prior_sptr; //! sets any default values /*! Has to be called by set_defaults in the leaf-class */ @@ -355,7 +304,7 @@ class GeneralisedObjectiveFunction: /*! Has to be called by initialise_keymap in the leaf-class */ virtual void initialise_keymap(); - //virtual bool post_processing(); + // virtual bool post_processing(); //! Implementation of function that checks subset balancing /*! @@ -363,7 +312,7 @@ class GeneralisedObjectiveFunction: \par Developer\'s note - The reason we have this function is that overloading + The reason we have this function is that overloading subsets_are_approximately_balanced(std::string&) in a derived class would hide subsets_are_approximately_balanced(). */ @@ -375,13 +324,11 @@ class GeneralisedObjectiveFunction: \par Developer\'s note - The reason we have this function is that overloading a function - in a derived class, hides all functions of the + The reason we have this function is that overloading a function + in a derived class, hides all functions of the same name. */ - virtual double - actual_compute_objective_function_without_penalty(const TargetT& current_estimate, - const int subset_num) = 0; + virtual double actual_compute_objective_function_without_penalty(const TargetT& current_estimate, const int subset_num) = 0; //! Implementation of the function that multiplies the approximate sub-Hessian with a vector. /*! @@ -392,33 +339,29 @@ class GeneralisedObjectiveFunction: \par Developer\'s note - The reason we have this function is that overloading a function - in a derived class, hides all functions of the + The reason we have this function is that overloading a function + in a derived class, hides all functions of the + same name. + */ + virtual Succeeded actual_add_multiplication_with_approximate_sub_Hessian_without_penalty(TargetT& output, const TargetT& input, + const int subset_num) const; + + //! Implementation of the function computes the sub-Hessian and multiplies by a vector. + /*! + \see accumulate_sub_Hessian_times_input_without_penalty(TargetT&,const TargetT&, TargetT&, const int). + + \warning The default implementation just calls error(). This behaviour has to be + overloaded by the derived classes. + + \par Developer\'s note + + The reason we have this function is that overloading a function + in a derived class, hides all functions of the same name. */ - virtual Succeeded - actual_add_multiplication_with_approximate_sub_Hessian_without_penalty(TargetT& output, - const TargetT& input, - const int subset_num) const; - - //! Implementation of the function computes the sub-Hessian and multiplies by a vector. - /*! - \see accumulate_sub_Hessian_times_input_without_penalty(TargetT&,const TargetT&, TargetT&, const int). - - \warning The default implementation just calls error(). This behaviour has to be - overloaded by the derived classes. - - \par Developer\'s note - - The reason we have this function is that overloading a function - in a derived class, hides all functions of the - same name. - */ - virtual Succeeded - actual_accumulate_sub_Hessian_times_input_without_penalty(TargetT& output, - const TargetT& current_image_estimate, - const TargetT& input, - const int subset_num) const; + virtual Succeeded actual_accumulate_sub_Hessian_times_input_without_penalty(TargetT& output, + const TargetT& current_image_estimate, + const TargetT& input, const int subset_num) const; }; END_NAMESPACE_STIR diff --git a/src/include/stir/recon_buildblock/GeneralisedPrior.h b/src/include/stir/recon_buildblock/GeneralisedPrior.h index 7e94a23fe1..dc3a7cc463 100644 --- a/src/include/stir/recon_buildblock/GeneralisedPrior.h +++ b/src/include/stir/recon_buildblock/GeneralisedPrior.h @@ -29,7 +29,6 @@ #ifndef __stir_recon_buildblock_GeneralisedPrior_H__ #define __stir_recon_buildblock_GeneralisedPrior_H__ - #include "stir/RegisteredObject.h" #include "stir/ParsingObject.h" @@ -41,32 +40,28 @@ class Succeeded; \ingroup priors \brief A base class for 'generalised' priors, i.e. priors for which at least - a 'gradient' is defined. + a 'gradient' is defined. This class exists to accomodate FilterRootPrior. Otherwise we could just live with Prior as a base class. */ template -class GeneralisedPrior: - public RegisteredObject > - +class GeneralisedPrior : public RegisteredObject> + { public: - - inline GeneralisedPrior(); + inline GeneralisedPrior(); //! compute the value of the function /*! For derived classes where this doesn't make sense, it's recommended to return 0. */ - virtual double - compute_value(const DataT ¤t_estimate) = 0; + virtual double compute_value(const DataT& current_estimate) = 0; //! This should compute the gradient of the log of the prior function at the \a current_estimate /*! The gradient is already multiplied with the penalisation_factor. \warning The derived class should overwrite any data in \a prior_gradient. */ - virtual void compute_gradient(DataT& prior_gradient, - const DataT ¤t_estimate) =0; + virtual void compute_gradient(DataT& prior_gradient, const DataT& current_estimate) = 0; //! This should compute the multiplication of the Hessian with a vector and add it to \a output /*! Default implementation just call error(). This function needs to be overridden by the @@ -75,27 +70,20 @@ class GeneralisedPrior: Instead, accumulate_Hessian_times_input() should be used. This method remains for backwards comparability. \warning The derived class should accumulate in \a output. */ - virtual Succeeded - add_multiplication_with_approximate_Hessian(DataT& output, - const DataT& input) const; - - //! This should compute the multiplication of the Hessian with a vector and add it to \a output - /*! Default implementation just call error(). This function needs to be overridden by the - derived class. - \warning The derived class should accumulate in \a output. - */ - virtual Succeeded - accumulate_Hessian_times_input(DataT& output, - const DataT& current_estimate, - const DataT& input) const; + virtual Succeeded add_multiplication_with_approximate_Hessian(DataT& output, const DataT& input) const; + //! This should compute the multiplication of the Hessian with a vector and add it to \a output + /*! Default implementation just call error(). This function needs to be overridden by the + derived class. + \warning The derived class should accumulate in \a output. + */ + virtual Succeeded accumulate_Hessian_times_input(DataT& output, const DataT& current_estimate, const DataT& input) const; inline float get_penalisation_factor() const; inline void set_penalisation_factor(float new_penalisation_factor); //! Has to be called before using this object - virtual Succeeded - set_up(shared_ptr const& target_sptr); + virtual Succeeded set_up(shared_ptr const& target_sptr); protected: float penalisation_factor; diff --git a/src/include/stir/recon_buildblock/GeneralisedPrior.inl b/src/include/stir/recon_buildblock/GeneralisedPrior.inl index 9a84a21261..9fcc0df525 100644 --- a/src/include/stir/recon_buildblock/GeneralisedPrior.inl +++ b/src/include/stir/recon_buildblock/GeneralisedPrior.inl @@ -28,25 +28,21 @@ START_NAMESPACE_STIR - template -GeneralisedPrior::GeneralisedPrior() -{ - penalisation_factor =0; +GeneralisedPrior::GeneralisedPrior() { + penalisation_factor = 0; } - template float -GeneralisedPrior:: -get_penalisation_factor() const -{ return penalisation_factor; } +GeneralisedPrior::get_penalisation_factor() const { + return penalisation_factor; +} template void -GeneralisedPrior:: -set_penalisation_factor(const float new_penalisation_factor) -{ penalisation_factor = new_penalisation_factor; } +GeneralisedPrior::set_penalisation_factor(const float new_penalisation_factor) { + penalisation_factor = new_penalisation_factor; +} END_NAMESPACE_STIR - diff --git a/src/include/stir/recon_buildblock/IterativeReconstruction.h b/src/include/stir/recon_buildblock/IterativeReconstruction.h index 28e0b23fd6..f587193335 100644 --- a/src/include/stir/recon_buildblock/IterativeReconstruction.h +++ b/src/include/stir/recon_buildblock/IterativeReconstruction.h @@ -19,12 +19,12 @@ See STIR/LICENSE.txt for details */ #ifndef __stir_recon_buildblock_IterativeReconstruction_h__ -#define __stir_recon_buildblock_IterativeReconstruction_h__ +# define __stir_recon_buildblock_IterativeReconstruction_h__ /*! - \file + \file \ingroup recon_buildblock - + \brief declares the stir::IterativeReconstruction class @@ -39,42 +39,42 @@ KT 10122001 - added get_initial_data_ptr and 0 argument reconstruct() */ -#include "stir/recon_buildblock/Reconstruction.h" -#include "stir/shared_ptr.h" -#include "stir/DataProcessor.h" -#include "stir/recon_buildblock/GeneralisedObjectiveFunction.h" +# include "stir/recon_buildblock/Reconstruction.h" +# include "stir/shared_ptr.h" +# include "stir/DataProcessor.h" +# include "stir/recon_buildblock/GeneralisedObjectiveFunction.h" START_NAMESPACE_STIR -/*! +/*! \brief base class for iterative reconstruction objects \ingroup recon_buildblock This is the base class for all iterative reconstruction methods. It provides the basic iteration mechanisms. What each iteration does has to be implemented in a derived class. - + \par Parsing parameters \verbatim ; any parameters from Reconstruction ; see GeneralisedObjectiveFunction - objective function type:= + objective function type:= + - number of subsets:= 1 start at subset:= 0 number of subiterations:= 1 save images at subiteration intervals:= 1 start at subiteration number:=2 - initial image := + initial image := enforce initial positivity condition:=1 ; specify processing after every few subiterations, see DataProcessor - inter-iteration filter subiteration interval:= - inter-iteration filter type := + inter-iteration filter subiteration interval:= + inter-iteration filter type := ; write objective function value to stderr at certain subiterations ; default value of 0 means: do not write it at all. @@ -86,17 +86,13 @@ START_NAMESPACE_STIR */ template -class IterativeReconstruction : public Reconstruction -{ - private: - typedef - Reconstruction - base_type; -public: +class IterativeReconstruction : public Reconstruction { +private: + typedef Reconstruction base_type; +public: //! accessor for the subiteration counter - int get_subiteration_num() const - {return subiteration_num;} + int get_subiteration_num() const { return subiteration_num; } //! accessor for finding the current subset number /*! The subset number is determined from the subiteration number. @@ -112,16 +108,15 @@ class IterativeReconstruction : public Reconstruction int get_subset_num(); //! Gets a pointer to the initial data - /*! This is either read from file, or constructed by construct_target_ptr(). + /*! This is either read from file, or constructed by construct_target_ptr(). In the latter case, its values are set to 0 or 1, depending on the value of IterativeReconstruction::initial_data_filename. - \todo Dependency on explicit strings "1" or "0" in - IterativeReconstruction::initial_data_filename is not nice. + \todo Dependency on explicit strings "1" or "0" in + IterativeReconstruction::initial_data_filename is not nice. \todo should not return a 'bare' pointer. */ - virtual TargetT * - get_initial_data_ptr() const; + virtual TargetT* get_initial_data_ptr() const; //! executes the reconstruction /*! @@ -130,9 +125,8 @@ class IterativeReconstruction : public Reconstruction See end_of_iteration_processing() for info on saving to file. \return Succeeded::yes if everything was alright. - */ - virtual Succeeded - reconstruct(); + */ + virtual Succeeded reconstruct(); //! executes the reconstruction with \a target_data_sptr as initial value /*! After calling set_up(), repeatedly calls update_estimate(); end_of_iteration_processing(); @@ -140,31 +134,26 @@ class IterativeReconstruction : public Reconstruction Final reconstruction is saved in \a target_data_sptr */ - virtual Succeeded - reconstruct(shared_ptr const& target_data_sptr); + virtual Succeeded reconstruct(shared_ptr const& target_data_sptr); //! A utility function that creates a filename_prefix by appending the current subiteration number /*! Only works when no extension is present. - */ - std::string - make_filename_prefix_subiteration_num(const std::string& filename_prefix) const; + */ + std::string make_filename_prefix_subiteration_num(const std::string& filename_prefix) const; //! A utility function that creates the output filename_prefix for the current subiteration number /*! Uses \a output_filename_prefix. Only works when no extension is present. */ - std::string - make_filename_prefix_subiteration_num() const; + std::string make_filename_prefix_subiteration_num() const; /*! \name Functions to get parameters - \warning Be careful with changing shared pointers. If you modify the objects in + \warning Be careful with changing shared pointers. If you modify the objects in one place, all objects that use the shared pointer will be affected. */ //@{ - GeneralisedObjectiveFunction const& - get_objective_function() const; + GeneralisedObjectiveFunction const& get_objective_function() const; - shared_ptr > - get_objective_function_sptr() const; + shared_ptr> get_objective_function_sptr() const; //! the maximum allowed number of full iterations const int get_max_num_full_iterations() const; @@ -172,7 +161,7 @@ class IterativeReconstruction : public Reconstruction //! the number of ordered subsets const int get_num_subsets() const; - //! the number of subiterations + //! the number of subiterations const int get_num_subiterations() const; //! value with which to initialize the subiteration counter @@ -181,7 +170,7 @@ class IterativeReconstruction : public Reconstruction //! the starting subset number const int get_start_subset_num() const; - //TODO rename + // TODO rename //! subiteration interval at which data will be saved const int get_save_interval() const; @@ -191,9 +180,9 @@ class IterativeReconstruction : public Reconstruction //! inter-iteration filter const DataProcessor& get_inter_iteration_filter() const; - shared_ptr > get_inter_iteration_filter_sptr(); + shared_ptr> get_inter_iteration_filter_sptr(); - //! subiteration interval at which to apply inter-iteration filters + //! subiteration interval at which to apply inter-iteration filters const int get_inter_iteration_filter_interval() const; //! subiteration interval at which to report the values of the objective function @@ -202,12 +191,12 @@ class IterativeReconstruction : public Reconstruction /*! \name Functions to set parameters This can be used as alternative to the parsing mechanism. - \warning Be careful with changing shared pointers. If you modify the objects in + \warning Be careful with changing shared pointers. If you modify the objects in one place, all objects that use the shared pointer will be affected. */ //@{ //! The objective function that will be optimised - void set_objective_function_sptr(const shared_ptr >&); + void set_objective_function_sptr(const shared_ptr>&); //! the maximum allowed number of full iterations void set_max_num_full_iterations(const int); @@ -215,7 +204,7 @@ class IterativeReconstruction : public Reconstruction //! the number of ordered subsets void set_num_subsets(const int); - //! the number of subiterations + //! the number of subiterations void set_num_subiterations(const int); //! value with which to initialize the subiteration counter @@ -224,7 +213,7 @@ class IterativeReconstruction : public Reconstruction //! the starting subset number void set_start_subset_num(const int); - //TODO rename + // TODO rename //! subiteration interval at which data will be saved void set_save_interval(const int); @@ -232,9 +221,9 @@ class IterativeReconstruction : public Reconstruction void set_randomise_subset_order(const bool); //! inter-iteration filter - void set_inter_iteration_filter_ptr(const shared_ptr >&); + void set_inter_iteration_filter_ptr(const shared_ptr>&); - //! subiteration interval at which to apply inter-iteration filters + //! subiteration interval at which to apply inter-iteration filters void set_inter_iteration_filter_interval(const int); //! subiteration interval at which to report the values of the objective function @@ -247,34 +236,32 @@ class IterativeReconstruction : public Reconstruction virtual const ExamData& get_input_data() const; //@} - virtual Succeeded set_up(shared_ptr const& target_data_ptr); + virtual Succeeded set_up(shared_ptr const& target_data_ptr); //! the principal operations for updating the data iterates at each iteration - virtual void update_estimate(TargetT ¤t_estimate)=0; + virtual void update_estimate(TargetT& current_estimate) = 0; protected: - IterativeReconstruction(); //! operations for the end of the iteration - /*! At specific subiteration numbers, this + /*! At specific subiteration numbers, this
    • applies the inter-filtering and/or post-filtering data processor,
    • -
    • writes the current data to file at the designated subiteration numbers +
    • writes the current data to file at the designated subiteration numbers (including the final one). Filenames used are determined by Reconstruction::output_filename_prefix,
    • -
    • writes the objective function values (using +
    • writes the objective function values (using GeneralisedObjectiveFunction::report_objective_function_values) to stderr.
    If your derived class redefines this virtual function, you will - probably want to call + probably want to call IterativeReconstruction::end_of_iteration_processing() in there anyway. */ // KT 14/12/2001 remove =0 as it's not a pure virtual and the default implementation is usually fine. - virtual void end_of_iteration_processing(TargetT ¤t_estimate); + virtual void end_of_iteration_processing(TargetT& current_estimate); - shared_ptr > - objective_function_sptr; + shared_ptr> objective_function_sptr; //! the subiteration counter int subiteration_num; @@ -282,16 +269,15 @@ class IterativeReconstruction : public Reconstruction //! used to abort the loop over iterations bool terminate_iterations; - // parameters - protected: +protected: //! the maximum allowed number of full iterations int max_num_full_iterations; //! the number of ordered subsets int num_subsets; - //! the number of subiterations + //! the number of subiterations int num_subiterations; //! value with which to initialize the subiteration counter @@ -303,7 +289,7 @@ class IterativeReconstruction : public Reconstruction //! the starting subset number int start_subset_num; - //TODO rename + // TODO rename //! subiteration interval at which data will be saved int save_interval; @@ -311,16 +297,12 @@ class IterativeReconstruction : public Reconstruction //! signals whether to randomise the subset order in each iteration bool randomise_subset_order; - //! inter-iteration filter - shared_ptr > inter_iteration_filter_ptr; - + shared_ptr> inter_iteration_filter_ptr; - - - //! subiteration interval at which to apply inter-iteration filters + //! subiteration interval at which to apply inter-iteration filters int inter_iteration_filter_interval; - + //! subiteration interval at which to report the values of the objective function /*! \warning This is generally time-consuming. */ @@ -334,19 +316,15 @@ class IterativeReconstruction : public Reconstruction //! used to check acceptable parameter ranges, etc... virtual bool post_processing(); - private: +private: //! member storing the order in which the subsets will be traversed in this iteration /*! Initialised and used by get_subset_num() */ VectorWithOffset _current_subset_array; //! used to randomly generate a subset sequence order for the current iteration VectorWithOffset randomly_permute_subset_order() const; - - }; END_NAMESPACE_STIR #endif // __IterativeReconstruction_h__ - - diff --git a/src/include/stir/recon_buildblock/LogcoshPrior.h b/src/include/stir/recon_buildblock/LogcoshPrior.h index ab426cecdd..496c4894fa 100644 --- a/src/include/stir/recon_buildblock/LogcoshPrior.h +++ b/src/include/stir/recon_buildblock/LogcoshPrior.h @@ -29,11 +29,9 @@ \author Zeljko Kereta */ - #ifndef __stir_recon_buildblock_LogcoshPrior_H__ #define __stir_recon_buildblock_LogcoshPrior_H__ - #include "stir/RegisteredParsingObject.h" #include "stir/recon_buildblock/PriorWithParabolicSurrogate.h" #include "stir/Array.h" @@ -43,7 +41,6 @@ START_NAMESPACE_STIR - /*! \ingroup priors \brief @@ -89,164 +86,149 @@ START_NAMESPACE_STIR */ template -class LogcoshPrior: public - RegisteredParsingObject< - LogcoshPrior, - GeneralisedPrior >, - PriorWithParabolicSurrogate > - > -{ +class LogcoshPrior : public RegisteredParsingObject, GeneralisedPrior>, + PriorWithParabolicSurrogate>> { private: - typedef - RegisteredParsingObject< LogcoshPrior, - GeneralisedPrior >, - PriorWithParabolicSurrogate > > - base_type; + typedef RegisteredParsingObject, GeneralisedPrior>, + PriorWithParabolicSurrogate>> + base_type; public: - //! Name which will be used when parsing a GeneralisedPrior object - static const char * const registered_name; + //! Name which will be used when parsing a GeneralisedPrior object + static const char* const registered_name; - //! Default constructor - LogcoshPrior(); + //! Default constructor + LogcoshPrior(); - //! Constructs it explicitly - LogcoshPrior(const bool only_2D, float penalization_factor); + //! Constructs it explicitly + LogcoshPrior(const bool only_2D, float penalization_factor); - //! Constructs it explicitly with scalar - LogcoshPrior(const bool only_2D, float penalization_factor, const float scalar); + //! Constructs it explicitly with scalar + LogcoshPrior(const bool only_2D, float penalization_factor, const float scalar); - virtual bool - parabolic_surrogate_curvature_depends_on_argument() const - { return false; } + virtual bool parabolic_surrogate_curvature_depends_on_argument() const { return false; } - //! compute the value of the function - double - compute_value(const DiscretisedDensity<3,elemT> ¤t_image_estimate); + //! compute the value of the function + double compute_value(const DiscretisedDensity<3, elemT>& current_image_estimate); - //! compute gradient - void compute_gradient(DiscretisedDensity<3,elemT>& prior_gradient, - const DiscretisedDensity<3,elemT> ¤t_image_estimate); + //! compute gradient + void compute_gradient(DiscretisedDensity<3, elemT>& prior_gradient, const DiscretisedDensity<3, elemT>& current_image_estimate); - //! compute the parabolic surrogate for the prior - void parabolic_surrogate_curvature(DiscretisedDensity<3,elemT>& parabolic_surrogate_curvature, - const DiscretisedDensity<3,elemT> ¤t_image_estimate); + //! compute the parabolic surrogate for the prior + void parabolic_surrogate_curvature(DiscretisedDensity<3, elemT>& parabolic_surrogate_curvature, + const DiscretisedDensity<3, elemT>& current_image_estimate); - //! compute Hessian - void compute_Hessian(DiscretisedDensity<3,elemT>& prior_Hessian_for_single_densel, - const BasicCoordinate<3,int>& coords, - const DiscretisedDensity<3,elemT> ¤t_image_estimate); + //! compute Hessian + void compute_Hessian(DiscretisedDensity<3, elemT>& prior_Hessian_for_single_densel, const BasicCoordinate<3, int>& coords, + const DiscretisedDensity<3, elemT>& current_image_estimate); - //! Compute the multiplication of the hessian of the prior multiplied by the input. - virtual Succeeded accumulate_Hessian_times_input(DiscretisedDensity<3,elemT>& output, - const DiscretisedDensity<3,elemT>& current_estimate, - const DiscretisedDensity<3,elemT>& input) const; + //! Compute the multiplication of the hessian of the prior multiplied by the input. + virtual Succeeded accumulate_Hessian_times_input(DiscretisedDensity<3, elemT>& output, + const DiscretisedDensity<3, elemT>& current_estimate, + const DiscretisedDensity<3, elemT>& input) const; - //! get penalty weights for the neigbourhood - Array<3,float> get_weights() const; + //! get penalty weights for the neigbourhood + Array<3, float> get_weights() const; - //! set penalty weights for the neigbourhood - void set_weights(const Array<3,float>&); + //! set penalty weights for the neigbourhood + void set_weights(const Array<3, float>&); - //! get current kappa image - /*! \warning As this function returns a shared_ptr, this is dangerous. You should not - modify the image by manipulating the image referred to by this pointer. - Unpredictable results will occur. - */ - shared_ptr > get_kappa_sptr() const; + //! get current kappa image + /*! \warning As this function returns a shared_ptr, this is dangerous. You should not + modify the image by manipulating the image referred to by this pointer. + Unpredictable results will occur. + */ + shared_ptr> get_kappa_sptr() const; - //! set kappa image - void set_kappa_sptr(const shared_ptr >&); + //! set kappa image + void set_kappa_sptr(const shared_ptr>&); - //! Get the scalar value - float get_scalar() const; + //! Get the scalar value + float get_scalar() const; - //! Set the scalar value - void set_scalar(float scalar_v); + //! Set the scalar value + void set_scalar(float scalar_v); protected: - //! can be set during parsing to restrict the weights to the 2D case - bool only_2D; + //! can be set during parsing to restrict the weights to the 2D case + bool only_2D; - //! controls the transition between the quadratic (smooth) and linear (edge-preserving) nature of the prior - float scalar; + //! controls the transition between the quadratic (smooth) and linear (edge-preserving) nature of the prior + float scalar; - //! filename prefix for outputting the gradient whenever compute_gradient() is called. - /*! An internal counter is used to keep track of the number of times the - gradient is computed. The filename will be constructed by concatenating - gradient_filename_prefix and the counter. - */ - std::string gradient_filename_prefix; + //! filename prefix for outputting the gradient whenever compute_gradient() is called. + /*! An internal counter is used to keep track of the number of times the + gradient is computed. The filename will be constructed by concatenating + gradient_filename_prefix and the counter. + */ + std::string gradient_filename_prefix; - //! penalty weights - /*! - \todo This member is mutable at present because some const functions initialise it. - That initialisation should be moved to a new set_up() function. - */ - mutable Array<3,float> weights; + //! penalty weights + /*! + \todo This member is mutable at present because some const functions initialise it. + That initialisation should be moved to a new set_up() function. + */ + mutable Array<3, float> weights; - //! Filename for the \f$\kappa\f$ image that will be read by post_processing() - std::string kappa_filename; + //! Filename for the \f$\kappa\f$ image that will be read by post_processing() + std::string kappa_filename; - virtual void set_defaults(); - virtual void initialise_keymap(); - virtual bool post_processing(); + virtual void set_defaults(); + virtual void initialise_keymap(); + virtual bool post_processing(); private: - //! Spatially variant penalty penalty image ptr - shared_ptr > kappa_ptr; - - //! The Log(cosh()) function and its approximation - /*! - Cosh(x) = 0.5(e^x + e^-x) is an exponential function and hence cannot be evaluated for large x. - Make the approximation: - log(Cosh(x)) = log(0.5) + |x| + log(1 + e^(-2|x|)) = log(0.5) + |x| + O(10^(-27)), for |x|>30 - */ - static inline float logcosh(const float d) - { - const float x = fabs(d); - if ( x < 30.f ){ - return log(cosh(x)); - } else { - return x + log(0.5f); - } + //! Spatially variant penalty penalty image ptr + shared_ptr> kappa_ptr; + + //! The Log(cosh()) function and its approximation + /*! + Cosh(x) = 0.5(e^x + e^-x) is an exponential function and hence cannot be evaluated for large x. + Make the approximation: + log(Cosh(x)) = log(0.5) + |x| + log(1 + e^(-2|x|)) = log(0.5) + |x| + O(10^(-27)), for |x|>30 + */ + static inline float logcosh(const float d) { + const float x = fabs(d); + if (x < 30.f) { + return log(cosh(x)); + } else { + return x + log(0.5f); } - - //! The surrogate of the logcosh function is tanh(x)/x - /*! - * @param d should be the difference between the ith and jth voxel. - However, it will use the taylor expansion if the x is too small (to prevent division by 0). - * @param scalar is the logcosh scalar value controlling the priors transition between the quadratic and linear behaviour - * @return the surrogate of the log-cosh function - */ - static inline float surrogate(const float d, const float scalar) - { - const float eps = 0.01; - const float x = d * scalar; - // If abs(x) is less than eps, - // use Taylor approximatation of tanh: tanh(x)/x ~= (x - x^3/3)/x = 1- x^2/3. - // Prevents divide by zeros - if (fabs(x) -{ +class BackProjectorByBinNiftyPET : public RegisteredParsingObject { public: - //! Name which will be used when parsing a BackProjectorByBin object - static const char * const registered_name; + //! Name which will be used when parsing a BackProjectorByBin object + static const char* const registered_name; //! Default constructor calls reset_timers() BackProjectorByBinNiftyPET(); @@ -73,23 +70,21 @@ class BackProjectorByBinNiftyPET : virtual void initialise_keymap(); //! Stores all necessary geometric info - /*! - If necessary, set_up() can be called more than once. - */ - virtual void set_up( - const shared_ptr& proj_data_info_ptr, - const shared_ptr >& density_info_sptr // TODO should be Info only - ); + /*! + If necessary, set_up() can be called more than once. + */ + virtual void set_up(const shared_ptr& proj_data_info_ptr, + const shared_ptr>& density_info_sptr // TODO should be Info only + ); //! Symmetries not used, so returns TrivialDataSymmetriesForBins. - virtual const DataSymmetriesForViewSegmentNumbers * get_symmetries_used() const; + virtual const DataSymmetriesForViewSegmentNumbers* get_symmetries_used() const; /// Back project void back_project(const ProjData&, int subset_num = 0, int num_subsets = 1); - /// Get output - virtual void get_output(DiscretisedDensity<3,float> &) const; - + /// Get output + virtual void get_output(DiscretisedDensity<3, float>&) const; /*! \brief tell the back projector to start accumulating into a new target. This function has to be called before any back-projection is initiated.*/ @@ -103,12 +98,10 @@ class BackProjectorByBinNiftyPET : void set_use_truncation(const bool use_truncation) { _use_truncation = use_truncation; } protected: + virtual void actual_back_project(const RelatedViewgrams&, const int min_axial_pos_num, const int max_axial_pos_num, + const int min_tangential_pos_num, const int max_tangential_pos_num); - virtual void actual_back_project(const RelatedViewgrams&, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num); - - private: +private: shared_ptr _symmetries_sptr; NiftyPETHelper _helper; int _cuda_device; @@ -119,5 +112,4 @@ class BackProjectorByBinNiftyPET : END_NAMESPACE_STIR - #endif // __stir_gpu_BackProjectorByBinNiftyPET_h__ diff --git a/src/include/stir/recon_buildblock/NiftyPET_projector/ForwardProjectorByBinNiftyPET.h b/src/include/stir/recon_buildblock/NiftyPET_projector/ForwardProjectorByBinNiftyPET.h index 4724859231..637d46cb32 100644 --- a/src/include/stir/recon_buildblock/NiftyPET_projector/ForwardProjectorByBinNiftyPET.h +++ b/src/include/stir/recon_buildblock/NiftyPET_projector/ForwardProjectorByBinNiftyPET.h @@ -58,70 +58,65 @@ Current limitations: - Projects all of the data in one go - Only debugged for span 11. */ -class ForwardProjectorByBinNiftyPET: - public RegisteredParsingObject -{ +class ForwardProjectorByBinNiftyPET : public RegisteredParsingObject { public: //! Name which will be used when parsing a ForwardProjectorByBin object - static const char * const registered_name; + static const char* const registered_name; //! Default constructor calls reset_timers() - //inline - ForwardProjectorByBinNiftyPET(); + // inline + ForwardProjectorByBinNiftyPET(); - /// Constructor - virtual ~ForwardProjectorByBinNiftyPET(); + /// Constructor + virtual ~ForwardProjectorByBinNiftyPET(); - /// Keymap - virtual void initialise_keymap(); + /// Keymap + virtual void initialise_keymap(); //! Stores all necessary geometric info - /*! - If necessary, set_up() can be called more than once. + /*! + If necessary, set_up() can be called more than once. - Derived classes can assume that forward_project() will be called - with input corresponding to the arguments of the last call to set_up(). + Derived classes can assume that forward_project() will be called + with input corresponding to the arguments of the last call to set_up(). - \warning there is currently no check on this. - \warning Derived classes have to call set_up from the base class. - */ -virtual void set_up( - const shared_ptr& proj_data_info_ptr, - const shared_ptr >& density_info_sptr // TODO should be Info only - ); + \warning there is currently no check on this. + \warning Derived classes have to call set_up from the base class. + */ + virtual void set_up(const shared_ptr& proj_data_info_ptr, + const shared_ptr>& density_info_sptr // TODO should be Info only + ); //! Symmetries not used, so returns TrivialDataSymmetriesForBins. - virtual const DataSymmetriesForViewSegmentNumbers * get_symmetries_used() const; + virtual const DataSymmetriesForViewSegmentNumbers* get_symmetries_used() const; - /// Set input - virtual void set_input(const DiscretisedDensity<3,float>&); + /// Set input + virtual void set_input(const DiscretisedDensity<3, float>&); - /// Set verbosity - void set_verbosity(const bool verbosity) { _cuda_verbosity = verbosity; } + /// Set verbosity + void set_verbosity(const bool verbosity) { _cuda_verbosity = verbosity; } - /// Set use truncation - truncate before forward - /// projection and after back projection - void set_use_truncation(const bool use_truncation) { _use_truncation = use_truncation; } + /// Set use truncation - truncate before forward + /// projection and after back projection + void set_use_truncation(const bool use_truncation) { _use_truncation = use_truncation; } protected: //! This virtual function has to be implemented by the derived class. - virtual void actual_forward_project(RelatedViewgrams&, - const DiscretisedDensity<3,float>&, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num); + virtual void actual_forward_project(RelatedViewgrams&, const DiscretisedDensity<3, float>&, const int min_axial_pos_num, + const int max_axial_pos_num, const int min_tangential_pos_num, + const int max_tangential_pos_num); - virtual void actual_forward_project(RelatedViewgrams& viewgrams, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num); + virtual void actual_forward_project(RelatedViewgrams& viewgrams, const int min_axial_pos_num, + const int max_axial_pos_num, const int min_tangential_pos_num, + const int max_tangential_pos_num); private: - shared_ptr _symmetries_sptr; - shared_ptr _projected_data_sptr; - NiftyPETHelper _helper; - int _cuda_device; - bool _cuda_verbosity; - bool _use_truncation; + shared_ptr _symmetries_sptr; + shared_ptr _projected_data_sptr; + NiftyPETHelper _helper; + int _cuda_device; + bool _cuda_verbosity; + bool _use_truncation; }; END_NAMESPACE_STIR diff --git a/src/include/stir/recon_buildblock/NiftyPET_projector/NiftyPETHelper.h b/src/include/stir/recon_buildblock/NiftyPET_projector/NiftyPETHelper.h index 0ce0147b9e..adf7740321 100644 --- a/src/include/stir/recon_buildblock/NiftyPET_projector/NiftyPETHelper.h +++ b/src/include/stir/recon_buildblock/NiftyPET_projector/NiftyPETHelper.h @@ -53,126 +53,124 @@ struct axialLUT; START_NAMESPACE_STIR -template class DiscretisedDensity; +template +class DiscretisedDensity; class ProjData; -template class Viewgram; -template class VoxelsOnCartesianGrid; +template +class Viewgram; +template +class VoxelsOnCartesianGrid; /*! \ingroup projection \brief Helper class for the wrapped NiftyPET projectors. */ -class NiftyPETHelper -{ +class NiftyPETHelper { public: + /// Default constructor + NiftyPETHelper() : _already_set_up(false), _span(-1), _devid(0), _att(-1), _scanner_type(Scanner::Unknown_scanner) {} - /// Default constructor - NiftyPETHelper() : - _already_set_up(false), _span(-1), _devid(0), _att(-1), _scanner_type(Scanner::Unknown_scanner) - {} + /// Destructor + virtual ~NiftyPETHelper(); - /// Destructor - virtual ~NiftyPETHelper(); + /// Set CUDA device ID + void set_cuda_device_id(const int devid) { _devid = char(devid); } - /// Set CUDA device ID - void set_cuda_device_id(const int devid) { _devid = char(devid); } + /// Set span + void set_span(const char span) { _span = span; } - /// Set span - void set_span(const char span) { _span = span; } + /// Set emission (0) or transmission (1) - whether to exp{-result} for attenuation maps + void set_att(const char att) { _att = att; } - /// Set emission (0) or transmission (1) - whether to exp{-result} for attenuation maps - void set_att(const char att) { _att = att; } + /// Set verbosity level for CUDA output + void set_verbose(const bool verbose) { _verbose = verbose; } - /// Set verbosity level for CUDA output - void set_verbose(const bool verbose) { _verbose = verbose; } + /// Set scanner type + void set_scanner_type(const Scanner::Type scanner_type) { _scanner_type = scanner_type; } - /// Set scanner type - void set_scanner_type(const Scanner::Type scanner_type) { _scanner_type = scanner_type; } + /// Set up + void set_up(); - /// Set up - void set_up(); + /// Create NiftyPET image + static std::vector create_niftyPET_image(); - /// Create NiftyPET image - static std::vector create_niftyPET_image(); + /// Create STIR image with mMR dimensions + static shared_ptr> create_stir_im(); - /// Create STIR image with mMR dimensions - static shared_ptr > create_stir_im(); + /// Create NiftyPET singram with no gaps. Forward project into this + std::vector create_niftyPET_sinogram_no_gaps() const; - /// Create NiftyPET singram with no gaps. Forward project into this - std::vector create_niftyPET_sinogram_no_gaps() const; + /// Create NiftyPET sinogram with gaps. Use this before converting to stir. + std::vector create_niftyPET_sinogram_with_gaps() const; - /// Create NiftyPET sinogram with gaps. Use this before converting to stir. - std::vector create_niftyPET_sinogram_with_gaps() const; + /// Convert STIR image to NiftyPET image + static void convert_image_stir_to_niftyPET(std::vector& np, const DiscretisedDensity<3, float>& stir); - /// Convert STIR image to NiftyPET image - static void convert_image_stir_to_niftyPET(std::vector &np, const DiscretisedDensity<3,float> &stir); + /// Convert NiftyPET image to STIR image + static void convert_image_niftyPET_to_stir(DiscretisedDensity<3, float>& stir, const std::vector& np_vec); - /// Convert NiftyPET image to STIR image - static void convert_image_niftyPET_to_stir(DiscretisedDensity<3,float> &stir, const std::vector &np_vec); + /// Convert STIR proj data to NiftyPET proj data + void convert_proj_data_stir_to_niftyPET(std::vector& np_vec, const ProjData& stir) const; - /// Convert STIR proj data to NiftyPET proj data - void convert_proj_data_stir_to_niftyPET(std::vector &np_vec, const ProjData& stir) const; + /// Convert STIR viewgram to NiftyPET + void convert_viewgram_stir_to_niftyPET(std::vector& np_vec, const Viewgram& viewgram) const; - /// Convert STIR viewgram to NiftyPET - void convert_viewgram_stir_to_niftyPET(std::vector &np_vec, const Viewgram& viewgram) const; + /// Convert NiftyPET proj data to STIR proj data + void convert_proj_data_niftyPET_to_stir(ProjData& stir_sptr, const std::vector& np_vec) const; - /// Convert NiftyPET proj data to STIR proj data - void convert_proj_data_niftyPET_to_stir(ProjData &stir_sptr, const std::vector &np_vec) const; + /// Remove gaps from sinogram. Do some unavoidable const_casting as the wrapped methods don't use const + void remove_gaps(std::vector& sino_no_gaps, const std::vector& sino_w_gaps) const; - /// Remove gaps from sinogram. Do some unavoidable const_casting as the wrapped methods don't use const - void remove_gaps(std::vector &sino_no_gaps, const std::vector &sino_w_gaps) const; + /// Put gaps into sinogram. Do some unavoidable const_casting as the wrapped methods don't use const + void put_gaps(std::vector& sino_w_gaps, const std::vector& sino_no_gaps) const; - /// Put gaps into sinogram. Do some unavoidable const_casting as the wrapped methods don't use const - void put_gaps(std::vector &sino_w_gaps, const std::vector &sino_no_gaps) const; + /// Back project. Do some unavoidable const_casting as the wrapped methods don't use const + void back_project(std::vector& image, const std::vector& sino_no_gaps) const; - /// Back project. Do some unavoidable const_casting as the wrapped methods don't use const - void back_project(std::vector &image, const std::vector &sino_no_gaps) const; + /// Forward project, returns sinogram without gaps. Do some unavoidable const_casting as the wrapped methods don't use const + void forward_project(std::vector& sino_no_gaps, const std::vector& image) const; - /// Forward project, returns sinogram without gaps. Do some unavoidable const_casting as the wrapped methods don't use const - void forward_project(std::vector &sino_no_gaps, const std::vector &image) const; + /// Create a STIR sinogram + static shared_ptr create_stir_sino(); - /// Create a STIR sinogram - static shared_ptr create_stir_sino(); - - /// Listmode to sinogram - void lm_to_proj_data(shared_ptr &prompts_sptr, shared_ptr &delayeds_sptr, - shared_ptr &randoms_sptr, shared_ptr &norm_sptr, - const int tstart, const int tstop, - const std::string &lm_binary_file, const std::string &norm_binary_file="") const; + /// Listmode to sinogram + void lm_to_proj_data(shared_ptr& prompts_sptr, shared_ptr& delayeds_sptr, + shared_ptr& randoms_sptr, shared_ptr& norm_sptr, const int tstart, const int tstop, + const std::string& lm_binary_file, const std::string& norm_binary_file = "") const; private: - - /// Check that set up has been run before returning data - void check_set_up() const; - - /// Permute the data - void permute(std::vector &output_array, const std::vector &orig_array, const unsigned output_dims[3], const unsigned *permute_order) const; - - /// Convert 3d NiftyPET proj data index to 1d - unsigned convert_NiftyPET_proj_3d_to_1d_idx(const unsigned ang, const unsigned bins, const unsigned sino) const; - - /// Convert 1d NiftyPET proj data index to 3d - void convert_NiftyPET_proj_1d_to_3d_idx(unsigned &ang, unsigned &bins, unsigned &sino, const unsigned idx) const; - - bool _already_set_up; - char _span; - char _devid; - shared_ptr _cnt_sptr; - int _nsinos; - char _att; - std::vector _isub; - bool _verbose; - Scanner::Type _scanner_type; - shared_ptr _txlut_sptr; - shared_ptr _axlut_sptr; - - std::vector _crs; - std::vector _s2c; - - // Get axLUT - std::vector _li2rng; - std::vector _li2sn; - std::vector _li2nos; + /// Check that set up has been run before returning data + void check_set_up() const; + + /// Permute the data + void permute(std::vector& output_array, const std::vector& orig_array, const unsigned output_dims[3], + const unsigned* permute_order) const; + + /// Convert 3d NiftyPET proj data index to 1d + unsigned convert_NiftyPET_proj_3d_to_1d_idx(const unsigned ang, const unsigned bins, const unsigned sino) const; + + /// Convert 1d NiftyPET proj data index to 3d + void convert_NiftyPET_proj_1d_to_3d_idx(unsigned& ang, unsigned& bins, unsigned& sino, const unsigned idx) const; + + bool _already_set_up; + char _span; + char _devid; + shared_ptr _cnt_sptr; + int _nsinos; + char _att; + std::vector _isub; + bool _verbose; + Scanner::Type _scanner_type; + shared_ptr _txlut_sptr; + shared_ptr _axlut_sptr; + + std::vector _crs; + std::vector _s2c; + + // Get axLUT + std::vector _li2rng; + std::vector _li2sn; + std::vector _li2nos; }; END_NAMESPACE_STIR diff --git a/src/include/stir/recon_buildblock/NiftyPET_projector/ProjectorByBinPairUsingNiftyPET.h b/src/include/stir/recon_buildblock/NiftyPET_projector/ProjectorByBinPairUsingNiftyPET.h index a816adac90..6d40245cf5 100644 --- a/src/include/stir/recon_buildblock/NiftyPET_projector/ProjectorByBinPairUsingNiftyPET.h +++ b/src/include/stir/recon_buildblock/NiftyPET_projector/ProjectorByBinPairUsingNiftyPET.h @@ -39,22 +39,16 @@ class Succeeded; \ingroup projection \brief A projector pair based on NiftyPET projectors */ -class ProjectorByBinPairUsingNiftyPET : - public RegisteredParsingObject -{ - private: - typedef - RegisteredParsingObject - base_type; +class ProjectorByBinPairUsingNiftyPET + : public RegisteredParsingObject { +private: + typedef RegisteredParsingObject base_type; + public: //! Name which will be used when parsing a ProjectorByBinPair object - static const char * const registered_name; + static const char* const registered_name; - //! Default constructor + //! Default constructor ProjectorByBinPairUsingNiftyPET(); /// Set verbosity @@ -65,7 +59,6 @@ class ProjectorByBinPairUsingNiftyPET : void set_use_truncation(const bool use_truncation); private: - void set_defaults(); void initialise_keymap(); bool post_processing(); @@ -75,5 +68,4 @@ class ProjectorByBinPairUsingNiftyPET : END_NAMESPACE_STIR - #endif // __stir_recon_buildblock_ProjectorByBinPairUsingNiftyPET_h_ diff --git a/src/include/stir/recon_buildblock/PLSPrior.h b/src/include/stir/recon_buildblock/PLSPrior.h index 372e0cd547..5dc9673b02 100644 --- a/src/include/stir/recon_buildblock/PLSPrior.h +++ b/src/include/stir/recon_buildblock/PLSPrior.h @@ -26,11 +26,9 @@ \author Yu-Jung Tsai */ - #ifndef __stir_recon_buildblock_PLSPrior_H__ #define __stir_recon_buildblock_PLSPrior_H__ - #include "stir/RegisteredParsingObject.h" #include "stir/recon_buildblock/PriorWithParabolicSurrogate.h" #include "stir/Array.h" @@ -40,7 +38,6 @@ START_NAMESPACE_STIR - /*! \ingroup priors \brief @@ -102,22 +99,16 @@ START_NAMESPACE_STIR */ template -class PLSPrior: public - RegisteredParsingObject< PLSPrior, - GeneralisedPrior >, - GeneralisedPrior > - > -{ - private: - typedef - RegisteredParsingObject< PLSPrior, - GeneralisedPrior >, - GeneralisedPrior > > - base_type; - - public: +class PLSPrior : public RegisteredParsingObject, GeneralisedPrior>, + GeneralisedPrior>> { +private: + typedef RegisteredParsingObject, GeneralisedPrior>, + GeneralisedPrior>> + base_type; + +public: //! Name which will be used when parsing a GeneralisedPrior object - static const char * const registered_name; + static const char* const registered_name; //! Default constructor PLSPrior(); @@ -127,47 +118,44 @@ class PLSPrior: public //! Has to be called before using this object /*! \todo set the anatomical image to zero if not defined */ - virtual Succeeded set_up(shared_ptr > const& target_sptr); + virtual Succeeded set_up(shared_ptr> const& target_sptr); //! compute the value of the function - double - compute_value(const DiscretisedDensity<3,elemT> ¤t_image_estimate); + double compute_value(const DiscretisedDensity<3, elemT>& current_image_estimate); //! compute gradient - void compute_gradient(DiscretisedDensity<3,elemT>& prior_gradient, - const DiscretisedDensity<3,elemT> ¤t_image_estimate); + void compute_gradient(DiscretisedDensity<3, elemT>& prior_gradient, const DiscretisedDensity<3, elemT>& current_image_estimate); //! get current kappa image /*! \warning As this function returns a shared_ptr, this is dangerous. You should not modify the image by manipulating the image refered to by this pointer. Unpredictable results will occur. */ - shared_ptr > get_kappa_sptr() const; - shared_ptr > get_anatomical_grad_sptr(int direction) const; - shared_ptr > get_norm_sptr() const; + shared_ptr> get_kappa_sptr() const; + shared_ptr> get_anatomical_grad_sptr(int direction) const; + shared_ptr> get_norm_sptr() const; - //!get eta and alpha parameters + //! get eta and alpha parameters double get_eta() const; double get_alpha() const; - //!set eta parameter + //! set eta parameter void set_eta(const double); - //!set alpha parameter + //! set alpha parameter void set_alpha(const double); //! set anatomical pointer - void set_anatomical_image_sptr(const shared_ptr >&); + void set_anatomical_image_sptr(const shared_ptr>&); //! get anatomical pointer - shared_ptr > get_anatomical_image_sptr() const; + shared_ptr> get_anatomical_image_sptr() const; /// Set anatomical filename void set_anatomical_filename(const std::string& filename); //! set kappa image - void set_kappa_sptr(const shared_ptr >&); + void set_kappa_sptr(const shared_ptr>&); /// Set kappa filename void set_kappa_filename(const std::string& filename); - /// Set only 2D void set_only_2D(const bool arg) { only_2D = arg; } /// Get only 2D @@ -196,40 +184,34 @@ class PLSPrior: public virtual bool post_processing(); //! Check that the prior is ready to be used - virtual void check(DiscretisedDensity<3,elemT> const& current_image_estimate) const; - - private: + virtual void check(DiscretisedDensity<3, elemT> const& current_image_estimate) const; +private: //! compute the component x, y or z of the image gradient using forward difference - static void compute_image_gradient_element(DiscretisedDensity<3,elemT> & image_gradient_elem, - int direction, - const DiscretisedDensity<3,elemT> & image ); + static void compute_image_gradient_element(DiscretisedDensity<3, elemT>& image_gradient_elem, int direction, + const DiscretisedDensity<3, elemT>& image); //! compute normalisation for the gradient of the anatomical image (Eq. (5) of the paper) - void compute_normalisation_anatomical_gradient(DiscretisedDensity<3, elemT> &norm_im_grad, - const DiscretisedDensity<3,elemT> &image_grad_z, - const DiscretisedDensity<3,elemT> &image_grad_y, - const DiscretisedDensity<3,elemT> &image_grad_x); + void compute_normalisation_anatomical_gradient(DiscretisedDensity<3, elemT>& norm_im_grad, + const DiscretisedDensity<3, elemT>& image_grad_z, + const DiscretisedDensity<3, elemT>& image_grad_y, + const DiscretisedDensity<3, elemT>& image_grad_x); //! Inner product in Eq. (9) of the paper but also the penalty function. - void compute_inner_product_and_penalty(DiscretisedDensity<3,elemT> &inner_product, - DiscretisedDensity<3,elemT> &penalty, - DiscretisedDensity<3,elemT> &pet_im_grad_z, - DiscretisedDensity<3,elemT> &pet_im_grad_y, - DiscretisedDensity<3,elemT> &pet_im_grad_x, - const DiscretisedDensity<3,elemT> &pet_image); - - shared_ptr > anatomical_grad_x_sptr; - shared_ptr > anatomical_grad_y_sptr; - shared_ptr > anatomical_grad_z_sptr; - shared_ptr > anatomical_sptr; - shared_ptr > norm_sptr; - shared_ptr > kappa_ptr; - void set_anatomical_grad_sptr(const shared_ptr >&, int); - void set_anatomical_grad_norm_sptr(const shared_ptr >&); - }; - + void compute_inner_product_and_penalty(DiscretisedDensity<3, elemT>& inner_product, DiscretisedDensity<3, elemT>& penalty, + DiscretisedDensity<3, elemT>& pet_im_grad_z, DiscretisedDensity<3, elemT>& pet_im_grad_y, + DiscretisedDensity<3, elemT>& pet_im_grad_x, + const DiscretisedDensity<3, elemT>& pet_image); + + shared_ptr> anatomical_grad_x_sptr; + shared_ptr> anatomical_grad_y_sptr; + shared_ptr> anatomical_grad_z_sptr; + shared_ptr> anatomical_sptr; + shared_ptr> norm_sptr; + shared_ptr> kappa_ptr; + void set_anatomical_grad_sptr(const shared_ptr>&, int); + void set_anatomical_grad_norm_sptr(const shared_ptr>&); +}; END_NAMESPACE_STIR #endif - diff --git a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData.h b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData.h index 1415e7d8e1..1274a00922 100644 --- a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData.h +++ b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData.h @@ -46,7 +46,7 @@ START_NAMESPACE_STIR /*! \ingroup GeneralisedObjectiveFunction \ingroup modelling - \brief a base class for LogLikelihood of independent Poisson variables + \brief a base class for LogLikelihood of independent Poisson variables where the mean values are linear combinations of the kinetic parameters. \par Parameters for parsing @@ -54,21 +54,19 @@ START_NAMESPACE_STIR */ template -class PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData: -public RegisteredParsingObject, - GeneralisedObjectiveFunction, - PoissonLogLikelihoodWithLinearModelForMean > -{ - private: - typedef RegisteredParsingObject, - GeneralisedObjectiveFunction, - PoissonLogLikelihoodWithLinearModelForMean > base_type; - typedef PoissonLogLikelihoodWithLinearModelForMeanAndProjData > SingleFrameObjFunc ; +class PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData + : public RegisteredParsingObject, + GeneralisedObjectiveFunction, PoissonLogLikelihoodWithLinearModelForMean> { +private: + typedef RegisteredParsingObject, + GeneralisedObjectiveFunction, PoissonLogLikelihoodWithLinearModelForMean> + base_type; + typedef PoissonLogLikelihoodWithLinearModelForMeanAndProjData> SingleFrameObjFunc; VectorWithOffset _single_frame_obj_funcs; - public: - + +public: //! Name which will be used when parsing a GeneralisedObjectiveFunction object - static const char * const registered_name; + static const char* const registered_name; PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData(); @@ -76,42 +74,32 @@ public RegisteredParsingObject - get_exam_info_uptr_for_target() const; - protected: - virtual double - actual_compute_objective_function_without_penalty(const TargetT& current_estimate, - const int subset_num); - - virtual Succeeded set_up_before_sensitivity(shared_ptr const& target_sptr); + virtual TargetT* construct_target_ptr() const; + virtual void compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, const TargetT& current_estimate, + const int subset_num); + + virtual std::unique_ptr get_exam_info_uptr_for_target() const; + +protected: + virtual double actual_compute_objective_function_without_penalty(const TargetT& current_estimate, const int subset_num); + + virtual Succeeded set_up_before_sensitivity(shared_ptr const& target_sptr); //! Add subset sensitivity to existing data /*! \todo Current implementation does NOT add to the subset sensitivity, but overwrites */ - virtual void - add_subset_sensitivity(TargetT& sensitivity, const int subset_num) const; + virtual void add_subset_sensitivity(TargetT& sensitivity, const int subset_num) const; - virtual Succeeded - actual_add_multiplication_with_approximate_sub_Hessian_without_penalty(TargetT& output, - const TargetT& input, - const int subset_num) const; + virtual Succeeded actual_add_multiplication_with_approximate_sub_Hessian_without_penalty(TargetT& output, const TargetT& input, + const int subset_num) const; - virtual Succeeded - actual_accumulate_sub_Hessian_times_input_without_penalty(TargetT &output, - const TargetT ¤t_image_estimate, - const TargetT &input, - const int subset_num) const; + virtual Succeeded actual_accumulate_sub_Hessian_times_input_without_penalty(TargetT& output, + const TargetT& current_image_estimate, + const TargetT& input, const int subset_num) const; - public: +public: /*! \name Functions to get parameters - \warning Be careful with changing shared pointers. If you modify the objects in + \warning Be careful with changing shared pointers. If you modify the objects in one place, all objects that use the shared pointer will be affected. */ //@{ @@ -130,7 +118,7 @@ public RegisteredParsingObject&); virtual void set_additive_proj_data_sptr(const shared_ptr&); - virtual void set_input_data(const shared_ptr &); + virtual void set_input_data(const shared_ptr&); virtual const DynamicProjData& get_input_data() const; //@} - protected: +protected: //! Filename with input projection data std::string _input_filename; @@ -161,7 +149,7 @@ public RegisteredParsingObject _additive_dyn_proj_data_sptr; /*! the normalisation or/and attenuation data */ @@ -178,14 +166,13 @@ public RegisteredParsingObject -#include +#include // For the Patlak Plot Modelling #include "stir/modelling/ModelMatrix.h" #include "stir/recon_buildblock/PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData.h" #ifndef NDEBUG -#include "stir/IO/write_to_file.h" +# include "stir/IO/write_to_file.h" #endif START_NAMESPACE_STIR -template -const char * const -PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData:: -registered_name = -"PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData"; +template +const char* const PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData::registered_name = + "PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData"; -template +template void -PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData:: -set_defaults() -{ +PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData::set_defaults() { base_type::set_defaults(); - this->_input_filename=""; - this->_max_segment_num_to_process=-1; // use all segments - //num_views_to_add=1; // KT 20/06/2001 disabled + this->_input_filename = ""; + this->_max_segment_num_to_process = -1; // use all segments + // num_views_to_add=1; // KT 20/06/2001 disabled this->_dyn_proj_data_sptr.reset(); this->_zero_seg0_end_planes = 0; @@ -87,13 +83,12 @@ set_defaults() shared_ptr forward_projector_ptr(new ForwardProjectorByBinUsingRayTracing()); shared_ptr back_projector_ptr(new BackProjectorByBinUsingInterpolation()); #else - shared_ptr PM(new ProjMatrixByBinUsingRayTracing()); - shared_ptr forward_projector_ptr(new ForwardProjectorByBinUsingProjMatrixByBin(PM)); - shared_ptr back_projector_ptr(new BackProjectorByBinUsingProjMatrixByBin(PM)); + shared_ptr PM(new ProjMatrixByBinUsingRayTracing()); + shared_ptr forward_projector_ptr(new ForwardProjectorByBinUsingProjMatrixByBin(PM)); + shared_ptr back_projector_ptr(new BackProjectorByBinUsingProjMatrixByBin(PM)); #endif - this->_projector_pair_ptr. - reset(new ProjectorByBinPairUsingSeparateProjectors(forward_projector_ptr, back_projector_ptr)); + this->_projector_pair_ptr.reset(new ProjectorByBinPairUsingSeparateProjectors(forward_projector_ptr, back_projector_ptr)); this->_normalisation_sptr.reset(new TrivialBinNormalisation); this->target_parameter_parser.set_defaults(); @@ -102,15 +97,13 @@ set_defaults() this->_patlak_plot_sptr.reset(); } -template +template void -PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData:: -initialise_keymap() -{ +PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData::initialise_keymap() { base_type::initialise_keymap(); this->parser.add_start_key("PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData Parameters"); this->parser.add_stop_key("End PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData Parameters"); - this->parser.add_key("input file",&this->_input_filename); + this->parser.add_key("input file", &this->_input_filename); // parser.add_key("mash x views", &num_views_to_add); // KT 20/06/2001 disabled this->parser.add_key("maximum absolute segment number to process", &this->_max_segment_num_to_process); @@ -120,7 +113,7 @@ initialise_keymap() this->parser.add_parsing_key("Projector pair type", &this->_projector_pair_ptr); // Scatter correction - this->parser.add_key("additive sinograms",&this->_additive_dyn_proj_data_filename); + this->parser.add_key("additive sinograms", &this->_additive_dyn_proj_data_filename); // normalisation (and attenuation correction) this->parser.add_parsing_key("Bin Normalisation type", &this->_normalisation_sptr); @@ -132,220 +125,200 @@ initialise_keymap() // this->parser.add_parsing_key("prior type", &this->_prior_sptr); } -template +template bool -PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData:: -post_processing() -{ +PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData::post_processing() { if (base_type::post_processing() == true) return true; - if (this->_input_filename.length() == 0) - { warning("You need to specify an input filename"); return true; } - + if (this->_input_filename.length() == 0) { + warning("You need to specify an input filename"); + return true; + } + #if 0 // KT 20/06/2001 disabled as not functional yet if (num_views_to_add!=1 && (num_views_to_add<=0 || num_views_to_add%2 != 0)) { warning("The 'mash x views' key has an invalid value (must be 1 or even number)"); return true; } #endif - + this->_dyn_proj_data_sptr = DynamicProjData::read_from_file(_input_filename); - if (is_null_ptr(this->_dyn_proj_data_sptr)) - { warning("Error reading input file %s", _input_filename.c_str()); return true; } + if (is_null_ptr(this->_dyn_proj_data_sptr)) { + warning("Error reading input file %s", _input_filename.c_str()); + return true; + } this->target_parameter_parser.check_values(); - if (this->_additive_dyn_proj_data_filename != "0") - { - info(boost::format("Reading additive projdata data %1%") % this->_additive_dyn_proj_data_filename); - this->_additive_dyn_proj_data_sptr = DynamicProjData::read_from_file(this->_additive_dyn_proj_data_filename); - if (is_null_ptr(this->_additive_dyn_proj_data_sptr)) - { warning("Error reading additive input file %s", _additive_dyn_proj_data_filename.c_str()); return true; } - + if (this->_additive_dyn_proj_data_filename != "0") { + info(boost::format("Reading additive projdata data %1%") % this->_additive_dyn_proj_data_filename); + this->_additive_dyn_proj_data_sptr = DynamicProjData::read_from_file(this->_additive_dyn_proj_data_filename); + if (is_null_ptr(this->_additive_dyn_proj_data_sptr)) { + warning("Error reading additive input file %s", _additive_dyn_proj_data_filename.c_str()); + return true; } + } return false; } template -PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData:: -PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData() -{ +PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData< + TargetT>::PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData() { this->set_defaults(); } template -TargetT * -PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData:: -construct_target_ptr() const -{ - return - this->target_parameter_parser.create(this->get_input_data()); +TargetT* +PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData::construct_target_ptr() const { + return this->target_parameter_parser.create(this->get_input_data()); } -template - std::unique_ptr - PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData:: - get_exam_info_uptr_for_target() const -{ - auto exam_info_uptr = this->get_exam_info_uptr_for_target(); - if (auto norm_ptr = dynamic_cast(get_normalisation_sptr().get())) - { - exam_info_uptr->set_calibration_factor(norm_ptr->get_calibration_factor()); - // somehow tell the image that it's calibrated (do we have a way?) - } - else - { - exam_info_uptr->set_calibration_factor(1.F); - // somehow tell the image that it's not calibrated (do we have a way?) - } - return exam_info_uptr; +template +std::unique_ptr +PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData::get_exam_info_uptr_for_target() const { + auto exam_info_uptr = this->get_exam_info_uptr_for_target(); + if (auto norm_ptr = dynamic_cast(get_normalisation_sptr().get())) { + exam_info_uptr->set_calibration_factor(norm_ptr->get_calibration_factor()); + // somehow tell the image that it's calibrated (do we have a way?) + } else { + exam_info_uptr->set_calibration_factor(1.F); + // somehow tell the image that it's not calibrated (do we have a way?) + } + return exam_info_uptr; } /*************************************************************** subset balancing ***************************************************************/ -template +template bool -PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData:: -actual_subsets_are_approximately_balanced(std::string& warning_message) const -{ // call actual_subsets_are_approximately_balanced( for first single_frame_obj_func +PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData::actual_subsets_are_approximately_balanced( + std::string& warning_message) const { // call actual_subsets_are_approximately_balanced( for first single_frame_obj_func if (this->_patlak_plot_sptr->get_time_frame_definitions().get_num_frames() == 0 || this->_single_frame_obj_funcs.size() == 0) error("PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData:\n" "actual_subsets_are_approximately_balanced called but not frames yet.\n"); - else if(this->_single_frame_obj_funcs.size() != 0) - { - bool frames_are_balanced=true; - for(unsigned int frame_num=this->_patlak_plot_sptr->get_starting_frame();frame_num<=this->_patlak_plot_sptr->get_time_frame_definitions().get_num_frames();++frame_num) - frames_are_balanced &= this->_single_frame_obj_funcs[frame_num].subsets_are_approximately_balanced(warning_message); - return frames_are_balanced; - } - else + else if (this->_single_frame_obj_funcs.size() != 0) { + bool frames_are_balanced = true; + for (unsigned int frame_num = this->_patlak_plot_sptr->get_starting_frame(); + frame_num <= this->_patlak_plot_sptr->get_time_frame_definitions().get_num_frames(); ++frame_num) + frames_are_balanced &= this->_single_frame_obj_funcs[frame_num].subsets_are_approximately_balanced(warning_message); + return frames_are_balanced; + } else error("Something strange happened in PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData:\n" - "actual_subsets_are_approximately_balanced called before setup()?\n"); - return - false; + "actual_subsets_are_approximately_balanced called before setup()?\n"); + return false; } /*************************************************************** get_ functions ***************************************************************/ template -const DynamicProjData& -PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData:: -get_dyn_proj_data() const -{ return *this->_dyn_proj_data_sptr; } +const DynamicProjData& +PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData::get_dyn_proj_data() const { + return *this->_dyn_proj_data_sptr; +} template -const shared_ptr& -PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData:: -get_dyn_proj_data_sptr() const -{ return this->_dyn_proj_data_sptr; } +const shared_ptr& +PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData::get_dyn_proj_data_sptr() const { + return this->_dyn_proj_data_sptr; +} template -const int -PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData:: -get_max_segment_num_to_process() const -{ return this->_max_segment_num_to_process; } +const int +PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData::get_max_segment_num_to_process() const { + return this->_max_segment_num_to_process; +} template -const bool -PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData:: -get_zero_seg0_end_planes() const -{ return this->_zero_seg0_end_planes; } +const bool +PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData::get_zero_seg0_end_planes() const { + return this->_zero_seg0_end_planes; +} template -const DynamicProjData& -PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData:: -get_additive_dyn_proj_data() const -{ return *this->_additive_dyn_proj_data_sptr; } +const DynamicProjData& +PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData::get_additive_dyn_proj_data() const { + return *this->_additive_dyn_proj_data_sptr; +} template -const shared_ptr& -PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData:: -get_additive_dyn_proj_data_sptr() const -{ return this->_additive_dyn_proj_data_sptr; } +const shared_ptr& +PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData::get_additive_dyn_proj_data_sptr() const { + return this->_additive_dyn_proj_data_sptr; +} template -const ProjectorByBinPair& -PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData:: -get_projector_pair() const -{ return *this->_projector_pair_ptr; } +const ProjectorByBinPair& +PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData::get_projector_pair() const { + return *this->_projector_pair_ptr; +} template -const shared_ptr& -PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData:: -get_projector_pair_sptr() const -{ return this->_projector_pair_ptr; } +const shared_ptr& +PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData::get_projector_pair_sptr() const { + return this->_projector_pair_ptr; +} template -const BinNormalisation& -PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData:: -get_normalisation() const -{ return *this->_normalisation_sptr; } +const BinNormalisation& +PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData::get_normalisation() const { + return *this->_normalisation_sptr; +} template -const shared_ptr& -PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData:: -get_normalisation_sptr() const -{ return this->_normalisation_sptr; } - +const shared_ptr& +PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData::get_normalisation_sptr() const { + return this->_normalisation_sptr; +} /*************************************************************** set_ functions ***************************************************************/ -template +template int -PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData:: -set_num_subsets(const int num_subsets) -{ - for(unsigned int frame_num=this->_patlak_plot_sptr->get_starting_frame();frame_num<=this->_patlak_plot_sptr->get_time_frame_definitions().get_num_frames();++frame_num) - { - if(this->_single_frame_obj_funcs.size() != 0) - if(this->_single_frame_obj_funcs[frame_num].set_num_subsets(num_subsets) != num_subsets) - error("set_num_subsets didn't work"); - } - this->num_subsets=num_subsets; +PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData::set_num_subsets(const int num_subsets) { + for (unsigned int frame_num = this->_patlak_plot_sptr->get_starting_frame(); + frame_num <= this->_patlak_plot_sptr->get_time_frame_definitions().get_num_frames(); ++frame_num) { + if (this->_single_frame_obj_funcs.size() != 0) + if (this->_single_frame_obj_funcs[frame_num].set_num_subsets(num_subsets) != num_subsets) + error("set_num_subsets didn't work"); + } + this->num_subsets = num_subsets; return this->num_subsets; } /*************************************************************** set_up() ***************************************************************/ -template -Succeeded -PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData:: -set_up_before_sensitivity(shared_ptr const& target_sptr) -{ - if (this->_max_segment_num_to_process==-1) - this->_max_segment_num_to_process = - (this->_dyn_proj_data_sptr)->get_proj_data_sptr(1)->get_max_segment_num(); - - if (this->_max_segment_num_to_process > (this->_dyn_proj_data_sptr)->get_proj_data_sptr(1)->get_max_segment_num()) - { - warning("_max_segment_num_to_process (%d) is too large", - this->_max_segment_num_to_process); - return Succeeded::no; - } +template +Succeeded +PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData::set_up_before_sensitivity( + shared_ptr const& target_sptr) { + if (this->_max_segment_num_to_process == -1) + this->_max_segment_num_to_process = (this->_dyn_proj_data_sptr)->get_proj_data_sptr(1)->get_max_segment_num(); + + if (this->_max_segment_num_to_process > (this->_dyn_proj_data_sptr)->get_proj_data_sptr(1)->get_max_segment_num()) { + warning("_max_segment_num_to_process (%d) is too large", this->_max_segment_num_to_process); + return Succeeded::no; + } const shared_ptr proj_data_info_sptr( - (this->_dyn_proj_data_sptr->get_proj_data_sptr(1))->get_proj_data_info_sptr()->clone()); - proj_data_info_sptr-> - reduce_segment_range(-this->_max_segment_num_to_process, - +this->_max_segment_num_to_process); - - if (is_null_ptr(this->_projector_pair_ptr)) - { warning("You need to specify a projector pair"); return Succeeded::no; } - - if (this->num_subsets <= 0) - { - warning("Number of subsets %d should be larger than 0.", - this->num_subsets); - return Succeeded::no; - } + (this->_dyn_proj_data_sptr->get_proj_data_sptr(1))->get_proj_data_info_sptr()->clone()); + proj_data_info_sptr->reduce_segment_range(-this->_max_segment_num_to_process, +this->_max_segment_num_to_process); - if (is_null_ptr(this->_normalisation_sptr)) - { - warning("Invalid normalisation object"); - return Succeeded::no; - } + if (is_null_ptr(this->_projector_pair_ptr)) { + warning("You need to specify a projector pair"); + return Succeeded::no; + } + + if (this->num_subsets <= 0) { + warning("Number of subsets %d should be larger than 0.", this->num_subsets); + return Succeeded::no; + } + + if (is_null_ptr(this->_normalisation_sptr)) { + warning("Invalid normalisation object"); + return Succeeded::no; + } if (this->_normalisation_sptr->set_up(this->_dyn_proj_data_sptr->get_exam_info_sptr(), proj_data_info_sptr) == Succeeded::no) return Succeeded::no; @@ -353,279 +326,247 @@ set_up_before_sensitivity(shared_ptr const& target_sptr) if (this->_patlak_plot_sptr->set_up() == Succeeded::no) return Succeeded::no; - if (this->_patlak_plot_sptr->get_starting_frame()<=0 || this->_patlak_plot_sptr->get_starting_frame()>this->_patlak_plot_sptr->get_time_frame_definitions().get_num_frames()) - { - warning("Starting frame is %d. Generally, it should be a late frame,\nbut in any case it should be less than the number of frames %d\nand at least 1.",this->_patlak_plot_sptr->get_starting_frame(), this->_patlak_plot_sptr->get_time_frame_definitions().get_num_frames()); - return Succeeded::no; - } + if (this->_patlak_plot_sptr->get_starting_frame() <= 0 || + this->_patlak_plot_sptr->get_starting_frame() > this->_patlak_plot_sptr->get_time_frame_definitions().get_num_frames()) { + warning("Starting frame is %d. Generally, it should be a late frame,\nbut in any case it should be less than the number of " + "frames %d\nand at least 1.", + this->_patlak_plot_sptr->get_starting_frame(), + this->_patlak_plot_sptr->get_time_frame_definitions().get_num_frames()); + return Succeeded::no; + } { - const shared_ptr > - density_template_sptr((target_sptr->construct_single_density(1)).get_empty_copy()); + const shared_ptr> density_template_sptr( + (target_sptr->construct_single_density(1)).get_empty_copy()); const shared_ptr scanner_sptr(new Scanner(*proj_data_info_sptr->get_scanner_ptr())); - this->_dyn_image_template= - DynamicDiscretisedDensity(this->_patlak_plot_sptr->get_time_frame_definitions(), - this->_dyn_proj_data_sptr->get_start_time_in_secs_since_1970(), - scanner_sptr, - density_template_sptr); + this->_dyn_image_template = DynamicDiscretisedDensity(this->_patlak_plot_sptr->get_time_frame_definitions(), + this->_dyn_proj_data_sptr->get_start_time_in_secs_since_1970(), + scanner_sptr, density_template_sptr); // construct _single_frame_obj_funcs - this->_single_frame_obj_funcs.resize(this->_patlak_plot_sptr->get_starting_frame(),this->_patlak_plot_sptr->get_time_frame_definitions().get_num_frames()); - - for(unsigned int frame_num=this->_patlak_plot_sptr->get_starting_frame();frame_num<=this->_patlak_plot_sptr->get_time_frame_definitions().get_num_frames();++frame_num) - { - this->_single_frame_obj_funcs[frame_num].set_projector_pair_sptr(this->_projector_pair_ptr); - this->_single_frame_obj_funcs[frame_num].set_proj_data_sptr(this->_dyn_proj_data_sptr->get_proj_data_sptr(frame_num)); - this->_single_frame_obj_funcs[frame_num].set_max_segment_num_to_process(this->_max_segment_num_to_process); - this->_single_frame_obj_funcs[frame_num].set_zero_seg0_end_planes(this->_zero_seg0_end_planes!=0); - if(!is_null_ptr(this->_additive_dyn_proj_data_sptr)) - this->_single_frame_obj_funcs[frame_num].set_additive_proj_data_sptr(this->_additive_dyn_proj_data_sptr->get_proj_data_sptr(frame_num)); - this->_single_frame_obj_funcs[frame_num].set_num_subsets(this->num_subsets); - this->_single_frame_obj_funcs[frame_num].set_frame_num(frame_num); - this->_single_frame_obj_funcs[frame_num].set_frame_definitions(this->_patlak_plot_sptr->get_time_frame_definitions()); - this->_single_frame_obj_funcs[frame_num].set_normalisation_sptr(this->_normalisation_sptr); - this->_single_frame_obj_funcs[frame_num].set_recompute_sensitivity(this->get_recompute_sensitivity()); - this->_single_frame_obj_funcs[frame_num].set_use_subset_sensitivities(this->get_use_subset_sensitivities()); - if(this->_single_frame_obj_funcs[frame_num].set_up(density_template_sptr) != Succeeded::yes) - error("Single frame objective functions is not set correctly!"); - } - }//_single_frame_obj_funcs[frame_num] + this->_single_frame_obj_funcs.resize(this->_patlak_plot_sptr->get_starting_frame(), + this->_patlak_plot_sptr->get_time_frame_definitions().get_num_frames()); + + for (unsigned int frame_num = this->_patlak_plot_sptr->get_starting_frame(); + frame_num <= this->_patlak_plot_sptr->get_time_frame_definitions().get_num_frames(); ++frame_num) { + this->_single_frame_obj_funcs[frame_num].set_projector_pair_sptr(this->_projector_pair_ptr); + this->_single_frame_obj_funcs[frame_num].set_proj_data_sptr(this->_dyn_proj_data_sptr->get_proj_data_sptr(frame_num)); + this->_single_frame_obj_funcs[frame_num].set_max_segment_num_to_process(this->_max_segment_num_to_process); + this->_single_frame_obj_funcs[frame_num].set_zero_seg0_end_planes(this->_zero_seg0_end_planes != 0); + if (!is_null_ptr(this->_additive_dyn_proj_data_sptr)) + this->_single_frame_obj_funcs[frame_num].set_additive_proj_data_sptr( + this->_additive_dyn_proj_data_sptr->get_proj_data_sptr(frame_num)); + this->_single_frame_obj_funcs[frame_num].set_num_subsets(this->num_subsets); + this->_single_frame_obj_funcs[frame_num].set_frame_num(frame_num); + this->_single_frame_obj_funcs[frame_num].set_frame_definitions(this->_patlak_plot_sptr->get_time_frame_definitions()); + this->_single_frame_obj_funcs[frame_num].set_normalisation_sptr(this->_normalisation_sptr); + this->_single_frame_obj_funcs[frame_num].set_recompute_sensitivity(this->get_recompute_sensitivity()); + this->_single_frame_obj_funcs[frame_num].set_use_subset_sensitivities(this->get_use_subset_sensitivities()); + if (this->_single_frame_obj_funcs[frame_num].set_up(density_template_sptr) != Succeeded::yes) + error("Single frame objective functions is not set correctly!"); + } + } //_single_frame_obj_funcs[frame_num] return Succeeded::yes; } -template +template void -PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData:: -set_input_data(const shared_ptr & arg) -{ - this->_dyn_proj_data_sptr = dynamic_pointer_cast(arg); +PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData::set_input_data(const shared_ptr& arg) { + this->_dyn_proj_data_sptr = dynamic_pointer_cast(arg); } -template +template const DynamicProjData& -PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData:: -get_input_data() const -{ +PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData::get_input_data() const { return *this->_dyn_proj_data_sptr; } -template +template void -PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData:: -set_additive_proj_data_sptr(const shared_ptr &arg) -{ - this->_additive_dyn_proj_data_sptr = dynamic_pointer_cast(arg); +PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData::set_additive_proj_data_sptr( + const shared_ptr& arg) { + this->_additive_dyn_proj_data_sptr = dynamic_pointer_cast(arg); } -template +template void -PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData:: -set_normalisation_sptr(const shared_ptr& arg) -{ -// this->normalisation_sptr = arg; - error("Not implemeted yet"); +PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData::set_normalisation_sptr( + const shared_ptr& arg) { + // this->normalisation_sptr = arg; + error("Not implemeted yet"); } - /************************************************************************* functions that compute the value/gradient of the objective function etc *************************************************************************/ -template +template void -PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData:: -compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, - const TargetT ¤t_estimate, - const int subset_num) -{ - if (subset_num<0 || subset_num>=this->get_num_subsets()) +PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData< + TargetT>::compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, const TargetT& current_estimate, + const int subset_num) { + if (subset_num < 0 || subset_num >= this->get_num_subsets()) error("compute_sub_gradient_without_penalty subset_num out-of-range error"); - DynamicDiscretisedDensity dyn_gradient=this->_dyn_image_template; - DynamicDiscretisedDensity dyn_image_estimate=this->_dyn_image_template; + DynamicDiscretisedDensity dyn_gradient = this->_dyn_image_template; + DynamicDiscretisedDensity dyn_image_estimate = this->_dyn_image_template; - for(unsigned int frame_num=this->_patlak_plot_sptr->get_starting_frame();frame_num<=this->_patlak_plot_sptr->get_time_frame_definitions().get_num_frames();++frame_num) - std::fill(dyn_image_estimate[frame_num].begin_all(), - dyn_image_estimate[frame_num].end_all(), - 1.F); + for (unsigned int frame_num = this->_patlak_plot_sptr->get_starting_frame(); + frame_num <= this->_patlak_plot_sptr->get_time_frame_definitions().get_num_frames(); ++frame_num) + std::fill(dyn_image_estimate[frame_num].begin_all(), dyn_image_estimate[frame_num].end_all(), 1.F); - this->_patlak_plot_sptr->get_dynamic_image_from_parametric_image(dyn_image_estimate,current_estimate) ; - - // loop over single_frame and use model_matrix - for(unsigned int frame_num=this->_patlak_plot_sptr->get_starting_frame();frame_num<=this->_patlak_plot_sptr->get_time_frame_definitions().get_num_frames();++frame_num) - { - std::fill(dyn_gradient[frame_num].begin_all(), - dyn_gradient[frame_num].end_all(), - 1.F); + this->_patlak_plot_sptr->get_dynamic_image_from_parametric_image(dyn_image_estimate, current_estimate); + // loop over single_frame and use model_matrix + for (unsigned int frame_num = this->_patlak_plot_sptr->get_starting_frame(); + frame_num <= this->_patlak_plot_sptr->get_time_frame_definitions().get_num_frames(); ++frame_num) { + std::fill(dyn_gradient[frame_num].begin_all(), dyn_gradient[frame_num].end_all(), 1.F); - this->_single_frame_obj_funcs[frame_num]. - compute_sub_gradient_without_penalty_plus_sensitivity(dyn_gradient[frame_num], - dyn_image_estimate[frame_num], - subset_num); - } + this->_single_frame_obj_funcs[frame_num].compute_sub_gradient_without_penalty_plus_sensitivity( + dyn_gradient[frame_num], dyn_image_estimate[frame_num], subset_num); + } - this->_patlak_plot_sptr->multiply_dynamic_image_with_model_gradient(gradient, - dyn_gradient) ; + this->_patlak_plot_sptr->multiply_dynamic_image_with_model_gradient(gradient, dyn_gradient); } -template +template double -PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData:: -actual_compute_objective_function_without_penalty(const TargetT& current_estimate, - const int subset_num) -{ - assert(subset_num>=0); - assert(subset_numnum_subsets); +PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData::actual_compute_objective_function_without_penalty( + const TargetT& current_estimate, const int subset_num) { + assert(subset_num >= 0); + assert(subset_num < this->num_subsets); double result = 0.; - DynamicDiscretisedDensity dyn_image_estimate=this->_dyn_image_template; + DynamicDiscretisedDensity dyn_image_estimate = this->_dyn_image_template; // TODO why fill with 1? - for(unsigned int frame_num=this->_patlak_plot_sptr->get_starting_frame();frame_num<=this->_patlak_plot_sptr->get_time_frame_definitions().get_num_frames();++frame_num) - std::fill(dyn_image_estimate[frame_num].begin_all(), - dyn_image_estimate[frame_num].end_all(), - 1.F); - this->_patlak_plot_sptr->get_dynamic_image_from_parametric_image(dyn_image_estimate,current_estimate) ; - + for (unsigned int frame_num = this->_patlak_plot_sptr->get_starting_frame(); + frame_num <= this->_patlak_plot_sptr->get_time_frame_definitions().get_num_frames(); ++frame_num) + std::fill(dyn_image_estimate[frame_num].begin_all(), dyn_image_estimate[frame_num].end_all(), 1.F); + this->_patlak_plot_sptr->get_dynamic_image_from_parametric_image(dyn_image_estimate, current_estimate); + // loop over single_frame - for(unsigned int frame_num=this->_patlak_plot_sptr->get_starting_frame(); - frame_num<=this->_patlak_plot_sptr->get_time_frame_definitions().get_num_frames(); - ++frame_num) - { - result += - this->_single_frame_obj_funcs[frame_num]. - compute_objective_function_without_penalty(dyn_image_estimate[frame_num], - subset_num); - } + for (unsigned int frame_num = this->_patlak_plot_sptr->get_starting_frame(); + frame_num <= this->_patlak_plot_sptr->get_time_frame_definitions().get_num_frames(); ++frame_num) { + result += this->_single_frame_obj_funcs[frame_num].compute_objective_function_without_penalty(dyn_image_estimate[frame_num], + subset_num); + } return result; } -template +template void -PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData:: -add_subset_sensitivity(TargetT& sensitivity, const int subset_num) const -{ - DynamicDiscretisedDensity dyn_sensitivity=this->_dyn_image_template; +PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData::add_subset_sensitivity(TargetT& sensitivity, + const int subset_num) const { + DynamicDiscretisedDensity dyn_sensitivity = this->_dyn_image_template; // loop over single_frame and use model_matrix - for(unsigned int frame_num=this->_patlak_plot_sptr->get_starting_frame();frame_num<=this->_patlak_plot_sptr->get_time_frame_definitions().get_num_frames();++frame_num) - { - dyn_sensitivity[frame_num]=this->_single_frame_obj_funcs[frame_num].get_subset_sensitivity(subset_num); - // add_subset_sensitivity(dyn_sensitivity[frame_num],subset_num); - } + for (unsigned int frame_num = this->_patlak_plot_sptr->get_starting_frame(); + frame_num <= this->_patlak_plot_sptr->get_time_frame_definitions().get_num_frames(); ++frame_num) { + dyn_sensitivity[frame_num] = this->_single_frame_obj_funcs[frame_num].get_subset_sensitivity(subset_num); + // add_subset_sensitivity(dyn_sensitivity[frame_num],subset_num); + } - this->_patlak_plot_sptr->multiply_dynamic_image_with_model_gradient_and_add_to_input(sensitivity, - dyn_sensitivity) ; + this->_patlak_plot_sptr->multiply_dynamic_image_with_model_gradient_and_add_to_input(sensitivity, dyn_sensitivity); } -template +template Succeeded -PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData:: -actual_add_multiplication_with_approximate_sub_Hessian_without_penalty(TargetT& output, - const TargetT& input, - const int subset_num) const -{ +PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData< + TargetT>::actual_add_multiplication_with_approximate_sub_Hessian_without_penalty(TargetT& output, const TargetT& input, + const int subset_num) const { { std::string explanation; - if (!input.has_same_characteristics(this->get_sensitivity(), - explanation)) - { - warning("PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData:\n" - "sensitivity and input for add_multiplication_with_approximate_Hessian_without_penalty\n" - "should have the same characteristics.\n%s", - explanation.c_str()); - return Succeeded::no; - } - } + if (!input.has_same_characteristics(this->get_sensitivity(), explanation)) { + warning("PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData:\n" + "sensitivity and input for add_multiplication_with_approximate_Hessian_without_penalty\n" + "should have the same characteristics.\n%s", + explanation.c_str()); + return Succeeded::no; + } + } #ifndef NDEBUG - info(boost::format("INPUT max: (%1% , %2%)") % input.construct_single_density(1).find_max() % input.construct_single_density(2).find_max()); -#endif //NDEBUG - DynamicDiscretisedDensity dyn_input=this->_dyn_image_template; - DynamicDiscretisedDensity dyn_output=this->_dyn_image_template; - this->_patlak_plot_sptr->get_dynamic_image_from_parametric_image(dyn_input,input) ; - - VectorWithOffset scale_factor(this->_patlak_plot_sptr->get_starting_frame(),this->_patlak_plot_sptr->get_time_frame_definitions().get_num_frames()); - for(unsigned int frame_num=this->_patlak_plot_sptr->get_starting_frame(); - frame_num<=this->_patlak_plot_sptr->get_time_frame_definitions().get_num_frames(); - ++frame_num) - { - assert(dyn_input[frame_num].find_max()==dyn_input[frame_num].find_min()); - if (dyn_input[frame_num].find_max()==dyn_input[frame_num].find_min() && dyn_input[frame_num].find_min()>0.F) - scale_factor[frame_num]=dyn_input[frame_num].find_max(); - else - error("The input image should be uniform even after multiplying with the Patlak Plot.\n"); - -/*! /note This is used to avoid higher values than these set in the precompute_denominator_of_conditioner_without_penalty() function. -/sa for more information see the recon_array_functions.cxx and the value of the max_quotient (originaly set to 10000.F) -*/ - dyn_input[frame_num]/=scale_factor[frame_num]; + info(boost::format("INPUT max: (%1% , %2%)") % input.construct_single_density(1).find_max() % + input.construct_single_density(2).find_max()); +#endif // NDEBUG + DynamicDiscretisedDensity dyn_input = this->_dyn_image_template; + DynamicDiscretisedDensity dyn_output = this->_dyn_image_template; + this->_patlak_plot_sptr->get_dynamic_image_from_parametric_image(dyn_input, input); + + VectorWithOffset scale_factor(this->_patlak_plot_sptr->get_starting_frame(), + this->_patlak_plot_sptr->get_time_frame_definitions().get_num_frames()); + for (unsigned int frame_num = this->_patlak_plot_sptr->get_starting_frame(); + frame_num <= this->_patlak_plot_sptr->get_time_frame_definitions().get_num_frames(); ++frame_num) { + assert(dyn_input[frame_num].find_max() == dyn_input[frame_num].find_min()); + if (dyn_input[frame_num].find_max() == dyn_input[frame_num].find_min() && dyn_input[frame_num].find_min() > 0.F) + scale_factor[frame_num] = dyn_input[frame_num].find_max(); + else + error("The input image should be uniform even after multiplying with the Patlak Plot.\n"); + + /*! /note This is used to avoid higher values than these set in the precompute_denominator_of_conditioner_without_penalty() + function. /sa for more information see the recon_array_functions.cxx and the value of the max_quotient (originaly set to + 10000.F) + */ + dyn_input[frame_num] /= scale_factor[frame_num]; #ifndef NDEBUG - info(boost::format("scale factor[%1%]: %2%") % frame_num % scale_factor[frame_num]); - info(boost::format("dyn_input[%1%] max after scale: %2%") % frame_num % dyn_input[frame_num].find_max()); -#endif //NDEBUG - this->_single_frame_obj_funcs[frame_num]. - add_multiplication_with_approximate_sub_Hessian_without_penalty(dyn_output[frame_num], - dyn_input[frame_num], - subset_num); + info(boost::format("scale factor[%1%]: %2%") % frame_num % scale_factor[frame_num]); + info(boost::format("dyn_input[%1%] max after scale: %2%") % frame_num % dyn_input[frame_num].find_max()); +#endif // NDEBUG + this->_single_frame_obj_funcs[frame_num].add_multiplication_with_approximate_sub_Hessian_without_penalty( + dyn_output[frame_num], dyn_input[frame_num], subset_num); #ifndef NDEBUG - info(boost::format("dyn_output[%1%] max before scale: %2%") % frame_num % dyn_output[frame_num].find_max()); -#endif //NDEBUG - dyn_output[frame_num]*=scale_factor[frame_num]; + info(boost::format("dyn_output[%1%] max before scale: %2%") % frame_num % dyn_output[frame_num].find_max()); +#endif // NDEBUG + dyn_output[frame_num] *= scale_factor[frame_num]; #ifndef NDEBUG - info(boost::format("dyn_output[%1%] max after scale: %2%") % frame_num % dyn_output[frame_num].find_max()); -#endif //NDEBUG - } // end of loop over frames + info(boost::format("dyn_output[%1%] max after scale: %2%") % frame_num % dyn_output[frame_num].find_max()); +#endif // NDEBUG + } // end of loop over frames shared_ptr unnormalised_temp(output.get_empty_copy()); shared_ptr temp(output.get_empty_copy()); - this->_patlak_plot_sptr->multiply_dynamic_image_with_model_gradient(*unnormalised_temp, - dyn_output) ; - // Trick to use a better step size for the two parameters. - (this->_patlak_plot_sptr->get_model_matrix()).normalise_parametric_image_with_model_sum(*temp,*unnormalised_temp) ; + this->_patlak_plot_sptr->multiply_dynamic_image_with_model_gradient(*unnormalised_temp, dyn_output); + // Trick to use a better step size for the two parameters. + (this->_patlak_plot_sptr->get_model_matrix()).normalise_parametric_image_with_model_sum(*temp, *unnormalised_temp); #ifndef NDEBUG - info(boost::format("TEMP max: (%1% , %2%)") % temp->construct_single_density(1).find_max() % temp->construct_single_density(2).find_max()); + info(boost::format("TEMP max: (%1% , %2%)") % temp->construct_single_density(1).find_max() % + temp->construct_single_density(2).find_max()); // Writing images write_to_file("all_params_one_input.img", input); write_to_file("temp_denominator.img", *temp); dyn_input.write_to_ecat7("dynamic_input_from_all_params_one.img"); dyn_output.write_to_ecat7("dynamic_precomputed_denominator.img"); DynamicProjData temp_projdata = this->get_dyn_proj_data(); - for(unsigned int frame_num=this->_patlak_plot_sptr->get_starting_frame(); - frame_num<=this->_patlak_plot_sptr->get_time_frame_definitions().get_num_frames(); - ++frame_num) - temp_projdata.set_proj_data_sptr(this->_single_frame_obj_funcs[frame_num].get_proj_data_sptr(),frame_num); - + for (unsigned int frame_num = this->_patlak_plot_sptr->get_starting_frame(); + frame_num <= this->_patlak_plot_sptr->get_time_frame_definitions().get_num_frames(); ++frame_num) + temp_projdata.set_proj_data_sptr(this->_single_frame_obj_funcs[frame_num].get_proj_data_sptr(), frame_num); + temp_projdata.write_to_ecat7("DynamicProjections.S"); #endif // NDEBUG // output += temp typename TargetT::full_iterator out_iter = output.begin_all(); typename TargetT::full_iterator out_end = output.end_all(); typename TargetT::const_full_iterator temp_iter = temp->begin_all_const(); - while (out_iter != out_end) - { - *out_iter += *temp_iter; - ++out_iter; ++temp_iter; - } + while (out_iter != out_end) { + *out_iter += *temp_iter; + ++out_iter; + ++temp_iter; + } #ifndef NDEBUG - info(boost::format("OUTPUT max: (%1% , %2%)") % output.construct_single_density(1).find_max() % output.construct_single_density(2).find_max()); + info(boost::format("OUTPUT max: (%1% , %2%)") % output.construct_single_density(1).find_max() % + output.construct_single_density(2).find_max()); #endif // NDEBUG - return Succeeded::yes; } -template +template Succeeded -PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData:: -actual_accumulate_sub_Hessian_times_input_without_penalty(TargetT& output, - const TargetT& current_image_estimate, - const TargetT& input, - const int subset_num) const -{ +PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData< + TargetT>::actual_accumulate_sub_Hessian_times_input_without_penalty(TargetT& output, const TargetT& current_image_estimate, + const TargetT& input, const int subset_num) const { { // check argument characteristics std::string explanation; - if (!input.has_same_characteristics(this->get_sensitivity(), explanation)) - { + if (!input.has_same_characteristics(this->get_sensitivity(), explanation)) { warning("PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData:\n" "sensitivity and input for actual_accumulate_sub_Hessian_times_input_without_penalty\n" "should have the same characteristics.\n%s", @@ -633,74 +574,65 @@ actual_accumulate_sub_Hessian_times_input_without_penalty(TargetT& output, return Succeeded::no; } - if (!current_image_estimate.has_same_characteristics(this->get_sensitivity(), explanation)) - { + if (!current_image_estimate.has_same_characteristics(this->get_sensitivity(), explanation)) { warning("PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData:\n" "sensitivity and current_image_estimate for actual_accumulate_sub_Hessian_times_input_without_penalty\n" "should have the same characteristics.\n%s", explanation.c_str()); return Succeeded::no; } - } #ifndef NDEBUG - info(boost::format("INPUT max: (%1% , %2%)") % input.construct_single_density(1).find_max() % input.construct_single_density(2).find_max()); -#endif //NDEBUG - DynamicDiscretisedDensity dyn_input=this->_dyn_image_template; - DynamicDiscretisedDensity dyn_current_image_estimate=this->_dyn_image_template; - DynamicDiscretisedDensity dyn_output=this->_dyn_image_template; + info(boost::format("INPUT max: (%1% , %2%)") % input.construct_single_density(1).find_max() % + input.construct_single_density(2).find_max()); +#endif // NDEBUG + DynamicDiscretisedDensity dyn_input = this->_dyn_image_template; + DynamicDiscretisedDensity dyn_current_image_estimate = this->_dyn_image_template; + DynamicDiscretisedDensity dyn_output = this->_dyn_image_template; this->_patlak_plot_sptr->get_dynamic_image_from_parametric_image(dyn_input, input); this->_patlak_plot_sptr->get_dynamic_image_from_parametric_image(dyn_current_image_estimate, current_image_estimate); - for(unsigned int frame_num=this->_patlak_plot_sptr->get_starting_frame(); - frame_num<=this->_patlak_plot_sptr->get_time_frame_definitions().get_num_frames(); - ++frame_num) - { - assert(dyn_input[frame_num].find_max()==dyn_input[frame_num].find_min()); - this->_single_frame_obj_funcs[frame_num]. - accumulate_sub_Hessian_times_input_without_penalty(dyn_output[frame_num], - dyn_current_image_estimate[frame_num], - dyn_input[frame_num], - subset_num); + for (unsigned int frame_num = this->_patlak_plot_sptr->get_starting_frame(); + frame_num <= this->_patlak_plot_sptr->get_time_frame_definitions().get_num_frames(); ++frame_num) { + assert(dyn_input[frame_num].find_max() == dyn_input[frame_num].find_min()); + this->_single_frame_obj_funcs[frame_num].accumulate_sub_Hessian_times_input_without_penalty( + dyn_output[frame_num], dyn_current_image_estimate[frame_num], dyn_input[frame_num], subset_num); } // end of loop over frames shared_ptr unnormalised_temp(output.get_empty_copy()); shared_ptr temp(output.get_empty_copy()); - this->_patlak_plot_sptr->multiply_dynamic_image_with_model_gradient(*unnormalised_temp, - dyn_output) ; + this->_patlak_plot_sptr->multiply_dynamic_image_with_model_gradient(*unnormalised_temp, dyn_output); // Trick to use a better step size for the two parameters. - (this->_patlak_plot_sptr->get_model_matrix()).normalise_parametric_image_with_model_sum(*temp,*unnormalised_temp) ; + (this->_patlak_plot_sptr->get_model_matrix()).normalise_parametric_image_with_model_sum(*temp, *unnormalised_temp); #ifndef NDEBUG - info(boost::format("TEMP max: (%1% , %2%)") % temp->construct_single_density(1).find_max() % temp->construct_single_density(2).find_max()); -// Writing images -write_to_file("all_params_one_input.img", input); -write_to_file("temp_denominator.img", *temp); -dyn_input.write_to_ecat7("dynamic_input_from_all_params_one.img"); -dyn_output.write_to_ecat7("dynamic_precomputed_denominator.img"); -DynamicProjData temp_projdata = this->get_dyn_proj_data(); -for(unsigned int frame_num=this->_patlak_plot_sptr->get_starting_frame(); - frame_num<=this->_patlak_plot_sptr->get_time_frame_definitions().get_num_frames(); - ++frame_num) -temp_projdata.set_proj_data_sptr(this->_single_frame_obj_funcs[frame_num].get_proj_data_sptr(),frame_num); - -temp_projdata.write_to_ecat7("DynamicProjections.S"); + info(boost::format("TEMP max: (%1% , %2%)") % temp->construct_single_density(1).find_max() % + temp->construct_single_density(2).find_max()); + // Writing images + write_to_file("all_params_one_input.img", input); + write_to_file("temp_denominator.img", *temp); + dyn_input.write_to_ecat7("dynamic_input_from_all_params_one.img"); + dyn_output.write_to_ecat7("dynamic_precomputed_denominator.img"); + DynamicProjData temp_projdata = this->get_dyn_proj_data(); + for (unsigned int frame_num = this->_patlak_plot_sptr->get_starting_frame(); + frame_num <= this->_patlak_plot_sptr->get_time_frame_definitions().get_num_frames(); ++frame_num) + temp_projdata.set_proj_data_sptr(this->_single_frame_obj_funcs[frame_num].get_proj_data_sptr(), frame_num); + + temp_projdata.write_to_ecat7("DynamicProjections.S"); #endif // NDEBUG // output += temp typename TargetT::full_iterator out_iter = output.begin_all(); typename TargetT::full_iterator out_end = output.end_all(); typename TargetT::const_full_iterator temp_iter = temp->begin_all_const(); - while (out_iter != out_end) - { + while (out_iter != out_end) { *out_iter += *temp_iter; - ++out_iter; ++temp_iter; + ++out_iter; + ++temp_iter; } #ifndef NDEBUG - info(boost::format("OUTPUT max: (%1% , %2%)") % output.construct_single_density(1).find_max() % output.construct_single_density(2).find_max()); + info(boost::format("OUTPUT max: (%1% , %2%)") % output.construct_single_density(1).find_max() % + output.construct_single_density(2).find_max()); #endif // NDEBUG - return Succeeded::yes; } END_NAMESPACE_STIR - - diff --git a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMean.h b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMean.h index a42e2ba0df..16ffcb0128 100644 --- a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMean.h +++ b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMean.h @@ -30,24 +30,23 @@ START_NAMESPACE_STIR - /*! \ingroup GeneralisedObjectiveFunction - \brief a base class for LogLikelihood of independent Poisson variables + \brief a base class for LogLikelihood of independent Poisson variables where the mean values are linear combinations of the parameters. Suppose the measured data \f$y\f$ are statistically independent and - Poisson distributed with mean \f$\bar y\f$. The log likelihood is then + Poisson distributed with mean \f$\bar y\f$. The log likelihood is then given by \f[ L(y\;\bar y) = \sum_b y_b log \bar y_b - \mathrm{log} y_b! - \bar y_b \f] - + If the means are modeled by some functions of some parameters - \f$Y(\lambda)\f$, the gradient w.r.t. those parameters follows + \f$Y(\lambda)\f$, the gradient w.r.t. those parameters follows immediately as \f[ {\partial L \over \partial \lambda_v} = - \sum_b \left( {y_b \over Y_b} - 1 \right) + \sum_b \left( {y_b \over Y_b} - 1 \right) {\partial Y_b \over \partial \lambda_v } \f] @@ -65,8 +64,8 @@ START_NAMESPACE_STIR \f[ {\partial L \over \partial \lambda_v} = \sum_b P_{bv} {y_b \over Y_b} - P_v \f] - where - \f[ + where + \f[ P_v = \sum_b P_{bv} \f] where the sum is over all possible bins (not only those where @@ -75,14 +74,14 @@ START_NAMESPACE_STIR probability of detecting a count (in any bin) originating from \f$v\f$. This class computes the gradient as a sum of these two terms. The - sensitivity has to be computed by the virtual function + sensitivity has to be computed by the virtual function \c add_subset_sensitivity(). The sum is computed by \c compute_sub_gradient_without_penalty_plus_sensitivity(). The reason for this is that the sensitivity is data-independent, and can be computed only once. See also PoissonLogLikelihoodWithLinearModelForMeanAndListModeData. - + \par Relation with Kullback-Leibler distance Note that up to terms independent of \f$\bar y\f$, the log likelihood @@ -91,14 +90,14 @@ START_NAMESPACE_STIR \f[ \mathrm{KL}(y,\bar y) = \sum_b y_b \mathrm{log}(y_b/\bar y_b) + \bar y_b - y_b \f] - which has the nice property that \f$\mathrm{KL}(y,\bar y)=0\f$ implies that + which has the nice property that \f$\mathrm{KL}(y,\bar y)=0\f$ implies that \f$y=\bar y\f$. \par Parameters for parsing Defaults are indicated below \verbatim - ; specifies if we keep separate sensitivity images (which is more accurate and is - ; recommended) or if we assume the subsets are exactly balanced + ; specifies if we keep separate sensitivity images (which is more accurate and is + ; recommended) or if we assume the subsets are exactly balanced ; and hence compute the subset-senstivity as sensitivity/num_subsets (this uses less memory). use_subset_sensitivities := 1 ; for recomputing sensitivity, even if a filename is specified @@ -114,25 +113,22 @@ START_NAMESPACE_STIR \endverbatim \par Terminology - We currently use \c sub_gradient for the gradient of the likelihood of the subset (not + We currently use \c sub_gradient for the gradient of the likelihood of the subset (not the mathematical subgradient). */ template -class PoissonLogLikelihoodWithLinearModelForMean: -public GeneralisedObjectiveFunction -{ - private: +class PoissonLogLikelihoodWithLinearModelForMean : public GeneralisedObjectiveFunction { +private: typedef GeneralisedObjectiveFunction base_type; - public: - - //PoissonLogLikelihoodWithLinearModelForMean(); +public: + // PoissonLogLikelihoodWithLinearModelForMean(); //! Implementation in terms of compute_sub_gradient_without_penalty_plus_sensitivity() - /*! \warning If separate subsensitivities are not used, we just subtract the total + /*! \warning If separate subsensitivities are not used, we just subtract the total sensitivity divided by the number of subsets. - This is fine for some algorithms as the sum over all the subsets is - equal to gradient of the objective function (without prior). + This is fine for some algorithms as the sum over all the subsets is + equal to gradient of the objective function (without prior). Other algorithms do not behave very stable under this approximation however. So, currently setup() will return an error if !subsets_are_approximately_balanced() and subset sensitivities @@ -140,13 +136,10 @@ public GeneralisedObjectiveFunction \see get_use_subset_sensitivities() */ - virtual void - compute_sub_gradient_without_penalty(TargetT& gradient, - const TargetT ¤t_estimate, - const int subset_num); + virtual void compute_sub_gradient_without_penalty(TargetT& gradient, const TargetT& current_estimate, const int subset_num); //! This should compute the gradient of the (unregularised) objective function plus the (sub)sensitivity - /*! + /*! This function is used for instance by OSMAPOSL. This computes @@ -156,14 +149,12 @@ public GeneralisedObjectiveFunction (see the class general documentation). The sum will however be restricted to a subset. */ - virtual void - compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, - const TargetT ¤t_estimate, - const int subset_num) =0; + virtual void compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, const TargetT& current_estimate, + const int subset_num) = 0; //! set-up sensitivity etc if possible /*! If \c recompute_sensitivity is \c false, we will try to - read it from either \c subset_sensitivity_filenames or \c sensitivity_filename, + read it from either \c subset_sensitivity_filenames or \c sensitivity_filename, depending on the setting of get_use_subset_sensitivities(). If \c sensitivity_filename is equal @@ -174,7 +165,7 @@ public GeneralisedObjectiveFunction Calls set_up_before_sensitivity(). */ - virtual Succeeded set_up(shared_ptr const& target_sptr); + virtual Succeeded set_up(shared_ptr const& target_sptr); //! Get a const reference to the total sensitivity const TargetT& get_sensitivity() const; @@ -182,12 +173,11 @@ public GeneralisedObjectiveFunction const TargetT& get_subset_sensitivity(const int subset_num) const; //! Add subset sensitivity to existing data - virtual void - add_subset_sensitivity(TargetT& sensitivity, const int subset_num) const = 0; + virtual void add_subset_sensitivity(TargetT& sensitivity, const int subset_num) const = 0; //! find out if subset_sensitivities are used /*! If \c true, the sub_gradient and subset_sensitivity functions use the sensitivity - for the given subset, otherwise, we use the total sensitivity divided by the number + for the given subset, otherwise, we use the total sensitivity divided by the number of subsets. The latter uses less memory, but is less stable for most (all?) algorithms. */ bool get_use_subset_sensitivities() const; @@ -208,7 +198,7 @@ public GeneralisedObjectiveFunction /*! \name Functions to set parameters This can be used as alternative to the parsing mechanism. \warning After using any of these, you have to call set_up(). - \warning Be careful with setting shared pointers. If you modify the objects in + \warning Be careful with setting shared pointers. If you modify the objects in one place, all objects that use the shared pointer will be affected. */ //@{ @@ -234,24 +224,21 @@ public GeneralisedObjectiveFunction /*! The implementation checks if the sensitivity of a voxel is zero. If so, it will the target voxel will be assigned the desired value. */ - void - fill_nonidentifiable_target_parameters(TargetT& target, const float value ) const; - - private: + void fill_nonidentifiable_target_parameters(TargetT& target, const float value) const; +private: std::string sensitivity_filename; std::string subsensitivity_filenames; bool recompute_sensitivity; bool use_subset_sensitivities; - VectorWithOffset > subsensitivity_sptrs; + VectorWithOffset> subsensitivity_sptrs; shared_ptr sensitivity_sptr; //! Get the subset sensitivity sptr - shared_ptr - get_subset_sensitivity_sptr(const int subset_num) const; + shared_ptr get_subset_sensitivity_sptr(const int subset_num) const; //! compute total from subsensitivity or vice versa - /*! This function will be called by set_up() after reading new images, and/or + /*! This function will be called by set_up() after reading new images, and/or by compute_sensitivities(). if get_use_subset_sensitivities() is true, the total sensitivity is computed @@ -261,25 +248,23 @@ public GeneralisedObjectiveFunction void set_total_or_subset_sensitivities(); protected: - //! set-up specifics for the derived class - virtual Succeeded - set_up_before_sensitivity(shared_ptr const& target_sptr) = 0; + //! set-up specifics for the derived class + virtual Succeeded set_up_before_sensitivity(shared_ptr const& target_sptr) = 0; //! compute subset and total sensitivity /*! This function fills in the sensitivity data by calling add_subset_sensitivity() - for all subsets. It assumes that the subsensitivity for the 1st subset has been + for all subsets. It assumes that the subsensitivity for the 1st subset has been allocated already (and is the correct size). */ void compute_sensitivities(); - //! Sets defaults for parsing + //! Sets defaults for parsing /*! Resets \c sensitivity_filename, \c subset_sensitivity_filenames to empty, \c recompute_sensitivity to \c false, and \c use_subset_sensitivities to false. */ virtual void set_defaults(); virtual void initialise_keymap(); virtual bool post_processing(); - }; END_NAMESPACE_STIR diff --git a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion.h b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion.h index 0df1484168..3066284043 100644 --- a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion.h +++ b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion.h @@ -7,12 +7,12 @@ it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. - + This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - + See STIR/LICENSE.txt for details */ /*! @@ -43,40 +43,37 @@ START_NAMESPACE_STIR /*! \ingroup GeneralisedObjectiveFunction - \brief a base class for LogLikelihood of independent Poisson variables + \brief a base class for LogLikelihood of independent Poisson variables where the mean values are linear combinations of the gated images. \f[ \begin{array}{lcl} - \Lambda_{\nu}^{(s+1)}&&=\Lambda_{\nu}^{(s)} \frac{1}{ \sum\limits_{b\in S_{l}, g} \sum\limits_{\nu'} \hat{W}^{-1} _{\nu'g\rightarrow \nu}P_{\nu' b}A_{bg}+\beta \nabla_{\Lambda_{\nu}} E_{\nu}^{(s)}}\\ - &&\times \sum\limits_{b\in S_{l}, g} \sum\limits_{\nu'}\left(\hat{W}^{-1} _{\nu'g\rightarrow \nu}P_{\nu' b}\frac{Y_{bg}}{\sum\limits_{\tilde{\nu}}P_{b\tilde{\nu}}\sum\limits_{\tilde{\nu}'}\hat{W} _{\tilde{\nu}'\rightarrow \tilde{\nu}g}\Lambda_{\tilde{\nu}'}^{(s)}+\frac{B_{bg}}{A_{bg}}}\right) - \end{array} -\f] - \par Parameters for parsing - + \Lambda_{\nu}^{(s+1)}&&=\Lambda_{\nu}^{(s)} \frac{1}{ \sum\limits_{b\in S_{l}, g} \sum\limits_{\nu'} \hat{W}^{-1} +_{\nu'g\rightarrow \nu}P_{\nu' b}A_{bg}+\beta \nabla_{\Lambda_{\nu}} E_{\nu}^{(s)}}\\ + &&\times \sum\limits_{b\in S_{l}, g} \sum\limits_{\nu'}\left(\hat{W}^{-1} _{\nu'g\rightarrow \nu}P_{\nu' +b}\frac{Y_{bg}}{\sum\limits_{\tilde{\nu}}P_{b\tilde{\nu}}\sum\limits_{\tilde{\nu}'}\hat{W} _{\tilde{\nu}'\rightarrow +\tilde{\nu}g}\Lambda_{\tilde{\nu}'}^{(s)}+\frac{B_{bg}}{A_{bg}}}\right) \end{array} \f] \par Parameters for parsing + For more information: Tsoumpas et al (2013) Physics in Medicine and Biology */ template -class PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion: -public RegisteredParsingObject, - GeneralisedObjectiveFunction, - PoissonLogLikelihoodWithLinearModelForMean > -{ - private: - typedef RegisteredParsingObject, - GeneralisedObjectiveFunction, - PoissonLogLikelihoodWithLinearModelForMean > base_type; - typedef PoissonLogLikelihoodWithLinearModelForMeanAndProjData > SingleGateObjFunc ; +class PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion + : public RegisteredParsingObject, + GeneralisedObjectiveFunction, PoissonLogLikelihoodWithLinearModelForMean> { +private: + typedef RegisteredParsingObject, + GeneralisedObjectiveFunction, PoissonLogLikelihoodWithLinearModelForMean> + base_type; + typedef PoissonLogLikelihoodWithLinearModelForMeanAndProjData> SingleGateObjFunc; VectorWithOffset _single_gate_obj_funcs; TimeGateDefinitions _time_gate_definitions; - public: - +public: //! Name which will be used when parsing a GeneralisedObjectiveFunction object - static const char * const registered_name; + static const char* const registered_name; PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion(); @@ -84,38 +81,28 @@ public RegisteredParsingObject const& target_sptr); + virtual Succeeded set_up_before_sensitivity(shared_ptr const& target_sptr); //! Add subset sensitivity to existing data - virtual void - add_subset_sensitivity(TargetT& sensitivity, const int subset_num) const; + virtual void add_subset_sensitivity(TargetT& sensitivity, const int subset_num) const; - virtual Succeeded - actual_add_multiplication_with_approximate_sub_Hessian_without_penalty(TargetT& output, - const TargetT& input, - const int subset_num) const; - virtual Succeeded - actual_accumulate_sub_Hessian_times_input_without_penalty(TargetT &output, - const TargetT ¤t_image_estimate, - const TargetT &input, - const int subset_num) const; + virtual Succeeded actual_add_multiplication_with_approximate_sub_Hessian_without_penalty(TargetT& output, const TargetT& input, + const int subset_num) const; + virtual Succeeded actual_accumulate_sub_Hessian_times_input_without_penalty(TargetT& output, + const TargetT& current_image_estimate, + const TargetT& input, const int subset_num) const; - void set_time_gate_definitions(const TimeGateDefinitions & time_gate_definitions); + void set_time_gate_definitions(const TimeGateDefinitions& time_gate_definitions); /*! \name Functions to get parameters - \warning Be careful with changing shared pointers. If you modify the objects in + \warning Be careful with changing shared pointers. If you modify the objects in one place, all objects that use the shared pointer will be affected. */ //@{ @@ -134,7 +121,7 @@ public RegisteredParsingObject&); virtual void set_additive_proj_data_sptr(const shared_ptr&); - virtual void set_input_data(const shared_ptr &); + virtual void set_input_data(const shared_ptr&); virtual const GatedProjData& get_input_data() const; //@} - protected: +protected: //! Filename with input projection data std::string _input_filename; std::string _motion_vectors_filename_prefix; @@ -162,8 +149,7 @@ public RegisteredParsingObject target_parameter_parser; @@ -185,7 +171,8 @@ public RegisteredParsingObject -#include +#include // For Motion #include "stir/spatial_transformation/GatedSpatialTransformation.h" #include "stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion.h" @@ -54,65 +54,53 @@ START_NAMESPACE_STIR -template -const char * const -PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion:: -registered_name = -"PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion"; +template +const char* const PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion::registered_name = + "PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion"; -template +template void -PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion:: -set_defaults() -{ +PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion::set_defaults() { base_type::set_defaults(); - this->_input_filename=""; - this->_max_segment_num_to_process=-1; // use all segments - //num_views_to_add=1; // KT 20/06/2001 disabled + this->_input_filename = ""; + this->_max_segment_num_to_process = -1; // use all segments + // num_views_to_add=1; // KT 20/06/2001 disabled - this->_gated_proj_data_sptr.reset(); + this->_gated_proj_data_sptr.reset(); this->_zero_seg0_end_planes = 0; - this->_reverse_motion_vectors_filename_prefix="0"; - this->_normalisation_gated_proj_data_filename="1"; + this->_reverse_motion_vectors_filename_prefix = "0"; + this->_normalisation_gated_proj_data_filename = "1"; this->_normalisation_gated_proj_data_sptr.reset(); // this->_reverse_motion_vectors_sptr=NULL; - this->_motion_vectors_filename_prefix="0"; + this->_motion_vectors_filename_prefix = "0"; // this->_motion_vectors_sptr=NULL; - this->_gate_definitions_filename="0"; + this->_gate_definitions_filename = "0"; // this->_time_gate_definitions_sptr=NULL; this->_additive_gated_proj_data_filename = "0"; this->_additive_gated_proj_data_sptr.reset(); #ifndef USE_PMRT // set default for _projector_pair_ptr - shared_ptr forward_projector_ptr - (new ForwardProjectorByBinUsingRayTracing()); - shared_ptr back_projector_ptr - (new BackProjectorByBinUsingInterpolation()); + shared_ptr forward_projector_ptr(new ForwardProjectorByBinUsingRayTracing()); + shared_ptr back_projector_ptr(new BackProjectorByBinUsingInterpolation()); #else - shared_ptr PM - (new ProjMatrixByBinUsingRayTracing()); - shared_ptr forward_projector_ptr - (new ForwardProjectorByBinUsingProjMatrixByBin(PM)); - shared_ptr back_projector_ptr - (new BackProjectorByBinUsingProjMatrixByBin(PM)); + shared_ptr PM(new ProjMatrixByBinUsingRayTracing()); + shared_ptr forward_projector_ptr(new ForwardProjectorByBinUsingProjMatrixByBin(PM)); + shared_ptr back_projector_ptr(new BackProjectorByBinUsingProjMatrixByBin(PM)); #endif - this->_projector_pair_ptr.reset( - new ProjectorByBinPairUsingSeparateProjectors(forward_projector_ptr, back_projector_ptr)); + this->_projector_pair_ptr.reset(new ProjectorByBinPairUsingSeparateProjectors(forward_projector_ptr, back_projector_ptr)); this->target_parameter_parser.set_defaults(); } -template +template void -PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion:: -initialise_keymap() -{ +PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion::initialise_keymap() { base_type::initialise_keymap(); this->parser.add_start_key("PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion Parameters"); this->parser.add_stop_key("End PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion Parameters"); - this->parser.add_key("input filename",&this->_input_filename); + this->parser.add_key("input filename", &this->_input_filename); // parser.add_key("mash x views", &num_views_to_add); // KT 20/06/2001 disabled this->parser.add_key("maximum absolute segment number to process", &this->_max_segment_num_to_process); @@ -122,44 +110,40 @@ initialise_keymap() this->parser.add_parsing_key("Projector pair type", &this->_projector_pair_ptr); // Scatter correction - this->parser.add_key("additive sinograms",&this->_additive_gated_proj_data_filename); + this->parser.add_key("additive sinograms", &this->_additive_gated_proj_data_filename); // normalisation (and attenuation correction) this->parser.add_key("normalisation sinograms", &this->_normalisation_gated_proj_data_filename); - + // Motion Information this->parser.add_key("Gate Definitions filename", &this->_gate_definitions_filename); this->parser.add_key("Motion Vectors filename prefix", &this->_motion_vectors_filename_prefix); - this->parser.add_key("Reverse Motion Vectors filename prefix", &this->_reverse_motion_vectors_filename_prefix); + this->parser.add_key("Reverse Motion Vectors filename prefix", &this->_reverse_motion_vectors_filename_prefix); } -template +template bool -PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion:: -post_processing() -{ +PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion::post_processing() { if (base_type::post_processing() == true) return true; - if (this->_input_filename.length() == 0) - { warning("You need to specify an input filename"); return true; } - + if (this->_input_filename.length() == 0) { + warning("You need to specify an input filename"); + return true; + } + this->_gated_proj_data_sptr = GatedProjData::read_from_file(this->_input_filename); - + // image stuff this->target_parameter_parser.check_values(); - if (this->_additive_gated_proj_data_filename != "0") - { - info(boost::format("Reading additive projdata data %1%") % this->_additive_gated_proj_data_filename); - this->_additive_gated_proj_data_sptr = - GatedProjData::read_from_file(this->_additive_gated_proj_data_filename); - } - if (this->_normalisation_gated_proj_data_filename != "1") - { - info(boost::format("Reading normalisation projdata data %1%") % this->_normalisation_gated_proj_data_filename); - this->_normalisation_gated_proj_data_sptr = - GatedProjData::read_from_file(this->_normalisation_gated_proj_data_filename); - } + if (this->_additive_gated_proj_data_filename != "0") { + info(boost::format("Reading additive projdata data %1%") % this->_additive_gated_proj_data_filename); + this->_additive_gated_proj_data_sptr = GatedProjData::read_from_file(this->_additive_gated_proj_data_filename); + } + if (this->_normalisation_gated_proj_data_filename != "1") { + info(boost::format("Reading normalisation projdata data %1%") % this->_normalisation_gated_proj_data_filename); + this->_normalisation_gated_proj_data_sptr = GatedProjData::read_from_file(this->_normalisation_gated_proj_data_filename); + } this->_time_gate_definitions.read_gdef_file(this->_gate_definitions_filename); @@ -171,248 +155,226 @@ post_processing() } template -PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion:: -PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion() -{ +PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion< + TargetT>::PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion() { this->set_defaults(); } template -TargetT * -PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion:: -construct_target_ptr() const -{ - return - this->target_parameter_parser.create(this->get_input_data()); +TargetT* +PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion::construct_target_ptr() const { + return this->target_parameter_parser.create(this->get_input_data()); } /*************************************************************** subset balancing ***************************************************************/ -template +template bool -PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion:: -actual_subsets_are_approximately_balanced(std::string& warning_message) const -{ // call actual_subsets_are_approximately_balanced() for first single_gate_obj_func +PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion::actual_subsets_are_approximately_balanced( + std::string& warning_message) const { // call actual_subsets_are_approximately_balanced() for first single_gate_obj_func if (this->get_time_gate_definitions().get_num_gates() == 0 || this->_single_gate_obj_funcs.size() == 0) error("PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion:\n" - "actual_subsets_are_approximately_balanced called but no gates yet."); - else if(this->_single_gate_obj_funcs.size() != 0) - { - bool gates_are_balanced=true; - for(unsigned int gate_num=1;gate_num<=this->get_time_gate_definitions().get_num_gates();++gate_num) - gates_are_balanced &= this->_single_gate_obj_funcs[gate_num].subsets_are_approximately_balanced(warning_message); - return gates_are_balanced; - } - else + "actual_subsets_are_approximately_balanced called but no gates yet."); + else if (this->_single_gate_obj_funcs.size() != 0) { + bool gates_are_balanced = true; + for (unsigned int gate_num = 1; gate_num <= this->get_time_gate_definitions().get_num_gates(); ++gate_num) + gates_are_balanced &= this->_single_gate_obj_funcs[gate_num].subsets_are_approximately_balanced(warning_message); + return gates_are_balanced; + } else error("Something strange happened in PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion:\n" - "actual_subsets_are_approximately_balanced called before setup()?"); - return - false; + "actual_subsets_are_approximately_balanced called before setup()?"); + return false; } /*************************************************************** get_ functions ***************************************************************/ template -const TimeGateDefinitions & -PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion:: -get_time_gate_definitions() const -{ return this->_time_gate_definitions; } +const TimeGateDefinitions& +PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion::get_time_gate_definitions() const { + return this->_time_gate_definitions; +} template -const GatedProjData& -PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion:: -get_gated_proj_data() const -{ return *this->_gated_proj_data_sptr; } +const GatedProjData& +PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion::get_gated_proj_data() const { + return *this->_gated_proj_data_sptr; +} template -const shared_ptr& -PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion:: -get_gated_proj_data_sptr() const -{ return this->_gated_proj_data_sptr; } +const shared_ptr& +PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion::get_gated_proj_data_sptr() const { + return this->_gated_proj_data_sptr; +} template -const int -PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion:: -get_max_segment_num_to_process() const -{ return this->_max_segment_num_to_process; } +const int +PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion::get_max_segment_num_to_process() const { + return this->_max_segment_num_to_process; +} template -const bool -PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion:: -get_zero_seg0_end_planes() const -{ return this->_zero_seg0_end_planes; } +const bool +PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion::get_zero_seg0_end_planes() const { + return this->_zero_seg0_end_planes; +} template -const GatedProjData& -PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion:: -get_additive_gated_proj_data() const -{ return *this->_additive_gated_proj_data_sptr; } +const GatedProjData& +PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion::get_additive_gated_proj_data() const { + return *this->_additive_gated_proj_data_sptr; +} template -const shared_ptr& -PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion:: -get_additive_gated_proj_data_sptr() const -{ return this->_additive_gated_proj_data_sptr; } +const shared_ptr& +PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion::get_additive_gated_proj_data_sptr() const { + return this->_additive_gated_proj_data_sptr; +} template -const GatedProjData& -PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion:: -get_normalisation_gated_proj_data() const -{ return *this->_normalisation_gated_proj_data_sptr; } +const GatedProjData& +PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion::get_normalisation_gated_proj_data() const { + return *this->_normalisation_gated_proj_data_sptr; +} template -const shared_ptr& -PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion:: -get_normalisation_gated_proj_data_sptr() const -{ return this->_normalisation_gated_proj_data_sptr; } +const shared_ptr& +PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion::get_normalisation_gated_proj_data_sptr() const { + return this->_normalisation_gated_proj_data_sptr; +} template -const ProjectorByBinPair& -PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion:: -get_projector_pair() const -{ return *this->_projector_pair_ptr; } +const ProjectorByBinPair& +PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion::get_projector_pair() const { + return *this->_projector_pair_ptr; +} template -const shared_ptr& -PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion:: -get_projector_pair_sptr() const -{ return this->_projector_pair_ptr; } +const shared_ptr& +PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion::get_projector_pair_sptr() const { + return this->_projector_pair_ptr; +} /*************************************************************** set_ functions ***************************************************************/ -template +template int -PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion:: -set_num_subsets(const int num_subsets) -{ - for(unsigned int gate_num=1;gate_num<=this->get_time_gate_definitions().get_num_gates();++gate_num) - { - if(this->_single_gate_obj_funcs.size() != 0) - if(this->_single_gate_obj_funcs[gate_num].set_num_subsets(num_subsets) != num_subsets) - error("set_num_subsets didn't work"); - } - this->num_subsets=num_subsets; +PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion::set_num_subsets(const int num_subsets) { + for (unsigned int gate_num = 1; gate_num <= this->get_time_gate_definitions().get_num_gates(); ++gate_num) { + if (this->_single_gate_obj_funcs.size() != 0) + if (this->_single_gate_obj_funcs[gate_num].set_num_subsets(num_subsets) != num_subsets) + error("set_num_subsets didn't work"); + } + this->num_subsets = num_subsets; return this->num_subsets; } -template +template void -PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion:: -set_time_gate_definitions(const TimeGateDefinitions & time_gate_definitions) -{ this->_time_gate_definitions=time_gate_definitions; } +PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion::set_time_gate_definitions( + const TimeGateDefinitions& time_gate_definitions) { + this->_time_gate_definitions = time_gate_definitions; +} -template +template void -PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion:: -set_input_data(const shared_ptr & arg) -{ - this->_gated_proj_data_sptr = dynamic_pointer_cast(arg); +PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion::set_input_data(const shared_ptr& arg) { + this->_gated_proj_data_sptr = dynamic_pointer_cast(arg); } -template +template const GatedProjData& -PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion:: -get_input_data() const -{ +PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion::get_input_data() const { return *this->_gated_proj_data_sptr; } -template +template void -PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion:: -set_additive_proj_data_sptr(const shared_ptr &arg) -{ - this->_additive_gated_proj_data_sptr = dynamic_pointer_cast(arg); +PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion::set_additive_proj_data_sptr( + const shared_ptr& arg) { + this->_additive_gated_proj_data_sptr = dynamic_pointer_cast(arg); } -template +template void -PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion:: -set_normalisation_sptr(const shared_ptr& arg) -{ -// this->normalisation_sptr = arg; - error("Not implemeted yet"); +PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion::set_normalisation_sptr( + const shared_ptr& arg) { + // this->normalisation_sptr = arg; + error("Not implemeted yet"); } /*************************************************************** set_up() ***************************************************************/ -template -Succeeded -PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion:: -set_up_before_sensitivity(shared_ptr const& target_sptr) -{ - /*!todo define in the PoissonLogLikelihoodWithLinearModelForMean class to return Succeeded::yes +template +Succeeded +PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion::set_up_before_sensitivity( + shared_ptr const& target_sptr) { + /*!todo define in the PoissonLogLikelihoodWithLinearModelForMean class to return Succeeded::yes if (base_type::set_up_before_sensitivity(target_sptr) != Succeeded::yes) return Succeeded::no; */ - if (this->_max_segment_num_to_process==-1) - this->_max_segment_num_to_process = - (this->_gated_proj_data_sptr)->get_proj_data_sptr(1)->get_max_segment_num(); - - if (this->_max_segment_num_to_process > (this->_gated_proj_data_sptr)->get_proj_data_sptr(1)->get_max_segment_num()) - { - warning("_max_segment_num_to_process (%d) is too large", - this->_max_segment_num_to_process); - return Succeeded::no; - } + if (this->_max_segment_num_to_process == -1) + this->_max_segment_num_to_process = (this->_gated_proj_data_sptr)->get_proj_data_sptr(1)->get_max_segment_num(); + + if (this->_max_segment_num_to_process > (this->_gated_proj_data_sptr)->get_proj_data_sptr(1)->get_max_segment_num()) { + warning("_max_segment_num_to_process (%d) is too large", this->_max_segment_num_to_process); + return Succeeded::no; + } shared_ptr proj_data_info_sptr( - (this->_gated_proj_data_sptr->get_proj_data_sptr(1))->get_proj_data_info_sptr()->clone()); - proj_data_info_sptr-> - reduce_segment_range(-this->_max_segment_num_to_process, - +this->_max_segment_num_to_process); - - if (is_null_ptr(this->_projector_pair_ptr)) - { warning("You need to specify a projector pair"); return Succeeded::no; } - - if (this->num_subsets <= 0) - { - warning("Number of subsets %d should be larger than 0.", - this->num_subsets); - return Succeeded::no; - } + (this->_gated_proj_data_sptr->get_proj_data_sptr(1))->get_proj_data_info_sptr()->clone()); + proj_data_info_sptr->reduce_segment_range(-this->_max_segment_num_to_process, +this->_max_segment_num_to_process); + + if (is_null_ptr(this->_projector_pair_ptr)) { + warning("You need to specify a projector pair"); + return Succeeded::no; + } + + if (this->num_subsets <= 0) { + warning("Number of subsets %d should be larger than 0.", this->num_subsets); + return Succeeded::no; + } { - const shared_ptr > density_template_sptr(target_sptr->get_empty_copy()); // target_sptr appears not to be set up correctly + const shared_ptr> density_template_sptr( + target_sptr->get_empty_copy()); // target_sptr appears not to be set up correctly const shared_ptr scanner_sptr(new Scanner(*proj_data_info_sptr->get_scanner_ptr())); - this->_gated_image_template=GatedDiscretisedDensity(this->get_time_gate_definitions(), density_template_sptr); + this->_gated_image_template = GatedDiscretisedDensity(this->get_time_gate_definitions(), density_template_sptr); // construct _single_gate_obj_funcs - this->_single_gate_obj_funcs.resize(1,this->get_time_gate_definitions().get_num_gates()); - - for(unsigned int gate_num=1;gate_num<=this->get_time_gate_definitions().get_num_gates();++gate_num) - { - info(boost::format("Objective Function for Gate Number: %1%") % gate_num); - this->_single_gate_obj_funcs[gate_num].set_projector_pair_sptr(this->_projector_pair_ptr); - this->_single_gate_obj_funcs[gate_num].set_proj_data_sptr(this->_gated_proj_data_sptr->get_proj_data_sptr(gate_num)); - this->_single_gate_obj_funcs[gate_num].set_max_segment_num_to_process(this->_max_segment_num_to_process); - this->_single_gate_obj_funcs[gate_num].set_zero_seg0_end_planes(this->_zero_seg0_end_planes!=0); - if(!is_null_ptr(this->_additive_gated_proj_data_sptr)) - this->_single_gate_obj_funcs[gate_num].set_additive_proj_data_sptr(this->_additive_gated_proj_data_sptr->get_proj_data_sptr(gate_num)); - this->_single_gate_obj_funcs[gate_num].set_num_subsets(this->num_subsets); - this->_single_gate_obj_funcs[gate_num].set_frame_num(1);//This should be gate... - std::vector > frame_times(1, std::pair(0,1)); - this->_single_gate_obj_funcs[gate_num].set_frame_definitions(TimeFrameDefinitions(frame_times)); - - shared_ptr current_gate_norm_factors_sptr; - if (is_null_ptr(this->_normalisation_gated_proj_data_sptr)) - current_gate_norm_factors_sptr.reset(new TrivialBinNormalisation); - else { - shared_ptr norm_data_sptr(this->_normalisation_gated_proj_data_sptr->get_proj_data_sptr(gate_num)); - current_gate_norm_factors_sptr.reset( - new BinNormalisationFromProjData(norm_data_sptr)); - } - this->_single_gate_obj_funcs[gate_num].set_normalisation_sptr(current_gate_norm_factors_sptr); - this->_single_gate_obj_funcs[gate_num].set_recompute_sensitivity(this->get_recompute_sensitivity()); - this->_single_gate_obj_funcs[gate_num].set_use_subset_sensitivities(this->get_use_subset_sensitivities()); - - if(this->_single_gate_obj_funcs[gate_num].set_up(density_template_sptr) != Succeeded::yes) - error("Single gate objective functions is not set correctly!"); + this->_single_gate_obj_funcs.resize(1, this->get_time_gate_definitions().get_num_gates()); + + for (unsigned int gate_num = 1; gate_num <= this->get_time_gate_definitions().get_num_gates(); ++gate_num) { + info(boost::format("Objective Function for Gate Number: %1%") % gate_num); + this->_single_gate_obj_funcs[gate_num].set_projector_pair_sptr(this->_projector_pair_ptr); + this->_single_gate_obj_funcs[gate_num].set_proj_data_sptr(this->_gated_proj_data_sptr->get_proj_data_sptr(gate_num)); + this->_single_gate_obj_funcs[gate_num].set_max_segment_num_to_process(this->_max_segment_num_to_process); + this->_single_gate_obj_funcs[gate_num].set_zero_seg0_end_planes(this->_zero_seg0_end_planes != 0); + if (!is_null_ptr(this->_additive_gated_proj_data_sptr)) + this->_single_gate_obj_funcs[gate_num].set_additive_proj_data_sptr( + this->_additive_gated_proj_data_sptr->get_proj_data_sptr(gate_num)); + this->_single_gate_obj_funcs[gate_num].set_num_subsets(this->num_subsets); + this->_single_gate_obj_funcs[gate_num].set_frame_num(1); // This should be gate... + std::vector> frame_times(1, std::pair(0, 1)); + this->_single_gate_obj_funcs[gate_num].set_frame_definitions(TimeFrameDefinitions(frame_times)); + + shared_ptr current_gate_norm_factors_sptr; + if (is_null_ptr(this->_normalisation_gated_proj_data_sptr)) + current_gate_norm_factors_sptr.reset(new TrivialBinNormalisation); + else { + shared_ptr norm_data_sptr(this->_normalisation_gated_proj_data_sptr->get_proj_data_sptr(gate_num)); + current_gate_norm_factors_sptr.reset(new BinNormalisationFromProjData(norm_data_sptr)); } - }//_single_gate_obj_funcs[gate_num] + this->_single_gate_obj_funcs[gate_num].set_normalisation_sptr(current_gate_norm_factors_sptr); + this->_single_gate_obj_funcs[gate_num].set_recompute_sensitivity(this->get_recompute_sensitivity()); + this->_single_gate_obj_funcs[gate_num].set_use_subset_sensitivities(this->get_use_subset_sensitivities()); + + if (this->_single_gate_obj_funcs[gate_num].set_up(density_template_sptr) != Succeeded::yes) + error("Single gate objective functions is not set correctly!"); + } + } //_single_gate_obj_funcs[gate_num] return Succeeded::yes; } @@ -420,143 +382,117 @@ set_up_before_sensitivity(shared_ptr const& target_sptr) functions that compute the value/gradient of the objective function etc *************************************************************************/ -template +template void -PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion:: -compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, - const TargetT ¤t_estimate, - const int subset_num) -{ - assert(subset_num>=0); - assert(subset_numnum_subsets); - - GatedDiscretisedDensity gated_gradient=this->_gated_image_template; - GatedDiscretisedDensity gated_image_estimate=this->_gated_image_template; +PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion< + TargetT>::compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, const TargetT& current_estimate, + const int subset_num) { + assert(subset_num >= 0); + assert(subset_num < this->num_subsets); + + GatedDiscretisedDensity gated_gradient = this->_gated_image_template; + GatedDiscretisedDensity gated_image_estimate = this->_gated_image_template; // The following initialization doesn't stabilize reconstruction. - for(unsigned int gate_num=1;gate_num<=this->get_time_gate_definitions().get_num_gates();++gate_num) - std::fill(gated_image_estimate[gate_num].begin_all(), - gated_image_estimate[gate_num].end_all(), - 0.F); - this->_motion_vectors.warp_image(gated_image_estimate,current_estimate); - for(unsigned int gate_num=1;gate_num<=this->get_time_gate_definitions().get_num_gates();++gate_num) { - std::fill(gated_gradient[gate_num].begin_all(), - gated_gradient[gate_num].end_all(), - 0.F); - this->_single_gate_obj_funcs[gate_num]. - compute_sub_gradient_without_penalty_plus_sensitivity(gated_gradient[gate_num], - gated_image_estimate[gate_num], - subset_num); - } + for (unsigned int gate_num = 1; gate_num <= this->get_time_gate_definitions().get_num_gates(); ++gate_num) + std::fill(gated_image_estimate[gate_num].begin_all(), gated_image_estimate[gate_num].end_all(), 0.F); + this->_motion_vectors.warp_image(gated_image_estimate, current_estimate); + for (unsigned int gate_num = 1; gate_num <= this->get_time_gate_definitions().get_num_gates(); ++gate_num) { + std::fill(gated_gradient[gate_num].begin_all(), gated_gradient[gate_num].end_all(), 0.F); + this->_single_gate_obj_funcs[gate_num].compute_sub_gradient_without_penalty_plus_sensitivity( + gated_gradient[gate_num], gated_image_estimate[gate_num], subset_num); + } // if(this->_motion_correction_type==-1) - this->_reverse_motion_vectors.warp_image(gradient,gated_gradient) ; + this->_reverse_motion_vectors.warp_image(gradient, gated_gradient); // else - // this->_motion_vectors.warp_image(gradient,gated_gradient) ; + // this->_motion_vectors.warp_image(gradient,gated_gradient) ; } -template +template double -PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion:: -actual_compute_objective_function_without_penalty(const TargetT& current_estimate, - const int subset_num) -{ - assert(subset_num>=0); - assert(subset_numnum_subsets); +PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion::actual_compute_objective_function_without_penalty( + const TargetT& current_estimate, const int subset_num) { + assert(subset_num >= 0); + assert(subset_num < this->num_subsets); double result = 0.; - GatedDiscretisedDensity gated_image_estimate=this->_gated_image_template; + GatedDiscretisedDensity gated_image_estimate = this->_gated_image_template; // The following initialization doesn't stabilize reconstruction. - for(unsigned int gate_num=1; gate_num<=this->get_time_gate_definitions().get_num_gates(); ++gate_num) - std::fill(gated_image_estimate[gate_num].begin_all(), - gated_image_estimate[gate_num].end_all(), - 0.F); - this->_motion_vectors.warp_image(gated_image_estimate,current_estimate) ; + for (unsigned int gate_num = 1; gate_num <= this->get_time_gate_definitions().get_num_gates(); ++gate_num) + std::fill(gated_image_estimate[gate_num].begin_all(), gated_image_estimate[gate_num].end_all(), 0.F); + this->_motion_vectors.warp_image(gated_image_estimate, current_estimate); // loop over single_gate - for(unsigned int gate_num=1 ; - gate_num<=this->get_time_gate_definitions().get_num_gates(); - ++gate_num) - { - result += this->_single_gate_obj_funcs[gate_num]. - compute_objective_function_without_penalty(gated_image_estimate[gate_num], - subset_num); - } + for (unsigned int gate_num = 1; gate_num <= this->get_time_gate_definitions().get_num_gates(); ++gate_num) { + result += this->_single_gate_obj_funcs[gate_num].compute_objective_function_without_penalty(gated_image_estimate[gate_num], + subset_num); + } return result; } -template +template void -PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion:: -add_subset_sensitivity(TargetT& sensitivity, const int subset_num) const -{ - GatedDiscretisedDensity gated_subset_sensitivity=this->_gated_image_template; +PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion::add_subset_sensitivity( + TargetT& sensitivity, const int subset_num) const { + GatedDiscretisedDensity gated_subset_sensitivity = this->_gated_image_template; // loop over single_gate to get original subset sensitivity - for(unsigned int gate_num=1;gate_num<=this->get_time_gate_definitions().get_num_gates();++gate_num) - { - const shared_ptr > single_gate_subsensitivity_sptr( - this->_single_gate_obj_funcs[gate_num].get_subset_sensitivity(subset_num).clone()); - gated_subset_sensitivity.set_density_sptr(single_gate_subsensitivity_sptr,gate_num); - } + for (unsigned int gate_num = 1; gate_num <= this->get_time_gate_definitions().get_num_gates(); ++gate_num) { + const shared_ptr> single_gate_subsensitivity_sptr( + this->_single_gate_obj_funcs[gate_num].get_subset_sensitivity(subset_num).clone()); + gated_subset_sensitivity.set_density_sptr(single_gate_subsensitivity_sptr, gate_num); + } // perform warp - this->_reverse_motion_vectors.accumulate_warp_image(sensitivity,gated_subset_sensitivity) ; + this->_reverse_motion_vectors.accumulate_warp_image(sensitivity, gated_subset_sensitivity); } -//! /todo The PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion::actual_add_multiplication_with_approximate_sub_Hessian_without_penalty is not validated and at the moment OSSPS does not converge with motion correction. -template +//! /todo The +//! PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion::actual_add_multiplication_with_approximate_sub_Hessian_without_penalty +//! is not validated and at the moment OSSPS does not converge with motion correction. +template Succeeded -PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion:: -actual_add_multiplication_with_approximate_sub_Hessian_without_penalty(TargetT& output, - const TargetT& input, - const int subset_num) const -{ +PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion< + TargetT>::actual_add_multiplication_with_approximate_sub_Hessian_without_penalty(TargetT& output, const TargetT& input, + const int subset_num) const { // TODO this does not add but replace { std::string explanation; - if (!input.has_same_characteristics(this->get_subset_sensitivity(0), //////////////////// - explanation)) - { - warning("PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion:\n" - "sensitivity and input for add_multiplication_with_approximate_Hessian_without_penalty\n" - "should have the same characteristics.\n%s", - explanation.c_str()); - return Succeeded::no; - } - } - GatedDiscretisedDensity gated_input=this->_gated_image_template; - GatedDiscretisedDensity gated_output=this->_gated_image_template; - this->_motion_vectors.warp_image(gated_input,input) ; - - VectorWithOffset scale_factor(1,this->get_time_gate_definitions().get_num_gates()); - for(unsigned int gate_num=1; - gate_num<=this->get_time_gate_definitions().get_num_gates(); - ++gate_num) - { - scale_factor[gate_num]=gated_input[gate_num].find_max(); - /*! /note This is used to avoid higher values than these set in the precompute_denominator_of_conditioner_without_penalty() function. - /sa for more information see the recon_array_functions.cxx and the value of the max_quotient (originaly set to 10000.F) */ - gated_input[gate_num]/=scale_factor[gate_num]; - this->_single_gate_obj_funcs[gate_num]. - add_multiplication_with_approximate_sub_Hessian_without_penalty(gated_output[gate_num], - gated_input[gate_num], - subset_num); - gated_output[gate_num]*=scale_factor[gate_num]; - } // end of loop over gates - this->_reverse_motion_vectors.warp_image(output,gated_output); - output/=static_cast(this->get_time_gate_definitions().get_num_gates()); //Normalizing to get the average value to test if OSSPS works. + if (!input.has_same_characteristics(this->get_subset_sensitivity(0), //////////////////// + explanation)) { + warning("PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion:\n" + "sensitivity and input for add_multiplication_with_approximate_Hessian_without_penalty\n" + "should have the same characteristics.\n%s", + explanation.c_str()); + return Succeeded::no; + } + } + GatedDiscretisedDensity gated_input = this->_gated_image_template; + GatedDiscretisedDensity gated_output = this->_gated_image_template; + this->_motion_vectors.warp_image(gated_input, input); + + VectorWithOffset scale_factor(1, this->get_time_gate_definitions().get_num_gates()); + for (unsigned int gate_num = 1; gate_num <= this->get_time_gate_definitions().get_num_gates(); ++gate_num) { + scale_factor[gate_num] = gated_input[gate_num].find_max(); + /*! /note This is used to avoid higher values than these set in the precompute_denominator_of_conditioner_without_penalty() + function. /sa for more information see the recon_array_functions.cxx and the value of the max_quotient (originaly set to + 10000.F) */ + gated_input[gate_num] /= scale_factor[gate_num]; + this->_single_gate_obj_funcs[gate_num].add_multiplication_with_approximate_sub_Hessian_without_penalty( + gated_output[gate_num], gated_input[gate_num], subset_num); + gated_output[gate_num] *= scale_factor[gate_num]; + } // end of loop over gates + this->_reverse_motion_vectors.warp_image(output, gated_output); + output /= static_cast( + this->get_time_gate_definitions().get_num_gates()); // Normalizing to get the average value to test if OSSPS works. return Succeeded::yes; } -template +template Succeeded -PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion:: -actual_accumulate_sub_Hessian_times_input_without_penalty(TargetT& output, - const TargetT& current_image_estimate, - const TargetT& input, - const int subset_num) const -{ +PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion< + TargetT>::actual_accumulate_sub_Hessian_times_input_without_penalty(TargetT& output, const TargetT& current_image_estimate, + const TargetT& input, const int subset_num) const { { // check argument characteristics std::string explanation; - if (!input.has_same_characteristics(this->get_subset_sensitivity(0), explanation)) - { + if (!input.has_same_characteristics(this->get_subset_sensitivity(0), explanation)) { warning("PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion:\n" "sensitivity and input for actual_accumulate_sub_Hessian_times_input_without_penalty\n" "should have the same characteristics.\n%s", @@ -564,8 +500,7 @@ actual_accumulate_sub_Hessian_times_input_without_penalty(TargetT& output, return Succeeded::no; } - if (!current_image_estimate.has_same_characteristics(this->get_subset_sensitivity(0), explanation)) - { + if (!current_image_estimate.has_same_characteristics(this->get_subset_sensitivity(0), explanation)) { warning("PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion:\n" "sensitivity and current_image_estimate for actual_accumulate_sub_Hessian_times_input_without_penalty\n" "should have the same characteristics.\n%s", @@ -574,26 +509,20 @@ actual_accumulate_sub_Hessian_times_input_without_penalty(TargetT& output, } } - GatedDiscretisedDensity gated_input=this->_gated_image_template; - GatedDiscretisedDensity gated_current_image_estimate=this->_gated_image_template; - GatedDiscretisedDensity gated_output=this->_gated_image_template; - this->_motion_vectors.warp_image(gated_input,input); + GatedDiscretisedDensity gated_input = this->_gated_image_template; + GatedDiscretisedDensity gated_current_image_estimate = this->_gated_image_template; + GatedDiscretisedDensity gated_output = this->_gated_image_template; + this->_motion_vectors.warp_image(gated_input, input); this->_motion_vectors.warp_image(gated_current_image_estimate, current_image_estimate); - for(unsigned int gate_num=1; - gate_num<=this->get_time_gate_definitions().get_num_gates(); - ++gate_num) - { - this->_single_gate_obj_funcs[gate_num]. - accumulate_sub_Hessian_times_input_without_penalty(gated_output[gate_num], - gated_current_image_estimate[gate_num], - gated_input[gate_num], - subset_num); + for (unsigned int gate_num = 1; gate_num <= this->get_time_gate_definitions().get_num_gates(); ++gate_num) { + this->_single_gate_obj_funcs[gate_num].accumulate_sub_Hessian_times_input_without_penalty( + gated_output[gate_num], gated_current_image_estimate[gate_num], gated_input[gate_num], subset_num); } // end of loop over gates this->_reverse_motion_vectors.warp_image(output, gated_output); - output/=static_cast(this->get_time_gate_definitions().get_num_gates()); //Normalizing to get the average value to test if OSSPS works. + output /= static_cast( + this->get_time_gate_definitions().get_num_gates()); // Normalizing to get the average value to test if OSSPS works. return Succeeded::yes; } - END_NAMESPACE_STIR diff --git a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeData.h b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeData.h index ec82354276..d69b04ac6a 100644 --- a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeData.h +++ b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeData.h @@ -20,7 +20,7 @@ /*! \file \ingroup GeneralisedObjectiveFunction - \brief Declaration of class + \brief Declaration of class stir::PoissonLogLikelihoodWithLinearModelForMeanAndListModeData \author Kris Thielemans @@ -31,7 +31,6 @@ #ifndef __stir_recon_buildblock_PoissonLogLikelihoodWithLinearModelForMeanAndListModeData_H__ #define __stir_recon_buildblock_PoissonLogLikelihoodWithLinearModelForMeanAndListModeData_H__ - //#include "stir/RegisteredParsingObject.h" #include "stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMean.h" #include "stir/listmode/ListModeData.h" @@ -46,7 +45,7 @@ START_NAMESPACE_STIR The statistics for list mode data is slightly different from having a set of counts, see the paper - H. H. Barrett, L. Parra, and T. White, + H. H. Barrett, L. Parra, and T. White, List-mode likelihood, J. Optical Soc. Amer. A, vol. 14, no. 11, 1997. However, it is intuitive that the list mode likelihood can be @@ -69,41 +68,34 @@ START_NAMESPACE_STIR */ template -class PoissonLogLikelihoodWithLinearModelForMeanAndListModeData: -public PoissonLogLikelihoodWithLinearModelForMean -{ -private: - +class PoissonLogLikelihoodWithLinearModelForMeanAndListModeData : public PoissonLogLikelihoodWithLinearModelForMean { +private: typedef PoissonLogLikelihoodWithLinearModelForMean base_type; -public: - - - PoissonLogLikelihoodWithLinearModelForMeanAndListModeData(); - - //virtual TargetT * construct_target_ptr(); - - virtual Succeeded - set_up(shared_ptr const& target_sptr); - - virtual - void compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, - const TargetT ¤t_estimate, - const int subset_num)=0; +public: + PoissonLogLikelihoodWithLinearModelForMeanAndListModeData(); + + // virtual TargetT * construct_target_ptr(); + + virtual Succeeded set_up(shared_ptr const& target_sptr); + + virtual void compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, const TargetT& current_estimate, + const int subset_num) = 0; //! time frame definitions - /*! \todo This is currently used to be able to compute the gradient for + /*! \todo This is currently used to be able to compute the gradient for one time frame. However, it probably does not belong here. For instance when fitting kinetic model parameters from list mode data, time frames are in principle irrelevant. So, we will probably shift this to the derived class. */ - TimeFrameDefinitions frame_defs; + TimeFrameDefinitions frame_defs; - virtual void set_normalisation_sptr(const shared_ptr&); - virtual void set_additive_proj_data_sptr(const shared_ptr&); + virtual void set_normalisation_sptr(const shared_ptr&); + virtual void set_additive_proj_data_sptr(const shared_ptr&); + + virtual void set_input_data(const shared_ptr&); + virtual const ListModeData& get_input_data() const; - virtual void set_input_data(const shared_ptr &); - virtual const ListModeData& get_input_data() const; protected: std::string frame_defs_filename; @@ -113,31 +105,31 @@ public PoissonLogLikelihoodWithLinearModelForMean shared_ptr additive_proj_data_sptr; shared_ptr normalisation_sptr; - + //! Listmode pointer shared_ptr list_mode_data_sptr; - + unsigned int current_frame_num; //! This is part of some functionality I transfer from LmToProjData. long int num_events_to_use; - //! Reconstruct based on time frames - bool do_time_frame; - + //! Reconstruct based on time frames + bool do_time_frame; + //! sets any default values /*! Has to be called by set_defaults in the leaf-class */ virtual void set_defaults(); //! sets keys /*! Has to be called by initialise_keymap in the leaf-class */ - virtual void initialise_keymap(); + virtual void initialise_keymap(); - virtual bool post_processing(); + virtual bool post_processing(); - //! will be called when a new time frame starts - /*! The frame numbers start from 1. */ - virtual void start_new_time_frame(const unsigned int new_frame_num); + //! will be called when a new time frame starts + /*! The frame numbers start from 1. */ + virtual void start_new_time_frame(const unsigned int new_frame_num); - ParseAndCreateFrom target_parameter_parser; + ParseAndCreateFrom target_parameter_parser; }; END_NAMESPACE_STIR diff --git a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h index 4601a6bdfc..65302975a4 100644 --- a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h +++ b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h @@ -19,7 +19,7 @@ /*! \file \ingroup GeneralisedObjectiveFunction - \brief Declaration of class + \brief Declaration of class stir::PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin \author Nikos Efthimiou @@ -30,23 +30,21 @@ #ifndef __stir_recon_buildblock_PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin_H__ #define __stir_recon_buildblock_PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin_H__ - #include "stir/RegisteredParsingObject.h" #include "stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeData.h" -#include "stir/recon_buildblock/ProjMatrixByBin.h" +#include "stir/recon_buildblock/ProjMatrixByBin.h" #include "stir/ProjDataInMemory.h" #include "stir/recon_buildblock/ProjectorByBinPairUsingProjMatrixByBin.h" #include "stir/ExamInfo.h" START_NAMESPACE_STIR - /*! \ingroup GeneralisedObjectiveFunction \brief Class for PET list mode data from static images for a scanner with discrete detectors. If the scanner has discrete (and stationary) detectors, it can be modeled via ProjMatrixByBin and BinNormalisation. - + \see PoissonLogLikelihoodWithLinearModelForMeanAndProjData If the list mode data is binned (with LmToProjData) without merging @@ -55,63 +53,54 @@ START_NAMESPACE_STIR */ template -class PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin: -public RegisteredParsingObject, - GeneralisedObjectiveFunction, - PoissonLogLikelihoodWithLinearModelForMeanAndListModeData > +class PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin + : public RegisteredParsingObject, + GeneralisedObjectiveFunction, + PoissonLogLikelihoodWithLinearModelForMeanAndListModeData> { private: -typedef RegisteredParsingObject, - GeneralisedObjectiveFunction, - PoissonLogLikelihoodWithLinearModelForMeanAndListModeData > - base_type; + typedef RegisteredParsingObject, + GeneralisedObjectiveFunction, + PoissonLogLikelihoodWithLinearModelForMeanAndListModeData> + base_type; public: - - //! Name which will be d when parsing a GeneralisedObjectiveFunction object - static const char * const registered_name; - - PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin(); + //! Name which will be d when parsing a GeneralisedObjectiveFunction object + static const char* const registered_name; + + PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin(); //! This should compute the gradient of the objective function at the \a current_estimate overwriting \a gradient - virtual - void compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, - const TargetT ¤t_estimate, - const int subset_num); - virtual TargetT * construct_target_ptr() const; + virtual void compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, const TargetT& current_estimate, + const int subset_num); + virtual TargetT* construct_target_ptr() const; int set_num_subsets(const int new_num_subsets); - - const shared_ptr & - get_normalisation_sptr() const - { return this->normalisation_sptr; } - + + const shared_ptr& get_normalisation_sptr() const { return this->normalisation_sptr; } + virtual unique_ptr get_exam_info_uptr_for_target() const; - + protected: - virtual double - actual_compute_objective_function_without_penalty(const TargetT& current_estimate, - const int subset_num) - { // TODO + virtual double actual_compute_objective_function_without_penalty(const TargetT& current_estimate, + const int subset_num) { // TODO error("compute_objective_function_without_penalty Not implemented yet"); - return 0; + return 0; } - virtual Succeeded - set_up_before_sensitivity(shared_ptr const& target_sptr); - - virtual void - add_subset_sensitivity(TargetT& sensitivity, const int subset_num) const; - + virtual Succeeded set_up_before_sensitivity(shared_ptr const& target_sptr); + + virtual void add_subset_sensitivity(TargetT& sensitivity, const int subset_num) const; + //! Maximum ring difference to take into account /*! \todo Might be removed */ - int max_ring_difference_num_to_process; - + int max_ring_difference_num_to_process; + //! Triggers calculation of sensitivity using time-of-flight bool use_tofsens; - + //! Stores the projectors that are used for the computations shared_ptr PM_sptr; @@ -123,8 +112,8 @@ typedef RegisteredParsingObject additive_proj_data_sptr; - - std::string additive_projection_data_filename ; + + std::string additive_projection_data_filename; //! ProjDataInfo shared_ptr proj_data_info_sptr; @@ -138,8 +127,7 @@ typedef RegisteredParsingObjectnot \f$r\f$). @@ -109,9 +107,9 @@ class DistributedCachingInformation; ; see ProjectorByBinPair hierarchy for possible values Projector pair type := - + ; reserved value: 0 means none - ; see PoissonLogLikelihoodWithLinearModelForMeanAndProjData + ; see PoissonLogLikelihoodWithLinearModelForMeanAndProjData ; class documentation additive sinogram := @@ -128,33 +126,29 @@ class DistributedCachingInformation; \endverbatim */ template -class PoissonLogLikelihoodWithLinearModelForMeanAndProjData: -public RegisteredParsingObject, - GeneralisedObjectiveFunction, - PoissonLogLikelihoodWithLinearModelForMean > -{ - private: +class PoissonLogLikelihoodWithLinearModelForMeanAndProjData + : public RegisteredParsingObject, + GeneralisedObjectiveFunction, PoissonLogLikelihoodWithLinearModelForMean> { +private: typedef RegisteredParsingObject, - GeneralisedObjectiveFunction, - PoissonLogLikelihoodWithLinearModelForMean > - base_type; + GeneralisedObjectiveFunction, PoissonLogLikelihoodWithLinearModelForMean> + base_type; public: - //! Name which will be used when parsing a GeneralisedObjectiveFunction object - static const char * const registered_name; + static const char* const registered_name; // /*! \name Variables for STIR_MPI - Only used when STIR_MPI is enabled. + Only used when STIR_MPI is enabled. \todo move to protected area */ //@{ //! points to the information object needed to support distributed caching - DistributedCachingInformation * caching_info_ptr; + DistributedCachingInformation* caching_info_ptr; //#ifdef STIR_MPI - //!enable/disable key for distributed caching + //! enable/disable key for distributed caching bool distributed_cache_enabled; bool distributed_tests_enabled; bool message_timings_enabled; @@ -163,7 +157,6 @@ public RegisteredParsingObject&); - void set_projector_pair_sptr(const shared_ptr&) ; + void set_projector_pair_sptr(const shared_ptr&); void set_frame_num(const int); void set_frame_definitions(const TimeFrameDefinitions&); virtual void set_normalisation_sptr(const shared_ptr&); - virtual void set_input_data(const shared_ptr &); + virtual void set_input_data(const shared_ptr&); virtual const ProjData& get_input_data() const; -//@} - - virtual void - compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, - const TargetT ¤t_estimate, - const int subset_num); - - virtual std::unique_ptr - get_exam_info_uptr_for_target() const; + //@} + + virtual void compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, const TargetT& current_estimate, + const int subset_num); + + virtual std::unique_ptr get_exam_info_uptr_for_target() const; #if 0 // currently not used float sum_projection_data() const; #endif - virtual void - add_subset_sensitivity(TargetT& sensitivity, const int subset_num) const; + virtual void add_subset_sensitivity(TargetT& sensitivity, const int subset_num) const; - protected: - virtual Succeeded - set_up_before_sensitivity(shared_ptr const& target_sptr); +protected: + virtual Succeeded set_up_before_sensitivity(shared_ptr const& target_sptr); - virtual double - actual_compute_objective_function_without_penalty(const TargetT& current_estimate, - const int subset_num); + virtual double actual_compute_objective_function_without_penalty(const TargetT& current_estimate, const int subset_num); /*! The Hessian (without penalty) is approximately given by: @@ -256,15 +241,15 @@ public RegisteredParsingObject target_parameter_parser; + ParseAndCreateFrom target_parameter_parser; /********************************/ - //! Stores the projectors that are used for the computations shared_ptr projector_pair_ptr; @@ -332,18 +312,17 @@ public RegisteredParsingObject additive_proj_data_sptr; shared_ptr normalisation_sptr; - // TODO doc + // TODO doc int frame_num; std::string frame_definition_filename; TimeFrameDefinitions frame_defs; -//Loglikelihood computation parameters - // TODO rename and move higher up in the hierarchy + // Loglikelihood computation parameters + // TODO rename and move higher up in the hierarchy //! subiteration interval at which the loglikelihood function is evaluated int loglikelihood_computation_interval; @@ -360,17 +339,18 @@ public RegisteredParsingObject symmetries_sptr; #if 0 void @@ -379,7 +359,7 @@ public RegisteredParsingObject class Viewgram; -template class DataProcessor; +template +class Viewgram; +template +class DataProcessor; /*! \brief A very preliminary class that first smooths the image, then back projects. \warning. It assumes that the DataProcessor does not change the size of the image. */ -class PostsmoothingBackProjectorByBin : - public - RegisteredParsingObject -{ +class PostsmoothingBackProjectorByBin : public RegisteredParsingObject { #ifdef SWIG // work-around swig problem. It gets confused when using a private (or protected) // typedef in a definition of a public typedef/member public: #else - private: +private: #endif typedef BackProjectorByBin base_type; + public: //! Name which will be used when parsing a PostsmoothingBackProjectorByBin object - static const char * const registered_name; + static const char* const registered_name; //! Default constructor (calls set_defaults()) PostsmoothingBackProjectorByBin(); - ~ PostsmoothingBackProjectorByBin(); + ~PostsmoothingBackProjectorByBin(); //! Stores all necessary geometric info /*! Note that the density_info_ptr is not stored in this object. It's only used to get some info on sizes etc. - */ - virtual void set_up( - const shared_ptr& proj_data_info_ptr, - const shared_ptr >& density_info_ptr // TODO should be Info only - ); - + */ + virtual void set_up(const shared_ptr& proj_data_info_ptr, + const shared_ptr>& density_info_ptr // TODO should be Info only + ); - PostsmoothingBackProjectorByBin( - const shared_ptr& original_back_projector_ptr, - const shared_ptr > >&); + PostsmoothingBackProjectorByBin(const shared_ptr& original_back_projector_ptr, + const shared_ptr>>&); // Informs on which symmetries the projector handles // It should get data related by at least those symmetries. // Otherwise, a run-time error will occur (unless the derived // class has other behaviour). - const DataSymmetriesForViewSegmentNumbers * get_symmetries_used() const; + const DataSymmetriesForViewSegmentNumbers* get_symmetries_used() const; void update_filtered_density_image(DiscretisedDensity<3, float>&); - void init_filtered_density_image(DiscretisedDensity<3, float> &); + void init_filtered_density_image(DiscretisedDensity<3, float>&); BackProjectorByBin* get_original_back_projector_ptr() const; PostsmoothingBackProjectorByBin* clone() const; private: - shared_ptr original_back_projector_ptr; #ifdef STIR_PROJECTORS_AS_V3 - void actual_back_project(DiscretisedDensity<3,float>&, - const RelatedViewgrams&, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num); + void actual_back_project(DiscretisedDensity<3, float>&, const RelatedViewgrams&, const int min_axial_pos_num, + const int max_axial_pos_num, const int min_tangential_pos_num, const int max_tangential_pos_num); #endif - void actual_back_project(const RelatedViewgrams&, - const int min_axial_pos_num, const int max_axial_pos_num, + void actual_back_project(const RelatedViewgrams&, const int min_axial_pos_num, const int max_axial_pos_num, const int min_tangential_pos_num, const int max_tangential_pos_num); - void actual_back_project(DiscretisedDensity<3,float>& density, - const Bin& bin); - - shared_ptr > filtered_density_sptr; + void actual_back_project(DiscretisedDensity<3, float>& density, const Bin& bin); + shared_ptr> filtered_density_sptr; virtual void set_defaults(); virtual void initialise_keymap(); diff --git a/src/include/stir/recon_buildblock/PresmoothingForwardProjectorByBin.h b/src/include/stir/recon_buildblock/PresmoothingForwardProjectorByBin.h index 86fd84ec6d..a4767474a8 100644 --- a/src/include/stir/recon_buildblock/PresmoothingForwardProjectorByBin.h +++ b/src/include/stir/recon_buildblock/PresmoothingForwardProjectorByBin.h @@ -33,72 +33,64 @@ #include "stir/recon_buildblock/ForwardProjectorByBin.h" #include "stir/shared_ptr.h" - START_NAMESPACE_STIR -template class Viewgram; -template class DataProcessor; +template +class Viewgram; +template +class DataProcessor; /*! \brief A very preliminary class that first smooths the image, then forward projects. \warning. It assumes that the DataProcessor does not change the size of the image. */ -class PresmoothingForwardProjectorByBin : - public - RegisteredParsingObject -{ +class PresmoothingForwardProjectorByBin + : public RegisteredParsingObject { #ifdef SWIG // work-around swig problem. It gets confused when using a private (or protected) // typedef in a definition of a public typedef/member public: #else - private: +private: #endif typedef ForwardProjectorByBin base_type; + public: //! Name which will be used when parsing a PresmoothingForwardProjectorByBin object - static const char * const registered_name; + static const char* const registered_name; //! Default constructor (calls set_defaults()) PresmoothingForwardProjectorByBin(); - ~ PresmoothingForwardProjectorByBin(); - -// void update_filtered_density_image(const DiscretisedDensity<3,float>&); + ~PresmoothingForwardProjectorByBin(); + + // void update_filtered_density_image(const DiscretisedDensity<3,float>&); //! Stores all necessary geometric info /*! Note that the density_info_ptr is not stored in this object. It's only used to get some info on sizes etc. - */ - virtual void set_up( - const shared_ptr& proj_data_info_ptr, - const shared_ptr >& density_info_ptr // TODO should be Info only - ); + */ + virtual void set_up(const shared_ptr& proj_data_info_ptr, + const shared_ptr>& density_info_ptr // TODO should be Info only + ); - - PresmoothingForwardProjectorByBin( - const shared_ptr& original_forward_projector_ptr, - const shared_ptr > >&); + PresmoothingForwardProjectorByBin(const shared_ptr& original_forward_projector_ptr, + const shared_ptr>>&); // Informs on which symmetries the projector handles // It should get data related by at least those symmetries. // Otherwise, a run-time error will occur (unless the derived // class has other behaviour). - const DataSymmetriesForViewSegmentNumbers * get_symmetries_used() const; + const DataSymmetriesForViewSegmentNumbers* get_symmetries_used() const; private: - shared_ptr original_forward_projector_ptr; #ifdef STIR_PROJECTORS_AS_V3 - void actual_forward_project(RelatedViewgrams&, - const DiscretisedDensity<3,float>&, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num); + void actual_forward_project(RelatedViewgrams&, const DiscretisedDensity<3, float>&, const int min_axial_pos_num, + const int max_axial_pos_num, const int min_tangential_pos_num, const int max_tangential_pos_num); #endif - void actual_forward_project(RelatedViewgrams&, - const int min_axial_pos_num, const int max_axial_pos_num, + void actual_forward_project(RelatedViewgrams&, const int min_axial_pos_num, const int max_axial_pos_num, const int min_tangential_pos_num, const int max_tangential_pos_num); #if 0 // disabled as currently not used. needs to be written in the new style anyway diff --git a/src/include/stir/recon_buildblock/PriorWithParabolicSurrogate.h b/src/include/stir/recon_buildblock/PriorWithParabolicSurrogate.h index 8786db6c3f..ac88e4bfbe 100644 --- a/src/include/stir/recon_buildblock/PriorWithParabolicSurrogate.h +++ b/src/include/stir/recon_buildblock/PriorWithParabolicSurrogate.h @@ -5,7 +5,7 @@ \ingroup priors \brief Declaration of class stir::PriorWithParabolicSurrogate - \author Sanida Mustafovic + \author Sanida Mustafovic \author Kris Thielemans */ @@ -26,7 +26,6 @@ See STIR/LICENSE.txt for details */ - #ifndef __stir_recon_buildblock_PriorWithParabolicSurrogate_H__ #define __stir_recon_buildblock_PriorWithParabolicSurrogate_H__ @@ -39,28 +38,21 @@ START_NAMESPACE_STIR \ingroup priors \brief this class implements priors with a parabolic surrogate curvature - + See for example Erdogan and Fessler, Ordered subsets algorithms for transmission tomography, PMB, 44 (1999) 2835. - + */ template -class PriorWithParabolicSurrogate: - public GeneralisedPrior -{ +class PriorWithParabolicSurrogate : public GeneralisedPrior { public: - - //!this should calculate the parabolic surrogate curvature - virtual void - parabolic_surrogate_curvature(TargetT& parabolic_surrogate_curvature, - const TargetT ¤t_estimate) = 0; + //! this should calculate the parabolic surrogate curvature + virtual void parabolic_surrogate_curvature(TargetT& parabolic_surrogate_curvature, const TargetT& current_estimate) = 0; //! A function that allows skipping some computations if the curvature is independent of the \c current_estimate /*! Defaults to return \c true, but can be overloaded by the derived class. */ - virtual bool - parabolic_surrogate_curvature_depends_on_argument() const - { return true; } + virtual bool parabolic_surrogate_curvature_depends_on_argument() const { return true; } #if 0 // TODO this does not work for arbitrary TargetT, but only 3-dimensional things // maybe we could have a TargetT::index_type or so @@ -69,10 +61,8 @@ class PriorWithParabolicSurrogate: const BasicCoordinate<3,int>& coords, const TargetT ¤t_estimate) =0; #endif - }; - END_NAMESPACE_STIR #endif diff --git a/src/include/stir/recon_buildblock/ProjDataRebinning.h b/src/include/stir/recon_buildblock/ProjDataRebinning.h index ae717fc838..104b545f73 100644 --- a/src/include/stir/recon_buildblock/ProjDataRebinning.h +++ b/src/include/stir/recon_buildblock/ProjDataRebinning.h @@ -19,17 +19,16 @@ #ifndef __stir_recon_buildblock_ProjDataRebinning_H__ #define __stir_recon_buildblock_ProjDataRebinning_H__ /*! - \file + \file \ingroup recon_buildblock - + \brief declares the stir::ProjDataRebinning class \author Kris Thielemans */ /* Modification history -*/ - + */ #include "stir/RegisteredObject.h" #include "stir/TimedObject.h" @@ -40,7 +39,6 @@ START_NAMESPACE_STIR - class Succeeded; /*! @@ -54,8 +52,8 @@ class Succeeded; The utility rebin_projdata provides the user interface to this class. What follows is for developers. - Parameters need to be initialised somehow. This is usually done using the - parse() member functions (see ParsingObject). + Parameters need to be initialised somehow. This is usually done using the + parse() member functions (see ParsingObject). For some parameters, set_some_parameter() methods are provided. @@ -71,33 +69,28 @@ class Succeeded; \par Info for developers - For convenience, the class is derived from TimedObject. It is the + For convenience, the class is derived from TimedObject. It is the responsibility of the derived class to run these timers though. \todo there should be a method to rebin the data without writing the result to disk */ - -class ProjDataRebinning : - public TimedObject, - public RegisteredObject -{ +class ProjDataRebinning : public TimedObject, public RegisteredObject { public: //! virtual destructor virtual ~ProjDataRebinning(); - + //! gives method information virtual std::string method_info() const = 0; - + //! executes the rebinning /*! At the end of the rebinning, the final 2D projection data are saved to file as given in output_filename_prefix. \return Succeeded::yes if everything was alright. - */ - virtual Succeeded - rebin()=0; - + */ + virtual Succeeded rebin() = 0; + /*! \name get/set the number of segments to process \see max_segment_num_to_process @@ -119,19 +112,18 @@ class ProjDataRebinning : // KTTODO I'm not enthousiastic about the next function // why would you need this? shared_ptr get_proj_data_sptr(); - + // TODO needed at all? // KTTODO: yes, and change parameters //! operations prior to the rebinning virtual Succeeded set_up(); // parameters - protected: - +protected: //! file name for output projdata (should be without extension) - std::string output_filename_prefix; + std::string output_filename_prefix; //! file name for input projdata - std::string input_filename; + std::string input_filename; //! the maximum absolute segment number to use in the reconstruction /*! convention: if -1, use get_max_segment_num()*/ int max_segment_num_to_process; @@ -152,17 +144,14 @@ class ProjDataRebinning : #endif //! used to check acceptable parameter ranges, etc... - virtual bool post_processing(); + virtual bool post_processing(); virtual void set_defaults(); virtual void initialise_keymap(); - protected: // members - +protected: // members shared_ptr proj_data_sptr; }; END_NAMESPACE_STIR - #endif - diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBin.h b/src/include/stir/recon_buildblock/ProjMatrixByBin.h index c1712c3edb..54922c2d76 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBin.h +++ b/src/include/stir/recon_buildblock/ProjMatrixByBin.h @@ -4,9 +4,9 @@ /*! \file - \ingroup projection + \ingroup projection \brief declaration of stir::ProjMatrixByBin and its helpers classes - + \author Nikos Efthimiou \author Mustapha Sadki \author Kris Thielemans @@ -45,36 +45,36 @@ //#include #include #ifdef STIR_OPENMP -#include +# include #endif // define a local preprocessor symbol to keep code relatively clean #ifdef STIR_NO_MUTABLE -#define STIR_MUTABLE_CONST +# define STIR_MUTABLE_CONST #else -#define STIR_MUTABLE_CONST const +# define STIR_MUTABLE_CONST const #endif START_NAMESPACE_STIR -/* TODO +/* TODO class ProjMatrixElemsForOneViewgram; class SubsetInfo; */ - -class Bin; - + +class Bin; + /*! \ingroup projection -\brief - This is the (abstract) base class for all projection matrices +\brief + This is the (abstract) base class for all projection matrices which are organised by 'bin'. - This class provides essentially only 2 public members: a method to get a - 'row' of the matrix, and a method to get information on the symmetries. + This class provides essentially only 2 public members: a method to get a + 'row' of the matrix, and a method to get information on the symmetries. Currently, the class provides for some (basic) caching. - This functionality will probably be moved to a new class + This functionality will probably be moved to a new class ProjMatrixByBinWithCache. (TODO) \par Parsing parameters @@ -85,51 +85,43 @@ class Bin; store only basic bins in cache := true \endverbatim The 2nd option allows to cache the whole matrix. This results in the fastest - behaviour IF your system does not start swapping. The default choice caches + behaviour IF your system does not start swapping. The default choice caches only the 'basic' bins, and computes symmetry related bins from the 'basic' ones. */ -class ProjMatrixByBin : - public RegisteredObject, - public TimedObject -{ +class ProjMatrixByBin : public RegisteredObject, public TimedObject { public: - virtual ~ProjMatrixByBin() {} //! To be called before any calculation is performed /*! Note that get_proj_matrix_elems_for_one_bin() will expect objects of compatible sizes and other info. - \warning: Any implementation of set_up in a derived class has to + \warning: Any implementation of set_up in a derived class has to call ProjMatrixByBin::set_up first. */ - virtual void set_up( - const shared_ptr& proj_data_info_ptr, - const shared_ptr >& density_info_ptr // TODO should be Info only - ) = 0; + virtual void set_up(const shared_ptr& proj_data_info_ptr, + const shared_ptr>& density_info_ptr // TODO should be Info only + ) = 0; virtual ProjMatrixByBin* clone() const = 0; //! get a pointer to an object encoding all symmetries that are used by this ProjMatrixByBin - inline const DataSymmetriesForBins* get_symmetries_ptr() const; + inline const DataSymmetriesForBins* get_symmetries_ptr() const; //! get a shared_ptr to an object encoding all symmetries that are used by this ProjMatrixByBin inline const shared_ptr get_symmetries_sptr() const; - + //! The main method for getting a row of the matrix. - /*! + /*! The ProjMatrixElemsForOneBin argument will be overwritten (i.e. data is NOT appended). - + The implementation is inline as it just gets it in - terms of the cached_proj_matrix_elems_for_one_bin or + terms of the cached_proj_matrix_elems_for_one_bin or calculate_proj_matrix_elems_for_one_bin. N.E: Updated to accomondate TOF information. */ - inline void - get_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin&, - const Bin&) STIR_MUTABLE_CONST; + inline void get_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin&, const Bin&) STIR_MUTABLE_CONST; #if 0 // TODO @@ -140,19 +132,19 @@ class ProjMatrixByBin : */ virtual void write_to_file_by_bin( const char * const file_name_without_extension) const; -#endif +#endif // TODO implement this one at some point ? /* virtual void write_to_file_by_voxel( const char * const file_name_without_extension); */ - - //void set_maximum_cache_size(const unsigned long size){;} + + // void set_maximum_cache_size(const unsigned long size){;} /* TODO void set_subset_usage(const SubsetInfo&, const int num_access_times); */ void enable_cache(const bool v = true); - void store_only_basic_bins_in_cache(const bool v = true) ; + void store_only_basic_bins_in_cache(const bool v = true); bool is_cache_enabled() const; bool does_cache_store_only_basic_bins() const; @@ -160,30 +152,27 @@ class ProjMatrixByBin : // void reserve_num_elements_in_cache(const std::size_t); //! Remove all elements from the cache void clear_cache() STIR_MUTABLE_CONST; - + protected: shared_ptr symmetries_sptr; - + //! default ctor (calls set_defaults()) - /*! Note that due to the C++ definition (and some good reasons), + /*! Note that due to the C++ definition (and some good reasons), ProjMatrixByBin::set_defaults() is called, even though this is a virtual function. */ - ProjMatrixByBin(); - + ProjMatrixByBin(); + /*! \brief This method needs to be implemented in the derived class. - + bin-coordinates are obtained via the ProjMatrixElemsForOneBin::get_bin() method. Note that 'calculate' could just as well mean 'get from file' */ - virtual void - calculate_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& - ) const = 0; + virtual void calculate_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin&) const = 0; /////////////////////////////// parsing stuff ////////////////////// - + //! sets value for caching configuration (enables caching, but for 'basic' bins only) /*! Has to be called by set_defaults in the leaf-class */ virtual void set_defaults(); @@ -193,54 +182,52 @@ class ProjMatrixByBin : //! Checks if parameters have sensible values /*! Has to be called by post_processing in the leaf-class */ virtual bool post_processing(); - + /////////////////////////////// caching stuff ////////////////////// - bool cache_disabled; + bool cache_disabled; bool cache_stores_only_basic_bins; //! If activated TOF reconstruction will be performed. bool tof_enabled; /*! \brief The method that tries to get data from the cache. - + If it succeeds, it overwrites the ProjMatrixElemsForOneBin parameter and returns Succeeded::yes, otherwise it does not touch the ProjMatrixElemsForOneBin and returns Succeeded::false. */ - Succeeded get_cached_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& - ) const; - + Succeeded get_cached_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin&) const; + //! We need a local copy of the discretised density in order to find the //! cartesian coordinates of each voxel. - shared_ptr > image_info_sptr; + shared_ptr> image_info_sptr; //! We need a local copy of the proj_data_info to get the integration boundaries and RayTracing shared_ptr proj_data_info_sptr; //! The method to store data in the cache. - void cache_proj_matrix_elems_for_one_bin( const ProjMatrixElemsForOneBin&) - STIR_MUTABLE_CONST; + void cache_proj_matrix_elems_for_one_bin(const ProjMatrixElemsForOneBin&) STIR_MUTABLE_CONST; private: - typedef boost::uint32_t CacheKey; - // typedef std::map MapProjMatrixElemsForOneBin; - typedef boost::unordered_map MapProjMatrixElemsForOneBin; + // typedef std::map MapProjMatrixElemsForOneBin; + typedef boost::unordered_map MapProjMatrixElemsForOneBin; typedef MapProjMatrixElemsForOneBin::iterator MapProjMatrixElemsForOneBinIterator; typedef MapProjMatrixElemsForOneBin::const_iterator const_MapProjMatrixElemsForOneBinIterator; - - //! collection of ProjMatrixElemsForOneBin (internal cache ) + + //! collection of ProjMatrixElemsForOneBin (internal cache ) #ifndef STIR_NO_MUTABLE mutable #endif - VectorWithOffset > cache_collection; + VectorWithOffset> + cache_collection; #ifdef STIR_OPENMP -#ifndef STIR_NO_MUTABLE +# ifndef STIR_NO_MUTABLE mutable -#endif - VectorWithOffset > cache_locks; +# endif + VectorWithOffset> + cache_locks; #endif //! create the key for caching @@ -250,7 +237,7 @@ class ProjMatrixByBin : //! Activates the application of the timing kernel to the LOR //! and performs initial set_up(). //! \warning Must be called after set_up() - void enable_tof(const shared_ptr& proj_data_info_sptr,const bool v = true); + void enable_tof(const shared_ptr& proj_data_info_sptr, const bool v = true); //! A local copy of the scanner's time resolution in mm. float gauss_sigma_in_mm; @@ -261,19 +248,14 @@ class ProjMatrixByBin : //! The function which actually applies the TOF kernel on the LOR. inline void apply_tof_kernel_and_symm_transformation(ProjMatrixElemsForOneBin& probabilities, - const CartesianCoordinate3D& point1, - const CartesianCoordinate3D& point2, - const unique_ptr& symm_ptr) STIR_MUTABLE_CONST; - - + const CartesianCoordinate3D& point1, + const CartesianCoordinate3D& point2, + const unique_ptr& symm_ptr) STIR_MUTABLE_CONST; //! Get the interal value erf(m - v_j) - erf(m -v_j) inline void get_tof_value(const float d1, const float d2, float& val) const; - }; - - END_NAMESPACE_STIR #include "stir/recon_buildblock/ProjMatrixByBin.inl" @@ -281,6 +263,3 @@ END_NAMESPACE_STIR #undef STIR_MUTABLE_CONST #endif // __ProjMatrixByBin_H__ - - - diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl index 939c545073..f6cd98055f 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl +++ b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl @@ -8,7 +8,7 @@ \brief Implementations of inline functions for class stir::ProjMatrixByBin \author Nikos Efthimiou - \author Mustapha Sadki + \author Mustapha Sadki \author Kris Thielemans \author PARAPET project @@ -38,40 +38,30 @@ START_NAMESPACE_STIR const DataSymmetriesForBins* -ProjMatrixByBin:: get_symmetries_ptr() const -{ - return symmetries_sptr.get(); +ProjMatrixByBin::get_symmetries_ptr() const { + return symmetries_sptr.get(); } const shared_ptr -ProjMatrixByBin:: get_symmetries_sptr() const -{ - return symmetries_sptr; +ProjMatrixByBin::get_symmetries_sptr() const { + return symmetries_sptr; } -inline void -ProjMatrixByBin:: -get_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& probabilities, - const Bin& bin) STIR_MUTABLE_CONST -{ +inline void +ProjMatrixByBin::get_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& probabilities, const Bin& bin) STIR_MUTABLE_CONST { // start_timers(); TODO, can't do this in a const member - + // set to empty probabilities.erase(); - if (cache_stores_only_basic_bins) - { + if (cache_stores_only_basic_bins) { // find basic bin - Bin basic_bin = bin; - unique_ptr symm_ptr = - symmetries_sptr->find_symmetry_operation_from_basic_bin(basic_bin); - + Bin basic_bin = bin; + unique_ptr symm_ptr = symmetries_sptr->find_symmetry_operation_from_basic_bin(basic_bin); + probabilities.set_bin(basic_bin); - // check if basic bin is in cache - if (get_cached_proj_matrix_elems_for_one_bin(probabilities) == - Succeeded::no) - { + // check if basic bin is in cache + if (get_cached_proj_matrix_elems_for_one_bin(probabilities) == Succeeded::no) { // call 'calculate' just for the basic bin calculate_proj_matrix_elems_for_one_bin(probabilities); #ifndef NDEBUG @@ -79,39 +69,29 @@ get_proj_matrix_elems_for_one_bin( #endif cache_proj_matrix_elems_for_one_bin(probabilities); } - if ( proj_data_info_sptr->is_tof_data() && - this->tof_enabled) - { - LORInAxialAndNoArcCorrSinogramCoordinates lor; - proj_data_info_sptr->get_LOR(lor, bin); - LORAs2Points lor2(lor); - probabilities.set_bin(bin); - // now apply TOF kernel and transform to original bin - apply_tof_kernel_and_symm_transformation(probabilities, lor2.p1(), lor2.p2(), symm_ptr); - } - else - { - // now transform to original bin - symm_ptr->transform_proj_matrix_elems_for_one_bin(probabilities); + if (proj_data_info_sptr->is_tof_data() && this->tof_enabled) { + LORInAxialAndNoArcCorrSinogramCoordinates lor; + proj_data_info_sptr->get_LOR(lor, bin); + LORAs2Points lor2(lor); + probabilities.set_bin(bin); + // now apply TOF kernel and transform to original bin + apply_tof_kernel_and_symm_transformation(probabilities, lor2.p1(), lor2.p2(), symm_ptr); + } else { + // now transform to original bin + symm_ptr->transform_proj_matrix_elems_for_one_bin(probabilities); } - } - else // !cache_stores_only_basic_bins + } else // !cache_stores_only_basic_bins { probabilities.set_bin(bin); - // check if in cache - if (get_cached_proj_matrix_elems_for_one_bin(probabilities) == - Succeeded::no) - { + // check if in cache + if (get_cached_proj_matrix_elems_for_one_bin(probabilities) == Succeeded::no) { // find basic bin - Bin basic_bin = bin; - unique_ptr symm_ptr = - symmetries_sptr->find_symmetry_operation_from_basic_bin(basic_bin); + Bin basic_bin = bin; + unique_ptr symm_ptr = symmetries_sptr->find_symmetry_operation_from_basic_bin(basic_bin); probabilities.set_bin(basic_bin); // check if basic bin is in cache - if (get_cached_proj_matrix_elems_for_one_bin(probabilities) == - Succeeded::no) - { + if (get_cached_proj_matrix_elems_for_one_bin(probabilities) == Succeeded::no) { // call 'calculate' just for the basic bin calculate_proj_matrix_elems_for_one_bin(probabilities); #ifndef NDEBUG @@ -119,85 +99,73 @@ get_proj_matrix_elems_for_one_bin( #endif cache_proj_matrix_elems_for_one_bin(probabilities); } - if ( proj_data_info_sptr->is_tof_data() && - this->tof_enabled) - { - LORInAxialAndNoArcCorrSinogramCoordinates lor; - proj_data_info_sptr->get_LOR(lor, bin); - LORAs2Points lor2(lor); - probabilities.set_bin(bin); - // now apply TOF kernel and transform to original bin - apply_tof_kernel_and_symm_transformation(probabilities, lor2.p1(), lor2.p2(), symm_ptr); + if (proj_data_info_sptr->is_tof_data() && this->tof_enabled) { + LORInAxialAndNoArcCorrSinogramCoordinates lor; + proj_data_info_sptr->get_LOR(lor, bin); + LORAs2Points lor2(lor); + probabilities.set_bin(bin); + // now apply TOF kernel and transform to original bin + apply_tof_kernel_and_symm_transformation(probabilities, lor2.p1(), lor2.p2(), symm_ptr); + } else { + // now transform to original bin + symm_ptr->transform_proj_matrix_elems_for_one_bin(probabilities); } - else - { - // now transform to original bin - symm_ptr->transform_proj_matrix_elems_for_one_bin(probabilities); - } - cache_proj_matrix_elems_for_one_bin(probabilities); + cache_proj_matrix_elems_for_one_bin(probabilities); } - } + } // stop_timers(); TODO, can't do this in a const member } void ProjMatrixByBin::apply_tof_kernel_and_symm_transformation(ProjMatrixElemsForOneBin& probabilities, - const CartesianCoordinate3D& point1, - const CartesianCoordinate3D& point2, - const unique_ptr& symm_ptr) STIR_MUTABLE_CONST -{ - - CartesianCoordinate3D voxel_center; - float new_value = 0.f; - float low_dist = 0.f; - float high_dist = 0.f; + const CartesianCoordinate3D& point1, + const CartesianCoordinate3D& point2, + const unique_ptr& symm_ptr) STIR_MUTABLE_CONST { - // The direction can be from 1 -> 2 depending on the bin sign. - const CartesianCoordinate3D middle = (point1 + point2)*0.5f; - const CartesianCoordinate3D diff = point2 - middle; + CartesianCoordinate3D voxel_center; + float new_value = 0.f; + float low_dist = 0.f; + float high_dist = 0.f; - const float lor_length = 1.f / (std::sqrt(diff.x() * diff.x() + - diff.y() * diff.y() + - diff.z() * diff.z())); + // The direction can be from 1 -> 2 depending on the bin sign. + const CartesianCoordinate3D middle = (point1 + point2) * 0.5f; + const CartesianCoordinate3D diff = point2 - middle; - for (ProjMatrixElemsForOneBin::iterator element_ptr = probabilities.begin(); - element_ptr != probabilities.end(); ++element_ptr) - { - Coordinate3D c(element_ptr->get_coords()); - symm_ptr->transform_image_coordinates(c); + const float lor_length = 1.f / (std::sqrt(diff.x() * diff.x() + diff.y() * diff.y() + diff.z() * diff.z())); - voxel_center = - image_info_sptr->get_physical_coordinates_for_indices (c); + for (ProjMatrixElemsForOneBin::iterator element_ptr = probabilities.begin(); element_ptr != probabilities.end(); + ++element_ptr) { + Coordinate3D c(element_ptr->get_coords()); + symm_ptr->transform_image_coordinates(c); - project_point_on_a_line(point1, point2, voxel_center); + voxel_center = image_info_sptr->get_physical_coordinates_for_indices(c); - const CartesianCoordinate3D x = voxel_center - middle; + project_point_on_a_line(point1, point2, voxel_center); - const float d2 = -inner_product(x, diff) * lor_length; + const CartesianCoordinate3D x = voxel_center - middle; - low_dist = ((proj_data_info_sptr->tof_bin_boundaries_mm[probabilities.get_bin_ptr()->timing_pos_num()].low_lim - d2) * r_sqrt2_gauss_sigma); - high_dist = ((proj_data_info_sptr->tof_bin_boundaries_mm[probabilities.get_bin_ptr()->timing_pos_num()].high_lim - d2) * r_sqrt2_gauss_sigma); + const float d2 = -inner_product(x, diff) * lor_length; + low_dist = ((proj_data_info_sptr->tof_bin_boundaries_mm[probabilities.get_bin_ptr()->timing_pos_num()].low_lim - d2) * + r_sqrt2_gauss_sigma); + high_dist = ((proj_data_info_sptr->tof_bin_boundaries_mm[probabilities.get_bin_ptr()->timing_pos_num()].high_lim - d2) * + r_sqrt2_gauss_sigma); - if ((low_dist >= 4.f && high_dist >= 4.f) || - (low_dist <= -4.f && high_dist <= -4.f)) - { - *element_ptr = ProjMatrixElemsForOneBin::value_type(c, 0.0f); - continue; - } - - get_tof_value(low_dist, high_dist, new_value); - new_value *= element_ptr->get_value(); - *element_ptr = ProjMatrixElemsForOneBin::value_type(c, new_value); + if ((low_dist >= 4.f && high_dist >= 4.f) || (low_dist <= -4.f && high_dist <= -4.f)) { + *element_ptr = ProjMatrixElemsForOneBin::value_type(c, 0.0f); + continue; } + + get_tof_value(low_dist, high_dist, new_value); + new_value *= element_ptr->get_value(); + *element_ptr = ProjMatrixElemsForOneBin::value_type(c, new_value); + } } void -ProjMatrixByBin:: -get_tof_value(const float d1, const float d2, float& val) const -{ - val = 0.5f * (erf(d2) - erf(d1)); +ProjMatrixByBin::get_tof_value(const float d1, const float d2, float& val) const { + val = 0.5f * (erf(d2) - erf(d1)); } END_NAMESPACE_STIR diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBinFromFile.h b/src/include/stir/recon_buildblock/ProjMatrixByBinFromFile.h index 197b9371a4..d9b88fefb8 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBinFromFile.h +++ b/src/include/stir/recon_buildblock/ProjMatrixByBinFromFile.h @@ -20,7 +20,7 @@ \file \ingroup projection - \brief stir::ProjMatrixByBinFromFile's definition + \brief stir::ProjMatrixByBinFromFile's definition \author Kris Thielemans @@ -36,21 +36,20 @@ #include "stir/shared_ptr.h" #include - - START_NAMESPACE_STIR -template class DiscretisedDensity; +template +class DiscretisedDensity; class Bin; /*! \ingroup projection \brief Reads/writes a projection matrix from/to file The only supported file format consists of an Interfile-type header - and a binary file which stores the 'basic' elements in a sparse form, + and a binary file which stores the 'basic' elements in a sparse form, i.e. only the elements that cannot by constructed via symmetries. - \todo this class currently only works with VoxelsOnCartesianGrid. + \todo this class currently only works with VoxelsOnCartesianGrid. To fix this, we would need a DiscretisedDensityInfo class, and be able to have constructed the appropriate symmetries object by parsing the .par file @@ -61,62 +60,52 @@ class Bin; Version := 1.0 symmetries type := PET_CartesianGrid PET_CartesianGrid symmetries parameters:= - do_symmetry_90degrees_min_phi:= - do_symmetry_180degrees_min_phi:= - do_symmetry_swap_segment:= - do_symmetry_swap_s:= - do_symmetry_shift_z:= - End PET_CartesianGrid symmetries parameters:= + do_symmetry_90degrees_min_phi:= + do_symmetry_180degrees_min_phi:= + do_symmetry_swap_segment:= + do_symmetry_swap_s:= + do_symmetry_shift_z:= + End PET_CartesianGrid symmetries parameters:= ; example projection data of the same dimensions as used when constructing the matrix template proj data filename:= ; example image of the same dimensions as used when constructing the matrix template density filename:= ; binary data with projection matrix elements - data_filename:= + data_filename:= End ProjMatrixByBinFromFile Parameters:= \endverbatim */ -class ProjMatrixByBinFromFile : - public RegisteredParsingObject< - ProjMatrixByBinFromFile, - ProjMatrixByBin, - ProjMatrixByBin - > -{ -public : +class ProjMatrixByBinFromFile : public RegisteredParsingObject { +public: //! Name which will be used when parsing a ProjMatrixByBin object - static const char * const registered_name; - + static const char* const registered_name; + //! Writes a projection matrix to file in a format such that this class can read it back /*! Currently this will write an interfile-type header, a file with the binary data, a template image and template sinogram. You will need all 4 to be able to read the matrix back in. */ -static Succeeded - write_to_file(const std::string& output_filename_prefix, - const ProjMatrixByBin& proj_matrix, - const shared_ptr& proj_data_info_sptr, - const DiscretisedDensity<3,float>& template_density); - + static Succeeded write_to_file(const std::string& output_filename_prefix, const ProjMatrixByBin& proj_matrix, + const shared_ptr& proj_data_info_sptr, + const DiscretisedDensity<3, float>& template_density); + //! Default constructor (calls set_defaults()) ProjMatrixByBinFromFile(); //! Checks all necessary geometric info - virtual void set_up( - const shared_ptr& proj_data_info_ptr, - const shared_ptr >& density_info_ptr // TODO should be Info only - ); + virtual void set_up(const shared_ptr& proj_data_info_ptr, + const shared_ptr>& density_info_ptr // TODO should be Info only + ); virtual ProjMatrixByBinFromFile* clone() const; private: - std::string parsed_version; std::string template_density_filename; std::string template_proj_data_filename; std::string data_filename; - + std::string symmetries_type; // should be in symmetries bool do_symmetry_90degrees_min_phi; @@ -128,28 +117,20 @@ static Succeeded // TODO this only works as long as we only have VoxelsOnCartesianGrid // explicitly list necessary members for image details (should use an Info object instead) CartesianCoordinate3D voxel_size; - CartesianCoordinate3D origin; + CartesianCoordinate3D origin; IndexRange<3> densel_range; - shared_ptr proj_data_info_ptr; - - virtual void - calculate_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin&) const; + virtual void calculate_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin&) const; virtual void set_defaults(); virtual void initialise_keymap(); virtual bool post_processing(); Succeeded read_data(); - }; END_NAMESPACE_STIR #endif - - - diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBinSPECTUB.h b/src/include/stir/recon_buildblock/ProjMatrixByBinSPECTUB.h index e5714cadd9..afad02495a 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBinSPECTUB.h +++ b/src/include/stir/recon_buildblock/ProjMatrixByBinSPECTUB.h @@ -19,7 +19,7 @@ \file \ingroup projection - \brief stir::ProjMatrixByBinSPECTUB's definition + \brief stir::ProjMatrixByBinSPECTUB's definition \author Berta Marti Fuster \author Kris Thielemans @@ -35,28 +35,28 @@ #include "stir/shared_ptr.h" #include - #include "stir/recon_buildblock/SPECTUB_Tools.h" START_NAMESPACE_STIR -template class DiscretisedDensity; +template +class DiscretisedDensity; class Bin; /*! \ingroup projection \brief generates projection matrix for SPECT studies This functionality is described in - Berta Marti Fuster, Carles Falcon, Charalampos Tsoumpas, Lefteris Livieratos, Pablo Aguiar, Albert Cot, Domenec Ros and Kris Thielemans, - Integration of advanced 3D SPECT modeling into the open-source STIR framework, - Med. Phys. 40, 092502 (2013); http://dx.doi.org/10.1118/1.4816676 + Berta Marti Fuster, Carles Falcon, Charalampos Tsoumpas, Lefteris Livieratos, Pablo Aguiar, Albert Cot, Domenec Ros and Kris +Thielemans, Integration of advanced 3D SPECT modeling into the open-source STIR framework, Med. Phys. 40, 092502 (2013); +http://dx.doi.org/10.1118/1.4816676 - \warning this class currently only works with VoxelsOnCartesianGrid. + \warning this class currently only works with VoxelsOnCartesianGrid. \par Sample parameter file \verbatim - Projection Matrix By Bin SPECT UB Parameters:= + Projection Matrix By Bin SPECT UB Parameters:= ; width of PSF maximum number of sigmas:= 2.0 @@ -69,8 +69,8 @@ class Bin; collimator sigma 0(cm) := 0.1466 ;Attenuation correction { Simple // Full // No } - attenuation type := Simple - ;Values in attenuation map in cm-1 + attenuation type := Simple + ;Values in attenuation map in cm-1 attenuation map := attMapRec.hv ;Mask properties { Cylinder // Attenuation Map // Explicit Mask // No} @@ -84,17 +84,11 @@ End Projection Matrix By Bin SPECT UB Parameters:= \endverbatim */ -class ProjMatrixByBinSPECTUB : - public RegisteredParsingObject< - ProjMatrixByBinSPECTUB, - ProjMatrixByBin, - ProjMatrixByBin - > -{ - public : +class ProjMatrixByBinSPECTUB : public RegisteredParsingObject { +public: //! Name which will be used when parsing a ProjMatrixByBin object - static const char * const registered_name; - + static const char* const registered_name; + //! Default constructor (calls set_defaults()) ProjMatrixByBinSPECTUB(); @@ -102,10 +96,9 @@ class ProjMatrixByBinSPECTUB : ~ProjMatrixByBinSPECTUB(); //! Checks all necessary geometric info - virtual void set_up( - const shared_ptr& proj_data_info_ptr, - const shared_ptr >& density_info_ptr // TODO should be Info only - ); + virtual void set_up(const shared_ptr& proj_data_info_ptr, + const shared_ptr>& density_info_ptr // TODO should be Info only + ); bool get_keep_all_views_in_cache() const; //! Enable keeping the matrix in memory @@ -122,8 +115,7 @@ class ProjMatrixByBinSPECTUB : You have to call set_up() after this (unless the value didn't change). */ void set_attenuation_type(const std::string& value); - shared_ptr > - get_attenuation_image_sptr() const; + shared_ptr> get_attenuation_image_sptr() const; //! Sets attenuation image /*! The image has to have same characteristics as the emission image currently. @@ -132,10 +124,8 @@ class ProjMatrixByBinSPECTUB : You have to call set_up() after this. */ - void - set_attenuation_image_sptr(const shared_ptr > value); - void - set_attenuation_image_sptr(const std::string& value); + void set_attenuation_image_sptr(const shared_ptr> value); + void set_attenuation_image_sptr(const std::string& value); //! Set the parameters for the depth-dependent resolution model /*! The detector and collimator blurring is modelled as a Gaussian with sigma dependent on the @@ -152,17 +142,15 @@ class ProjMatrixByBinSPECTUB : You have to call set_up() after this. */ - void - set_resolution_model(const float collimator_sigma_0_in_mm, const float collimator_slope_in_mm, const bool full_3D = true); - - //Alex - //Fix to compile, missing function definition in header - ProjMatrixByBinSPECTUB * clone() const; + void set_resolution_model(const float collimator_sigma_0_in_mm, const float collimator_slope_in_mm, const bool full_3D = true); - private: + // Alex + // Fix to compile, missing function definition in header + ProjMatrixByBinSPECTUB* clone() const; +private: // parameters that will be parsed - + float minimum_weight; float maximum_number_of_sigmas; float spatial_resolution_PSF; @@ -177,47 +165,43 @@ class ProjMatrixByBinSPECTUB : // explicitly list necessary members for image details (should use an Info object instead) CartesianCoordinate3D voxel_size; - CartesianCoordinate3D origin; + CartesianCoordinate3D origin; IndexRange<3> densel_range; shared_ptr proj_data_info_ptr; bool already_setup; - - virtual void - calculate_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin&) const; + virtual void calculate_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin&) const; virtual void set_defaults(); virtual void initialise_keymap(); virtual bool post_processing(); - shared_ptr > attenuation_image_sptr; + shared_ptr> attenuation_image_sptr; // wm_SPECT starts here --------------------------------------------------------------------------------------------- - bool *msk_3d; //!< voxels to be included in matrix (no weight calculated outside the mask) - bool *msk_2d; //!< 2d collapse of msk_3d. + bool* msk_3d; //!< voxels to be included in matrix (no weight calculated outside the mask) + bool* msk_2d; //!< 2d collapse of msk_3d. //... variables for estimated sizes of arrays to allocate ................................ - int **NITEMS; //!< number of non-zero elements for each weight matrix row + int** NITEMS; //!< number of non-zero elements for each weight matrix row //... user defined structures (types defined in SPECTUB_Tools.h) ..................................... - SPECTUB::volume_type vol; //!< structure with volume (image) information - SPECTUB::proj_type prj; //!< structure with projection information + SPECTUB::volume_type vol; //!< structure with volume (image) information + SPECTUB::proj_type prj; //!< structure with projection information - SPECTUB::voxel_type vox; //!< structure with voxel information - SPECTUB::bin_type bin; //!< structure with bin information + SPECTUB::voxel_type vox; //!< structure with voxel information + SPECTUB::bin_type bin; //!< structure with bin information - SPECTUB::angle_type * ang; //!< structure with angle indices, values, ratios and voxel projections - float *attmap; //!< attenuation map (copied as float array) + SPECTUB::angle_type* ang; //!< structure with angle indices, values, ratios and voxel projections + float* attmap; //!< attenuation map (copied as float array) - SPECTUB::discrf_type gaussdens; //!< structure with gaussian density function + SPECTUB::discrf_type gaussdens; //!< structure with gaussian density function int maxszb; - void compute_one_subset(const int kOS) const; void delete_UB_SPECT_arrays(); mutable std::vector subset_already_processed; @@ -226,6 +210,3 @@ class ProjMatrixByBinSPECTUB : END_NAMESPACE_STIR #endif - - - diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBinUsingInterpolation.h b/src/include/stir/recon_buildblock/ProjMatrixByBinUsingInterpolation.h index 8538d83738..5f9aa3d6ba 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBinUsingInterpolation.h +++ b/src/include/stir/recon_buildblock/ProjMatrixByBinUsingInterpolation.h @@ -20,7 +20,7 @@ \file \ingroup projection - \brief stir::ProjMatrixByBinUsingInterpolation's definition + \brief stir::ProjMatrixByBinUsingInterpolation's definition \author Kris Thielemans @@ -34,49 +34,42 @@ #include "stir/CartesianCoordinate3D.h" #include "stir/shared_ptr.h" - - START_NAMESPACE_STIR -template class DiscretisedDensity; +template +class DiscretisedDensity; class Bin; /*! \ingroup projection \brief Computes projection matrix elements for VoxelsOnCartesianGrid images - by using an interpolation model. + by using an interpolation model. This class implements a projection model that interpolates in projection space. - When used for back-projection, it should give the same results as + When used for back-projection, it should give the same results as BackProjectorByByUsingInterpolation, but is probably much slower. The current implementation uses some quite generic code to handle symmetries, but - is very very slow to compute the elements. Once they are cached, performance is + is very very slow to compute the elements. Once they are cached, performance is as usual of course. \warning Preliminary code, not tested to usual STIR standards. */ -class ProjMatrixByBinUsingInterpolation : - public RegisteredParsingObject< - ProjMatrixByBinUsingInterpolation, - ProjMatrixByBin, - ProjMatrixByBin - > -{ -public : - //! Name which will be used when parsing a ProjMatrixByBin object - static const char * const registered_name; +class ProjMatrixByBinUsingInterpolation + : public RegisteredParsingObject { +public: + //! Name which will be used when parsing a ProjMatrixByBin object + static const char* const registered_name; //! Default constructor (calls set_defaults()) ProjMatrixByBinUsingInterpolation(); //! Stores all necessary geometric info /*! Note that the density_info_ptr is not stored in this object. It's only used to get some info on sizes etc. - */ - virtual void set_up( - const shared_ptr& proj_data_info_ptr, - const shared_ptr >& density_info_ptr // TODO should be Info only - ); + */ + virtual void set_up(const shared_ptr& proj_data_info_ptr, + const shared_ptr>& density_info_ptr // TODO should be Info only + ); virtual ProjMatrixByBinUsingInterpolation* clone() const; @@ -89,97 +82,80 @@ public : // explicitly list necessary members for image details (should use an Info object instead) CartesianCoordinate3D voxel_size; - CartesianCoordinate3D origin; + CartesianCoordinate3D origin; IndexRange<3> densel_range; - shared_ptr proj_data_info_ptr; // for Jacobian - const ProjDataInfoCylindrical& - proj_data_info_cyl() const - { return static_cast(*proj_data_info_ptr); } -/*! - \brief - The next class is used - to take geometric things - into account. It also includes some normalisation. (internal use only). - - \internal - - Use as follows: - TODO incorrect (also in original) - \code - const JacobianForIntBP jacobian(*(segment.scanner)); - jacobian(segment.get_average_delta(), s+ 0.5); - \endcode - */ - - -class JacobianForIntBP -{ -private: - // store some scanner related data to avoid recomputation - float R2; - float ring_spacing2; - bool arccor; - // total normalisation of backprojection, 3 factors: - // (_Pi/scanner.num_views) for discretisation of integral over phi - // scanner.ring_spacing for discretisation of integral over delta - // normalisation of projection space integral: 1/(2 Pi) - - float backprojection_normalisation; - - bool use_exact_Jacobian_now; - -public: - // default constructor needed as now member of projector class (better to make set_up) - JacobianForIntBP() {} - explicit JacobianForIntBP(const ProjDataInfoCylindrical* proj_data_info_ptr, bool exact); - // s in mm here! - float operator()(const float delta, const float s) const - { - float tmp; - if (use_exact_Jacobian_now) - tmp = 4*(R2 - s*s); - else - tmp = 4*R2; - if (!arccor) - tmp *= sqrt(tmp); - return - (arccor ? tmp : pow(tmp,1.5F)) / - pow(tmp + ring_spacing2*delta*delta, 1.5F)* backprojection_normalisation; - } -}; - + const ProjDataInfoCylindrical& proj_data_info_cyl() const { + return static_cast(*proj_data_info_ptr); + } + /*! + \brief + The next class is used + to take geometric things + into account. It also includes some normalisation. (internal use only). + + \internal + + Use as follows: + TODO incorrect (also in original) + \code + const JacobianForIntBP jacobian(*(segment.scanner)); + jacobian(segment.get_average_delta(), s+ 0.5); + \endcode + */ + + class JacobianForIntBP { + private: + // store some scanner related data to avoid recomputation + float R2; + float ring_spacing2; + bool arccor; + // total normalisation of backprojection, 3 factors: + // (_Pi/scanner.num_views) for discretisation of integral over phi + // scanner.ring_spacing for discretisation of integral over delta + // normalisation of projection space integral: 1/(2 Pi) + + float backprojection_normalisation; + + bool use_exact_Jacobian_now; + + public: + // default constructor needed as now member of projector class (better to make set_up) + JacobianForIntBP() {} + explicit JacobianForIntBP(const ProjDataInfoCylindrical* proj_data_info_ptr, bool exact); + // s in mm here! + float operator()(const float delta, const float s) const { + float tmp; + if (use_exact_Jacobian_now) + tmp = 4 * (R2 - s * s); + else + tmp = 4 * R2; + if (!arccor) + tmp *= sqrt(tmp); + return (arccor ? tmp : pow(tmp, 1.5F)) / pow(tmp + ring_spacing2 * delta * delta, 1.5F) * backprojection_normalisation; + } + }; JacobianForIntBP jacobian; bool use_piecewise_linear_interpolation_now; bool use_exact_Jacobian_now; - virtual void - calculate_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin&) const; + virtual void calculate_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin&) const; virtual void set_defaults(); virtual void initialise_keymap(); virtual bool post_processing(); - float - get_element(const Bin& bin, - const CartesianCoordinate3D& densel_ctr) const; - private: - void - find_tang_ax_pos_diff(float& tang_pos_diff, - float& ax_pos_diff, - const Bin& bin, - const CartesianCoordinate3D& point) const; - + float get_element(const Bin& bin, const CartesianCoordinate3D& densel_ctr) const; + +private: + void find_tang_ax_pos_diff(float& tang_pos_diff, float& ax_pos_diff, const Bin& bin, + const CartesianCoordinate3D& point) const; }; END_NAMESPACE_STIR #endif - - - diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBinUsingRayTracing.h b/src/include/stir/recon_buildblock/ProjMatrixByBinUsingRayTracing.h index 29076c14a6..22c3ec446d 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBinUsingRayTracing.h +++ b/src/include/stir/recon_buildblock/ProjMatrixByBinUsingRayTracing.h @@ -2,7 +2,7 @@ \file \ingroup projection - \brief stir::ProjMatrixByBinUsingRayTracing's definition + \brief stir::ProjMatrixByBinUsingRayTracing's definition \author Kris Thielemans \author Mustapha Sadki @@ -34,20 +34,19 @@ #include "stir/CartesianCoordinate3D.h" #include "stir/shared_ptr.h" - - START_NAMESPACE_STIR -template class DiscretisedDensity; +template +class DiscretisedDensity; class ProjDataInfo; /*! \ingroup projection \brief Computes projection matrix elements for VoxelsOnCartesianGrid images - by using a Length of Intersection (LOI) model. + by using a Length of Intersection (LOI) model. Currently, the LOIs are divided by voxel_size.x(), unless NEWSCALE is - #defined during compilation time of ProjMatrixByBinUsingRayTracing.cxx. + #defined during compilation time of ProjMatrixByBinUsingRayTracing.cxx. It is possible to use multiple LORs in tangential direction. The result will then be the average of the various contributions. Currently all these @@ -58,16 +57,16 @@ class ProjDataInfo; 2 or 3 LORs are used, to avoid missing voxels. (TODOdoc describe how). If use_actual_detector_boundaries is set (currently only possible - for non-arccorrected data, without mashing and/or axial compression), - the detectors are assumed to be on a cylinder. If only a single LOR - in tangential direction is used for ray tracing, + for non-arccorrected data, without mashing and/or axial compression), + the detectors are assumed to be on a cylinder. If only a single LOR + in tangential direction is used for ray tracing, the centre of those detectors is used, which is slightly different from the 'usual' LOR (due to interleaving of the sinogram). When multiple LORs are used, the actual detector sizes are used, such that the resulting strip is twice as wide. It is possible to use a cylindrical or cuboid FOV (in the latter case it - is going to be square in transaxial direction). In both cases, the FOV is + is going to be square in transaxial direction). In both cases, the FOV is slightly 'inside' the image (i.e. it is about 1 voxel at each side smaller than the maximum possible). @@ -81,7 +80,7 @@ class ProjDataInfo; Enabling more symmetries (from DataSymmetriesForBins_PET_CartesianGrid) means that less memory is needed to store the matrix (when caching), less time to compute it, but using the matrix might be slightly slower. By default, as many symmetries as possible are enabled. - + \par Parsing parameters The following parameters can be set (default values are indicated): @@ -98,7 +97,7 @@ class ProjDataInfo; do_symmetry_shift_z := 1 End Ray Tracing Matrix Parameters := \endverbatim - + \par Implementation details The implementation uses RayTraceVoxelsOnCartesianGrid(). @@ -108,37 +107,31 @@ class ProjDataInfo; \warning Only appropriate for VoxelsOnCartesianGrid type of images (otherwise error() will be called). - - \warning Care should be taken to select the number of rays in tangential direction + + \warning Care should be taken to select the number of rays in tangential direction such that the sampling is at least as small as the x,y voxel sizes. \warning Current implementation assumes that z voxel size is either smaller than or exactly twice the sampling in axial direction of the segments. - \bug Currently, strange things happen if the z voxel size is not exactly equal to half + \bug Currently, strange things happen if the z voxel size is not exactly equal to half the ring spacing of the scanner. */ -class ProjMatrixByBinUsingRayTracing : - public RegisteredParsingObject< - ProjMatrixByBinUsingRayTracing, - ProjMatrixByBin, - ProjMatrixByBin - > -{ -public : - //! Name which will be used when parsing a ProjMatrixByBin object - static const char * const registered_name; +class ProjMatrixByBinUsingRayTracing + : public RegisteredParsingObject { +public: + //! Name which will be used when parsing a ProjMatrixByBin object + static const char* const registered_name; //! Default constructor (calls set_defaults()) ProjMatrixByBinUsingRayTracing(); //! Stores all necessary geometric info /*! Note that the density_info_ptr is not stored in this object. It's only used to get some info on sizes etc. - */ - virtual void set_up( - const shared_ptr& proj_data_info_ptr, - const shared_ptr >& density_info_ptr // TODO should be Info only - ); + */ + virtual void set_up(const shared_ptr& proj_data_info_ptr, + const shared_ptr>& density_info_ptr // TODO should be Info only + ); virtual ProjMatrixByBinUsingRayTracing* clone() const; @@ -197,26 +190,19 @@ public : // explicitly list necessary members for image details (should use an Info object instead) CartesianCoordinate3D voxel_size; - CartesianCoordinate3D origin; + CartesianCoordinate3D origin; CartesianCoordinate3D min_index; CartesianCoordinate3D max_index; shared_ptr proj_data_info_ptr; + virtual void calculate_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin&) const; - virtual void - calculate_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin&) const; - - virtual void set_defaults(); - virtual void initialise_keymap(); - virtual bool post_processing(); - + virtual void set_defaults(); + virtual void initialise_keymap(); + virtual bool post_processing(); }; END_NAMESPACE_STIR #endif - - - diff --git a/src/include/stir/recon_buildblock/ProjMatrixElemsForOneBin.h b/src/include/stir/recon_buildblock/ProjMatrixElemsForOneBin.h index d0b6e13eac..5fc484dc00 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixElemsForOneBin.h +++ b/src/include/stir/recon_buildblock/ProjMatrixElemsForOneBin.h @@ -8,14 +8,14 @@ \file \ingroup projection - + \brief Declaration of class stir::ProjMatrixElemsForOneBin \author Nikos Efthimiou \author Mustapha Sadki \author Kris Thielemans \author PARAPET project - + */ /* Copyright (C) 2000 PARAPET partners @@ -36,8 +36,6 @@ See STIR/LICENSE.txt for details */ - - #include "stir/recon_buildblock/ProjMatrixElemsForOneBinValue.h" #include "stir/Bin.h" #include @@ -46,10 +44,8 @@ START_NAMESPACE_STIR class Succeeded; class RelatedBins; -template class DiscretisedDensity; - - - +template +class DiscretisedDensity; /*! \brief This stores the non-zero projection matrix elements @@ -65,22 +61,21 @@ template class DiscretisedDensity; \todo It might be useful to template this class in terms of the - element-type as well. That way, we could have 'compact' + element-type as well. That way, we could have 'compact' elements, efficient elements, etc. However, doing this will probably only be useful if all ProjMatrixByBin classes are then templated as well, which would be a pain. */ -/* +/* it might be a bit faster to derive this (privately) from std::vector as opposed to having a member of that type. TODO: check */ -class ProjMatrixElemsForOneBin -{ +class ProjMatrixElemsForOneBin { public: - /*! \brief Recommended way to call the type of the elements, instead of + /*! \brief Recommended way to call the type of the elements, instead of referring to the actual classname. Think about this name as 'the type of the value of a ProjMatrixElemsForOneBin::iterator *'. @@ -88,14 +83,15 @@ class ProjMatrixElemsForOneBin This typedef is also required for 'standard' iterators. */ typedef ProjMatrixElemsForOneBinValue value_type; + private: //! shorthand to keep typedefs below concise typedef std::vector Element_vector; -public: +public: //! typedefs for iterator support - typedef Element_vector::iterator iterator; - typedef Element_vector::const_iterator const_iterator; + typedef Element_vector::iterator iterator; + typedef Element_vector::const_iterator const_iterator; typedef Element_vector::size_type size_type; typedef Element_vector::difference_type difference_type; typedef std::random_access_iterator_tag iterator_category; @@ -103,22 +99,21 @@ class ProjMatrixElemsForOneBin typedef value_type& reference; typedef const value_type& const_reference; - //! constructor /*! \param bin effectively calls set_bin(bin) \param default_capacity effectively calls reserve(default_capacity) */ - explicit ProjMatrixElemsForOneBin(const Bin& bin= Bin(), const int default_capacity = 0); - - /* rely on compiler-generated versions + explicit ProjMatrixElemsForOneBin(const Bin& bin = Bin(), const int default_capacity = 0); + + /* rely on compiler-generated versions ProjMatrixElemsForOneBin( const ProjMatrixElemsForOneBin&); ProjMatrixElemsForOneBin& operator=(const ProjMatrixElemsForOneBin&) ; */ //! check if each voxel occurs only once Succeeded check_state() const; - + //! get the bin coordinates corresponding to this row inline Bin get_bin() const; //! and set the bin coordinates @@ -127,37 +122,37 @@ class ProjMatrixElemsForOneBin inline Bin* get_bin_ptr(); //! functions for allowing iterator access - inline iterator begin() ; - inline const_iterator begin() const; + inline iterator begin(); + inline const_iterator begin() const; inline iterator end(); inline const_iterator end() const; //! reset lor to 0 length void erase(); //! add a new value_type object at the end - /*! - \warning For future compatibility, it is required - (but not checked) that the elements are added such + /*! + \warning For future compatibility, it is required + (but not checked) that the elements are added such that calling sort() after the push_back() would not change the order of the elements. Otherwise, schemes for 'incremental' storing of coordinates would require too much overhead. */ - inline void push_back( const value_type&); + inline void push_back(const value_type&); //! reserve enough space for max_number elements (but don't fill them in) void reserve(size_type max_number); //! number of non-zero elements - inline size_type size() const; + inline size_type size() const; //! number of allocated elements size_type capacity() const; //! Multiplies all values with a constant - ProjMatrixElemsForOneBin& operator*=(const float d); + ProjMatrixElemsForOneBin& operator*=(const float d); //! Divides all values with a constant - ProjMatrixElemsForOneBin& operator/=(const float d); - + ProjMatrixElemsForOneBin& operator/=(const float d); + //! Sort the elements on coordinates of the voxels /*! Uses value_type::coordinates_less as ordering function. - */ + */ void sort(); //! merge 2nd lor into current object @@ -165,7 +160,7 @@ class ProjMatrixElemsForOneBin \warning This currently modifies the argument \c lor. */ // TODO make sure we can have a const argument - void merge(ProjMatrixElemsForOneBin &lor ); + void merge(ProjMatrixElemsForOneBin& lor); //! Compare 2 lors to see if they are equal /*! \warning Compares element by element. Does not sort first or so. @@ -174,15 +169,13 @@ class ProjMatrixElemsForOneBin \warning this is a fairly CPU intensive operation. */ bool operator==(const ProjMatrixElemsForOneBin&) const; - //! Compare 2 lors + //! Compare 2 lors bool operator!=(const ProjMatrixElemsForOneBin&) const; - #if 0 void write(std::fstream&fst) const; void read(std::fstream&fst ); #endif - //! Return sum of squares of all values /*! \warning This sums over all elements in the LOR, irrespective if they @@ -192,31 +185,24 @@ class ProjMatrixElemsForOneBin //******************** projection operations ********************// - //! back project a single bin - void back_project(DiscretisedDensity<3,float>&, - const Bin&) const; + //! back project a single bin + void back_project(DiscretisedDensity<3, float>&, const Bin&) const; //! forward project into a single bin - void forward_project(Bin&, - const DiscretisedDensity<3,float>&) const; - //! back project related bins - void back_project(DiscretisedDensity<3,float>&, - const RelatedBins&) const; + void forward_project(Bin&, const DiscretisedDensity<3, float>&) const; + //! back project related bins + void back_project(DiscretisedDensity<3, float>&, const RelatedBins&) const; //! forward project related bins - void forward_project(RelatedBins&, - const DiscretisedDensity<3,float>&) const; + void forward_project(RelatedBins&, const DiscretisedDensity<3, float>&) const; - private: - std::vector elements; + std::vector elements; Bin bin; - //! remove a single value_type inline iterator erase(iterator it); }; - END_NAMESPACE_STIR #include "stir/recon_buildblock/ProjMatrixElemsForOneBin.inl" diff --git a/src/include/stir/recon_buildblock/ProjMatrixElemsForOneBin.inl b/src/include/stir/recon_buildblock/ProjMatrixElemsForOneBin.inl index 4ab89b9a9e..9a7afbd632 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixElemsForOneBin.inl +++ b/src/include/stir/recon_buildblock/ProjMatrixElemsForOneBin.inl @@ -33,64 +33,55 @@ START_NAMESPACE_STIR -Bin -ProjMatrixElemsForOneBin:: -get_bin() const -{ +Bin +ProjMatrixElemsForOneBin::get_bin() const { return bin; } Bin* -ProjMatrixElemsForOneBin:: -get_bin_ptr() -{ +ProjMatrixElemsForOneBin::get_bin_ptr() { return &bin; } void -ProjMatrixElemsForOneBin:: -set_bin(const Bin& new_bin) -{ +ProjMatrixElemsForOneBin::set_bin(const Bin& new_bin) { bin = new_bin; } -void ProjMatrixElemsForOneBin::push_back( const ProjMatrixElemsForOneBin::value_type& el) -{ - elements.push_back(el); +void +ProjMatrixElemsForOneBin::push_back(const ProjMatrixElemsForOneBin::value_type& el) { + elements.push_back(el); } - -ProjMatrixElemsForOneBin::size_type -ProjMatrixElemsForOneBin:: -size() const -{ +ProjMatrixElemsForOneBin::size_type +ProjMatrixElemsForOneBin::size() const { return elements.size(); } -ProjMatrixElemsForOneBin::iterator -ProjMatrixElemsForOneBin::begin() - { return elements.begin(); } - -ProjMatrixElemsForOneBin::const_iterator -ProjMatrixElemsForOneBin:: -begin() const - { return elements.begin(); }; - -ProjMatrixElemsForOneBin::iterator -ProjMatrixElemsForOneBin:: -end() - { return elements.end(); }; - -ProjMatrixElemsForOneBin::const_iterator -ProjMatrixElemsForOneBin:: -end() const - { return elements.end(); }; - -ProjMatrixElemsForOneBin::iterator -ProjMatrixElemsForOneBin:: -erase(iterator it){ - return elements.erase(it); - } +ProjMatrixElemsForOneBin::iterator +ProjMatrixElemsForOneBin::begin() { + return elements.begin(); +} + +ProjMatrixElemsForOneBin::const_iterator +ProjMatrixElemsForOneBin::begin() const { + return elements.begin(); +}; + +ProjMatrixElemsForOneBin::iterator +ProjMatrixElemsForOneBin::end() { + return elements.end(); +}; + +ProjMatrixElemsForOneBin::const_iterator +ProjMatrixElemsForOneBin::end() const { + return elements.end(); +}; + +ProjMatrixElemsForOneBin::iterator +ProjMatrixElemsForOneBin::erase(iterator it) { + return elements.erase(it); +} #if 0 unsigned int ProjMatrixElemsForOneBin::make_key(int X,int Y,int Z) diff --git a/src/include/stir/recon_buildblock/ProjMatrixElemsForOneBinValue.h b/src/include/stir/recon_buildblock/ProjMatrixElemsForOneBinValue.h index b21cf3a65a..b1116cc002 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixElemsForOneBinValue.h +++ b/src/include/stir/recon_buildblock/ProjMatrixElemsForOneBinValue.h @@ -3,13 +3,13 @@ /*! \file \ingroup projection - + \brief Declaration of class stir::ProjMatrixElemsForOneBinValue - + \author Kris Thielemans \author Mustapha Sadki \author PARAPET project - + */ /* Copyright (C) 2000 PARAPET partners @@ -32,17 +32,17 @@ #ifndef __ProjMatrixElemsForOneBinValue_H__ #define __ProjMatrixElemsForOneBinValue_H__ - #include "stir/common.h" START_NAMESPACE_STIR -template class BasicCoordinate; +template +class BasicCoordinate; /*! \ingroup projection - \brief Stores voxel coordinates and the value of the matrix element. - + \brief Stores voxel coordinates and the value of the matrix element. + (Probably) only useful in class ProjMatrixElemsForOneBin. \warning It is recommended never to use this class name directly, but @@ -51,18 +51,14 @@ template class BasicCoordinate; \warning Voxel coordinates are currently stored as shorts for saving memory. */ -class ProjMatrixElemsForOneBinValue -{ +class ProjMatrixElemsForOneBinValue { public: - explicit inline - ProjMatrixElemsForOneBinValue(const BasicCoordinate<3,int>& coords, - const float ivalue=0); + explicit inline ProjMatrixElemsForOneBinValue(const BasicCoordinate<3, int>& coords, const float ivalue = 0); inline ProjMatrixElemsForOneBinValue(); - //! get the coordinates - inline BasicCoordinate<3,int> get_coords() const; + inline BasicCoordinate<3, int> get_coords() const; //! In effect the same as get_coords()[1] (but faster) inline int coord1() const; @@ -78,12 +74,11 @@ class ProjMatrixElemsForOneBinValue inline ProjMatrixElemsForOneBinValue& operator+=(const ProjMatrixElemsForOneBinValue& el2); //! Multiplies the value of with a float inline ProjMatrixElemsForOneBinValue& operator*=(const float d); - //! Adds a float to the value + //! Adds a float to the value inline ProjMatrixElemsForOneBinValue& operator+=(const float d); //! Divides the value of with a float inline ProjMatrixElemsForOneBinValue& operator/=(const float d); - //////// comparison functions //! Checks if the coordinates are equal @@ -94,20 +89,18 @@ class ProjMatrixElemsForOneBinValue //! Checks lexicographical order of the coordinates static inline bool coordinates_less(const ProjMatrixElemsForOneBinValue& el1, const ProjMatrixElemsForOneBinValue& el2); - + //! Checks coordinates and value are equal friend inline bool operator==(const ProjMatrixElemsForOneBinValue& el1, const ProjMatrixElemsForOneBinValue& el2); - + //! Checks lexicographical order of the coordinates and the value friend inline bool operator<(const ProjMatrixElemsForOneBinValue& el1, const ProjMatrixElemsForOneBinValue& el2); - + private: - short c3,c2,c1; + short c3, c2, c1; float value; - }; - END_NAMESPACE_STIR #include "stir/recon_buildblock/ProjMatrixElemsForOneBinValue.inl" diff --git a/src/include/stir/recon_buildblock/ProjMatrixElemsForOneBinValue.inl b/src/include/stir/recon_buildblock/ProjMatrixElemsForOneBinValue.inl index 6fd22eb4c3..6cb1dd8210 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixElemsForOneBinValue.inl +++ b/src/include/stir/recon_buildblock/ProjMatrixElemsForOneBinValue.inl @@ -3,13 +3,13 @@ /*! \file \ingroup projection - + \brief Inline implementations for class stir::ProjMatrixElemsForOneBinValue - + \author Kris Thielemans \author Mustapha Sadki \author PARAPET project - + */ /* Copyright (C) 2000 PARAPET partners @@ -31,133 +31,97 @@ #include "stir/Coordinate3D.h" -//for SHRT_MAX etc +// for SHRT_MAX etc #ifndef NDEBUG -#include +# include #endif START_NAMESPACE_STIR -ProjMatrixElemsForOneBinValue:: -ProjMatrixElemsForOneBinValue(const BasicCoordinate<3,int>& coords, - const float ivalue) - : c3(static_cast(coords[3])), - c2(static_cast(coords[2])), - c1(static_cast(coords[1])), - value(ivalue) -{ +ProjMatrixElemsForOneBinValue::ProjMatrixElemsForOneBinValue(const BasicCoordinate<3, int>& coords, const float ivalue) + : c3(static_cast(coords[3])), c2(static_cast(coords[2])), c1(static_cast(coords[1])), value(ivalue) { assert(coords[3] <= SHRT_MAX); assert(coords[3] >= SHRT_MIN); assert(coords[2] <= SHRT_MAX); assert(coords[2] >= SHRT_MIN); assert(coords[1] <= SHRT_MAX); assert(coords[1] >= SHRT_MIN); -} - -ProjMatrixElemsForOneBinValue:: -ProjMatrixElemsForOneBinValue() - : c3(0), - c2(0), - c1(0), - value(0) -{} - -BasicCoordinate<3,int> -ProjMatrixElemsForOneBinValue:: -get_coords() const -{ - return Coordinate3D(c1,c2,c3); } -int -ProjMatrixElemsForOneBinValue:: -coord1() const -{ return static_cast(c1); } - -int -ProjMatrixElemsForOneBinValue:: -coord2() const -{ return static_cast(c2); } - -int -ProjMatrixElemsForOneBinValue:: -coord3() const -{ return static_cast(c3); } - -float -ProjMatrixElemsForOneBinValue:: -get_value() const -{ return value; } - - -ProjMatrixElemsForOneBinValue& -ProjMatrixElemsForOneBinValue:: -operator+=(const ProjMatrixElemsForOneBinValue& el2) -{ +ProjMatrixElemsForOneBinValue::ProjMatrixElemsForOneBinValue() : c3(0), c2(0), c1(0), value(0) {} + +BasicCoordinate<3, int> +ProjMatrixElemsForOneBinValue::get_coords() const { + return Coordinate3D(c1, c2, c3); +} + +int +ProjMatrixElemsForOneBinValue::coord1() const { + return static_cast(c1); +} + +int +ProjMatrixElemsForOneBinValue::coord2() const { + return static_cast(c2); +} + +int +ProjMatrixElemsForOneBinValue::coord3() const { + return static_cast(c3); +} + +float +ProjMatrixElemsForOneBinValue::get_value() const { + return value; +} + +ProjMatrixElemsForOneBinValue& +ProjMatrixElemsForOneBinValue::operator+=(const ProjMatrixElemsForOneBinValue& el2) { assert(get_coords() == el2.get_coords()); value += el2.value; return *this; } -ProjMatrixElemsForOneBinValue& -ProjMatrixElemsForOneBinValue:: -operator+=(const float d) -{ +ProjMatrixElemsForOneBinValue& +ProjMatrixElemsForOneBinValue::operator+=(const float d) { value += d; return *this; } -ProjMatrixElemsForOneBinValue& -ProjMatrixElemsForOneBinValue:: -operator*=(const float d) -{ +ProjMatrixElemsForOneBinValue& +ProjMatrixElemsForOneBinValue::operator*=(const float d) { value *= d; return *this; } -ProjMatrixElemsForOneBinValue& -ProjMatrixElemsForOneBinValue:: -operator/=(const float d) -{ +ProjMatrixElemsForOneBinValue& +ProjMatrixElemsForOneBinValue::operator/=(const float d) { value /= d; return *this; } -bool -ProjMatrixElemsForOneBinValue:: -coordinates_equal(const ProjMatrixElemsForOneBinValue& el1, const ProjMatrixElemsForOneBinValue& el2) -{ - return el1.c3==el2.c3 && el1.c2==el2.c2 && el1.c1==el2.c1; +bool +ProjMatrixElemsForOneBinValue::coordinates_equal(const ProjMatrixElemsForOneBinValue& el1, + const ProjMatrixElemsForOneBinValue& el2) { + return el1.c3 == el2.c3 && el1.c2 == el2.c2 && el1.c1 == el2.c1; } -bool -ProjMatrixElemsForOneBinValue:: -coordinates_less(const ProjMatrixElemsForOneBinValue& el1, const ProjMatrixElemsForOneBinValue& el2) -{ - return el1.c1 - START_NAMESPACE_STIR class RelatedDensels; -template class DiscretisedDensity; +template +class DiscretisedDensity; class Succeeded; - - /*! \ingroup projection \brief This stores the non-zero projection matrix elements for every 'voxel'. @@ -56,22 +52,21 @@ class Succeeded; base class, templated in the type of element (TODO). It might be useful to template this class in terms of the - element-type as well. That way, we could have 'compact' + element-type as well. That way, we could have 'compact' elements, efficient elements, etc. However, doing this will probably only be useful if all ProjMatrixByDensel classes are then templated as well (TODO?). */ -/* +/* it might be a bit faster to derive this (privately) from std::vector as opposed to having a member of that type. TODO: check */ -class ProjMatrixElemsForOneDensel -{ +class ProjMatrixElemsForOneDensel { public: - /*! \brief Recommended way to call the type of the elements, instead of + /*! \brief Recommended way to call the type of the elements, instead of referring to the actual classname. Think about this name as 'the type of the value of a ProjMatrixElemsForOneDensel::iterator *'. @@ -79,14 +74,15 @@ class ProjMatrixElemsForOneDensel This typedef is also required for 'standard' iterators. */ typedef ProjMatrixElemsForOneDenselValue value_type; + private: //! shorthand to keep typedefs below concise typedef std::vector Element_vector; -public: +public: //! typedefs for iterator support - typedef Element_vector::iterator iterator; - typedef Element_vector::const_iterator const_iterator; + typedef Element_vector::iterator iterator; + typedef Element_vector::const_iterator const_iterator; typedef Element_vector::size_type size_type; typedef Element_vector::difference_type difference_type; typedef std::random_access_iterator_tag iterator_category; @@ -94,59 +90,58 @@ class ProjMatrixElemsForOneDensel typedef value_type& reference; typedef const value_type& const_reference; - //! constructor - ProjMatrixElemsForOneDensel(); + ProjMatrixElemsForOneDensel(); /*! \param Densel effectively calls set_densel(Densel) \param default_capacity effectively calls reserve(default_capacity) */ - explicit ProjMatrixElemsForOneDensel(const Densel& Densel, const int default_capacity = 300); - - /* rely on compiler-generated versions + explicit ProjMatrixElemsForOneDensel(const Densel& Densel, const int default_capacity = 300); + + /* rely on compiler-generated versions ProjMatrixElemsForOneDensel( const ProjMatrixElemsForOneDensel&); ProjMatrixElemsForOneDensel& operator=(const ProjMatrixElemsForOneDensel&) ; */ //! check if each voxel occurs only once Succeeded check_state() const; - + //! get the Densel coordinates corresponding to this row inline Densel get_densel() const; //! and set the Densel coordinates inline void set_densel(const Densel&); //! functions for allowing iterator access - inline iterator begin() ; - inline const_iterator begin() const; + inline iterator begin(); + inline const_iterator begin() const; inline iterator end(); inline const_iterator end() const; //! reset lor to 0 length void erase(); //! add a new value_type object at the end - /*! - \warning For future compatibility, it is required - (but not checked) that the elements are added such + /*! + \warning For future compatibility, it is required + (but not checked) that the elements are added such that calling sort() after the push_back() would not change the order of the elements. Otherwise, schemes for 'incremental' storing of coordinates would require too much overhead. */ - inline void push_back( const value_type&); + inline void push_back(const value_type&); //! reserve enough space for max_number elements (but don't fill them in) void reserve(size_type max_number); //! number of non-zero elements - inline size_type size() const; + inline size_type size() const; //! Multiplies all values with a constant - ProjMatrixElemsForOneDensel& operator*=(const float d); + ProjMatrixElemsForOneDensel& operator*=(const float d); //! Divides all values with a constant - ProjMatrixElemsForOneDensel& operator/=(const float d); - + ProjMatrixElemsForOneDensel& operator/=(const float d); + //! Sort the elements on coordinates of the voxels /*! Uses value_type::coordinates_less as ordering function. - */ + */ void sort(); //! merge 2nd lor into current object @@ -154,13 +149,12 @@ class ProjMatrixElemsForOneDensel \warning This currently modifies the argument \c lor. */ // TODO make sure we can have a const argument - void merge(ProjMatrixElemsForOneDensel &lor ); + void merge(ProjMatrixElemsForOneDensel& lor); #if 0 void write(fstream&fst) const; void read(fstream&fst ); #endif - //! Return sum of squares of all values /*! \warning This sums over all elements in the LOR, irrespective if they @@ -185,17 +179,15 @@ class ProjMatrixElemsForOneDensel const DiscretisedDensity<3,float>&) const; #endif - + private: - std::vector elements; + std::vector elements; Densel densel; - //! remove a single value_type inline iterator erase(iterator it); }; - END_NAMESPACE_STIR #include "stir/recon_buildblock/ProjMatrixElemsForOneDensel.inl" diff --git a/src/include/stir/recon_buildblock/ProjMatrixElemsForOneDensel.inl b/src/include/stir/recon_buildblock/ProjMatrixElemsForOneDensel.inl index 8ce19fc05c..a795ae8673 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixElemsForOneDensel.inl +++ b/src/include/stir/recon_buildblock/ProjMatrixElemsForOneDensel.inl @@ -30,57 +30,49 @@ START_NAMESPACE_STIR -Densel -ProjMatrixElemsForOneDensel:: -get_densel() const -{ +Densel +ProjMatrixElemsForOneDensel::get_densel() const { return densel; } void -ProjMatrixElemsForOneDensel:: -set_densel(const Densel& new_densel) -{ +ProjMatrixElemsForOneDensel::set_densel(const Densel& new_densel) { densel = new_densel; } -void ProjMatrixElemsForOneDensel::push_back( const ProjMatrixElemsForOneDensel::value_type& el) -{ - elements.push_back(el); +void +ProjMatrixElemsForOneDensel::push_back(const ProjMatrixElemsForOneDensel::value_type& el) { + elements.push_back(el); } - -ProjMatrixElemsForOneDensel::size_type -ProjMatrixElemsForOneDensel:: -size() const -{ +ProjMatrixElemsForOneDensel::size_type +ProjMatrixElemsForOneDensel::size() const { return elements.size(); } -ProjMatrixElemsForOneDensel::iterator -ProjMatrixElemsForOneDensel::begin() - { return elements.begin(); } - -ProjMatrixElemsForOneDensel::const_iterator -ProjMatrixElemsForOneDensel:: -begin() const - { return elements.begin(); }; +ProjMatrixElemsForOneDensel::iterator +ProjMatrixElemsForOneDensel::begin() { + return elements.begin(); +} -ProjMatrixElemsForOneDensel::iterator -ProjMatrixElemsForOneDensel:: -end() - { return elements.end(); }; +ProjMatrixElemsForOneDensel::const_iterator +ProjMatrixElemsForOneDensel::begin() const { + return elements.begin(); +}; -ProjMatrixElemsForOneDensel::const_iterator -ProjMatrixElemsForOneDensel:: -end() const - { return elements.end(); }; +ProjMatrixElemsForOneDensel::iterator +ProjMatrixElemsForOneDensel::end() { + return elements.end(); +}; -ProjMatrixElemsForOneDensel::iterator -ProjMatrixElemsForOneDensel:: -erase(iterator it){ - return elements.erase(it); - } +ProjMatrixElemsForOneDensel::const_iterator +ProjMatrixElemsForOneDensel::end() const { + return elements.end(); +}; +ProjMatrixElemsForOneDensel::iterator +ProjMatrixElemsForOneDensel::erase(iterator it) { + return elements.erase(it); +} END_NAMESPACE_STIR diff --git a/src/include/stir/recon_buildblock/ProjMatrixElemsForOneDenselValue.h b/src/include/stir/recon_buildblock/ProjMatrixElemsForOneDenselValue.h index 6448711c96..804a8eca99 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixElemsForOneDenselValue.h +++ b/src/include/stir/recon_buildblock/ProjMatrixElemsForOneDenselValue.h @@ -3,11 +3,11 @@ /*! \file \ingroup projection - + \brief Declaration of class stir::ProjMatrixElemsForOneDenselValue - + \author Kris Thielemans - + */ /* Copyright (C) 2000- 2009, Hammersmith Imanet Ltd @@ -33,11 +33,10 @@ START_NAMESPACE_STIR - /*! \ingroup projection - \brief Stores voxel coordinates and the value of the matrix element. - + \brief Stores voxel coordinates and the value of the matrix element. + (Probably) only useful in class ProjMatrixElemsForOneDensel. \warning It is recommended never to use this class name directly, but @@ -47,11 +46,9 @@ START_NAMESPACE_STIR \todo Simply derived from Bin for now. */ - class ProjMatrixElemsForOneDenselValue : public Bin -{ +class ProjMatrixElemsForOneDenselValue : public Bin { public: - explicit inline - ProjMatrixElemsForOneDenselValue(const Bin&); + explicit inline ProjMatrixElemsForOneDenselValue(const Bin&); inline ProjMatrixElemsForOneDenselValue(); @@ -59,15 +56,15 @@ START_NAMESPACE_STIR inline ProjMatrixElemsForOneDenselValue& operator+=(const ProjMatrixElemsForOneDenselValue& el2); //! Multiplies the value of with a float inline ProjMatrixElemsForOneDenselValue& operator*=(const float d); - //! Adds a float to the value + //! Adds a float to the value inline ProjMatrixElemsForOneDenselValue& operator+=(const float d); //! Divides the value of with a float inline ProjMatrixElemsForOneDenselValue& operator/=(const float d); // TODO inline float get_value() const { return get_bin_value(); } - inline void set_value(const float v) { set_bin_value(v); } - + inline void set_value(const float v) { set_bin_value(v); } + //////// comparison functions //! Checks if the coordinates are equal @@ -78,17 +75,14 @@ START_NAMESPACE_STIR //! Checks lexicographical order of the coordinates static inline bool coordinates_less(const ProjMatrixElemsForOneDenselValue& el1, const ProjMatrixElemsForOneDenselValue& el2); - + //! Checks coordinates and value are equal friend inline bool operator==(const ProjMatrixElemsForOneDenselValue& el1, const ProjMatrixElemsForOneDenselValue& el2); - + //! Checks lexicographical order of the coordinates and the value friend inline bool operator<(const ProjMatrixElemsForOneDenselValue& el1, const ProjMatrixElemsForOneDenselValue& el2); - - }; - END_NAMESPACE_STIR #include "stir/recon_buildblock/ProjMatrixElemsForOneDenselValue.inl" diff --git a/src/include/stir/recon_buildblock/ProjMatrixElemsForOneDenselValue.inl b/src/include/stir/recon_buildblock/ProjMatrixElemsForOneDenselValue.inl index d13ca2c919..8b4796db99 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixElemsForOneDenselValue.inl +++ b/src/include/stir/recon_buildblock/ProjMatrixElemsForOneDenselValue.inl @@ -3,11 +3,11 @@ /*! \file \ingroup projection - + \brief Inline implementations for class stir::ProjMatrixElemsForOneDenselValue - + \author Kris Thielemans - + */ /* Copyright (C) 2000- 2009, Hammersmith Imanet Ltd @@ -26,105 +26,71 @@ See STIR/LICENSE.txt for details */ - START_NAMESPACE_STIR -ProjMatrixElemsForOneDenselValue:: -ProjMatrixElemsForOneDenselValue(const Bin& bin) -: Bin(bin) -{ -} - -ProjMatrixElemsForOneDenselValue:: -ProjMatrixElemsForOneDenselValue() - : Bin() -{} +ProjMatrixElemsForOneDenselValue::ProjMatrixElemsForOneDenselValue(const Bin& bin) : Bin(bin) {} +ProjMatrixElemsForOneDenselValue::ProjMatrixElemsForOneDenselValue() : Bin() {} - -ProjMatrixElemsForOneDenselValue& -ProjMatrixElemsForOneDenselValue:: -operator+=(const ProjMatrixElemsForOneDenselValue& el2) -{ - //TODO assert(get_coords() == el2.get_coords()); +ProjMatrixElemsForOneDenselValue& +ProjMatrixElemsForOneDenselValue::operator+=(const ProjMatrixElemsForOneDenselValue& el2) { + // TODO assert(get_coords() == el2.get_coords()); //*this += static_cast(el2); set_bin_value(get_bin_value() + el2.get_bin_value()); return *this; } -ProjMatrixElemsForOneDenselValue& -ProjMatrixElemsForOneDenselValue:: -operator+=(const float d) -{ +ProjMatrixElemsForOneDenselValue& +ProjMatrixElemsForOneDenselValue::operator+=(const float d) { static_cast(*this) += d; return *this; } -ProjMatrixElemsForOneDenselValue& -ProjMatrixElemsForOneDenselValue:: -operator*=(const float d) -{ +ProjMatrixElemsForOneDenselValue& +ProjMatrixElemsForOneDenselValue::operator*=(const float d) { set_bin_value(get_bin_value() * d); return *this; } -ProjMatrixElemsForOneDenselValue& -ProjMatrixElemsForOneDenselValue:: -operator/=(const float d) -{ +ProjMatrixElemsForOneDenselValue& +ProjMatrixElemsForOneDenselValue::operator/=(const float d) { set_bin_value(get_bin_value() / d); return *this; } -bool -ProjMatrixElemsForOneDenselValue:: -coordinates_equal(const ProjMatrixElemsForOneDenselValue& el1, const ProjMatrixElemsForOneDenselValue& el2) -{ - return - el1.segment_num() == el2.segment_num() && - el1.view_num() == el2.view_num() && - el1.axial_pos_num() == el2.axial_pos_num() && - el1.tangential_pos_num() == el2.tangential_pos_num(); +bool +ProjMatrixElemsForOneDenselValue::coordinates_equal(const ProjMatrixElemsForOneDenselValue& el1, + const ProjMatrixElemsForOneDenselValue& el2) { + return el1.segment_num() == el2.segment_num() && el1.view_num() == el2.view_num() && + el1.axial_pos_num() == el2.axial_pos_num() && el1.tangential_pos_num() == el2.tangential_pos_num(); } -bool -ProjMatrixElemsForOneDenselValue:: -coordinates_less(const ProjMatrixElemsForOneDenselValue& el1, const ProjMatrixElemsForOneDenselValue& el2) -{ - return - el1.segment_num() < el2.segment_num() || - (el1.segment_num() == el2.segment_num() && - (el1.view_num() < el2.view_num() || - (el1.view_num() == el2.view_num() && - (el1.axial_pos_num() < el2.axial_pos_num() || - (el1.axial_pos_num() == el2.axial_pos_num() && - el1.tangential_pos_num() < el2.tangential_pos_num()))))); +bool +ProjMatrixElemsForOneDenselValue::coordinates_less(const ProjMatrixElemsForOneDenselValue& el1, + const ProjMatrixElemsForOneDenselValue& el2) { + return el1.segment_num() < el2.segment_num() || + (el1.segment_num() == el2.segment_num() && + (el1.view_num() < el2.view_num() || + (el1.view_num() == el2.view_num() && + (el1.axial_pos_num() < el2.axial_pos_num() || + (el1.axial_pos_num() == el2.axial_pos_num() && el1.tangential_pos_num() < el2.tangential_pos_num()))))); } - - -bool -operator==(const ProjMatrixElemsForOneDenselValue& el1, - const ProjMatrixElemsForOneDenselValue& el2) -{ +bool +operator==(const ProjMatrixElemsForOneDenselValue& el1, const ProjMatrixElemsForOneDenselValue& el2) { return static_cast(el1) == static_cast(el2); } - -bool -operator<(const ProjMatrixElemsForOneDenselValue& el1, - const ProjMatrixElemsForOneDenselValue& el2) -{ - return - el1.segment_num() < el2.segment_num() || - (el1.segment_num() == el2.segment_num() && - (el1.view_num() < el2.view_num() || - (el1.view_num() == el2.view_num() && - (el1.axial_pos_num() < el2.axial_pos_num() || +bool +operator<(const ProjMatrixElemsForOneDenselValue& el1, const ProjMatrixElemsForOneDenselValue& el2) { + return el1.segment_num() < el2.segment_num() || + (el1.segment_num() == el2.segment_num() && + (el1.view_num() < el2.view_num() || + (el1.view_num() == el2.view_num() && + (el1.axial_pos_num() < el2.axial_pos_num() || (el1.axial_pos_num() == el2.axial_pos_num() && (el1.tangential_pos_num() < el2.tangential_pos_num() || - (el1.tangential_pos_num() < el2.tangential_pos_num() && - el1.get_bin_value() class DiscretisedDensity; +template +class DiscretisedDensity; class ProjDataInfo; - /*! \ingroup projection \brief Abstract base class for all projector pairs - This class is useful for all algorithms which need both a forward - and back projector. It's only purpose in that case is to provide the - parsing mechanisms, such that the projectors can be defined in a .par + This class is useful for all algorithms which need both a forward + and back projector. It's only purpose in that case is to provide the + parsing mechanisms, such that the projectors can be defined in a .par file. */ -class ProjectorByBinPair : -public RegisteredObject -{ +class ProjectorByBinPair : public RegisteredObject { public: - - //! Default constructor + //! Default constructor ProjectorByBinPair(); virtual ~ProjectorByBinPair() {} //! Stores all necessary geometric info - /*! + /*! If necessary, set_up() can be called more than once. Derived classes can assume that the projectors will be called - with input corresponding to the arguments of the last call to set_up(). + with input corresponding to the arguments of the last call to set_up(). \warning Derived classes have to call set_up from the base class. */ - virtual Succeeded - set_up( - const shared_ptr&, - const shared_ptr >& // TODO should be Info only - ); + virtual Succeeded set_up(const shared_ptr&, + const shared_ptr>& // TODO should be Info only + ); + // ForwardProjectorByBin const * + const shared_ptr get_forward_projector_sptr() const; - //ForwardProjectorByBin const * - const shared_ptr - get_forward_projector_sptr() const; + // BackProjectorByBin const * + const shared_ptr get_back_projector_sptr() const; - //BackProjectorByBin const * - const shared_ptr - get_back_projector_sptr() const; - //! Provide access to the (minimal) symmetries used by the projectors /*! It is expected that the forward and back projector can handle the same symmetries. @@ -91,13 +83,11 @@ public RegisteredObject the symmetries returned by the back projector. \todo determine set of minimal symmetries */ - const DataSymmetriesForViewSegmentNumbers * get_symmetries_used() const - { - return get_back_projector_sptr()->get_symmetries_used(); - } + const DataSymmetriesForViewSegmentNumbers* get_symmetries_used() const { + return get_back_projector_sptr()->get_symmetries_used(); + } protected: - shared_ptr forward_projector_sptr; shared_ptr back_projector_sptr; @@ -106,17 +96,16 @@ public RegisteredObject If overriding this function in a derived class, you need to call this one. */ - virtual void check(const ProjDataInfo& proj_data_info, const DiscretisedDensity<3,float>& density_info) const; + virtual void check(const ProjDataInfo& proj_data_info, const DiscretisedDensity<3, float>& density_info) const; bool _already_set_up; - private: +private: shared_ptr _proj_data_info_sptr; //! The density ptr set with set_up() /*! \todo it is wasteful to have to store the whole image as this uses memory that we don't need. */ - shared_ptr > _density_info_sptr; + shared_ptr> _density_info_sptr; }; END_NAMESPACE_STIR - #endif // __stir_recon_buildblock_ProjectorByBinPair_h_ diff --git a/src/include/stir/recon_buildblock/ProjectorByBinPairUsingProjMatrixByBin.h b/src/include/stir/recon_buildblock/ProjectorByBinPairUsingProjMatrixByBin.h index 063faf64fc..93cbc31535 100644 --- a/src/include/stir/recon_buildblock/ProjectorByBinPairUsingProjMatrixByBin.h +++ b/src/include/stir/recon_buildblock/ProjectorByBinPairUsingProjMatrixByBin.h @@ -39,22 +39,16 @@ class Succeeded; \ingroup projection \brief A projector pair based on a single matrix */ -class ProjectorByBinPairUsingProjMatrixByBin : - public RegisteredParsingObject -{ - private: - typedef - RegisteredParsingObject - base_type; +class ProjectorByBinPairUsingProjMatrixByBin + : public RegisteredParsingObject { +private: + typedef RegisteredParsingObject base_type; + public: //! Name which will be used when parsing a ProjectorByBinPair object - static const char * const registered_name; + static const char* const registered_name; - //! Default constructor + //! Default constructor ProjectorByBinPairUsingProjMatrixByBin(); //! Constructor that sets the projection matrix @@ -62,20 +56,17 @@ class ProjectorByBinPairUsingProjMatrixByBin : //! Stores all necessary geometric info /*! First constructs forward and back projectors and then calls base_type::setup */ - virtual Succeeded set_up( - const shared_ptr& proj_data_info_sptr, - const shared_ptr >& density_info_sptr // TODO should be Info only - ); + virtual Succeeded set_up(const shared_ptr& proj_data_info_sptr, + const shared_ptr>& density_info_sptr // TODO should be Info only + ); - ProjMatrixByBin const * - get_proj_matrix_ptr() const; + ProjMatrixByBin const* get_proj_matrix_ptr() const; shared_ptr get_proj_matrix_sptr() const; void set_proj_matrix_sptr(const shared_ptr& sptr); private: - shared_ptr proj_matrix_sptr; void set_defaults(); void initialise_keymap(); @@ -84,5 +75,4 @@ class ProjectorByBinPairUsingProjMatrixByBin : END_NAMESPACE_STIR - #endif // __stir_recon_buildblock_ProjectorByBinPairUsingProjMatrixByBin_h_ diff --git a/src/include/stir/recon_buildblock/ProjectorByBinPairUsingSeparateProjectors.h b/src/include/stir/recon_buildblock/ProjectorByBinPairUsingSeparateProjectors.h index f8bf5b8df6..ff267f4a5b 100644 --- a/src/include/stir/recon_buildblock/ProjectorByBinPairUsingSeparateProjectors.h +++ b/src/include/stir/recon_buildblock/ProjectorByBinPairUsingSeparateProjectors.h @@ -33,36 +33,27 @@ START_NAMESPACE_STIR - /*! \ingroup projection \brief A projector pair based on a single matrix */ -class ProjectorByBinPairUsingSeparateProjectors : - public RegisteredParsingObject -{ - private: - typedef - RegisteredParsingObject - base_type; +class ProjectorByBinPairUsingSeparateProjectors + : public RegisteredParsingObject { +private: + typedef RegisteredParsingObject base_type; + public: //! Name which will be used when parsing a ProjectorByBinPair object - static const char * const registered_name; + static const char* const registered_name; - //! Default constructor + //! Default constructor ProjectorByBinPairUsingSeparateProjectors(); - //! Constructor that sets the pair + //! Constructor that sets the pair ProjectorByBinPairUsingSeparateProjectors(const shared_ptr& forward_projector_sptr, const shared_ptr& back_projector_sptr); - private: - void set_defaults(); void initialise_keymap(); bool post_processing(); @@ -70,5 +61,4 @@ class ProjectorByBinPairUsingSeparateProjectors : END_NAMESPACE_STIR - #endif // __stir_recon_buildblock_ProjectorByBinPairUsingSeparateProjectors_h_ diff --git a/src/include/stir/recon_buildblock/QuadraticPrior.h b/src/include/stir/recon_buildblock/QuadraticPrior.h index 0f0262e227..fea3230e25 100644 --- a/src/include/stir/recon_buildblock/QuadraticPrior.h +++ b/src/include/stir/recon_buildblock/QuadraticPrior.h @@ -26,11 +26,9 @@ */ - #ifndef __stir_recon_buildblock_QuadraticPrior_H__ #define __stir_recon_buildblock_QuadraticPrior_H__ - #include "stir/RegisteredParsingObject.h" #include "stir/recon_buildblock/PriorWithParabolicSurrogate.h" #include "stir/Array.h" @@ -40,33 +38,32 @@ START_NAMESPACE_STIR - /*! \ingroup priors \brief A class in the GeneralisedPrior hierarchy. This implements a quadratic Gibbs prior. The gradient of the prior is computed as follows: - + \f[ g_r = \sum_dr w_{dr} (\lambda_r - \lambda_{r+dr}) * \kappa_r * \kappa_{r+dr} \f] where \f$\lambda\f$ is the image and \f$r\f$ and \f$dr\f$ are indices and the sum is over the neighbourhood where the weights \f$w_{dr}\f$ are non-zero. - The \f$\kappa\f$ image can be used to have spatially-varying penalties such as in + The \f$\kappa\f$ image can be used to have spatially-varying penalties such as in Jeff Fessler's papers. It should have identical dimensions to the image for which the penalty is computed. If \f$\kappa\f$ is not set, this class will effectively use 1 for all \f$\kappa\f$'s. - By default, a 3x3 or 3x3x3 neigbourhood is used where the weights are set to + By default, a 3x3 or 3x3x3 neigbourhood is used where the weights are set to x-voxel_size divided by the Euclidean distance between the points. - + \par Parsing These are the keywords that can be used in addition to the ones in GeneralPrior. \verbatim Quadratic Prior Parameters:= - ; next defaults to 0, set to 1 for 2D inverse Euclidean weights, 0 for 3D + ; next defaults to 0, set to 1 for 2D inverse Euclidean weights, 0 for 3D only 2D:= 0 ; next can be used to set weights explicitly. Needs to be a 3D array (of floats). ' value of only_2D is ignored @@ -77,120 +74,107 @@ START_NAMESPACE_STIR ; kappa filename:= ; use next parameter to get gradient images at every subiteration ; see class documentation - gradient filename prefix:= + gradient filename prefix:= END Quadratic Prior Parameters:= \endverbatim */ template -class QuadraticPrior: public - RegisteredParsingObject< QuadraticPrior, - GeneralisedPrior >, - PriorWithParabolicSurrogate > - > -{ - private: - typedef - RegisteredParsingObject< QuadraticPrior, - GeneralisedPrior >, - PriorWithParabolicSurrogate > > - base_type; - - public: +class QuadraticPrior : public RegisteredParsingObject, GeneralisedPrior>, + PriorWithParabolicSurrogate>> { +private: + typedef RegisteredParsingObject, GeneralisedPrior>, + PriorWithParabolicSurrogate>> + base_type; + +public: //! Name which will be used when parsing a GeneralisedPrior object - static const char * const registered_name; + static const char* const registered_name; - //! Default constructor + //! Default constructor QuadraticPrior(); //! Constructs it explicitly QuadraticPrior(const bool only_2D, float penalization_factor); - - virtual bool - parabolic_surrogate_curvature_depends_on_argument() const - { return false; } + + virtual bool parabolic_surrogate_curvature_depends_on_argument() const { return false; } //! compute the value of the function - double - compute_value(const DiscretisedDensity<3,elemT> ¤t_image_estimate); + double compute_value(const DiscretisedDensity<3, elemT>& current_image_estimate); - //! compute gradient - void compute_gradient(DiscretisedDensity<3,elemT>& prior_gradient, - const DiscretisedDensity<3,elemT> ¤t_image_estimate); + //! compute gradient + void compute_gradient(DiscretisedDensity<3, elemT>& prior_gradient, const DiscretisedDensity<3, elemT>& current_image_estimate); //! compute the parabolic surrogate for the prior /*! in the case of quadratic priors this will just be the sum of weighting coefficients*/ - void parabolic_surrogate_curvature(DiscretisedDensity<3,elemT>& parabolic_surrogate_curvature, - const DiscretisedDensity<3,elemT> ¤t_image_estimate); + void parabolic_surrogate_curvature(DiscretisedDensity<3, elemT>& parabolic_surrogate_curvature, + const DiscretisedDensity<3, elemT>& current_image_estimate); - //! compute Hessian - void compute_Hessian(DiscretisedDensity<3,elemT>& prior_Hessian_for_single_densel, - const BasicCoordinate<3,int>& coords, - const DiscretisedDensity<3,elemT> ¤t_image_estimate); + //! compute Hessian + void compute_Hessian(DiscretisedDensity<3, elemT>& prior_Hessian_for_single_densel, const BasicCoordinate<3, int>& coords, + const DiscretisedDensity<3, elemT>& current_image_estimate); //! Call accumulate_Hessian_times_input - virtual Succeeded - add_multiplication_with_approximate_Hessian(DiscretisedDensity<3,elemT>& output, - const DiscretisedDensity<3,elemT>& input) const; + virtual Succeeded add_multiplication_with_approximate_Hessian(DiscretisedDensity<3, elemT>& output, + const DiscretisedDensity<3, elemT>& input) const; //! Compute the multiplication of the hessian of the prior multiplied by the input. //! For the quadratic function, the hessian of the prior is 1. //! Therefore this will return the weights multiplied by the input. - virtual Succeeded accumulate_Hessian_times_input(DiscretisedDensity<3,elemT>& output, - const DiscretisedDensity<3,elemT>& current_estimate, - const DiscretisedDensity<3,elemT>& input) const; + virtual Succeeded accumulate_Hessian_times_input(DiscretisedDensity<3, elemT>& output, + const DiscretisedDensity<3, elemT>& current_estimate, + const DiscretisedDensity<3, elemT>& input) const; //! get penalty weights for the neighbourhood - Array<3,float> get_weights() const; + Array<3, float> get_weights() const; //! set penalty weights for the neighbourhood - void set_weights(const Array<3,float>&); + void set_weights(const Array<3, float>&); //! get current kappa image /*! \warning As this function returns a shared_ptr, this is dangerous. You should not modify the image by manipulating the image refered to by this pointer. Unpredictable results will occur. */ - shared_ptr > get_kappa_sptr() const; + shared_ptr> get_kappa_sptr() const; //! set kappa image - void set_kappa_sptr(const shared_ptr >&); + void set_kappa_sptr(const shared_ptr>&); //! Has to be called before using this object - virtual Succeeded set_up(shared_ptr > const& target_sptr); - + virtual Succeeded set_up(shared_ptr> const& target_sptr); + protected: //! can be set during parsing to restrict the weights to the 2D case bool only_2D; //! filename prefix for outputing the gradient whenever compute_gradient() is called. /*! An internal counter is used to keep track of the number of times the - gradient is computed. The filename will be constructed by concatenating + gradient is computed. The filename will be constructed by concatenating gradient_filename_prefix and the counter. */ std::string gradient_filename_prefix; //! penalty weights - /*! + /*! \todo This member is mutable at present because some const functions initialise it. That initialisation should be moved to a new set_up() function. */ - mutable Array<3,float> weights; + mutable Array<3, float> weights; //! Filename for the \f$\kappa\f$ image that will be read by post_processing() std::string kappa_filename; //! Check that the prior is ready to be used - virtual void check(DiscretisedDensity<3,elemT> const& current_image_estimate) const; + virtual void check(DiscretisedDensity<3, elemT> const& current_image_estimate) const; virtual void set_defaults(); virtual void initialise_keymap(); virtual bool post_processing(); - private: - shared_ptr > kappa_ptr; -}; +private: + shared_ptr> kappa_ptr; +}; END_NAMESPACE_STIR #endif - diff --git a/src/include/stir/recon_buildblock/RayTraceVoxelsOnCartesianGrid.h b/src/include/stir/recon_buildblock/RayTraceVoxelsOnCartesianGrid.h index f2528d6ae5..69b656b01d 100644 --- a/src/include/stir/recon_buildblock/RayTraceVoxelsOnCartesianGrid.h +++ b/src/include/stir/recon_buildblock/RayTraceVoxelsOnCartesianGrid.h @@ -32,10 +32,11 @@ START_NAMESPACE_STIR class ProjMatrixElemsForOneBin; -template class CartesianCoordinate3D; +template +class CartesianCoordinate3D; /*! \ingroup recon_buildblock - + \brief RayTraceVoxelsOnCartesianGrid finds the Length of Intersections (LOIs) of an LOR with a grid of voxels and appends them to the ProjMatrixElemsForOneBin object. @@ -48,7 +49,7 @@ template class CartesianCoordinate3D; start_point and end_point have to be given in 'voxel grid units' (i.e. voxels are spaced 1 unit apart). The centre - of the voxels are assumed to be at integer coordinates + of the voxels are assumed to be at integer coordinates (e.g. (0,0,0) is the centre of a voxel). For the start voxel, the intersection length of the LOR with the @@ -60,7 +61,7 @@ template class CartesianCoordinate3D; RayTraceVoxelsOnCartesianGrid is based on Siddon's algorithm. - Siddon's algorithm works by looking at intersections of the + Siddon's algorithm works by looking at intersections of the 'intra-voxel' planes with the LOR. The LORs is parametrised as @@ -73,11 +74,8 @@ template class CartesianCoordinate3D; as this determines which plane the LOR intersects at this point. */ -void -RayTraceVoxelsOnCartesianGrid(ProjMatrixElemsForOneBin& lor, - const CartesianCoordinate3D& start_point, - const CartesianCoordinate3D& end_point, - const CartesianCoordinate3D& voxel_size, - const float normalisation_constant = 1.F); +void RayTraceVoxelsOnCartesianGrid(ProjMatrixElemsForOneBin& lor, const CartesianCoordinate3D& start_point, + const CartesianCoordinate3D& end_point, const CartesianCoordinate3D& voxel_size, + const float normalisation_constant = 1.F); END_NAMESPACE_STIR diff --git a/src/include/stir/recon_buildblock/Reconstruction.h b/src/include/stir/recon_buildblock/Reconstruction.h index 8620afda93..b55a790b08 100644 --- a/src/include/stir/recon_buildblock/Reconstruction.h +++ b/src/include/stir/recon_buildblock/Reconstruction.h @@ -21,9 +21,9 @@ #ifndef __stir_recon_buildblock_Reconstruction_H__ #define __stir_recon_buildblock_Reconstruction_H__ /*! - \file + \file \ingroup recon_buildblock - + \brief declares the stir::Reconstruction class \author Kris Thielemans @@ -45,7 +45,6 @@ START_NAMESPACE_STIR - class Succeeded; /*! @@ -55,7 +54,7 @@ class Succeeded; this base class is rather basic. It essentially takes care of constructing a target image, calls the virtual reconstruct() function, and writes the result to file. - For convenience, the class is derived from TimedObject. It is the + For convenience, the class is derived from TimedObject. It is the responsibility of the derived class to run these timers though. \par Parsing parameters @@ -66,7 +65,7 @@ class Succeeded; post-filter type := ; output file(s) will be written with the following file name - output filename prefix := + output filename prefix := ; output file(s) will use the following file format ; see OutputFileFormat output file format := @@ -75,30 +74,26 @@ class Succeeded; */ template -class Reconstruction : - public RegisteredObject >, - public TimedObject -{ +class Reconstruction : public RegisteredObject>, public TimedObject { public: //! default constructor (calls set_defaults()) Reconstruction(); //! virtual destructor - virtual ~Reconstruction() {}; - + virtual ~Reconstruction(){}; + //! gives method information virtual std::string method_info() const = 0; - + //! executes the reconstruction /*! \return Succeeded::yes if everything was alright. - */ - virtual Succeeded - reconstruct() = 0; + */ + virtual Succeeded reconstruct() = 0; //! executes the reconstruction storing result in \c target_image_sptr /*! - \param target_image_sptr The result of the reconstruction is stored in + \param target_image_sptr The result of the reconstruction is stored in \c *target_image_sptr. \return Succeeded::yes if everything was alright. @@ -108,12 +103,11 @@ class Reconstruction : in a derived class, will hide the other. So you have to overload both. \warning you need to call set_up() first. - */ - virtual Succeeded - reconstruct(shared_ptr const& target_image_sptr) = 0; + */ + virtual Succeeded reconstruct(shared_ptr const& target_image_sptr) = 0; //! operations prior to the reconstruction - /*! Will do various consistency checks and return Succeeded::no + /*! Will do various consistency checks and return Succeeded::no if something is wrong. \todo Currently, set_up() is called by reconstruct(). This is in @@ -121,23 +115,23 @@ class Reconstruction : has to be called before any actual processing. Maybe this should be made consistent. */ - virtual Succeeded set_up(shared_ptr const& target_data_sptr); + virtual Succeeded set_up(shared_ptr const& target_data_sptr); /*! \name Functions to set parameters This can be used as alternative to the parsing mechanism. - \warning Be careful with setting shared pointers. If you modify the objects in + \warning Be careful with setting shared pointers. If you modify the objects in one place, all objects that use the shared pointer will be affected. */ //@{ //! file name for output reconstructed images - void set_output_filename_prefix(const std::string&); + void set_output_filename_prefix(const std::string&); //! defines the format of the output files - void set_output_file_format_ptr(const shared_ptr >&); + void set_output_file_format_ptr(const shared_ptr>&); //! post-filter - void set_post_processor_sptr(const shared_ptr > &); + void set_post_processor_sptr(const shared_ptr>&); //! \brief set input data virtual void set_input_data(const shared_ptr&) = 0; @@ -167,19 +161,18 @@ class Reconstruction : //! \author Nikos Efthimiou //! \return //! - shared_ptr get_target_image(); + shared_ptr get_target_image(); // parameters - protected: - +protected: //! file name for output reconstructed images - std::string output_filename_prefix; + std::string output_filename_prefix; //! defines the format of the output files - shared_ptr > output_file_format_ptr; + shared_ptr> output_file_format_ptr; //! post-filter - shared_ptr > post_filter_sptr; + shared_ptr> post_filter_sptr; protected: //! do consistency checks @@ -190,12 +183,12 @@ class Reconstruction : virtual void check(TargetT const& target_data) const; bool _already_set_up; - /*! - \brief - This function initialises all parameters, either via parsing, + /*! + \brief + This function initialises all parameters, either via parsing, or by calling ask_parameters() (when parameter_filename is the empty string). - It should be called in the constructor of the last class in the + It should be called in the constructor of the last class in the hierarchy. At that time, all Interfile keys will have been initialised, and ask_parameters() will be the appropriate virtual function, such that questions are asked for all parameters. @@ -204,19 +197,19 @@ class Reconstruction : return Succeeded (or throw an exception). */ void initialise(const std::string& parameter_filename); - + virtual void set_defaults(); virtual void initialise_keymap(); //! used to check acceptable parameters after parsing /*! - The function should be used to set members that have + The function should be used to set members that have are not set directly by the parsing. For example, parsing might set \c input_filename, and \c post_processing() - might then read in the data and set the corresponding + might then read in the data and set the corresponding Reconstruction parameter. Consistency checks mostly belong in \c set_up(). The reason for this - is that for instance a GUI might not use the parsing mechanism and + is that for instance a GUI might not use the parsing mechanism and set parameters by calling various \c set_ functions (such as \c set_post_processor_sptr() ). */ @@ -225,7 +218,7 @@ class Reconstruction : //! //! \brief target_data_sptr //! - shared_ptr target_data_sptr; + shared_ptr target_data_sptr; //! //! \brief _disable_output @@ -235,14 +228,10 @@ class Reconstruction : //! from within some other code and want to use directly the output image. bool _disable_output; - /// Verbosity level int _verbosity; - - }; END_NAMESPACE_STIR #endif - diff --git a/src/include/stir/recon_buildblock/RelatedBins.h b/src/include/stir/recon_buildblock/RelatedBins.h index 899c60fda2..6ad36d5ad2 100644 --- a/src/include/stir/recon_buildblock/RelatedBins.h +++ b/src/include/stir/recon_buildblock/RelatedBins.h @@ -38,19 +38,17 @@ START_NAMESPACE_STIR class ProjData; class Bin; class DataSymmetriesForBins; -/*! +/*! \ingroup recon_buildblock - \brief This class contains all information about a set of bins related + \brief This class contains all information about a set of bins related by symmetry. */ -class RelatedBins -{ +class RelatedBins { public: - //! typedefs for iterator support - + //! typedefs for iterator support - typedef std::random_access_iterator_tag iterator_category; + typedef std::random_access_iterator_tag iterator_category; typedef Bin value_type; typedef value_type& reference; typedef const value_type& const_reference; @@ -65,8 +63,8 @@ class RelatedBins typedef vector::iterator iterator; typedef vector::const_iterator const_iterator; #endif - //!Default constructor: creates no bins, no symmetries - inline RelatedBins(); + //! Default constructor: creates no bins, no symmetries + inline RelatedBins(); //! get the number of related bins inline int get_num_related_bins() const; @@ -74,36 +72,31 @@ class RelatedBins //! get 'basic' bin coordinates inline Bin get_basic_bin() const; - // get the pointer to a ProjDataInfo class + // get the pointer to a ProjDataInfo class // inline const ProjDataInfo * get_proj_data_info_sptr() const; //! return the symmetries used - inline const DataSymmetriesForBins* get_symmetries_ptr() const ; - + inline const DataSymmetriesForBins* get_symmetries_ptr() const; + //! get an empty copy RelatedBins get_empty_copy() const; - // basic iterator support + // basic iterator support //! use to initialise an iterator to the first element of the vector - inline iterator begin(); - //! iterator 'past' the last element of the vector - inline iterator end(); - //! use to initialise an iterator to the first element of the (const) vector - inline const_iterator begin() const; - //! iterator 'past' the last element of the (const) vector - inline const_iterator end() const; - - + inline iterator begin(); + //! iterator 'past' the last element of the vector + inline iterator end(); + //! use to initialise an iterator to the first element of the (const) vector + inline const_iterator begin() const; + //! iterator 'past' the last element of the (const) vector + inline const_iterator end() const; private: - std::vector related_bins; - shared_ptr symmetries; - //! a private constructor which sets the members - inline RelatedBins(const std::vector& related_bins, - const shared_ptr& symmetries_used); - - + std::vector related_bins; + shared_ptr symmetries; + //! a private constructor which sets the members + inline RelatedBins(const std::vector& related_bins, const shared_ptr& symmetries_used); }; END_NAMESPACE_STIR @@ -111,5 +104,3 @@ END_NAMESPACE_STIR #include "stir/recon_buildblock/RelatedBins.inl" #endif //__RelatedBins_H__ - - diff --git a/src/include/stir/recon_buildblock/RelatedBins.inl b/src/include/stir/recon_buildblock/RelatedBins.inl index 3ef79b682f..c8f91ef5b1 100644 --- a/src/include/stir/recon_buildblock/RelatedBins.inl +++ b/src/include/stir/recon_buildblock/RelatedBins.inl @@ -34,31 +34,22 @@ START_NAMESPACE_STIR +RelatedBins::RelatedBins() : related_bins(), symmetries() {} - -RelatedBins::RelatedBins() -:related_bins(),symmetries() -{} - -RelatedBins::RelatedBins(const std::vector< Bin>& related_bins_v, - const shared_ptr& symmetries_used) -:related_bins(related_bins_v),symmetries(symmetries_used) -{} +RelatedBins::RelatedBins(const std::vector& related_bins_v, const shared_ptr& symmetries_used) + : related_bins(related_bins_v), symmetries(symmetries_used) {} int -RelatedBins::get_num_related_bins() const -{ +RelatedBins::get_num_related_bins() const { return static_cast(related_bins.size()); } Bin -RelatedBins::get_basic_bin() const -{ +RelatedBins::get_basic_bin() const { assert(related_bins.size() != 0); return related_bins[0]; } - #if 0 const ProjDataInfo * RelatedBins:: get_proj_data_info_sptr() const @@ -68,30 +59,29 @@ RelatedBins:: get_proj_data_info_sptr() const } #endif - const DataSymmetriesForBins* -RelatedBins::get_symmetries_ptr() const -{ +RelatedBins::get_symmetries_ptr() const { return symmetries.get(); } - -RelatedBins::iterator -RelatedBins::begin() -{ return related_bins.begin();} - RelatedBins::iterator -RelatedBins::end() -{return related_bins.end();} +RelatedBins::begin() { + return related_bins.begin(); +} -RelatedBins::const_iterator -RelatedBins::begin() const -{return related_bins.begin();} +RelatedBins::iterator +RelatedBins::end() { + return related_bins.end(); +} -RelatedBins::const_iterator -RelatedBins::end() const -{return related_bins.end();} +RelatedBins::const_iterator +RelatedBins::begin() const { + return related_bins.begin(); +} +RelatedBins::const_iterator +RelatedBins::end() const { + return related_bins.end(); +} END_NAMESPACE_STIR - diff --git a/src/include/stir/recon_buildblock/RelatedDensels.h b/src/include/stir/recon_buildblock/RelatedDensels.h index 58b467551c..0c8262566e 100644 --- a/src/include/stir/recon_buildblock/RelatedDensels.h +++ b/src/include/stir/recon_buildblock/RelatedDensels.h @@ -40,18 +40,16 @@ START_NAMESPACE_STIR class DataSymmetriesForDensels; -/*! +/*! \ingroup symmetries - \brief This class contains all information about a set of densels related + \brief This class contains all information about a set of densels related by symmetry. */ -class RelatedDensels -{ +class RelatedDensels { public: - //! typedefs for iterator support - + //! typedefs for iterator support - typedef std::random_access_iterator_tag iterator_category; + typedef std::random_access_iterator_tag iterator_category; typedef Densel value_type; typedef value_type& reference; typedef const value_type& const_reference; @@ -66,8 +64,8 @@ class RelatedDensels typedef vector::iterator iterator; typedef vector::const_iterator const_iterator; #endif - //!Default constructor: creates no densels, no symmetries - inline RelatedDensels(); + //! Default constructor: creates no densels, no symmetries + inline RelatedDensels(); //! get the number of related densels inline int get_num_related_densels() const; @@ -75,36 +73,31 @@ class RelatedDensels //! get 'basic' densel coordinates inline Densel get_basic_densel() const; - // get the pointer to a ProjDataInfo class + // get the pointer to a ProjDataInfo class // inline const ProjDataInfo * get_proj_data_info_sptr() const; //! return the symmetries used - inline const DataSymmetriesForDensels* get_symmetries_ptr() const ; - + inline const DataSymmetriesForDensels* get_symmetries_ptr() const; + //! get an empty copy RelatedDensels get_empty_copy() const; - // basic iterator support + // basic iterator support //! use to initialise an iterator to the first element of the vector - inline iterator begin(); - //! iterator 'past' the last element of the vector - inline iterator end(); - //! use to initialise an iterator to the first element of the (const) vector - inline const_iterator begin() const; - //! iterator 'past' the last element of the (const) vector - inline const_iterator end() const; - - + inline iterator begin(); + //! iterator 'past' the last element of the vector + inline iterator end(); + //! use to initialise an iterator to the first element of the (const) vector + inline const_iterator begin() const; + //! iterator 'past' the last element of the (const) vector + inline const_iterator end() const; private: - std::vector related_densels; - shared_ptr symmetries; - //! a private constructor which sets the members - inline RelatedDensels(const std::vector& related_densels, - const shared_ptr& symmetries_used); - - + std::vector related_densels; + shared_ptr symmetries; + //! a private constructor which sets the members + inline RelatedDensels(const std::vector& related_densels, const shared_ptr& symmetries_used); }; END_NAMESPACE_STIR @@ -112,5 +105,3 @@ END_NAMESPACE_STIR #include "stir/recon_buildblock/RelatedDensels.inl" #endif //__RelatedDensels_H__ - - diff --git a/src/include/stir/recon_buildblock/RelatedDensels.inl b/src/include/stir/recon_buildblock/RelatedDensels.inl index 7d8f640225..e46180a67f 100644 --- a/src/include/stir/recon_buildblock/RelatedDensels.inl +++ b/src/include/stir/recon_buildblock/RelatedDensels.inl @@ -34,31 +34,23 @@ START_NAMESPACE_STIR +RelatedDensels::RelatedDensels() : related_densels(), symmetries() {} - -RelatedDensels::RelatedDensels() -:related_densels(),symmetries() -{} - -RelatedDensels::RelatedDensels(const std::vector< Densel>& related_densels_v, - const shared_ptr& symmetries_used) -:related_densels(related_densels_v),symmetries(symmetries_used) -{} +RelatedDensels::RelatedDensels(const std::vector& related_densels_v, + const shared_ptr& symmetries_used) + : related_densels(related_densels_v), symmetries(symmetries_used) {} int -RelatedDensels::get_num_related_densels() const -{ +RelatedDensels::get_num_related_densels() const { return related_densels.size(); } Densel -RelatedDensels::get_basic_densel() const -{ +RelatedDensels::get_basic_densel() const { assert(related_densels.size() != 0); return related_densels[0]; } - #if 0 const ProjDataInfo * RelatedDensels:: get_proj_data_info_sptr() const @@ -68,30 +60,29 @@ RelatedDensels:: get_proj_data_info_sptr() const } #endif - const DataSymmetriesForDensels* -RelatedDensels::get_symmetries_ptr() const -{ +RelatedDensels::get_symmetries_ptr() const { return symmetries.get(); } - -RelatedDensels::iterator -RelatedDensels::begin() -{ return related_densels.begin();} - RelatedDensels::iterator -RelatedDensels::end() -{return related_densels.end();} +RelatedDensels::begin() { + return related_densels.begin(); +} -RelatedDensels::const_iterator -RelatedDensels::begin() const -{return related_densels.begin();} +RelatedDensels::iterator +RelatedDensels::end() { + return related_densels.end(); +} -RelatedDensels::const_iterator -RelatedDensels::end() const -{return related_densels.end();} +RelatedDensels::const_iterator +RelatedDensels::begin() const { + return related_densels.begin(); +} +RelatedDensels::const_iterator +RelatedDensels::end() const { + return related_densels.end(); +} END_NAMESPACE_STIR - diff --git a/src/include/stir/recon_buildblock/RelativeDifferencePrior.h b/src/include/stir/recon_buildblock/RelativeDifferencePrior.h index ad755df09b..84228a069f 100644 --- a/src/include/stir/recon_buildblock/RelativeDifferencePrior.h +++ b/src/include/stir/recon_buildblock/RelativeDifferencePrior.h @@ -28,11 +28,9 @@ */ - #ifndef __stir_recon_buildblock_RelativeDifferencePrior_H__ #define __stir_recon_buildblock_RelativeDifferencePrior_H__ - #include "stir/RegisteredParsingObject.h" #include "stir/recon_buildblock/GeneralisedPrior.h" #include "stir/Array.h" @@ -42,7 +40,6 @@ START_NAMESPACE_STIR - /*! \ingroup priors \brief @@ -52,7 +49,8 @@ START_NAMESPACE_STIR \f[ g_r = \sum_{dr} w_{dr} * - \frac{\left(\lambda_{j}-\lambda_{k}\right)\left(\gamma\left|\lambda_{j}-\lambda_{k}\right|+\lambda_{j}+3 \lambda_{k} + 2 \epsilon \right)} + \frac{\left(\lambda_{j}-\lambda_{k}\right)\left(\gamma\left|\lambda_{j}-\lambda_{k}\right|+\lambda_{j}+3 \lambda_{k} + 2 +\epsilon \right)} {\left(\lambda_{j}+\lambda_{k}+\gamma\left|\lambda_{j}-\lambda_{k}\right| + \epsilon \right)^{2}} * \kappa_r * \kappa_{r+dr} \f] @@ -65,7 +63,7 @@ START_NAMESPACE_STIR “A Concave Prior Penalizing Relative Differences for Maximum-a-Posteriori Reconstruction in Emission Tomography,” vol. 49, no. 1, pp. 56–60, 2002. - The \f$\kappa\f$ image can be used to have spatially-varying penalties such as in + The \f$\kappa\f$ image can be used to have spatially-varying penalties such as in Jeff Fessler's papers. It should have identical dimensions to the image for which the penalty is computed. If \f$\kappa\f$ is not set, this class will effectively use 1 for all \f$\kappa\f$'s. @@ -77,7 +75,7 @@ START_NAMESPACE_STIR These are the keywords that can be used in addition to the ones in GeneralPrior. \verbatim Relative Difference Prior Parameters:= - ; next defaults to 0, set to 1 for 2D inverse Euclidean weights, 0 for 3D + ; next defaults to 0, set to 1 for 2D inverse Euclidean weights, 0 for 3D only 2D:= 0 ; next can be used to set weights explicitly. Needs to be a 3D array (of floats). ' value of only_2D is ignored @@ -90,53 +88,41 @@ START_NAMESPACE_STIR ; kappa filename:= ; use next parameter to get gradient images at every subiteration ; see class documentation - gradient filename prefix:= + gradient filename prefix:= END Relative Difference Prior Parameters:= \endverbatim */ template -class RelativeDifferencePrior: public - RegisteredParsingObject< RelativeDifferencePrior, - GeneralisedPrior >, - GeneralisedPrior > - > -{ - private: - typedef - RegisteredParsingObject< RelativeDifferencePrior, - GeneralisedPrior >, - GeneralisedPrior > - > - base_type; - - public: +class RelativeDifferencePrior + : public RegisteredParsingObject, GeneralisedPrior>, + GeneralisedPrior>> { +private: + typedef RegisteredParsingObject, GeneralisedPrior>, + GeneralisedPrior>> + base_type; + +public: //! Name which will be used when parsing a GeneralisedPrior object - static const char * const registered_name; + static const char* const registered_name; //! Default constructor RelativeDifferencePrior(); //! Constructs it explicitly RelativeDifferencePrior(const bool only_2D, float penalization_factor, float gamma, float epsilon); - - virtual bool - parabolic_surrogate_curvature_depends_on_argument() const - { return false; } - //! compute the value of the function - double - compute_value(const DiscretisedDensity<3,elemT> ¤t_image_estimate); + virtual bool parabolic_surrogate_curvature_depends_on_argument() const { return false; } - //! compute gradient - void compute_gradient(DiscretisedDensity<3,elemT>& prior_gradient, - const DiscretisedDensity<3,elemT> ¤t_image_estimate); + //! compute the value of the function + double compute_value(const DiscretisedDensity<3, elemT>& current_image_estimate); + //! compute gradient + void compute_gradient(DiscretisedDensity<3, elemT>& prior_gradient, const DiscretisedDensity<3, elemT>& current_image_estimate); - virtual Succeeded - add_multiplication_with_approximate_Hessian(DiscretisedDensity<3,elemT>& output, - const DiscretisedDensity<3,elemT>& input) const; + virtual Succeeded add_multiplication_with_approximate_Hessian(DiscretisedDensity<3, elemT>& output, + const DiscretisedDensity<3, elemT>& input) const; //! get the gamma value used in RDP float get_gamma() const; @@ -148,26 +134,25 @@ class RelativeDifferencePrior: public //! set the epsilon value used in the RDP void set_epsilon(float e); - //! get penalty weights for the neigbourhood - Array<3,float> get_weights() const; + Array<3, float> get_weights() const; //! set penalty weights for the neigbourhood - void set_weights(const Array<3,float>&); + void set_weights(const Array<3, float>&); //! get current kappa image /*! \warning As this function returns a shared_ptr, this is dangerous. You should not modify the image by manipulating the image refered to by this pointer. Unpredictable results will occur. */ - shared_ptr > get_kappa_sptr() const; + shared_ptr> get_kappa_sptr() const; //! set kappa image - void set_kappa_sptr(const shared_ptr >&); + void set_kappa_sptr(const shared_ptr>&); //! Has to be called before using this object - virtual Succeeded set_up(shared_ptr > const& target_sptr); - + virtual Succeeded set_up(shared_ptr> const& target_sptr); + protected: //! Create variable gamma for Relative Difference Penalty float gamma; @@ -179,32 +164,31 @@ class RelativeDifferencePrior: public bool only_2D; //! filename prefix for outputing the gradient whenever compute_gradient() is called. /*! An internal counter is used to keep track of the number of times the - gradient is computed. The filename will be constructed by concatenating + gradient is computed. The filename will be constructed by concatenating gradient_filename_prefix and the counter. */ std::string gradient_filename_prefix; //! penalty weights - /*! + /*! \todo This member is mutable at present because some const functions initialise it. That initialisation should be moved to a new set_up() function. */ - mutable Array<3,float> weights; + mutable Array<3, float> weights; //! Filename for the \f$\kappa\f$ image that will be read by post_processing() std::string kappa_filename; //! Check that the prior is ready to be used - virtual void check(DiscretisedDensity<3,elemT> const& current_image_estimate) const; + virtual void check(DiscretisedDensity<3, elemT> const& current_image_estimate) const; virtual void set_defaults(); virtual void initialise_keymap(); virtual bool post_processing(); - private: - shared_ptr > kappa_ptr; -}; +private: + shared_ptr> kappa_ptr; +}; END_NAMESPACE_STIR #endif - diff --git a/src/include/stir/recon_buildblock/SPECTUB_Tools.h b/src/include/stir/recon_buildblock/SPECTUB_Tools.h index 13e686e81f..4735b2b340 100644 --- a/src/include/stir/recon_buildblock/SPECTUB_Tools.h +++ b/src/include/stir/recon_buildblock/SPECTUB_Tools.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2013, Biomedical Image Group (GIB), Universitat de Barcelona, Barcelona, Spain. + Copyright (c) 2013, Biomedical Image Group (GIB), Universitat de Barcelona, Barcelona, Spain. Copyright (c) 2013, University College London This file is part of STIR. @@ -31,355 +31,333 @@ namespace SPECTUB { //::: srtuctures :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //! collimator parameters structure -typedef struct -{ - int num; // number of collimator (see weight_64.cpp for options) - bool do_fb; // true: fanbeam collimator || false: parallel collimator - - //... parallel collimator parameters ..................... - - float A; // linear factor for dependency of sigma on distance: sigma=A*dist+B (parallel, fanbeam-vertical) - float B; // independent factor for dependency of sigma on distance: sigma=A*dist+B (parallel, fanbeam-vertical) - - //... fanbeam collimator parameters ...................... - - float F; // Focal length (fanbeam) - float L; // collimator to detector distance (?) (fanbeam) - float A_h; // linear factor for dependency of sigma on distance (fanbeam horizontal) - float A_v; // linear factor for dependency of sigma on distance (fanbeam horizontal) - float D; // collimator thicknes?? (fanbeam) - float w; // collimator thickness (?) (fanbeam) - float insgm; // intrinsic sigma (cristal resolution) (fanbeam) - +typedef struct { + int num; // number of collimator (see weight_64.cpp for options) + bool do_fb; // true: fanbeam collimator || false: parallel collimator + + //... parallel collimator parameters ..................... + + float A; // linear factor for dependency of sigma on distance: sigma=A*dist+B (parallel, fanbeam-vertical) + float B; // independent factor for dependency of sigma on distance: sigma=A*dist+B (parallel, fanbeam-vertical) + + //... fanbeam collimator parameters ...................... + + float F; // Focal length (fanbeam) + float L; // collimator to detector distance (?) (fanbeam) + float A_h; // linear factor for dependency of sigma on distance (fanbeam horizontal) + float A_v; // linear factor for dependency of sigma on distance (fanbeam horizontal) + float D; // collimator thicknes?? (fanbeam) + float w; // collimator thickness (?) (fanbeam) + float insgm; // intrinsic sigma (cristal resolution) (fanbeam) + } collim_type; //! structure for bin information -typedef struct -{ - int Nrow; // number of rows - int Ncol; // number of columns - int Nsli; // number of slices - int Npix; // number of pixels (axial planes) - int Nvox; // number of voxels (the whole volume) - - int first_sl; // first slice to reconstruct (0->Nslic-1) - int last_sl; // last slice to reconstruct + 1 (end of the 'for' loop) (1->Nslic) - - float Nrowd2; // half of Nrow - float Ncold2; // half of Ncol - float Nslid2; // half of Nsli - - float Xcmd2; // Half of the size of the volume, dimension x (cm); - float Ycmd2; // Half of the size of the volume, dimension y (cm); - float Zcmd2; // Half of the size of the volume, dimension z (cm); - - float szcm; // voxel size (side length in cm) - float thcm; // voxel thickness (cm) - - float x0; // x coordinate (cm, ref center of volume) of the first voxel - float y0; // y coordinate (cm, ref center of volume) of the first voxel - float z0; // z coordinate (cm, ref center of volume) of the first voxel - - float *val; // array of values - -} volume_type; +typedef struct { + int Nrow; // number of rows + int Ncol; // number of columns + int Nsli; // number of slices + int Npix; // number of pixels (axial planes) + int Nvox; // number of voxels (the whole volume) + + int first_sl; // first slice to reconstruct (0->Nslic-1) + int last_sl; // last slice to reconstruct + 1 (end of the 'for' loop) (1->Nslic) + float Nrowd2; // half of Nrow + float Ncold2; // half of Ncol + float Nslid2; // half of Nsli + + float Xcmd2; // Half of the size of the volume, dimension x (cm); + float Ycmd2; // Half of the size of the volume, dimension y (cm); + float Zcmd2; // Half of the size of the volume, dimension z (cm); + + float szcm; // voxel size (side length in cm) + float thcm; // voxel thickness (cm) + + float x0; // x coordinate (cm, ref center of volume) of the first voxel + float y0; // y coordinate (cm, ref center of volume) of the first voxel + float z0; // z coordinate (cm, ref center of volume) of the first voxel + + float* val; // array of values + +} volume_type; //! structure for projection information -typedef struct -{ - int Nbin; // length of the detection line in bins (number of bins per line) - float lngcm; // length of the detection line in cm. - float szcm; // bin size in cm - - int Nsli; // number of slices - float thcm; // slice thickness in cm - - int Nang; // number of projection angles - float ang0; // initial projection angle. degrees from upper detection plane (parallel to table). Negative for CW rotacions (see manual) - float incr; // angle increment between two consecutive projection angles. Degrees. Negative for CW, Positive for CCW - - int NOS; // number of subsets in which to split the matrix - int NangOS; // Number of angles in each subset = Nang/NOS - int Nbp; // number of bins in a 2D projection=lng*Nsli - int Nbt; // total number of bins= Nbp*Nang - int NbOS; // total number of bins per subset= Nbp*NangOS = Nbt/NOS - int *order; // order of the angles of projection (array formed by indexs of angles belonging to consecutive subsets) - - float Nbind2; // length of the detection line (in bins) divided by 2 - float lngcmd2; // length of the detection line in cm divided by 2 - float Nslid2; // number of slices divided by 2 - +typedef struct { + int Nbin; // length of the detection line in bins (number of bins per line) + float lngcm; // length of the detection line in cm. + float szcm; // bin size in cm + + int Nsli; // number of slices + float thcm; // slice thickness in cm + + int Nang; // number of projection angles + float ang0; // initial projection angle. degrees from upper detection plane (parallel to table). Negative for CW rotacions (see + // manual) + float incr; // angle increment between two consecutive projection angles. Degrees. Negative for CW, Positive for CCW + + int NOS; // number of subsets in which to split the matrix + int NangOS; // Number of angles in each subset = Nang/NOS + int Nbp; // number of bins in a 2D projection=lng*Nsli + int Nbt; // total number of bins= Nbp*Nang + int NbOS; // total number of bins per subset= Nbp*NangOS = Nbt/NOS + int* order; // order of the angles of projection (array formed by indexs of angles belonging to consecutive subsets) + + float Nbind2; // length of the detection line (in bins) divided by 2 + float lngcmd2; // length of the detection line in cm divided by 2 + float Nslid2; // number of slices divided by 2 + } proj_type; //! complementary information (matrix header) -typedef struct -{ - int subset_ind; // subset index of this matrix (-1: all subsets in a single file) - int *index; // included angles into this subset (index. Multiply by increm to get corresponding angles in degrees) - - float *Rrad; // Rotation radius (one value for each projection angle) - float min_w; // minimum weight to be taken into account - float psfres; // spatial resolution of continous distributions in PSF calculation - float maxsigm; // maximum number of sigmas in PSF calculation - - bool fixed_Rrad; // true: fixed radius of projection || false: variable radius of projection - bool do_psf; // true: to correct for PSF || false: do not correct for PSF - bool do_psf_3d; // true: 3d correction for PSF || false: 2d correction for PSF - bool predef_col; // true: predefined collimator || false: user defined PSF parametres - bool do_att; // true: to correct for attenuation || false: do not correct for attenuation - bool do_full_att; // true: diff att for each PSF bin || false: the whole PSF has the same att. factor (central line) - bool do_msk; // true: weights just inside msk || false: weights for the whole FOV - bool do_msk_slc; // true: weights for several slices || false: weights for all slices - bool do_msk_cyl; // true: to use cylinder as a mask || false: not to use cylinder as a mask - bool do_msk_att; // true: to use att map as a mask || false: not to use att map as a mask - bool do_msk_file; // true: explicit mask || false: not to use explicit mask - - std::string att_fn; // attenuation map filename - std::string msk_fn; // explicit mask filename - std::string col_fn; // collimator parameters filename - std::string Rrad_fn; // rotation radius file name - - volume_type vol; // - proj_type prj; // - collim_type COL; // collimator structure (see weight3d_64b.cpp for options) - +typedef struct { + int subset_ind; // subset index of this matrix (-1: all subsets in a single file) + int* index; // included angles into this subset (index. Multiply by increm to get corresponding angles in degrees) + + float* Rrad; // Rotation radius (one value for each projection angle) + float min_w; // minimum weight to be taken into account + float psfres; // spatial resolution of continous distributions in PSF calculation + float maxsigm; // maximum number of sigmas in PSF calculation + + bool fixed_Rrad; // true: fixed radius of projection || false: variable radius of projection + bool do_psf; // true: to correct for PSF || false: do not correct for PSF + bool do_psf_3d; // true: 3d correction for PSF || false: 2d correction for PSF + bool predef_col; // true: predefined collimator || false: user defined PSF parametres + bool do_att; // true: to correct for attenuation || false: do not correct for attenuation + bool do_full_att; // true: diff att for each PSF bin || false: the whole PSF has the same att. factor (central line) + bool do_msk; // true: weights just inside msk || false: weights for the whole FOV + bool do_msk_slc; // true: weights for several slices || false: weights for all slices + bool do_msk_cyl; // true: to use cylinder as a mask || false: not to use cylinder as a mask + bool do_msk_att; // true: to use att map as a mask || false: not to use att map as a mask + bool do_msk_file; // true: explicit mask || false: not to use explicit mask + + std::string att_fn; // attenuation map filename + std::string msk_fn; // explicit mask filename + std::string col_fn; // collimator parameters filename + std::string Rrad_fn; // rotation radius file name + + volume_type vol; // + proj_type prj; // + collim_type COL; // collimator structure (see weight3d_64b.cpp for options) + } wmh_type; //! weight_mat structure definition. Structure for reading weight matrix -typedef struct -{ - //weight matrix dimensions - - int ne; //nonzero elements - int NbOS; //dimension 1 (rows) of the weight matrix (NbOS or NBt) - int Nvox; //dimension 2 (columns) of the weight matrix (Nvox) - - //weight matrix values - - float *ar; //array of nonzero elements of weight matrix (by rows) - int *ja; //array of the column index of the above elements - int *ia; //array containing the indexes of the previous vector where a row change happens - - bool do_save_wmh; //to save or not to save weight_mat header info into weight_mat file +typedef struct { + // weight matrix dimensions + + int ne; // nonzero elements + int NbOS; // dimension 1 (rows) of the weight matrix (NbOS or NBt) + int Nvox; // dimension 2 (columns) of the weight matrix (Nvox) + + // weight matrix values + + float* ar; // array of nonzero elements of weight matrix (by rows) + int* ja; // array of the column index of the above elements + int* ia; // array containing the indexes of the previous vector where a row change happens + + bool do_save_wmh; // to save or not to save weight_mat header info into weight_mat file } wm_type; //! weight_mat_da structure definition. Structure for generating weight matrix -typedef struct -{ - int NbOS; // dimension 1 (rows) of the weight matrix (NbOS or NBt) - int Nvox; // dimension 2 (columns) of the weight matrix (Nvox) - float **val; // double array to store weights (index of the projection element, number of weight for that element) - int **col; // double array to store column indexs of the above element (index of the projection element, number of weight for that element) - int *ne; // array indicating how many elements has been stored for each element of projection - - //... filename ............................................. - - std::string fn; // matrix base name (filename without extension index) - std::string OSfn; // matrix filename - std::string fn_hdr; // matrix header file name - - //... indexs for STIR format ............................... - - int *na, *nb, *ns; //indexs for projection elements (angle, bin, slice respec.) - short int *nx, *ny, *nz; //indexs for image elements (x,y,z) - - //... format ............................................... - - bool do_save_wmh; // to save or not to save weight_mat header info into weight_mat file - bool do_save_STIR; // to save weight matrix with STIR format - +typedef struct { + int NbOS; // dimension 1 (rows) of the weight matrix (NbOS or NBt) + int Nvox; // dimension 2 (columns) of the weight matrix (Nvox) + float** val; // double array to store weights (index of the projection element, number of weight for that element) + int** col; // double array to store column indexs of the above element (index of the projection element, number of weight for + // that element) + int* ne; // array indicating how many elements has been stored for each element of projection + + //... filename ............................................. + + std::string fn; // matrix base name (filename without extension index) + std::string OSfn; // matrix filename + std::string fn_hdr; // matrix header file name + + //... indexs for STIR format ............................... + + int *na, *nb, *ns; // indexs for projection elements (angle, bin, slice respec.) + short int *nx, *ny, *nz; // indexs for image elements (x,y,z) + + //... format ............................................... + + bool do_save_wmh; // to save or not to save weight_mat header info into weight_mat file + bool do_save_STIR; // to save weight matrix with STIR format + } wm_da_type; //! structure for distribution function information -typedef struct -{ - int lng; // length (in discretization intervals) (odd number) - int lngd2; // half of the length (in discretization intervals) (lng-1)/2 - float res; // spatial resolution of distfunc (discretization interval) - float *val; // array of values - float *acu; // distribution function values (cumulative sum) +typedef struct { + int lng; // length (in discretization intervals) (odd number) + int lngd2; // half of the length (in discretization intervals) (lng-1)/2 + float res; // spatial resolution of distfunc (discretization interval) + float* val; // array of values + float* acu; // distribution function values (cumulative sum) } discrf_type; //! structure for PSF information -typedef struct -{ - int maxszb; // maximum size in bins (for allocation purposes) - - int di; // discretization interval (to reduce spatial resolution to bin resolution). (int: #points) - int *ind; // projection indexs for the bins of the PSF (horizontal) - int Nib; // actual number of bins forming the PSF (length of PSF in bins) - - float sgmcm; // sigma of the PSF in cm - float lngcm; // length of PSF (in cm) - float lngcmd2; // half of the length of PSF (in cm) - float *val; // array of values - float efres; // effective resolution (psfres rescaled to real psf length) - +typedef struct { + int maxszb; // maximum size in bins (for allocation purposes) + + int di; // discretization interval (to reduce spatial resolution to bin resolution). (int: #points) + int* ind; // projection indexs for the bins of the PSF (horizontal) + int Nib; // actual number of bins forming the PSF (length of PSF in bins) + + float sgmcm; // sigma of the PSF in cm + float lngcm; // length of PSF (in cm) + float lngcmd2; // half of the length of PSF (in cm) + float* val; // array of values + float efres; // effective resolution (psfres rescaled to real psf length) + } psf1d_type; //! structure for distribution function information -typedef struct -{ - int maxszb_h; // maximum size in bins horizontal (for allocation purposes) - int maxszb_v; // maximum size in bins vertical (for allocation purposes) - int maxszb_t; // maximum size in bins total (for allocation purposes) - - int *ib; // projection indexs for the bins of the PSF (horizontal) - int *jb; // projection indexs for the bins of the PSF (vertical) - int Nib; // actual number of bins forming the PSF (length of PSF in bins) - - float *val; // array of values - +typedef struct { + int maxszb_h; // maximum size in bins horizontal (for allocation purposes) + int maxszb_v; // maximum size in bins vertical (for allocation purposes) + int maxszb_t; // maximum size in bins total (for allocation purposes) + + int* ib; // projection indexs for the bins of the PSF (horizontal) + int* jb; // projection indexs for the bins of the PSF (vertical) + int Nib; // actual number of bins forming the PSF (length of PSF in bins) + + float* val; // array of values + } psf2da_type; //! structure to store angles values, indices and ratios -typedef struct -{ - int ind; // index of angle considering the whole set of projections (sequential order: 0->Nang-1) - int indOS; // index of angle considering the subjet - int iOS_proj; // index of the first bin for this angle (in subset of projections) - - float cos; // coninus of the angle - float sin; // sinus of the angle - - // parametres for describng the trapezoidal projection of a square voxel - - float p; // plateau higness - float m; // slope of the trapezoid - float n; // independent term of the slope - int N1; // index of the first vertice (end of plateau) in DX units - int N2; // index of the second vertice (end of the slope) in DX units - discrf_type vxprj; // projection of a square voxel in this direction (for no PSF) - - // variable rotation radius - - float Rrad ; // rotation radius for this angle - - // first bin position and increments - - float xbin0; // x coordinate for the first bin of the detection line corresponding to this angle - float ybin0; // y coordinate for the first bin of the detection line corresponding to this angle - float incx; // increment in x to the following bin in detection line - float incy; // increment in y to the following bin in detection line - -} angle_type; +typedef struct { + int ind; // index of angle considering the whole set of projections (sequential order: 0->Nang-1) + int indOS; // index of angle considering the subjet + int iOS_proj; // index of the first bin for this angle (in subset of projections) + float cos; // coninus of the angle + float sin; // sinus of the angle -//! structure for voxel information -typedef struct -{ - float szcm; // voxel size (side length in cm) - float thcm; // voxel thickness (cm) - - int irow; // row index - int icol; // column index - int islc; // slice index - int ip; // in plane index (considering the slice as an array) of the voxel - int iv; // volume index (considering the volume as an array) of the voxel - - float x; // x coordinate (cm, ref center of volume) - float y; // y coordinate (cm, ref center of volume) - float z; // z coordinate (cm, ref center of volume) - float x1; // x coordinade in rotated framework - - float dv2dp; // distance from voxel to detection plane - float costhe; // cosinus of theta (angle between focal-voxel line and line perpendicular to detection plane) (fanbeam) - float xdc; // distance (cm over detection line) from projected voxel to the center of the detection line - float xd0; // distance (cm over detection line) from projected voxel to the begin of the detection line - float zd0; // distance (cm) to the lowest plane of the volume - -} voxel_type; + // parametres for describng the trapezoidal projection of a square voxel -//! structure for bin information -typedef struct -{ - float szcm; // bin size (cm) - float szcmd2; // half of the above value - float thcm; // bin thickness (cm) - float thcmd2; // bin thickness (cm) - - float x; // x coordinate (cm, ref center of volume) - float y; // y coordinate (cm, ref center of volume) - float z; // z coordinate (cm, ref center of volume) - - float szdx; // bin size in resolution units - float thdx; // bin thickness in resolution units - -} bin_type; + float p; // plateau higness + float m; // slope of the trapezoid + float n; // independent term of the slope + int N1; // index of the first vertice (end of plateau) in DX units + int N2; // index of the second vertice (end of the slope) in DX units + discrf_type vxprj; // projection of a square voxel in this direction (for no PSF) -//! structure for attenuation calculus -typedef struct -{ - float *dl; // distance of attenuation path on each crossed voxel of the attenuation map - int *iv; // in-plane index (considering slices of attmap as an array) of any crossed voxel of the attenuation map - int lng; // number of elements in the attenuation path - int maxlng; // maximum number of elements in the attenuation path (for allocation) - -} attpth_type; + // variable rotation radius + float Rrad; // rotation radius for this angle -//::: functions ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + // first bin position and increments + float xbin0; // x coordinate for the first bin of the detection line corresponding to this angle + float ybin0; // y coordinate for the first bin of the detection line corresponding to this angle + float incx; // increment in x to the following bin in detection line + float incy; // increment in y to the following bin in detection line -//... functions from wmtools_SPECT.cpp ......................................... +} angle_type; +//! structure for voxel information +typedef struct { + float szcm; // voxel size (side length in cm) + float thcm; // voxel thickness (cm) + + int irow; // row index + int icol; // column index + int islc; // slice index + int ip; // in plane index (considering the slice as an array) of the voxel + int iv; // volume index (considering the volume as an array) of the voxel + + float x; // x coordinate (cm, ref center of volume) + float y; // y coordinate (cm, ref center of volume) + float z; // z coordinate (cm, ref center of volume) + float x1; // x coordinade in rotated framework + + float dv2dp; // distance from voxel to detection plane + float costhe; // cosinus of theta (angle between focal-voxel line and line perpendicular to detection plane) (fanbeam) + float xdc; // distance (cm over detection line) from projected voxel to the center of the detection line + float xd0; // distance (cm over detection line) from projected voxel to the begin of the detection line + float zd0; // distance (cm) to the lowest plane of the volume -void write_wm_FC (); // to write double array weight matrix +} voxel_type; -void write_wm_hdr (); // to write header of a matrix +//! structure for bin information +typedef struct { + float szcm; // bin size (cm) + float szcmd2; // half of the above value + float thcm; // bin thickness (cm) + float thcmd2; // bin thickness (cm) -void write_wm_STIR (); // to write matrix in STIR format + float x; // x coordinate (cm, ref center of volume) + float y; // y coordinate (cm, ref center of volume) + float z; // z coordinate (cm, ref center of volume) + float szdx; // bin size in resolution units + float thdx; // bin thickness in resolution units -void index_calc ( int *indexs ); // to calculate projection index order in subsets +} bin_type; -void read_Rrad (); // to read variable rotation radius from a text file (1 radius per line) +//! structure for attenuation calculus +typedef struct { + float* dl; // distance of attenuation path on each crossed voxel of the attenuation map + int* iv; // in-plane index (considering slices of attmap as an array) of any crossed voxel of the attenuation map + int lng; // number of elements in the attenuation path + int maxlng; // maximum number of elements in the attenuation path (for allocation) -// -//void col_params ( collim_type *COL ); // to fill collimator structure -// -//void read_col_params ( collim_type *COL); // to read collimator parameters from a file +} attpth_type; + +//::: functions ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +//... functions from wmtools_SPECT.cpp ......................................... -void fill_ang ( angle_type *ang ); // to fill angle structure +void write_wm_FC(); // to write double array weight matrix -void generate_msk ( bool *msk_3d, bool *msk_2d, float *att, volume_type *vol); // to create a boolean mask for wm (no weights outside the msk) +void write_wm_hdr(); // to write header of a matrix -void read_msk_file ( bool * msk ); // to read mask from a file +void write_wm_STIR(); // to write matrix in STIR format +void index_calc(int* indexs); // to calculate projection index order in subsets -void read_att_map ( float *attmap ); // to read attenuation map from a file +void read_Rrad(); // to read variable rotation radius from a text file (1 radius per line) + +// +// void col_params ( collim_type *COL ); // to fill collimator structure +// +// void read_col_params ( collim_type *COL); // to read collimator parameters from a file +void fill_ang(angle_type* ang); // to fill angle structure -int max_psf_szb ( angle_type *ang ); +void generate_msk(bool* msk_3d, bool* msk_2d, float* att, + volume_type* vol); // to create a boolean mask for wm (no weights outside the msk) -float calc_sigma_h ( voxel_type vox, collim_type COL); +void read_msk_file(bool* msk); // to read mask from a file -float calc_sigma_v ( voxel_type vox, collim_type COL); +void read_att_map(float* attmap); // to read attenuation map from a file +int max_psf_szb(angle_type* ang); -char *itoa ( int n, char *s); // to conver integer to ascii +float calc_sigma_h(voxel_type vox, collim_type COL); -void free_wm ( wm_type *f ); // to free weight_mat +float calc_sigma_v(voxel_type vox, collim_type COL); -void free_wm_da ( wm_da_type *f ); // to free weight_mat_da +char* itoa(int n, char* s); // to conver integer to ascii +void free_wm(wm_type* f); // to free weight_mat -void error_wmtools_SPECT(int nerr, std::string txt); // error messages in wm_SPECT +void free_wm_da(wm_da_type* f); // to free weight_mat_da +void error_wmtools_SPECT(int nerr, std::string txt); // error messages in wm_SPECT //... functions from wm_SPECT.2.0............................ -//int wm_SPECT( std::string inputFile); +// int wm_SPECT( std::string inputFile); // void error_wm_SPECT( int nerr, std::string txt); //list of error messages ////void wm_inputs( std::string fileName, proj_type * prj, volume_type *vol, voxel_type *vox, bin_type *bin ); -//void wm_inputs(char **argv, -// int argc, +// void wm_inputs(char **argv, +// int argc, // proj_type *prj, // volume_type *vol, // voxel_type *vox, @@ -387,17 +365,17 @@ void error_wmtools_SPECT(int nerr, std::string txt); // error messages in wm_ // // ////void read_inputs( std::string param, proj_type * prj, volume_type *vol, voxel_type *vox, bin_type *bin ); -//void read_inputs(vector param, +// void read_inputs(vector param, // proj_type *prj, // volume_type *vol, // voxel_type *vox, //// bin_type *bin); // -//extern wmh_type wmh; // weight matrix header. Global variable +// extern wmh_type wmh; // weight matrix header. Global variable // -//extern wm_da_type wm; // double array weight matrix structure. Global variable +// extern wm_da_type wm; // double array weight matrix structure. Global variable // -//extern float * Rrad; // variable projection radius (in acquisition order) +// extern float * Rrad; // variable projection radius (in acquisition order) } // namespace SPECTUB diff --git a/src/include/stir/recon_buildblock/SPECTUB_Weight3d.h b/src/include/stir/recon_buildblock/SPECTUB_Weight3d.h index 3595256d44..a33678ae2a 100644 --- a/src/include/stir/recon_buildblock/SPECTUB_Weight3d.h +++ b/src/include/stir/recon_buildblock/SPECTUB_Weight3d.h @@ -1,21 +1,21 @@ - /* - Copyright (c) 2013, Biomedical Image Group (GIB), Universitat de Barcelona, Barcelona, Spain. - Copyright (c) 2013, University College London - This file is part of STIR. +/* + Copyright (c) 2013, Biomedical Image Group (GIB), Universitat de Barcelona, Barcelona, Spain. + Copyright (c) 2013, University College London + This file is part of STIR. - This file is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. + This file is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. - This file is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. - See STIR/LICENSE.txt for details + See STIR/LICENSE.txt for details - \author Carles Falcon + \author Carles Falcon */ @@ -28,9 +28,9 @@ This namespace encompasses function to construct a projection matrix suitable for SPECT. The integration of this library into STIR is described in - Berta Marti Fuster, Carles Falcon, Charalampos Tsoumpas, Lefteris Livieratos, Pablo Aguiar, Albert Cot, Domenec Ros and Kris Thielemans, - Integration of advanced 3D SPECT modeling into the open-source STIR framework, - Med. Phys. 40, 092502 (2013); http://dx.doi.org/10.1118/1.4816676 + Berta Marti Fuster, Carles Falcon, Charalampos Tsoumpas, Lefteris Livieratos, Pablo Aguiar, Albert Cot, Domenec Ros and Kris + Thielemans, Integration of advanced 3D SPECT modeling into the open-source STIR framework, Med. Phys. 40, 092502 (2013); + http://dx.doi.org/10.1118/1.4816676 \todo Variables wm, wmh and Rrad are currently global variables. This means that this code would be very dangerous in a parallel setting. @@ -38,79 +38,55 @@ namespace SPECTUB { -/* Global variables: the matrix etc TODO +/* Global variables: the matrix etc TODO Need to be defined elsewhere A lot of the functions below modify these variables. */ - extern wm_da_type wm; //! weight (or probability) matrix - extern wmh_type wmh; //! information to construct wm - extern float * Rrad; //! radii per view - - -void wm_calculation( const int kOS, - const angle_type *const ang, - voxel_type vox, - bin_type bin, - const volume_type& vol, - const proj_type& prj, - const float *attmap, - const bool *msk_3d, - const bool *msk_2d, - const int maxszb, - const discrf_type *const gaussdens, - const int *const NITEMS - ); - -void wm_size_estimation (int kOS, - const angle_type * const ang, - voxel_type vox, - bin_type bin, - const volume_type& vol, - const proj_type& prj, - const bool * const msk_3d, - const bool *const msk_2d, - const int maxszb, - const discrf_type * const gaussdens, - int *NITEMS); +extern wm_da_type wm; //! weight (or probability) matrix +extern wmh_type wmh; //! information to construct wm +extern float* Rrad; //! radii per view +void wm_calculation(const int kOS, const angle_type* const ang, voxel_type vox, bin_type bin, const volume_type& vol, + const proj_type& prj, const float* attmap, const bool* msk_3d, const bool* msk_2d, const int maxszb, + const discrf_type* const gaussdens, const int* const NITEMS); -//... geometric component ............................................ +void wm_size_estimation(int kOS, const angle_type* const ang, voxel_type vox, bin_type bin, const volume_type& vol, + const proj_type& prj, const bool* const msk_3d, const bool* const msk_2d, const int maxszb, + const discrf_type* const gaussdens, int* NITEMS); -void calc_gauss ( discrf_type *gaussdens ); +//... geometric component ............................................ -void calc_vxprj ( angle_type *ang ); +void calc_gauss(discrf_type* gaussdens); -void voxel_projection ( voxel_type *vox, float * eff, float lngcmd2 ); +void calc_vxprj(angle_type* ang); -void fill_psf_no( psf2da_type *psf, psf1d_type *psf1d_h, const voxel_type& vox, const angle_type * const ang, float szdx ); +void voxel_projection(voxel_type* vox, float* eff, float lngcmd2); -void fill_psf_2d( psf2da_type *psf, psf1d_type *psf1d_h, const voxel_type& vox, discrf_type const*const gaussdens, float szdx ); +void fill_psf_no(psf2da_type* psf, psf1d_type* psf1d_h, const voxel_type& vox, const angle_type* const ang, float szdx); -void fill_psf_3d(psf2da_type *psf, - psf1d_type *psf1d_h, - psf1d_type *psf1d_v, - const voxel_type& vox, discrf_type const * const gaussdens, float szdx, float thdx, float thcmd2 ); +void fill_psf_2d(psf2da_type* psf, psf1d_type* psf1d_h, const voxel_type& vox, discrf_type const* const gaussdens, float szdx); -void calc_psf_bin ( float center_psf, float binszcm, discrf_type const * const vxprj, psf1d_type *psf ); +void fill_psf_3d(psf2da_type* psf, psf1d_type* psf1d_h, psf1d_type* psf1d_v, const voxel_type& vox, + discrf_type const* const gaussdens, float szdx, float thdx, float thcmd2); +void calc_psf_bin(float center_psf, float binszcm, discrf_type const* const vxprj, psf1d_type* psf); //... attenuation................................................... // not used -//void size_attpth_simple( psf2da_type *psf, voxel_type vox, volume_type vol, float *att, angle_type *ang ); - -//void size_attpth_full( psf2da_type *psf, voxel_type vox, volume_type vol, float *att, angle_type *ang ); +// void size_attpth_simple( psf2da_type *psf, voxel_type vox, volume_type vol, float *att, angle_type *ang ); -void calc_att_path ( const bin_type& bin, const voxel_type& vox, const volume_type& vol, attpth_type *attpth ); +// void size_attpth_full( psf2da_type *psf, voxel_type vox, volume_type vol, float *att, angle_type *ang ); -float calc_att ( const attpth_type *const attpth, const float *const attmap, int islc ); +void calc_att_path(const bin_type& bin, const voxel_type& vox, const volume_type& vol, attpth_type* attpth); -int comp_dist ( float dx, float dy, float dz, float dlast ); +float calc_att(const attpth_type* const attpth, const float* const attmap, int islc); +int comp_dist(float dx, float dy, float dz, float dlast); -void error_weight3d ( int nerr, const std::string& txt); // error messages in weight3d_SPECT +void error_weight3d(int nerr, const std::string& txt); // error messages in weight3d_SPECT -} // napespace SPECTUB +} // namespace SPECTUB #endif diff --git a/src/include/stir/recon_buildblock/SqrtHessianRowSum.h b/src/include/stir/recon_buildblock/SqrtHessianRowSum.h index 5bd66c672f..300678f4ca 100644 --- a/src/include/stir/recon_buildblock/SqrtHessianRowSum.h +++ b/src/include/stir/recon_buildblock/SqrtHessianRowSum.h @@ -63,99 +63,96 @@ class Succeeded; https://doi.org/10.1109/TMI.2019.2913889 */ template -class SqrtHessianRowSum: - public ParsingObject -{ +class SqrtHessianRowSum : public ParsingObject { public: - //! Default constructor - /*! calls set_defaults().*/ - SqrtHessianRowSum(); - explicit SqrtHessianRowSum(const std::string&); - - //! sets default values - /*! Sets \c use_approximate_hessian to \c true and \c compute_with_penalty to \c false - */ - void set_defaults(); - - //! The main function to compute and save the sqrt of the Hessian row sum volume - /*! Different Hessian row sum methods can be used, see compute_Hessian_row_sum() and - compute_approximate_Hessian_row_sum().*/ - void process_data(); - - //! \name get and set methods for the objective function sptr - //@{ - GeneralisedObjectiveFunction const& get_objective_function_sptr(); - void set_objective_function_sptr(const shared_ptr > &obj_fun); - //@} - - //! \name get and set methods for the input image - //@{ - shared_ptr get_input_image_sptr(); - void set_input_image_sptr(shared_ptr const& image); - //@} - - //! get method for returning the sqrt row sum image - shared_ptr get_output_target_sptr(); - - void set_up(); - - //! \name get and set methods for use approximate hessian bool - //@{ - bool get_use_approximate_hessian() const; - void set_use_approximate_hessian(bool use_approximate); - //@} - - //! \name get and set methods for the compute with penalty bool - //@{ - bool get_compute_with_penalty() const; - void set_compute_with_penalty(bool with_penalty); - //@} - - //! Computes the objective function Hessian row sum at the current image estimate. - //! Can compute the penalty's Hessian if it exists for the selected prior. - void compute_Hessian_row_sum(); - - //! Computes the approximate Hessian of the objective function. - //! Cannot use penalty's approximate Hessian, see compute_with_penalty - void compute_approximate_Hessian_row_sum(); + //! Default constructor + /*! calls set_defaults().*/ + SqrtHessianRowSum(); + explicit SqrtHessianRowSum(const std::string&); + + //! sets default values + /*! Sets \c use_approximate_hessian to \c true and \c compute_with_penalty to \c false + */ + void set_defaults(); + + //! The main function to compute and save the sqrt of the Hessian row sum volume + /*! Different Hessian row sum methods can be used, see compute_Hessian_row_sum() and + compute_approximate_Hessian_row_sum().*/ + void process_data(); + + //! \name get and set methods for the objective function sptr + //@{ + GeneralisedObjectiveFunction const& get_objective_function_sptr(); + void set_objective_function_sptr(const shared_ptr>& obj_fun); + //@} + + //! \name get and set methods for the input image + //@{ + shared_ptr get_input_image_sptr(); + void set_input_image_sptr(shared_ptr const& image); + //@} + + //! get method for returning the sqrt row sum image + shared_ptr get_output_target_sptr(); + + void set_up(); + + //! \name get and set methods for use approximate hessian bool + //@{ + bool get_use_approximate_hessian() const; + void set_use_approximate_hessian(bool use_approximate); + //@} + + //! \name get and set methods for the compute with penalty bool + //@{ + bool get_compute_with_penalty() const; + void set_compute_with_penalty(bool with_penalty); + //@} + + //! Computes the objective function Hessian row sum at the current image estimate. + //! Can compute the penalty's Hessian if it exists for the selected prior. + void compute_Hessian_row_sum(); + + //! Computes the approximate Hessian of the objective function. + //! Cannot use penalty's approximate Hessian, see compute_with_penalty + void compute_approximate_Hessian_row_sum(); protected: - private: - bool _already_setup = false; + bool _already_setup = false; - //! Objective function object - shared_ptr > objective_function_sptr; + //! Objective function object + shared_ptr> objective_function_sptr; - //! The filename the for the output sqrt row sum image - std::string output_filename; + //! The filename the for the output sqrt row sum image + std::string output_filename; - //! Used to load an image as a template or current image estimate to compute sqrt row sum - std::string input_image_filename; + //! Used to load an image as a template or current image estimate to compute sqrt row sum + std::string input_image_filename; - //! The input image, can be template or current_image_estimate, dependant on which sqrt row sum method used - shared_ptr input_image_sptr; + //! The input image, can be template or current_image_estimate, dependant on which sqrt row sum method used + shared_ptr input_image_sptr; - //! The output image that the row sum computation methods will populate - shared_ptr output_target_sptr; + //! The output image that the row sum computation methods will populate + shared_ptr output_target_sptr; - //! Used to toggle which of the two row sum methods will be utilised. - //! This toggles the usage of input_image_sptr. - //! If true, input_image_sptr is only used as a template for the output back-projection, - //! else, input_image_sptr is used as the current_image_estimate - bool use_approximate_hessian; + //! Used to toggle which of the two row sum methods will be utilised. + //! This toggles the usage of input_image_sptr. + //! If true, input_image_sptr is only used as a template for the output back-projection, + //! else, input_image_sptr is used as the current_image_estimate + bool use_approximate_hessian; - //! When computing the hessian row sum of the objective function, include the penalty term or not. - //! Does not work with use_approximate_hessian as priors do not have an approximate method. - bool compute_with_penalty; + //! When computing the hessian row sum of the objective function, include the penalty term or not. + //! Does not work with use_approximate_hessian as priors do not have an approximate method. + bool compute_with_penalty; - //! File-format to save images - shared_ptr > output_file_format_sptr; + //! File-format to save images + shared_ptr> output_file_format_sptr; - //! used to check acceptable parameter ranges, etc... - bool post_processing(); - void initialise_keymap(); + //! used to check acceptable parameter ranges, etc... + bool post_processing(); + void initialise_keymap(); }; END_NAMESPACE_STIR -#endif //STIR_SQRTHESSIANROWSUM_H +#endif // STIR_SQRTHESSIANROWSUM_H diff --git a/src/include/stir/recon_buildblock/SumOfGeneralisedObjectiveFunctions.h b/src/include/stir/recon_buildblock/SumOfGeneralisedObjectiveFunctions.h index 0340a1080f..67316d2d76 100644 --- a/src/include/stir/recon_buildblock/SumOfGeneralisedObjectiveFunctions.h +++ b/src/include/stir/recon_buildblock/SumOfGeneralisedObjectiveFunctions.h @@ -28,7 +28,6 @@ #ifndef __stir_recon_buildblock_SumOfGeneralisedObjectiveFunctions_H__ #define __stir_recon_buildblock_SumOfGeneralisedObjectiveFunctions_H__ - #include "stir/shared_ptr.h" #include "stir/recon_buildblock/GeneralisedObjectiveFunction.h" #include @@ -39,33 +38,25 @@ START_NAMESPACE_STIR \ingroup recon_buildblock \brief A base class for sums of 'generalised' objective functions, i.e. objective - functions for which at least a 'gradient' is defined. + functions for which at least a 'gradient' is defined. \todo document why use of ParentT template \todo doc subsets */ -template > -class SumOfGeneralisedObjectiveFunctions : - public ParentT -{ +template > +class SumOfGeneralisedObjectiveFunctions : public ParentT { typedef ParentT base_type; typedef SumOfGeneralisedObjectiveFunctions self_type; + public: - - inline - SumOfGeneralisedObjectiveFunctions(); + inline SumOfGeneralisedObjectiveFunctions(); template - inline - SumOfGeneralisedObjectiveFunctions(IterT begin, IterT end); - - inline virtual - ~SumOfGeneralisedObjectiveFunctions(); + inline SumOfGeneralisedObjectiveFunctions(IterT begin, IterT end); + + inline virtual ~SumOfGeneralisedObjectiveFunctions(); template - inline - void set_functions(IterT begin, IterT end); + inline void set_functions(IterT begin, IterT end); #if 0 //! Creates a suitable target as determined by the parameters @@ -74,39 +65,29 @@ class SumOfGeneralisedObjectiveFunctions : */ inline virtual TargetT * - construct_target_ptr() const; + construct_target_ptr() const; #endif //! Has to be called before using this object /*! Will call set_up() for all terms in the sum, but will stop as soon as one set_up() fails. */ - inline virtual - Succeeded - set_up(shared_ptr const& target_sptr); + inline virtual Succeeded set_up(shared_ptr const& target_sptr); //! This computes the gradient of the unregularised objective function at the \a current_estimate /*! It is computed as the sum of the subgradients for each term, depending on the subset scheme. - */ - inline virtual - void - compute_sub_gradient_without_penalty(TargetT& gradient, - const TargetT ¤t_estimate, - const int subset_num); + */ + inline virtual void compute_sub_gradient_without_penalty(TargetT& gradient, const TargetT& current_estimate, + const int subset_num); - inline virtual - double - actual_compute_objective_function_without_penalty(const TargetT& current_estimate, - const int subset_num); + inline virtual double actual_compute_objective_function_without_penalty(const TargetT& current_estimate, const int subset_num); - //! Attempts to change the number of subsets. + //! Attempts to change the number of subsets. /*! \return The number of subsets that will be used later, which is not guaranteed to be what you asked for. */ - inline virtual - int set_num_subsets(const int num_subsets) ; + inline virtual int set_num_subsets(const int num_subsets); protected: - typedef std::vector _functions_type; typedef typename _functions_type::iterator _functions_iterator_type; typedef typename _functions_type::const_iterator _functions_const_iterator_type; @@ -115,8 +96,7 @@ class SumOfGeneralisedObjectiveFunctions : /*! \todo doc subset */ - inline virtual - bool actual_subsets_are_approximately_balanced(std::string& warning_message) const; + inline virtual bool actual_subsets_are_approximately_balanced(std::string& warning_message) const; }; END_NAMESPACE_STIR diff --git a/src/include/stir/recon_buildblock/SumOfGeneralisedObjectiveFunctions.inl b/src/include/stir/recon_buildblock/SumOfGeneralisedObjectiveFunctions.inl index 7b861ac022..f91ade321c 100644 --- a/src/include/stir/recon_buildblock/SumOfGeneralisedObjectiveFunctions.inl +++ b/src/include/stir/recon_buildblock/SumOfGeneralisedObjectiveFunctions.inl @@ -31,31 +31,22 @@ START_NAMESPACE_STIR template template void -SumOfGeneralisedObjectiveFunctions:: -set_functions(IterT begin, IterT end) -{ +SumOfGeneralisedObjectiveFunctions::set_functions(IterT begin, IterT end) { this->_functions.resize(0); std::copy(begin, end, this->_functions.begin()); } template -SumOfGeneralisedObjectiveFunctions:: -SumOfGeneralisedObjectiveFunctions() -{} +SumOfGeneralisedObjectiveFunctions::SumOfGeneralisedObjectiveFunctions() {} template template -SumOfGeneralisedObjectiveFunctions:: -SumOfGeneralisedObjectiveFunctions(IterT begin, IterT end) -{ +SumOfGeneralisedObjectiveFunctions::SumOfGeneralisedObjectiveFunctions(IterT begin, IterT end) { set_functions(begin, end); } template -SumOfGeneralisedObjectiveFunctions:: -~SumOfGeneralisedObjectiveFunctions() -{} - +SumOfGeneralisedObjectiveFunctions::~SumOfGeneralisedObjectiveFunctions() {} #if 0 // this fails, as _functions might only be balid after set_up @@ -72,95 +63,75 @@ construct_target_ptr() const #endif template -Succeeded -SumOfGeneralisedObjectiveFunctions:: -set_up(shared_ptr const& target_sptr) -{ +Succeeded +SumOfGeneralisedObjectiveFunctions::set_up(shared_ptr const& target_sptr) { if (base_type::set_up(target_sptr) != Succeeded::yes) return Succeeded::no; _functions_iterator_type iter = this->_functions.begin(); _functions_iterator_type end_iter = this->_functions.end(); - while (iter != end_iter) - { - if (iter->set_up(target_sptr) == Succeeded::no) - return Succeeded::no; - ++iter; - } + while (iter != end_iter) { + if (iter->set_up(target_sptr) == Succeeded::no) + return Succeeded::no; + ++iter; + } return Succeeded::yes; } - + template -void -SumOfGeneralisedObjectiveFunctions:: -compute_sub_gradient_without_penalty(TargetT& gradient, - const TargetT ¤t_estimate, - const int subset_num) -{ +void +SumOfGeneralisedObjectiveFunctions::compute_sub_gradient_without_penalty( + TargetT& gradient, const TargetT& current_estimate, const int subset_num) { _functions_iterator_type iter = this->_functions.begin(); _functions_iterator_type end_iter = this->_functions.end(); - while (iter != end_iter) - { - iter->compute_sub_gradient_without_penalty(gradient, - current_estimate, - subset_num); - - ++iter; - } + while (iter != end_iter) { + iter->compute_sub_gradient_without_penalty(gradient, current_estimate, subset_num); + + ++iter; + } } template double -SumOfGeneralisedObjectiveFunctions:: -actual_compute_objective_function_without_penalty(const TargetT& current_estimate, - const int subset_num) -{ +SumOfGeneralisedObjectiveFunctions::actual_compute_objective_function_without_penalty( + const TargetT& current_estimate, const int subset_num) { _functions_iterator_type iter = this->_functions.begin(); _functions_iterator_type end_iter = this->_functions.end(); double result = 0; - while (iter != end_iter) - { - result += - iter->compute_objective_function_without_penalty(current_estimate, - subset_num); - - ++iter; - } + while (iter != end_iter) { + result += iter->compute_objective_function_without_penalty(current_estimate, subset_num); + + ++iter; + } return result; } - template int -SumOfGeneralisedObjectiveFunctions:: -set_num_subsets(const int new_num_subsets) -{ +SumOfGeneralisedObjectiveFunctions::set_num_subsets(const int new_num_subsets) { this->num_subsets = new_num_subsets; _functions_iterator_type iter = this->_functions.begin(); _functions_iterator_type end_iter = this->_functions.end(); - while (iter != end_iter) - { - // we could check if they all return the same num_subsets, but cannot be bothered now - if (iter->set_num_subsets(this->num_subsets) != this->num_subsets) - error("set_num_subsets failed to set to %d subsets", this->num_subsets); - ++iter; - } + while (iter != end_iter) { + // we could check if they all return the same num_subsets, but cannot be bothered now + if (iter->set_num_subsets(this->num_subsets) != this->num_subsets) + error("set_num_subsets failed to set to %d subsets", this->num_subsets); + ++iter; + } return this->num_subsets; } template bool -SumOfGeneralisedObjectiveFunctions:: -actual_subsets_are_approximately_balanced(std::string& warning_message) const -{ +SumOfGeneralisedObjectiveFunctions::actual_subsets_are_approximately_balanced( + std::string& warning_message) const { _functions_const_iterator_type iter = this->_functions.begin(); _functions_const_iterator_type end_iter = this->_functions.end(); - while (iter != end_iter) - { - if (!iter->subsets_are_approximately_balanced(warning_message)) - return false; - ++iter; - } + while (iter != end_iter) { + if (!iter->subsets_are_approximately_balanced(warning_message)) + return false; + ++iter; + } return true; } diff --git a/src/include/stir/recon_buildblock/SymmetryOperation.h b/src/include/stir/recon_buildblock/SymmetryOperation.h index 7e2424262e..61917b152c 100644 --- a/src/include/stir/recon_buildblock/SymmetryOperation.h +++ b/src/include/stir/recon_buildblock/SymmetryOperation.h @@ -32,24 +32,23 @@ #include "stir/common.h" - START_NAMESPACE_STIR -template class BasicCoordinate; +template +class BasicCoordinate; class ViewSegmentNumbers; class ProjMatrixElemsForOneBin; class ProjMatrixElemsForOneDensel; class Bin; - /*! \ingroup buildblock \brief Encodes symmetry operation on image coordinates and projection data coordinates This class is mainly (only?) useful for ProjMatrix classes and their - 'users'. Together with DataSymmetriesForBins, it provides the basic - way to be able to write generic code without knowing which + 'users'. Together with DataSymmetriesForBins, it provides the basic + way to be able to write generic code without knowing which particular symmetries the data have. Ideally, there would be no reference here to ProjMatrixElemsForOneBin, @@ -57,7 +56,7 @@ class Bin; will allow the compiler to inline the symmetry operations, resulting in a dramatic speed-up. - Price to pay (aside from some tedious repetition in the derived classes): + Price to pay (aside from some tedious repetition in the derived classes): the need for a SymmetryOperation::transform_proj_matrix_elems_for_one_bin member, and hence knowledge of the ProjMatrixElemsForOneBin class @@ -67,60 +66,40 @@ class Bin; See recon_buildblock/SymmetryOperations_PET_CartesianGrid.cxx for some more info. */ -class SymmetryOperation -{ +class SymmetryOperation { public: virtual inline ~SymmetryOperation() {} - virtual inline bool is_trivial() const { return false;} - virtual void - transform_bin_coordinates(Bin&) const = 0; - virtual void - transform_view_segment_indices(ViewSegmentNumbers&) const = 0; - virtual void - transform_image_coordinates(BasicCoordinate<3,int>&) const = 0; + virtual inline bool is_trivial() const { return false; } + virtual void transform_bin_coordinates(Bin&) const = 0; + virtual void transform_view_segment_indices(ViewSegmentNumbers&) const = 0; + virtual void transform_image_coordinates(BasicCoordinate<3, int>&) const = 0; #if 0 // would be useful at some point virtual void transform_incremental_image_coordinates(BasicCoordinate<3,int>&) const = 0; #endif - virtual void - transform_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& lor) const; - - virtual void - transform_proj_matrix_elems_for_one_densel( - ProjMatrixElemsForOneDensel&) const; + virtual void transform_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& lor) const; + virtual void transform_proj_matrix_elems_for_one_densel(ProjMatrixElemsForOneDensel&) const; }; - - /*! \ingroup symmetries \brief A class implementing the trivial case where the symmetry operation does nothing at all. */ -class TrivialSymmetryOperation : public SymmetryOperation -{ +class TrivialSymmetryOperation : public SymmetryOperation { public: - inline bool is_trivial() const { return true;} - inline void - transform_bin_coordinates(Bin& b) const {} - inline void - transform_view_segment_indices(ViewSegmentNumbers& n) const {} - inline void - transform_image_coordinates(BasicCoordinate<3,int>& c) const {} - inline void - transform_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& lor) const {} + inline bool is_trivial() const { return true; } + inline void transform_bin_coordinates(Bin& b) const {} + inline void transform_view_segment_indices(ViewSegmentNumbers& n) const {} + inline void transform_image_coordinates(BasicCoordinate<3, int>& c) const {} + inline void transform_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& lor) const {} - virtual void - transform_proj_matrix_elems_for_one_densel( - ProjMatrixElemsForOneDensel&) const {} + virtual void transform_proj_matrix_elems_for_one_densel(ProjMatrixElemsForOneDensel&) const {} }; - END_NAMESPACE_STIR //#include "stir/recon_buildblock/SymmetryOperation.inl" diff --git a/src/include/stir/recon_buildblock/SymmetryOperations_PET_CartesianGrid.h b/src/include/stir/recon_buildblock/SymmetryOperations_PET_CartesianGrid.h index ff5d96a123..a9901cf283 100644 --- a/src/include/stir/recon_buildblock/SymmetryOperations_PET_CartesianGrid.h +++ b/src/include/stir/recon_buildblock/SymmetryOperations_PET_CartesianGrid.h @@ -4,30 +4,30 @@ \file \ingroup symmetries - + \brief Declaration of all symmetry classes for PET (cylindrical) scanners and cartesian images \see class stir::DataSymmetriesForBins_PET_CartesianGrid - - \warning These classes should only be used by the + + \warning These classes should only be used by the stir::DataSymmetriesForBins_PET_CartesianGrid class. \warning It is strongly recommended not to derive from any of these - classes. If you do, you have to reimplement the + classes. If you do, you have to reimplement the transform_proj_matrix_elems_for_one_bin() member, or the wrong implementations will be called. All these classes have transform_proj_matrix_elems_for_one_bin() - members which essentially repeats just the default + members which essentially repeats just the default implementation. This is for efficiency. See - recon_buildblock/SymmetryOperations_PET_CartesianGrid.cxx for + recon_buildblock/SymmetryOperations_PET_CartesianGrid.cxx for more info. - + \author Kris Thielemans \author PARAPET project - -*/ + +*/ /* Copyright (C) 2000 PARAPET partners Copyright (C) 2000- 2009, Hammersmith Imanet Ltd @@ -46,7 +46,6 @@ See STIR/LICENSE.txt for details */ - #ifndef __SymmetryOperations_PET_CartesianGrid_H__ #define __SymmetryOperations_PET_CartesianGrid_H__ @@ -54,59 +53,42 @@ START_NAMESPACE_STIR -class SymmetryOperation_PET_CartesianGrid_z_shift : public SymmetryOperation -{ +class SymmetryOperation_PET_CartesianGrid_z_shift : public SymmetryOperation { private: typedef SymmetryOperation_PET_CartesianGrid_z_shift self; + public: SymmetryOperation_PET_CartesianGrid_z_shift(const int axial_pos_shift, const int z_shift) - : axial_pos_shift(axial_pos_shift), z_shift(z_shift) - {} + : axial_pos_shift(axial_pos_shift), z_shift(z_shift) {} - inline void - transform_bin_coordinates(Bin&) const; - inline void - transform_view_segment_indices(ViewSegmentNumbers&) const; - inline void - transform_image_coordinates(BasicCoordinate<3,int>& c) const; + inline void transform_bin_coordinates(Bin&) const; + inline void transform_view_segment_indices(ViewSegmentNumbers&) const; + inline void transform_image_coordinates(BasicCoordinate<3, int>& c) const; - void - transform_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& lor) const; + void transform_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& lor) const; - virtual void - transform_proj_matrix_elems_for_one_densel( - ProjMatrixElemsForOneDensel& ) const; + virtual void transform_proj_matrix_elems_for_one_densel(ProjMatrixElemsForOneDensel&) const; private: int axial_pos_shift; int z_shift; }; -class SymmetryOperation_PET_CartesianGrid_swap_xmx_zq : public SymmetryOperation -{ +class SymmetryOperation_PET_CartesianGrid_swap_xmx_zq : public SymmetryOperation { private: typedef SymmetryOperation_PET_CartesianGrid_swap_xmx_zq self; + public: SymmetryOperation_PET_CartesianGrid_swap_xmx_zq(const int num_views, const int axial_pos_shift, const int z_shift, const int q) - : view180(num_views), axial_pos_shift(axial_pos_shift), z_shift(z_shift), q(q) - {} - - inline void - transform_bin_coordinates(Bin&) const; - inline void - transform_view_segment_indices(ViewSegmentNumbers&) const; - inline void - transform_image_coordinates(BasicCoordinate<3,int>& c) const; + : view180(num_views), axial_pos_shift(axial_pos_shift), z_shift(z_shift), q(q) {} - void - transform_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& lor) const; + inline void transform_bin_coordinates(Bin&) const; + inline void transform_view_segment_indices(ViewSegmentNumbers&) const; + inline void transform_image_coordinates(BasicCoordinate<3, int>& c) const; + void transform_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& lor) const; - virtual void - transform_proj_matrix_elems_for_one_densel( - ProjMatrixElemsForOneDensel&) const; + virtual void transform_proj_matrix_elems_for_one_densel(ProjMatrixElemsForOneDensel&) const; private: int view180; @@ -115,33 +97,24 @@ class SymmetryOperation_PET_CartesianGrid_swap_xmx_zq : public SymmetryOperation int q; }; - /////////////////////////////////////// -class SymmetryOperation_PET_CartesianGrid_swap_xmy_yx_zq : public SymmetryOperation -{ +class SymmetryOperation_PET_CartesianGrid_swap_xmy_yx_zq : public SymmetryOperation { private: typedef SymmetryOperation_PET_CartesianGrid_swap_xmy_yx_zq self; -public: - SymmetryOperation_PET_CartesianGrid_swap_xmy_yx_zq(const int num_views, const int axial_pos_shift, const int z_shift, const int q) - : view180(num_views), axial_pos_shift(axial_pos_shift), z_shift(z_shift), q(q) - {} - inline void - transform_bin_coordinates(Bin&) const; - inline void - transform_view_segment_indices(ViewSegmentNumbers&) const; - inline void - transform_image_coordinates(BasicCoordinate<3,int>& c) const; +public: + SymmetryOperation_PET_CartesianGrid_swap_xmy_yx_zq(const int num_views, const int axial_pos_shift, const int z_shift, + const int q) + : view180(num_views), axial_pos_shift(axial_pos_shift), z_shift(z_shift), q(q) {} - void - transform_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& lor) const; + inline void transform_bin_coordinates(Bin&) const; + inline void transform_view_segment_indices(ViewSegmentNumbers&) const; + inline void transform_image_coordinates(BasicCoordinate<3, int>& c) const; + void transform_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& lor) const; - virtual void - transform_proj_matrix_elems_for_one_densel( - ProjMatrixElemsForOneDensel&) const; + virtual void transform_proj_matrix_elems_for_one_densel(ProjMatrixElemsForOneDensel&) const; private: int view180; @@ -150,31 +123,22 @@ class SymmetryOperation_PET_CartesianGrid_swap_xmy_yx_zq : public SymmetryOperat int q; }; - -class SymmetryOperation_PET_CartesianGrid_swap_xy_yx_zq : public SymmetryOperation -{ +class SymmetryOperation_PET_CartesianGrid_swap_xy_yx_zq : public SymmetryOperation { private: typedef SymmetryOperation_PET_CartesianGrid_swap_xy_yx_zq self; -public: - SymmetryOperation_PET_CartesianGrid_swap_xy_yx_zq(const int num_views, const int axial_pos_shift, const int z_shift, const int q) - : view180(num_views), axial_pos_shift(axial_pos_shift), z_shift(z_shift), q(q) - {} - inline void - transform_bin_coordinates(Bin&) const; - inline void - transform_view_segment_indices(ViewSegmentNumbers&) const; - inline void - transform_image_coordinates(BasicCoordinate<3,int>& c) const; +public: + SymmetryOperation_PET_CartesianGrid_swap_xy_yx_zq(const int num_views, const int axial_pos_shift, const int z_shift, + const int q) + : view180(num_views), axial_pos_shift(axial_pos_shift), z_shift(z_shift), q(q) {} - void - transform_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& lor) const; + inline void transform_bin_coordinates(Bin&) const; + inline void transform_view_segment_indices(ViewSegmentNumbers&) const; + inline void transform_image_coordinates(BasicCoordinate<3, int>& c) const; + void transform_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& lor) const; - virtual void - transform_proj_matrix_elems_for_one_densel( - ProjMatrixElemsForOneDensel&) const; + virtual void transform_proj_matrix_elems_for_one_densel(ProjMatrixElemsForOneDensel&) const; private: int view180; @@ -183,30 +147,21 @@ class SymmetryOperation_PET_CartesianGrid_swap_xy_yx_zq : public SymmetryOperati int q; }; -class SymmetryOperation_PET_CartesianGrid_swap_xmy_yx : public SymmetryOperation -{ +class SymmetryOperation_PET_CartesianGrid_swap_xmy_yx : public SymmetryOperation { private: typedef SymmetryOperation_PET_CartesianGrid_swap_xmy_yx self; + public: SymmetryOperation_PET_CartesianGrid_swap_xmy_yx(const int num_views, const int axial_pos_shift, const int z_shift) - : view180(num_views), axial_pos_shift(axial_pos_shift), z_shift(z_shift) - {} - - inline void - transform_bin_coordinates(Bin&) const; - inline void - transform_view_segment_indices(ViewSegmentNumbers&) const; - inline void - transform_image_coordinates(BasicCoordinate<3,int>& c) const; + : view180(num_views), axial_pos_shift(axial_pos_shift), z_shift(z_shift) {} - void - transform_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& lor) const; + inline void transform_bin_coordinates(Bin&) const; + inline void transform_view_segment_indices(ViewSegmentNumbers&) const; + inline void transform_image_coordinates(BasicCoordinate<3, int>& c) const; + void transform_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& lor) const; - virtual void - transform_proj_matrix_elems_for_one_densel( - ProjMatrixElemsForOneDensel&) const; + virtual void transform_proj_matrix_elems_for_one_densel(ProjMatrixElemsForOneDensel&) const; private: int view180; @@ -214,31 +169,21 @@ class SymmetryOperation_PET_CartesianGrid_swap_xmy_yx : public SymmetryOperation int z_shift; }; - -class SymmetryOperation_PET_CartesianGrid_swap_xy_yx : public SymmetryOperation -{ +class SymmetryOperation_PET_CartesianGrid_swap_xy_yx : public SymmetryOperation { private: typedef SymmetryOperation_PET_CartesianGrid_swap_xy_yx self; -public: - SymmetryOperation_PET_CartesianGrid_swap_xy_yx(const int num_views, const int axial_pos_shift, const int z_shift) - : view180(num_views), axial_pos_shift(axial_pos_shift), z_shift(z_shift) - {} - inline void - transform_bin_coordinates(Bin&) const; - inline void - transform_view_segment_indices(ViewSegmentNumbers&) const; - inline void - transform_image_coordinates(BasicCoordinate<3,int>& c) const; +public: + SymmetryOperation_PET_CartesianGrid_swap_xy_yx(const int num_views, const int axial_pos_shift, const int z_shift) + : view180(num_views), axial_pos_shift(axial_pos_shift), z_shift(z_shift) {} - void - transform_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& lor) const; + inline void transform_bin_coordinates(Bin&) const; + inline void transform_view_segment_indices(ViewSegmentNumbers&) const; + inline void transform_image_coordinates(BasicCoordinate<3, int>& c) const; + void transform_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& lor) const; - virtual void - transform_proj_matrix_elems_for_one_densel( - ProjMatrixElemsForOneDensel&) const; + virtual void transform_proj_matrix_elems_for_one_densel(ProjMatrixElemsForOneDensel&) const; private: int view180; @@ -246,32 +191,21 @@ class SymmetryOperation_PET_CartesianGrid_swap_xy_yx : public SymmetryOperation int z_shift; }; - - -class SymmetryOperation_PET_CartesianGrid_swap_xmx : public SymmetryOperation -{ +class SymmetryOperation_PET_CartesianGrid_swap_xmx : public SymmetryOperation { private: typedef SymmetryOperation_PET_CartesianGrid_swap_xmx self; + public: SymmetryOperation_PET_CartesianGrid_swap_xmx(const int num_views, const int axial_pos_shift, const int z_shift) - : view180(num_views), axial_pos_shift(axial_pos_shift), z_shift(z_shift) - {} + : view180(num_views), axial_pos_shift(axial_pos_shift), z_shift(z_shift) {} - inline void - transform_bin_coordinates(Bin&) const; - inline void - transform_view_segment_indices(ViewSegmentNumbers&) const; - inline void - transform_image_coordinates(BasicCoordinate<3,int>& c) const; + inline void transform_bin_coordinates(Bin&) const; + inline void transform_view_segment_indices(ViewSegmentNumbers&) const; + inline void transform_image_coordinates(BasicCoordinate<3, int>& c) const; - void - transform_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& lor) const; + void transform_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& lor) const; - - virtual void - transform_proj_matrix_elems_for_one_densel( - ProjMatrixElemsForOneDensel&) const; + virtual void transform_proj_matrix_elems_for_one_densel(ProjMatrixElemsForOneDensel&) const; private: int view180; @@ -279,61 +213,43 @@ class SymmetryOperation_PET_CartesianGrid_swap_xmx : public SymmetryOperation int z_shift; }; -class SymmetryOperation_PET_CartesianGrid_swap_ymy : public SymmetryOperation -{ +class SymmetryOperation_PET_CartesianGrid_swap_ymy : public SymmetryOperation { private: typedef SymmetryOperation_PET_CartesianGrid_swap_ymy self; + public: SymmetryOperation_PET_CartesianGrid_swap_ymy(const int num_views, const int axial_pos_shift, const int z_shift) - : view180(num_views), axial_pos_shift(axial_pos_shift), z_shift(z_shift) - {} - - inline void - transform_bin_coordinates(Bin&) const; - inline void - transform_view_segment_indices(ViewSegmentNumbers&) const; - inline void - transform_image_coordinates(BasicCoordinate<3,int>& c) const; + : view180(num_views), axial_pos_shift(axial_pos_shift), z_shift(z_shift) {} - void - transform_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& lor) const; + inline void transform_bin_coordinates(Bin&) const; + inline void transform_view_segment_indices(ViewSegmentNumbers&) const; + inline void transform_image_coordinates(BasicCoordinate<3, int>& c) const; + void transform_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& lor) const; - virtual void - transform_proj_matrix_elems_for_one_densel( - ProjMatrixElemsForOneDensel&) const; + virtual void transform_proj_matrix_elems_for_one_densel(ProjMatrixElemsForOneDensel&) const; private: int view180; int axial_pos_shift; int z_shift; - }; +}; -class SymmetryOperation_PET_CartesianGrid_swap_zq : public SymmetryOperation -{ +class SymmetryOperation_PET_CartesianGrid_swap_zq : public SymmetryOperation { private: typedef SymmetryOperation_PET_CartesianGrid_swap_zq self; + public: SymmetryOperation_PET_CartesianGrid_swap_zq(const int num_views, const int axial_pos_shift, const int z_shift, const int q) - : view180(num_views), axial_pos_shift(axial_pos_shift), z_shift(z_shift), q(q) - {} - - inline void - transform_bin_coordinates(Bin&) const; - inline void - transform_view_segment_indices(ViewSegmentNumbers&) const; - inline void - transform_image_coordinates(BasicCoordinate<3,int>& c) const; + : view180(num_views), axial_pos_shift(axial_pos_shift), z_shift(z_shift), q(q) {} - void - transform_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& lor) const; + inline void transform_bin_coordinates(Bin&) const; + inline void transform_view_segment_indices(ViewSegmentNumbers&) const; + inline void transform_image_coordinates(BasicCoordinate<3, int>& c) const; + void transform_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& lor) const; - virtual void - transform_proj_matrix_elems_for_one_densel( - ProjMatrixElemsForOneDensel&) const; + virtual void transform_proj_matrix_elems_for_one_densel(ProjMatrixElemsForOneDensel&) const; private: int view180; @@ -342,30 +258,22 @@ class SymmetryOperation_PET_CartesianGrid_swap_zq : public SymmetryOperation int q; }; -class SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy_zq : public SymmetryOperation -{ +class SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy_zq : public SymmetryOperation { private: typedef SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy_zq self; -public: - SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy_zq(const int num_views, const int axial_pos_shift, const int z_shift, const int q) - : view180(num_views), axial_pos_shift(axial_pos_shift), z_shift(z_shift), q(q) - {} - inline void - transform_bin_coordinates(Bin&) const; - inline void - transform_view_segment_indices(ViewSegmentNumbers&) const; - inline void - transform_image_coordinates(BasicCoordinate<3,int>& c) const; +public: + SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy_zq(const int num_views, const int axial_pos_shift, const int z_shift, + const int q) + : view180(num_views), axial_pos_shift(axial_pos_shift), z_shift(z_shift), q(q) {} - void - transform_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& lor) const; + inline void transform_bin_coordinates(Bin&) const; + inline void transform_view_segment_indices(ViewSegmentNumbers&) const; + inline void transform_image_coordinates(BasicCoordinate<3, int>& c) const; + void transform_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& lor) const; - virtual void - transform_proj_matrix_elems_for_one_densel( - ProjMatrixElemsForOneDensel&) const; + virtual void transform_proj_matrix_elems_for_one_densel(ProjMatrixElemsForOneDensel&) const; private: int view180; @@ -374,30 +282,22 @@ class SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy_zq : public SymmetryOpera int q; }; -class SymmetryOperation_PET_CartesianGrid_swap_xy_ymx_zq : public SymmetryOperation -{ +class SymmetryOperation_PET_CartesianGrid_swap_xy_ymx_zq : public SymmetryOperation { private: typedef SymmetryOperation_PET_CartesianGrid_swap_xy_ymx_zq self; -public: - SymmetryOperation_PET_CartesianGrid_swap_xy_ymx_zq(const int num_views, const int axial_pos_shift, const int z_shift, const int q) - : view180(num_views), axial_pos_shift(axial_pos_shift), z_shift(z_shift), q(q) - {} - inline void - transform_bin_coordinates(Bin&) const; - inline void - transform_view_segment_indices(ViewSegmentNumbers&) const; - inline void - transform_image_coordinates(BasicCoordinate<3,int>& c) const; +public: + SymmetryOperation_PET_CartesianGrid_swap_xy_ymx_zq(const int num_views, const int axial_pos_shift, const int z_shift, + const int q) + : view180(num_views), axial_pos_shift(axial_pos_shift), z_shift(z_shift), q(q) {} - void - transform_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& lor) const; + inline void transform_bin_coordinates(Bin&) const; + inline void transform_view_segment_indices(ViewSegmentNumbers&) const; + inline void transform_image_coordinates(BasicCoordinate<3, int>& c) const; + void transform_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& lor) const; - virtual void - transform_proj_matrix_elems_for_one_densel( - ProjMatrixElemsForOneDensel&) const; + virtual void transform_proj_matrix_elems_for_one_densel(ProjMatrixElemsForOneDensel&) const; private: int view180; @@ -406,30 +306,21 @@ class SymmetryOperation_PET_CartesianGrid_swap_xy_ymx_zq : public SymmetryOperat int q; }; -class SymmetryOperation_PET_CartesianGrid_swap_xy_ymx : public SymmetryOperation -{ +class SymmetryOperation_PET_CartesianGrid_swap_xy_ymx : public SymmetryOperation { private: typedef SymmetryOperation_PET_CartesianGrid_swap_xy_ymx self; + public: SymmetryOperation_PET_CartesianGrid_swap_xy_ymx(const int num_views, const int axial_pos_shift, const int z_shift) - : view180(num_views), axial_pos_shift(axial_pos_shift), z_shift(z_shift) - {} - - inline void - transform_bin_coordinates(Bin&) const; - inline void - transform_view_segment_indices(ViewSegmentNumbers&) const; - inline void - transform_image_coordinates(BasicCoordinate<3,int>& c) const; + : view180(num_views), axial_pos_shift(axial_pos_shift), z_shift(z_shift) {} - void - transform_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& lor) const; + inline void transform_bin_coordinates(Bin&) const; + inline void transform_view_segment_indices(ViewSegmentNumbers&) const; + inline void transform_image_coordinates(BasicCoordinate<3, int>& c) const; + void transform_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& lor) const; - virtual void - transform_proj_matrix_elems_for_one_densel( - ProjMatrixElemsForOneDensel&) const; + virtual void transform_proj_matrix_elems_for_one_densel(ProjMatrixElemsForOneDensel&) const; private: int view180; @@ -437,30 +328,21 @@ class SymmetryOperation_PET_CartesianGrid_swap_xy_ymx : public SymmetryOperation int z_shift; }; -class SymmetryOperation_PET_CartesianGrid_swap_xmy_ymx : public SymmetryOperation -{ +class SymmetryOperation_PET_CartesianGrid_swap_xmy_ymx : public SymmetryOperation { private: typedef SymmetryOperation_PET_CartesianGrid_swap_xmy_ymx self; + public: SymmetryOperation_PET_CartesianGrid_swap_xmy_ymx(const int num_views, const int axial_pos_shift, const int z_shift) - : view180(num_views), axial_pos_shift(axial_pos_shift), z_shift(z_shift) - {} - - inline void - transform_bin_coordinates(Bin&) const; - inline void - transform_view_segment_indices(ViewSegmentNumbers&) const; - inline void - transform_image_coordinates(BasicCoordinate<3,int>& c) const; + : view180(num_views), axial_pos_shift(axial_pos_shift), z_shift(z_shift) {} - void - transform_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& lor) const; + inline void transform_bin_coordinates(Bin&) const; + inline void transform_view_segment_indices(ViewSegmentNumbers&) const; + inline void transform_image_coordinates(BasicCoordinate<3, int>& c) const; + void transform_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& lor) const; - virtual void - transform_proj_matrix_elems_for_one_densel( - ProjMatrixElemsForOneDensel&) const; + virtual void transform_proj_matrix_elems_for_one_densel(ProjMatrixElemsForOneDensel&) const; private: int view180; @@ -468,30 +350,21 @@ class SymmetryOperation_PET_CartesianGrid_swap_xmy_ymx : public SymmetryOperatio int z_shift; }; -class SymmetryOperation_PET_CartesianGrid_swap_ymy_zq : public SymmetryOperation -{ +class SymmetryOperation_PET_CartesianGrid_swap_ymy_zq : public SymmetryOperation { private: typedef SymmetryOperation_PET_CartesianGrid_swap_ymy_zq self; + public: SymmetryOperation_PET_CartesianGrid_swap_ymy_zq(const int num_views, const int axial_pos_shift, const int z_shift, const int q) - : view180(num_views), axial_pos_shift(axial_pos_shift), z_shift(z_shift), q(q) - {} + : view180(num_views), axial_pos_shift(axial_pos_shift), z_shift(z_shift), q(q) {} - inline void - transform_bin_coordinates(Bin&) const; - inline void - transform_view_segment_indices(ViewSegmentNumbers&) const; - inline void - transform_image_coordinates(BasicCoordinate<3,int>& c) const; + inline void transform_bin_coordinates(Bin&) const; + inline void transform_view_segment_indices(ViewSegmentNumbers&) const; + inline void transform_image_coordinates(BasicCoordinate<3, int>& c) const; - void - transform_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& lor) const; + void transform_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& lor) const; - - virtual void - transform_proj_matrix_elems_for_one_densel( - ProjMatrixElemsForOneDensel&) const; + virtual void transform_proj_matrix_elems_for_one_densel(ProjMatrixElemsForOneDensel&) const; private: int view180; @@ -500,30 +373,21 @@ class SymmetryOperation_PET_CartesianGrid_swap_ymy_zq : public SymmetryOperation int q; }; -class SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy : public SymmetryOperation -{ +class SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy : public SymmetryOperation { private: typedef SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy self; + public: SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy(const int num_views, const int axial_pos_shift, const int z_shift) - : view180(num_views), axial_pos_shift(axial_pos_shift), z_shift(z_shift) - {} - - inline void - transform_bin_coordinates(Bin&) const; - inline void - transform_view_segment_indices(ViewSegmentNumbers&) const; - inline void - transform_image_coordinates(BasicCoordinate<3,int>& c) const; + : view180(num_views), axial_pos_shift(axial_pos_shift), z_shift(z_shift) {} - void - transform_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& lor) const; + inline void transform_bin_coordinates(Bin&) const; + inline void transform_view_segment_indices(ViewSegmentNumbers&) const; + inline void transform_image_coordinates(BasicCoordinate<3, int>& c) const; + void transform_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& lor) const; - virtual void - transform_proj_matrix_elems_for_one_densel( - ProjMatrixElemsForOneDensel&) const; + virtual void transform_proj_matrix_elems_for_one_densel(ProjMatrixElemsForOneDensel&) const; private: int view180; @@ -531,30 +395,22 @@ class SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy : public SymmetryOperatio int z_shift; }; -class SymmetryOperation_PET_CartesianGrid_swap_xmy_ymx_zq : public SymmetryOperation -{ +class SymmetryOperation_PET_CartesianGrid_swap_xmy_ymx_zq : public SymmetryOperation { private: typedef SymmetryOperation_PET_CartesianGrid_swap_xmy_ymx_zq self; -public: - SymmetryOperation_PET_CartesianGrid_swap_xmy_ymx_zq(const int num_views, const int axial_pos_shift, const int z_shift, const int q) - : view180(num_views), axial_pos_shift(axial_pos_shift), z_shift(z_shift), q(q) - {} - inline void - transform_bin_coordinates(Bin&) const; - inline void - transform_view_segment_indices(ViewSegmentNumbers&) const; - inline void - transform_image_coordinates(BasicCoordinate<3,int>& c) const; +public: + SymmetryOperation_PET_CartesianGrid_swap_xmy_ymx_zq(const int num_views, const int axial_pos_shift, const int z_shift, + const int q) + : view180(num_views), axial_pos_shift(axial_pos_shift), z_shift(z_shift), q(q) {} - void - transform_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& lor) const; + inline void transform_bin_coordinates(Bin&) const; + inline void transform_view_segment_indices(ViewSegmentNumbers&) const; + inline void transform_image_coordinates(BasicCoordinate<3, int>& c) const; + void transform_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& lor) const; - virtual void - transform_proj_matrix_elems_for_one_densel( - ProjMatrixElemsForOneDensel&) const; + virtual void transform_proj_matrix_elems_for_one_densel(ProjMatrixElemsForOneDensel&) const; private: int view180; @@ -563,11 +419,8 @@ class SymmetryOperation_PET_CartesianGrid_swap_xmy_ymx_zq : public SymmetryOpera int q; }; - - END_NAMESPACE_STIR #include "stir/recon_buildblock/SymmetryOperations_PET_CartesianGrid.inl" - #endif diff --git a/src/include/stir/recon_buildblock/SymmetryOperations_PET_CartesianGrid.inl b/src/include/stir/recon_buildblock/SymmetryOperations_PET_CartesianGrid.inl index 5bcccd1213..eb08a20931 100644 --- a/src/include/stir/recon_buildblock/SymmetryOperations_PET_CartesianGrid.inl +++ b/src/include/stir/recon_buildblock/SymmetryOperations_PET_CartesianGrid.inl @@ -42,77 +42,62 @@ START_NAMESPACE_STIR -void -SymmetryOperation_PET_CartesianGrid_z_shift:: - transform_bin_coordinates(Bin& b) const -{ +void +SymmetryOperation_PET_CartesianGrid_z_shift::transform_bin_coordinates(Bin& b) const { b.axial_pos_num() += axial_pos_shift; } -void -SymmetryOperation_PET_CartesianGrid_z_shift:: - transform_view_segment_indices(ViewSegmentNumbers& vs) const -{ -} - -void SymmetryOperation_PET_CartesianGrid_z_shift::transform_image_coordinates(BasicCoordinate<3,int>&c) const -{ +void +SymmetryOperation_PET_CartesianGrid_z_shift::transform_view_segment_indices(ViewSegmentNumbers& vs) const {} + +void +SymmetryOperation_PET_CartesianGrid_z_shift::transform_image_coordinates(BasicCoordinate<3, int>& c) const { c[1] += z_shift; } ////////////////////////////////////////// -void -SymmetryOperation_PET_CartesianGrid_swap_xmx_zq:: - transform_bin_coordinates(Bin& b) const -{ +void +SymmetryOperation_PET_CartesianGrid_swap_xmx_zq::transform_bin_coordinates(Bin& b) const { b.axial_pos_num() += axial_pos_shift; b.view_num() = view180 - b.view_num(); - assert(0<=b.view_num()); - assert(b.view_num()&c) const -{ - c[3] = -c[3]; + +void +SymmetryOperation_PET_CartesianGrid_swap_xmx_zq::transform_image_coordinates(BasicCoordinate<3, int>& c) const { + c[3] = -c[3]; c[1] = q - c[1] + z_shift; } ////////////////////////////////////////// - -void -SymmetryOperation_PET_CartesianGrid_swap_xmy_yx_zq:: - transform_bin_coordinates(Bin& b) const -{ +void +SymmetryOperation_PET_CartesianGrid_swap_xmy_yx_zq::transform_bin_coordinates(Bin& b) const { b.axial_pos_num() += axial_pos_shift; b.segment_num() *= -1; - b.view_num() += view180/2; - assert(0<=b.view_num()); - assert(b.view_num()&c) const -{ +void +SymmetryOperation_PET_CartesianGrid_swap_xmy_yx_zq::transform_image_coordinates(BasicCoordinate<3, int>& c) const { const int tmp = c[3]; c[3] = -c[2]; c[2] = tmp; @@ -121,27 +106,22 @@ void SymmetryOperation_PET_CartesianGrid_swap_xmy_yx_zq::transform_image_coordin /////////////////////////////////////// -void -SymmetryOperation_PET_CartesianGrid_swap_xy_yx_zq:: - transform_bin_coordinates(Bin& b) const -{ +void +SymmetryOperation_PET_CartesianGrid_swap_xy_yx_zq::transform_bin_coordinates(Bin& b) const { b.axial_pos_num() += axial_pos_shift; - b.view_num() = view180/2 - b.view_num(); - assert(0<=b.view_num()); - assert(b.view_num()&c) const -{ + b.view_num() = view180 / 2 - b.view_num(); + assert(0 <= b.view_num()); + assert(b.view_num() < view180); +} +void +SymmetryOperation_PET_CartesianGrid_swap_xy_yx_zq::transform_view_segment_indices(ViewSegmentNumbers& vs) const { + vs.view_num() = view180 / 2 - vs.view_num(); + assert(0 <= vs.view_num()); + assert(vs.view_num() < view180); +} + +void +SymmetryOperation_PET_CartesianGrid_swap_xy_yx_zq::transform_image_coordinates(BasicCoordinate<3, int>& c) const { const int tmp = c[3]; c[3] = c[2]; c[2] = tmp; @@ -150,45 +130,34 @@ void SymmetryOperation_PET_CartesianGrid_swap_xy_yx_zq::transform_image_coordina /////////////////////////////////////// - -void -SymmetryOperation_PET_CartesianGrid_swap_xmy_yx:: - transform_bin_coordinates(Bin& b) const -{ +void +SymmetryOperation_PET_CartesianGrid_swap_xmy_yx::transform_bin_coordinates(Bin& b) const { b.axial_pos_num() += axial_pos_shift; - if (b.view_num() < view180/2) - { - b.view_num() += view180/2; - } - else - { + if (b.view_num() < view180 / 2) { + b.view_num() += view180 / 2; + } else { b.segment_num() *= -1; - b.view_num() -= view180/2; + b.view_num() -= view180 / 2; b.tangential_pos_num() *= -1; } - assert(0<=b.view_num()); - assert(b.view_num()&c) const -{ + +void +SymmetryOperation_PET_CartesianGrid_swap_xmy_yx::transform_image_coordinates(BasicCoordinate<3, int>& c) const { const int tmp = c[3]; c[3] = -c[2]; c[2] = tmp; @@ -196,415 +165,316 @@ void SymmetryOperation_PET_CartesianGrid_swap_xmy_yx::transform_image_coordinate } /////////////////////////////////////// -void -SymmetryOperation_PET_CartesianGrid_swap_xy_yx:: - transform_bin_coordinates(Bin& b) const -{ +void +SymmetryOperation_PET_CartesianGrid_swap_xy_yx::transform_bin_coordinates(Bin& b) const { b.axial_pos_num() += axial_pos_shift; - if (b.view_num() <= view180/2) - { + if (b.view_num() <= view180 / 2) { b.segment_num() *= -1; - b.view_num() = view180/2 - b.view_num(); - } - else - { - b.view_num() = 3*view180/2 - b.view_num(); + b.view_num() = view180 / 2 - b.view_num(); + } else { + b.view_num() = 3 * view180 / 2 - b.view_num(); b.tangential_pos_num() *= -1; } - assert(0<=b.view_num()); - assert(b.view_num()&c) const -{ +void +SymmetryOperation_PET_CartesianGrid_swap_xy_yx::transform_image_coordinates(BasicCoordinate<3, int>& c) const { const int tmp = c[3]; c[3] = c[2]; - c[2] = tmp; + c[2] = tmp; c[1] += z_shift; } /////////////////////////////////////// - -void -SymmetryOperation_PET_CartesianGrid_swap_xmx:: - transform_bin_coordinates(Bin& b) const -{ +void +SymmetryOperation_PET_CartesianGrid_swap_xmx::transform_bin_coordinates(Bin& b) const { b.axial_pos_num() += axial_pos_shift; - if (b.view_num()!=0) - { + if (b.view_num() != 0) { b.segment_num() *= -1; b.view_num() = view180 - b.view_num(); - } - else - { + } else { b.tangential_pos_num() *= -1; } - assert(0<=b.view_num()); - assert(b.view_num()&c) const -{ - c[3] = -c[3]; +void +SymmetryOperation_PET_CartesianGrid_swap_xmx::transform_image_coordinates(BasicCoordinate<3, int>& c) const { + c[3] = -c[3]; c[1] += z_shift; -} +} /////////////////////////////////////// - -void -SymmetryOperation_PET_CartesianGrid_swap_ymy:: - transform_bin_coordinates(Bin& b) const -{ +void +SymmetryOperation_PET_CartesianGrid_swap_ymy::transform_bin_coordinates(Bin& b) const { b.axial_pos_num() += axial_pos_shift; - if (b.view_num()!=0) - { + if (b.view_num() != 0) { b.view_num() = view180 - b.view_num(); b.tangential_pos_num() *= -1; - } - else - { + } else { b.segment_num() *= -1; } - assert(0<=b.view_num()); - assert(b.view_num()&c) const -{ +void +SymmetryOperation_PET_CartesianGrid_swap_ymy::transform_image_coordinates(BasicCoordinate<3, int>& c) const { c[2] = -c[2]; c[1] += z_shift; -} +} /////////////////////////////////////// - -void -SymmetryOperation_PET_CartesianGrid_swap_zq:: - transform_bin_coordinates(Bin& b) const -{ +void +SymmetryOperation_PET_CartesianGrid_swap_zq::transform_bin_coordinates(Bin& b) const { b.axial_pos_num() += axial_pos_shift; b.segment_num() *= -1; } -void -SymmetryOperation_PET_CartesianGrid_swap_zq:: - transform_view_segment_indices(ViewSegmentNumbers& vs) const -{ +void +SymmetryOperation_PET_CartesianGrid_swap_zq::transform_view_segment_indices(ViewSegmentNumbers& vs) const { vs.segment_num() *= -1; } - -void SymmetryOperation_PET_CartesianGrid_swap_zq::transform_image_coordinates(BasicCoordinate<3,int>&c) const -{ - c[1] = q - c[1] + z_shift; +void +SymmetryOperation_PET_CartesianGrid_swap_zq::transform_image_coordinates(BasicCoordinate<3, int>& c) const { + c[1] = q - c[1] + z_shift; } /////////////////////////////////////// - -void -SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy_zq:: - transform_bin_coordinates(Bin& b) const -{ +void +SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy_zq::transform_bin_coordinates(Bin& b) const { b.axial_pos_num() += axial_pos_shift; b.tangential_pos_num() *= -1; } -void -SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy_zq:: - transform_view_segment_indices(ViewSegmentNumbers& vs) const -{ - -} - +void +SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy_zq::transform_view_segment_indices(ViewSegmentNumbers& vs) const {} -void SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy_zq::transform_image_coordinates(BasicCoordinate<3,int>&c) const -{ +void +SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy_zq::transform_image_coordinates(BasicCoordinate<3, int>& c) const { c[3] = -c[3]; - c[2] = -c[2]; - c[1] = q - c[1] + z_shift; + c[2] = -c[2]; + c[1] = q - c[1] + z_shift; } /////////////////////////////////////// - -void -SymmetryOperation_PET_CartesianGrid_swap_xy_ymx_zq:: - transform_bin_coordinates(Bin& b) const -{ +void +SymmetryOperation_PET_CartesianGrid_swap_xy_ymx_zq::transform_bin_coordinates(Bin& b) const { b.axial_pos_num() += axial_pos_shift; - if (b.view_num() < view180/2) - { - b.view_num() += view180/2; + if (b.view_num() < view180 / 2) { + b.view_num() += view180 / 2; b.tangential_pos_num() *= -1; - } - else - { + } else { b.segment_num() *= -1; - b.view_num() -= view180/2; + b.view_num() -= view180 / 2; } - assert(0<=b.view_num()); - assert(b.view_num()&c) const -{ +void +SymmetryOperation_PET_CartesianGrid_swap_xy_ymx_zq::transform_image_coordinates(BasicCoordinate<3, int>& c) const { const int tmp = c[3]; c[3] = c[2]; - c[2] = -tmp; - c[1] = q - c[1] + z_shift; + c[2] = -tmp; + c[1] = q - c[1] + z_shift; } /////////////////////////////////////// - -void -SymmetryOperation_PET_CartesianGrid_swap_xy_ymx:: - transform_bin_coordinates(Bin& b) const -{ +void +SymmetryOperation_PET_CartesianGrid_swap_xy_ymx::transform_bin_coordinates(Bin& b) const { b.axial_pos_num() += axial_pos_shift; - if (b.view_num() < view180/2) - { + if (b.view_num() < view180 / 2) { b.segment_num() *= -1; - b.view_num() += view180/2; + b.view_num() += view180 / 2; b.tangential_pos_num() *= -1; + } else { + b.view_num() -= view180 / 2; } - else - { - b.view_num() -= view180/2; - } - assert(0<=b.view_num()); - assert(b.view_num()&c) const -{ + +void +SymmetryOperation_PET_CartesianGrid_swap_xy_ymx::transform_image_coordinates(BasicCoordinate<3, int>& c) const { const int tmp = c[3]; - c[3] = c[2]; - c[2] = -tmp; + c[3] = c[2]; + c[2] = -tmp; c[1] += z_shift; } /////////////////////////////////////// - -void -SymmetryOperation_PET_CartesianGrid_swap_xmy_ymx:: - transform_bin_coordinates(Bin& b) const -{ +void +SymmetryOperation_PET_CartesianGrid_swap_xmy_ymx::transform_bin_coordinates(Bin& b) const { b.axial_pos_num() += axial_pos_shift; - if (b.view_num() <= view180/2) - { - b.view_num() = view180/2 - b.view_num(); + if (b.view_num() <= view180 / 2) { + b.view_num() = view180 / 2 - b.view_num(); b.tangential_pos_num() *= -1; - } - else - { + } else { b.segment_num() *= -1; - b.view_num() = 3*view180/2 - b.view_num(); + b.view_num() = 3 * view180 / 2 - b.view_num(); } - assert(0<=b.view_num()); - assert(b.view_num()&c) const -{ + +void +SymmetryOperation_PET_CartesianGrid_swap_xmy_ymx::transform_image_coordinates(BasicCoordinate<3, int>& c) const { const int tmp = c[3]; - c[3] = -c[2]; - c[2] = -tmp; + c[3] = -c[2]; + c[2] = -tmp; c[1] += z_shift; } /////////////////////////////////////// - -void -SymmetryOperation_PET_CartesianGrid_swap_ymy_zq:: - transform_bin_coordinates(Bin& b) const -{ +void +SymmetryOperation_PET_CartesianGrid_swap_ymy_zq::transform_bin_coordinates(Bin& b) const { b.axial_pos_num() += axial_pos_shift; b.segment_num() *= -1; b.view_num() = view180 - b.view_num(); b.tangential_pos_num() *= -1; - assert(0<=b.view_num()); - assert(b.view_num()&c) const -{ - c[2] = -c[2]; - c[1] = q - c[1] + z_shift; +void +SymmetryOperation_PET_CartesianGrid_swap_ymy_zq::transform_image_coordinates(BasicCoordinate<3, int>& c) const { + c[2] = -c[2]; + c[1] = q - c[1] + z_shift; } /////////////////////////////////////// - -void -SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy:: - transform_bin_coordinates(Bin& b) const -{ +void +SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy::transform_bin_coordinates(Bin& b) const { b.axial_pos_num() += axial_pos_shift; b.segment_num() *= -1; b.tangential_pos_num() *= -1; } -void -SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy:: - transform_view_segment_indices(ViewSegmentNumbers& vs) const -{ +void +SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy::transform_view_segment_indices(ViewSegmentNumbers& vs) const { vs.segment_num() *= -1; } - -void SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy::transform_image_coordinates(BasicCoordinate<3,int>&c) const -{ - c[3] = -c[3]; - c[2] = -c[2]; + +void +SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy::transform_image_coordinates(BasicCoordinate<3, int>& c) const { + c[3] = -c[3]; + c[2] = -c[2]; c[1] += z_shift; } /////////////////////////////////////// - -void -SymmetryOperation_PET_CartesianGrid_swap_xmy_ymx_zq:: - transform_bin_coordinates(Bin& b) const -{ +void +SymmetryOperation_PET_CartesianGrid_swap_xmy_ymx_zq::transform_bin_coordinates(Bin& b) const { b.axial_pos_num() += axial_pos_shift; b.segment_num() *= -1; - b.view_num() = view180/2 - b.view_num(); + b.view_num() = view180 / 2 - b.view_num(); b.tangential_pos_num() *= -1; - assert(0<=b.view_num()); - assert(b.view_num()&c) const -{ + +void +SymmetryOperation_PET_CartesianGrid_swap_xmy_ymx_zq::transform_image_coordinates(BasicCoordinate<3, int>& c) const { const int tmp = c[3]; c[3] = -c[2]; - c[2] = -tmp; + c[2] = -tmp; c[1] = q - c[1] + z_shift; } diff --git a/src/include/stir/recon_buildblock/TrivialBinNormalisation.h b/src/include/stir/recon_buildblock/TrivialBinNormalisation.h index 663cc10712..3eea603ac1 100644 --- a/src/include/stir/recon_buildblock/TrivialBinNormalisation.h +++ b/src/include/stir/recon_buildblock/TrivialBinNormalisation.h @@ -39,24 +39,21 @@ START_NAMESPACE_STIR \todo Make sure that the keyword value \c None corresponds to this class. */ -class TrivialBinNormalisation : - public RegisteredParsingObject -{ +class TrivialBinNormalisation : public RegisteredParsingObject { public: //! Name which will be used when parsing a BinNormalisation object - static const char * const registered_name; + static const char* const registered_name; - virtual inline void apply(RelatedViewgrams&,const double start_time, const double end_time) const {} - virtual inline void undo(RelatedViewgrams&,const double start_time, const double end_time) const {} - - virtual inline float get_bin_efficiency(const Bin& bin,const double start_time, const double end_time) const { return 1;} + virtual inline void apply(RelatedViewgrams&, const double start_time, const double end_time) const {} + virtual inline void undo(RelatedViewgrams&, const double start_time, const double end_time) const {} - virtual inline bool is_trivial() const { return true;} + virtual inline float get_bin_efficiency(const Bin& bin, const double start_time, const double end_time) const { return 1; } + + virtual inline bool is_trivial() const { return true; } private: virtual inline void set_defaults() {} virtual inline void initialise_keymap() {} - }; END_NAMESPACE_STIR diff --git a/src/include/stir/recon_buildblock/TrivialDataSymmetriesForBins.h b/src/include/stir/recon_buildblock/TrivialDataSymmetriesForBins.h index b04031a823..c9ecd957e0 100644 --- a/src/include/stir/recon_buildblock/TrivialDataSymmetriesForBins.h +++ b/src/include/stir/recon_buildblock/TrivialDataSymmetriesForBins.h @@ -33,66 +33,52 @@ START_NAMESPACE_STIR - /*! \ingroup symmetries \brief A class derived from DataSymmetriesForBins that says that there are no symmetries at all. */ -class TrivialDataSymmetriesForBins : public DataSymmetriesForBins -{ +class TrivialDataSymmetriesForBins : public DataSymmetriesForBins { public: TrivialDataSymmetriesForBins(const shared_ptr& proj_data_info_ptr); - virtual + virtual #ifndef STIR_NO_COVARIANT_RETURN_TYPES - TrivialDataSymmetriesForBins + TrivialDataSymmetriesForBins #else - DataSymmetriesForViewSegmentNumbers + DataSymmetriesForViewSegmentNumbers #endif - * clone() const; + * + clone() const; - virtual void - get_related_bins(std::vector&, const Bin& b, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num, - const int min_timing_pos_num = 0, const int max_timing_pos_num = 0) const; + virtual void get_related_bins(std::vector&, const Bin& b, const int min_axial_pos_num, const int max_axial_pos_num, + const int min_tangential_pos_num, const int max_tangential_pos_num, + const int min_timing_pos_num = 0, const int max_timing_pos_num = 0) const; - virtual void - get_related_bins_factorised(std::vector&, const Bin& b, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) const; + virtual void get_related_bins_factorised(std::vector&, const Bin& b, const int min_axial_pos_num, + const int max_axial_pos_num, const int min_tangential_pos_num, + const int max_tangential_pos_num) const; - virtual int - num_related_bins(const Bin& b) const; + virtual int num_related_bins(const Bin& b) const; - virtual unique_ptr - find_symmetry_operation_from_basic_bin(Bin&) const; + virtual unique_ptr find_symmetry_operation_from_basic_bin(Bin&) const; - virtual bool - find_basic_bin(Bin& b) const; + virtual bool find_basic_bin(Bin& b) const; - virtual bool - is_basic(const Bin& v_s) const; + virtual bool is_basic(const Bin& v_s) const; - virtual unique_ptr - find_symmetry_operation_from_basic_view_segment_numbers(ViewSegmentNumbers&) const; + virtual unique_ptr find_symmetry_operation_from_basic_view_segment_numbers(ViewSegmentNumbers&) const; - virtual void - get_related_view_segment_numbers(std::vector&, const ViewSegmentNumbers&) const; + virtual void get_related_view_segment_numbers(std::vector&, const ViewSegmentNumbers&) const; - virtual int - num_related_view_segment_numbers(const ViewSegmentNumbers&) const; - virtual bool - find_basic_view_segment_numbers(ViewSegmentNumbers&) const; + virtual int num_related_view_segment_numbers(const ViewSegmentNumbers&) const; + virtual bool find_basic_view_segment_numbers(ViewSegmentNumbers&) const; private: - virtual bool blindly_equals(const root_type * const) const; + virtual bool blindly_equals(const root_type* const) const; }; END_NAMESPACE_STIR - #endif - diff --git a/src/include/stir/recon_buildblock/distributable.h b/src/include/stir/recon_buildblock/distributable.h index 262b7c4d68..e61b046d15 100644 --- a/src/include/stir/recon_buildblock/distributable.h +++ b/src/include/stir/recon_buildblock/distributable.h @@ -36,8 +36,10 @@ START_NAMESPACE_STIR -template class RelatedViewgrams; -template class DiscretisedDensity; +template +class RelatedViewgrams; +template +class DiscretisedDensity; class BinNormalisation; class ProjData; class ProjDataInfo; @@ -48,37 +50,34 @@ class BackProjectorByBin; class ProjectorByBinPair; class DistributedCachingInformation; - //! \name Task-ids currently understood by stir::DistributedWorker /*! \ingroup distributable */ //!@{ -const int task_stop_processing=0; -const int task_setup_distributable_computation=200; -const int task_do_distributable_gradient_computation=42; -const int task_do_distributable_loglikelihood_computation=43; -const int task_do_distributable_sensitivity_computation=44; +const int task_stop_processing = 0; +const int task_setup_distributable_computation = 200; +const int task_do_distributable_gradient_computation = 42; +const int task_do_distributable_loglikelihood_computation = 43; +const int task_do_distributable_sensitivity_computation = 44; //!@} //! set-up parameters before calling distributable_computation() /*! \ingroup distributable - Empty unless STIR_MPI is defined, in which case it sends parameters to the + Empty unless STIR_MPI is defined, in which case it sends parameters to the slaves (see stir::DistributedWorker). \todo currently uses some global variables for configuration in the distributed namespace. This needs to be converted to a class, e.g. \c DistributedMaster */ -void setup_distributable_computation( - const shared_ptr& proj_pair_sptr, +void setup_distributable_computation(const shared_ptr& proj_pair_sptr, const shared_ptr& exam_info_sptr, const shared_ptr proj_data_info_sptr, - const shared_ptr >& target_sptr, - const bool zero_seg0_end_planes, - const bool distributed_cache_enabled); + const shared_ptr>& target_sptr, + const bool zero_seg0_end_planes, const bool distributed_cache_enabled); //! clean-up after a sequence of computations /*! \ingroup distributable - Empty unless STIR_MPI is defined, in which case it sends the "stop" task to + Empty unless STIR_MPI is defined, in which case it sends the "stop" task to the slaves (see stir::DistributedWorker) */ void end_distributable_computation(); @@ -90,16 +89,15 @@ void end_distributable_computation(); \a count and \a count2 are normally incremental counters that accumulate over the loop in distributable_computation(). - \warning The data in *measured_viewgrams_ptr are allowed to be overwritten, but the new data - will not be used. + \warning The data in *measured_viewgrams_ptr are allowed to be overwritten, but the new data + will not be used. */ -typedef void RPC_process_related_viewgrams_type ( - const shared_ptr& forward_projector_sptr, - const shared_ptr& back_projector_sptr, - RelatedViewgrams* measured_viewgrams_ptr, - int& count, int& count2, double* log_likelihood_ptr, - const RelatedViewgrams* additive_binwise_correction_ptr, - const RelatedViewgrams* mult_viewgrams_ptr); +typedef void RPC_process_related_viewgrams_type(const shared_ptr& forward_projector_sptr, + const shared_ptr& back_projector_sptr, + RelatedViewgrams* measured_viewgrams_ptr, int& count, int& count2, + double* log_likelihood_ptr, + const RelatedViewgrams* additive_binwise_correction_ptr, + const RelatedViewgrams* mult_viewgrams_ptr); /*! \brief This function essentially implements a loop over segments and all views in the current subset. @@ -111,8 +109,8 @@ typedef void RPC_process_related_viewgrams_type ( If STIR_MPI is defined, this function distributes the computation over the slaves. - Subsets are currently defined on views. A particular \a subset_num contains all views which are symmetry related to - \code + Subsets are currently defined on views. A particular \a subset_num contains all views which are symmetry related to + \code proj_data_ptr->min_view_num()+subset_num + n*num_subsets \endcode for n=0,1,,.. \c and for which the above view_num is 'basic' (for some segment_num in the range). @@ -123,15 +121,15 @@ typedef void RPC_process_related_viewgrams_type ( You first need to call setup_distributable_computation(), then you can do multiple calls to distributable_computation() with different images (but the same projection data, as - this is potentially cached). If you want to change the image characteristics (e.g. + this is potentially cached). If you want to change the image characteristics (e.g. size, or origin so), you have to call setup_distributable_computation() again. Finally, end the sequence of computations by a call to end_distributable_computation(). \param output_image_ptr will store the output image if non-zero. \param input_image_ptr input when non-zero. \param proj_data_ptr input projection data - \param read_from_proj_data if true, the \a measured_viewgrams_ptr argument of the call_back function - will be constructed using ProjData::get_related_viewgrams, otherwise + \param read_from_proj_data if true, the \a measured_viewgrams_ptr argument of the call_back function + will be constructed using ProjData::get_related_viewgrams, otherwise ProjData::get_empty_related_viewgrams is used. \param subset_num the number of the current subset (see above). Should be between 0 and num_subsets-1. \param num_subsets the number of subsets to consider. 1 will process all data. @@ -147,61 +145,48 @@ typedef void RPC_process_related_viewgrams_type ( \param start_time_of_frame is passed to normalise_sptr \param end_time_of_frame is passed to normalise_sptr \param RPC_process_related_viewgrams function that does the actual work. - \param caching_info_ptr ignored unless STIR_MPI=1, in which case it enables caching of viewgrams at the slave side + \param caching_info_ptr ignored unless STIR_MPI=1, in which case it enables caching of viewgrams at the slave side \warning There is NO check that the resulting subsets are balanced. \warning The function assumes that \a min_segment_num, \a max_segment_num are such that - symmetries map this range onto itself (i.e. no segment_num is obtained outside the range). - This usually means that \a min_segment_num = -\a max_segment_num. This assumption is checked with + symmetries map this range onto itself (i.e. no segment_num is obtained outside the range). + This usually means that \a min_segment_num = -\a max_segment_num. This assumption is checked with assert(). \todo The subset-scheme should be moved somewhere else (a Subset class?). - \warning If STIR_MPI is defined, there can only be one set_up active, as the + \warning If STIR_MPI is defined, there can only be one set_up active, as the slaves use only one set of variabiles to store projectors etc. \see DistributedWorker for how the slaves perform the computation if STIR_MPI is defined. */ void distributable_computation( - const shared_ptr& forward_projector_sptr, - const shared_ptr& back_projector_sptr, - const shared_ptr& symmetries_sptr, - DiscretisedDensity<3,float>* output_image_ptr, - const DiscretisedDensity<3,float>* input_image_ptr, - const shared_ptr& proj_data_ptr, - const bool read_from_proj_data, - int subset_num, int num_subsets, - int min_segment_num, int max_segment_num, - bool zero_seg0_end_planes, - double* double_out_ptr, - const shared_ptr& additive_binwise_correction, - const shared_ptr normalise_sptr, - const double start_time_of_frame, - const double end_time_of_frame, - RPC_process_related_viewgrams_type * RPC_process_related_viewgrams, - DistributedCachingInformation* caching_info_ptr, - int min_timing_pos_num = 0, int max_timing_pos_num = 0); - - - /*! \name Tag-names currently used by stir::distributable_computation and related functions0 - \ingroup distributable - */ - //!@{ - const int AVAILABLE_NOTIFICATION_TAG=2; - const int END_ITERATION_TAG=3; - const int END_RECONSTRUCTION_TAG=4; - const int END_NOTIFICATION_TAG=5; - const int BINWISE_CORRECTION_TAG=6; - const int BINWISE_MULT_TAG=66; - const int REUSE_VIEWGRAM_TAG=10; - const int NEW_VIEWGRAM_TAG=11; - const int USE_DOUBLE_ARG_TAG=70; - const int USE_OUTPUT_IMAGE_ARG_TAG=71; - - //!@} + const shared_ptr& forward_projector_sptr, const shared_ptr& back_projector_sptr, + const shared_ptr& symmetries_sptr, DiscretisedDensity<3, float>* output_image_ptr, + const DiscretisedDensity<3, float>* input_image_ptr, const shared_ptr& proj_data_ptr, + const bool read_from_proj_data, int subset_num, int num_subsets, int min_segment_num, int max_segment_num, + bool zero_seg0_end_planes, double* double_out_ptr, const shared_ptr& additive_binwise_correction, + const shared_ptr normalise_sptr, const double start_time_of_frame, const double end_time_of_frame, + RPC_process_related_viewgrams_type* RPC_process_related_viewgrams, DistributedCachingInformation* caching_info_ptr, + int min_timing_pos_num = 0, int max_timing_pos_num = 0); + +/*! \name Tag-names currently used by stir::distributable_computation and related functions0 + \ingroup distributable +*/ +//!@{ +const int AVAILABLE_NOTIFICATION_TAG = 2; +const int END_ITERATION_TAG = 3; +const int END_RECONSTRUCTION_TAG = 4; +const int END_NOTIFICATION_TAG = 5; +const int BINWISE_CORRECTION_TAG = 6; +const int BINWISE_MULT_TAG = 66; +const int REUSE_VIEWGRAM_TAG = 10; +const int NEW_VIEWGRAM_TAG = 11; +const int USE_DOUBLE_ARG_TAG = 70; +const int USE_OUTPUT_IMAGE_ARG_TAG = 71; +//!@} END_NAMESPACE_STIR #endif // __stir_recon_buildblock_DISTRIBUTABLE_H__ - diff --git a/src/include/stir/recon_buildblock/distributableMPICacheEnabled.h b/src/include/stir/recon_buildblock/distributableMPICacheEnabled.h index eee7658863..ea2382dde1 100644 --- a/src/include/stir/recon_buildblock/distributableMPICacheEnabled.h +++ b/src/include/stir/recon_buildblock/distributableMPICacheEnabled.h @@ -40,11 +40,9 @@ START_NAMESPACE_STIR class DistributedCachingInformation; - //!@{ //! \ingroup distributable - /*! \brief This function essentially implements a loop over segments and all views in the current subset in the parallel case @@ -57,31 +55,18 @@ class DistributedCachingInformation; \internal */ void distributable_computation_cache_enabled( - const shared_ptr& forward_projector_ptr, - const shared_ptr& back_projector_ptr, - const shared_ptr& symmetries_ptr, - DiscretisedDensity<3,float>* output_image_ptr, - const DiscretisedDensity<3,float>* input_image_ptr, - const shared_ptr& proj_data_sptr, - const bool read_from_proj_data, - int subset_num, int num_subsets, - int min_segment_num, int max_segment_num, - bool zero_seg0_end_planes, - double* double_out_ptr, - const shared_ptr& additive_binwise_correction, - const shared_ptr normalise_sptr, - const double start_time_of_frame, - const double end_time_of_frame, - RPC_process_related_viewgrams_type * RPC_process_related_viewgrams, - DistributedCachingInformation* caching_info_ptr, - int min_timing_pos_num = 0, int max_timing_pos_num = 0 - ); - - -void test_image_estimate(shared_ptr > input_image_ptr); + const shared_ptr& forward_projector_ptr, const shared_ptr& back_projector_ptr, + const shared_ptr& symmetries_ptr, DiscretisedDensity<3, float>* output_image_ptr, + const DiscretisedDensity<3, float>* input_image_ptr, const shared_ptr& proj_data_sptr, + const bool read_from_proj_data, int subset_num, int num_subsets, int min_segment_num, int max_segment_num, + bool zero_seg0_end_planes, double* double_out_ptr, const shared_ptr& additive_binwise_correction, + const shared_ptr normalise_sptr, const double start_time_of_frame, const double end_time_of_frame, + RPC_process_related_viewgrams_type* RPC_process_related_viewgrams, DistributedCachingInformation* caching_info_ptr, + int min_timing_pos_num = 0, int max_timing_pos_num = 0); + +void test_image_estimate(shared_ptr> input_image_ptr); //!@} END_NAMESPACE_STIR #endif // __stir_recon_buildblock_DISTRIBUTABLEMPI_H__ - diff --git a/src/include/stir/recon_buildblock/distributable_main.h b/src/include/stir/recon_buildblock/distributable_main.h index a190a4d7b8..87760fdfd7 100644 --- a/src/include/stir/recon_buildblock/distributable_main.h +++ b/src/include/stir/recon_buildblock/distributable_main.h @@ -39,12 +39,12 @@ START_NAMESPACE_STIR //! main function that starts processing on the master or slave as appropriate /*! This function should be implemented by the application, replacing the usual ::main(). - + DistributedWorker.cxx provides a ::main() function that will set-up everything for - parallel processing (as appropriate on the master and slaves), and then calls + parallel processing (as appropriate on the master and slaves), and then calls distributable_main() on the master (passing any arguments along). - If STIR_MPI is not defined, ::main() will simply call distributable_main(). + If STIR_MPI is not defined, ::main() will simply call distributable_main(). Skeleton of a program that uses this module: \code @@ -57,11 +57,10 @@ START_NAMESPACE_STIR \endcode */ -int distributable_main(int argc, char **argv); +int distributable_main(int argc, char** argv); //!@} END_NAMESPACE_STIR #endif // __stir_recon_buildblock_DISTRIBUTABLE_main_H__ - diff --git a/src/include/stir/recon_buildblock/distributed_functions.h b/src/include/stir/recon_buildblock/distributed_functions.h index ea2a65ee9f..fa301538b2 100644 --- a/src/include/stir/recon_buildblock/distributed_functions.h +++ b/src/include/stir/recon_buildblock/distributed_functions.h @@ -20,9 +20,9 @@ #define __stir_recon_buildblock_DistributedFunctions_h__ /*! - \file + \file \ingroup distributable - + \brief Declaration of functions in the distributed namespace \author Tobias Beisel @@ -33,11 +33,11 @@ \namespace distributed \brief Namespace for distributed computation with MPI - This is a collection of functions to send and receive objects and data - needed for distributed computation with MPI. They all come in a separate + This is a collection of functions to send and receive objects and data + needed for distributed computation with MPI. They all come in a separate namespace "distributed". There was no need to have an object providing this functionality as that would have been more costly. - + Note that every send function has a corresponding receive function. \see STIR_MPI @@ -47,8 +47,8 @@ */ #ifdef DOXYGEN_SKIP // need to define the following to get the documentation -#define STIR_MPI -#define STIR_MPI_TIMINGS +# define STIR_MPI +# define STIR_MPI_TIMINGS #endif /*! \def STIR_MPI @@ -62,16 +62,16 @@ measurements included which print times from start to end of a single Send or Receive command. This will uncover some maybe unnecessary long waiting times. It was actually used while developing the parallel version to check whether some code - rearrangements would lead to faster computation. + rearrangements would lead to faster computation. To enable these timings, compile the MPI-Version with the preprocessor variable STIR_MPI_TIMINGS defined. - + If compiled with STIR_MPI_TIMINGS defined, you still have the possibility to enable/disable the timings by using the parsing parameter (for stir::stir::PoissonLogLikelihoodWithLinearModelForMeanAndProjData). \verbatim enable message timings := 1 \endverbatim - To give the printed times a threshold use + To give the printed times a threshold use \verbatim message timings threshold := 0.1 \endverbatim @@ -87,356 +87,337 @@ #include "stir/ProjDataInfo.h" namespace stir { - class ExamInfo; +class ExamInfo; } -namespace distributed -{ - /*! \name Global variables used for STIR_MPI - */ - //@{ - //!the number of processes used for distributed computation - extern int num_processors; - - //!some stuff in distributable_computation needs to be done only in the first iteration - extern bool first_iteration; - - //!enable/disable tests - extern bool test; - - //for timings - extern bool rpc_time; //!enable timings for PRC_process_related_viewgrams_gradient() computation - extern bool test_send_receive_times; //!enable timings for every single send/receive operation - - extern double total_rpc_time; //!adding up the time used for PRC_process_related_viewgrams_gradient() computation at all slaves - extern double total_rpc_time_2; //!adding up the time used for PRC_process_related_viewgrams_gradient() computation at a single slave - extern double total_rpc_time_slaves; //!value to reduce the total_rpc_time values - extern double min_threshold; //!threshold for displaying send/receive times, initially set to 0.1 seconds - - - //----------------------Send operations---------------------------------- - - /*! \brief sends or broadcasts an integer value - * \param value the int value to be sent - * \param destination the process id where to send the interger value. If set to -1 a Broadcast will be done - */ - void send_int_value(int value, int destination); - - - /*! \brief sends or broadcasts a string - * \param str the string to be sent - * \param tag identifier to associate messages - * \param destination the process id where to send the string. If set to -1 a Broadcast will be done - */ - void send_string(const std::string& str, int tag, int destination); - - - /*! \brief send or broadcast a bool value - * \param value the bool value to be sent - * \param tag identifier to associate messages. If the tag is -1 a Broadcast will be done - * \param destination the process id where to send the bool value. If set to -1 a Broadcast will be done - * - * This function actually sends an integer value (0 or 1) as there is no bool datatype in MPI - */ - void send_bool_value(bool value, int tag, int destination); - - - /*! \brief sends or broadcasts some integer values - * \param values pointer to integer values to be sent - * \param count the count of integer values to be sent - * \param tag identifier to associate messages - * \param destination the process id where to send the int values. If set to -1 a Broadcast will be done - */ - void send_int_values(int * values, int count, int tag, int destination); - - - /*! \brief send or broadcast double values - * \param values pointer to the double values to be sent - * \param count the count of double values to be sent - * \param tag identifier to associate messages - * \param destination the process id where to send the double values. If set to -1 a Broadcast will be done - */ - void send_double_values(double * values, int count, int tag, int destination); - - /*! \brief send or broadcast ViewSegmentNumbers object - * \param vs_num value to be sent - * \param tag identifier to associate messages - * \param destination the process id where to send the double values. If set to -1 a Broadcast will be done - */ - void send_view_segment_numbers(const stir::ViewSegmentNumbers& vs_num, int tag, int destination); - - /*! \brief send or broadcast a projector-pair object - * \param proj_pair_sptr value to be sent - * \param destination the process id where to send the double values. If set to -1 a Broadcast will be done - * - * \warning This function works by sending the parameter_info. Therefore, if file-names are used (e.g. for - * a projection matrix on disk) this will only work on systems with shared file systems. - */ - void send_projectors(const stir::shared_ptr &proj_pair_sptr, int destination); - - /*! \brief sends or broadcasts the parameters of a DiscretisedDensity object - * \param input_image_ptr the image_ptr to be sent - * \param tag identifier to associate messages - * \param destination the process id where to send the image parameters. If set to -1 a Broadcast will be done - * - * This function sends all parameters needed to construct a corresponding image object at the - * slave. The image values are sent separately. This is done, as sending the parameters is not - * needed everytime the values are sent. The slave needs to receive the current_image_estimate - * every iteration, but he already knows the parameters. - * - * The actual parameters sent are the image dimensions, the origin and the grid_spacing - */ - void send_image_parameters(const stir::DiscretisedDensity<3,float>* input_image_ptr, int tag, int destination); - - - /*! \brief sends or broadcasts the values of a DiscretisedDensity object - * \param input_image_ptr the image_ptr to be sent - * \param destination the process id where to send the image values. If set to -1 a Broadcast will be done - * - * This function sends the values of an image. The values are serialized to a one-dimensional array - * as MPI only sends that kind of data structures. - */ - void send_image_estimate(const stir::DiscretisedDensity<3,float>* input_image_ptr, int destination); - - - /*! \brief sends or broadcasts the information from ExamInfo and ProjDataInfo - * \param exam_info the ExamInfo pointer to be sent - * \param proj_data_info the ProjDataInfo pointer to be sent - * \param destination the process id where to send the values. If set to -1 a Broadcast will be done - * - * For sending the objects a rather "dirty trick" is used: - * Instead of sending all needed data for constructing the objects, - * the information is temporarily stored as ProjDataInterfile and then buffered - * as text from the file. Afterwards, that is sent to the slave. - * - * Doing this, the slave is able to construct the objects by using the received information. - * The sent char-array is used as stream-input to the parse() function of InterfileHeader. - * - * \warning Creates a temporary Interfile header in the local working directory. - */ - void send_exam_and_proj_data_info(const stir::ExamInfo& exam_info, const stir::ProjDataInfo& proj_data_info, int destination); - - - /*! \brief sends a RelatedViegrams object - * \param viewgrams the viewgrams to be sent - * \param destination the process id where to send the related viewgrams - * - * This function iterates through the relatedviewgrams object and calls \c send_viewgram() - * for each comprised viewgram. - * The only value sent is the count of viewgrams contained within the related_viewgrams object - * to make sure that the worker knows how many viewgrams he has to receive. - */ - void send_related_viewgrams(stir::RelatedViewgrams* viewgrams, int destination); - - - /*! \brief sends a Viewgram object - * \param viewgram the viewgrams to be sent - * \param destination the process id where to send the viewgram - * - * This function sends all parameters needed for the construction of the viewgram at the worker, - * as well as the actual values of the viewgram. That would mean that 2 Messages are sent: - * 1. The dimensions of the viewgram and the vs_num - * 2. The values detwermined by iterating through the viewgram and serializing it to a one-dimensional array - */ - void send_viewgram(const stir::Viewgram& viewgram, int destination); - - - //----------------------Receive operations---------------------------------- - - - /*! \brief receives a single integer value - * \param source the process id from which to receive the interger value. If set to -1 the receive will be done from broadcast - * \returns the received int value - */ - int receive_int_value(int source); - - - /*! \brief receives a string - * \param tag unique identifier to associate messages - * \param source the process id from which to receive the string - * \returns the received string - */ - std::string receive_string(int tag, int source); - - - /*! \brief receives all needed information to subsequently construct a ProjectorByBinPair object - * \param projector_pair_ptr address pointer of the new ProjectorByBinPair pointer - * \param source the process id from which to receive the ProjectorByBinPair - * - * First the registered_name string of the ProjectorByBinPair is received. - * Then the parameter_info() of the masters ProjectorByBinPair is received. - * Using the registered_name and the parameter_info() as stream, both can be used as - * input to the read_registered_object function, which then constructs the new ProjectorByBinPair pointer. - * - * The passed address pointer parameter will then be redirected to the address of ProjectorByBinPair created - * using the received parameters. - */ - void receive_and_initialize_projectors(stir::shared_ptr &projector_pair_ptr, int source); - - - /*! \brief receives a bool value - * \param tag unique identifier to associate messages - * \param source the process id from which to receive the bool value - * \returns the received bool value - * - * This function actually receives an integer value (0 or 1) - * as there is no bool datatype in MPI, but it will return a bool value - */ - bool receive_bool_value(int tag, int source); - - - /*! \brief receives some integer values - * \param values pointer to the receive buffer - * \param count the count of integer values to be received - * \param tag identifier to associate messages - * \returns MPI_Status object to query the source of the message - * - * The tag needs to be set to ARBITRARY_TAG (=8) if MPI_ANY_TAG shall be used - */ - MPI_Status receive_int_values(int * values, int count, int tag); - - - /*! \brief receives some double values - * \param values pointer to the receive buffer - * \param count the count of double values to be received - * \param tag identifier to associate messages - * \returns MPI_Status object to query the source of the message - * - * The tag needs to be set to ARBITRARY_TAG (=8) if MPI_ANY_TAG shall be used - */ - MPI_Status receive_double_values(double* values, int count, int tag); - - /*! \brief receive a ViewSegmentNumbers object - * \param[out] vs_num value that will be set - * \param tag identifier to associate messages - * \returns MPI_Status object to query the source of the message - * - * The tag needs to be set to ARBITRARY_TAG (=8) if MPI_ANY_TAG shall be used - */ - MPI_Status receive_view_segment_numbers(stir::ViewSegmentNumbers& vs_num, int tag); - - /*! \brief receives the parameters of a DiscretisedDensity object - * \param image_ptr address pointer of the new DiscretisedDensity - * \param buffer saves the image buffer size to be reused when receiving the image values - * \param tag identifier to associate messages. If set to -1 a Broadcast will be done - * \param source the process id from which to receive the image parameters. - * - * This function receives all parameters needed to construct an image object at the - * slave. The image values are sent separately. - * - * The actual parameters received are the image dimensions, the origin and the grid_spacing - * - * The function currently only supports VoxelsOnCartesianGrid - */ - void receive_and_set_image_parameters(stir::shared_ptr > &image_ptr, int &buffer, int tag, int source); - - - /*! \brief receives the values of a DiscretisedDensity object - * \param image_ptr the image_ptr to be sent - * \param buffer_size gives the needed size of the receive buffer - * \param source the process id from which to receive the image values. - * \returns MPI_Status object to query the source of the message - * - * The image_ptr is filled by iterating through the target pointer and copying - * the single values from the receive buffer. - * - * The buffer_size is used again to reduce the image values - */ - MPI_Status receive_image_values_and_fill_image_ptr(stir::shared_ptr > &image_ptr, int buffer_size, int source); - - - /*! \brief receives information of ExamInfo and ProjDataInfo objects and constructs new ones from it - * \param exam_info_sptr the new ExamInfo pointer to be set up - * \param proj_data_info_sptr the new ProjDataInfo pointer to be set up - * \param source the process id from which to receive from - * - * The parameter info is received as a Interfile Header string. That way the slave is able - * to construct a ProjDataInfo within a InterfilePDFSHeader using the received - * char-array as stream-input to the parse() function of InterfilePDFSHeader. - */ - void receive_and_construct_exam_and_proj_data_info_ptr(stir::shared_ptr& exam_info_sptr, - stir::shared_ptr& proj_data_info_sptr, - int source); - - - /*! \brief receives and constructs a RelatedViewgrams object - * \param viewgrams object that will be filled with the data - * \param proj_data_info_ptr the ProjDataInfo pointer describing the data - * \param symmetries_sptr the symmetries pointer constructed when setting up the projectors - * \param source the process id from which to receive the ProjDataInfo - * - * First of all it is important to notice, that this function is not independent. To construct - * a new RelatedViegrams object, the symmetries_ptr must be available. That would mean, - * that the slave has to call \c receive_and_initialize_projectors() to set up the symmetries_ptr - * before the related_vewgrams can be received. - * Additionally the ProjDataInfo-pointer must be available for receiving a single viewgrams. - * That implies calling \c receive_and_construct_proj_data_info_ptr() before. - * To make this function independent, both of these objects have to be sent here. On the other hand - * that would lead to the overhead of sending it everytime a related_viewgram is sent, - * which is really expensive. - * - * This function receives the count of viewgrams to be received and calls - * \c receive_and_construct_viewgram that often. Every received viewgram is pushed back - * to a viewgram vector, which afterwards is used with the symmetries to construct - * a RelatedViewgrams object. - */ - void receive_and_construct_related_viewgrams(stir::RelatedViewgrams*& viewgrams, - const stir::shared_ptr& proj_data_info_ptr, - const stir::shared_ptr symmetries_sptr, - int source); - - /*! \brief receives a Viewgram object - * \param viewgram the viewgrams to be constructed - * \param proj_data_info_ptr the ProjDataInfo pointer describing the data - * \param source the process id from which to receive the ProjDataInfo - * - * This function received all parameters needed for the construction of the viewgram at the worker, - * as well as the actual values of the viewgram. That would mean that 2 Messages are received: - * 1. The dimensions of the viewgram and the vs_num - * 2. The values of the viewgram - * - * The buffer_size needed to receive the values is calculated from the dimensions received. - * The viewgram is filled by iterating througn it and copying the values of the received values. - */ - void receive_and_construct_viewgram(stir::Viewgram*& viewgram, - const stir::shared_ptr& proj_data_info_ptr, - int source); - - //-----------------------reduce operations------------------------------------- - - /*! \brief the function called by the master to reduce the output image - * \param output_image_ptr the image pointer where the reduced image is saved - * \param destination the process id where the output_image is reduced - */ - void reduce_received_output_image(stir::DiscretisedDensity<3,float>* output_image_ptr, int destination); - - /*! \brief the function called by the slaves to reduce the output image - * \param output_image_ptr the image pointer where the reduced image is saved - * \param image_buffer_size the buffer size needed for the image - * \param my_rank rank of the slave, only used for screen output - * \param destination the process id where the output_image is reduced - * - * The buffer size was calculated in \c receive_image_values_and_fill_image_ptr(). - * Alternatively it can be calculated by the image parameters. - */ - void reduce_output_image(stir::shared_ptr > &output_image_ptr, int image_buffer_size, int my_rank, int destination); - - /*! \name Tag-names currently used by functions in the distributed namespace - */ - //!@{ - const int INT_TAG=7; - const int ARBITRARY_TAG=8; //!< special tag, equivalent to MPI_ANY_TAG in some functions - const int STIR_MPI_CONF_TAG=9; - const int IMAGE_ESTIMATE_TAG=23; - const int IMAGE_PARAMETER_TAG=24; - const int VIEWGRAM_DIMENSIONS_TAG=27; - const int VIEWGRAM_TAG=28; - const int VIEWGRAM_COUNT_TAG=29; - const int PROJECTION_DATA_INFO_TAG=30; - const int PARAMETER_INFO_TAG=21; - const int REGISTERED_NAME_TAG=25; - //!@} -} +namespace distributed { +/*! \name Global variables used for STIR_MPI + */ +//@{ +//! the number of processes used for distributed computation +extern int num_processors; -#endif +//! some stuff in distributable_computation needs to be done only in the first iteration +extern bool first_iteration; + +//! enable/disable tests +extern bool test; + +// for timings +extern bool rpc_time; //! enable timings for PRC_process_related_viewgrams_gradient() computation +extern bool test_send_receive_times; //! enable timings for every single send/receive operation + +extern double total_rpc_time; //! adding up the time used for PRC_process_related_viewgrams_gradient() computation at all slaves +extern double + total_rpc_time_2; //! adding up the time used for PRC_process_related_viewgrams_gradient() computation at a single slave +extern double total_rpc_time_slaves; //! value to reduce the total_rpc_time values +extern double min_threshold; //! threshold for displaying send/receive times, initially set to 0.1 seconds + +//----------------------Send operations---------------------------------- + +/*! \brief sends or broadcasts an integer value + * \param value the int value to be sent + * \param destination the process id where to send the interger value. If set to -1 a Broadcast will be done + */ +void send_int_value(int value, int destination); + +/*! \brief sends or broadcasts a string + * \param str the string to be sent + * \param tag identifier to associate messages + * \param destination the process id where to send the string. If set to -1 a Broadcast will be done + */ +void send_string(const std::string& str, int tag, int destination); + +/*! \brief send or broadcast a bool value + * \param value the bool value to be sent + * \param tag identifier to associate messages. If the tag is -1 a Broadcast will be done + * \param destination the process id where to send the bool value. If set to -1 a Broadcast will be done + * + * This function actually sends an integer value (0 or 1) as there is no bool datatype in MPI + */ +void send_bool_value(bool value, int tag, int destination); + +/*! \brief sends or broadcasts some integer values + * \param values pointer to integer values to be sent + * \param count the count of integer values to be sent + * \param tag identifier to associate messages + * \param destination the process id where to send the int values. If set to -1 a Broadcast will be done + */ +void send_int_values(int* values, int count, int tag, int destination); + +/*! \brief send or broadcast double values + * \param values pointer to the double values to be sent + * \param count the count of double values to be sent + * \param tag identifier to associate messages + * \param destination the process id where to send the double values. If set to -1 a Broadcast will be done + */ +void send_double_values(double* values, int count, int tag, int destination); + +/*! \brief send or broadcast ViewSegmentNumbers object + * \param vs_num value to be sent + * \param tag identifier to associate messages + * \param destination the process id where to send the double values. If set to -1 a Broadcast will be done + */ +void send_view_segment_numbers(const stir::ViewSegmentNumbers& vs_num, int tag, int destination); + +/*! \brief send or broadcast a projector-pair object + * \param proj_pair_sptr value to be sent + * \param destination the process id where to send the double values. If set to -1 a Broadcast will be done + * + * \warning This function works by sending the parameter_info. Therefore, if file-names are used (e.g. for + * a projection matrix on disk) this will only work on systems with shared file systems. + */ +void send_projectors(const stir::shared_ptr& proj_pair_sptr, int destination); + +/*! \brief sends or broadcasts the parameters of a DiscretisedDensity object + * \param input_image_ptr the image_ptr to be sent + * \param tag identifier to associate messages + * \param destination the process id where to send the image parameters. If set to -1 a Broadcast will be done + * + * This function sends all parameters needed to construct a corresponding image object at the + * slave. The image values are sent separately. This is done, as sending the parameters is not + * needed everytime the values are sent. The slave needs to receive the current_image_estimate + * every iteration, but he already knows the parameters. + * + * The actual parameters sent are the image dimensions, the origin and the grid_spacing + */ +void send_image_parameters(const stir::DiscretisedDensity<3, float>* input_image_ptr, int tag, int destination); + +/*! \brief sends or broadcasts the values of a DiscretisedDensity object + * \param input_image_ptr the image_ptr to be sent + * \param destination the process id where to send the image values. If set to -1 a Broadcast will be done + * + * This function sends the values of an image. The values are serialized to a one-dimensional array + * as MPI only sends that kind of data structures. + */ +void send_image_estimate(const stir::DiscretisedDensity<3, float>* input_image_ptr, int destination); + +/*! \brief sends or broadcasts the information from ExamInfo and ProjDataInfo + * \param exam_info the ExamInfo pointer to be sent + * \param proj_data_info the ProjDataInfo pointer to be sent + * \param destination the process id where to send the values. If set to -1 a Broadcast will be done + * + * For sending the objects a rather "dirty trick" is used: + * Instead of sending all needed data for constructing the objects, + * the information is temporarily stored as ProjDataInterfile and then buffered + * as text from the file. Afterwards, that is sent to the slave. + * + * Doing this, the slave is able to construct the objects by using the received information. + * The sent char-array is used as stream-input to the parse() function of InterfileHeader. + * + * \warning Creates a temporary Interfile header in the local working directory. + */ +void send_exam_and_proj_data_info(const stir::ExamInfo& exam_info, const stir::ProjDataInfo& proj_data_info, int destination); + +/*! \brief sends a RelatedViegrams object + * \param viewgrams the viewgrams to be sent + * \param destination the process id where to send the related viewgrams + * + * This function iterates through the relatedviewgrams object and calls \c send_viewgram() + * for each comprised viewgram. + * The only value sent is the count of viewgrams contained within the related_viewgrams object + * to make sure that the worker knows how many viewgrams he has to receive. + */ +void send_related_viewgrams(stir::RelatedViewgrams* viewgrams, int destination); +/*! \brief sends a Viewgram object + * \param viewgram the viewgrams to be sent + * \param destination the process id where to send the viewgram + * + * This function sends all parameters needed for the construction of the viewgram at the worker, + * as well as the actual values of the viewgram. That would mean that 2 Messages are sent: + * 1. The dimensions of the viewgram and the vs_num + * 2. The values detwermined by iterating through the viewgram and serializing it to a one-dimensional array + */ +void send_viewgram(const stir::Viewgram& viewgram, int destination); + +//----------------------Receive operations---------------------------------- + +/*! \brief receives a single integer value + * \param source the process id from which to receive the interger value. If set to -1 the receive will be done from broadcast + * \returns the received int value + */ +int receive_int_value(int source); + +/*! \brief receives a string + * \param tag unique identifier to associate messages + * \param source the process id from which to receive the string + * \returns the received string + */ +std::string receive_string(int tag, int source); + +/*! \brief receives all needed information to subsequently construct a ProjectorByBinPair object + * \param projector_pair_ptr address pointer of the new ProjectorByBinPair pointer + * \param source the process id from which to receive the ProjectorByBinPair + * + * First the registered_name string of the ProjectorByBinPair is received. + * Then the parameter_info() of the masters ProjectorByBinPair is received. + * Using the registered_name and the parameter_info() as stream, both can be used as + * input to the read_registered_object function, which then constructs the new ProjectorByBinPair pointer. + * + * The passed address pointer parameter will then be redirected to the address of ProjectorByBinPair created + * using the received parameters. + */ +void receive_and_initialize_projectors(stir::shared_ptr& projector_pair_ptr, int source); + +/*! \brief receives a bool value + * \param tag unique identifier to associate messages + * \param source the process id from which to receive the bool value + * \returns the received bool value + * + * This function actually receives an integer value (0 or 1) + * as there is no bool datatype in MPI, but it will return a bool value + */ +bool receive_bool_value(int tag, int source); + +/*! \brief receives some integer values + * \param values pointer to the receive buffer + * \param count the count of integer values to be received + * \param tag identifier to associate messages + * \returns MPI_Status object to query the source of the message + * + * The tag needs to be set to ARBITRARY_TAG (=8) if MPI_ANY_TAG shall be used + */ +MPI_Status receive_int_values(int* values, int count, int tag); + +/*! \brief receives some double values + * \param values pointer to the receive buffer + * \param count the count of double values to be received + * \param tag identifier to associate messages + * \returns MPI_Status object to query the source of the message + * + * The tag needs to be set to ARBITRARY_TAG (=8) if MPI_ANY_TAG shall be used + */ +MPI_Status receive_double_values(double* values, int count, int tag); + +/*! \brief receive a ViewSegmentNumbers object + * \param[out] vs_num value that will be set + * \param tag identifier to associate messages + * \returns MPI_Status object to query the source of the message + * + * The tag needs to be set to ARBITRARY_TAG (=8) if MPI_ANY_TAG shall be used + */ +MPI_Status receive_view_segment_numbers(stir::ViewSegmentNumbers& vs_num, int tag); + +/*! \brief receives the parameters of a DiscretisedDensity object + * \param image_ptr address pointer of the new DiscretisedDensity + * \param buffer saves the image buffer size to be reused when receiving the image values + * \param tag identifier to associate messages. If set to -1 a Broadcast will be done + * \param source the process id from which to receive the image parameters. + * + * This function receives all parameters needed to construct an image object at the + * slave. The image values are sent separately. + * + * The actual parameters received are the image dimensions, the origin and the grid_spacing + * + * The function currently only supports VoxelsOnCartesianGrid + */ +void receive_and_set_image_parameters(stir::shared_ptr>& image_ptr, int& buffer, int tag, + int source); + +/*! \brief receives the values of a DiscretisedDensity object + * \param image_ptr the image_ptr to be sent + * \param buffer_size gives the needed size of the receive buffer + * \param source the process id from which to receive the image values. + * \returns MPI_Status object to query the source of the message + * + * The image_ptr is filled by iterating through the target pointer and copying + * the single values from the receive buffer. + * + * The buffer_size is used again to reduce the image values + */ +MPI_Status receive_image_values_and_fill_image_ptr(stir::shared_ptr>& image_ptr, + int buffer_size, int source); + +/*! \brief receives information of ExamInfo and ProjDataInfo objects and constructs new ones from it + * \param exam_info_sptr the new ExamInfo pointer to be set up + * \param proj_data_info_sptr the new ProjDataInfo pointer to be set up + * \param source the process id from which to receive from + * + * The parameter info is received as a Interfile Header string. That way the slave is able + * to construct a ProjDataInfo within a InterfilePDFSHeader using the received + * char-array as stream-input to the parse() function of InterfilePDFSHeader. + */ +void receive_and_construct_exam_and_proj_data_info_ptr(stir::shared_ptr& exam_info_sptr, + stir::shared_ptr& proj_data_info_sptr, int source); + +/*! \brief receives and constructs a RelatedViewgrams object + * \param viewgrams object that will be filled with the data + * \param proj_data_info_ptr the ProjDataInfo pointer describing the data + * \param symmetries_sptr the symmetries pointer constructed when setting up the projectors + * \param source the process id from which to receive the ProjDataInfo + * + * First of all it is important to notice, that this function is not independent. To construct + * a new RelatedViegrams object, the symmetries_ptr must be available. That would mean, + * that the slave has to call \c receive_and_initialize_projectors() to set up the symmetries_ptr + * before the related_vewgrams can be received. + * Additionally the ProjDataInfo-pointer must be available for receiving a single viewgrams. + * That implies calling \c receive_and_construct_proj_data_info_ptr() before. + * To make this function independent, both of these objects have to be sent here. On the other hand + * that would lead to the overhead of sending it everytime a related_viewgram is sent, + * which is really expensive. + * + * This function receives the count of viewgrams to be received and calls + * \c receive_and_construct_viewgram that often. Every received viewgram is pushed back + * to a viewgram vector, which afterwards is used with the symmetries to construct + * a RelatedViewgrams object. + */ +void receive_and_construct_related_viewgrams(stir::RelatedViewgrams*& viewgrams, + const stir::shared_ptr& proj_data_info_ptr, + const stir::shared_ptr symmetries_sptr, + int source); + +/*! \brief receives a Viewgram object + * \param viewgram the viewgrams to be constructed + * \param proj_data_info_ptr the ProjDataInfo pointer describing the data + * \param source the process id from which to receive the ProjDataInfo + * + * This function received all parameters needed for the construction of the viewgram at the worker, + * as well as the actual values of the viewgram. That would mean that 2 Messages are received: + * 1. The dimensions of the viewgram and the vs_num + * 2. The values of the viewgram + * + * The buffer_size needed to receive the values is calculated from the dimensions received. + * The viewgram is filled by iterating througn it and copying the values of the received values. + */ +void receive_and_construct_viewgram(stir::Viewgram*& viewgram, + const stir::shared_ptr& proj_data_info_ptr, int source); + +//-----------------------reduce operations------------------------------------- + +/*! \brief the function called by the master to reduce the output image + * \param output_image_ptr the image pointer where the reduced image is saved + * \param destination the process id where the output_image is reduced + */ +void reduce_received_output_image(stir::DiscretisedDensity<3, float>* output_image_ptr, int destination); + +/*! \brief the function called by the slaves to reduce the output image + * \param output_image_ptr the image pointer where the reduced image is saved + * \param image_buffer_size the buffer size needed for the image + * \param my_rank rank of the slave, only used for screen output + * \param destination the process id where the output_image is reduced + * + * The buffer size was calculated in \c receive_image_values_and_fill_image_ptr(). + * Alternatively it can be calculated by the image parameters. + */ +void reduce_output_image(stir::shared_ptr>& output_image_ptr, int image_buffer_size, + int my_rank, int destination); + +/*! \name Tag-names currently used by functions in the distributed namespace + */ +//!@{ +const int INT_TAG = 7; +const int ARBITRARY_TAG = 8; //!< special tag, equivalent to MPI_ANY_TAG in some functions +const int STIR_MPI_CONF_TAG = 9; +const int IMAGE_ESTIMATE_TAG = 23; +const int IMAGE_PARAMETER_TAG = 24; +const int VIEWGRAM_DIMENSIONS_TAG = 27; +const int VIEWGRAM_TAG = 28; +const int VIEWGRAM_COUNT_TAG = 29; +const int PROJECTION_DATA_INFO_TAG = 30; +const int PARAMETER_INFO_TAG = 21; +const int REGISTERED_NAME_TAG = 25; +//!@} +} // namespace distributed + +#endif diff --git a/src/include/stir/recon_buildblock/distributed_test_functions.h b/src/include/stir/recon_buildblock/distributed_test_functions.h index dc1c74de2e..764da11169 100644 --- a/src/include/stir/recon_buildblock/distributed_test_functions.h +++ b/src/include/stir/recon_buildblock/distributed_test_functions.h @@ -21,36 +21,35 @@ #define __stir_recon_buildblock_DistributedTestFunctions_h__ /*! - \file + \file \ingroup distributable - + \brief Declaration of test functions for the distributed namespace - This is a collection of functions to test the function implemented - in DistributedFunction.cxx . Each of the possible tests consists of a + This is a collection of functions to test the function implemented + in DistributedFunction.cxx . Each of the possible tests consists of a master and a slave function to be called by different processes. Note that every master function has a corresponding slave function. - + \todo Currently no independent test functions are implemented. The tests are used - by embedding them into the reconstruction functions and calling them once. - - This is only done in debug mode and can be enabled/disabled with the following - parsing parameter: + by embedding them into the reconstruction functions and calling them once. + + This is only done in debug mode and can be enabled/disabled with the following + parsing parameter: \verbatim enable distributed tests := 1 \endverbatim The default value is 0. - + This obviously not a good way to do testing, so there is a need to write independent test functions. The problem with that is, that all needed objects have to be set up. - The function headers give an idea of what would have to be constructed. - + The function headers give an idea of what would have to be constructed. + \author Tobias Beisel */ - #include "mpi.h" #include "stir/shared_ptr.h" #include "stir/DataSymmetriesForViewSegmentNumbers.h" @@ -61,43 +60,39 @@ #include #include -namespace distributed -{ - //-----------------------test functions------------------------------------------ - - - void test_viewgram_slave(const stir::shared_ptr& proj_data_info_ptr); - - void test_viewgram_master(stir::Viewgram viewgram, const stir::shared_ptr& proj_data_info_ptr); - - void test_image_estimate_master(const stir::DiscretisedDensity<3,float>* input_image_ptr, int slave); - - void test_image_estimate_slave(); - - void test_related_viewgrams_master(const stir::shared_ptr& proj_data_info_ptr, - const stir::shared_ptr symmetries_sptr, - stir::RelatedViewgrams* y, int slave); - - void test_related_viewgrams_slave(const stir::shared_ptr& proj_data_info_ptr, - const stir::shared_ptr symmetries_sptr - ); - - void test_parameter_info_master(const std::string str, int slave, char const * const text); - - void test_parameter_info_slave(const std::string str); - - void test_bool_value_master(bool value, int slave); - - void test_bool_value_slave(); - - void test_int_value_master(int value, int slave); - - void test_int_value_slave(); - - void test_int_values_master(int slave); - - void test_int_values_slave(); -} +namespace distributed { +//-----------------------test functions------------------------------------------ -#endif +void test_viewgram_slave(const stir::shared_ptr& proj_data_info_ptr); + +void test_viewgram_master(stir::Viewgram viewgram, const stir::shared_ptr& proj_data_info_ptr); + +void test_image_estimate_master(const stir::DiscretisedDensity<3, float>* input_image_ptr, int slave); + +void test_image_estimate_slave(); + +void test_related_viewgrams_master(const stir::shared_ptr& proj_data_info_ptr, + const stir::shared_ptr symmetries_sptr, + stir::RelatedViewgrams* y, int slave); +void test_related_viewgrams_slave(const stir::shared_ptr& proj_data_info_ptr, + const stir::shared_ptr symmetries_sptr); + +void test_parameter_info_master(const std::string str, int slave, char const* const text); + +void test_parameter_info_slave(const std::string str); + +void test_bool_value_master(bool value, int slave); + +void test_bool_value_slave(); + +void test_int_value_master(int value, int slave); + +void test_int_value_slave(); + +void test_int_values_master(int slave); + +void test_int_values_slave(); +} // namespace distributed + +#endif diff --git a/src/include/stir/recon_buildblock/find_basic_vs_nums_in_subsets.h b/src/include/stir/recon_buildblock/find_basic_vs_nums_in_subsets.h index ebd80ecf7d..5a63d14c12 100644 --- a/src/include/stir/recon_buildblock/find_basic_vs_nums_in_subsets.h +++ b/src/include/stir/recon_buildblock/find_basic_vs_nums_in_subsets.h @@ -26,7 +26,6 @@ \author Kris Thielemans */ - #include "stir/ViewSegmentNumbers.h" #include @@ -35,23 +34,21 @@ START_NAMESPACE_STIR class ProjDataInfo; class DataSymmetriesForViewSegmentNumbers; -namespace detail -{ +namespace detail { - /*! - \brief a helper function to find which view/segments are in a subset - \ingroup recon_buildblock +/*! + \brief a helper function to find which view/segments are in a subset + \ingroup recon_buildblock - This function is used by projectors and distributable_computation etc - to construct a list of view/segments that are in a subset, and which are - "basic" w.r.t the symmetries. - */ - std::vector - find_basic_vs_nums_in_subset(const ProjDataInfo& proj_data_info, - const DataSymmetriesForViewSegmentNumbers& symmetries, - const int min_segment_num, const int max_segment_num, - const int subset_num, const int num_subsets); + This function is used by projectors and distributable_computation etc + to construct a list of view/segments that are in a subset, and which are + "basic" w.r.t the symmetries. +*/ +std::vector find_basic_vs_nums_in_subset(const ProjDataInfo& proj_data_info, + const DataSymmetriesForViewSegmentNumbers& symmetries, + const int min_segment_num, const int max_segment_num, + const int subset_num, const int num_subsets); -} +} // namespace detail END_NAMESPACE_STIR diff --git a/src/include/stir/recon_buildblock/test/PoissonLLReconstructionTests.h b/src/include/stir/recon_buildblock/test/PoissonLLReconstructionTests.h index c49c2fbea2..e86e2ef4e0 100644 --- a/src/include/stir/recon_buildblock/test/PoissonLLReconstructionTests.h +++ b/src/include/stir/recon_buildblock/test/PoissonLLReconstructionTests.h @@ -31,39 +31,35 @@ START_NAMESPACE_STIR PoissonLogLikelihoodWithLinearModelForMeanAndProjData */ template -class PoissonLLReconstructionTests : public ReconstructionTests -{ +class PoissonLLReconstructionTests : public ReconstructionTests { private: typedef ReconstructionTests base_type; + public: //! Constructor that can take some input data to run the test with - explicit inline - PoissonLLReconstructionTests(const std::string &proj_data_filename = "", - const std::string & density_filename = "") - : base_type(proj_data_filename, density_filename) - {} + explicit inline PoissonLLReconstructionTests(const std::string& proj_data_filename = "", + const std::string& density_filename = "") + : base_type(proj_data_filename, density_filename) {} //! creates Poisson log likelihood /*! sets \c _proj_data_sptr and uses \c _input_density_sptr for set_up. - */ + */ virtual inline void construct_log_likelihood(); protected: - shared_ptr > _objective_function_sptr; + shared_ptr> _objective_function_sptr; }; template void -PoissonLLReconstructionTests:: -construct_log_likelihood() -{ +PoissonLLReconstructionTests::construct_log_likelihood() { this->_objective_function_sptr.reset(new PoissonLogLikelihoodWithLinearModelForMeanAndProjData); PoissonLogLikelihoodWithLinearModelForMeanAndProjData& objective_function = - reinterpret_cast< PoissonLogLikelihoodWithLinearModelForMeanAndProjData& >(*this->_objective_function_sptr); + reinterpret_cast&>(*this->_objective_function_sptr); objective_function.set_proj_data_sptr(this->_proj_data_sptr); shared_ptr proj_matrix_sptr(new ProjMatrixByBinUsingRayTracing()); shared_ptr proj_pair_sptr(new ProjectorByBinPairUsingProjMatrixByBin(proj_matrix_sptr)); - objective_function.set_projector_pair_sptr(proj_pair_sptr) ; + objective_function.set_projector_pair_sptr(proj_pair_sptr); /*objective_function.set_normalisation_sptr(bin_norm_sptr); objective_function.set_additive_proj_data_sptr(add_proj_data_sptr); */ diff --git a/src/include/stir/recon_buildblock/test/ReconstructionTests.h b/src/include/stir/recon_buildblock/test/ReconstructionTests.h index 5d2dca120a..2fc6f63b16 100644 --- a/src/include/stir/recon_buildblock/test/ReconstructionTests.h +++ b/src/include/stir/recon_buildblock/test/ReconstructionTests.h @@ -37,13 +37,10 @@ START_NAMESPACE_STIR \brief Base class for simple test on reconstruction */ template -class ReconstructionTests : public RunTests -{ +class ReconstructionTests : public RunTests { public: //! Constructor that can take some input data to run the test with - explicit inline - ReconstructionTests(const std::string &proj_data_filename = "", - const std::string & density_filename = ""); + explicit inline ReconstructionTests(const std::string& proj_data_filename = "", const std::string& density_filename = ""); virtual ~ReconstructionTests() {} @@ -71,86 +68,64 @@ class ReconstructionTests : public RunTests std::string _input_density_filename; shared_ptr _proj_data_sptr; shared_ptr _input_density_sptr; - shared_ptr > _recon_sptr; + shared_ptr> _recon_sptr; }; template -ReconstructionTests:: -ReconstructionTests(const std::string &proj_data_filename, - const std::string & density_filename) : - _proj_data_filename(proj_data_filename), - _input_density_filename(density_filename) -{ -} - +ReconstructionTests::ReconstructionTests(const std::string& proj_data_filename, const std::string& density_filename) + : _proj_data_filename(proj_data_filename), _input_density_filename(density_filename) {} template void -ReconstructionTests:: -construct_input_data() -{ +ReconstructionTests::construct_input_data() { Verbosity::set(1); - if (this->_proj_data_filename.empty()) - { - // construct a small scanner and sinogram - shared_ptr scanner_sptr(new Scanner(Scanner::E953)); - scanner_sptr->set_num_rings(5); - shared_ptr proj_data_info_sptr( - ProjDataInfo::ProjDataInfoCTI(scanner_sptr, - /*span=*/3, - /*max_delta=*/4, - /*num_views=*/128, - /*num_tang_poss=*/128)); - shared_ptr exam_info_sptr(new ExamInfo); - exam_info_sptr->imaging_modality = ImagingModality::PT; - _proj_data_sptr.reset(new ProjDataInMemory (exam_info_sptr, proj_data_info_sptr)); - std::cerr << "Will run tests with projection data with the following settings:\n" - << proj_data_info_sptr->parameter_info(); - } - else - { - shared_ptr proj_data_sptr = - ProjData::read_from_file(this->_proj_data_filename); - _proj_data_sptr.reset(new ProjDataInMemory (*proj_data_sptr)); - } - - if (this->_input_density_filename.empty()) - { - CartesianCoordinate3D origin (0,0,0); - const float zoom=.7F; - - shared_ptr > - vox_sptr(new VoxelsOnCartesianGrid(this->_proj_data_sptr->get_exam_info_sptr(), - *this->_proj_data_sptr->get_proj_data_info_sptr(), - zoom,origin)); - - // create very long cylinder, such that we don't have to think about origin - EllipsoidalCylinder cylinder(/*length_z*/1000.F, - /*radius_y*/100.F, - /*radius_x*/90.F, - CartesianCoordinate3D(0.F,0.F,0.F)); - cylinder.construct_volume(*vox_sptr, CartesianCoordinate3D(2,2,2)); - - // filter it a bit to avoid too high frequency stuff creating trouble in the comparison - SeparableGaussianImageFilter filter; - filter.set_fwhms(make_coordinate(10.F,10.F,10.F)); - filter.set_up(*vox_sptr); - filter.apply(*vox_sptr); - this->_input_density_sptr = vox_sptr; - } - else - { - shared_ptr aptr(read_from_file(this->_input_density_filename)); - this->_input_density_sptr = aptr; - } + if (this->_proj_data_filename.empty()) { + // construct a small scanner and sinogram + shared_ptr scanner_sptr(new Scanner(Scanner::E953)); + scanner_sptr->set_num_rings(5); + shared_ptr proj_data_info_sptr(ProjDataInfo::ProjDataInfoCTI(scanner_sptr, + /*span=*/3, + /*max_delta=*/4, + /*num_views=*/128, + /*num_tang_poss=*/128)); + shared_ptr exam_info_sptr(new ExamInfo); + exam_info_sptr->imaging_modality = ImagingModality::PT; + _proj_data_sptr.reset(new ProjDataInMemory(exam_info_sptr, proj_data_info_sptr)); + std::cerr << "Will run tests with projection data with the following settings:\n" << proj_data_info_sptr->parameter_info(); + } else { + shared_ptr proj_data_sptr = ProjData::read_from_file(this->_proj_data_filename); + _proj_data_sptr.reset(new ProjDataInMemory(*proj_data_sptr)); + } + + if (this->_input_density_filename.empty()) { + CartesianCoordinate3D origin(0, 0, 0); + const float zoom = .7F; + + shared_ptr> vox_sptr(new VoxelsOnCartesianGrid( + this->_proj_data_sptr->get_exam_info_sptr(), *this->_proj_data_sptr->get_proj_data_info_sptr(), zoom, origin)); + + // create very long cylinder, such that we don't have to think about origin + EllipsoidalCylinder cylinder(/*length_z*/ 1000.F, + /*radius_y*/ 100.F, + /*radius_x*/ 90.F, CartesianCoordinate3D(0.F, 0.F, 0.F)); + cylinder.construct_volume(*vox_sptr, CartesianCoordinate3D(2, 2, 2)); + + // filter it a bit to avoid too high frequency stuff creating trouble in the comparison + SeparableGaussianImageFilter filter; + filter.set_fwhms(make_coordinate(10.F, 10.F, 10.F)); + filter.set_up(*vox_sptr); + filter.apply(*vox_sptr); + this->_input_density_sptr = vox_sptr; + } else { + shared_ptr aptr(read_from_file(this->_input_density_filename)); + this->_input_density_sptr = aptr; + } // forward project { - shared_ptr PM_sptr(new ProjMatrixByBinUsingRayTracing); - shared_ptr fwd_proj_sptr = - MAKE_SHARED(PM_sptr); - fwd_proj_sptr->set_up(this->_proj_data_sptr->get_proj_data_info_sptr(), - this->_input_density_sptr); + shared_ptr PM_sptr(new ProjMatrixByBinUsingRayTracing); + shared_ptr fwd_proj_sptr = MAKE_SHARED(PM_sptr); + fwd_proj_sptr->set_up(this->_proj_data_sptr->get_proj_data_info_sptr(), this->_input_density_sptr); fwd_proj_sptr->set_input(*this->_input_density_sptr); fwd_proj_sptr->forward_project(*this->_proj_data_sptr); } @@ -158,31 +133,24 @@ construct_input_data() template void -ReconstructionTests:: -reconstruct(shared_ptr target_sptr) -{ +ReconstructionTests::reconstruct(shared_ptr target_sptr) { this->_recon_sptr->set_input_data(this->_proj_data_sptr); this->_recon_sptr->set_disable_output(true); // set a prefix anyway, as some reconstruction algorithms write some files even with disabled output this->_recon_sptr->set_output_filename_prefix("test_recon_" + this->_recon_sptr->method_info()); - if (this->_recon_sptr->set_up(target_sptr)==Succeeded::no) + if (this->_recon_sptr->set_up(target_sptr) == Succeeded::no) error("recon::set_up() failed"); - - if (this->_recon_sptr->reconstruct(target_sptr)==Succeeded::no) + + if (this->_recon_sptr->reconstruct(target_sptr) == Succeeded::no) error("recon::reconstruct() failed"); - std::cerr << "\n================================\nReconstruction " - << this->_recon_sptr->method_info() - << " finished!\n\n"; + std::cerr << "\n================================\nReconstruction " << this->_recon_sptr->method_info() << " finished!\n\n"; } template void -ReconstructionTests:: -compare(const shared_ptr output_sptr) -{ - if (!check(this->_input_density_sptr->has_same_characteristics(*output_sptr), - "output image has wrong characteristics")) +ReconstructionTests::compare(const shared_ptr output_sptr) { + if (!check(this->_input_density_sptr->has_same_characteristics(*output_sptr), "output image has wrong characteristics")) return; shared_ptr diff_sptr(output_sptr->clone()); @@ -191,19 +159,18 @@ compare(const shared_ptr output_sptr) const float diff_max = diff_sptr->find_max(); const float max_input = this->_input_density_sptr->find_max(); in_place_abs(*diff_sptr); - const float mean_abs_error=diff_sptr->sum() / this->_input_density_sptr->size_all(); + const float mean_abs_error = diff_sptr->sum() / this->_input_density_sptr->size_all(); std::cerr << "Reconstruction diff relative range: " - << "[" << diff_min/max_input << ", " << diff_max/max_input << "]\n" - << "mean abs diff normalised was " << mean_abs_error/max_input << "\n"; - if (!check_if_less(-0.3F, diff_min/max_input, "relative diff min") || - !check_if_less(diff_max/max_input, .3F, "relative diff max") || - !check_if_less(mean_abs_error/max_input, .01F, "relative mean abs diff")) - { - const std::string prefix = "test_recon_" + this->_recon_sptr->method_info(); - write_to_file(prefix + "_output.hv", *output_sptr); - write_to_file(prefix + "_original.hv", *this->_input_density_sptr); - write_to_file(prefix + "_diff.hv", *diff_sptr); - } + << "[" << diff_min / max_input << ", " << diff_max / max_input << "]\n" + << "mean abs diff normalised was " << mean_abs_error / max_input << "\n"; + if (!check_if_less(-0.3F, diff_min / max_input, "relative diff min") || + !check_if_less(diff_max / max_input, .3F, "relative diff max") || + !check_if_less(mean_abs_error / max_input, .01F, "relative mean abs diff")) { + const std::string prefix = "test_recon_" + this->_recon_sptr->method_info(); + write_to_file(prefix + "_output.hv", *output_sptr); + write_to_file(prefix + "_original.hv", *this->_input_density_sptr); + write_to_file(prefix + "_diff.hv", *diff_sptr); + } } END_NAMESPACE_STIR diff --git a/src/include/stir/round.h b/src/include/stir/round.h index c3e9c44c53..247d27499b 100644 --- a/src/include/stir/round.h +++ b/src/include/stir/round.h @@ -21,12 +21,12 @@ /*! \file \ingroup buildblock - + \brief Declaration of the stir::round functions - + \author Kris Thielemans \author Charalampos Tsoumpas - + */ #include "stir/BasicCoordinate.h" @@ -35,26 +35,26 @@ START_NAMESPACE_STIR \ingroup buildblock \name Functions for rounding floating point numbers */ - //@{ +//@{ //! Implements rounding of floating point numbers /*! - - round() has the property that + + round() has the property that \code round(x) == -round(-x) \endcode - The usual (int)(x+.5) has machine dependent behaviour for + The usual (int)(x+.5) has machine dependent behaviour for negative numbers. .5 is rounded to 1 (and hence -.5 to -1). - \warning There is no check on overflow (i.e. if \a x is too + \warning There is no check on overflow (i.e. if \a x is too large to fit in an \c int). */ inline int round(const float x); //! Implements rounding of double numbers -/*! +/*! \see round(const float) */ inline int round(const double x); @@ -64,9 +64,7 @@ inline int round(const double x); \see round(const float) */ template -inline BasicCoordinate -round(const BasicCoordinate& x); - +inline BasicCoordinate round(const BasicCoordinate& x); //! Implements rounding of floating point numbers to other integer types /*! @@ -80,16 +78,13 @@ round(const BasicCoordinate& x); \todo add code to check that \c integerT is really an integer type at compilation time */ template -inline void -round_to(integerT& result, const float x); +inline void round_to(integerT& result, const float x); //! Implements rounding of a BasicCoordinate object to other integer types /*! \see round_to(integerT, float) */ template -inline void -round_to(BasicCoordinate& result, - const BasicCoordinate& x); +inline void round_to(BasicCoordinate& result, const BasicCoordinate& x); //@} diff --git a/src/include/stir/round.inl b/src/include/stir/round.inl index 7a7da9f5ed..a7a391aac5 100644 --- a/src/include/stir/round.inl +++ b/src/include/stir/round.inl @@ -3,12 +3,12 @@ /*! \file \ingroup buildblock - + \brief Implementation of the stir::round functions - + \author Kris Thielemans \author Charalampos Tsoumpas - + */ /* Copyright (C) 2000- 2010, Hammersmith Imanet Ltd @@ -31,73 +31,64 @@ START_NAMESPACE_STIR template inline void -round_to(integerT& result, const float x) -{ - if (x>=0) - result = static_cast(x+0.5F); +round_to(integerT& result, const float x) { + if (x >= 0) + result = static_cast(x + 0.5F); else - result = -static_cast(-x+0.5F); + result = -static_cast(-x + 0.5F); } - template inline void -round_to(integerT& result, const double x) -{ - if (x>=0) - result = static_cast(x+0.5); +round_to(integerT& result, const double x) { + if (x >= 0) + result = static_cast(x + 0.5); else - result = -static_cast(-x+0.5); + result = -static_cast(-x + 0.5); } /* next 2 are just to avoid compiler warnings about using - on an unsigned type */ inline void -round_to(unsigned& result, const double x) -{ - result = static_cast(x+0.5); +round_to(unsigned& result, const double x) { + result = static_cast(x + 0.5); } inline void -round_to(unsigned long& result, const double x) -{ - result = static_cast(x+0.5); +round_to(unsigned long& result, const double x) { + result = static_cast(x + 0.5); } /* could be implemented in terms of the above */ -int round(const float x) -{ - if (x>=0) - return static_cast(x+0.5F); +int +round(const float x) { + if (x >= 0) + return static_cast(x + 0.5F); else - return -static_cast(-x+0.5F); + return -static_cast(-x + 0.5F); } -int round(const double x) -{ - if (x>=0) - return static_cast(x+0.5); +int +round(const double x) { + if (x >= 0) + return static_cast(x + 0.5); else - return -static_cast(-x+0.5); + return -static_cast(-x + 0.5); } template -BasicCoordinate -round(const BasicCoordinate& x) -{ - BasicCoordinate rnd_x; - for(int i=1;i<=num_dimensions;++i) - rnd_x[i]=round(x[i]); - return rnd_x; +BasicCoordinate +round(const BasicCoordinate& x) { + BasicCoordinate rnd_x; + for (int i = 1; i <= num_dimensions; ++i) + rnd_x[i] = round(x[i]); + return rnd_x; } template -inline void -round_to(BasicCoordinate& result, - const BasicCoordinate& x) -{ - for(int i=1;i<=num_dimensions;++i) - round_to(result[i], x[i]); +inline void +round_to(BasicCoordinate& result, const BasicCoordinate& x) { + for (int i = 1; i <= num_dimensions; ++i) + round_to(result[i], x[i]); } END_NAMESPACE_STIR - diff --git a/src/include/stir/scale_sinograms.h b/src/include/stir/scale_sinograms.h index 1f8a772fbb..2c1a517151 100644 --- a/src/include/stir/scale_sinograms.h +++ b/src/include/stir/scale_sinograms.h @@ -20,7 +20,7 @@ \file \ingroup projdata \brief declaration of stir::scale_sinograms and stir::get_scale_factors_per_sinogram - + \author Charalampos Tsoumpas \author Kris Thielemans @@ -39,9 +39,8 @@ class Succeeded; corresponds to segments, the second to axial positions. \return indicates if writing failed or not */ -Succeeded scale_sinograms(ProjData& output_proj_data, - const ProjData& input_proj_data, - const Array<2,float> scale_factors_per_sinogram); +Succeeded scale_sinograms(ProjData& output_proj_data, const ProjData& input_proj_data, + const Array<2, float> scale_factors_per_sinogram); //! find scale factors between two different sinograms /*! \ingroup projdata @@ -59,9 +58,7 @@ Succeeded scale_sinograms(ProjData& output_proj_data, Currently this function sets the scale factor or a sinogram to 1 (and calls warning()) when the denominator gets too small. */ -Array<2,float> - get_scale_factors_per_sinogram(const ProjData& numerator_proj_data, - const ProjData& denominator_proj_data, - const ProjData& weights_proj_data); +Array<2, float> get_scale_factors_per_sinogram(const ProjData& numerator_proj_data, const ProjData& denominator_proj_data, + const ProjData& weights_proj_data); END_NAMESPACE_STIR diff --git a/src/include/stir/scatter/CreateTailMaskFromACFs.h b/src/include/stir/scatter/CreateTailMaskFromACFs.h index 323c898d73..5bca595108 100644 --- a/src/include/stir/scatter/CreateTailMaskFromACFs.h +++ b/src/include/stir/scatter/CreateTailMaskFromACFs.h @@ -51,7 +51,6 @@ #include "stir/Succeeded.h" #include "stir/is_null_ptr.h" - #include "stir/ParsingObject.h" #include "stir/ProjDataInMemory.h" @@ -63,65 +62,64 @@ START_NAMESPACE_STIR //! \details This class implements the functionality of the executable. //! It was nessesary in order to be able to perform this calculations from //! the ScatterEstimationByBin. -class CreateTailMaskFromACFs : public ParsingObject -{ +class CreateTailMaskFromACFs : public ParsingObject { public: - CreateTailMaskFromACFs(); + CreateTailMaskFromACFs(); - virtual Succeeded process_data(); + virtual Succeeded process_data(); - void set_input_projdata_sptr(shared_ptr &); + void set_input_projdata_sptr(shared_ptr&); - void set_input_projdata(std::string&); + void set_input_projdata(std::string&); - void set_output_projdata_sptr(shared_ptr&); + void set_output_projdata_sptr(shared_ptr&); - void set_output_projdata(std::string&); + void set_output_projdata(std::string&); - //! - //! \brief get_output_projdata - //! \return - //! \details Use this function to return the output - //! projdata. - shared_ptr get_output_projdata_sptr(); + //! + //! \brief get_output_projdata + //! \return + //! \details Use this function to return the output + //! projdata. + shared_ptr get_output_projdata_sptr(); - //! - //! \brief ACF_threshold - //! \warning ACF-threshold defaults to 1.1 (should be larger than 1) - float ACF_threshold; + //! + //! \brief ACF_threshold + //! \warning ACF-threshold defaults to 1.1 (should be larger than 1) + float ACF_threshold; - //! - //! \brief safety_margin - //! - int safety_margin; + //! + //! \brief safety_margin + //! + int safety_margin; protected: - void initialise_keymap(); - bool post_processing(); - void set_defaults(); + void initialise_keymap(); + bool post_processing(); + void set_defaults(); private: - //! - //! \brief ACF_sptr - //! \details Input projdata - shared_ptr ACF_sptr; - - //! - //! \brief mask_proj_data - //! \details Output projdata - shared_ptr mask_proj_data; - - //! - //! \brief _input_filename - //! \details The input filename can be omitted in the par file - //! but has to be set, later, using the set_input_projdata(). - std::string _input_filename; - - //! - //! \brief _output_filename - //! \details This is the output filename. - //! It can be omited, if an output is not nessesary. - std::string _output_filename; + //! + //! \brief ACF_sptr + //! \details Input projdata + shared_ptr ACF_sptr; + + //! + //! \brief mask_proj_data + //! \details Output projdata + shared_ptr mask_proj_data; + + //! + //! \brief _input_filename + //! \details The input filename can be omitted in the par file + //! but has to be set, later, using the set_input_projdata(). + std::string _input_filename; + + //! + //! \brief _output_filename + //! \details This is the output filename. + //! It can be omited, if an output is not nessesary. + std::string _output_filename; }; END_NAMESPACE_STIR diff --git a/src/include/stir/scatter/ScatterEstimation.h b/src/include/stir/scatter/ScatterEstimation.h index 6a16eeff15..5eed3c3999 100644 --- a/src/include/stir/scatter/ScatterEstimation.h +++ b/src/include/stir/scatter/ScatterEstimation.h @@ -23,7 +23,7 @@ \file \ingroup scatter \brief Definition of class stir::ScatterEstimation. - + \author Nikos Efthimiou \author Kris Thielemans */ @@ -46,17 +46,17 @@ START_NAMESPACE_STIR -template class PostFiltering; +template +class PostFiltering; class BinNormalisation; //! A struct to hold the parameters for image masking. -struct MaskingParameters -{ +struct MaskingParameters { float min_threshold; //! filter parameter file to be used in mask calculation std::string filter_filename; //! filter to apply before thresholding - shared_ptr > > filter_sptr; + shared_ptr>> filter_sptr; }; /*! @@ -85,298 +85,284 @@ struct MaskingParameters (Ensuring backwards compatibility was not so easy, so the code might look confusing.) */ -class ScatterEstimation: public ParsingObject -{ +class ScatterEstimation : public ParsingObject { public: - //! upsample coarse scatter estimate and fit it to tails of the emission data - /*! Current procedure: - 1. interpolate segment 0 of \a scatter_proj_data to size of segment 0 of \a emission_proj_data - 2. inverseSSRB to create oblique segments - 3. undo normalisation (as measured data is not normalised) - 4. find scale factors with get_scale_factors_per_sinogram() - 5. apply thresholds - 6. filter scale-factors in axial direction (independently for every segment) - 7. apply scale factors using scale_sinograms() - */ - static void - upsample_and_fit_scatter_estimate(ProjData& scaled_scatter_proj_data, - const ProjData& emission_proj_data, - const ProjData& scatter_proj_data, - BinNormalisation& scatter_normalisation, - const ProjData& weights_proj_data, - const float min_scale_factor, - const float max_scale_factor, - const unsigned half_filter_width, - BSpline::BSplineType spline_type, - const bool remove_interleaving = true); - - - //! Default constructor (calls set_defaults()) - ScatterEstimation(); - //! Overloaded constructor with parameter file and initialisation - explicit ScatterEstimation(const std::string& parameter_filename); - - //! Full process_data which performs set_up() before beginning - virtual Succeeded process_data(); - - //! Get current scatter estimate - shared_ptr get_output() const; - - //! - //! \brief set_up - //! \return - //! \details This function will take care most of the initialisation needed: - //!
      - //!
    • Procedure: - //!
        - //!
      1. Check if debug mode and activate all export flags. - //!
      2. Load the input_projdata_3d_sptr and perform SSRB - //!
      3. Initialise (partially for the moment) the reconstruction method: - //!
      4. Load Normalisation data and perform SSRB - //!
      5. Load the background data (randoms) and do normalisation (to get the additive data) - //!
      - //!
    - virtual Succeeded set_up(); - - // Set functions - //! Set the input projdata. - inline void set_input_proj_data_sptr(const shared_ptr); - //! Set the input projdata - /*! Using same name as Reconstruction */ + //! upsample coarse scatter estimate and fit it to tails of the emission data + /*! Current procedure: + 1. interpolate segment 0 of \a scatter_proj_data to size of segment 0 of \a emission_proj_data + 2. inverseSSRB to create oblique segments + 3. undo normalisation (as measured data is not normalised) + 4. find scale factors with get_scale_factors_per_sinogram() + 5. apply thresholds + 6. filter scale-factors in axial direction (independently for every segment) + 7. apply scale factors using scale_sinograms() +*/ + static void upsample_and_fit_scatter_estimate(ProjData& scaled_scatter_proj_data, const ProjData& emission_proj_data, + const ProjData& scatter_proj_data, BinNormalisation& scatter_normalisation, + const ProjData& weights_proj_data, const float min_scale_factor, + const float max_scale_factor, const unsigned half_filter_width, + BSpline::BSplineType spline_type, const bool remove_interleaving = true); + + //! Default constructor (calls set_defaults()) + ScatterEstimation(); + //! Overloaded constructor with parameter file and initialisation + explicit ScatterEstimation(const std::string& parameter_filename); + + //! Full process_data which performs set_up() before beginning + virtual Succeeded process_data(); + + //! Get current scatter estimate + shared_ptr get_output() const; + + //! + //! \brief set_up + //! \return + //! \details This function will take care most of the initialisation needed: + //!
      + //!
    • Procedure: + //!
        + //!
      1. Check if debug mode and activate all export flags. + //!
      2. Load the input_projdata_3d_sptr and perform SSRB + //!
      3. Initialise (partially for the moment) the reconstruction method: + //!
      4. Load Normalisation data and perform SSRB + //!
      5. Load the background data (randoms) and do normalisation (to get the additive data) + //!
      + //!
    + virtual Succeeded set_up(); + + // Set functions + //! Set the input projdata. + inline void set_input_proj_data_sptr(const shared_ptr); + //! Set the input projdata + /*! Using same name as Reconstruction */ #if STIR_VERSION < 050000 - void set_input_data(const shared_ptr& data); + void set_input_data(const shared_ptr& data); #else - void set_input_data(const shared_ptr& data); + void set_input_data(const shared_ptr& data); #endif - shared_ptr get_input_data() const; - - //! Set the reconstruction method for the scatter estimation - inline void set_reconstruction_method_sptr(const shared_ptr > >); - //! Set the full resolution attenuation image. - inline void set_attenuation_image_sptr(const shared_ptr > ); - //! set projection data that contains the attenuation correction factors - void set_attenuation_correction_proj_data_sptr(const shared_ptr); - //! set normalisation object (excluding attenuation) - void set_normalisation_sptr(const shared_ptr); - //! - inline void set_background_proj_data_sptr(const shared_ptr); - //! - inline void set_initial_activity_image_sptr(const shared_ptr >); - - inline void set_mask_image_sptr(const shared_ptr >); - //! set mask for tail-fitting - /*! \c arg will not be modified */ - inline void set_mask_proj_data_sptr(const shared_ptr arg); - - inline void set_scatter_simulation_method_sptr(const shared_ptr); - - inline void set_num_iterations(int); - - void set_output_scatter_estimate_prefix(const std::string&); - void set_export_scatter_estimates_of_each_iteration(bool); - - //! Set the zoom factor in the XY plane for the downsampling of the activity and attenuation image. - inline void set_zoom_xy(float); - //! Set the zoom factor in the Z axis for the downsampling of the activity and attenuation image. - inline void set_zoom_z(float); - - - // Get functions - //! Get the number of iterations for the scatter estimation - /*! \deprecated Use get_num_iterations() */ - int get_iterations_num() const; - - //! Get the number of iterations for the scatter estimation - int get_num_iterations() const; - - //! Get the (low resolution) estimate of the activity image - shared_ptr > get_estimated_activity_image_sptr() const; - - //! allows checking if we have called set_up() - virtual bool already_setup() const; - - protected: - //! All recomputes_** will default true - virtual void set_defaults(); - virtual void initialise_keymap(); - virtual bool post_processing(); - - //! Recompute or load the mask image. - bool recompute_mask_image; - //! If set the mask projdata will be recomputed - bool recompute_mask_projdata; - //! If set to 1 the attenuation coefficients are going to - //! be recalculated. - bool recompute_atten_projdata; - - //! This is the reconstruction object which is going to be used for the scatter estimation - //! and the calculation of the initial activity image (if recompute set). It can be defined in the same - //! parameters file as the scatter parameters or in an external defined in the - //! reconstruction_template_par_filename - shared_ptr < Reconstruction < DiscretisedDensity < 3, float > > > - reconstruction_template_sptr; - //! The current activity estimate. - shared_ptr > current_activity_image_sptr; - //! Image with attenuation values. - shared_ptr > atten_image_sptr; - //! normalisation components in 3D (without atten) - shared_ptr norm_3d_sptr; - //! Mask proj_data - shared_ptr mask_projdata_sptr; - //! The full 3D projdata are used for the calculation of the 2D - //! and later for the upsampling back to 3D. - shared_ptr input_projdata_sptr; - //! The 2D projdata are used for the scatter estimation. - shared_ptr input_projdata_2d_sptr; - //! Additive projection data after SSRB -- Randoms - shared_ptr add_projdata_2d_sptr; - //! Prompts - randoms - shared_ptr data_to_fit_projdata_sptr; - - shared_ptr add_projdata_sptr; - //! (Additive + Scatter Estimate) * Mult in 2D - shared_ptr back_projdata_2d_sptr; - //! Initially this points to the un-normalised randoms. - shared_ptr back_projdata_sptr; - //! Filename of mask image - std::string mask_image_filename; - //! Filename of mask's projdata - std::string mask_projdata_filename; - //! Filename of background projdata - std::string back_projdata_filename; - //! Optional parameter file for the tail fitting. - /*! If not provided, sensible defaults are used */ - std::string tail_mask_par_filename; - //! Filename of the measured emission 3D data. - std::string input_projdata_filename; - //! This is the image file name with the anatomic information. - std::string atten_image_filename; - //! The filename for the parameters file of the reconstruction method. - std::string recon_template_par_filename; - //! The file name for the attenuation coefficients. - //! If they are to be recalculated they will be stored here, if set. - std::string atten_coeff_filename; - - //! \details the set of parameters to obtain a mask from the attenuation image - /*! The attenuation image will be thresholded to find a plausible mask for where there - can be emission data. This mask will be then forward projected to find the tails in the projection data. - - This is a simple strategy that can fail due to motion etc, so the attenuation image is first blurred, - and the default threshold is low. - - Note that there is currently no attempt to eliminate the bed from the attenuation image first. - Tails are therefore going to be too small, which could create trouble. - - By default, a Gaussian filter of FWHM (15,20,20) will be applied before thresholding with a value 0.003 cm^-1 - */ - MaskingParameters masking_parameters; - //! \details The number of iterations the scatter estimation will perform. - //! Default = 5. - int num_scatter_iterations; - //! Output file name prefix - std::string output_scatter_estimate_prefix; - - std::string output_additive_estimate_prefix; + shared_ptr get_input_data() const; + + //! Set the reconstruction method for the scatter estimation + inline void set_reconstruction_method_sptr(const shared_ptr>>); + //! Set the full resolution attenuation image. + inline void set_attenuation_image_sptr(const shared_ptr>); + //! set projection data that contains the attenuation correction factors + void set_attenuation_correction_proj_data_sptr(const shared_ptr); + //! set normalisation object (excluding attenuation) + void set_normalisation_sptr(const shared_ptr); + //! + inline void set_background_proj_data_sptr(const shared_ptr); + //! + inline void set_initial_activity_image_sptr(const shared_ptr>); + + inline void set_mask_image_sptr(const shared_ptr>); + //! set mask for tail-fitting + /*! \c arg will not be modified */ + inline void set_mask_proj_data_sptr(const shared_ptr arg); + + inline void set_scatter_simulation_method_sptr(const shared_ptr); + + inline void set_num_iterations(int); + + void set_output_scatter_estimate_prefix(const std::string&); + void set_export_scatter_estimates_of_each_iteration(bool); + + //! Set the zoom factor in the XY plane for the downsampling of the activity and attenuation image. + inline void set_zoom_xy(float); + //! Set the zoom factor in the Z axis for the downsampling of the activity and attenuation image. + inline void set_zoom_z(float); + + // Get functions + //! Get the number of iterations for the scatter estimation + /*! \deprecated Use get_num_iterations() */ + int get_iterations_num() const; + + //! Get the number of iterations for the scatter estimation + int get_num_iterations() const; + + //! Get the (low resolution) estimate of the activity image + shared_ptr> get_estimated_activity_image_sptr() const; + + //! allows checking if we have called set_up() + virtual bool already_setup() const; + +protected: + //! All recomputes_** will default true + virtual void set_defaults(); + virtual void initialise_keymap(); + virtual bool post_processing(); + + //! Recompute or load the mask image. + bool recompute_mask_image; + //! If set the mask projdata will be recomputed + bool recompute_mask_projdata; + //! If set to 1 the attenuation coefficients are going to + //! be recalculated. + bool recompute_atten_projdata; + + //! This is the reconstruction object which is going to be used for the scatter estimation + //! and the calculation of the initial activity image (if recompute set). It can be defined in the same + //! parameters file as the scatter parameters or in an external defined in the + //! reconstruction_template_par_filename + shared_ptr>> reconstruction_template_sptr; + //! The current activity estimate. + shared_ptr> current_activity_image_sptr; + //! Image with attenuation values. + shared_ptr> atten_image_sptr; + //! normalisation components in 3D (without atten) + shared_ptr norm_3d_sptr; + //! Mask proj_data + shared_ptr mask_projdata_sptr; + //! The full 3D projdata are used for the calculation of the 2D + //! and later for the upsampling back to 3D. + shared_ptr input_projdata_sptr; + //! The 2D projdata are used for the scatter estimation. + shared_ptr input_projdata_2d_sptr; + //! Additive projection data after SSRB -- Randoms + shared_ptr add_projdata_2d_sptr; + //! Prompts - randoms + shared_ptr data_to_fit_projdata_sptr; + + shared_ptr add_projdata_sptr; + //! (Additive + Scatter Estimate) * Mult in 2D + shared_ptr back_projdata_2d_sptr; + //! Initially this points to the un-normalised randoms. + shared_ptr back_projdata_sptr; + //! Filename of mask image + std::string mask_image_filename; + //! Filename of mask's projdata + std::string mask_projdata_filename; + //! Filename of background projdata + std::string back_projdata_filename; + //! Optional parameter file for the tail fitting. + /*! If not provided, sensible defaults are used */ + std::string tail_mask_par_filename; + //! Filename of the measured emission 3D data. + std::string input_projdata_filename; + //! This is the image file name with the anatomic information. + std::string atten_image_filename; + //! The filename for the parameters file of the reconstruction method. + std::string recon_template_par_filename; + //! The file name for the attenuation coefficients. + //! If they are to be recalculated they will be stored here, if set. + std::string atten_coeff_filename; + + //! \details the set of parameters to obtain a mask from the attenuation image + /*! The attenuation image will be thresholded to find a plausible mask for where there + can be emission data. This mask will be then forward projected to find the tails in the projection data. + + This is a simple strategy that can fail due to motion etc, so the attenuation image is first blurred, + and the default threshold is low. + + Note that there is currently no attempt to eliminate the bed from the attenuation image first. + Tails are therefore going to be too small, which could create trouble. + + By default, a Gaussian filter of FWHM (15,20,20) will be applied before thresholding with a value 0.003 cm^-1 + */ + MaskingParameters masking_parameters; + //! \details The number of iterations the scatter estimation will perform. + //! Default = 5. + int num_scatter_iterations; + //! Output file name prefix + std::string output_scatter_estimate_prefix; + + std::string output_additive_estimate_prefix; private: - //! variable to check if we have called set_up() - bool _already_setup; - - //! attenuation in 3D - shared_ptr atten_norm_3d_sptr; - - //! ((1/SSRB(1/norm3D)) * SSRB(atten)). - /*! Created such that the first term is the norm and second the atten */ - shared_ptr multiplicative_binnorm_2d_sptr; - - //! (norm * atten) in 3D. - /*! Created such that the first term is the norm and second the atten */ - shared_ptr multiplicative_binnorm_sptr; - - //! variable for storing current scatter estimate - shared_ptr scatter_estimate_sptr; - - //! variable storing the mask image - shared_ptr < const DiscretisedDensity < 3, float > > mask_image_sptr; - - //! \brief set_up iterative reconstruction - Succeeded set_up_iterative(shared_ptr > > arg); - - //! \brief set_up analytic reconstruction - Succeeded set_up_analytic(); - - //! \details A helper function to reduce the size of set_up(). - Succeeded project_mask_image(); - - //! reconstruct image with current scatter estimate (iteratively) - /*! \a scat_iter is used for determining the filename for saving */ - void reconstruct_iterative(int scat_iter, shared_ptr >& output_sptr); - - //! reconstruct image with current scatter estimate (analytic reconstruction) - /*! \a scat_iter is used for determining the filename for saving */ - void reconstruct_analytic(int scat_iter, shared_ptr > & output_sptr); - - //! \details Find a mask by thresholding etc - static void apply_mask_in_place(DiscretisedDensity<3, float> &, - const MaskingParameters&); - - void add_proj_data(ProjData&, const ProjData&); - - void subtract_proj_data(ProjData&, const ProjData&); - - void apply_to_proj_data(ProjData& , const pow_times_add&); - - //! Create combined norm from norm and atten - void create_multiplicative_binnorm_sptr(); - - //! extract the normalisation component of a combined norm - shared_ptr - get_normalisation_object_sptr(const shared_ptr& combined_norm_sptr) const; - - //! extract the attenuation factors from a combined norm - shared_ptr - get_attenuation_correction_factors_sptr(const shared_ptr& combined_norm_sptr) const; - - //! Returns a shared pointer to a new ProjData. If we run in run_debug_mode and - //! the extras_path has been set, then it will be a ProjDataInterfile, otherwise it will be a ProjDataInMemory. - shared_ptr create_new_proj_data(const std::string& filename, - const shared_ptr exam_info_sptr, - const shared_ptr proj_data_info_sptr) const; - - //! \details Average the two first activity images 0 and 1 (defaults to \c true) - bool do_average_at_2; - //! for upsampling (defaults to \c true) - bool remove_interleaving; - //! Save all scatter simulated sinograms - bool export_scatter_estimates_of_each_iteration; - //! Run the process in 2D by SSRB the 3D sinograms - bool run_in_2d_projdata; - //! This bool will allow the ScatterEstimation to override the value of - //! the density image set in ScatterSimulation par file (defaults to \c true) - bool override_density_image; - //! This will over-ride the scanner template in scatter sinogram simulation (defaults to \c true) - bool override_scanner_template; - //! In debug mode a lot of extra files are going to be saved in the disk. - bool run_debug_mode; - //! Parameter file for scatter simulation - //! \warning Values in this file could be overridden. - std::string scatter_sim_par_filename; - //! \details Class which will implement the scatter simulation. - shared_ptr < ScatterSimulation > scatter_simulation_sptr; - //! This path is used in the debug mode to store all the intermediate files, as they are many. - FilePath extras_path; - - //! Default value = 100 - float max_scale_value; - //! Default value = 0.4 - float min_scale_value; - - bool downsample_scanner_bool; - //! - unsigned int half_filter_width; - - //! \details internal variable set to \c true when using iterative reconstruction - bool iterative_method; + //! variable to check if we have called set_up() + bool _already_setup; + + //! attenuation in 3D + shared_ptr atten_norm_3d_sptr; + + //! ((1/SSRB(1/norm3D)) * SSRB(atten)). + /*! Created such that the first term is the norm and second the atten */ + shared_ptr multiplicative_binnorm_2d_sptr; + + //! (norm * atten) in 3D. + /*! Created such that the first term is the norm and second the atten */ + shared_ptr multiplicative_binnorm_sptr; + + //! variable for storing current scatter estimate + shared_ptr scatter_estimate_sptr; + + //! variable storing the mask image + shared_ptr> mask_image_sptr; + + //! \brief set_up iterative reconstruction + Succeeded set_up_iterative(shared_ptr>> arg); + + //! \brief set_up analytic reconstruction + Succeeded set_up_analytic(); + + //! \details A helper function to reduce the size of set_up(). + Succeeded project_mask_image(); + + //! reconstruct image with current scatter estimate (iteratively) + /*! \a scat_iter is used for determining the filename for saving */ + void reconstruct_iterative(int scat_iter, shared_ptr>& output_sptr); + + //! reconstruct image with current scatter estimate (analytic reconstruction) + /*! \a scat_iter is used for determining the filename for saving */ + void reconstruct_analytic(int scat_iter, shared_ptr>& output_sptr); + + //! \details Find a mask by thresholding etc + static void apply_mask_in_place(DiscretisedDensity<3, float>&, const MaskingParameters&); + + void add_proj_data(ProjData&, const ProjData&); + + void subtract_proj_data(ProjData&, const ProjData&); + + void apply_to_proj_data(ProjData&, const pow_times_add&); + + //! Create combined norm from norm and atten + void create_multiplicative_binnorm_sptr(); + + //! extract the normalisation component of a combined norm + shared_ptr get_normalisation_object_sptr(const shared_ptr& combined_norm_sptr) const; + + //! extract the attenuation factors from a combined norm + shared_ptr get_attenuation_correction_factors_sptr(const shared_ptr& combined_norm_sptr) const; + + //! Returns a shared pointer to a new ProjData. If we run in run_debug_mode and + //! the extras_path has been set, then it will be a ProjDataInterfile, otherwise it will be a ProjDataInMemory. + shared_ptr create_new_proj_data(const std::string& filename, const shared_ptr exam_info_sptr, + const shared_ptr proj_data_info_sptr) const; + + //! \details Average the two first activity images 0 and 1 (defaults to \c true) + bool do_average_at_2; + //! for upsampling (defaults to \c true) + bool remove_interleaving; + //! Save all scatter simulated sinograms + bool export_scatter_estimates_of_each_iteration; + //! Run the process in 2D by SSRB the 3D sinograms + bool run_in_2d_projdata; + //! This bool will allow the ScatterEstimation to override the value of + //! the density image set in ScatterSimulation par file (defaults to \c true) + bool override_density_image; + //! This will over-ride the scanner template in scatter sinogram simulation (defaults to \c true) + bool override_scanner_template; + //! In debug mode a lot of extra files are going to be saved in the disk. + bool run_debug_mode; + //! Parameter file for scatter simulation + //! \warning Values in this file could be overridden. + std::string scatter_sim_par_filename; + //! \details Class which will implement the scatter simulation. + shared_ptr scatter_simulation_sptr; + //! This path is used in the debug mode to store all the intermediate files, as they are many. + FilePath extras_path; + + //! Default value = 100 + float max_scale_value; + //! Default value = 0.4 + float min_scale_value; + + bool downsample_scanner_bool; + //! + unsigned int half_filter_width; + + //! \details internal variable set to \c true when using iterative reconstruction + bool iterative_method; }; END_NAMESPACE_STIR diff --git a/src/include/stir/scatter/ScatterEstimation.inl b/src/include/stir/scatter/ScatterEstimation.inl index ef1260bcf5..e9c788b395 100644 --- a/src/include/stir/scatter/ScatterEstimation.inl +++ b/src/include/stir/scatter/ScatterEstimation.inl @@ -3,73 +3,55 @@ START_NAMESPACE_STIR void -ScatterEstimation:: -set_input_proj_data_sptr(const shared_ptr arg) -{ +ScatterEstimation::set_input_proj_data_sptr(const shared_ptr arg) { this->_already_setup = false; - this->input_projdata_sptr = arg; + this->input_projdata_sptr = arg; } void -ScatterEstimation:: -set_reconstruction_method_sptr(const shared_ptr > > arg) -{ +ScatterEstimation::set_reconstruction_method_sptr(const shared_ptr>> arg) { this->_already_setup = false; - this->reconstruction_template_sptr = arg; + this->reconstruction_template_sptr = arg; } void -ScatterEstimation:: -set_attenuation_image_sptr(const shared_ptr > arg) -{ +ScatterEstimation::set_attenuation_image_sptr(const shared_ptr> arg) { this->_already_setup = false; - this->atten_image_sptr = arg; + this->atten_image_sptr = arg; } void -ScatterEstimation:: -set_background_proj_data_sptr(const shared_ptr arg) -{ +ScatterEstimation::set_background_proj_data_sptr(const shared_ptr arg) { this->_already_setup = false; - this->back_projdata_sptr = arg; + this->back_projdata_sptr = arg; } void -ScatterEstimation:: -set_initial_activity_image_sptr(const shared_ptr > arg) -{ +ScatterEstimation::set_initial_activity_image_sptr(const shared_ptr> arg) { this->_already_setup = false; - this->current_activity_image_sptr.reset(arg->clone()); + this->current_activity_image_sptr.reset(arg->clone()); } void -ScatterEstimation:: -set_mask_image_sptr(const shared_ptr > arg) -{ +ScatterEstimation::set_mask_image_sptr(const shared_ptr> arg) { this->_already_setup = false; - this->mask_image_sptr = arg; + this->mask_image_sptr = arg; } void -ScatterEstimation:: -set_mask_proj_data_sptr(const shared_ptr arg) -{ +ScatterEstimation::set_mask_proj_data_sptr(const shared_ptr arg) { this->_already_setup = false; - this->mask_projdata_sptr = arg; + this->mask_projdata_sptr = arg; } void -ScatterEstimation:: -set_scatter_simulation_method_sptr(const shared_ptr arg) -{ +ScatterEstimation::set_scatter_simulation_method_sptr(const shared_ptr arg) { this->_already_setup = false; - this->scatter_simulation_sptr = arg; + this->scatter_simulation_sptr = arg; } void -ScatterEstimation:: -set_num_iterations(int arg) -{ +ScatterEstimation::set_num_iterations(int arg) { this->num_scatter_iterations = arg; } diff --git a/src/include/stir/scatter/ScatterSimulation.h b/src/include/stir/scatter/ScatterSimulation.h index 9b0d215e31..8ad2a1c612 100644 --- a/src/include/stir/scatter/ScatterSimulation.h +++ b/src/include/stir/scatter/ScatterSimulation.h @@ -87,349 +87,297 @@ START_NAMESPACE_STIR
  4. \c timing_pos_num : indexes different positions in the LOR, based on + the photon detection time difference. The number of axial positions is allowed to depend on segment_num. @@ -276,6 +278,14 @@ class ProjData : public ExamData inline int get_num_views() const; //! Get number of tangential positions inline int get_num_tangential_poss() const; + //! Get number of TOF positions + inline int get_num_timing_poss() const; + //! Get the index of the first timing position + inline int get_min_timing_pos_num() const; + //! Get the index of the last timgin position. + inline int get_max_timing_pos_num() const; + //! Get TOG mash factor + inline int get_tof_mash_factor() const; //! Get minimum segment number inline int get_min_segment_num() const; //! Get maximum segment number diff --git a/src/include/stir/ProjData.inl b/src/include/stir/ProjData.inl index 4007518aa0..c48e200f8a 100644 --- a/src/include/stir/ProjData.inl +++ b/src/include/stir/ProjData.inl @@ -3,6 +3,7 @@ \ingroup projdata \brief Implementations for inline functions of class stir::ProjData + \author Nikos Efthimiou \author Sanida Mustafovic \author Kris Thielemans \author PARAPET project @@ -11,6 +12,7 @@ Copyright (C) 2000 PARAPET partners Copyright (C) 2000-2009, Hammersmith Imanet Ltd Copyright (C) 2013, 2015 University College London + Copyright (C) 2016, University of Hull This file is part of STIR. This file is free software; you can redistribute it and/or modify @@ -66,6 +68,12 @@ int ProjData::get_num_views() const int ProjData::get_num_tangential_poss() const { return proj_data_info_ptr->get_num_tangential_poss(); } +int ProjData::get_num_timing_poss() const +{ return proj_data_info_ptr->get_num_timing_poss(); } + +int ProjData::get_tof_mash_factor() const +{ return proj_data_info_ptr->get_tof_mash_factor(); } + int ProjData::get_min_segment_num() const { return proj_data_info_ptr->get_min_segment_num(); } @@ -90,6 +98,12 @@ int ProjData::get_min_tangential_pos_num() const int ProjData::get_max_tangential_pos_num() const { return proj_data_info_ptr->get_max_tangential_pos_num(); } +int ProjData::get_min_timing_pos_num() const +{ return proj_data_info_ptr->get_min_timing_pos_num(); } + +int ProjData::get_max_timing_pos_num() const +{ return proj_data_info_ptr->get_max_timing_pos_num(); } + int ProjData::get_num_sinograms() const { int num_sinos = proj_data_info_ptr->get_num_axial_poss(0); diff --git a/src/include/stir/ProjDataInfo.h b/src/include/stir/ProjDataInfo.h index d1a453f641..0d6af07307 100644 --- a/src/include/stir/ProjDataInfo.h +++ b/src/include/stir/ProjDataInfo.h @@ -4,6 +4,7 @@ Copyright (C) 2000 PARAPET partners Copyright (C) 2000 - 2011-10-14, Hammersmith Imanet Ltd Copyright (C) 2011-07-01 - 2011, Kris Thielemans + Copyright (C) 2016, University of Hull This file is part of STIR. This file is free software; you can redistribute it and/or modify @@ -23,6 +24,7 @@ \brief Declaration of class stir::ProjDataInfo + \author Nikos Efthimiou \author Sanida Mustafovic \author Kris Thielemans \author PARAPET project @@ -103,6 +105,13 @@ class ProjDataInfo const int num_views, const int num_tangential_poss); + //! Overloaded Contructor with TOF initialisation + ProjDataInfo(const shared_ptr& scanner_ptr, + const VectorWithOffset& num_axial_pos_per_segment, + const int num_views, + const int num_tangential_poss, + const int tof_mash_factor); + //! Standard trick for a 'virtual copy-constructor' virtual ProjDataInfo* clone() const = 0; @@ -158,6 +167,8 @@ class ProjDataInfo //! Set maximum tangential position number /*! This function is virtual in case a derived class needs to know the number changed. */ virtual void set_max_tangential_pos_num(const int max_tang_poss); + //! The the tof mashing factor. Min and Max timing position will be recalculated. + virtual void set_tof_mash_factor(const int new_num); //@} //! \name Functions that return info on the data size @@ -186,6 +197,20 @@ class ProjDataInfo inline int get_min_tangential_pos_num() const; //! Get maximum tangential position number inline int get_max_tangential_pos_num() const; + //! Get number of TOF positions + inline int get_num_timing_poss() const; + //! Get TOG mash factor + inline int get_tof_mash_factor() const; + //! Get the index of the first timing position + inline int get_min_timing_pos_num() const; + //! Get the index of the last timgin position. + inline int get_max_timing_pos_num() const; + //! Get the coincide window in pico seconds + //! \warning Proposed convension: If the scanner is not TOF ready then + //! the coincidence windowis in the timing bin size. + inline float get_coincidence_window_in_pico_sec() const; + //! Get the total width of the coincide window in mm + inline float get_coincidence_window_width() const; //@} //| \name Functions that return geometrical info for a Bin @@ -227,6 +252,10 @@ class ProjDataInfo normal to the projection plane */ virtual float get_s(const Bin&) const =0; + //! Get value ot the timing location along the LOR (in mm) + //! k is a line segment connecting the centers of the two detectors. + float get_k(const Bin&) const; + //! Get LOR corresponding to a given bin /*! \see get_bin() @@ -266,6 +295,9 @@ class ProjDataInfo \endcode */ virtual float get_sampling_in_s(const Bin&) const; + + //! Get sampling distance in the k \c coordinate + float get_sampling_in_k(const Bin&) const; //@} @@ -326,6 +358,12 @@ class ProjDataInfo //! Return a string describing the object virtual std::string parameter_info() const; + + //! Struct which holds two floating numbers + struct Float1Float2 { float low_lim; float high_lim; }; + + //! Vector which holds the lower and higher boundary for each timing position, for faster access. + mutable VectorWithOffset timing_bin_boundaries; protected: virtual bool blindly_equals(const root_type * const) const = 0; @@ -336,6 +374,14 @@ class ProjDataInfo int max_view_num; int min_tangential_pos_num; int max_tangential_pos_num; + //! Minimum timing pos + int min_timing_pos_num; + //! Maximum timing pos + int max_timing_pos_num; + //! TOF mash factor. + int tof_mash_factor; + //! Finally (with any mashing factor) timing bin increament. + float timing_increament_in_mm; VectorWithOffset min_axial_pos_per_seg; VectorWithOffset max_axial_pos_per_seg; From f14178f9af185a778700a1e3da48a1e820e7ebbb Mon Sep 17 00:00:00 2001 From: NikEfth Date: Thu, 22 Dec 2016 10:53:27 +0000 Subject: [PATCH 032/170] More code Clean and better documented --- .../ProjDataInfoCylindricalNoArcCorr.cxx | 2 +- src/buildblock/Scanner.cxx | 1 + src/include/stir/ProjDataInfo.inl | 39 +++++- src/include/stir/ProjDataInfoCylindrical.h | 19 +++ .../stir/ProjDataInfoCylindricalNoArcCorr.h | 7 ++ src/include/stir/Scanner.h | 89 ++++++++++++-- src/include/stir/Scanner.inl | 37 ++++++ src/include/stir/geometry/line_distances.h | 30 +++++ src/include/stir/listmode/CListModeDataROOT.h | 7 ++ src/include/stir/listmode/CListRecord.h | 21 ++++ src/include/stir/listmode/CListRecordROOT.h | 53 ++++++--- .../GeneralisedObjectiveFunction.h | 7 ++ .../stir/recon_buildblock/ProjMatrixByBin.h | 42 ++++++- .../stir/recon_buildblock/ProjMatrixByBin.inl | 111 ++++++++++++++++++ src/listmode_buildblock/CListModeDataROOT.cxx | 19 ++- ...wardProjectorByBinUsingProjMatrixByBin.cxx | 2 +- .../GeneralisedObjectiveFunction.cxx | 19 ++- src/recon_buildblock/ProjMatrixByBin.cxx | 19 ++- 18 files changed, 491 insertions(+), 33 deletions(-) diff --git a/src/buildblock/ProjDataInfoCylindricalNoArcCorr.cxx b/src/buildblock/ProjDataInfoCylindricalNoArcCorr.cxx index 35754c5242..3a0ee2c858 100644 --- a/src/buildblock/ProjDataInfoCylindricalNoArcCorr.cxx +++ b/src/buildblock/ProjDataInfoCylindricalNoArcCorr.cxx @@ -498,7 +498,7 @@ find_cartesian_coordinates_given_scanner_coordinates (CartesianCoordinate3Dis_tof_ready()? (scanner_ptr->get_num_max_of_timing_bins() * + scanner_ptr->get_size_of_timing_bin()) + :(scanner_ptr->get_size_of_timing_bin()); +} + +float +ProjDataInfo::get_coincidence_window_width() const +{ + // Speed of light 0.299792458 mm / psec. + return get_coincidence_window_in_pico_sec() * 0.299792458f; +} + float ProjDataInfo::get_costheta(const Bin& bin) const { diff --git a/src/include/stir/ProjDataInfoCylindrical.h b/src/include/stir/ProjDataInfoCylindrical.h index 225064dbe1..cdbae24267 100644 --- a/src/include/stir/ProjDataInfoCylindrical.h +++ b/src/include/stir/ProjDataInfoCylindrical.h @@ -3,6 +3,7 @@ Copyright (C) 2000-2009, Hammersmith Imanet Ltd Copyright (C) 2013, University College London Copyright (C) 2013, Institute for Bioengineering of Catalonia + Copyright (C) 2016, University of Hull This file is part of STIR. This file is free software; you can redistribute it and/or modify @@ -23,6 +24,7 @@ \ingroup projdata \brief Declaration of class stir::ProjDataInfoCylindrical + \author Nikos Efthimiou \author Sanida Mustafovic \author Kris Thielemans \author Berta Marti Fuster @@ -33,6 +35,7 @@ #include "stir/ProjDataInfo.h" +#include "stir/CartesianCoordinate3D.h" #include #include @@ -96,6 +99,20 @@ class ProjDataInfoCylindrical: public ProjDataInfo get_LOR(LORInAxialAndNoArcCorrSinogramCoordinates& lor, const Bin& bin) const; + //! This function returns the two points connecting the two detectors of the LOR. + //! \warning there is not a specific guarantee that these are going to be the two + //! central points. This might be in the future a source of error. + void + get_LOR_as_two_points(CartesianCoordinate3D& coord_1, + CartesianCoordinate3D& coord_2, + const Bin& bin) const; + + //! This function is the same as get_LOR_as_two_points() but should faster. + //! \warning More testing needed. + void + get_LOR_as_two_points_alt(CartesianCoordinate3D& coord_1, + CartesianCoordinate3D& coord_2, + const Bin& bin) const; void set_azimuthal_angle_sampling(const float angle); @@ -109,6 +126,8 @@ class ProjDataInfoCylindrical: public ProjDataInfo virtual void set_num_views(const int new_num_views); + virtual void set_tof_mash_factor(const int new_num); + //! Get the azimuthal sampling (in radians) inline float get_azimuthal_angle_sampling() const; virtual inline float get_sampling_in_t(const Bin&) const; diff --git a/src/include/stir/ProjDataInfoCylindricalNoArcCorr.h b/src/include/stir/ProjDataInfoCylindricalNoArcCorr.h index 1a03be4af2..ce11e41793 100644 --- a/src/include/stir/ProjDataInfoCylindricalNoArcCorr.h +++ b/src/include/stir/ProjDataInfoCylindricalNoArcCorr.h @@ -3,6 +3,7 @@ /* Copyright (C) 2000- 2011-06-24, Hammersmith Imanet Ltd Copyright (C) 2011-07-01 - 2011, Kris Thielemans + Copyright (C) 2016, University of Hull This file is part of STIR. This file is free software; you can redistribute it and/or modify @@ -23,6 +24,7 @@ \brief Declaration of class stir::ProjDataInfoCylindricalNoArcCorr + \author Nikos Efthimiou \author Kris Thielemans */ @@ -270,6 +272,11 @@ class ProjDataInfoCylindricalNoArcCorr : public ProjDataInfoCylindrical Succeeded find_scanner_coordinates_given_cartesian_coordinates(int& det1, int& det2, int& ring1, int& ring2, const CartesianCoordinate3D& c1, const CartesianCoordinate3D& c2) const; + + void find_cartesian_coordinates_given_scanner_coordinates_of_the_front_surface(CartesianCoordinate3D& coord_1, + CartesianCoordinate3D& coord_2, + const int Ring_A,const int Ring_B, + const int det1, const int det2) const; void find_cartesian_coordinates_of_detection(CartesianCoordinate3D& coord_1, CartesianCoordinate3D& coord_2, diff --git a/src/include/stir/Scanner.h b/src/include/stir/Scanner.h index 6256f509f2..3cf517932e 100644 --- a/src/include/stir/Scanner.h +++ b/src/include/stir/Scanner.h @@ -3,6 +3,7 @@ Copyright (C) 2000-2010, Hammersmith Imanet Ltd Copyright (C) 2011-2013, King's College London Copyright (C) 2016, UCL + Copyright (C) 2016, University of Hull This file is part of STIR. @@ -25,6 +26,7 @@ \brief Declaration of class stir::Scanner + \author Nikos Efthimiou \author Claire Labbe \author Kris Thielemans \author Sanida Mustafovic @@ -78,6 +80,8 @@ class Succeeded; for which we can get singles rates. \warning This information is only sensible for discrete detector-based scanners. + \warning Currently, in a TOF compatible scanner template, the last three types have to + be explicitly defined to avoid ambiguity. \todo Some scanners do not have all info filled in at present. Values are then set to 0. @@ -109,6 +113,7 @@ class Scanner any given parameters. */ enum Type {E931, E951, E953, E921, E925, E961, E962, E966, E1080, Siemens_mMR, RPT,HiDAC, + /*Type_mCT, Type_mCT_TOF, Type_mCT_TOF_100, Type_mCT_TOF_200, Type_mCT_TOF_400,*/ Advance, DiscoveryLS, DiscoveryST, DiscoverySTE, DiscoveryRX, Discovery600, HZLR, RATPET, PANDA, HYPERimage, nanoPET, HRRT, Allegro, GeminiTF, User_defined_scanner, Unknown_scanner}; @@ -156,6 +161,43 @@ class Scanner float reference_energy_v = -1.0f); + //! TOF constructor -(list of names) + Scanner(Type type_v, const std::list& list_of_names_v, + int num_detectors_per_ring_v, int num_rings_v, + int max_num_non_arccorrected_bins_v, + int default_num_arccorrected_bins_v, + float inner_ring_radius_v, float average_depth_of_interaction_v, + float ring_spacing_v, float bin_size_v, float intrinsic_tilt_v, + int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, + int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, + int num_axial_crystals_per_singles_unit_v, + int num_transaxial_crystals_per_singles_unit_v, + int num_detector_layers_v, + int max_num_of_timing_bins, + float size_timing_bin, + float timing_resolution, + float energy_resolution_v = -1.0f, + float reference_energy_v = -1.0f); + + //! TOF constructor ( a single name) + Scanner(Type type_v, const std::string& name, + int num_detectors_per_ring_v, int num_rings_v, + int max_num_non_arccorrected_bins_v, + int default_num_arccorrected_bins_v, + float inner_ring_radius_v, float average_depth_of_interaction_v, + float ring_spacing_v, float bin_size_v, float intrinsic_tilt_v, + int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, + int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, + int num_axial_crystals_per_singles_unit_v, + int num_transaxial_crystals_per_singles_unit_v, + int num_detector_layers_v, + int max_num_of_timing_bins, + float size_timing_bin, + float timing_resolution, + float energy_resolution_v = -1.0f, + float reference_energy_v = -1.0f); + + //! get scanner parameters as a std::string std::string parameter_info() const; @@ -261,7 +303,12 @@ class Scanner inline int get_num_transaxial_singles_units() const; /* inline int get_num_layers_singles_units() const; */ inline int get_num_singles_units() const; - + //! Get the maximum number of TOF bins. + inline int get_num_max_of_timing_bins() const; + //! Get the delta t which correspnds to the max number of TOF bins in picosecs. + inline float get_size_of_timing_bin() const; + //! Get the timing resolution of the scanner. + inline float get_timing_resolution() const; //@} (end of block/bucket info) @@ -326,21 +373,27 @@ class Scanner //! set the reference energy of the energy resolution //! A negative value indicates, unknown || not set inline void set_reference_energy(const float new_num); + //! Set the maximum number of TOF bins. + inline void set_num_max_of_timing_bins(int new_num); + //! Set the delta t which correspnds to the max number of TOF bins. + inline void set_size_of_timing_bin(float new_num); + //! Set timing resolution + inline void set_timing_resolution(float new_num_in_ps); //@} (end of set info) //@} (end of set info) - // Calculate a singles bin index from axial and transaxial singles bin coordinates. + //! Calculate a singles bin index from axial and transaxial singles bin coordinates. inline int get_singles_bin_index(int axial_index, int transaxial_index) const; - // Method used to calculate a singles bin index from - // a detection position. + //! Method used to calculate a singles bin index from + //! a detection position. inline int get_singles_bin_index(const DetectionPosition<>& det_pos) const; - // Get the axial singles bin coordinate from a singles bin. + //! Get the axial singles bin coordinate from a singles bin. inline int get_axial_singles_unit(int singles_bin_index) const; - // Get the transaxial singles bin coordinate from a singles bin. + //! Get the transaxial singles bin coordinate from a singles bin. inline int get_transaxial_singles_unit(int singles_bin_index) const; @@ -367,7 +420,7 @@ class Scanner int num_axial_crystals_per_singles_unit; int num_transaxial_crystals_per_singles_unit; - //! + //! //! \brief energy_resolution //! \author Nikos Efthimiou //! \details This is the energy resolution of the system. @@ -382,8 +435,26 @@ class Scanner //! A negative value indicates, unknown. float reference_energy; + //! + //! \brief timing_resolution + //! \author Nikos Efthimiou + //! \details The timing resolution of the scanner, in psec. + float timing_resolution; + + //! + //! \brief num_tof_bins + //! \author Nikos Efthimiou + //! \details The number of TOF bins. Without any mash factors + int max_num_of_timing_bins; + + //! + //! \brief size_timing_bin + //! \author Nikos Efthimiou + //! \details This number corresponds the the least significant clock digit. + float size_timing_bin; + - // ! set all parameters, case where default_num_arccorrected_bins==max_num_non_arccorrected_bins + //! set all parameters, case where default_num_arccorrected_bins==max_num_non_arccorrected_bins void set_params(Type type_v, const std::list& list_of_names_v, int num_rings_v, int max_num_non_arccorrected_bins_v, @@ -400,7 +471,7 @@ class Scanner float energy_resolution_v = -1.0f, float reference_energy = -1.0f); - // ! set all parameters + //! set all parameters void set_params(Type type_v, const std::list& list_of_names_v, int num_rings_v, int max_num_non_arccorrected_bins_v, diff --git a/src/include/stir/Scanner.inl b/src/include/stir/Scanner.inl index e5cadcd67d..952dce0db1 100644 --- a/src/include/stir/Scanner.inl +++ b/src/include/stir/Scanner.inl @@ -229,6 +229,28 @@ Scanner::get_reference_energy() const return reference_energy; } +int Scanner::get_num_max_of_timing_bins() const +{ + return max_num_of_timing_bins; +} + +float Scanner::get_size_of_timing_bin() const +{ + return size_timing_bin; +} + +float Scanner::get_timing_resolution() const +{ + return timing_resolution; +} + +bool Scanner::is_tof_ready() const +{ + return (max_num_of_timing_bins > 0 + && timing_resolution > 0.0f + && timing_resolution > 0.0f); +} + //************************ set ******************************8 void Scanner::set_type(const Type & new_type) @@ -334,6 +356,21 @@ Scanner::set_reference_energy(const float new_num) reference_energy = new_num; } +void Scanner::set_num_max_of_timing_bins(const int new_num) +{ + max_num_of_timing_bins = new_num; +} + +void Scanner::set_size_of_timing_bin(const float new_num) +{ + size_timing_bin = new_num; +} + +void Scanner::set_timing_resolution(const float new_num_in_ps) +{ + timing_resolution = new_num_in_ps; +} + /******** Calculate singles bin index from detection position *********/ diff --git a/src/include/stir/geometry/line_distances.h b/src/include/stir/geometry/line_distances.h index 949d310eec..76331eb52f 100644 --- a/src/include/stir/geometry/line_distances.h +++ b/src/include/stir/geometry/line_distances.h @@ -6,10 +6,12 @@ \ingroup geometry \brief A few functions to compute distances between lines etc \todo move implementations to .cxx + \author Nikos Efthimiou \author Kris Thielemans */ /* Copyright (C) 2005- 2005, Hammersmith Imanet Ltd + Copyright (C) 2016, University of Hull This file is part of STIR. @@ -163,4 +165,32 @@ distance_between_line_and_point( } } +/*! \ingroup geometry + \brief Project a point on a line. + + \author Nikos Efthimiou +*/ +template +inline void +project_point_on_a_line( + const CartesianCoordinate3D& p1, + const CartesianCoordinate3D& p2, + CartesianCoordinate3D& r1 ) +{ + + const CartesianCoordinate3D& r0 = p1; + const CartesianCoordinate3D difference = p2 - p1; + + const CartesianCoordinate3D r10 = r1-r0; + + float inner_prod = inner_product(difference, difference); + + const coordT u = inner_product(r10,difference) / inner_prod ; + + r1.x() = p1.x() + u * difference.x(); + r1.y() = p1.y() + u * difference.y(); + r1.z() = p1.z() + u * difference.z(); + +} + END_NAMESPACE_STIR diff --git a/src/include/stir/listmode/CListModeDataROOT.h b/src/include/stir/listmode/CListModeDataROOT.h index 104ff24290..c8c82cc885 100644 --- a/src/include/stir/listmode/CListModeDataROOT.h +++ b/src/include/stir/listmode/CListModeDataROOT.h @@ -1,6 +1,7 @@ /* * Copyright (C) 2015, 2016 University of Leeds Copyright (C) 2016, UCL + Copyright (C) 2016, University of Hull This file is part of STIR. This file is free software; you can redistribute it and/or modify @@ -98,6 +99,12 @@ class CListModeDataROOT : public CListModeData // int number_of_views; // int number_of_segments; + int max_num_timing_bins; + float size_timing_bin; + float timing_resolution; + + int tof_mash_factor; + KeyParser parser; //! Name of input chain which is going to be used. diff --git a/src/include/stir/listmode/CListRecord.h b/src/include/stir/listmode/CListRecord.h index 014bb653e7..85ed55ef16 100644 --- a/src/include/stir/listmode/CListRecord.h +++ b/src/include/stir/listmode/CListRecord.h @@ -6,11 +6,13 @@ \brief Declarations of classes stir::CListRecord, stir::CListTime and stir::CListEvent which are used for list mode data. + \author Nikos Efthimiou \author Kris Thielemans */ /* Copyright (C) 2003- 2011, Hammersmith Imanet Ltd + Copyright (C) 2016, University of Hull This file is part of STIR. This file is free software; you can redistribute it and/or modify @@ -139,6 +141,19 @@ class CListTime return set_time_in_millisecs(time_in_millisecs); } + virtual inline int get_timing_bin() const + { + error("Function CListTime::get_timing_bin() currently is implemented only " + "ROOT data. Abort."); + } + + //! Get the timing component of the bin. + virtual inline void get_bin(Bin&, const ProjDataInfo&) const + { + error("CListTime::get_bin() currently is implemented only " + "ROOT data. Abort."); + } + }; //! A class recording external input to the scanner (normally used for gating) @@ -189,6 +204,12 @@ class CListRecord virtual bool operator==(const CListRecord& e2) const = 0; bool operator!=(const CListRecord& e2) const { return !(*this == e2); } + //! Used in TOF reconstruction to get both the geometric and the timing + //! component of the event + virtual void full_event(Bin&, const ProjDataInfo&) const + {error("CListRecord::full_event() is implemented only for records which " + "hold timing and spatial information.");} + }; class CListRecordWithGatingInput : public CListRecord diff --git a/src/include/stir/listmode/CListRecordROOT.h b/src/include/stir/listmode/CListRecordROOT.h index cc41e000ff..1c10bf2b25 100644 --- a/src/include/stir/listmode/CListRecordROOT.h +++ b/src/include/stir/listmode/CListRecordROOT.h @@ -1,6 +1,7 @@ /* Copyright (C) 2015-2016 University of Leeds Copyright (C) 2016 UCL + Copyright (C) 2016, University of Hull This file is part of STIR. This file is free software; you can redistribute it and/or modify @@ -81,10 +82,10 @@ class CListEventROOT : public CListEventCylindricalScannerWithDiscreteDetectors class CListTimeROOT : public CListTime { public: - void init_from_data(double time1, double time2) + void init_from_data(float _timeA, float _delta_time) { - timeA = time1; - timeB = time2; + timeA = _timeA; + delta_time = _delta_time; } //! Returns always true @@ -96,35 +97,46 @@ class CListTimeROOT : public CListTime { return timeA * 1e3; } //! Get the detection time of the first photon //! in milliseconds - inline double get_timeA_in_millisecs() const + inline unsigned long get_timeA_in_millisecs() const { return timeA * 1e3; } //! Get the detection time of the second photon //! in milliseconds - inline double get_timeB_in_millisecs() const - { return timeB * 1e3; } + inline unsigned long get_timeB_in_millisecs() const + { return (delta_time - timeA) * 1e3; } //! Get the delta Time between the two events - inline double get_delta_time_in_millisecs() const - { return (timeB - timeA) * 1e3; } + inline unsigned long get_delta_time_in_millisecs() const + { return delta_time * 1e3; } //! Get delta time in picoseconds - inline double get_delta_time_in_picosecs() const + inline unsigned long get_delta_time_in_picosecs() const { return (timeB - timeA) * 1e12; } + inline Succeeded set_time_in_millisecs(const unsigned long time_in_millisecs) { warning("set_time_in_millisecs: Not implemented for ROOT files. Aborting."); return Succeeded::no; } + virtual inline void get_bin(Bin& bin, const ProjDataInfo& proj_data_info) const + { + delta_timing_bin > 0 ? + bin.timing_pos_num() = static_cast ( ( delta_timing_bin / proj_data_info.get_tof_mash_factor()) + 0.5) + : bin.timing_pos_num() = static_cast ( ( delta_timing_bin / proj_data_info.get_tof_mash_factor()) - 0.5); + + if (bin.timing_pos_num() < proj_data_info.get_min_timing_pos_num() || + bin.timing_pos_num() > proj_data_info.get_max_timing_pos_num()) + { + bin.set_bin_value(-1.f); + } + } + private: //! //! \brief timeA //! \details The detection time of the first of the two photons, in seconds - double timeA; + float timeA; - //! - //! \brief timeB - //! \details The detection time of the second of the two photons - double timeB; + float delta_time; }; //! A class for a general element of a listmode file for a Siemens scanner using the ROOT files @@ -173,7 +185,7 @@ class CListRecordROOT : public CListRecord // currently no gating yet const int& ring2, const int& crystal1, const int& crystal2, - double time1, double time2, + float time1, float delta_time, const int& event1, const int& event2) { /// \warning ROOT data are time and event at the same time. @@ -181,8 +193,15 @@ class CListRecordROOT : public CListRecord // currently no gating yet this->event_data.init_from_data(ring1, ring2, crystal1, crystal2); - this->time_data.init_from_data( - time1,time2); + if(!this->event_data.is_swapped()) + this->time_data.init_from_data( + time1, delta_timing_bin); + else + { +// delta_timing_bin = -delta_timing_bin; + this->time_data.init_from_data( + time1, -delta_timing_bin); + } // We can make a singature raw based on the two events IDs. // It is pretty unique. diff --git a/src/include/stir/recon_buildblock/GeneralisedObjectiveFunction.h b/src/include/stir/recon_buildblock/GeneralisedObjectiveFunction.h index 83b941df93..bd63c3ad05 100644 --- a/src/include/stir/recon_buildblock/GeneralisedObjectiveFunction.h +++ b/src/include/stir/recon_buildblock/GeneralisedObjectiveFunction.h @@ -2,6 +2,7 @@ // /* Copyright (C) 2003- 2009, Hammersmith Imanet Ltd + Copyright (C) 2016, University of Hull This file is part of STIR. This file is free software; you can redistribute it and/or modify @@ -21,6 +22,7 @@ \ingroup GeneralisedObjectiveFunction \brief Declaration of class stir::GeneralisedObjectiveFunction + \author Nikos Efthimiou \author Kris Thielemans \author Sanida Mustafovic @@ -228,6 +230,8 @@ class GeneralisedObjectiveFunction: //! Return the number of subsets in-use int get_num_subsets() const; + //! Return the status of TOF + bool get_tof_status() const; //! Attempts to change the number of subsets. /*! \return The number of subsets that will be used later, which is not @@ -295,6 +299,9 @@ class GeneralisedObjectiveFunction: protected: int num_subsets; + //! If set TOF information will be taken into account. + bool use_tof; + shared_ptr > prior_sptr; //! sets any default values diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBin.h b/src/include/stir/recon_buildblock/ProjMatrixByBin.h index 684a03dbcc..5f80faca56 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBin.h +++ b/src/include/stir/recon_buildblock/ProjMatrixByBin.h @@ -7,6 +7,7 @@ \ingroup projection \brief declaration of stir::ProjMatrixByBin and its helpers classes + \author Nikos Efthimiou \author Mustapha Sadki \author Kris Thielemans \author PARAPET project @@ -15,6 +16,7 @@ Copyright (C) 2000 PARAPET partners Copyright (C) 2000-2009, Hammersmith Imanet Ltd Copyright (C) 2013, 2015 University College London + Copyright (C) 2016, University of Hull This file is part of STIR. @@ -38,6 +40,7 @@ #include "stir/shared_ptr.h" #include "stir/VectorWithOffset.h" #include "stir/TimedObject.h" +#include "stir/VoxelsOnCartesianGrid.h" #include //#include #include @@ -123,6 +126,19 @@ class ProjMatrixByBin : get_proj_matrix_elems_for_one_bin( ProjMatrixElemsForOneBin&, const Bin&) STIR_MUTABLE_CONST; + + //! Returns a LOR with elements after application of the TOF + //! kernel. The central_point of the LOR is needed in order to + //! correlate the physical position of the LOR elements with the + //! timing bin dimentions which have as reference the center of the LOR. + //! \warning Currently, first it calculates a non-TOF LOR and then + //! kernel is applied. Which is slow. + inline void + get_proj_matrix_elems_for_one_bin_with_tof( + ProjMatrixElemsForOneBin&, + const Bin&, + const CartesianCoordinate3D& point1, + const CartesianCoordinate3D& point2) STIR_MUTABLE_CONST; #if 0 // TODO @@ -192,6 +208,8 @@ class ProjMatrixByBin : bool cache_disabled; bool cache_stores_only_basic_bins; + //! If activated TOF reconstruction will be performed. + bool tof_enabled; /*! \brief The method that tries to get data from the cache. @@ -232,7 +250,29 @@ class ProjMatrixByBin : // KT 15/05/2002 not static anymore as it uses cache_stores_only_basic_bins CacheKey cache_key(const Bin& bin) const; - + //! A local copy of the scanner's time resolution in mm. + float gauss_sigma_in_mm; + //! 1/(2*sigma_in_mm) + float r_sqrt2_gauss_sigma; + + //! We need a local copy of the discretised density in order to find the + //! cartesian coordinates of each voxel. + shared_ptr > image_info_sptr; + + //! We need a local copy of the proj_data_info to get the integration boundaries. + shared_ptr proj_data_info_sptr; + + //! The function which actually applies the TOF kernel on the LOR. + inline void apply_tof_kernel( ProjMatrixElemsForOneBin& nonTOF_probabilities, + ProjMatrixElemsForOneBin& tof_probabilities, + const CartesianCoordinate3D& point1, + const CartesianCoordinate3D& point2) STIR_MUTABLE_CONST; + + + + //! Get the interal value erf(m - v_j) - erf(m -v_j) + inline void get_tof_value(float& d1, float& d2, float& val) const; + }; diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl index e288d8a5cf..7bf623390f 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl +++ b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl @@ -7,6 +7,7 @@ \brief Implementations of inline functions for class stir::ProjMatrixByBin + \author Nikos Efthimiou \author Mustapha Sadki \author Kris Thielemans \author PARAPET project @@ -15,6 +16,7 @@ /* Copyright (C) 2000 PARAPET partners Copyright (C) 2000- 2013, Hammersmith Imanet Ltd + Copyright (C) 2016, University of Hull This file is part of STIR. This file is free software; you can redistribute it and/or modify @@ -31,6 +33,8 @@ */ #include "stir/Succeeded.h" #include "stir/recon_buildblock/SymmetryOperation.h" +#include "stir/geometry/line_distances.h" +#include "stir/numerics/erf.h" START_NAMESPACE_STIR @@ -111,4 +115,111 @@ get_proj_matrix_elems_for_one_bin( // stop_timers(); TODO, can't do this in a const member } +inline void +ProjMatrixByBin:: +get_proj_matrix_elems_for_one_bin_with_tof( + ProjMatrixElemsForOneBin& probabilities, + const Bin& bin, + const CartesianCoordinate3D& point1, + const CartesianCoordinate3D& point2) STIR_MUTABLE_CONST +{ + // start_timers(); TODO, can't do this in a const member + + if (!tof_enabled) + error("The function get_proj_matrix_elems_for_one_bin_with_tof() needs proper timing " + "initialisation. Abort."); + // set to empty + probabilities.erase(); + ProjMatrixElemsForOneBin tmp_probabilities; + + if (cache_stores_only_basic_bins) + { + // find basic bin + Bin basic_bin = bin; + std::auto_ptr symm_ptr = + symmetries_sptr->find_symmetry_operation_from_basic_bin(basic_bin); + + tmp_probabilities.set_bin(basic_bin); + probabilities.set_bin(bin); + // check if basic bin is in cache + if (get_cached_proj_matrix_elems_for_one_bin(tmp_probabilities) == + Succeeded::no) + { + // call 'calculate' just for the basic bin + calculate_proj_matrix_elems_for_one_bin(tmp_probabilities); +#ifndef NDEBUG + tmp_probabilities.check_state(); +#endif + cache_proj_matrix_elems_for_one_bin(tmp_probabilities); + } +// else +// int nikos = 0; +// tmp_probabilities.set_bin(bin); + + // now transform to original bin + symm_ptr->transform_proj_matrix_elems_for_one_bin(tmp_probabilities); + apply_tof_kernel(tmp_probabilities, probabilities, point1, point2); + } + else // !cache_stores_only_basic_bins + { + error("This option has been deactivated as the amount of memory required is not realistic. Abort."); + } + // stop_timers(); TODO, can't do this in a const member +} + +void +ProjMatrixByBin::apply_tof_kernel(ProjMatrixElemsForOneBin& nonTOF_probabilities, + ProjMatrixElemsForOneBin& tof_probabilities, + const CartesianCoordinate3D& point1, + const CartesianCoordinate3D& point2) STIR_MUTABLE_CONST +{ + + CartesianCoordinate3D voxel_center; + float new_value = 0.f; + float low_dist = 0.f; + float high_dist = 0.f; + + float lor_length = std::sqrt((point1.x() - point2.x()) *(point1.x() - point2.x()) + + (point1.y() - point2.y()) *(point1.y() - point2.y()) + + (point1.z() - point2.z()) *(point1.z() - point2.z())); + + for (ProjMatrixElemsForOneBin::iterator element_ptr = nonTOF_probabilities.begin(); + element_ptr != nonTOF_probabilities.end(); ++element_ptr) + { + voxel_center = + image_info_sptr->get_physical_coordinates_for_indices (element_ptr->get_coords()); + + project_point_to_a_line_f(point1, point2, voxel_center ); + + float d1 = std::sqrt((point1.x() - voxel_center.x()) *(point1.x() - voxel_center.x()) + + (point1.y() - voxel_center.y()) *(point1.y() - voxel_center.y()) + + (point1.z() - voxel_center.z()) *(point1.z() - voxel_center.z())); + + // This might be risky. + // The advantage is significant speed up. + // float d2 = std::sqrt( (point2.x() - voxel_center.x()) *(point2.x() - voxel_center.x()) + + // (point2.y() - voxel_center.y()) *(point2.y() - voxel_center.y()) + + // (point2.z() - voxel_center.z()) *(point2.z() - voxel_center.z())); + + float m = (lor_length - d1 - d1) * 0.5f; + low_dist = (proj_data_info_sptr->timing_bin_boundaries[tof_probabilities.get_bin_ptr()->timing_pos_num()].low_lim - m) * r_sqrt2_gauss_sigma; + high_dist = (proj_data_info_sptr->timing_bin_boundaries[tof_probabilities.get_bin_ptr()->timing_pos_num()].high_lim - m) * r_sqrt2_gauss_sigma; + + get_tof_value(low_dist, high_dist, new_value); + new_value *= element_ptr->get_value(); + + if (new_value <= 0.0001f) + continue; + tof_probabilities.push_back(ProjMatrixElemsForOneBin::value_type(element_ptr->get_coords(), new_value)); + + } +} + +void +ProjMatrixByBin:: +get_tof_value(float& d1, float& d2, float& val) const +{ + val = ( erf(d2) - erf(d1)) * 0.5; +} + END_NAMESPACE_STIR diff --git a/src/listmode_buildblock/CListModeDataROOT.cxx b/src/listmode_buildblock/CListModeDataROOT.cxx index 73945cdecf..5fe8ead23a 100644 --- a/src/listmode_buildblock/CListModeDataROOT.cxx +++ b/src/listmode_buildblock/CListModeDataROOT.cxx @@ -1,6 +1,7 @@ /* Copyright (C) 2015, 2016 University of Leeds Copyright (C) 2016 University College London + Copyright (C) 2016, University of Hull This file is part of STIR. This file is free software; you can redistribute it and/or modify @@ -50,6 +51,11 @@ CListModeDataROOT(const std::string& listmode_filename) // number_of_views = -1; // number_of_segments = -1; + max_num_timing_bins = -1; + size_timing_bin = -1.f; + timing_resolution = -1.f; + tof_mash_factor = -1; + // Scanner related & Physical dimentions. this->parser.add_key("originating system", &this->originating_system); this->parser.add_key("Number of rings", &this->num_rings); @@ -68,6 +74,12 @@ CListModeDataROOT(const std::string& listmode_filename) // this->parser.add_key("%number_of_projections", &number_of_projections); // this->parser.add_key("%number_of_views", &number_of_views); // this->parser.add_key("%number_of_segments", &number_of_segments); + + this->parser.add_key("number of TOF time bins", &this->max_num_timing_bins); + this->parser.add_key("Size of timing bin (in picoseconds)", &this->size_timing_bin); + this->parser.add_key("Timing resolution (in picoseconds)", &this->timing_resolution); + + this->parser.add_key("%TOF mashing factor", &this->tof_mash_factor); // // ROOT related @@ -125,7 +137,10 @@ CListModeDataROOT(const std::string& listmode_filename) this->current_lm_data_ptr->get_num_axial_crystals_per_singles_unit(), /*num_transaxial_crystals_per_singles_unit_v*/ this->current_lm_data_ptr->get_num_trans_crystals_per_singles_unit(), - /*num_detector_layers_v*/ 1 )); + /*num_detector_layers_v*/ 1, + max_num_timing_bins, + size_timing_bin, + timing_resolution)); } if (this->open_lm_file() == Succeeded::no) @@ -146,6 +161,8 @@ CListModeDataROOT(const std::string& listmode_filename) num_detectors_per_ring/2, max_num_non_arccorrected_bins, /* arc_correction*/false)); + if (tof_mash_factor > 0) + this->proj_data_info_sptr->set_tof_mash_factor(tof_mash_factor); } std::string diff --git a/src/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.cxx b/src/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.cxx index 128eb2b2d7..3405dbcfe3 100644 --- a/src/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.cxx +++ b/src/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.cxx @@ -142,7 +142,7 @@ ForwardProjectorByBinUsingProjMatrixByBin:: for ( int tang_pos = min_tangential_pos_num ;tang_pos <= max_tangential_pos_num ;++tang_pos) for ( int ax_pos = min_axial_pos_num; ax_pos <= max_axial_pos_num ;++ax_pos) { - Bin bin(segment_num, view_num, ax_pos, tang_pos, 0); + Bin bin(segment_num, view_num, ax_pos, tang_pos, 0.f); proj_matrix_ptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, bin); proj_matrix_row.forward_project(bin,image); viewgram[ax_pos][tang_pos] = bin.get_bin_value(); diff --git a/src/recon_buildblock/GeneralisedObjectiveFunction.cxx b/src/recon_buildblock/GeneralisedObjectiveFunction.cxx index 689576d0c8..9d20ee1bfc 100644 --- a/src/recon_buildblock/GeneralisedObjectiveFunction.cxx +++ b/src/recon_buildblock/GeneralisedObjectiveFunction.cxx @@ -2,6 +2,7 @@ // /* Copyright (C) 2003- 2011, Hammersmith Imanet Ltd + Copyright (C) 2016, University of Hull This file is part of STIR. This file is free software; you can redistribute it and/or modify @@ -21,6 +22,7 @@ \ingroup GeneralisedObjectiveFunction \brief Declaration of class stir::GeneralisedObjectiveFunction + \author Nikos Efthimiou \author Kris Thielemans \author Sanida Mustafovic @@ -32,7 +34,7 @@ #include "stir/Succeeded.h" #include "stir/modelling/ParametricDiscretisedDensity.h" #include "stir/modelling/KineticParameters.h" - +#include "stir/info.h" using std::string; START_NAMESPACE_STIR @@ -45,6 +47,7 @@ set_defaults() this->prior_sptr.reset(); // note: cannot use set_num_subsets(1) here, as other parameters (such as projectors) are not set-up yet. this->num_subsets = 1; + use_tof = false; } template @@ -53,6 +56,7 @@ GeneralisedObjectiveFunction:: initialise_keymap() { this->parser.add_parsing_key("prior type", &prior_sptr); + this->parser.add_key("Use TOF information", &use_tof); } template @@ -76,6 +80,11 @@ set_up(shared_ptr const& target_data_ptr) return Succeeded::no; } + if (use_tof) + { + info("Time-Of-Flight reconstruction activated!"); + } + return Succeeded::yes; } @@ -169,6 +178,14 @@ get_num_subsets() const return this->num_subsets; } +template +bool +GeneralisedObjectiveFunction:: +get_tof_status() const +{ + return this->use_tof; +} + template double GeneralisedObjectiveFunction:: diff --git a/src/recon_buildblock/ProjMatrixByBin.cxx b/src/recon_buildblock/ProjMatrixByBin.cxx index 4aa5993fc2..951859650b 100644 --- a/src/recon_buildblock/ProjMatrixByBin.cxx +++ b/src/recon_buildblock/ProjMatrixByBin.cxx @@ -4,7 +4,8 @@ \ingroup projection \brief implementation of the stir::ProjMatrixByBin class - + + \author Nikos Efthimiou \author Mustapha Sadki \author Kris Thielemans \author PARAPET project @@ -13,6 +14,7 @@ Copyright (C) 2000 PARAPET partners Copyright (C) 2000-2009, Hammersmith Imanet Ltd Copyright (C) 2013, 2015 University College London + Copyright (C) 2016, University of Hull This file is part of STIR. @@ -46,6 +48,8 @@ void ProjMatrixByBin::set_defaults() { cache_disabled=false; cache_stores_only_basic_bins=true; + gauss_sigma_in_mm = 0.f; + r_sqrt2_gauss_sigma = 0.f; } void @@ -71,6 +75,19 @@ ProjMatrixByBin:: enable_cache(const bool v) { cache_disabled = !v;} +void +ProjMatrixByBin:: +enable_tof(const shared_ptr& _proj_data_info_sptr, const bool v) +{ + if (v) + { + tof_enabled = true; + proj_data_info_sptr = _proj_data_info_sptr; + gauss_sigma_in_mm = (proj_data_info_sptr->get_scanner_ptr()->get_timing_resolution() * 0.299792458f) / 2.355f; + r_sqrt2_gauss_sigma = 1.0f/ (gauss_sigma_in_mm * static_cast(sqrt(2.0))); + } +} + void ProjMatrixByBin:: store_only_basic_bins_in_cache(const bool v) From 0dd73eed5c0351ce22552145d99fe7b200b31082 Mon Sep 17 00:00:00 2001 From: NikEfth Date: Thu, 22 Dec 2016 11:45:58 +0000 Subject: [PATCH 033/170] compiles but new scanner is not ready --- src/buildblock/ProjDataInfo.cxx | 1 + src/buildblock/ProjDataInfoCylindrical.cxx | 9 ++- src/buildblock/Scanner.cxx | 67 +++++++++++++++++++ src/include/stir/Bin.h | 1 + src/include/stir/ProjDataInfoCylindrical.h | 7 +- src/include/stir/Scanner.h | 4 +- src/include/stir/listmode/CListRecordROOT.h | 19 ++---- .../stir/recon_buildblock/ProjMatrixByBin.h | 4 ++ .../stir/recon_buildblock/ProjMatrixByBin.inl | 2 +- .../ProjMatrixElemsForOneBin.h | 6 +- .../ProjMatrixElemsForOneBin.inl | 7 ++ 11 files changed, 106 insertions(+), 21 deletions(-) diff --git a/src/buildblock/ProjDataInfo.cxx b/src/buildblock/ProjDataInfo.cxx index 4e0b0eb1a2..162c6d0d5b 100644 --- a/src/buildblock/ProjDataInfo.cxx +++ b/src/buildblock/ProjDataInfo.cxx @@ -60,6 +60,7 @@ #include "stir/info.h" #include "boost/foreach.hpp" +#include "boost/format.hpp" #ifndef STIR_NO_NAMESPACES using std::string; diff --git a/src/buildblock/ProjDataInfoCylindrical.cxx b/src/buildblock/ProjDataInfoCylindrical.cxx index 808e861b34..0d54ea6587 100644 --- a/src/buildblock/ProjDataInfoCylindrical.cxx +++ b/src/buildblock/ProjDataInfoCylindrical.cxx @@ -584,7 +584,10 @@ void ProjDataInfoCylindrical:: get_LOR_as_two_points_alt(CartesianCoordinate3D& coord_1, CartesianCoordinate3D& coord_2, - const Bin& bin) const + const int& det1, + const int& det2, + const int& ring1, + const int& ring2) const { const int num_detectors_per_ring = get_scanner_ptr()->get_num_detectors_per_ring(); @@ -603,8 +606,8 @@ get_LOR_as_two_points_alt(CartesianCoordinate3D& coord_1, cyl_coords.p1().psi() = static_cast((2.*_PI/num_detectors_per_ring)*(det1)); cyl_coords.p2().psi() = static_cast((2.*_PI/num_detectors_per_ring)*(det2)); - cyl_coords.p1().z() = Ring_A*get_scanner_ptr()->get_ring_spacing() - h_scanner_height; - cyl_coords.p2().z() = Ring_B*get_scanner_ptr()->get_ring_spacing() - h_scanner_height; + cyl_coords.p1().z() = ring1*get_scanner_ptr()->get_ring_spacing() - h_scanner_height; + cyl_coords.p2().z() = ring2*get_scanner_ptr()->get_ring_spacing() - h_scanner_height; LORAs2Points lor(cyl_coords); coord_1 = lor.p1(); diff --git a/src/buildblock/Scanner.cxx b/src/buildblock/Scanner.cxx index eeccf6cc19..536eb1fc6a 100644 --- a/src/buildblock/Scanner.cxx +++ b/src/buildblock/Scanner.cxx @@ -486,6 +486,73 @@ Scanner::Scanner(Type type_v, const string& name, } +Scanner::Scanner(Type type_v, const list& list_of_names_v, + int num_detectors_per_ring_v, int num_rings_v, + int max_num_non_arccorrected_bins_v, + int default_num_arccorrected_bins_v, + float inner_ring_radius_v, float average_depth_of_interaction_v, + float ring_spacing_v, float bin_size_v, float intrinsic_tilt_v, + int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, + int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, + int num_axial_crystals_per_singles_unit_v, + int num_transaxial_crystals_per_singles_unit_v, + int num_detector_layers_v, + int max_num_of_timing_bins, + float size_timing_bin, + float timing_resolution, + float energy_resolution_v, + float reference_energy_v) +{ +// set_params(type_v, list_of_names_v, num_rings_v, +// max_num_non_arccorrected_bins_v, +// default_num_arccorrected_bins_v, +// num_detectors_per_ring_v, +// inner_ring_radius_v, +// average_depth_of_interaction_v, +// ring_spacing_v, bin_size_v, intrinsic_tilt_v, +// num_axial_blocks_per_bucket_v, num_transaxial_blocks_per_bucket_v, +// num_axial_crystals_per_block_v, num_transaxial_crystals_per_block_v, +// num_axial_crystals_per_singles_unit_v, +// num_transaxial_crystals_per_singles_unit_v, +// num_detector_layers_v, +// energy_resolution_v, +// reference_energy_v); +} + + + +Scanner::Scanner(Type type_v, const string& name, + int num_detectors_per_ring_v, int num_rings_v, + int max_num_non_arccorrected_bins_v, + int default_num_arccorrected_bins_v, + float inner_ring_radius_v, float average_depth_of_interaction_v, + float ring_spacing_v, float bin_size_v, float intrinsic_tilt_v, + int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, + int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, + int num_axial_crystals_per_singles_unit_v, + int num_transaxial_crystals_per_singles_unit_v, + int num_detector_layers_v, + int max_num_of_timing_bins, + float size_timing_bin, + float timing_resolution, + float energy_resolution_v, + float reference_energy_v) +{ +// set_params(type_v, string_list(name), num_rings_v, +// max_num_non_arccorrected_bins_v, +// default_num_arccorrected_bins_v, +// num_detectors_per_ring_v, +// inner_ring_radius_v, +// average_depth_of_interaction_v, +// ring_spacing_v, bin_size_v, intrinsic_tilt_v, +// num_axial_blocks_per_bucket_v, num_transaxial_blocks_per_bucket_v, +// num_axial_crystals_per_block_v, num_transaxial_crystals_per_block_v, +// num_axial_crystals_per_singles_unit_v, +// num_transaxial_crystals_per_singles_unit_v, +// num_detector_layers_v, +// energy_resolution_v, +// reference_energy_v); +} diff --git a/src/include/stir/Bin.h b/src/include/stir/Bin.h index 3b79c91429..1a9377ce93 100644 --- a/src/include/stir/Bin.h +++ b/src/include/stir/Bin.h @@ -91,6 +91,7 @@ class Bin inline int& segment_num(); inline int& tangential_pos_num(); inline int& view_num(); + inline int& timing_pos_num(); //! get an empty copy inline Bin get_empty_copy() const; diff --git a/src/include/stir/ProjDataInfoCylindrical.h b/src/include/stir/ProjDataInfoCylindrical.h index cdbae24267..8604973aa6 100644 --- a/src/include/stir/ProjDataInfoCylindrical.h +++ b/src/include/stir/ProjDataInfoCylindrical.h @@ -111,8 +111,11 @@ class ProjDataInfoCylindrical: public ProjDataInfo //! \warning More testing needed. void get_LOR_as_two_points_alt(CartesianCoordinate3D& coord_1, - CartesianCoordinate3D& coord_2, - const Bin& bin) const; + CartesianCoordinate3D& coord_2, + const int& det1, + const int& det2, + const int& ring1, + const int& ring2) const; void set_azimuthal_angle_sampling(const float angle); diff --git a/src/include/stir/Scanner.h b/src/include/stir/Scanner.h index 3cf517932e..565d6597be 100644 --- a/src/include/stir/Scanner.h +++ b/src/include/stir/Scanner.h @@ -395,7 +395,9 @@ class Scanner //! Get the transaxial singles bin coordinate from a singles bin. inline int get_transaxial_singles_unit(int singles_bin_index) const; - + + //! True if it is TOF compatible. + inline bool is_tof_ready() const; private: Type type; diff --git a/src/include/stir/listmode/CListRecordROOT.h b/src/include/stir/listmode/CListRecordROOT.h index 1c10bf2b25..3db20736af 100644 --- a/src/include/stir/listmode/CListRecordROOT.h +++ b/src/include/stir/listmode/CListRecordROOT.h @@ -106,9 +106,6 @@ class CListTimeROOT : public CListTime //! Get the delta Time between the two events inline unsigned long get_delta_time_in_millisecs() const { return delta_time * 1e3; } - //! Get delta time in picoseconds - inline unsigned long get_delta_time_in_picosecs() const - { return (timeB - timeA) * 1e12; } inline Succeeded set_time_in_millisecs(const unsigned long time_in_millisecs) { @@ -118,9 +115,9 @@ class CListTimeROOT : public CListTime virtual inline void get_bin(Bin& bin, const ProjDataInfo& proj_data_info) const { - delta_timing_bin > 0 ? - bin.timing_pos_num() = static_cast ( ( delta_timing_bin / proj_data_info.get_tof_mash_factor()) + 0.5) - : bin.timing_pos_num() = static_cast ( ( delta_timing_bin / proj_data_info.get_tof_mash_factor()) - 0.5); + delta_time > 0 ? + bin.timing_pos_num() = static_cast ( ( delta_time / proj_data_info.get_tof_mash_factor()) + 0.5) + : bin.timing_pos_num() = static_cast ( ( delta_time / proj_data_info.get_tof_mash_factor()) - 0.5); if (bin.timing_pos_num() < proj_data_info.get_min_timing_pos_num() || bin.timing_pos_num() > proj_data_info.get_max_timing_pos_num()) @@ -193,15 +190,11 @@ class CListRecordROOT : public CListRecord // currently no gating yet this->event_data.init_from_data(ring1, ring2, crystal1, crystal2); - if(!this->event_data.is_swapped()) + this->event_data.is_swapped() ? this->time_data.init_from_data( - time1, delta_timing_bin); - else - { -// delta_timing_bin = -delta_timing_bin; + time1, delta_time) : this->time_data.init_from_data( - time1, -delta_timing_bin); - } + time1, -delta_time); // We can make a singature raw based on the two events IDs. // It is pretty unique. diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBin.h b/src/include/stir/recon_buildblock/ProjMatrixByBin.h index 5f80faca56..811167f522 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBin.h +++ b/src/include/stir/recon_buildblock/ProjMatrixByBin.h @@ -170,6 +170,10 @@ class ProjMatrixByBin : //! Remove all elements from the cache void clear_cache() STIR_MUTABLE_CONST; + //! Activates the application of the timing kernel to the LOR + //! and performs initial set_up(). + //! \warning Must be called after set_up() + void enable_tof(const shared_ptr& proj_data_info_sptr,const bool v = true); protected: shared_ptr symmetries_sptr; diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl index 7bf623390f..56e482d323 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl +++ b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl @@ -189,7 +189,7 @@ ProjMatrixByBin::apply_tof_kernel(ProjMatrixElemsForOneBin& nonTOF_probabilities voxel_center = image_info_sptr->get_physical_coordinates_for_indices (element_ptr->get_coords()); - project_point_to_a_line_f(point1, point2, voxel_center ); + project_point_on_a_line(point1, point2, voxel_center ); float d1 = std::sqrt((point1.x() - voxel_center.x()) *(point1.x() - voxel_center.x()) + (point1.y() - voxel_center.y()) *(point1.y() - voxel_center.y()) + diff --git a/src/include/stir/recon_buildblock/ProjMatrixElemsForOneBin.h b/src/include/stir/recon_buildblock/ProjMatrixElemsForOneBin.h index a869a966c9..d0b6e13eac 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixElemsForOneBin.h +++ b/src/include/stir/recon_buildblock/ProjMatrixElemsForOneBin.h @@ -10,7 +10,8 @@ \ingroup projection \brief Declaration of class stir::ProjMatrixElemsForOneBin - + + \author Nikos Efthimiou \author Mustapha Sadki \author Kris Thielemans \author PARAPET project @@ -19,6 +20,7 @@ /* Copyright (C) 2000 PARAPET partners Copyright (C) 2000- 2009, Hammersmith Imanet Ltd + Copyright (C) 2016, University of Hull This file is part of STIR. This file is free software; you can redistribute it and/or modify @@ -121,6 +123,8 @@ class ProjMatrixElemsForOneBin inline Bin get_bin() const; //! and set the bin coordinates inline void set_bin(const Bin&); + //! get a ref to the bin + inline Bin* get_bin_ptr(); //! functions for allowing iterator access inline iterator begin() ; diff --git a/src/include/stir/recon_buildblock/ProjMatrixElemsForOneBin.inl b/src/include/stir/recon_buildblock/ProjMatrixElemsForOneBin.inl index 4fac67243c..4ab89b9a9e 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixElemsForOneBin.inl +++ b/src/include/stir/recon_buildblock/ProjMatrixElemsForOneBin.inl @@ -40,6 +40,13 @@ get_bin() const return bin; } +Bin* +ProjMatrixElemsForOneBin:: +get_bin_ptr() +{ + return &bin; +} + void ProjMatrixElemsForOneBin:: set_bin(const Bin& new_bin) From 61e543274841a487933bb16deb31358c59033705 Mon Sep 17 00:00:00 2001 From: NikEfth Date: Fri, 23 Dec 2016 19:37:19 +0000 Subject: [PATCH 034/170] Experimenting on the integration boundaries --- src/buildblock/ProjDataInfo.cxx | 79 +-- src/buildblock/Scanner.cxx | 474 +++++++++++++++--- src/include/stir/Scanner.h | 253 +++++++--- .../stir/recon_buildblock/ProjMatrixByBin.h | 14 +- .../stir/recon_buildblock/ProjMatrixByBin.inl | 6 +- .../ProjMatrixByBinUsingRayTracing.h | 4 +- ...MeanAndListModeDataWithProjMatrixByBin.cxx | 35 +- .../ProjMatrixByBinUsingRayTracing.cxx | 26 +- 8 files changed, 669 insertions(+), 222 deletions(-) diff --git a/src/buildblock/ProjDataInfo.cxx b/src/buildblock/ProjDataInfo.cxx index 162c6d0d5b..702c7418b5 100644 --- a/src/buildblock/ProjDataInfo.cxx +++ b/src/buildblock/ProjDataInfo.cxx @@ -208,10 +208,15 @@ ProjDataInfo::set_tof_mash_factor(const int new_num) float lowest_boundary = lowest_t * k; float highest_boundary = highest_t * l; - int num_tof_positions_in_FOV = static_cast(((highest_boundary - lowest_boundary) / (2.f * timing_increament_in_mm))+0.5); + int num_tof_positions_in_FOV = static_cast(((highest_boundary - lowest_boundary) / ( timing_increament_in_mm))); - min_timing_pos_num = -num_tof_positions_in_FOV/2; - max_timing_pos_num = min_timing_pos_num + num_tof_positions_in_FOV; + min_timing_pos_num = - (scanner_ptr->get_num_max_of_timing_bins() / tof_mash_factor)/2; + max_timing_pos_num = min_timing_pos_num + (scanner_ptr->get_num_max_of_timing_bins() / tof_mash_factor); + //min_timing_pos_num = -num_tof_positions_in_FOV/2; + //max_timing_pos_num = min_timing_pos_num + num_tof_positions_in_FOV; + + info(boost::format("bound: %1%: %2% - %3% | %4%") %lowest_boundary % highest_boundary %num_tof_positions_in_FOV + %timing_increament_in_mm); // Upper and lower boundaries of the timing poss; timing_bin_boundaries.grow(min_timing_pos_num, max_timing_pos_num); @@ -224,32 +229,48 @@ ProjDataInfo::set_tof_mash_factor(const int new_num) float cur_low = get_k(bin); float cur_high = get_k(bin) + get_sampling_in_k(bin); - if (cur_low< 0 || cur_high < 0 ) - { - if (cur_low < lowest_boundary && cur_high > lowest_boundary) - { - timing_bin_boundaries[i].low_lim = lowest_boundary; - timing_bin_boundaries[i].high_lim = cur_high; - } - else if (cur_low >= lowest_boundary && cur_high >= lowest_boundary) - { - timing_bin_boundaries[i].low_lim = cur_low; - timing_bin_boundaries[i].high_lim = cur_high; - } - } - else - { - if (cur_high > highest_boundary && cur_low < highest_boundary) - { - timing_bin_boundaries[i].low_lim = cur_low; - timing_bin_boundaries[i].high_lim = highest_boundary; - } - else if (cur_low <= highest_boundary && cur_high <= highest_boundary) - { - timing_bin_boundaries[i].low_lim = cur_low; - timing_bin_boundaries[i].high_lim = cur_high; - } - } +// info(boost::format("tic: %1%: %2%") %cur_low % cur_high); + +// if (cur_low < lowest_boundary) +// cur_low = lowest_boundary; +// else if (cur_low > highest_boundary) +// cur_low = highest_boundary; + +// if (cur_high < lowest_boundary) +// cur_high = lowest_boundary; +// else if (cur_high > highest_boundary) +// cur_high = highest_boundary; + +// info(boost::format("tac: %1%: %2%") % cur_low % cur_high); +// if (cur_low< 0 || cur_high < 0 ) +// { +// if (cur_low < lowest_boundary && cur_high > lowest_boundary) +// { +// timing_bin_boundaries[i].low_lim = lowest_boundary; +// timing_bin_boundaries[i].high_lim = cur_high; +// } +// else if (cur_low >= lowest_boundary && cur_high >= lowest_boundary) +// { +// timing_bin_boundaries[i].low_lim = cur_low; +// timing_bin_boundaries[i].high_lim = cur_high; +// } +// } +// else +// { +// if (cur_high > highest_boundary && cur_low < highest_boundary) +// { +// timing_bin_boundaries[i].low_lim = cur_low; +// timing_bin_boundaries[i].high_lim = highest_boundary; +// } +// else if (cur_low <= highest_boundary && cur_high <= highest_boundary) +// { +// timing_bin_boundaries[i].low_lim = cur_low; +// timing_bin_boundaries[i].high_lim = cur_high; +// } +// } + + timing_bin_boundaries[i].low_lim = cur_low; + timing_bin_boundaries[i].high_lim = cur_high; float lowt = (timing_bin_boundaries[i].low_lim / 0.299792458f ) ; float hight = ( timing_bin_boundaries[i].high_lim/ 0.299792458f); info(boost::format("Tbin %1%: %2% - %3% mm (%4% - %5% ps) = %6%") %i % timing_bin_boundaries[i].low_lim % timing_bin_boundaries[i].high_lim diff --git a/src/buildblock/Scanner.cxx b/src/buildblock/Scanner.cxx index 536eb1fc6a..bbcc8348f0 100644 --- a/src/buildblock/Scanner.cxx +++ b/src/buildblock/Scanner.cxx @@ -206,6 +206,114 @@ Scanner::Scanner(Type scanner_type) 2, 1, 8, 9, 16, 9, 1 ); // TODO bucket/singles info incorrect? 224 buckets in total, but not sure how distributed break; + case Type_mCT: // dummy + // 8x8 blocks, 1 virtual "crystal", 56 blocks along the ring, 8 blocks in axial direction + // Transaxial blocks have 8 physical crystals and a gap at the + // 9th crystal where the counts are zero. + set_params(Type_mCT, string_list("Type mCT", "tmCT", "t2011"), + 52, 312, 624, + 424.5F, 7.0F, 4.16F, 2.17242F, 0.0F, + 1, 48, 52, 13, 13, 13, 1 ); // TODO bucket/singles info incorrect? 224 buckets in total, but not sure how distributed + break; + + case Type_mCT_TOF: // dummy + // 8x8 blocks, 1 virtual "crystal", 56 blocks along the ring, 8 blocks in axial direction + // Transaxial blocks have 8 physical crystals and a gap at the + // 9th crystal where the counts are zero. + // For TOF scanners the three last types have to be defined to avoid ambiguity. + set_params(Type_mCT_TOF, string_list("Type mCT_TOF", "tmCT_TOF", "t2011_tof"), + 52, 312, 624, + 424.5F, 7.0F, 4.16F, 2.17242F, 0.0F, + 1, 48, 52, 13, 13, 13, 1, + (short int)(315), + (float)(13.015F), + (float)(600.0F)); // TODO bucket/singles info incorrect? 224 buckets in total, but not sure how distributed + break; + + case Type_mCT_TOF_400: // dummy + // 8x8 blocks, 1 virtual "crystal", 56 blocks along the ring, 8 blocks in axial direction + // Transaxial blocks have 8 physical crystals and a gap at the + // 9th crystal where the counts are zero. + // For TOF scanners the three last types have to be defined to avoid ambiguity. + set_params(Type_mCT_TOF_400, string_list("Type mCT_TOF_400", "tmCT_TOF_400", "t2011_tof_400"), + 52, 312, 624, + 424.5F, 7.0F, 4.16F, 2.17242F, 0.0F, + 1, 48, 52, 13, 13, 13, 1, + (short int)(315), + (float)(13.015F), + (float)(400.0F)); // TODO bucket/singles info incorrect? 224 buckets in total, but not sure how distributed + break; + + case Type_mCT_TOF_200: // dummy + // 8x8 blocks, 1 virtual "crystal", 56 blocks along the ring, 8 blocks in axial direction + // Transaxial blocks have 8 physical crystals and a gap at the + // 9th crystal where the counts are zero. + // For TOF scanners the three last types have to be defined to avoid ambiguity. + set_params(Type_mCT_TOF_200, string_list("Type mCT_TOF_200", "tmCT_TOF_200", "t2011_tof_200"), + 52, 312, 624, + 424.5F, 7.0F, 4.16F, 2.17242F, 0.0F, + 1, 48, 52, 13, 13, 13, 1, + (short int)(315), + (float)(13.015F), + (float)(200.0F)); // TODO bucket/singles info incorrect? 224 buckets in total, but not sure how distributed + break; + + case Type_mCT_TOF_100: // dummy + // 8x8 blocks, 1 virtual "crystal", 56 blocks along the ring, 8 blocks in axial direction + // Transaxial blocks have 8 physical crystals and a gap at the + // 9th crystal where the counts are zero. + // For TOF scanners the three last types have to be defined to avoid ambiguity. + set_params(Type_mCT_TOF_100, string_list("Type mCT_TOF_100", "tmCT_TOF_100", "t2011_tof_100"), + 52, 312, 624, + 424.5F, 7.0F, 4.16F, 2.17242F, 0.0F, + 1, 48, 52, 13, 13, 13, 1, + (short int)(315), + (float)(13.015F), + (float)(100.0F)); // TODO bucket/singles info incorrect? 224 buckets in total, but not sure how distributed + break; + + case Type_mCT_TOF_50: // dummy + // 8x8 blocks, 1 virtual "crystal", 56 blocks along the ring, 8 blocks in axial direction + // Transaxial blocks have 8 physical crystals and a gap at the + // 9th crystal where the counts are zero. + // For TOF scanners the three last types have to be defined to avoid ambiguity. + set_params(Type_mCT_TOF_50, string_list("Type mCT_TOF_50", "tmCT_TOF_50", "t2011_tof_50"), + 52, 312, 624, + 424.5F, 7.0F, 4.16F, 2.17242F, 0.0F, + 1, 48, 52, 13, 13, 13, 1, + (short int)(315), + (float)(13.015F), + (float)(50.0F)); // TODO bucket/singles info incorrect? 224 buckets in total, but not sure how distributed + break; + + case Type_mCT_TOF_20: // dummy + // 8x8 blocks, 1 virtual "crystal", 56 blocks along the ring, 8 blocks in axial direction + // Transaxial blocks have 8 physical crystals and a gap at the + // 9th crystal where the counts are zero. + // For TOF scanners the three last types have to be defined to avoid ambiguity. + set_params(Type_mCT_TOF_20, string_list("Type mCT_TOF_20", "tmCT_TOF_20", "t2011_tof_20"), + 52, 312, 624, + 424.5F, 7.0F, 4.16F, 2.17242F, 0.0F, + 1, 48, 52, 13, 13, 13, 1, + (short int)(315), + (float)(13.015F), + (float)(20.0F)); // TODO bucket/singles info incorrect? 224 buckets in total, but not sure how distributed + break; + + case Type_mCT_TOF_10: // dummy + // 8x8 blocks, 1 virtual "crystal", 56 blocks along the ring, 8 blocks in axial direction + // Transaxial blocks have 8 physical crystals and a gap at the + // 9th crystal where the counts are zero. + // For TOF scanners the three last types have to be defined to avoid ambiguity. + set_params(Type_mCT_TOF_10, string_list("Type mCT_TOF_10", "tmCT_TOF_10", "t2011_tof_10"), + 52, 312, 624, + 424.5F, 7.0F, 4.16F, 2.17242F, 0.0F, + 1, 48, 52, 13, 13, 13, 1, + (short int)(315), + (float)(13.015F), + (float)(10.0F)); // TODO bucket/singles info incorrect? 224 buckets in total, but not sure how distributed + break; + case RPT: set_params(RPT, string_list("PRT-1", "RPT"), @@ -233,8 +341,8 @@ Scanner::Scanner(Type scanner_type) set_params(PANDA, string_list("PANDA"), 1 /*NumRings*/, 512 /*MaxBinsNonArcCor*/, 512 /*MaxBinsArcCor*/, 2048 /*NumDetPerRing*/, - /*MeanInnerRadius*/ 75.5/2.F, /*AverageDoI*/ 10.F, /*Ring Spacing*/ 3.F, /*BinSize*/ 0.1F, /*IntrinsicTilt*/ 0.F, - 1, 1, 1, 1, 0, 0, 1); + /*MeanInnerRadius*/ 75.5/2.F, /*AverageDoI*/ 10.F, /*Ring Spacing*/ 3.F, /*BinSize*/ 0.1F, /*IntrinsicTilt*/ 0.F, + 1, 1, 1, 1, 0, 0, 1); break; case nanoPET: @@ -424,14 +532,40 @@ Scanner::Scanner(Type scanner_type) Scanner::Scanner(Type type_v, const list& list_of_names_v, - int num_detectors_per_ring_v, int num_rings_v, + int num_detectors_per_ring_v, int num_rings_v, int max_num_non_arccorrected_bins_v, int default_num_arccorrected_bins_v, - float inner_ring_radius_v, float average_depth_of_interaction_v, + float inner_ring_radius_v, float average_depth_of_interaction_v, float ring_spacing_v, float bin_size_v, float intrinsic_tilt_v, int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, - int num_axial_crystals_per_singles_unit_v, + int num_axial_crystals_per_singles_unit_v, + int num_transaxial_crystals_per_singles_unit_v, + int num_detector_layers_v) +{ + set_params(type_v, list_of_names_v, num_rings_v, + max_num_non_arccorrected_bins_v, + default_num_arccorrected_bins_v, + num_detectors_per_ring_v, + inner_ring_radius_v, + average_depth_of_interaction_v, + ring_spacing_v, bin_size_v, intrinsic_tilt_v, + num_axial_blocks_per_bucket_v, num_transaxial_blocks_per_bucket_v, + num_axial_crystals_per_block_v, num_transaxial_crystals_per_block_v, + num_axial_crystals_per_singles_unit_v, + num_transaxial_crystals_per_singles_unit_v, + num_detector_layers_v); +} + +Scanner::Scanner(Type type_v, const list& list_of_names_v, + int num_detectors_per_ring_v, int num_rings_v, + int max_num_non_arccorrected_bins_v, + int default_num_arccorrected_bins_v, + float inner_ring_radius_v, float average_depth_of_interaction_v, + float ring_spacing_v, float bin_size_v, float intrinsic_tilt_v, + int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, + int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, + int num_axial_crystals_per_singles_unit_v, int num_transaxial_crystals_per_singles_unit_v, int num_detector_layers_v, float energy_resolution_v, @@ -444,30 +578,59 @@ Scanner::Scanner(Type type_v, const list& list_of_names_v, inner_ring_radius_v, average_depth_of_interaction_v, ring_spacing_v, bin_size_v, intrinsic_tilt_v, + energy_resolution_v, + reference_energy_v, num_axial_blocks_per_bucket_v, num_transaxial_blocks_per_bucket_v, num_axial_crystals_per_block_v, num_transaxial_crystals_per_block_v, num_axial_crystals_per_singles_unit_v, num_transaxial_crystals_per_singles_unit_v, - num_detector_layers_v, - energy_resolution_v, - reference_energy_v); + num_detector_layers_v); } +Scanner::Scanner(Type type_v, const list& list_of_names_v, + int num_detectors_per_ring_v, int num_rings_v, + int max_num_non_arccorrected_bins_v, + int default_num_arccorrected_bins_v, + float inner_ring_radius_v, float average_depth_of_interaction_v, + float ring_spacing_v, float bin_size_v, float intrinsic_tilt_v, + int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, + int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, + int num_axial_crystals_per_singles_unit_v, + int num_transaxial_crystals_per_singles_unit_v, + int num_detector_layers_v, + short int max_num_of_timing_bins_v, + float size_timing_bin_v, + float timing_resolution_v) +{ + set_params(type_v, list_of_names_v, num_rings_v, + max_num_non_arccorrected_bins_v, + default_num_arccorrected_bins_v, + num_detectors_per_ring_v, + inner_ring_radius_v, + average_depth_of_interaction_v, + ring_spacing_v, bin_size_v, intrinsic_tilt_v, + num_axial_blocks_per_bucket_v, num_transaxial_blocks_per_bucket_v, + num_axial_crystals_per_block_v, num_transaxial_crystals_per_block_v, + num_axial_crystals_per_singles_unit_v, + num_transaxial_crystals_per_singles_unit_v, + num_detector_layers_v, + max_num_of_timing_bins_v, + size_timing_bin_v, + timing_resolution_v); +} Scanner::Scanner(Type type_v, const string& name, - int num_detectors_per_ring_v, int num_rings_v, + int num_detectors_per_ring_v, int num_rings_v, int max_num_non_arccorrected_bins_v, int default_num_arccorrected_bins_v, - float inner_ring_radius_v, float average_depth_of_interaction_v, + float inner_ring_radius_v, float average_depth_of_interaction_v, float ring_spacing_v, float bin_size_v, float intrinsic_tilt_v, int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, - int num_axial_crystals_per_singles_unit_v, + int num_axial_crystals_per_singles_unit_v, int num_transaxial_crystals_per_singles_unit_v, - int num_detector_layers_v, - float energy_resolution_v, - float reference_energy_v) + int num_detector_layers_v) { set_params(type_v, string_list(name), num_rings_v, max_num_non_arccorrected_bins_v, @@ -480,13 +643,10 @@ Scanner::Scanner(Type type_v, const string& name, num_axial_crystals_per_block_v, num_transaxial_crystals_per_block_v, num_axial_crystals_per_singles_unit_v, num_transaxial_crystals_per_singles_unit_v, - num_detector_layers_v, - energy_resolution_v, - reference_energy_v); + num_detector_layers_v); } - -Scanner::Scanner(Type type_v, const list& list_of_names_v, +Scanner::Scanner(Type type_v, const string& name, int num_detectors_per_ring_v, int num_rings_v, int max_num_non_arccorrected_bins_v, int default_num_arccorrected_bins_v, @@ -497,30 +657,25 @@ Scanner::Scanner(Type type_v, const list& list_of_names_v, int num_axial_crystals_per_singles_unit_v, int num_transaxial_crystals_per_singles_unit_v, int num_detector_layers_v, - int max_num_of_timing_bins, - float size_timing_bin, - float timing_resolution, float energy_resolution_v, float reference_energy_v) { -// set_params(type_v, list_of_names_v, num_rings_v, -// max_num_non_arccorrected_bins_v, -// default_num_arccorrected_bins_v, -// num_detectors_per_ring_v, -// inner_ring_radius_v, -// average_depth_of_interaction_v, -// ring_spacing_v, bin_size_v, intrinsic_tilt_v, -// num_axial_blocks_per_bucket_v, num_transaxial_blocks_per_bucket_v, -// num_axial_crystals_per_block_v, num_transaxial_crystals_per_block_v, -// num_axial_crystals_per_singles_unit_v, -// num_transaxial_crystals_per_singles_unit_v, -// num_detector_layers_v, -// energy_resolution_v, -// reference_energy_v); + set_params(type_v, string_list(name), num_rings_v, + max_num_non_arccorrected_bins_v, + default_num_arccorrected_bins_v, + num_detectors_per_ring_v, + inner_ring_radius_v, + average_depth_of_interaction_v, + ring_spacing_v, bin_size_v, intrinsic_tilt_v, + energy_resolution_v, + reference_energy_v, + num_axial_blocks_per_bucket_v, num_transaxial_blocks_per_bucket_v, + num_axial_crystals_per_block_v, num_transaxial_crystals_per_block_v, + num_axial_crystals_per_singles_unit_v, + num_transaxial_crystals_per_singles_unit_v, + num_detector_layers_v); } - - Scanner::Scanner(Type type_v, const string& name, int num_detectors_per_ring_v, int num_rings_v, int max_num_non_arccorrected_bins_v, @@ -532,71 +687,132 @@ Scanner::Scanner(Type type_v, const string& name, int num_axial_crystals_per_singles_unit_v, int num_transaxial_crystals_per_singles_unit_v, int num_detector_layers_v, - int max_num_of_timing_bins, - float size_timing_bin, - float timing_resolution, - float energy_resolution_v, - float reference_energy_v) + short int max_num_of_timing_bins_v, + float size_timing_bin_v, + float timing_resolution_v + ) { -// set_params(type_v, string_list(name), num_rings_v, -// max_num_non_arccorrected_bins_v, -// default_num_arccorrected_bins_v, -// num_detectors_per_ring_v, -// inner_ring_radius_v, -// average_depth_of_interaction_v, -// ring_spacing_v, bin_size_v, intrinsic_tilt_v, -// num_axial_blocks_per_bucket_v, num_transaxial_blocks_per_bucket_v, -// num_axial_crystals_per_block_v, num_transaxial_crystals_per_block_v, -// num_axial_crystals_per_singles_unit_v, -// num_transaxial_crystals_per_singles_unit_v, -// num_detector_layers_v, -// energy_resolution_v, -// reference_energy_v); + set_params(type_v, string_list(name), num_rings_v, + max_num_non_arccorrected_bins_v, + default_num_arccorrected_bins_v, + num_detectors_per_ring_v, + inner_ring_radius_v, + average_depth_of_interaction_v, + ring_spacing_v, bin_size_v, intrinsic_tilt_v, + num_axial_blocks_per_bucket_v, num_transaxial_blocks_per_bucket_v, + num_axial_crystals_per_block_v, num_transaxial_crystals_per_block_v, + num_axial_crystals_per_singles_unit_v, + num_transaxial_crystals_per_singles_unit_v, + num_detector_layers_v, + max_num_of_timing_bins_v, + size_timing_bin_v, + timing_resolution_v); } - - - - void Scanner:: set_params(Type type_v,const list& list_of_names_v, - int num_rings_v, + int num_rings_v, int max_num_non_arccorrected_bins_v, int num_detectors_per_ring_v, float inner_ring_radius_v, float average_depth_of_interaction_v, float ring_spacing_v, float bin_size_v, float intrinsic_tilt_v, - int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, + int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, int num_axial_crystals_per_singles_unit_v, int num_transaxial_crystals_per_singles_unit_v, - int num_detector_layers_v, - float energy_resolution_v, - float reference_energy_v) + int num_detector_layers_v) { set_params(type_v, list_of_names_v, num_rings_v, max_num_non_arccorrected_bins_v, - max_num_non_arccorrected_bins_v, - num_detectors_per_ring_v, - inner_ring_radius_v, + max_num_non_arccorrected_bins_v, + num_detectors_per_ring_v, + inner_ring_radius_v, average_depth_of_interaction_v, ring_spacing_v, bin_size_v, intrinsic_tilt_v, - num_axial_blocks_per_bucket_v, num_transaxial_blocks_per_bucket_v, - num_axial_crystals_per_block_v, num_transaxial_crystals_per_block_v, - num_axial_crystals_per_singles_unit_v, + num_axial_blocks_per_bucket_v, num_transaxial_blocks_per_bucket_v, + num_axial_crystals_per_block_v, num_transaxial_crystals_per_block_v, + num_axial_crystals_per_singles_unit_v, num_transaxial_crystals_per_singles_unit_v, - num_detector_layers_v, + num_detector_layers_v); +} + +void +Scanner:: +set_params(Type type_v,const list& list_of_names_v, + int num_rings_v, + int max_num_non_arccorrected_bins_v, + int num_detectors_per_ring_v, + float inner_ring_radius_v, + float average_depth_of_interaction_v, + float ring_spacing_v, + float bin_size_v, float intrinsic_tilt_v, + float energy_resolution_v, + float reference_energy_v, + int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, + int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, + int num_axial_crystals_per_singles_unit_v, + int num_transaxial_crystals_per_singles_unit_v, + int num_detector_layers_v) +{ + set_params(type_v, list_of_names_v, num_rings_v, + max_num_non_arccorrected_bins_v, + max_num_non_arccorrected_bins_v, + num_detectors_per_ring_v, + inner_ring_radius_v, + average_depth_of_interaction_v, + ring_spacing_v, bin_size_v, intrinsic_tilt_v, energy_resolution_v, - reference_energy_v); + reference_energy_v, + num_axial_blocks_per_bucket_v, num_transaxial_blocks_per_bucket_v, + num_axial_crystals_per_block_v, num_transaxial_crystals_per_block_v, + num_axial_crystals_per_singles_unit_v, + num_transaxial_crystals_per_singles_unit_v, + num_detector_layers_v); } +void +Scanner:: +set_params(Type type_v,const list& list_of_names_v, + int num_rings_v, + int max_num_non_arccorrected_bins_v, + int num_detectors_per_ring_v, + float inner_ring_radius_v, + float average_depth_of_interaction_v, + float ring_spacing_v, + float bin_size_v, float intrinsic_tilt_v, + int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, + int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, + int num_axial_crystals_per_singles_unit_v, + int num_transaxial_crystals_per_singles_unit_v, + int num_detector_layers_v, + short int max_num_of_timing_bins_v, + float size_timing_bin_v, + float timing_resolution_v) +{ + set_params(type_v, list_of_names_v, num_rings_v, + max_num_non_arccorrected_bins_v, + max_num_non_arccorrected_bins_v, + num_detectors_per_ring_v, + inner_ring_radius_v, + average_depth_of_interaction_v, + ring_spacing_v, bin_size_v, intrinsic_tilt_v, + num_axial_blocks_per_bucket_v, num_transaxial_blocks_per_bucket_v, + num_axial_crystals_per_block_v, num_transaxial_crystals_per_block_v, + num_axial_crystals_per_singles_unit_v, + num_transaxial_crystals_per_singles_unit_v, + num_detector_layers_v, + max_num_of_timing_bins_v, + size_timing_bin_v, + timing_resolution_v); +} void Scanner:: -set_params(Type type_v,const list& list_of_names_v, - int num_rings_v, +set_params(Type type_v,const list& list_of_names_v, + int num_rings_v, int max_num_non_arccorrected_bins_v, int default_num_arccorrected_bins_v, int num_detectors_per_ring_v, @@ -604,25 +820,69 @@ set_params(Type type_v,const list& list_of_names_v, float average_depth_of_interaction_v, float ring_spacing_v, float bin_size_v, float intrinsic_tilt_v, - int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, + int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, int num_axial_crystals_per_singles_unit_v, int num_transaxial_crystals_per_singles_unit_v, - int num_detector_layers_v, + int num_detector_layers_v) +{ + type = type_v; + list_of_names = list_of_names_v; + num_rings = num_rings_v; + max_num_non_arccorrected_bins = max_num_non_arccorrected_bins_v; + default_num_arccorrected_bins = default_num_arccorrected_bins_v; + num_detectors_per_ring = num_detectors_per_ring_v; + inner_ring_radius = inner_ring_radius_v; + average_depth_of_interaction = average_depth_of_interaction_v; + ring_spacing = ring_spacing_v; + bin_size = bin_size_v; + intrinsic_tilt = intrinsic_tilt_v; + num_transaxial_blocks_per_bucket = num_transaxial_blocks_per_bucket_v; + num_axial_blocks_per_bucket = num_axial_blocks_per_bucket_v; + num_axial_crystals_per_block= num_axial_crystals_per_block_v; + num_transaxial_crystals_per_block= num_transaxial_crystals_per_block_v; + num_axial_crystals_per_singles_unit = num_axial_crystals_per_singles_unit_v; + num_transaxial_crystals_per_singles_unit = num_transaxial_crystals_per_singles_unit_v; + num_detector_layers = num_detector_layers_v; + + energy_resolution = -1.f; + reference_energy = -1.f; + max_num_of_timing_bins = -1; + size_timing_bin = -1.f; + timing_resolution = -1.f; + +} + +void +Scanner:: +set_params(Type type_v,const list& list_of_names_v, + int num_rings_v, + int max_num_non_arccorrected_bins_v, + int default_num_arccorrected_bins_v, + int num_detectors_per_ring_v, + float inner_ring_radius_v, + float average_depth_of_interaction_v, + float ring_spacing_v, + float bin_size_v, float intrinsic_tilt_v, float energy_resolution_v, - float reference_energy_v) + float reference_energy_v, + int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, + int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, + int num_axial_crystals_per_singles_unit_v, + int num_transaxial_crystals_per_singles_unit_v, + int num_detector_layers_v) { type = type_v; - list_of_names = list_of_names_v; + list_of_names = list_of_names_v; num_rings = num_rings_v; max_num_non_arccorrected_bins = max_num_non_arccorrected_bins_v; default_num_arccorrected_bins = default_num_arccorrected_bins_v; - num_detectors_per_ring = num_detectors_per_ring_v; + num_detectors_per_ring = num_detectors_per_ring_v; inner_ring_radius = inner_ring_radius_v; average_depth_of_interaction = average_depth_of_interaction_v; ring_spacing = ring_spacing_v; bin_size = bin_size_v; - intrinsic_tilt = intrinsic_tilt_v; + intrinsic_tilt = intrinsic_tilt_v; num_transaxial_blocks_per_bucket = num_transaxial_blocks_per_bucket_v; num_axial_blocks_per_bucket = num_axial_blocks_per_bucket_v; num_axial_crystals_per_block= num_axial_crystals_per_block_v; @@ -633,7 +893,57 @@ set_params(Type type_v,const list& list_of_names_v, energy_resolution = energy_resolution_v; reference_energy = reference_energy_v; + max_num_of_timing_bins = -1; + size_timing_bin = -1.f; + timing_resolution = -1.f; + +} + +void +Scanner:: +set_params(Type type_v,const list& list_of_names_v, + int num_rings_v, + int max_num_non_arccorrected_bins_v, + int default_num_arccorrected_bins_v, + int num_detectors_per_ring_v, + float inner_ring_radius_v, + float average_depth_of_interaction_v, + float ring_spacing_v, + float bin_size_v, float intrinsic_tilt_v, + int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, + int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, + int num_axial_crystals_per_singles_unit_v, + int num_transaxial_crystals_per_singles_unit_v, + int num_detector_layers_v, + short int max_num_of_timing_bins_v, + float size_timing_bin_v, + float timing_resolution_v) +{ + type = type_v; + list_of_names = list_of_names_v; + num_rings = num_rings_v; + max_num_non_arccorrected_bins = max_num_non_arccorrected_bins_v; + default_num_arccorrected_bins = default_num_arccorrected_bins_v; + num_detectors_per_ring = num_detectors_per_ring_v; + inner_ring_radius = inner_ring_radius_v; + average_depth_of_interaction = average_depth_of_interaction_v; + ring_spacing = ring_spacing_v; + bin_size = bin_size_v; + intrinsic_tilt = intrinsic_tilt_v; + num_transaxial_blocks_per_bucket = num_transaxial_blocks_per_bucket_v; + num_axial_blocks_per_bucket = num_axial_blocks_per_bucket_v; + num_axial_crystals_per_block= num_axial_crystals_per_block_v; + num_transaxial_crystals_per_block= num_transaxial_crystals_per_block_v; + num_axial_crystals_per_singles_unit = num_axial_crystals_per_singles_unit_v; + num_transaxial_crystals_per_singles_unit = num_transaxial_crystals_per_singles_unit_v; + num_detector_layers = num_detector_layers_v; + + max_num_of_timing_bins = max_num_of_timing_bins_v; + size_timing_bin = size_timing_bin_v; + timing_resolution = timing_resolution_v; + energy_resolution = -1.f; + reference_energy = -1.f; } diff --git a/src/include/stir/Scanner.h b/src/include/stir/Scanner.h index 565d6597be..d27c4a0530 100644 --- a/src/include/stir/Scanner.h +++ b/src/include/stir/Scanner.h @@ -113,7 +113,7 @@ class Scanner any given parameters. */ enum Type {E931, E951, E953, E921, E925, E961, E962, E966, E1080, Siemens_mMR, RPT,HiDAC, - /*Type_mCT, Type_mCT_TOF, Type_mCT_TOF_100, Type_mCT_TOF_200, Type_mCT_TOF_400,*/ + Type_mCT, Type_mCT_TOF, Type_mCT_TOF_10, Type_mCT_TOF_20, Type_mCT_TOF_50, Type_mCT_TOF_100, Type_mCT_TOF_200, Type_mCT_TOF_400, Advance, DiscoveryLS, DiscoveryST, DiscoverySTE, DiscoveryRX, Discovery600, HZLR, RATPET, PANDA, HYPERimage, nanoPET, HRRT, Allegro, GeminiTF, User_defined_scanner, Unknown_scanner}; @@ -123,79 +123,100 @@ class Scanner //! constructor -(list of names) - /*! size info is in mm - \param intrinsic_tilt_v value in radians, \see get_default_intrinsic_tilt() - \warning calls error() when block/bucket info are inconsistent - */ - Scanner(Type type_v, const std::list& list_of_names_v, - int num_detectors_per_ring_v, int num_rings_v, - int max_num_non_arccorrected_bins_v, - int default_num_arccorrected_bins_v, - float inner_ring_radius_v, float average_depth_of_interaction_v, - float ring_spacing_v, float bin_size_v, float intrinsic_tilt_v, - int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, - int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, - int num_axial_crystals_per_singles_unit_v, - int num_transaxial_crystals_per_singles_unit_v, - int num_detector_layers_v, - float energy_resolution_v = -1.0f, - float reference_energy_v = -1.0f); - - //! constructor ( a single name) - /*! size info is in mm - \param intrinsic_tilt value in radians, \see get_default_intrinsic_tilt() - \warning calls error() when block/bucket info are inconsistent - */ - Scanner(Type type_v, const std::string& name, - int num_detectors_per_ring_v, int num_rings_v, - int max_num_non_arccorrected_bins_v, - int default_num_arccorrected_bins_v, - float inner_ring_radius_v, float average_depth_of_interaction_v, - float ring_spacing_v, float bin_size_v, float intrinsic_tilt_v, - int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, - int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, - int num_axial_crystals_per_singles_unit_v, - int num_transaxial_crystals_per_singles_unit_v, - int num_detector_layers_v, - float energy_resolution_v = -1.0f, - float reference_energy_v = -1.0f); - - - //! TOF constructor -(list of names) - Scanner(Type type_v, const std::list& list_of_names_v, - int num_detectors_per_ring_v, int num_rings_v, - int max_num_non_arccorrected_bins_v, - int default_num_arccorrected_bins_v, - float inner_ring_radius_v, float average_depth_of_interaction_v, - float ring_spacing_v, float bin_size_v, float intrinsic_tilt_v, - int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, - int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, - int num_axial_crystals_per_singles_unit_v, - int num_transaxial_crystals_per_singles_unit_v, - int num_detector_layers_v, - int max_num_of_timing_bins, - float size_timing_bin, - float timing_resolution, - float energy_resolution_v = -1.0f, - float reference_energy_v = -1.0f); - - //! TOF constructor ( a single name) - Scanner(Type type_v, const std::string& name, - int num_detectors_per_ring_v, int num_rings_v, - int max_num_non_arccorrected_bins_v, - int default_num_arccorrected_bins_v, - float inner_ring_radius_v, float average_depth_of_interaction_v, - float ring_spacing_v, float bin_size_v, float intrinsic_tilt_v, - int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, - int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, - int num_axial_crystals_per_singles_unit_v, - int num_transaxial_crystals_per_singles_unit_v, - int num_detector_layers_v, - int max_num_of_timing_bins, - float size_timing_bin, - float timing_resolution, - float energy_resolution_v = -1.0f, - float reference_energy_v = -1.0f); + /*! size info is in mm + \param intrinsic_tilt_v value in radians, \see get_default_intrinsic_tilt() + \warning calls error() when block/bucket info are inconsistent + */ + Scanner(Type type_v, const std::list& list_of_names_v, + int num_detectors_per_ring_v, int num_rings_v, + int max_num_non_arccorrected_bins_v, + int default_num_arccorrected_bins_v, + float inner_ring_radius_v, float average_depth_of_interaction_v, + float ring_spacing_v, float bin_size_v, float intrinsic_tilt_v, + int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, + int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, + int num_axial_crystals_per_singles_unit_v, + int num_transaxial_crystals_per_singles_unit_v, + int num_detector_layers_v); + + //! Overloaded for energy resolution + Scanner(Type type_v, const std::list& list_of_names_v, + int num_detectors_per_ring_v, int num_rings_v, + int max_num_non_arccorrected_bins_v, + int default_num_arccorrected_bins_v, + float inner_ring_radius_v, float average_depth_of_interaction_v, + float ring_spacing_v, float bin_size_v, float intrinsic_tilt_v, + int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, + int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, + int num_axial_crystals_per_singles_unit_v, + int num_transaxial_crystals_per_singles_unit_v, + int num_detector_layers_v, + float energy_resolution_v, + float reference_energy_v); + + //! Overloaded constructed with TOF information + Scanner(Type type_v, const std::list& list_of_names_v, + int num_detectors_per_ring_v, int num_rings_v, + int max_num_non_arccorrected_bins_v, + int default_num_arccorrected_bins_v, + float inner_ring_radius_v, float average_depth_of_interaction_v, + float ring_spacing_v, float bin_size_v, float intrinsic_tilt_v, + int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, + int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, + int num_axial_crystals_per_singles_unit_v, + int num_transaxial_crystals_per_singles_unit_v, + int num_detector_layers_v, + short int max_num_of_timing_bins, + float size_timing_bin, + float timing_resolution); + + //! constructor ( a single name) + /*! size info is in mm + \param intrinsic_tilt value in radians, \see get_default_intrinsic_tilt() + \warning calls error() when block/bucket info are inconsistent + */ + Scanner(Type type_v, const std::string& name, + int num_detectors_per_ring_v, int num_rings_v, + int max_num_non_arccorrected_bins_v, + int default_num_arccorrected_bins_v, + float inner_ring_radius_v, float average_depth_of_interaction_v, + float ring_spacing_v, float bin_size_v, float intrinsic_tilt_v, + int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, + int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, + int num_axial_crystals_per_singles_unit_v, + int num_transaxial_crystals_per_singles_unit_v, + int num_detector_layers_v); + + //! Overloaded for energy resolution + Scanner(Type type_v, const std::string& name, + int num_detectors_per_ring_v, int num_rings_v, + int max_num_non_arccorrected_bins_v, + int default_num_arccorrected_bins_v, + float inner_ring_radius_v, float average_depth_of_interaction_v, + float ring_spacing_v, float bin_size_v, float intrinsic_tilt_v, + int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, + int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, + int num_axial_crystals_per_singles_unit_v, + int num_transaxial_crystals_per_singles_unit_v, + int num_detector_layers_v, + float energy_resolution_v, + float reference_energy_v); + + //! Overloaded constructor with TOF information ( a single name) + Scanner(Type type_v, const std::string& name, + int num_detectors_per_ring_v, int num_rings_v, + int max_num_non_arccorrected_bins_v, + int default_num_arccorrected_bins_v, + float inner_ring_radius_v, float average_depth_of_interaction_v, + float ring_spacing_v, float bin_size_v, float intrinsic_tilt_v, + int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, + int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, + int num_axial_crystals_per_singles_unit_v, + int num_transaxial_crystals_per_singles_unit_v, + int num_detector_layers_v, + short int max_num_of_timing_bins, + float size_timing_bin, + float timing_resolution); @@ -458,24 +479,89 @@ class Scanner //! set all parameters, case where default_num_arccorrected_bins==max_num_non_arccorrected_bins void set_params(Type type_v, const std::list& list_of_names_v, - int num_rings_v, + int num_rings_v, + int max_num_non_arccorrected_bins_v, + int num_detectors_per_ring_v, + float inner_ring_radius_v, + float average_depth_of_interaction_v, + float ring_spacing_v, + float bin_size_v, float intrinsic_tilt_v, + int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, + int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, + int num_axial_crystals_per_singles_unit_v, + int num_transaxial_crystals_per_singles_unit_v, + int num_detector_layers_v); + + void set_params(Type type_v, const std::list& list_of_names_v, + int num_rings_v, + int max_num_non_arccorrected_bins_v, + int num_detectors_per_ring_v, + float inner_ring_radius_v, + float average_depth_of_interaction_v, + float ring_spacing_v, + float bin_size_v, float intrinsic_tilt_v, + float energy_resolution_v, + float reference_energy, + int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, + int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, + int num_axial_crystals_per_singles_unit_v, + int num_transaxial_crystals_per_singles_unit_v, + int num_detector_layers_v); + + //! Overloaded with TOF stuff. + void set_params(Type type_v, const std::list& list_of_names_v, + int num_rings_v, int max_num_non_arccorrected_bins_v, int num_detectors_per_ring_v, float inner_ring_radius_v, float average_depth_of_interaction_v, float ring_spacing_v, float bin_size_v, float intrinsic_tilt_v, - int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, + int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, int num_axial_crystals_per_singles_unit_v, int num_transaxial_crystals_per_singles_unit_v, int num_detector_layers_v, - float energy_resolution_v = -1.0f, - float reference_energy = -1.0f); + short int max_num_of_timing_bins_v, + float size_timing_bin_v, + float timing_resolution_v); + + // ! set all parameters + void set_params(Type type_v, const std::list& list_of_names_v, + int num_rings_v, + int max_num_non_arccorrected_bins_v, + int default_num_arccorrected_bins_v, + int num_detectors_per_ring_v, + float inner_ring_radius_v, + float average_depth_of_interaction_v, + float ring_spacing_v, + float bin_size_v, float intrinsic_tilt_v, + int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, + int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, + int num_axial_crystals_per_singles_unit_v, + int num_transaxial_crystals_per_singles_unit_v, + int num_detector_layers_v); + + void set_params(Type type_v, const std::list& list_of_names_v, + int num_rings_v, + int max_num_non_arccorrected_bins_v, + int default_num_arccorrected_bins_v, + int num_detectors_per_ring_v, + float inner_ring_radius_v, + float average_depth_of_interaction_v, + float ring_spacing_v, + float bin_size_v, float intrinsic_tilt_v, + float energy_resolution_v, + float reference_energy, + int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, + int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, + int num_axial_crystals_per_singles_unit_v, + int num_transaxial_crystals_per_singles_unit_v, + int num_detector_layers_v); - //! set all parameters + //! Overloaded with TOF stuff. void set_params(Type type_v, const std::list& list_of_names_v, - int num_rings_v, + int num_rings_v, int max_num_non_arccorrected_bins_v, int default_num_arccorrected_bins_v, int num_detectors_per_ring_v, @@ -483,13 +569,14 @@ class Scanner float average_depth_of_interaction_v, float ring_spacing_v, float bin_size_v, float intrinsic_tilt_v, - int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, + int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, int num_axial_crystals_per_singles_unit_v, int num_transaxial_crystals_per_singles_unit_v, int num_detector_layers_v, - float energy_resolution_v = -1.0f, - float reference_energy = -1.0f); + short int max_num_of_timing_bins_v, + float size_timing_bin_v, + float timing_resolution_v); }; diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBin.h b/src/include/stir/recon_buildblock/ProjMatrixByBin.h index 811167f522..a82f98a814 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBin.h +++ b/src/include/stir/recon_buildblock/ProjMatrixByBin.h @@ -225,6 +225,13 @@ class ProjMatrixByBin : ProjMatrixElemsForOneBin& ) const; + //! We need a local copy of the discretised density in order to find the + //! cartesian coordinates of each voxel. + shared_ptr > image_info_sptr; + + //! We need a local copy of the proj_data_info to get the integration boundaries. + shared_ptr proj_data_info_sptr; + //! The method to store data in the cache. void cache_proj_matrix_elems_for_one_bin( const ProjMatrixElemsForOneBin&) STIR_MUTABLE_CONST; @@ -259,13 +266,6 @@ class ProjMatrixByBin : //! 1/(2*sigma_in_mm) float r_sqrt2_gauss_sigma; - //! We need a local copy of the discretised density in order to find the - //! cartesian coordinates of each voxel. - shared_ptr > image_info_sptr; - - //! We need a local copy of the proj_data_info to get the integration boundaries. - shared_ptr proj_data_info_sptr; - //! The function which actually applies the TOF kernel on the LOR. inline void apply_tof_kernel( ProjMatrixElemsForOneBin& nonTOF_probabilities, ProjMatrixElemsForOneBin& tof_probabilities, diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl index 56e482d323..6c7590c1f3 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl +++ b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl @@ -208,8 +208,8 @@ ProjMatrixByBin::apply_tof_kernel(ProjMatrixElemsForOneBin& nonTOF_probabilities get_tof_value(low_dist, high_dist, new_value); new_value *= element_ptr->get_value(); - if (new_value <= 0.0001f) - continue; + //if (new_value <= 0.0001f) + // continue; tof_probabilities.push_back(ProjMatrixElemsForOneBin::value_type(element_ptr->get_coords(), new_value)); } @@ -219,7 +219,7 @@ void ProjMatrixByBin:: get_tof_value(float& d1, float& d2, float& val) const { - val = ( erf(d2) - erf(d1)) * 0.5; + val = ( erf(d2) - erf(d1)) * 0.5f; } END_NAMESPACE_STIR diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBinUsingRayTracing.h b/src/include/stir/recon_buildblock/ProjMatrixByBinUsingRayTracing.h index dface8d895..a274c2e6bf 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBinUsingRayTracing.h +++ b/src/include/stir/recon_buildblock/ProjMatrixByBinUsingRayTracing.h @@ -136,8 +136,8 @@ public : /*! Note that the density_info_ptr is not stored in this object. It's only used to get some info on sizes etc. */ virtual void set_up( - const shared_ptr& proj_data_info_ptr, - const shared_ptr >& density_info_ptr // TODO should be Info only + const shared_ptr& proj_data_info_sptr_v, + const shared_ptr >& density_info_sptr_v // TODO should be Info only ); //! \name If a cylindrical FOV or the whole image will be handled diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx index f79fb6c043..22522342f8 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx @@ -1,6 +1,7 @@ /* Copyright (C) 2003- 2011, Hammersmith Imanet Ltd Copyright (C) 2014, 2016, University College London + Copyright (C) 2016, University of Hull This file is part of STIR. This file is free software; you can redistribute it and/or modify @@ -29,6 +30,7 @@ #include "stir/recon_buildblock/ProjMatrixByBinUsingRayTracing.h" #include "stir/recon_buildblock/ProjMatrixElemsForOneBin.h" #include "stir/recon_buildblock/ProjectorByBinPairUsingProjMatrixByBin.h" +#include "stir/LORCoordinates.h" #include "stir/ProjDataInfoCylindricalNoArcCorr.h" #include "stir/ProjData.h" #include "stir/listmode/CListRecord.h" @@ -198,6 +200,8 @@ set_up_before_sensitivity(shared_ptr const& target_sptr) // set projector to be used for the calculations this->PM_sptr->set_up(proj_data_info_cyl_sptr->create_shared_clone(),target_sptr); + this->PM_sptr->enable_tof(proj_data_info_cyl_sptr->create_shared_clone(), this->use_tof); + shared_ptr forward_projector_ptr(new ForwardProjectorByBinUsingProjMatrixByBin(this->PM_sptr)); shared_ptr back_projector_ptr(new BackProjectorByBinUsingProjMatrixByBin(this->PM_sptr)); @@ -424,12 +428,19 @@ compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, ProjDataInfoCylindricalNoArcCorr* proj_data_no_arc_ptr = dynamic_cast (proj_data_info_cyl_sptr.get()); + CartesianCoordinate3D lor_point_1, lor_point_2; + const double start_time = this->frame_defs.get_start_time(this->current_frame_num); const double end_time = this->frame_defs.get_end_time(this->current_frame_num); long num_stored_events = 0; const float max_quotient = 10000.F; + // Putting the Bins here I avoid rellocation. + Bin measured_bin; + Bin fwd_bin; + LORAs2Points lor_points; + //go to the beginning of this frame // list_mode_data_sptr->set_get_position(start_time); // TODO implement function that will do this for a random time @@ -463,17 +474,22 @@ compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, if (record.is_event() && record.event().is_prompt()) { - Bin measured_bin; measured_bin.set_bin_value(1.0f); - record.event().get_bin(measured_bin, *proj_data_info_cyl_sptr); + this->use_tof ? record.full_event(measured_bin, *proj_data_info_cyl_sptr): + record.event().get_bin(measured_bin, *proj_data_info_cyl_sptr); + + // In theory we have already done all these checks so we can + // remove this if statement. if (measured_bin.get_bin_value() != 1.0f || measured_bin.segment_num() < proj_data_info_cyl_sptr->get_min_segment_num() || measured_bin.segment_num() > proj_data_info_cyl_sptr->get_max_segment_num() || measured_bin.tangential_pos_num() < proj_data_info_cyl_sptr->get_min_tangential_pos_num() || measured_bin.tangential_pos_num() > proj_data_info_cyl_sptr->get_max_tangential_pos_num() || measured_bin.axial_pos_num() < proj_data_info_cyl_sptr->get_min_axial_pos_num(measured_bin.segment_num()) - || measured_bin.axial_pos_num() > proj_data_info_cyl_sptr->get_max_axial_pos_num(measured_bin.segment_num())) + || measured_bin.axial_pos_num() > proj_data_info_cyl_sptr->get_max_axial_pos_num(measured_bin.segment_num()) + || measured_bin.timing_pos_num() < proj_data_info_cyl_sptr->get_min_timing_pos_num() + || measured_bin.timing_pos_num() > proj_data_info_cyl_sptr->get_max_timing_pos_num()) { continue; } @@ -493,9 +509,18 @@ compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, } } - this->PM_sptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, measured_bin); + if(this->use_tof) + { + // proj_data_no_arc_ptr->get_LOR_as_two_points(lor_point_1, lor_point_2, measured_bin); + lor_points = record.event().get_LOR(); + this->PM_sptr->get_proj_matrix_elems_for_one_bin_with_tof(proj_matrix_row, + measured_bin, + lor_points.p1(), lor_points.p2()); + } + else + this->PM_sptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, measured_bin); + //in_the_range++; - Bin fwd_bin; fwd_bin.set_bin_value(0.0f); proj_matrix_row.forward_project(fwd_bin,current_estimate); // additive sinogram diff --git a/src/recon_buildblock/ProjMatrixByBinUsingRayTracing.cxx b/src/recon_buildblock/ProjMatrixByBinUsingRayTracing.cxx index 8560fc0f42..0702526c1f 100644 --- a/src/recon_buildblock/ProjMatrixByBinUsingRayTracing.cxx +++ b/src/recon_buildblock/ProjMatrixByBinUsingRayTracing.cxx @@ -2,6 +2,7 @@ Copyright (C) 2000 PARAPET partners Copyright (C) 2000-2011, Hammersmith Imanet Ltd Copyright (C) 2013-2014, University College London + Copyright (C) 2016, University of Hull This file is part of STIR. This file is free software; you can redistribute it and/or modify @@ -23,6 +24,7 @@ \brief non-inline implementations for stir::ProjMatrixByBinUsingRayTracing + \author Nikos Efthimiou \author Mustapha Sadki \author Kris Thielemans \author PARAPET project @@ -267,26 +269,28 @@ static bool is_multiple(const float a, const float b) void ProjMatrixByBinUsingRayTracing:: set_up( - const shared_ptr& proj_data_info_ptr_v, - const shared_ptr >& density_info_ptr // TODO should be Info only + const shared_ptr& proj_data_info_sptr_v, + const shared_ptr >& density_info_sptr_v // TODO should be Info only ) { - ProjMatrixByBin::set_up(proj_data_info_ptr_v, density_info_ptr); + ProjMatrixByBin::set_up(proj_data_info_sptr_v, density_info_sptr_v); - proj_data_info_ptr= proj_data_info_ptr_v; - const VoxelsOnCartesianGrid * image_info_ptr = - dynamic_cast*> (density_info_ptr.get()); + proj_data_info_ptr= proj_data_info_sptr_v; + image_info_sptr.reset( + dynamic_cast* > (density_info_sptr_v->clone() )); +// const VoxelsOnCartesianGrid * image_info_ptr = +// dynamic_cast*> (density_info_ptr.get()); - if (image_info_ptr == NULL) + if(is_null_ptr(image_info_sptr)) error("ProjMatrixByBinUsingRayTracing initialised with a wrong type of DiscretisedDensity\n"); - voxel_size = image_info_ptr->get_voxel_size(); - origin = image_info_ptr->get_origin(); - image_info_ptr->get_regular_range(min_index, max_index); + voxel_size = image_info_sptr->get_voxel_size(); + origin = image_info_sptr->get_origin(); + image_info_sptr->get_regular_range(min_index, max_index); symmetries_sptr.reset( new DataSymmetriesForBins_PET_CartesianGrid(proj_data_info_ptr, - density_info_ptr, + density_info_sptr_v, do_symmetry_90degrees_min_phi, do_symmetry_180degrees_min_phi, do_symmetry_swap_segment, From aae2d7b0861effedd92cc2501dded21f24803136 Mon Sep 17 00:00:00 2001 From: NikEfth Date: Fri, 23 Dec 2016 20:29:34 +0000 Subject: [PATCH 035/170] Forgot the function full_event() --- src/include/stir/listmode/CListRecordROOT.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/include/stir/listmode/CListRecordROOT.h b/src/include/stir/listmode/CListRecordROOT.h index 3db20736af..8164b523cd 100644 --- a/src/include/stir/listmode/CListRecordROOT.h +++ b/src/include/stir/listmode/CListRecordROOT.h @@ -167,6 +167,12 @@ class CListRecordROOT : public CListRecord // currently no gating yet return this->time_data; } + virtual void full_event(Bin&, const ProjDataInfo&) const + { + event_data.get_bin(bin, proj_data_info); + time_data.get_bin(bin, proj_data_info); + } + bool operator==(const CListRecord& e2) const { return dynamic_cast(&e2) != 0 && From b3f7047c636f2c78a4f8df4ff3b84a17108c22ce Mon Sep 17 00:00:00 2001 From: NikEfth Date: Fri, 23 Dec 2016 20:31:37 +0000 Subject: [PATCH 036/170] small bugfix --- src/include/stir/listmode/CListRecordROOT.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/include/stir/listmode/CListRecordROOT.h b/src/include/stir/listmode/CListRecordROOT.h index 8164b523cd..5a2cb84264 100644 --- a/src/include/stir/listmode/CListRecordROOT.h +++ b/src/include/stir/listmode/CListRecordROOT.h @@ -167,7 +167,7 @@ class CListRecordROOT : public CListRecord // currently no gating yet return this->time_data; } - virtual void full_event(Bin&, const ProjDataInfo&) const + virtual void full_event(Bin& bin, const ProjDataInfo& proj_data_info) const { event_data.get_bin(bin, proj_data_info); time_data.get_bin(bin, proj_data_info); From af479ce15c0b7c46f0bc0ec416da509075621601 Mon Sep 17 00:00:00 2001 From: NikEfth Date: Fri, 23 Dec 2016 21:51:27 +0000 Subject: [PATCH 037/170] keep forgetting code. --- src/IO/InputStreamFromROOTFileForCylindricalPET.cxx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/IO/InputStreamFromROOTFileForCylindricalPET.cxx b/src/IO/InputStreamFromROOTFileForCylindricalPET.cxx index 6027f8235c..0f5682f805 100644 --- a/src/IO/InputStreamFromROOTFileForCylindricalPET.cxx +++ b/src/IO/InputStreamFromROOTFileForCylindricalPET.cxx @@ -112,10 +112,18 @@ get_next_record(CListRecordROOT& record) crystal1 += offset_dets; crystal2 += offset_dets; + short int delta_timing_bin = 0; + double delta_time = time2 - time1; + + if (delta_time >=0) + delta_timing_bin = static_cast(delta_time * least_significant_clock_bit); + else + delta_timing_bin = static_cast(delta_time * least_significant_clock_bit); + return record.init_from_data(ring1, ring2, crystal1, crystal2, - time1, time2, + time1, delta_timing_bin, event1, event2); } From bbdf71838d98e14da90fb0b9ae15e937fbd3eeee Mon Sep 17 00:00:00 2001 From: NikEfth Date: Sat, 24 Dec 2016 09:44:49 +0000 Subject: [PATCH 038/170] floats --- src/IO/InputStreamFromROOTFileForCylindricalPET.cxx | 2 +- src/include/stir/recon_buildblock/ProjMatrixByBin.inl | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/IO/InputStreamFromROOTFileForCylindricalPET.cxx b/src/IO/InputStreamFromROOTFileForCylindricalPET.cxx index 0f5682f805..0f4d687987 100644 --- a/src/IO/InputStreamFromROOTFileForCylindricalPET.cxx +++ b/src/IO/InputStreamFromROOTFileForCylindricalPET.cxx @@ -112,7 +112,7 @@ get_next_record(CListRecordROOT& record) crystal1 += offset_dets; crystal2 += offset_dets; - short int delta_timing_bin = 0; + float delta_timing_bin = 0; double delta_time = time2 - time1; if (delta_time >=0) diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl index 6c7590c1f3..3c372cf3c4 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl +++ b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl @@ -208,8 +208,6 @@ ProjMatrixByBin::apply_tof_kernel(ProjMatrixElemsForOneBin& nonTOF_probabilities get_tof_value(low_dist, high_dist, new_value); new_value *= element_ptr->get_value(); - //if (new_value <= 0.0001f) - // continue; tof_probabilities.push_back(ProjMatrixElemsForOneBin::value_type(element_ptr->get_coords(), new_value)); } From a22566fbde922a0c7423442ef2551ef4c9d99c7c Mon Sep 17 00:00:00 2001 From: NikEfth Date: Sat, 24 Dec 2016 09:50:53 +0000 Subject: [PATCH 039/170] initialisation of least_significant_clock_bit --- src/IO/InputStreamFromROOTFile.cxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/IO/InputStreamFromROOTFile.cxx b/src/IO/InputStreamFromROOTFile.cxx index db182f597d..1e78e7293c 100644 --- a/src/IO/InputStreamFromROOTFile.cxx +++ b/src/IO/InputStreamFromROOTFile.cxx @@ -74,6 +74,8 @@ InputStreamFromROOTFile::post_processing() stream_ptr->SetBranchAddress("energy2", &energy2); stream_ptr->SetBranchAddress("comptonPhantom1", &comptonphantom1); stream_ptr->SetBranchAddress("comptonPhantom2", &comptonphantom2); + + least_significant_clock_bit = 5.0e+11 / least_significant_clock_bit; return false; } From f292a2d9bfa377045f11ebd7747dff81497cd4dc Mon Sep 17 00:00:00 2001 From: NikEfth Date: Sat, 24 Dec 2016 10:27:33 +0000 Subject: [PATCH 040/170] init least_significant bit --- src/IO/InputStreamFromROOTFile.cxx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/IO/InputStreamFromROOTFile.cxx b/src/IO/InputStreamFromROOTFile.cxx index 1e78e7293c..6198a1b428 100644 --- a/src/IO/InputStreamFromROOTFile.cxx +++ b/src/IO/InputStreamFromROOTFile.cxx @@ -44,7 +44,9 @@ InputStreamFromROOTFile(std::string filename, void InputStreamFromROOTFile::set_defaults() -{} +{ + least_significant_clock_bit = 1.0; +} void InputStreamFromROOTFile::initialise_keymap() @@ -57,6 +59,7 @@ InputStreamFromROOTFile::initialise_keymap() this->parser.add_key("offset (num of detectors)", &this->offset_dets); this->parser.add_key("low energy window (keV)", &this->low_energy_window); this->parser.add_key("upper energy window (keV)", &this->up_energy_window); + this->parser.add_key("minimum timing step (in picoseconds)", &this->least_significant_clock_bit); } bool From 17898cb15212d797afaacd33de0f89ce524ab15f Mon Sep 17 00:00:00 2001 From: NikEfth Date: Sat, 24 Dec 2016 10:59:12 +0000 Subject: [PATCH 041/170] invert bins --- src/include/stir/listmode/CListRecordROOT.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/include/stir/listmode/CListRecordROOT.h b/src/include/stir/listmode/CListRecordROOT.h index 5a2cb84264..93f4183d2e 100644 --- a/src/include/stir/listmode/CListRecordROOT.h +++ b/src/include/stir/listmode/CListRecordROOT.h @@ -167,7 +167,7 @@ class CListRecordROOT : public CListRecord // currently no gating yet return this->time_data; } - virtual void full_event(Bin& bin, const ProjDataInfo& proj_data_info) const + virtual void full_event(Bin& bin, const ProjDataInfo& proj_data_int) const { event_data.get_bin(bin, proj_data_info); time_data.get_bin(bin, proj_data_info); @@ -198,9 +198,9 @@ class CListRecordROOT : public CListRecord // currently no gating yet this->event_data.is_swapped() ? this->time_data.init_from_data( - time1, delta_time) : + time1, -delta_time) : this->time_data.init_from_data( - time1, -delta_time); + time1, delta_time); // We can make a singature raw based on the two events IDs. // It is pretty unique. From fd0cb5eb4d839bba8e515f1cf1596598fcab6c07 Mon Sep 17 00:00:00 2001 From: NikEfth Date: Sat, 24 Dec 2016 23:12:14 +0000 Subject: [PATCH 042/170] :) --- src/include/stir/listmode/CListRecordROOT.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/include/stir/listmode/CListRecordROOT.h b/src/include/stir/listmode/CListRecordROOT.h index 93f4183d2e..6fccff86a8 100644 --- a/src/include/stir/listmode/CListRecordROOT.h +++ b/src/include/stir/listmode/CListRecordROOT.h @@ -167,7 +167,7 @@ class CListRecordROOT : public CListRecord // currently no gating yet return this->time_data; } - virtual void full_event(Bin& bin, const ProjDataInfo& proj_data_int) const + virtual void full_event(Bin& bin, const ProjDataInfo& proj_data_info) const { event_data.get_bin(bin, proj_data_info); time_data.get_bin(bin, proj_data_info); From 62116f3bc499303b3eab8bae5ed583a29749fdc0 Mon Sep 17 00:00:00 2001 From: NikEfth Date: Sun, 25 Dec 2016 13:35:46 +0000 Subject: [PATCH 043/170] Faster scanner for 50, 20, 10 timing resolutions --- src/buildblock/Scanner.cxx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/buildblock/Scanner.cxx b/src/buildblock/Scanner.cxx index bbcc8348f0..2741a19b72 100644 --- a/src/buildblock/Scanner.cxx +++ b/src/buildblock/Scanner.cxx @@ -281,8 +281,8 @@ Scanner::Scanner(Type scanner_type) 52, 312, 624, 424.5F, 7.0F, 4.16F, 2.17242F, 0.0F, 1, 48, 52, 13, 13, 13, 1, - (short int)(315), - (float)(13.015F), + (short int)(630), + (float)(6.507), (float)(50.0F)); // TODO bucket/singles info incorrect? 224 buckets in total, but not sure how distributed break; @@ -295,8 +295,8 @@ Scanner::Scanner(Type scanner_type) 52, 312, 624, 424.5F, 7.0F, 4.16F, 2.17242F, 0.0F, 1, 48, 52, 13, 13, 13, 1, - (short int)(315), - (float)(13.015F), + (short int)(630), + (float)(6.507), (float)(20.0F)); // TODO bucket/singles info incorrect? 224 buckets in total, but not sure how distributed break; @@ -309,8 +309,8 @@ Scanner::Scanner(Type scanner_type) 52, 312, 624, 424.5F, 7.0F, 4.16F, 2.17242F, 0.0F, 1, 48, 52, 13, 13, 13, 1, - (short int)(315), - (float)(13.015F), + (short int)(630), + (float)(6.507), (float)(10.0F)); // TODO bucket/singles info incorrect? 224 buckets in total, but not sure how distributed break; From b7f1db95999ab8988a1eaa684c228a9c67580fb7 Mon Sep 17 00:00:00 2001 From: NikEfth Date: Tue, 27 Dec 2016 09:14:32 +0000 Subject: [PATCH 044/170] Faster 100 ps scanner --- src/buildblock/Scanner.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/buildblock/Scanner.cxx b/src/buildblock/Scanner.cxx index 2741a19b72..074bd24c98 100644 --- a/src/buildblock/Scanner.cxx +++ b/src/buildblock/Scanner.cxx @@ -267,8 +267,8 @@ Scanner::Scanner(Type scanner_type) 52, 312, 624, 424.5F, 7.0F, 4.16F, 2.17242F, 0.0F, 1, 48, 52, 13, 13, 13, 1, - (short int)(315), - (float)(13.015F), + (short int)(630), + (float)(6.507F), (float)(100.0F)); // TODO bucket/singles info incorrect? 224 buckets in total, but not sure how distributed break; From 216a3cde5fc4a0f821bf7b62534d613cd3db1d80 Mon Sep 17 00:00:00 2001 From: NikEfth Date: Mon, 2 Jan 2017 23:35:34 +0000 Subject: [PATCH 045/170] TOF tests part 1 --- src/test/CMakeLists.txt | 2 + src/test/test_time_of_flight.cxx | 506 +++++++++++++++++++++++++++++++ 2 files changed, 508 insertions(+) create mode 100644 src/test/test_time_of_flight.cxx diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 358d1d675c..d39da0b304 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -35,6 +35,7 @@ Set(${dir_INVOLVED_TEST_EXE_SOURCES} test_OutputFileFormat test_linear_regression test_stir_math + test_time_of_flight # the next 2 are interactive, so we don't add a test for it, but only compile them test_display test_interpolate @@ -82,6 +83,7 @@ ADD_TEST(NAME test_stir_math ) set_tests_properties(test_stir_math PROPERTIES DEPENDS stir_math) +ADD_TEST(NAME test_time_of_flight COMMAND test_time_of_flight) # Final note: we could use TARGET_FILE to avoid the use of ${CMAKE_CURRENT_BINARY_DIR} in the other tests, but both strategies work fine. ## add tests for OutputFileFormat diff --git a/src/test/test_time_of_flight.cxx b/src/test/test_time_of_flight.cxx new file mode 100644 index 0000000000..c9f16d8bdf --- /dev/null +++ b/src/test/test_time_of_flight.cxx @@ -0,0 +1,506 @@ +/* + Copyright (C) 2016, UCL + Copyright (C) 2016, University of Hull + This file is part of STIR. + + This file is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + See STIR/LICENSE.txt for details +*/ +/*! + \ingroup test + \brief Test class for Time-Of-Flight + \author Nikos Efthimiou +*/ +#include "stir/ProjDataInfoCylindricalNoArcCorr.h" +#include "stir/recon_buildblock/ProjMatrixByBin.h" +#include "stir/recon_buildblock/ProjMatrixByBinUsingRayTracing.h" +#include "stir/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.h" +#include "stir/recon_buildblock/BackProjectorByBinUsingProjMatrixByBin.h" +#include "stir/recon_buildblock/ProjMatrixElemsForOneBin.h" +#include "stir/recon_buildblock/ProjectorByBinPair.h" +#include "stir/recon_buildblock/ProjectorByBinPairUsingSeparateProjectors.h" +#include "stir/HighResWallClockTimer.h" +#include "stir/DiscretisedDensity.h" +#include "stir/VoxelsOnCartesianGrid.h" +#include "stir/recon_buildblock/ProjMatrixElemsForOneBin.h" +#include "stir/ViewSegmentNumbers.h" +#include "stir/RelatedViewgrams.h" +//#include "stir/geometry/line_distances.h" +#include "stir/Succeeded.h" +#include "stir/shared_ptr.h" +#include "stir/RunTests.h" +#include "stir/Scanner.h" + +#include "stir/info.h" +#include "stir/warning.h" + +START_NAMESPACE_STIR + +//! A helper class to keep the combination of a view, a segment and +//! a key tight. +//! \author Nikos Efthimiou +class cache_index{ +public: + cache_index() { + view_num = 0; + seg_num = 0; + key = 0; + } + + inline bool operator==(const cache_index& Y) const + { + return view_num == Y.view_num && + seg_num == Y.seg_num && + key == Y.key; + } + + inline bool operator!=(const cache_index& Y) const + { + return !(*this == Y); + } + + inline bool operator< (const cache_index& Y) const + { + return view_num < Y.view_num && + seg_num < Y.seg_num && + key < Y.key; + } + + int view_num; + int seg_num; + boost::uint32_t key; +}; + +// Helper class. +class FloatFloat{ +public: + FloatFloat() {} + float float1; + float float2; +}; + +class TOF_Tests : public RunTests +{ +public: + void run_tests(); + +private: + + void test_tof_proj_data_info(); + + void test_tof_geometry_1(); + + void test_tof_geometry_2(); + + //! This checks peaks a specific bin, finds the LOR and applies all the + //! kernels of all available timing positions. + void test_tof_kernel_application(); + + void + export_lor(ProjMatrixElemsForOneBin& probabilities, + const CartesianCoordinate3D& point1, + const CartesianCoordinate3D& point2,int current_id); + + void + export_lor(ProjMatrixElemsForOneBin& probabilities, + const CartesianCoordinate3D& point1, + const CartesianCoordinate3D& point2,int current_id, + ProjMatrixElemsForOneBin& template_probabilities); + + shared_ptr test_scanner_sptr; + shared_ptr test_proj_data_info_sptr; + shared_ptr > test_discretised_density_sptr; + shared_ptr test_proj_matrix_sptr; + shared_ptr projector_pair_sptr; + shared_ptr symmetries_used_sptr; +}; + +void +TOF_Tests::run_tests() +{ + // New Scanner + test_scanner_sptr.reset(new Scanner(Scanner::Type_mCT_TOF)); + + // New Proj_Data_Info + const int test_tof_mashing_factor = 6; + test_proj_data_info_sptr.reset(ProjDataInfo::ProjDataInfoCTI(test_scanner_sptr, + 1,test_scanner_sptr->get_num_rings() -1, + test_scanner_sptr->get_num_detectors_per_ring()/2, + test_scanner_sptr->get_max_num_non_arccorrected_bins(), + /* arc_correction*/false)); + test_proj_data_info_sptr->set_tof_mash_factor(test_tof_mashing_factor); + + test_tof_proj_data_info(); +// test_tof_geometry_1(); + + // New Discretised Density + test_discretised_density_sptr.reset( new VoxelsOnCartesianGrid (*test_proj_data_info_sptr, 1.f, + CartesianCoordinate3D(0.f, 0.f, 0.f), + CartesianCoordinate3D(-1, -1, -1))); + // New ProjMatrix + test_proj_matrix_sptr.reset(new ProjMatrixByBinUsingRayTracing()); + dynamic_cast(test_proj_matrix_sptr.get())->set_num_tangential_LORs(1); + dynamic_cast(test_proj_matrix_sptr.get())->set_up(test_proj_data_info_sptr, test_discretised_density_sptr); + test_proj_matrix_sptr->enable_tof(test_proj_data_info_sptr); + + shared_ptr forward_projector_ptr( + new ForwardProjectorByBinUsingProjMatrixByBin(test_proj_matrix_sptr)); + shared_ptr back_projector_ptr( + new BackProjectorByBinUsingProjMatrixByBin(test_proj_matrix_sptr)); + + projector_pair_sptr.reset( + new ProjectorByBinPairUsingSeparateProjectors(forward_projector_ptr, back_projector_ptr)); + projector_pair_sptr->set_up(test_proj_data_info_sptr, test_discretised_density_sptr); + + symmetries_used_sptr.reset(projector_pair_sptr->get_symmetries_used()->clone()); + + // Deactivated it now because it takes a long time to finish. + // test_cache(); + + test_tof_kernel_application(); +} + +void +TOF_Tests::test_tof_proj_data_info() +{ + const int correct_tof_mashing_factor = 13; + const int num_timing_positions = 27; + float correct_width_of_tof_bin = test_scanner_sptr->get_size_of_timing_bin() * + test_proj_data_info_sptr->get_tof_mash_factor() * 0.299792458f; + float correct_timing_locations[num_timing_positions] = {-683.077f, -632.334f, -581.591f, -530.849f, -480.106f, -429.363f, + -378.62f, -327.877f, -277.134f, -226.391f, -175.648f, + -124.906f, -74.1626f, -23.4197f, 27.3231f, 78.066f, 128.809f, + 179.552f, 230.295f, 281.038f, 331.78f, 382.523f, 433.266f, 484.009f, + 534.752f, 585.495f, 636.238f}; + + check_if_equal(correct_tof_mashing_factor, + test_proj_data_info_sptr->get_tof_mash_factor(), "Diffent tof mash factor."); + + check_if_equal(num_timing_positions, + test_proj_data_info_sptr->get_num_timing_poss(), "Diffent in number of timing positions."); + + for (int timing_num = test_proj_data_info_sptr->get_min_timing_pos_num(), counter = 0; + timing_num <= test_proj_data_info_sptr->get_max_timing_pos_num(); ++ timing_num, counter++) + { + Bin bin(0, 0, 0, 0, timing_num, 1.f); + + check_if_equal(static_cast(correct_width_of_tof_bin), + static_cast(test_proj_data_info_sptr->get_sampling_in_k(bin)), "Error in get_sampling_in_k()"); + check_if_equal(static_cast(correct_timing_locations[counter]), + static_cast(test_proj_data_info_sptr->get_k(bin)), "Error in get_sampling_in_k()"); + } + + float total_width = test_proj_data_info_sptr->get_k(Bin(0,0,0,0,test_proj_data_info_sptr->get_max_timing_pos_num(),1.f)) + - test_proj_data_info_sptr->get_k(Bin(0,0,0,0,test_proj_data_info_sptr->get_min_timing_pos_num(),1.f)) + + test_proj_data_info_sptr->get_sampling_in_k(Bin(0,0,0,0,0,1.f)); + + set_tolerance(static_cast(0.005)); + check_if_equal(static_cast(total_width), static_cast(test_proj_data_info_sptr->get_coincidence_window_width()), + "Coincidence widths don't match."); + + +} + +void +TOF_Tests::test_tof_geometry_1() +{ + + float correct_scanner_length = test_scanner_sptr->get_ring_spacing() * + test_scanner_sptr->get_num_rings() - test_proj_data_info_sptr->get_sampling_in_m(Bin(0,0,0,0,0)); + + CartesianCoordinate3D ez1_coord0, ez2_coord0, ez3_coord0, ez4_coord0, ez5_coord0; + ProjDataInfoCylindrical* proj_data_ptr = + dynamic_cast (test_proj_data_info_sptr.get()); + + int mid_seg = test_proj_data_info_sptr->get_num_segments()/2; + + int mid_axial_0 = (test_proj_data_info_sptr->get_min_axial_pos_num(0) + + test_proj_data_info_sptr->get_max_axial_pos_num(0)) /2; + + int mid_axial_mid_seg = (test_proj_data_info_sptr->get_min_axial_pos_num(mid_seg) + + test_proj_data_info_sptr->get_max_axial_pos_num(mid_seg)) /2; + + // Some easy to validate bins: + Bin ez1_bin(0,0,0,0,0,1.f); + Bin ez2_bin(0,0,mid_axial_0,0,0,1.f); + Bin ez3_bin(mid_seg,0,mid_axial_mid_seg,0,0,1.f); + Bin ez4_bin(0,0,test_proj_data_info_sptr->get_min_axial_pos_num(0),0,0,1.f); + Bin ez5_bin(0,0,test_proj_data_info_sptr->get_max_axial_pos_num(0),0,0,1.f); + + // Get middle points +// proj_data_ptr->get_LOR_middle_point(ez1_coord0, ez1_bin); +// proj_data_ptr->get_LOR_middle_point(ez2_coord0, ez2_bin); +// proj_data_ptr->get_LOR_middle_point(ez3_coord0, ez3_bin); +// proj_data_ptr->get_LOR_middle_point(ez4_coord0, ez4_bin); +// proj_data_ptr->get_LOR_middle_point(ez5_coord0, ez5_bin); + + // axial ez1 && ez4 should be -scanner_length/2.f + check_if_equal(static_cast(ez1_coord0.z()), + static_cast(-correct_scanner_length/2.f), + "Min axial possitions of mid-points don't look " + "resonable."); + + check_if_equal(static_cast(ez4_coord0.z()), + static_cast(-correct_scanner_length/2.f), + "Min axial possitions of mid-points don't look " + "resonable."); + + // axial ez2 should be -ring_spacing/2 + check_if_equal(static_cast(ez2_coord0.z()), + static_cast(-test_scanner_sptr->get_ring_spacing()/2.f), + "[1]Central axial possitions of mid-points don't look " + "resonable."); + + // axial ez3 should be 0 + check_if_equal(static_cast(ez3_coord0.z()), + static_cast(-test_proj_data_info_sptr->get_m(Bin(mid_seg,0,0,0,0))), + "[2]Central axial possitions of mid-points don't look " + "resonable."); + + // axial ez5 should be scanner_length/2.f + check_if_equal(static_cast(ez5_coord0.z()), + static_cast(correct_scanner_length/2.f), + "Max axial possitions of mid-points don't look " + "resonable."); + + //TODO: more tests for X and Y +} + +void +TOF_Tests::test_tof_geometry_2() +{ + float correct_scanner_length = test_scanner_sptr->get_ring_spacing() * + test_scanner_sptr->get_num_rings() - test_proj_data_info_sptr->get_sampling_in_m(Bin(0,0,0,0,0)); + + CartesianCoordinate3D ez1_coord1, ez2_coord1, ez3_coord1, ez4_coord1, ez5_coord1; + CartesianCoordinate3D ez1_coord2, ez2_coord2, ez3_coord2, ez4_coord2, ez5_coord2; + ProjDataInfoCylindrical* proj_data_ptr = + dynamic_cast (test_proj_data_info_sptr.get()); + + int mid_seg = test_proj_data_info_sptr->get_num_segments()/2; + + int mid_axial_0 = (test_proj_data_info_sptr->get_min_axial_pos_num(0) + + test_proj_data_info_sptr->get_max_axial_pos_num(0)) /2; + + int mid_axial_mid_seg = (test_proj_data_info_sptr->get_min_axial_pos_num(mid_seg) + + test_proj_data_info_sptr->get_max_axial_pos_num(mid_seg)) /2; + + // Some easy to validate bins: + Bin ez1_bin(0,0,0,0,0,1.f); + Bin ez2_bin(0,0,mid_axial_0,0,0,1.f); + Bin ez3_bin(mid_seg,0,mid_axial_mid_seg,0,0,1.f); + Bin ez4_bin(0,0,test_proj_data_info_sptr->get_min_axial_pos_num(0),0,0,1.f); + Bin ez5_bin(0,0,test_proj_data_info_sptr->get_max_axial_pos_num(0),0,0,1.f); + + // Get middle points +// proj_data_ptr->get_LOR_as_two_points(ez1_coord1,ez1_coord2, ez1_bin); +// proj_data_ptr->get_LOR_as_two_points(ez2_coord1,ez2_coord2, ez2_bin); +// proj_data_ptr->get_LOR_as_two_points(ez3_coord1,ez3_coord2, ez3_bin); +// proj_data_ptr->get_LOR_as_two_points(ez4_coord1,ez4_coord2, ez4_bin); +// proj_data_ptr->get_LOR_as_two_points(ez5_coord1,ez5_coord2, ez5_bin); + + // TESTS TO COME. + + // TEST IF THE FLIPING IS OK. +} + +void +TOF_Tests::test_tof_kernel_application() +{ + int seg_num = 0; + int view_num = 0; + int axial_num = 0; + int tang_num = 0; + CartesianCoordinate3D lor_point_1, lor_point_2; + ProjMatrixElemsForOneBin proj_matrix_row; + HighResWallClockTimer t; + std::vector times_of_tofing; + + ProjDataInfoCylindrical* proj_data_ptr = + dynamic_cast (test_proj_data_info_sptr.get()); + + Bin this_bin(seg_num, view_num, axial_num, tang_num, 1.f); + proj_data_ptr->get_LOR_as_two_points(lor_point_1, lor_point_2, this_bin); + + std::cerr<< lor_point_1.x() << " " << lor_point_1.y() << " " << lor_point_1.z() << " " << + lor_point_2.x() << " " << lor_point_2.y() << " " << lor_point_2.z() << std::endl; + + t.reset(); t.start(); + test_proj_matrix_sptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, this_bin); + t.stop(); + std::cerr<<"Execution time for nonTOF: "<get_min_timing_pos_num(); + timing_num <= test_proj_data_info_sptr->get_max_timing_pos_num(); ++ timing_num) + { + ProjMatrixElemsForOneBin new_proj_matrix_row; + Bin bin(seg_num, view_num, axial_num, tang_num, timing_num, 1.f); + + t.reset(); t.start(); + test_proj_matrix_sptr->get_proj_matrix_elems_for_one_bin_with_tof(new_proj_matrix_row, + bin, + lor_point_1, lor_point_2); + t.stop(); + times_of_tofing.push_back(t.value()); + export_lor(new_proj_matrix_row, + lor_point_1, lor_point_2, timing_num, + proj_matrix_row); + } + + double mean = 0.0; + for (unsigned i = 0; i < times_of_tofing.size(); i++) + mean += times_of_tofing.at(i); + + mean /= (times_of_tofing.size()); + + double s=0.0; + for (unsigned i = 0; i < times_of_tofing.size(); i++) + s += (times_of_tofing.at(i) - mean) * (times_of_tofing.at(i) - mean) / (times_of_tofing.size()-1); + + s = std::sqrt(s); + + std::cerr<<" Execution time for TOF: "<& point1, + const CartesianCoordinate3D& point2, int current_id) +{ + std::ofstream myfile; + std::string file_name = "glor_" + std::to_string(current_id) + ".txt"; + myfile.open (file_name.c_str()); + + CartesianCoordinate3D voxel_center; + + std::vector lor_to_export; + lor_to_export.reserve(probabilities.size()); + + ProjMatrixElemsForOneBin::iterator element_ptr = probabilities.begin(); + while (element_ptr != probabilities.end()) + { + voxel_center = + test_discretised_density_sptr->get_physical_coordinates_for_indices (element_ptr->get_coords()); + + if(voxel_center.z() == 0.f) + { + project_point_on_a_line(point1, point2, voxel_center ); + + float d1 = std::sqrt((point1.x() - voxel_center.x()) *(point1.x() - voxel_center.x()) + + (point1.y() - voxel_center.y()) *(point1.y() - voxel_center.y()) + + (point1.z() - voxel_center.z()) *(point1.z() - voxel_center.z())); + + float d2 = std::sqrt( (point2.x() - voxel_center.x()) *(point2.x() - voxel_center.x()) + + (point2.y() - voxel_center.y()) *(point2.y() - voxel_center.y()) + + (point2.z() - voxel_center.z()) *(point2.z() - voxel_center.z())); + + + float d12 = (d2 - d1) * 0.5f; + + std::cerr<< voxel_center.x() << " " << voxel_center.y() << " " << voxel_center.z() << " " << + d1 << " " << d2 << " " << d12 <get_value(); + lor_to_export.push_back(tmp); +} + ++element_ptr; + } + + for (unsigned int i = 0; i < lor_to_export.size(); i++) + myfile << lor_to_export.at(i).float1 << " " << lor_to_export.at(i).float2 << std::endl; + + myfile << std::endl; + myfile.close(); +} + +void +TOF_Tests:: +export_lor(ProjMatrixElemsForOneBin& probabilities, + const CartesianCoordinate3D& point1, + const CartesianCoordinate3D& point2, int current_id, + ProjMatrixElemsForOneBin& template_probabilities) +{ + std::ofstream myfile; + std::string file_name = "glor_" + std::to_string(current_id) + ".txt"; + myfile.open (file_name.c_str()); + + CartesianCoordinate3D voxel_center; + + std::vector lor_to_export; + lor_to_export.reserve(template_probabilities.size()); + + ProjMatrixElemsForOneBin::iterator tmpl_element_ptr = template_probabilities.begin(); + while (tmpl_element_ptr != template_probabilities.end()) + { + voxel_center = + test_discretised_density_sptr->get_physical_coordinates_for_indices (tmpl_element_ptr->get_coords()); + if(voxel_center.z() == 0.f) + { + project_point_on_a_line(point1, point2, voxel_center ); + + float d1 = std::sqrt((point1.x() - voxel_center.x()) *(point1.x() - voxel_center.x()) + + (point1.y() - voxel_center.y()) *(point1.y() - voxel_center.y()) + + (point1.z() - voxel_center.z()) *(point1.z() - voxel_center.z())); + + float d2 = std::sqrt( (point2.x() - voxel_center.x()) *(point2.x() - voxel_center.x()) + + (point2.y() - voxel_center.y()) *(point2.y() - voxel_center.y()) + + (point2.z() - voxel_center.z()) *(point2.z() - voxel_center.z())); + + float d12 = (d2 - d1) * 0.5f; + + FloatFloat tmp; + tmp.float1 = d12; + + ProjMatrixElemsForOneBin::iterator element_ptr = probabilities.begin(); + bool found = false; + + while (element_ptr != probabilities.end()) + { + if (element_ptr->get_coords() == tmpl_element_ptr->get_coords()) + { + tmp.float2 = element_ptr->get_value(); + found = true; + break; + } + ++element_ptr; + } + + if (!found) + tmp.float2 = 0.f; + + + lor_to_export.push_back(tmp); +} + ++tmpl_element_ptr; + } + + for (unsigned int i = 0; i < lor_to_export.size(); i++) + myfile << lor_to_export.at(i).float1 << " " << lor_to_export.at(i).float2 << std::endl; + + myfile << std::endl; + myfile.close(); +} + +END_NAMESPACE_STIR + +int main() +{ + USING_NAMESPACE_STIR + TOF_Tests tests; + tests.run_tests(); + return tests.main_return_value(); +} From 54fdbbae59b959dac8d7b6295e6fdc721be51578 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Mon, 16 Jan 2017 18:27:24 +0000 Subject: [PATCH 046/170] Correction on ROOT half_block rotation bug. When the default constructor was used, the half_block addtion wasn't applied. --- ...putStreamFromROOTFileForCylindricalPET.cxx | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/IO/InputStreamFromROOTFileForCylindricalPET.cxx b/src/IO/InputStreamFromROOTFileForCylindricalPET.cxx index 6027f8235c..b4bc67221e 100644 --- a/src/IO/InputStreamFromROOTFileForCylindricalPET.cxx +++ b/src/IO/InputStreamFromROOTFileForCylindricalPET.cxx @@ -25,7 +25,15 @@ InputStreamFromROOTFileForCylindricalPET::registered_name = InputStreamFromROOTFileForCylindricalPET:: InputStreamFromROOTFileForCylindricalPET(): base_type() -{} +{ + filename = ""; + chain_name = ""; + exclude_scattered = false; + exclude_randoms = false; + low_energy_window = 0; + up_energy_window = 1000; + offset_dets = 0; +} InputStreamFromROOTFileForCylindricalPET:: InputStreamFromROOTFileForCylindricalPET(std::string _filename, @@ -105,8 +113,9 @@ get_next_record(CListRecordROOT& record) // GATE counts crystal ID =0 the most negative. Therefore // ID = 0 should be negative, in Rsector 0 and the mid crystal ID be 0 . - crystal1 -= half_block; - crystal2 -= half_block; + // Moved to post_processings(). + //crystal1 -= half_block; + //crystal2 -= half_block; // Add offset crystal1 += offset_dets; @@ -170,6 +179,11 @@ post_processing() if (nentries == 0) error("The total number of entries in the ROOT file is zero. Abort."); + half_block = module_repeater_y * submodule_repeater_y * crystal_repeater_y / 2; + if (half_block < 0 ) + half_block = 0; + + offset_dets -= half_block; return false; } From 5be23f0f0cd5603672bf3d1e304f6cfed3091df5 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Tue, 17 Jan 2017 13:01:12 +0000 Subject: [PATCH 047/170] Simplification of the TOF algorithm LUT is used to get the TOF bin. --- src/IO/InputStreamFromROOTFile.cxx | 3 +- ...putStreamFromROOTFileForCylindricalPET.cxx | 12 +-- src/buildblock/ProjDataInfo.cxx | 83 +++---------------- src/include/stir/ProjDataInfo.h | 14 +++- src/include/stir/ProjDataInfo.inl | 15 ++++ src/include/stir/listmode/CListRecordROOT.h | 25 +++--- 6 files changed, 55 insertions(+), 97 deletions(-) diff --git a/src/IO/InputStreamFromROOTFile.cxx b/src/IO/InputStreamFromROOTFile.cxx index 6198a1b428..954229f1e6 100644 --- a/src/IO/InputStreamFromROOTFile.cxx +++ b/src/IO/InputStreamFromROOTFile.cxx @@ -1,6 +1,7 @@ /* * Copyright (C) 2015, 2016 University of Leeds Copyright (C) 2016, UCL + Copyright (C) 2016, 2017 University of Hull This file is part of STIR. This file is free software; you can redistribute it and/or modify @@ -78,7 +79,7 @@ InputStreamFromROOTFile::post_processing() stream_ptr->SetBranchAddress("comptonPhantom1", &comptonphantom1); stream_ptr->SetBranchAddress("comptonPhantom2", &comptonphantom2); - least_significant_clock_bit = 5.0e+11 / least_significant_clock_bit; + least_significant_clock_bit = 5.0e+11;// / least_significant_clock_bit; return false; } diff --git a/src/IO/InputStreamFromROOTFileForCylindricalPET.cxx b/src/IO/InputStreamFromROOTFileForCylindricalPET.cxx index ada155d6d2..d6cc9e15e5 100644 --- a/src/IO/InputStreamFromROOTFileForCylindricalPET.cxx +++ b/src/IO/InputStreamFromROOTFileForCylindricalPET.cxx @@ -59,7 +59,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 = static_cast( (module_repeater_y * submodule_repeater_y * crystal_repeater_y) / 2); if (half_block < 0 ) half_block = 0; } @@ -121,13 +121,7 @@ get_next_record(CListRecordROOT& record) crystal1 += offset_dets; crystal2 += offset_dets; - float delta_timing_bin = 0; - double delta_time = time2 - time1; - - if (delta_time >=0) - delta_timing_bin = static_cast(delta_time * least_significant_clock_bit); - else - delta_timing_bin = static_cast(delta_time * least_significant_clock_bit); + double delta_timing_bin = (time2 - time1) * least_significant_clock_bit; return record.init_from_data(ring1, ring2, @@ -187,7 +181,7 @@ post_processing() if (nentries == 0) error("The total number of entries in the ROOT file is zero. Abort."); - half_block = module_repeater_y * submodule_repeater_y * crystal_repeater_y / 2; + half_block = static_cast( (module_repeater_y * submodule_repeater_y * crystal_repeater_y) / 2); if (half_block < 0 ) half_block = 0; diff --git a/src/buildblock/ProjDataInfo.cxx b/src/buildblock/ProjDataInfo.cxx index 702c7418b5..1603d74825 100644 --- a/src/buildblock/ProjDataInfo.cxx +++ b/src/buildblock/ProjDataInfo.cxx @@ -191,35 +191,17 @@ ProjDataInfo::set_tof_mash_factor(const int new_num) "the scanner's number of max timing bins. Abort."); tof_mash_factor = new_num; - timing_increament_in_mm = tof_mash_factor * scanner_ptr->get_size_of_timing_bin() * 0.299792458f; - - // limit to the size of the diameter. - - Bin b; - - float lowest_t = get_min_tangential_pos_num(); - float highest_t = get_max_tangential_pos_num(); - - b.tangential_pos_num() = lowest_t; - float k = get_sampling_in_t(b); - b.tangential_pos_num() = highest_t; - float l = get_sampling_in_t(b); - - float lowest_boundary = lowest_t * k; - float highest_boundary = highest_t * l; - - int num_tof_positions_in_FOV = static_cast(((highest_boundary - lowest_boundary) / ( timing_increament_in_mm))); + timing_increament_in_mm = (tof_mash_factor * scanner_ptr->get_size_of_timing_bin() * 0.299792458f); min_timing_pos_num = - (scanner_ptr->get_num_max_of_timing_bins() / tof_mash_factor)/2; max_timing_pos_num = min_timing_pos_num + (scanner_ptr->get_num_max_of_timing_bins() / tof_mash_factor); - //min_timing_pos_num = -num_tof_positions_in_FOV/2; - //max_timing_pos_num = min_timing_pos_num + num_tof_positions_in_FOV; - info(boost::format("bound: %1%: %2% - %3% | %4%") %lowest_boundary % highest_boundary %num_tof_positions_in_FOV - %timing_increament_in_mm); + num_tof_bins = max_timing_pos_num - min_timing_pos_num; // Upper and lower boundaries of the timing poss; - timing_bin_boundaries.grow(min_timing_pos_num, max_timing_pos_num); + timing_bin_boundaries_mm.grow(min_timing_pos_num, max_timing_pos_num); + + timing_bin_boundaries_ps.grow(min_timing_pos_num, max_timing_pos_num); for (int i = min_timing_pos_num; i <= max_timing_pos_num; ++i ) { @@ -229,55 +211,12 @@ ProjDataInfo::set_tof_mash_factor(const int new_num) float cur_low = get_k(bin); float cur_high = get_k(bin) + get_sampling_in_k(bin); -// info(boost::format("tic: %1%: %2%") %cur_low % cur_high); - -// if (cur_low < lowest_boundary) -// cur_low = lowest_boundary; -// else if (cur_low > highest_boundary) -// cur_low = highest_boundary; - -// if (cur_high < lowest_boundary) -// cur_high = lowest_boundary; -// else if (cur_high > highest_boundary) -// cur_high = highest_boundary; - -// info(boost::format("tac: %1%: %2%") % cur_low % cur_high); -// if (cur_low< 0 || cur_high < 0 ) -// { -// if (cur_low < lowest_boundary && cur_high > lowest_boundary) -// { -// timing_bin_boundaries[i].low_lim = lowest_boundary; -// timing_bin_boundaries[i].high_lim = cur_high; -// } -// else if (cur_low >= lowest_boundary && cur_high >= lowest_boundary) -// { -// timing_bin_boundaries[i].low_lim = cur_low; -// timing_bin_boundaries[i].high_lim = cur_high; -// } -// } -// else -// { -// if (cur_high > highest_boundary && cur_low < highest_boundary) -// { -// timing_bin_boundaries[i].low_lim = cur_low; -// timing_bin_boundaries[i].high_lim = highest_boundary; -// } -// else if (cur_low <= highest_boundary && cur_high <= highest_boundary) -// { -// timing_bin_boundaries[i].low_lim = cur_low; -// timing_bin_boundaries[i].high_lim = cur_high; -// } -// } - - timing_bin_boundaries[i].low_lim = cur_low; - timing_bin_boundaries[i].high_lim = cur_high; - float lowt = (timing_bin_boundaries[i].low_lim / 0.299792458f ) ; - float hight = ( timing_bin_boundaries[i].high_lim/ 0.299792458f); - info(boost::format("Tbin %1%: %2% - %3% mm (%4% - %5% ps) = %6%") %i % timing_bin_boundaries[i].low_lim % timing_bin_boundaries[i].high_lim - % lowt% hight % get_sampling_in_k(bin)); - // Go to natural coordinates - opted out. - // timing_bin_boundaries[i].low_lim *= r_sqrtdPI_gauss_sigma; - // timing_bin_boundaries[i].high_lim *= r_sqrtdPI_gauss_sigma; + timing_bin_boundaries_mm[i].low_lim = cur_low; + timing_bin_boundaries_mm[i].high_lim = cur_high; + timing_bin_boundaries_ps[i].low_lim = (timing_bin_boundaries_mm[i].low_lim * 3.33564095198f ) ; + timing_bin_boundaries_ps[i].high_lim = ( timing_bin_boundaries_mm[i].high_lim * 3.33564095198f); + info(boost::format("Tbin %1%: %2% - %3% mm (%4% - %5% ps) = %6%") %i % timing_bin_boundaries_mm[i].low_lim % timing_bin_boundaries_mm[i].high_lim + % timing_bin_boundaries_ps[i].low_lim % timing_bin_boundaries_ps[i].high_lim % get_sampling_in_k(bin)); } } else diff --git a/src/include/stir/ProjDataInfo.h b/src/include/stir/ProjDataInfo.h index 0d6af07307..6bb6c0ddeb 100644 --- a/src/include/stir/ProjDataInfo.h +++ b/src/include/stir/ProjDataInfo.h @@ -181,6 +181,10 @@ class ProjDataInfo inline int get_num_views() const; //! Get number of tangential positions inline int get_num_tangential_poss() const; + //! Get number of tof bins + inline int get_tof_bin(double delta) const; + //! Get number of TOF bins + inline int get_num_tof_poss() const; //! Get minimum segment number inline int get_min_segment_num() const; //! Get maximum segment number @@ -199,9 +203,9 @@ class ProjDataInfo inline int get_max_tangential_pos_num() const; //! Get number of TOF positions inline int get_num_timing_poss() const; - //! Get TOG mash factor + //! Get TOF mash factor inline int get_tof_mash_factor() const; - //! Get the index of the first timing position + //! Get the index of the first timing position inline int get_min_timing_pos_num() const; //! Get the index of the last timgin position. inline int get_max_timing_pos_num() const; @@ -363,7 +367,9 @@ class ProjDataInfo struct Float1Float2 { float low_lim; float high_lim; }; //! Vector which holds the lower and higher boundary for each timing position, for faster access. - mutable VectorWithOffset timing_bin_boundaries; + mutable VectorWithOffset timing_bin_boundaries_mm; + + mutable VectorWithOffset timing_bin_boundaries_ps; protected: virtual bool blindly_equals(const root_type * const) const = 0; @@ -382,6 +388,8 @@ class ProjDataInfo int tof_mash_factor; //! Finally (with any mashing factor) timing bin increament. float timing_increament_in_mm; + //! Number of tof bins (TOF mash factor applied) + int num_tof_bins; VectorWithOffset min_axial_pos_per_seg; VectorWithOffset max_axial_pos_per_seg; diff --git a/src/include/stir/ProjDataInfo.inl b/src/include/stir/ProjDataInfo.inl index d1d6fbe0c2..ca82f61fcd 100644 --- a/src/include/stir/ProjDataInfo.inl +++ b/src/include/stir/ProjDataInfo.inl @@ -61,6 +61,21 @@ int ProjDataInfo::get_num_timing_poss() const { return max_timing_pos_num - min_timing_pos_num +1; } +int +ProjDataInfo::get_num_tof_poss() const +{ return num_tof_bins; } + +int +ProjDataInfo::get_tof_bin(double delta) const +{ + for (int i = min_timing_pos_num; i < max_timing_pos_num; i++) + { + if ( delta > timing_bin_boundaries_ps[i].low_lim && + delta < timing_bin_boundaries_ps[i].high_lim) + return i; + } +} + int ProjDataInfo::get_tof_mash_factor() const { return tof_mash_factor; } diff --git a/src/include/stir/listmode/CListRecordROOT.h b/src/include/stir/listmode/CListRecordROOT.h index 6fccff86a8..658b56b915 100644 --- a/src/include/stir/listmode/CListRecordROOT.h +++ b/src/include/stir/listmode/CListRecordROOT.h @@ -115,15 +115,16 @@ class CListTimeROOT : public CListTime virtual inline void get_bin(Bin& bin, const ProjDataInfo& proj_data_info) const { - delta_time > 0 ? - bin.timing_pos_num() = static_cast ( ( delta_time / proj_data_info.get_tof_mash_factor()) + 0.5) - : bin.timing_pos_num() = static_cast ( ( delta_time / proj_data_info.get_tof_mash_factor()) - 0.5); - - if (bin.timing_pos_num() < proj_data_info.get_min_timing_pos_num() || - bin.timing_pos_num() > proj_data_info.get_max_timing_pos_num()) - { - bin.set_bin_value(-1.f); - } +// delta_time >= 0.0 ? +// bin.timing_pos_num() = static_cast ( ( delta_time / proj_data_info.get_num_tof_poss()) + 0.5) +// : bin.timing_pos_num() = static_cast ( ( delta_time / proj_data_info.get_num_tof_poss()) - 0.5); + +// if (bin.timing_pos_num() < proj_data_info.get_min_timing_pos_num() || +// bin.timing_pos_num() > proj_data_info.get_max_timing_pos_num()) +// { +// bin.set_bin_value(-1.f); +// } + bin.timing_pos_num() = proj_data_info.get_tof_bin(delta_time); } private: @@ -131,9 +132,9 @@ class CListTimeROOT : public CListTime //! //! \brief timeA //! \details The detection time of the first of the two photons, in seconds - float timeA; + double timeA; - float delta_time; + double delta_time; }; //! A class for a general element of a listmode file for a Siemens scanner using the ROOT files @@ -188,7 +189,7 @@ class CListRecordROOT : public CListRecord // currently no gating yet const int& ring2, const int& crystal1, const int& crystal2, - float time1, float delta_time, + const double& time1, const double& delta_time, const int& event1, const int& event2) { /// \warning ROOT data are time and event at the same time. From d257628e68fc5e5bcd7254338cb14d597a14b9eb Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Tue, 17 Jan 2017 13:30:40 +0000 Subject: [PATCH 048/170] New scanners. Some refs as inputs. threshold at 5.5 stds --- src/buildblock/Scanner.cxx | 630 +++++++++--------- src/include/stir/ProjDataInfo.h | 2 +- src/include/stir/ProjDataInfo.inl | 2 +- .../stir/recon_buildblock/ProjMatrixByBin.inl | 9 +- 4 files changed, 323 insertions(+), 320 deletions(-) diff --git a/src/buildblock/Scanner.cxx b/src/buildblock/Scanner.cxx index 074bd24c98..5d3713b890 100644 --- a/src/buildblock/Scanner.cxx +++ b/src/buildblock/Scanner.cxx @@ -60,19 +60,19 @@ using std::list; START_NAMESPACE_STIR // local convenience functions to make a list of strings -static list +static list string_list(const string&); -static list +static list string_list(const string&, const string&); -static list +static list string_list(const string&, const string&, const string&); -static list +static list string_list(const string&, const string&, const string&, const string&); - - + + Scanner::Scanner(Type scanner_type) { @@ -81,47 +81,47 @@ Scanner::Scanner(Type scanner_type) // Type type_v, // const list& list_of_names_v, // - // int num_rings_v, - // int max_num_non_arccorrected_bins_v, - // (optional) int default_num_arccorrected_bins_v, + // int num_rings_v, + // int max_num_non_arccorrected_bins_v, + // (optional) int default_num_arccorrected_bins_v, // int num_detectors_per_ring_v, // // float inner_ring_radius_v, // float average_depth_of_interaction_v, // float ring_spacing_v, - // float bin_size_v, + // float bin_size_v, // float intrinsic_tilt_v, // - // int num_axial_blocks_per_bucket_v, - // int num_transaxial_blocks_per_bucket_v, - // int num_axial_crystals_per_block_v, + // int num_axial_blocks_per_bucket_v, + // int num_transaxial_blocks_per_bucket_v, + // int num_axial_crystals_per_block_v, // int num_transaxial_crystals_per_block_v, // int num_axial_crystals_per_singles_unit_v, // int num_transaxial_crystals_per_singles_unit_v, // int num_detector_layers_v // - + /* for CTI scanners (at least upto 966): - before arc-correction, central_bin_size ~= ring_radius* pi/num_detectors - num_transaxial_crystals_per_singles_unit= + before arc-correction, central_bin_size ~= ring_radius* pi/num_detectors + num_transaxial_crystals_per_singles_unit= transaxial_blocks_per_bucket*transaxial_crystals_per_block - num_axial_crystals_per_singles_unit= + num_axial_crystals_per_singles_unit= axial_crystals_per_block * x where x=1 except for the 966 where x=2 */ - + switch ( scanner_type ) { case E931: // KT 25/01/2002 corrected ring_spacing - set_params(E931, string_list("ECAT 931"), - 8, 192, 2 * 256, - 510.0F, 7.0F, 13.5F, 3.129F, 0.0F, + set_params(E931, string_list("ECAT 931"), + 8, 192, 2 * 256, + 510.0F, 7.0F, 13.5F, 3.129F, 0.0F, 2, 4, 4, 8, 4, 8 * 4, 1); // 16 BUCKETS per ring in TWO rings - i.e. 32 buckets in total @@ -129,60 +129,60 @@ Scanner::Scanner(Type scanner_type) case E951: - set_params(E951, string_list("ECAT 951"), - 16, 192, 2 * 256, - 510.0F, 7.0F, 6.75F, 3.12932F, 0.0F, + set_params(E951, string_list("ECAT 951"), + 16, 192, 2 * 256, + 510.0F, 7.0F, 6.75F, 3.12932F, 0.0F, 1, 4, 8, 8, 8, 8 * 4, 1); break; case E953: - set_params(E953, string_list("ECAT 953"), - 16, 160, 2 * 192, - 382.5F, 7.0F, 6.75F, 3.12932F, static_cast(15.*_PI/180), + set_params(E953, string_list("ECAT 953"), + 16, 160, 2 * 192, + 382.5F, 7.0F, 6.75F, 3.12932F, static_cast(15.*_PI/180), 1, 4, 8, 8, 8, 8 * 4, 1); break; case E921: - set_params(E921, string_list("ECAT 921", "ECAT EXACT", "EXACT"), - 24, 192, 2* 192, + set_params(E921, string_list("ECAT 921", "ECAT EXACT", "EXACT"), + 24, 192, 2* 192, 412.5F, 7.0F, 6.75F, 3.375F, static_cast(15.*_PI/180), 1, 4, 8, 8, 8, 8 * 4, 1); break; case E925: - - set_params(E925, string_list("ECAT 925", "ECAT ART"), - 24, 192, 2* 192, + + set_params(E925, string_list("ECAT 925", "ECAT ART"), + 24, 192, 2* 192, 412.5F, 7.0F, 6.75F, 3.375F, static_cast(15.*_PI/180), 3, 4, 8, 8, 8, 8 * 4, 1); break; - + case E961: - set_params(E961,string_list("ECAT 961", "ECAT HR"), - 24, 336, 2* 392, + set_params(E961,string_list("ECAT 961", "ECAT HR"), + 24, 336, 2* 392, 412.0F, 7.0F, 6.25F, 1.650F, static_cast(13.*_PI/180), 1, 8, 8, 7, 8, 7 * 8, 1); - break; + break; case E962: - set_params(E962,string_list("ECAT 962","ECAT HR+"), - 32, 288, 2* 288, - 412.0F, 7.0F, 4.85F, 2.25F, 0.0F, + set_params(E962,string_list("ECAT 962","ECAT HR+"), + 32, 288, 2* 288, + 412.0F, 7.0F, 4.85F, 2.25F, 0.0F, 4, 3, 8, 8, 8, 8 * 3, 1); break; case E966: - set_params(E966, string_list("ECAT EXACT 3D", "EXACT 3D", "ECAT HR++","ECAT 966"), - 48, 288, 2* 288, - 412.0F, 7.0F, 4.850F, 2.250F, 0.0, + set_params(E966, string_list("ECAT EXACT 3D", "EXACT 3D", "ECAT HR++","ECAT 966"), + 48, 288, 2* 288, + 412.0F, 7.0F, 4.850F, 2.250F, 0.0, 6, 2, 8, 8, 2 * 8, 8 * 2, 1); - break; + break; case E1080: // data added by Robert Barnett, Westmead Hospital, Sydney @@ -190,7 +190,7 @@ Scanner::Scanner(Type scanner_type) 41, 336, 2* 336, 412.0F, 7.0F, 4.0F, 2.000F, 0.0F, 1, 2, 41, 14, 41, 14, 1); - // Transaxial blocks have 13 physical crystals and a gap at the + // Transaxial blocks have 13 physical crystals and a gap at the // 140th crystal where the counts are zero. // There are 39 rings with 13 axial crystals per block. Two virtual // rings are added, but contain counts after applying axial compression. @@ -198,7 +198,7 @@ Scanner::Scanner(Type scanner_type) case Siemens_mMR: // 8x8 blocks, 1 virtual "crystal", 56 blocks along the ring, 8 blocks in axial direction - // Transaxial blocks have 8 physical crystals and a gap at the + // Transaxial blocks have 8 physical crystals and a gap at the // 9th crystal where the counts are zero. set_params(Siemens_mMR, string_list("Siemens mMR", "mMR", "2008"), 64, 344, 2* 252, @@ -225,8 +225,8 @@ Scanner::Scanner(Type scanner_type) 52, 312, 624, 424.5F, 7.0F, 4.16F, 2.17242F, 0.0F, 1, 48, 52, 13, 13, 13, 1, - (short int)(315), - (float)(13.015F), + (short int)(410), + (float)(10.0F), (float)(600.0F)); // TODO bucket/singles info incorrect? 224 buckets in total, but not sure how distributed break; @@ -239,8 +239,8 @@ Scanner::Scanner(Type scanner_type) 52, 312, 624, 424.5F, 7.0F, 4.16F, 2.17242F, 0.0F, 1, 48, 52, 13, 13, 13, 1, - (short int)(315), - (float)(13.015F), + (short int)(410), + (float)(10.0F), (float)(400.0F)); // TODO bucket/singles info incorrect? 224 buckets in total, but not sure how distributed break; @@ -253,8 +253,8 @@ Scanner::Scanner(Type scanner_type) 52, 312, 624, 424.5F, 7.0F, 4.16F, 2.17242F, 0.0F, 1, 48, 52, 13, 13, 13, 1, - (short int)(315), - (float)(13.015F), + (short int)(410), + (float)(10.0F), (float)(200.0F)); // TODO bucket/singles info incorrect? 224 buckets in total, but not sure how distributed break; @@ -267,8 +267,8 @@ Scanner::Scanner(Type scanner_type) 52, 312, 624, 424.5F, 7.0F, 4.16F, 2.17242F, 0.0F, 1, 48, 52, 13, 13, 13, 1, - (short int)(630), - (float)(6.507F), + (short int)(820), + (float)(5.00F), (float)(100.0F)); // TODO bucket/singles info incorrect? 224 buckets in total, but not sure how distributed break; @@ -281,8 +281,8 @@ Scanner::Scanner(Type scanner_type) 52, 312, 624, 424.5F, 7.0F, 4.16F, 2.17242F, 0.0F, 1, 48, 52, 13, 13, 13, 1, - (short int)(630), - (float)(6.507), + (short int)(820), + (float)(5.0F), (float)(50.0F)); // TODO bucket/singles info incorrect? 224 buckets in total, but not sure how distributed break; @@ -295,8 +295,8 @@ Scanner::Scanner(Type scanner_type) 52, 312, 624, 424.5F, 7.0F, 4.16F, 2.17242F, 0.0F, 1, 48, 52, 13, 13, 13, 1, - (short int)(630), - (float)(6.507), + (short int)(1025), + (float)(1.F), (float)(20.0F)); // TODO bucket/singles info incorrect? 224 buckets in total, but not sure how distributed break; @@ -309,137 +309,137 @@ Scanner::Scanner(Type scanner_type) 52, 312, 624, 424.5F, 7.0F, 4.16F, 2.17242F, 0.0F, 1, 48, 52, 13, 13, 13, 1, - (short int)(630), - (float)(6.507), + (short int)(1025), + (float)(1.F), (float)(10.0F)); // TODO bucket/singles info incorrect? 224 buckets in total, but not sure how distributed break; case RPT: - - set_params(RPT, string_list("PRT-1", "RPT"), - 16, 128, 2 * 192, - 380.0F - 7.0F, 7.0F, 6.75F, 3.1088F, 0.0F, + + set_params(RPT, string_list("PRT-1", "RPT"), + 16, 128, 2 * 192, + 380.0F - 7.0F, 7.0F, 6.75F, 3.1088F, 0.0F, 1, 4, 8, 8, 8, 32, 1); // Default 7.0mm average interaction depth. // This 7mm taken off the inner ring radius so that the effective radius remains 380mm - break; + break; case RATPET: - - set_params(RATPET, string_list("RATPET"), - 8, 56, 2 * 56, - 115 / 2.F, 7.0F, 6.25F, 1.65F, 0.0F, + + set_params(RATPET, string_list("RATPET"), + 8, 56, 2 * 56, + 115 / 2.F, 7.0F, 6.25F, 1.65F, 0.0F, 1, 16, 8, 7, 8, 0, 1); // HR block, 4 buckets per ring - + // Default 7.0mm average interaction depth. - // 8 x 0 crystals per singles unit because not known + // 8 x 0 crystals per singles unit because not known // although likely transaxial_blocks_per_bucket*transaxial_crystals_per_block break; case PANDA: - - set_params(PANDA, string_list("PANDA"), - 1 /*NumRings*/, 512 /*MaxBinsNonArcCor*/, 512 /*MaxBinsArcCor*/, 2048 /*NumDetPerRing*/, + + set_params(PANDA, string_list("PANDA"), + 1 /*NumRings*/, 512 /*MaxBinsNonArcCor*/, 512 /*MaxBinsArcCor*/, 2048 /*NumDetPerRing*/, /*MeanInnerRadius*/ 75.5/2.F, /*AverageDoI*/ 10.F, /*Ring Spacing*/ 3.F, /*BinSize*/ 0.1F, /*IntrinsicTilt*/ 0.F, 1, 1, 1, 1, 0, 0, 1); break; - + case nanoPET: - - set_params(nanoPET, string_list("nanoPET"), /*Modelling the gap with one fake crystal */ - 81, 39*3, /* We could also model gaps in the future as one detector so 39->39+1, while 1 (point source), 3 (mouse) or 5 (rats) */ - 39*3, /* Just put the same with NonArcCor for now*/ - 12 * 39, 174.F, 5.0F, 1.17F, 1.17F, /* Actual size is 1.12 and 0.05 is the thickness of the optical reflector */ 0.0F, /* not sure for this */ - 0,0,0,0,0,0, 1); - break; + + set_params(nanoPET, string_list("nanoPET"), /*Modelling the gap with one fake crystal */ + 81, 39*3, /* We could also model gaps in the future as one detector so 39->39+1, while 1 (point source), 3 (mouse) or 5 (rats) */ + 39*3, /* Just put the same with NonArcCor for now*/ + 12 * 39, 174.F, 5.0F, 1.17F, 1.17F, /* Actual size is 1.12 and 0.05 is the thickness of the optical reflector */ 0.0F, /* not sure for this */ + 0,0,0,0,0,0, 1); + break; case HYPERimage: - - set_params(HYPERimage, string_list("HYPERimage"), /*Modelling the gap with one fake crystal */ - 22, 239, 245, - 490, 103.97F, 3.0F, 1.4F, 1.4F, /* Actual size is 1.3667 and assume 0.0333 is the thickness of the optical reflector */ 0.F, - 0,0,0,0,0,0,1); - break; - - + + set_params(HYPERimage, string_list("HYPERimage"), /*Modelling the gap with one fake crystal */ + 22, 239, 245, + 490, 103.97F, 3.0F, 1.4F, 1.4F, /* Actual size is 1.3667 and assume 0.0333 is the thickness of the optical reflector */ 0.F, + 0,0,0,0,0,0,1); + break; + + case Advance: - - // 283 bins (non-uniform sampling) + + // 283 bins (non-uniform sampling) // 281 bins (uniform sampling) /* crystal size 4x8x30*/ - set_params(Advance, string_list("GE Advance", "Advance"), - 18, 283, 281, 2 * 336, + set_params(Advance, string_list("GE Advance", "Advance"), + 18, 283, 281, 2 * 336, 471.875F - 8.4F, 8.4F, 8.5F, 1.970177F, 0.0F, //TODO view offset shouldn't be zero 3, 2, 6, 6, 1, 1, 1); - break; + break; case DiscoveryLS: // identical to Advance - set_params(DiscoveryLS, string_list("GE Discovery LS", "Discovery LS"), - 18, 283, 281, 2 * 336, + set_params(DiscoveryLS, string_list("GE Discovery LS", "Discovery LS"), + 18, 283, 281, 2 * 336, 471.875F - 8.4F, 8.4F, 8.5F, 1.970177F, 0.0F, //TODO view offset shouldn't be zero 3, 2, 6, 6, 1, 1, 1); break; - case DiscoveryST: + case DiscoveryST: - // 249 bins (non-uniform sampling) + // 249 bins (non-uniform sampling) // 221 bins (uniform sampling) /* crystal size: 6.3 x 6.3 x 30 mm*/ - set_params(DiscoveryST, string_list("GE Discovery ST", "Discovery ST"), - 24, 249, 221, 2 * 210, - 886.2F/2.F, 8.4F, 6.54F, 3.195F, - static_cast(-4.54224*_PI/180),//sign? - 4, 2, 6, 6, 1, 1, 1);// TODO not sure about sign of view_offset + set_params(DiscoveryST, string_list("GE Discovery ST", "Discovery ST"), + 24, 249, 221, 2 * 210, + 886.2F/2.F, 8.4F, 6.54F, 3.195F, + static_cast(-4.54224*_PI/180),//sign? + 4, 2, 6, 6, 1, 1, 1);// TODO not sure about sign of view_offset break; - case DiscoverySTE: + case DiscoverySTE: - set_params(DiscoverySTE, string_list("GE Discovery STE", "Discovery STE"), + set_params(DiscoverySTE, string_list("GE Discovery STE", "Discovery STE"), 24, 329, 293, 2 * 280, 886.2F/2.F, 8.4F, 6.54F, 2.397F, - static_cast(-4.5490*_PI/180),//sign? + static_cast(-4.5490*_PI/180),//sign? 4, 2, 6, 8, 1, 1, 1);// TODO not sure about sign of view_offset break; - case DiscoveryRX: - - set_params(DiscoveryRX, string_list("GE Discovery RX", "Discovery RX"), - 24, - 367, - 331, - 2 * 315, - 886.2F/2.F, - 9.4F, - 6.54F, 2.13F, - static_cast(-4.5950*_PI/180),//sign? - 4, - 2, - 6, 9, 1, 1, 1);// TODO not sure about sign of view_offset + case DiscoveryRX: + + set_params(DiscoveryRX, string_list("GE Discovery RX", "Discovery RX"), + 24, + 367, + 331, + 2 * 315, + 886.2F/2.F, + 9.4F, + 6.54F, 2.13F, + static_cast(-4.5950*_PI/180),//sign? + 4, + 2, + 6, 9, 1, 1, 1);// TODO not sure about sign of view_offset break; - case Discovery600: - - set_params(Discovery600, string_list("GE Discovery 600", "Discovery 600"), - 24, - 339, - 293, // TODO - 2 * 256, - 826.70F/2.F - 8.4F, - 8.4F, - 6.54F, - 2.3974F, - static_cast(-4.5490*_PI/180),//sign? TODO value - 4, - 2, - 6, 8, 1, 1, 1); + case Discovery600: + + set_params(Discovery600, string_list("GE Discovery 600", "Discovery 600"), + 24, + 339, + 293, // TODO + 2 * 256, + 826.70F/2.F - 8.4F, + 8.4F, + 6.54F, + 2.3974F, + static_cast(-4.5490*_PI/180),//sign? TODO value + 4, + 2, + 6, 8, 1, 1, 1); break; - + case HZLR: - set_params(HZLR, string_list("Positron HZL/R"), - 32, 256, 2 * 192, - 780.0F, 7.0F, 5.1875F, 2.F, 0.0F, + set_params(HZLR, string_list("Positron HZL/R"), + 32, 256, 2 * 192, + 780.0F, 7.0F, 5.1875F, 2.F, 0.0F, 0, 0, 0, 0, 0,0, 1); // Default 7.0mm average interaction depth. // crystals per singles unit etc unknown @@ -447,9 +447,9 @@ Scanner::Scanner(Type scanner_type) case HRRT: - set_params(HRRT, string_list("HRRT"), - 104, 288, 2 * 288, - 234.765F, 7.0F, 2.4375F, 1.21875F, 0.0F, + set_params(HRRT, string_list("HRRT"), + 104, 288, 2 * 288, + 234.765F, 7.0F, 2.4375F, 1.21875F, 0.0F, 0, 0, 0, 0, 0, 0, 2); // added by Dylan Togane // warning: used 7.0mm average interaction depth. // crystals per singles unit etc unknown @@ -457,75 +457,75 @@ Scanner::Scanner(Type scanner_type) case Allegro: - /* + /* The following info is partially from - + Journal of Nuclear Medicine Vol. 45 No. 6 1040-1049 - Imaging Characteristics of a 3-Dimensional GSO Whole-Body PET Camera - Suleman Surti, PhD and Joel S. Karp, PhD + Imaging Characteristics of a 3-Dimensional GSO Whole-Body PET Camera + Suleman Surti, PhD and Joel S. Karp, PhD http://jnm.snmjournals.org/cgi/content/full/45/6/1040 Other info is from Ralph Brinks (Philips Research Lab, Aachen). - + The Allegro scanner is comprised of 28 flat modules of a 22 x 29 array of 4 x 6 x 20 mm3 GSO crystals. The output sinograms however consist - of 23 x 29 logical crystals per module. + of 23 x 29 logical crystals per module. This creates problems for the current version of STIR as the current - Scanner object does not support does. At present, KT put the + Scanner object does not support does. At present, KT put the transaxial info on crystals to 0. For 662keV photons the mean positron range in GSO is about 14 mm, so we put in 12mm for 511 keV, but we don't really know. Ralph Brinks things there is only one singles rate for the whole scanner. */ - set_params(Allegro,string_list("Allegro", "Philips Allegro"), - 29, 295, 28*23, - 430.05F, 12.F, - 6.3F, 4.3F, 0.0F, - 1, 0, - 29, 0 /* 23* or 22*/, - 29, 0 /* all detectors in a ring? */, - 1); + set_params(Allegro,string_list("Allegro", "Philips Allegro"), + 29, 295, 28*23, + 430.05F, 12.F, + 6.3F, 4.3F, 0.0F, + 1, 0, + 29, 0 /* 23* or 22*/, + 29, 0 /* all detectors in a ring? */, + 1); break; case GeminiTF: - set_params(GeminiTF,string_list("GeminiTF", "Philips GeminiTF"), + set_params(GeminiTF,string_list("GeminiTF", "Philips GeminiTF"), 44, 322, 287, // Based on GATE output - Normally it is 644 detectors at each of the 44 rings 322*2, // Actual number of crystals is 644 450.17F, 8.F, // DOI is from XXX et al 2008 MIC - 4.F, 4.F, 0.F, - 0, 0, - 0, 0, // Not considering any gap, but this is per module 28 flat modules in total, while 420 PMTs - 0, 0 /* Not sure about these, but shouldn't be important */, + 4.F, 4.F, 0.F, + 0, 0, + 0, 0, // Not considering any gap, but this is per module 28 flat modules in total, while 420 PMTs + 0, 0 /* Not sure about these, but shouldn't be important */, 1); break; case HiDAC: // all of these don't make any sense for the HiDAC - set_params(HiDAC, string_list("HiDAC"), - 0, 0, 0, - 0.F, 0.F, 0.F, 0.F, 0.F, + set_params(HiDAC, string_list("HiDAC"), + 0, 0, 0, + 0.F, 0.F, 0.F, 0.F, 0.F, 0, 0, 0, 0, 0, 0, 0); - + break; - + case User_defined_scanner: // zlong, 08-04-2004, Userdefined support - set_params(User_defined_scanner, string_list("Userdefined"), - 0, 0, 0, - 0.F, 0.F, 0.F, 0.F, 0.F, + set_params(User_defined_scanner, string_list("Userdefined"), + 0, 0, 0, + 0.F, 0.F, 0.F, 0.F, 0.F, 0, 0, 0, 0, 0, 0, 0); - + break; default: - // warning("Unknown scanner type used for initialisation of Scanner\n"); - set_params(Unknown_scanner, string_list("Unknown"), - 0, 0, 0, - 0.F, 0.F, 0.F, 0.F, 0.F, + // warning("Unknown scanner type used for initialisation of Scanner\n"); + set_params(Unknown_scanner, string_list("Unknown"), + 0, 0, 0, + 0.F, 0.F, 0.F, 0.F, 0.F, 0, 0, 0, 0, 0, 0, 0); - + break; - + } } @@ -948,133 +948,133 @@ set_params(Type type_v,const list& list_of_names_v, -Succeeded +Succeeded Scanner:: check_consistency() const { if (intrinsic_tilt<-_PI || intrinsic_tilt>_PI) warning("Scanner %s: intrinsic_tilt is very large. maybe it's in degrees (but should be in radians)", - this->get_name().c_str()); + this->get_name().c_str()); { if (get_num_transaxial_crystals_per_block() <= 0 || - get_num_transaxial_blocks() <= 0) + get_num_transaxial_blocks() <= 0) warning("Scanner %s: transaxial block info is not set (probably irrelevant unless you use a projector or normalisation that needs this block info)", - this->get_name().c_str()); + this->get_name().c_str()); else { - const int dets_per_ring = - get_num_transaxial_blocks() * - get_num_transaxial_crystals_per_block(); - if ( dets_per_ring != get_num_detectors_per_ring()) - { - warning("Scanner %s: inconsistent transaxial block info", - this->get_name().c_str()); - return Succeeded::no; - } + const int dets_per_ring = + get_num_transaxial_blocks() * + get_num_transaxial_crystals_per_block(); + if ( dets_per_ring != get_num_detectors_per_ring()) + { + warning("Scanner %s: inconsistent transaxial block info", + this->get_name().c_str()); + return Succeeded::no; + } } } { if (get_num_transaxial_blocks_per_bucket() <= 0 || - get_num_transaxial_buckets() <=0) + get_num_transaxial_buckets() <=0) warning("Scanner %s: transaxial bucket info is not set (probably irrelevant unless you use dead-time correction that needs this info)", - this->get_name().c_str()); + this->get_name().c_str()); else { - const int blocks_per_ring = - get_num_transaxial_buckets() * - get_num_transaxial_blocks_per_bucket(); - if ( blocks_per_ring != get_num_transaxial_blocks()) - { - warning("Scanner %s: inconsistent transaxial block/bucket info", - this->get_name().c_str()); - return Succeeded::no; - } + const int blocks_per_ring = + get_num_transaxial_buckets() * + get_num_transaxial_blocks_per_bucket(); + if ( blocks_per_ring != get_num_transaxial_blocks()) + { + warning("Scanner %s: inconsistent transaxial block/bucket info", + this->get_name().c_str()); + return Succeeded::no; + } } } { if (get_num_axial_crystals_per_block() <= 0 || - get_num_axial_blocks() <=0) + get_num_axial_blocks() <=0) warning("Scanner %s: axial block info is not set (probably irrelevant unless you use a projector or normalisation that needs this block info)", - this->get_name().c_str()); + this->get_name().c_str()); else { - const int dets_axial = - get_num_axial_blocks() * - get_num_axial_crystals_per_block(); - if ( dets_axial != get_num_rings()) - { - warning("Scanner %s: inconsistent axial block info", - this->get_name().c_str()); - return Succeeded::no; - } + const int dets_axial = + get_num_axial_blocks() * + get_num_axial_crystals_per_block(); + if ( dets_axial != get_num_rings()) + { + warning("Scanner %s: inconsistent axial block info", + this->get_name().c_str()); + return Succeeded::no; + } } } { if (get_num_axial_blocks_per_bucket() <= 0 || - get_num_axial_buckets() <=0) + get_num_axial_buckets() <=0) warning("Scanner %s: axial bucket info is not set (probably irrelevant unless you use dead-time correction that needs this info)", - this->get_name().c_str()); + this->get_name().c_str()); else { - const int blocks_axial = - get_num_axial_buckets() * - get_num_axial_blocks_per_bucket(); - if ( blocks_axial != get_num_axial_blocks()) - { - warning("Scanner %s: inconsistent axial block/bucket info", - this->get_name().c_str()); - return Succeeded::no; - } + const int blocks_axial = + get_num_axial_buckets() * + get_num_axial_blocks_per_bucket(); + if ( blocks_axial != get_num_axial_blocks()) + { + warning("Scanner %s: inconsistent axial block/bucket info", + this->get_name().c_str()); + return Succeeded::no; + } } } // checks on singles units { if (get_num_transaxial_crystals_per_singles_unit() <= 0) warning("Scanner %s: transaxial singles_unit info is not set (probably irrelevant unless you use dead-time correction that needs this info)", - this->get_name().c_str()); + this->get_name().c_str()); else { - if ( get_num_detectors_per_ring() % get_num_transaxial_crystals_per_singles_unit() != 0) - { - warning("Scanner %s: inconsistent transaxial singles unit info:\n" - "\tnum_detectors_per_ring %d should be a multiple of num_transaxial_crystals_per_singles_unit %d", - this->get_name().c_str(), - get_num_detectors_per_ring(), get_num_transaxial_crystals_per_singles_unit()); - return Succeeded::no; - } - if ( get_num_transaxial_crystals_per_bucket() % get_num_transaxial_crystals_per_singles_unit() != 0) - { - warning("Scanner %s: inconsistent transaxial singles unit info:\n" - "\tnum_transaxial_crystals_per_bucket %d should be a multiple of num_transaxial_crystals_per_singles_unit %d", - this->get_name().c_str(), - get_num_transaxial_crystals_per_bucket(), get_num_transaxial_crystals_per_singles_unit()); - return Succeeded::no; - } + if ( get_num_detectors_per_ring() % get_num_transaxial_crystals_per_singles_unit() != 0) + { + warning("Scanner %s: inconsistent transaxial singles unit info:\n" + "\tnum_detectors_per_ring %d should be a multiple of num_transaxial_crystals_per_singles_unit %d", + this->get_name().c_str(), + get_num_detectors_per_ring(), get_num_transaxial_crystals_per_singles_unit()); + return Succeeded::no; + } + if ( get_num_transaxial_crystals_per_bucket() % get_num_transaxial_crystals_per_singles_unit() != 0) + { + warning("Scanner %s: inconsistent transaxial singles unit info:\n" + "\tnum_transaxial_crystals_per_bucket %d should be a multiple of num_transaxial_crystals_per_singles_unit %d", + this->get_name().c_str(), + get_num_transaxial_crystals_per_bucket(), get_num_transaxial_crystals_per_singles_unit()); + return Succeeded::no; + } } } { if (get_num_axial_crystals_per_singles_unit() <= 0) warning("Scanner %s: axial singles_unit info is not set (probably irrelevant unless you use dead-time correction that needs this info)", - this->get_name().c_str()); + this->get_name().c_str()); else { - if ( get_num_rings() % get_num_axial_crystals_per_singles_unit() != 0) - { - warning("Scanner %s: inconsistent axial singles unit info:\n" - "\tnum_rings %d should be a multiple of num_axial_crystals_per_singles_unit %d", - this->get_name().c_str(), - get_num_rings(), get_num_axial_crystals_per_singles_unit()); - return Succeeded::no; - } - if ( get_num_axial_crystals_per_bucket() % get_num_axial_crystals_per_singles_unit() != 0) - { - warning("Scanner %s: inconsistent axial singles unit info:\n" - "\tnum_axial_crystals_per_bucket %d should be a multiple of num_axial_crystals_per_singles_unit %d", - this->get_name().c_str(), - get_num_axial_crystals_per_bucket(), get_num_axial_crystals_per_singles_unit()); - return Succeeded::no; - } + if ( get_num_rings() % get_num_axial_crystals_per_singles_unit() != 0) + { + warning("Scanner %s: inconsistent axial singles unit info:\n" + "\tnum_rings %d should be a multiple of num_axial_crystals_per_singles_unit %d", + this->get_name().c_str(), + get_num_rings(), get_num_axial_crystals_per_singles_unit()); + return Succeeded::no; + } + if ( get_num_axial_crystals_per_bucket() % get_num_axial_crystals_per_singles_unit() != 0) + { + warning("Scanner %s: inconsistent axial singles unit info:\n" + "\tnum_axial_crystals_per_bucket %d should be a multiple of num_axial_crystals_per_singles_unit %d", + this->get_name().c_str(), + get_num_axial_crystals_per_bucket(), get_num_axial_crystals_per_singles_unit()); + return Succeeded::no; + } } } @@ -1093,7 +1093,7 @@ bool static close_enough(const double a, const double b) return fabs(a-b) <= std::min(fabs(a), fabs(b)) * 10E-4; } -bool +bool Scanner::operator ==(const Scanner& scanner) const { if (!close_enough(energy_resolution, scanner.energy_resolution) && @@ -1123,7 +1123,7 @@ if (!close_enough(energy_resolution, scanner.energy_resolution) && } -const list& +const list& Scanner::get_all_names() const {return list_of_names;} @@ -1131,9 +1131,9 @@ Scanner::get_all_names() const const string& Scanner::get_name() const { - - return *(list_of_names.begin()); - + + return *(list_of_names.begin()); + } string @@ -1149,7 +1149,7 @@ Scanner::parameter_info() const #endif s << "Scanner parameters:= "<<'\n'; - s << "Scanner type := " << get_name() <<'\n'; + s << "Scanner type := " << get_name() <<'\n'; s << "Number of rings := " << num_rings << '\n'; s << "Number of detectors per ring := " << get_num_detectors_per_ring() << '\n'; @@ -1184,7 +1184,7 @@ Scanner::parameter_info() const << get_num_axial_crystals_per_singles_unit() << '\n' << "Number of crystals per singles unit in transaxial direction := " << get_num_transaxial_crystals_per_singles_unit() << '\n'; - + s << "end scanner parameters:=\n"; return s.str(); @@ -1204,7 +1204,7 @@ string Scanner::list_names() const // work-around VC bug std:: #endif - list::const_iterator iterator = list_of_names.begin(); + list::const_iterator iterator = list_of_names.begin(); s << *iterator; ++iterator; while(iterator!=list_of_names.end()) @@ -1219,7 +1219,7 @@ string Scanner::list_names() const /************************************************ static members *************************************************/ -Scanner* Scanner::ask_parameters() +Scanner* Scanner::ask_parameters() { cerr << list_all_names(); @@ -1227,7 +1227,7 @@ Scanner* Scanner::ask_parameters() const string name=ask_string("Enter the name of the scanner"); //get the type from the name itself - Scanner* scanner_ptr = + Scanner* scanner_ptr = get_scanner_from_name(name); // N.E: New optional parameters have been added, namely @@ -1246,44 +1246,44 @@ Scanner* Scanner::ask_parameters() if (scanner_ptr->type == Unknown_scanner) cerr << "I didn't recognise the scanner you entered."; cerr << "I'll ask lots of questions\n"; - + while (true) { - int num_detectors_per_ring = - ask_num("Enter number of detectors per ring:",0,2000,128); - - int NoRings = + int num_detectors_per_ring = + ask_num("Enter number of detectors per ring:",0,2000,128); + + int NoRings = ask_num("Enter number of rings :",0,1000,16); - - int NoBins = + + int NoBins = ask_num("Enter default number of tangential positions for this scanner: ",0,3000,128); - + float InnerRingRadius= - ask_num("Enter inner ring radius (in mm): ",0.F,600.F,256.F); - - float AverageDepthOfInteraction = + ask_num("Enter inner ring radius (in mm): ",0.F,600.F,256.F); + + float AverageDepthOfInteraction = ask_num("Enter average depth of interaction (in mm): ", 0.F, 100.F, 0.F); - - float RingSpacing= + + float RingSpacing= ask_num("Enter ring spacing (in mm): ",0.F,30.F,6.75F); - - float BinSize= + + float BinSize= ask_num("Enter default (tangential) bin size after arc-correction (in mm):",0.F,60.F,3.75F); float intrTilt= - ask_num("Enter intrinsic_tilt (in degrees):",-180.F,360.F,0.F); - int TransBlocksPerBucket = - ask_num("Enter number of transaxial blocks per bucket: ",0,10,2); - int AxialBlocksPerBucket = - ask_num("Enter number of axial blocks per bucket: ",0,10,6); - int AxialCrystalsPerBlock = - ask_num("Enter number of axial crystals per block: ",0,12,8); - int TransaxialCrystalsPerBlock = - ask_num("Enter number of transaxial crystals per block: ",0,12,8); - int AxialCrstalsPerSinglesUnit = + ask_num("Enter intrinsic_tilt (in degrees):",-180.F,360.F,0.F); + int TransBlocksPerBucket = + ask_num("Enter number of transaxial blocks per bucket: ",0,10,2); + int AxialBlocksPerBucket = + ask_num("Enter number of axial blocks per bucket: ",0,10,6); + int AxialCrystalsPerBlock = + ask_num("Enter number of axial crystals per block: ",0,12,8); + int TransaxialCrystalsPerBlock = + ask_num("Enter number of transaxial crystals per block: ",0,12,8); + int AxialCrstalsPerSinglesUnit = ask_num("Enter number of axial crystals per singles unit: ", 0, NoRings, 1); - int TransaxialCrystalsPerSinglesUnit = + int TransaxialCrystalsPerSinglesUnit = ask_num("Enter number of transaxial crystals per singles unit: ", 0, num_detectors_per_ring, 1); - + float EnergyResolution = ask_num("Enter the energy resolution of the scanner : ", 0.0f, 1000.0f, -1.0f); @@ -1293,7 +1293,7 @@ Scanner* Scanner::ask_parameters() int num_detector_layers = ask_num("Enter number of detector layers per block: ",1,100,1); Type type = User_defined_scanner; - + if (EnergyResolution > -1 && ReferenceEnergy > -1) Scanner* scanner_ptr = new Scanner(type, string_list(name), @@ -1318,11 +1318,11 @@ Scanner* Scanner::ask_parameters() AxialCrystalsPerBlock,TransaxialCrystalsPerBlock, AxialCrstalsPerSinglesUnit, TransaxialCrystalsPerSinglesUnit, num_detector_layers); - + if (scanner_ptr->check_consistency()==Succeeded::yes || - !ask("Ask questions again?",true)) - return scanner_ptr; - + !ask("Ask questions again?",true)) + return scanner_ptr; + delete scanner_ptr; } // infinite loop } @@ -1331,26 +1331,26 @@ Scanner* Scanner::ask_parameters() Scanner * Scanner::get_scanner_from_name(const string& name) -{ +{ Scanner * scanner_ptr; const string matching_name = standardise_interfile_keyword(name); - Type type= E931; + Type type= E931; while (type != Unknown_scanner) { scanner_ptr = new Scanner(type); const list& list_of_names = scanner_ptr->get_all_names(); for (std::list::const_iterator iter =list_of_names.begin(); - iter!=list_of_names.end(); - ++iter) + iter!=list_of_names.end(); + ++iter) { - const string matching_scanner_name = - standardise_interfile_keyword(*iter); - if (matching_scanner_name==matching_name) - return scanner_ptr; + const string matching_scanner_name = + standardise_interfile_keyword(*iter); + if (matching_scanner_name==matching_name) + return scanner_ptr; } - + // we didn't find it yet delete scanner_ptr; // tricky business to find next type @@ -1374,24 +1374,24 @@ string Scanner:: list_all_names() std::ostringstream s; #endif - Type type= E931; + Type type= E931; while (type != Unknown_scanner) { scanner_ptr = new Scanner(type); s << scanner_ptr->list_names() << '\n'; - + delete scanner_ptr; // tricky business to find next type int int_type = type; ++int_type; type = static_cast(int_type); } - + return s.str(); } -static list +static list string_list(const string& s) { list l; @@ -1399,7 +1399,7 @@ string_list(const string& s) return l; } -static list +static list string_list(const string& s1, const string& s2) { list l; @@ -1408,7 +1408,7 @@ string_list(const string& s1, const string& s2) return l; } -static list +static list string_list(const string& s1, const string& s2, const string& s3) { list l; @@ -1418,7 +1418,7 @@ string_list(const string& s1, const string& s2, const string& s3) return l; } -static list +static list string_list(const string& s1, const string& s2, const string& s3, const string& s4) { list l; diff --git a/src/include/stir/ProjDataInfo.h b/src/include/stir/ProjDataInfo.h index 6bb6c0ddeb..2d3f4851be 100644 --- a/src/include/stir/ProjDataInfo.h +++ b/src/include/stir/ProjDataInfo.h @@ -182,7 +182,7 @@ class ProjDataInfo //! Get number of tangential positions inline int get_num_tangential_poss() const; //! Get number of tof bins - inline int get_tof_bin(double delta) const; + inline int get_tof_bin(double& delta) const; //! Get number of TOF bins inline int get_num_tof_poss() const; //! Get minimum segment number diff --git a/src/include/stir/ProjDataInfo.inl b/src/include/stir/ProjDataInfo.inl index ca82f61fcd..ca82cd69c6 100644 --- a/src/include/stir/ProjDataInfo.inl +++ b/src/include/stir/ProjDataInfo.inl @@ -66,7 +66,7 @@ ProjDataInfo::get_num_tof_poss() const { return num_tof_bins; } int -ProjDataInfo::get_tof_bin(double delta) const +ProjDataInfo::get_tof_bin(double& delta) const { for (int i = min_timing_pos_num; i < max_timing_pos_num; i++) { diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl index 3c372cf3c4..08a8120a92 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl +++ b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl @@ -202,12 +202,15 @@ ProjMatrixByBin::apply_tof_kernel(ProjMatrixElemsForOneBin& nonTOF_probabilities // (point2.z() - voxel_center.z()) *(point2.z() - voxel_center.z())); float m = (lor_length - d1 - d1) * 0.5f; - low_dist = (proj_data_info_sptr->timing_bin_boundaries[tof_probabilities.get_bin_ptr()->timing_pos_num()].low_lim - m) * r_sqrt2_gauss_sigma; - high_dist = (proj_data_info_sptr->timing_bin_boundaries[tof_probabilities.get_bin_ptr()->timing_pos_num()].high_lim - m) * r_sqrt2_gauss_sigma; + low_dist = (proj_data_info_sptr->timing_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].low_lim - m) * r_sqrt2_gauss_sigma; + high_dist = (proj_data_info_sptr->timing_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].high_lim - m) * r_sqrt2_gauss_sigma; + + // Cut-off really small values. + if (abs(low_dist) > 5.5 && abs(high_dist) > 5.5) + continue; get_tof_value(low_dist, high_dist, new_value); new_value *= element_ptr->get_value(); - tof_probabilities.push_back(ProjMatrixElemsForOneBin::value_type(element_ptr->get_coords(), new_value)); } From ed6b89c5975dc512d1e69890712b7494114ae0da Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Tue, 17 Jan 2017 13:56:06 +0000 Subject: [PATCH 049/170] correction --- src/include/stir/ProjDataInfo.h | 2 +- src/include/stir/ProjDataInfo.inl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/include/stir/ProjDataInfo.h b/src/include/stir/ProjDataInfo.h index 2d3f4851be..43f64c3171 100644 --- a/src/include/stir/ProjDataInfo.h +++ b/src/include/stir/ProjDataInfo.h @@ -182,7 +182,7 @@ class ProjDataInfo //! Get number of tangential positions inline int get_num_tangential_poss() const; //! Get number of tof bins - inline int get_tof_bin(double& delta) const; + inline int get_tof_bin(const double& delta) const; //! Get number of TOF bins inline int get_num_tof_poss() const; //! Get minimum segment number diff --git a/src/include/stir/ProjDataInfo.inl b/src/include/stir/ProjDataInfo.inl index ca82cd69c6..b3487b6532 100644 --- a/src/include/stir/ProjDataInfo.inl +++ b/src/include/stir/ProjDataInfo.inl @@ -66,7 +66,7 @@ ProjDataInfo::get_num_tof_poss() const { return num_tof_bins; } int -ProjDataInfo::get_tof_bin(double& delta) const +ProjDataInfo::get_tof_bin(const double& delta) const { for (int i = min_timing_pos_num; i < max_timing_pos_num; i++) { From ca66bf192e4f6b2cc26ca6ed9a31bdd76b365992 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Sat, 21 Jan 2017 19:20:42 +0000 Subject: [PATCH 050/170] Mostly everything but ProjDataFromStream We can read/write interfile headers, read/write TOF scanners, LM_TO_PROJDATA is aware of timing information. But the low level work hasnot finish, yet. So we cannot actually read / write TOF data from the disk . --- src/IO/InterfileHeader.cxx | 219 +++++++++---- src/IO/interfile.cxx | 186 ++++++----- src/buildblock/ProjData.cxx | 6 +- src/buildblock/ProjDataFromStream.cxx | 16 + src/buildblock/ProjDataInfo.cxx | 42 ++- .../ProjDataInfoCylindricalArcCorr.cxx | 9 +- .../ProjDataInfoCylindricalNoArcCorr.cxx | 15 +- src/buildblock/Scanner.cxx | 65 +++- src/include/stir/Bin.inl | 2 +- src/include/stir/IO/InterfileHeader.h | 7 + src/include/stir/ProjData.h | 8 +- src/include/stir/ProjDataFromStream.h | 24 +- src/include/stir/ProjDataFromStream.inl | 4 + src/include/stir/ProjDataInfo.h | 20 +- .../stir/ProjDataInfoCylindricalArcCorr.h | 3 +- .../stir/ProjDataInfoCylindricalNoArcCorr.h | 6 +- src/include/stir/listmode/LmToProjData.h | 10 +- .../stir/listmode/LmToProjDataBootstrap.h | 2 +- src/listmode_buildblock/LmToProjData.cxx | 307 ++++++++++++++++-- .../LmToProjDataBootstrap.cxx | 6 +- src/listmode_utilities/lm_to_projdata.cxx | 2 +- ...MeanAndListModeDataWithProjMatrixByBin.cxx | 1 - src/test/test_proj_data_info.cxx | 22 +- src/utilities/create_projdata_template.cxx | 9 +- 24 files changed, 746 insertions(+), 245 deletions(-) diff --git a/src/IO/InterfileHeader.cxx b/src/IO/InterfileHeader.cxx index a6cd22f8bf..c556f75614 100644 --- a/src/IO/InterfileHeader.cxx +++ b/src/IO/InterfileHeader.cxx @@ -557,6 +557,19 @@ InterfilePDFSHeader::InterfilePDFSHeader() add_key("Reference energy (in keV)", &reference_energy); + tof_mash_factor=-1; + add_key("%TOF mashing factor", + &tof_mash_factor); + max_num_timing_poss = -1; + add_key("Number of TOF time bins", + &max_num_timing_poss); + size_of_timing_pos = -1.f; + add_key("Size of timing bin (ps)", + &size_of_timing_pos); + timing_resolution = -1.f; + add_key("Timing resolution (ps)", + &timing_resolution); + add_key("end scanner parameters", KeyArgument::NONE, &KeyParser::do_nothing); @@ -595,9 +608,10 @@ 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; } @@ -614,16 +628,31 @@ 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) + { + 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 + { + 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") { @@ -845,6 +874,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() { @@ -1171,7 +1201,7 @@ bool InterfilePDFSHeader::post_processing() { if (energy_resolution != guessed_scanner_ptr->get_energy_resolution()) { - warning("Interfile warning: 'energy resolution' (%d) is expected to be %d. " + warning("Interfile warning: 'energy resolution' (%f) is expected to be %d. " "Currently, the energy resolution and the reference energy, are used only in" " scatter correction.", energy_resolution, guessed_scanner_ptr->get_energy_resolution()); @@ -1179,7 +1209,7 @@ bool InterfilePDFSHeader::post_processing() } if (reference_energy != guessed_scanner_ptr->get_reference_energy()) { - warning("Interfile warning: 'reference energy' (%d) is expected to be %d." + warning("Interfile warning: 'reference energy' (%f) is expected to be %d." "Currently, the energy resolution and the reference energy, are used only in" " scatter correction.", reference_energy, guessed_scanner_ptr->get_reference_energy()); @@ -1187,6 +1217,28 @@ bool InterfilePDFSHeader::post_processing() } } + if (guessed_scanner_ptr->is_tof_ready()) + { + if (max_num_timing_poss != guessed_scanner_ptr->get_num_max_of_timing_bins()) + { + warning("Interfile warning: 'Number of TOF time bins' (%d) is expected to be %d.", + max_num_timing_poss, guessed_scanner_ptr->get_num_max_of_timing_bins()); + mismatch_between_header_and_guess = true; + } + if (size_of_timing_pos != guessed_scanner_ptr->get_size_of_timing_bin()) + { + 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_bin()); + 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()); + mismatch_between_header_and_guess = true; + } + } + // end of checks. If they failed, we ignore the guess if (mismatch_between_header_and_guess) { @@ -1229,86 +1281,117 @@ 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( - new Scanner(guessed_scanner_ptr->get_type(), - get_exam_info_ptr()->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, - num_transaxial_crystals_per_singles_unit, - num_detector_layers, - energy_resolution, - reference_energy)); + + shared_ptr scanner_sptr_from_file; + if (!guessed_scanner_ptr->is_tof_ready()) + { + scanner_sptr_from_file.reset( + new Scanner(guessed_scanner_ptr->get_type(), + get_exam_info_ptr()->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, + num_transaxial_crystals_per_singles_unit, + num_detector_layers, + energy_resolution, + reference_energy)); + } + else + { + scanner_sptr_from_file.reset( + new Scanner(guessed_scanner_ptr->get_type(), + get_exam_info_ptr()->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, + num_transaxial_crystals_per_singles_unit, + num_detector_layers, + max_num_timing_poss, + size_of_timing_pos, + timing_resolution)); + } + 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("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().c_str()); } // float azimuthal_angle_sampling =_PI/num_views; - + //TODO: TOF ProjDataInfo 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) - warning("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); + 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("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_sptr_from_file->get_default_bin_size()/10); - data_info_ptr = - new ProjDataInfoCylindricalArcCorr ( - scanner_ptr_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); + data_info_ptr = + new ProjDataInfoCylindricalArcCorr ( + 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, tof_mash_factor); } else { - data_info_ptr = - new ProjDataInfoCylindricalNoArcCorr ( - scanner_ptr_from_file, - sorted_num_rings_per_segment, - sorted_min_ring_diff, - sorted_max_ring_diff, - num_views,num_bins); + data_info_ptr = + new ProjDataInfoCylindricalNoArcCorr ( + scanner_sptr_from_file, + sorted_num_rings_per_segment, + sorted_min_ring_diff, + sorted_max_ring_diff, + num_views,num_bins, tof_mash_factor); if (effective_central_bin_size_in_cm>0 && - fabs(effective_central_bin_size_in_cm - - data_info_ptr->get_sampling_in_s(Bin(0,0,0,0))/10.)>.01) - { - warning("Interfile warning: inconsistent effective_central_bin_size_in_cm\n" - "Value in header is %g while I expect %g from the inner ring radius etc\n" - "Ignoring value in header", - effective_central_bin_size_in_cm, - data_info_ptr->get_sampling_in_s(Bin(0,0,0,0))/10.); - } + fabs(effective_central_bin_size_in_cm - + data_info_ptr->get_sampling_in_s(Bin(0,0,0,0))/10.)>.01) + { + warning("Interfile warning: inconsistent effective_central_bin_size_in_cm\n" + "Value in header is %g while I expect %g from the inner ring radius etc\n" + "Ignoring value in header", + effective_central_bin_size_in_cm, + data_info_ptr->get_sampling_in_s(Bin(0,0,0,0))/10.); + } } //cerr << data_info_ptr->parameter_info() << endl; diff --git a/src/IO/interfile.cxx b/src/IO/interfile.cxx index 9fb86aa7f1..5e60d8f495 100644 --- a/src/IO/interfile.cxx +++ b/src/IO/interfile.cxx @@ -847,57 +847,76 @@ 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_ptr()->get_tof_mash_factor()>1 ? + output_header << "number of dimensions := 5\n" : + output_header << "number of dimensions := 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_ptr()->get_num_views() << "\n"; - + << pdfs.get_proj_data_info_ptr()->get_num_views() << "\n"; + output_header << "matrix axis label [" << order_of_z << "] := axial coordinate\n"; output_header << "!matrix size [" << order_of_z << "] := "; // tedious way to print a list of numbers @@ -905,62 +924,73 @@ 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_ptr()->get_num_axial_poss(*seg); + output_header << "," << pdfs.get_proj_data_info_ptr()->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 TOF is supported add this in the header. + if (pdfs.get_proj_data_info_ptr()->get_scanner_ptr()->is_tof_ready() && + pdfs.get_proj_data_info_ptr()->get_tof_mash_factor() > 1) + { + // 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_ptr()->get_tof_mash_factor() << "\n"; + } } - const ProjDataInfoCylindrical* proj_data_info_ptr = + const ProjDataInfoCylindrical* proj_data_info_ptr = dynamic_cast< const ProjDataInfoCylindrical*> (pdfs.get_proj_data_info_ptr()); if (proj_data_info_ptr!=NULL) { // 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_ptr->get_min_ring_difference(*seg); - for (seg++; seg != segment_sequence.end(); seg++) - output_header << "," <get_min_ring_difference(*seg); - output_header << "}\n"; + std::vector::const_iterator seg = segment_sequence.begin(); + output_header << "{ " << proj_data_info_ptr->get_min_ring_difference(*seg); + for (seg++; seg != segment_sequence.end(); seg++) + output_header << "," <get_min_ring_difference(*seg); + output_header << "}\n"; } output_header << "maximum ring difference per segment := "; { - std::vector::const_iterator seg = segment_sequence.begin(); - output_header << "{ " <get_max_ring_difference(*seg); - for (seg++; seg != segment_sequence.end(); seg++) - output_header << "," <get_max_ring_difference(*seg); - output_header << "}\n"; + std::vector::const_iterator seg = segment_sequence.begin(); + output_header << "{ " <get_max_ring_difference(*seg); + for (seg++; seg != segment_sequence.end(); seg++) + output_header << "," <get_max_ring_difference(*seg); + output_header << "}\n"; } - + const Scanner& scanner = *proj_data_info_ptr->get_scanner_ptr(); if (fabs(proj_data_info_ptr->get_ring_radius()- - scanner.get_effective_ring_radius()) > .1) - warning("write_basic_interfile_PDFS_header: inconsistent effective ring radius:\n" - "\tproj_data_info has %g, scanner has %g.\n" - "\tThis really should not happen and signifies a bug.\n" - "\tYou will have a problem reading this data back in.", - proj_data_info_ptr->get_ring_radius(), - scanner.get_effective_ring_radius()); + scanner.get_effective_ring_radius()) > .1) + warning("write_basic_interfile_PDFS_header: inconsistent effective ring radius:\n" + "\tproj_data_info has %g, scanner has %g.\n" + "\tThis really should not happen and signifies a bug.\n" + "\tYou will have a problem reading this data back in.", + proj_data_info_ptr->get_ring_radius(), + scanner.get_effective_ring_radius()); if (fabs(proj_data_info_ptr->get_ring_spacing()- - scanner.get_ring_spacing()) > .1) - warning("write_basic_interfile_PDFS_header: inconsistent ring spacing:\n" - "\tproj_data_info has %g, scanner has %g.\n" - "\tThis really should not happen and signifies a bug.\n" - "\tYou will have a problem reading this data back in.", - proj_data_info_ptr->get_ring_spacing(), - scanner.get_ring_spacing()); + scanner.get_ring_spacing()) > .1) + warning("write_basic_interfile_PDFS_header: inconsistent ring spacing:\n" + "\tproj_data_info has %g, scanner has %g.\n" + "\tThis really should not happen and signifies a bug.\n" + "\tYou will have a problem reading this data back in.", + proj_data_info_ptr->get_ring_spacing(), + scanner.get_ring_spacing()); output_header << scanner.parameter_info(); - output_header << "effective central bin size (cm) := " - << proj_data_info_ptr->get_sampling_in_s(Bin(0,0,0,0))/10. << endl; + output_header << "effective central bin size (cm) := " + << proj_data_info_ptr->get_sampling_in_s(Bin(0,0,0,0))/10. << endl; } // end of cylindrical scanner else @@ -975,22 +1005,22 @@ write_basic_interfile_PDFS_header(const string& header_file_name, const TimeFrameDefinitions& frame_defs(pdfs.get_exam_info_ptr()->time_frame_definitions); if (frame_defs.get_num_time_frames()>0) { - output_header << "number of time frames := " << frame_defs.get_num_time_frames() << '\n'; - for (unsigned int frame_num=1; frame_num<=frame_defs.get_num_time_frames(); ++frame_num) - { - if (frame_defs.get_duration(frame_num)>0) - { - output_header << "image duration (sec)[" << frame_num << "] := " - << frame_defs.get_duration(frame_num) << '\n'; - output_header << "image relative start time (sec)[" << frame_num << "] := " - << frame_defs.get_start_time(frame_num) << '\n'; - } - } + output_header << "number of time frames := " << frame_defs.get_num_time_frames() << '\n'; + for (unsigned int frame_num=1; frame_num<=frame_defs.get_num_time_frames(); ++frame_num) + { + if (frame_defs.get_duration(frame_num)>0) + { + output_header << "image duration (sec)[" << frame_num << "] := " + << frame_defs.get_duration(frame_num) << '\n'; + output_header << "image relative start time (sec)[" << frame_num << "] := " + << frame_defs.get_start_time(frame_num) << '\n'; + } + } } else { - // need to write this anyway to allow vectored keys below - output_header << "number of time frames := 1\n"; + // need to write this anyway to allow vectored keys below + output_header << "number of time frames := 1\n"; } } // Write energy window lower and upper thresholds, if they are not -1 diff --git a/src/buildblock/ProjData.cxx b/src/buildblock/ProjData.cxx index 1eea479fa7..416a62804e 100644 --- a/src/buildblock/ProjData.cxx +++ b/src/buildblock/ProjData.cxx @@ -353,7 +353,8 @@ SegmentByView ProjData::get_segment_by_view(const int segment_num) const } Succeeded -ProjData::set_segment(const SegmentBySinogram& segment) +ProjData::set_segment(const SegmentBySinogram& segment, + const int& timing_pos) { for (int view_num = get_min_view_num(); view_num <= get_max_view_num(); ++view_num) { @@ -365,7 +366,8 @@ ProjData::set_segment(const SegmentBySinogram& segment) } Succeeded -ProjData::set_segment(const SegmentByView& segment) +ProjData::set_segment(const SegmentByView& segment, + const int& timing_pos) { for (int view_num = get_min_view_num(); view_num <= get_max_view_num(); ++view_num) { diff --git a/src/buildblock/ProjDataFromStream.cxx b/src/buildblock/ProjDataFromStream.cxx index 0e19dee214..c768561476 100644 --- a/src/buildblock/ProjDataFromStream.cxx +++ b/src/buildblock/ProjDataFromStream.cxx @@ -119,6 +119,22 @@ ProjDataFromStream::ProjDataFromStream(shared_ptr const& exam_info_spt { segment_sequence[i] =segment_num; } + + // In this case lets start a TOF stream - Similar to segments + if (storage_order == Timing_Segment_View_AxialPos_TangPos && + proj_data_info_ptr->get_tof_mash_factor() > 1) + { + timing_poss_sequence.resize(proj_data_info_ptr->get_num_timing_poss()); + int timing_pos_num; + + for (i= 0, timing_pos_num = proj_data_info_ptr->get_min_timing_pos_num(); + timing_pos_num<=proj_data_info_ptr->get_max_timing_pos_num(); + ++i, ++timing_pos_num) + { + timing_poss_sequence[i] = timing_pos_num; + } + + } } Viewgram diff --git a/src/buildblock/ProjDataInfo.cxx b/src/buildblock/ProjDataInfo.cxx index 1603d74825..7bae650c39 100644 --- a/src/buildblock/ProjDataInfo.cxx +++ b/src/buildblock/ProjDataInfo.cxx @@ -194,9 +194,12 @@ ProjDataInfo::set_tof_mash_factor(const int new_num) timing_increament_in_mm = (tof_mash_factor * scanner_ptr->get_size_of_timing_bin() * 0.299792458f); min_timing_pos_num = - (scanner_ptr->get_num_max_of_timing_bins() / tof_mash_factor)/2; - max_timing_pos_num = min_timing_pos_num + (scanner_ptr->get_num_max_of_timing_bins() / tof_mash_factor); + max_timing_pos_num = min_timing_pos_num + (scanner_ptr->get_num_max_of_timing_bins() / tof_mash_factor) -1; - num_tof_bins = max_timing_pos_num - min_timing_pos_num; + num_tof_bins = max_timing_pos_num - min_timing_pos_num +1 ; + + 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; timing_bin_boundaries_mm.grow(min_timing_pos_num, max_timing_pos_num); @@ -413,9 +416,10 @@ ProjDataInfo::get_empty_related_viewgrams(const ViewSegmentNumbers& view_segmnet ProjDataInfo* ProjDataInfo::ProjDataInfoCTI(const shared_ptr& 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) { if (span < 1) error("ProjDataInfoCTI: span %d has to be larger than 0\n", span); @@ -499,23 +503,27 @@ 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_views,num_tangential_poss, + tof_mash_factor); } // 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) { @@ -562,14 +570,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); } @@ -589,6 +599,10 @@ ProjDataInfo* ProjDataInfo::ask_parameters() const int num_views = scanner_ptr->get_max_num_views()/ ask_num("Mash factor for views",1,16,1); + const int tof_mash_factor = scanner_ptr->is_tof_ready() ? + ask_num("Time-of-flight mash factor (1: No TOF):", 1, + scanner_ptr->get_num_max_of_timing_bins(), 1) : 1; + const bool arc_corrected = ask("Is the data arc-corrected?",true); @@ -615,8 +629,8 @@ ProjDataInfo* ProjDataInfo::ask_parameters() ProjDataInfo * pdi_ptr = span==0 - ? ProjDataInfoGE(scanner_ptr,max_delta,num_views,num_tangential_poss,arc_corrected) - : ProjDataInfoCTI(scanner_ptr,span,max_delta,num_views,num_tangential_poss,arc_corrected); + ? 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() <& num_axial_pos_per_segment, const VectorWithOffset& min_ring_diff_v, const VectorWithOffset& max_ring_diff_v, - const int num_views,const int num_tangential_poss) + const int num_views,const int num_tangential_poss, + const int tof_mash_factor) :ProjDataInfoCylindrical(scanner_ptr, num_axial_pos_per_segment, min_ring_diff_v, max_ring_diff_v, num_views, num_tangential_poss), bin_size(bin_size_v) -{} +{ + // If tof_mash_factor == 1 then there is only tof bin ... effectively no TOF + if (tof_mash_factor > 1) + set_tof_mash_factor(tof_mash_factor); +} void diff --git a/src/buildblock/ProjDataInfoCylindricalNoArcCorr.cxx b/src/buildblock/ProjDataInfoCylindricalNoArcCorr.cxx index 3a0ee2c858..8018bfc901 100644 --- a/src/buildblock/ProjDataInfoCylindricalNoArcCorr.cxx +++ b/src/buildblock/ProjDataInfoCylindricalNoArcCorr.cxx @@ -61,7 +61,8 @@ ProjDataInfoCylindricalNoArcCorr(const shared_ptr scanner_ptr, const VectorWithOffset& num_axial_pos_per_segment, const VectorWithOffset& min_ring_diff_v, const VectorWithOffset& max_ring_diff_v, - const int num_views,const int num_tangential_poss) + const int num_views,const int num_tangential_poss, + const int tof_mash_factor) : ProjDataInfoCylindrical(scanner_ptr, num_axial_pos_per_segment, min_ring_diff_v, max_ring_diff_v, @@ -71,6 +72,9 @@ ProjDataInfoCylindricalNoArcCorr(const shared_ptr scanner_ptr, { uncompressed_view_tangpos_to_det1det2_initialised = false; det1det2_to_uncompressed_view_tangpos_initialised = false; + // If tof_mash_factor == 1 then there is only tof bin ... effectively no TOF + if (tof_mash_factor > 1) + set_tof_mash_factor(tof_mash_factor); //this->initialise_uncompressed_view_tangpos_to_det1det2(); //this->initialise_det1det2_to_uncompressed_view_tangpos(); } @@ -78,9 +82,10 @@ ProjDataInfoCylindricalNoArcCorr(const shared_ptr scanner_ptr, ProjDataInfoCylindricalNoArcCorr:: ProjDataInfoCylindricalNoArcCorr(const shared_ptr scanner_ptr, const VectorWithOffset& num_axial_pos_per_segment, - const VectorWithOffset& min_ring_diff_v, + const VectorWithOffset& min_ring_diff_v, const VectorWithOffset& max_ring_diff_v, - const int num_views,const int num_tangential_poss) + const int num_views, const int num_tangential_poss, + const int tof_mash_factor) : ProjDataInfoCylindrical(scanner_ptr, num_axial_pos_per_segment, min_ring_diff_v, max_ring_diff_v, @@ -91,6 +96,10 @@ ProjDataInfoCylindricalNoArcCorr(const shared_ptr scanner_ptr, angular_increment = static_cast(_PI/scanner_ptr->get_num_detectors_per_ring()); uncompressed_view_tangpos_to_det1det2_initialised = false; det1det2_to_uncompressed_view_tangpos_initialised = false; + + // If tof_mash_factor == 1 then there is only tof bin ... effectively no TOF + if (tof_mash_factor > 1) + set_tof_mash_factor(tof_mash_factor); //this->initialise_uncompressed_view_tangpos_to_det1det2(); //this->initialise_det1det2_to_uncompressed_view_tangpos(); } diff --git a/src/buildblock/Scanner.cxx b/src/buildblock/Scanner.cxx index 5d3713b890..4a440aaacc 100644 --- a/src/buildblock/Scanner.cxx +++ b/src/buildblock/Scanner.cxx @@ -1169,6 +1169,13 @@ Scanner::parameter_info() const s << "Reference energy (in keV) := " << get_reference_energy() << '\n'; } + if (is_tof_ready()) + { + s << "Number of TOF time bins :=" << get_num_max_of_timing_bins() << "\n"; + s << "Size of timing bin (ps) :=" << get_size_of_timing_bin() << "\n"; + s << "Timing resolution (ps) :=" << get_timing_resolution() << "\n"; + } + // block/bucket description s << "Number of blocks per bucket in transaxial direction := " << get_num_transaxial_blocks_per_bucket() << '\n' @@ -1237,8 +1244,9 @@ Scanner* Scanner::ask_parameters() if (scanner_ptr->type != Unknown_scanner && scanner_ptr->type != User_defined_scanner) { info("Two new options are available: (a) Energy Resolution and (b) Reference energy (in keV)." - "They are used in Scatter Simulation. In case, you need them, please set them" - "manually in your file."); + "They are used in Scatter Simulation. In case, you need them, please set them " + "manually in your file. More over, the creation of a Time-Of-Flight scanner with energy" + "information is not supported. You have to do it manually."); return scanner_ptr; } @@ -1284,6 +1292,13 @@ Scanner* Scanner::ask_parameters() int TransaxialCrystalsPerSinglesUnit = ask_num("Enter number of transaxial crystals per singles unit: ", 0, num_detectors_per_ring, 1); + int Num_TOF_bins = + ask_num("Number of TOF time bins :", 0.0f, 800.0f, 0.0f); + float Size_TOF_bin = + ask_num("Size of timing bin (ps) :", 0.0f, 100.0f, 0.0f); + float TOF_resolution = + ask_num("Timing resolution (ps) :", 0.0f, 1000.0f, 0.0f); + float EnergyResolution = ask_num("Enter the energy resolution of the scanner : ", 0.0f, 1000.0f, -1.0f); @@ -1294,6 +1309,21 @@ Scanner* Scanner::ask_parameters() ask_num("Enter number of detector layers per block: ",1,100,1); Type type = User_defined_scanner; + bool make_tof_scanner = false; + if (Num_TOF_bins > 0 && Size_TOF_bin > 0 && TOF_resolution > 0) + make_tof_scanner = true; + + Scanner* scanner_ptr = + new Scanner(type, string_list(name), + num_detectors_per_ring, NoRings, + NoBins, NoBins, + InnerRingRadius, AverageDepthOfInteraction, + RingSpacing, BinSize,intrTilt*float(_PI)/180, + AxialBlocksPerBucket,TransBlocksPerBucket, + AxialCrystalsPerBlock,TransaxialCrystalsPerBlock, + AxialCrstalsPerSinglesUnit, TransaxialCrystalsPerSinglesUnit, + num_detector_layers); + if (EnergyResolution > -1 && ReferenceEnergy > -1) Scanner* scanner_ptr = new Scanner(type, string_list(name), @@ -1308,16 +1338,27 @@ Scanner* Scanner::ask_parameters() EnergyResolution, ReferenceEnergy ); else - Scanner* scanner_ptr = - new Scanner(type, string_list(name), - num_detectors_per_ring, NoRings, - NoBins, NoBins, - InnerRingRadius, AverageDepthOfInteraction, - RingSpacing, BinSize,intrTilt*float(_PI)/180, - AxialBlocksPerBucket,TransBlocksPerBucket, - AxialCrystalsPerBlock,TransaxialCrystalsPerBlock, - AxialCrstalsPerSinglesUnit, TransaxialCrystalsPerSinglesUnit, - num_detector_layers); + Scanner* scanner_ptr = make_tof_scanner ? new Scanner(type, string_list(name), + num_detectors_per_ring, NoRings, + NoBins, NoBins, + InnerRingRadius, AverageDepthOfInteraction, + RingSpacing, BinSize,intrTilt*float(_PI)/180, + AxialBlocksPerBucket,TransBlocksPerBucket, + AxialCrystalsPerBlock,TransaxialCrystalsPerBlock, + AxialCrstalsPerSinglesUnit, TransaxialCrystalsPerSinglesUnit, + num_detector_layers, + Num_TOF_bins, + Size_TOF_bin, + TOF_resolution) : + new Scanner(type, string_list(name), + num_detectors_per_ring, NoRings, + NoBins, NoBins, + InnerRingRadius, AverageDepthOfInteraction, + RingSpacing, BinSize,intrTilt*float(_PI)/180, + AxialBlocksPerBucket,TransBlocksPerBucket, + AxialCrystalsPerBlock,TransaxialCrystalsPerBlock, + AxialCrstalsPerSinglesUnit, TransaxialCrystalsPerSinglesUnit, + num_detector_layers); if (scanner_ptr->check_consistency()==Succeeded::yes || !ask("Ask questions again?",true)) diff --git a/src/include/stir/Bin.inl b/src/include/stir/Bin.inl index 4f0e37bd20..9b3872d145 100644 --- a/src/include/stir/Bin.inl +++ b/src/include/stir/Bin.inl @@ -41,7 +41,7 @@ Bin::Bin():segment(0),view(0), Bin::Bin(int segment_num,int view_num, int axial_pos_num,int tangential_pos_num, float bin_value) :segment(segment_num),view(view_num), - axial_pos(axial_pos_num),tangential_pos(tangential_pos_num), bin_value(bin_value), timing_pos(0) + axial_pos(axial_pos_num),tangential_pos(tangential_pos_num), timing_pos(0), bin_value(bin_value) {} Bin::Bin(int segment_num,int view_num, int axial_pos_num,int tangential_pos_num) diff --git a/src/include/stir/IO/InterfileHeader.h b/src/include/stir/IO/InterfileHeader.h index 29998e32f7..b716a6dc80 100644 --- a/src/include/stir/IO/InterfileHeader.h +++ b/src/include/stir/IO/InterfileHeader.h @@ -196,6 +196,7 @@ class InterfilePDFSHeader : public InterfileHeader std::vector applied_corrections; // derived values + int num_timing_poss; int num_segments; int num_views; int num_bins; @@ -233,6 +234,12 @@ class InterfilePDFSHeader : public InterfileHeader float energy_resolution; //! Reference energy. float reference_energy; + + int max_num_timing_poss; + float size_of_timing_pos; + float timing_resolution; + + int tof_mash_factor; // end scanner parameters double effective_central_bin_size_in_cm; diff --git a/src/include/stir/ProjData.h b/src/include/stir/ProjData.h index 1bf1227f84..159a32cda3 100644 --- a/src/include/stir/ProjData.h +++ b/src/include/stir/ProjData.h @@ -183,11 +183,15 @@ class ProjData : public ExamData virtual SegmentByView get_segment_by_view(const int segment_num) const; //! Set segment by sinogram + //! N.E. Extended to have timging positions. virtual Succeeded - set_segment(const SegmentBySinogram&); + set_segment(const SegmentBySinogram&, + const int& timing_pos = 1); //! Set segment by view + //! N.E. Extended to have timing positions. virtual Succeeded - set_segment(const SegmentByView&); + set_segment(const SegmentByView&, + const int& timing_pos = 1); //! Get related viewgrams virtual RelatedViewgrams diff --git a/src/include/stir/ProjDataFromStream.h b/src/include/stir/ProjDataFromStream.h index 1e374194ba..a0adfd2453 100644 --- a/src/include/stir/ProjDataFromStream.h +++ b/src/include/stir/ProjDataFromStream.h @@ -61,7 +61,9 @@ class ProjDataFromStream : public ProjData public: enum StorageOrder { - Segment_AxialPos_View_TangPos, Segment_View_AxialPos_TangPos, + Segment_AxialPos_View_TangPos, + Segment_View_AxialPos_TangPos, + Timing_Segment_View_AxialPos_TangPos, Unsupported }; #if 0 static ProjDataFromStream* ask_parameters(const bool on_disk = true); @@ -87,20 +89,20 @@ class ProjDataFromStream : public ProjData StorageOrder o = Segment_View_AxialPos_TangPos, NumericType data_type = NumericType::FLOAT, ByteOrder byte_order = ByteOrder::native, - float scale_factor = 1 ); + float scale_factor = 1.f ); //! as above, but with a default value for segment_sequence_in_stream /*! The default value for segment_sequence_in_stream is a vector with values min_segment_num, min_segment_num+1, ..., max_segment_num */ ProjDataFromStream (shared_ptr const& exam_info_sptr, - shared_ptr const& proj_data_info_ptr, - shared_ptr const& s, - const std::streamoff offs = 0, - StorageOrder o = Segment_View_AxialPos_TangPos, - NumericType data_type = NumericType::FLOAT, - ByteOrder byte_order = ByteOrder::native, - float scale_factor = 1 ); + shared_ptr const& proj_data_info_ptr, + shared_ptr const& s, + const std::streamoff offs = 0, + StorageOrder o = Segment_View_AxialPos_TangPos, + NumericType data_type = NumericType::FLOAT, + ByteOrder byte_order = ByteOrder::native, + float scale_factor = 1.f); //! Obtain the storage order inline StorageOrder get_storage_order() const; @@ -116,6 +118,8 @@ class ProjDataFromStream : public ProjData //! Get the segment sequence inline std::vector get_segment_sequence_in_stream() const; + //! Get the timing bins sequence + inline std::vector get_timing_poss_sequence_in_stream() const; //! Get & set viewgram Viewgram get_viewgram(const int view_num, const int segment_num,const bool make_num_tangential_poss_odd=false) const; @@ -153,6 +157,8 @@ class ProjDataFromStream : public ProjData //!the order in which the segments occur in the stream std::vector segment_sequence; + //!the order in which the timing bins occur in the stream + std::vector timing_poss_sequence; inline int find_segment_index_in_sequence(const int segment_num) const; diff --git a/src/include/stir/ProjDataFromStream.inl b/src/include/stir/ProjDataFromStream.inl index 83791557f8..93c6aa9e96 100644 --- a/src/include/stir/ProjDataFromStream.inl +++ b/src/include/stir/ProjDataFromStream.inl @@ -73,6 +73,10 @@ std::vector ProjDataFromStream::get_segment_sequence_in_stream() const { return segment_sequence; } +std::vector +ProjDataFromStream::get_timing_poss_sequence_in_stream() const +{ return timing_poss_sequence; } + #if 0 // this does not make a lot of sense. How to compare files etc. ? bool diff --git a/src/include/stir/ProjDataInfo.h b/src/include/stir/ProjDataInfo.h index 43f64c3171..0d1b9522fb 100644 --- a/src/include/stir/ProjDataInfo.h +++ b/src/include/stir/ProjDataInfo.h @@ -72,21 +72,25 @@ class ProjDataInfo ask_parameters(); //! Construct a ProjDataInfo suitable for GE Advance data + //! \warning N.E: TOF mash factor, means no TOF static ProjDataInfo* - ProjDataInfoGE(const shared_ptr& scanner_ptr, - const int max_delta, - const int num_views, const int num_tangential_poss, - const bool arc_corrected = true); + ProjDataInfoGE(const shared_ptr& scanner_ptr, + const int max_delta, + const int num_views, const int num_tangential_poss, + const bool arc_corrected = true, + const int tof_mash_factor = 1); //! Construct a ProjDataInfo suitable for CTI data /*! \c span is used to denote the amount of axial compression (see CTI doc). It has to be an odd number. */ + //! \warning N.E: TOF mash factor, means no TOF static ProjDataInfo* - ProjDataInfoCTI(const shared_ptr& scanner_ptr, - const int span, const int max_delta, - const int num_views, const int num_tangential_poss, - const bool arc_corrected = true); + ProjDataInfoCTI(const shared_ptr& scanner_ptr, + const int span, const int max_delta, + const int num_views, const int num_tangential_poss, + const bool arc_corrected = true, + const int tof_mash_factor = 1); /************ constructors ***********/ diff --git a/src/include/stir/ProjDataInfoCylindricalArcCorr.h b/src/include/stir/ProjDataInfoCylindricalArcCorr.h index 2375f75461..7c6bfc3fdf 100644 --- a/src/include/stir/ProjDataInfoCylindricalArcCorr.h +++ b/src/include/stir/ProjDataInfoCylindricalArcCorr.h @@ -62,7 +62,8 @@ class ProjDataInfoCylindricalArcCorr : public ProjDataInfoCylindrical const VectorWithOffset& num_axial_pos_per_segment, const VectorWithOffset& min_ring_diff_v, const VectorWithOffset& max_ring_diff_v, - const int num_views,const int num_tangential_poss); + const int num_views,const int num_tangential_poss, + const int tof_mash_factor = 1); ProjDataInfo* clone() const; diff --git a/src/include/stir/ProjDataInfoCylindricalNoArcCorr.h b/src/include/stir/ProjDataInfoCylindricalNoArcCorr.h index ce11e41793..4137d4d8d5 100644 --- a/src/include/stir/ProjDataInfoCylindricalNoArcCorr.h +++ b/src/include/stir/ProjDataInfoCylindricalNoArcCorr.h @@ -108,7 +108,8 @@ class ProjDataInfoCylindricalNoArcCorr : public ProjDataInfoCylindrical const VectorWithOffset& num_axial_pos_per_segment, const VectorWithOffset& min_ring_diff_v, const VectorWithOffset& max_ring_diff_v, - const int num_views,const int num_tangential_poss); + const int num_views,const int num_tangential_poss, + const int tof_mash_factor = 1); //! Constructor which gets \a ring_radius and \a angular_increment from the scanner /*! \a angular_increment is determined as Pi divided by the number of detectors in a ring. @@ -117,7 +118,8 @@ class ProjDataInfoCylindricalNoArcCorr : public ProjDataInfoCylindrical const VectorWithOffset& num_axial_pos_per_segment, const VectorWithOffset& min_ring_diff_v, const VectorWithOffset& max_ring_diff_v, - const int num_views,const int num_tangential_poss); + const int num_views,const int num_tangential_poss, + const int tof_mash_factor = 1); ProjDataInfo* clone() const; diff --git a/src/include/stir/listmode/LmToProjData.h b/src/include/stir/listmode/LmToProjData.h index 96b92ed210..7a97ea2c2e 100644 --- a/src/include/stir/listmode/LmToProjData.h +++ b/src/include/stir/listmode/LmToProjData.h @@ -174,10 +174,16 @@ class LmToProjData : public ParsingObject LmToProjData(); //! This function does the actual work + //! N.E: In order to keep the ToF functions separate from the non-TOF + //! STIR this function just call the appropriate actual_process_data_with(out)_tof(). virtual void process_data(); protected: + //! This function does the non-TOF work + virtual void actual_process_data_without_tof(); + //! This function does the TOF work + virtual void actual_process_data_with_tof(); //! will be called when a new time frame starts /*! The frame numbers start from 1. */ @@ -192,7 +198,7 @@ class LmToProjData : public ParsingObject (on top of anything done by normalisation_ptr). \todo Would need timing info or so for e.g. time dependent normalisation or angle info for a rotating scanner.*/ - virtual void get_bin_from_event(Bin& bin, const CListEvent&) const; + virtual void get_bin_from_record(Bin& bin, const CListRecord&) const; //! A function that should return the number of uncompressed bins in the current bin /*! \todo it is not compatiable with e.g. HiDAC doesn't belong here anyway @@ -224,6 +230,8 @@ class LmToProjData : public ParsingObject bool do_pre_normalisation; bool store_prompts; bool store_delayeds; + //! Use TOF information + bool use_tof; int num_segments_in_memory; // TODO make long (or even unsigned long) but can't do this yet because we can't parse longs yet unsigned long int num_events_to_store; diff --git a/src/include/stir/listmode/LmToProjDataBootstrap.h b/src/include/stir/listmode/LmToProjDataBootstrap.h index 4d98a813eb..ea7cd8c78c 100644 --- a/src/include/stir/listmode/LmToProjDataBootstrap.h +++ b/src/include/stir/listmode/LmToProjDataBootstrap.h @@ -87,7 +87,7 @@ class LmToProjDataBootstrap : public LmToProjDataT /*! Initialises a vector with the number of times each event has to be replicated */ virtual void start_new_time_frame(const unsigned int new_frame_num); - virtual void get_bin_from_event(Bin& bin, const CListEvent&) const; + virtual void get_bin_from_record(Bin& bin, const CListRecord&) const; // \name parsing variables diff --git a/src/listmode_buildblock/LmToProjData.cxx b/src/listmode_buildblock/LmToProjData.cxx index 2113b26737..73f412020f 100644 --- a/src/listmode_buildblock/LmToProjData.cxx +++ b/src/listmode_buildblock/LmToProjData.cxx @@ -129,10 +129,10 @@ allocate_segments(VectorWithOffset& segments, */ static void save_and_delete_segments(shared_ptr& output, - VectorWithOffset& segments, - const int start_segment_index, - const int end_segment_index, - ProjData& proj_data); + VectorWithOffset& segments, + const int start_segment_index, + const int end_segment_index, + ProjData& proj_data, const int timing_pos = 1); static shared_ptr construct_proj_data(shared_ptr& output, @@ -156,7 +156,7 @@ set_defaults() post_normalisation_ptr.reset(new TrivialBinNormalisation); do_pre_normalisation =0; num_events_to_store = 0; - + use_tof = false; } void @@ -379,15 +379,16 @@ LmToProjData(const char * const par_filename) Here follows the implementation of get_bin_from_event this function is complicated because of the normalisation stuff. sorry + N.E: Get_bin_from_event became Get_bin_from_record ***************************************************************/ void LmToProjData:: -get_bin_from_event(Bin& bin, const CListEvent& event) const +get_bin_from_record(Bin& bin, const CListRecord& record) const { if (do_pre_normalisation) { Bin uncompressed_bin; - event.get_bin(uncompressed_bin, *proj_data_info_cyl_uncompressed_ptr); + record.event().get_bin(uncompressed_bin, *proj_data_info_cyl_uncompressed_ptr); if (uncompressed_bin.get_bin_value()<=0) return; // rejected for some strange reason @@ -410,7 +411,7 @@ get_bin_from_event(Bin& bin, const CListEvent& event) const bin_efficiency, uncompressed_bin.segment_num(), uncompressed_bin.view_num(), uncompressed_bin.axial_pos_num(), uncompressed_bin.tangential_pos_num()); - bin.set_bin_value(-1); + bin.set_bin_value(-1.f); return; } @@ -418,20 +419,22 @@ get_bin_from_event(Bin& bin, const CListEvent& event) const // Also, adjust the normalisation factor according to the number of // uncompressed bins in a compressed bin - const float bin_value = 1/bin_efficiency; + const float bin_value = 1.f/bin_efficiency; // TODO wasteful: we decode the event twice. replace by something like // template_proj_data_info_ptr->get_bin_from_uncompressed(bin, uncompressed_bin); - event.get_bin(bin, *template_proj_data_info_ptr); + - if (bin.get_bin_value()>0) - { - bin.set_bin_value(bin_value); - } + if (use_tof) + record.full_event(bin, *template_proj_data_info_ptr); + else + record.event().get_bin(bin, *template_proj_data_info_ptr); + + bin.set_bin_value(bin_value); } else { - event.get_bin(bin, *template_proj_data_info_ptr); + record.event().get_bin(bin, *template_proj_data_info_ptr); } } @@ -510,9 +513,29 @@ start_new_time_frame(const unsigned int) It's essentially simple, but is in fact complicated because of the facility to store only part of the segments in memory. ***************************************************************/ + void LmToProjData:: process_data() +{ + assert(!is_null_ptr(template_proj_data_info_ptr)); + + if (template_proj_data_info_ptr->get_num_tof_poss() > 1) + { + use_tof = true; + actual_process_data_with_tof(); + } + else + { + use_tof = false; + actual_process_data_without_tof(); + } +} + + +void +LmToProjData:: +actual_process_data_without_tof() { CPUTimer timer; timer.start(); @@ -643,7 +666,7 @@ process_data() // set value in case the event decoder doesn't touch it // otherwise it would be 0 and all events will be ignored bin.set_bin_value(1); - get_bin_from_event(bin, record.event()); + get_bin_from_record(bin, record); // check if it's inside the range we want to store if (bin.get_bin_value()>0 @@ -727,6 +750,236 @@ process_data() } +void +LmToProjData:: +actual_process_data_with_tof() +{ + CPUTimer timer; + timer.start(); + + // assume list mode data starts at time 0 + // we have to do this because the first time tag might occur only after a + // few coincidence events (as happens with ECAT scanners) + current_time = 0; + + double time_of_last_stored_event = 0; + long num_stored_events = 0; + VectorWithOffset + segments (template_proj_data_info_ptr->get_min_segment_num(), + template_proj_data_info_ptr->get_max_segment_num()); + + VectorWithOffset + frame_start_positions(1, static_cast(frame_defs.get_num_frames())); + shared_ptr record_sptr = lm_data_ptr->get_empty_record_sptr(); + CListRecord& record = *record_sptr; + + /* Here starts the main loop which will store the listmode data. */ + for (current_frame_num = 1; + current_frame_num<=frame_defs.get_num_frames(); + ++current_frame_num) + { + start_new_time_frame(current_frame_num); + + // construct ExamInfo appropriate for a single projdata with this time frame + ExamInfo this_frame_exam_info(*lm_data_ptr->get_exam_info_ptr()); + { + TimeFrameDefinitions this_time_frame_defs(frame_defs, current_frame_num); + this_frame_exam_info.set_time_frame_definitions(this_time_frame_defs); + } + + // *********** open output file + shared_ptr output; + shared_ptr proj_data_ptr; + + { + char rest[50]; + sprintf(rest, "_f%dg1d0b0", current_frame_num); + const string output_filename = output_filename_prefix + rest; + + proj_data_ptr = + construct_proj_data(output, output_filename, this_frame_exam_info, template_proj_data_info_ptr); + } + + long num_prompts_in_frame = 0; + long num_delayeds_in_frame = 0; + + const double start_time = frame_defs.get_start_time(current_frame_num); + const double end_time = frame_defs.get_end_time(current_frame_num); + + for (int current_timing_pos_index = proj_data_ptr->get_min_timing_pos_num(); + current_timing_pos_index <= proj_data_ptr->get_max_timing_pos_num(); + current_timing_pos_index += 1) + { + /* + For each start_segment_index, we check which events occur in the + segments between start_segment_index and + start_segment_index+num_segments_in_memory. + */ + for (int start_segment_index = proj_data_ptr->get_min_segment_num(); + start_segment_index <= proj_data_ptr->get_max_segment_num(); + start_segment_index += num_segments_in_memory) + { + + const int end_segment_index = + min( proj_data_ptr->get_max_segment_num()+1, start_segment_index + num_segments_in_memory) - 1; + + if (!interactive) + allocate_segments(segments, start_segment_index, end_segment_index, proj_data_ptr->get_proj_data_info_ptr()); + + // the next variable is used to see if there are more events to store for the current segments + // num_events_to_store-more_events will be the number of allowed coincidence events currently seen in the file + // ('allowed' independent on the fact of we have its segment in memory or not) + // When do_time_frame=true, the number of events is irrelevant, so we + // just set more_events to 1, and never change it + unsigned long int more_events = + do_time_frame? 1 : num_events_to_store; + + if (start_segment_index != proj_data_ptr->get_min_segment_num()) + { + // we're going once more through the data (for the next batch of segments) + cerr << "\nProcessing next batch of segments\n"; + // go to the beginning of the listmode data for this frame + lm_data_ptr->set_get_position(frame_start_positions[current_frame_num]); + current_time = start_time; + } + else + { + cerr << "\nProcessing time frame " << current_frame_num << '\n'; + + // Note: we already have current_time from previous frame, so don't + // need to set it. In fact, setting it to start_time would be wrong + // as we first might have to skip some events before we get to start_time. + // So, let's do that now. + while (current_time < start_time && + lm_data_ptr->get_next_record(record) == Succeeded::yes) + { + if (record.is_time()) + current_time = record.time().get_time_in_secs(); + } + // now save position such that we can go back + frame_start_positions[current_frame_num] = + lm_data_ptr->save_get_position(); + } + { + // loop over all events in the listmode file + while (more_events) + { + if (lm_data_ptr->get_next_record(record) == Succeeded::no) + { + // no more events in file for some reason + break; //get out of while loop + } + if (record.is_time() && end_time > 0.01) // Direct comparison within doubles is unsafe. + { + current_time = record.time().get_time_in_secs(); + if (do_time_frame && current_time >= end_time) + break; // get out of while loop + assert(current_time>=start_time); + process_new_time_event(record.time()); + } + // note: could do "else if" here if we would be sure that + // a record can never be both timing and coincidence event + // and there might be a scanner around that has them both combined. + if (record.is_event()) + { + assert(start_time <= current_time); + Bin bin; + // set value in case the event decoder doesn't touch it + // otherwise it would be 0 and all events will be ignored + bin.set_bin_value(1.f); + + get_bin_from_record(bin, record); + + // check if it's inside the range we want to store + if (bin.get_bin_value()>0 + && bin.tangential_pos_num()>= proj_data_ptr->get_min_tangential_pos_num() + && bin.tangential_pos_num()<= proj_data_ptr->get_max_tangential_pos_num() + && bin.axial_pos_num()>=proj_data_ptr->get_min_axial_pos_num(bin.segment_num()) + && bin.axial_pos_num()<=proj_data_ptr->get_max_axial_pos_num(bin.segment_num()) + && bin.timing_pos_num()>=proj_data_ptr->get_min_timing_pos_num() + && bin.timing_pos_num()<=proj_data_ptr->get_max_timing_pos_num() + ) + { + assert(bin.view_num()>=proj_data_ptr->get_min_view_num()); + assert(bin.view_num()<=proj_data_ptr->get_max_view_num()); + + // see if we increment or decrement the value in the sinogram + const int event_increment = + record.event().is_prompt() + ? ( store_prompts ? 1 : 0 ) // it's a prompt + : delayed_increment;//it is a delayed-coincidence event + + if (event_increment==0) + continue; + + if (!do_time_frame) + more_events -= event_increment; + + if (bin.timing_pos_num() == current_timing_pos_index) + { + // now check if we have its segment in memory + if (bin.segment_num() >= start_segment_index && bin.segment_num()<=end_segment_index) + { + do_post_normalisation(bin); + + num_stored_events += event_increment; + if (record.event().is_prompt()) + ++num_prompts_in_frame; + else + ++num_delayeds_in_frame; + + if (num_stored_events%500000L==0) cout << "\r" << num_stored_events << " events stored" << flush; + + if (interactive) + printf("TOFbin %4d Seg %4d view %4d ax_pos %4d tang_pos %4d time %8g stored with incr %d \n", + bin.timing_pos_num(),bin.segment_num(), + bin.view_num(), bin.axial_pos_num(), bin.tangential_pos_num(), + current_time, event_increment); + else + (*segments[bin.segment_num()])[bin.view_num()][bin.axial_pos_num()][bin.tangential_pos_num()] += + bin.get_bin_value() * + event_increment; + } + } + } + else // event is rejected for some reason + { + if (interactive) + printf("TOFbin %4d Seg %4d view %4d ax_pos %4d tang_pos %4d time %8g ignored\n", + bin.timing_pos_num(), bin.segment_num(), bin.view_num(), + bin.axial_pos_num(), bin.tangential_pos_num(), current_time); + } + } // end of spatial event processing + } // end of while loop over all events + + time_of_last_stored_event = + max(time_of_last_stored_event,current_time); + } + + if (!interactive) + save_and_delete_segments(output, segments, + start_segment_index, end_segment_index, + *proj_data_ptr, current_timing_pos_index); + } // end of for loop for segment range + + } // end of for loop for timing positions + cerr << "\nNumber of prompts stored in this time period : " << num_prompts_in_frame + << "\nNumber of delayeds stored in this time period: " << num_delayeds_in_frame + << '\n'; + } // end of loop over frames + + timer.stop(); + + cerr << "Last stored event was recorded before time-tick at " << time_of_last_stored_event << " secs\n"; + if (!do_time_frame && + (num_stored_events<=0 || + /*static_cast*/(num_stored_events)& output, VectorWithOffset& segments, const int start_segment_index, const int end_segment_index, - ProjData& proj_data) + ProjData& proj_data, + const int timing_pos) { for (int seg=start_segment_index; seg<=end_segment_index; seg++) { { #ifdef USE_SegmentByView - proj_data.set_segment(*segments[seg]); + proj_data.set_segment(*segments[seg], timing_pos); #else (*segments[seg]).write_data(*output); #endif @@ -786,13 +1040,20 @@ construct_proj_data(shared_ptr& output, const shared_ptr& proj_data_info_ptr) { shared_ptr exam_info_sptr(new ExamInfo(exam_info)); - + shared_ptr proj_data_sptr; #ifdef USE_SegmentByView // don't need output stream in this case - shared_ptr proj_data_sptr(new ProjDataInterfile(exam_info_sptr, - proj_data_info_ptr, output_filename, ios::out, - ProjDataFromStream::Segment_View_AxialPos_TangPos, - OUTPUTNumericType)); + if (proj_data_info_ptr->get_tof_mash_factor() == 1) + proj_data_sptr.reset(new ProjDataInterfile(exam_info_sptr, + proj_data_info_ptr, output_filename, ios::out, + ProjDataFromStream::Segment_View_AxialPos_TangPos, + OUTPUTNumericType)); + else + proj_data_sptr.reset(new ProjDataInterfile(exam_info_sptr, + proj_data_info_ptr, output_filename, ios::out, + ProjDataFromStream::Timing_Segment_View_AxialPos_TangPos, + OUTPUTNumericType)); + return proj_data_sptr; #else // this code would work for USE_SegmentByView as well, but the above is far simpler... diff --git a/src/listmode_buildblock/LmToProjDataBootstrap.cxx b/src/listmode_buildblock/LmToProjDataBootstrap.cxx index d67de6db04..6327f8b193 100644 --- a/src/listmode_buildblock/LmToProjDataBootstrap.cxx +++ b/src/listmode_buildblock/LmToProjDataBootstrap.cxx @@ -166,7 +166,7 @@ start_new_time_frame(const unsigned int new_frame_num) // set value in case the event decoder doesn't touch it // otherwise it would be 0 and all events will be ignored bin.set_bin_value(1); - base_type::get_bin_from_event(bin, record.event()); + base_type::get_bin_from_record(bin, record); // check if it's inside the range we want to store if (bin.get_bin_value()>0 && bin.tangential_pos_num()>= this->template_proj_data_info_ptr->get_min_tangential_pos_num() @@ -232,12 +232,12 @@ start_new_time_frame(const unsigned int new_frame_num) template void LmToProjDataBootstrap:: -get_bin_from_event(Bin& bin, const CListEvent& event) const +get_bin_from_record(Bin& bin, const CListRecord& record) const { assert(num_times_to_replicate_iter != num_times_to_replicate.end()); if (*num_times_to_replicate_iter > 0) { - base_type::get_bin_from_event(bin, event); + base_type::get_bin_from_record(bin, record); bin.set_bin_value(bin.get_bin_value() * *num_times_to_replicate_iter); } else diff --git a/src/listmode_utilities/lm_to_projdata.cxx b/src/listmode_utilities/lm_to_projdata.cxx index 6c81789320..9fa37abaf0 100644 --- a/src/listmode_utilities/lm_to_projdata.cxx +++ b/src/listmode_utilities/lm_to_projdata.cxx @@ -50,7 +50,7 @@ int main(int argc, char * argv[]) "Run "<use_tof) { - // proj_data_no_arc_ptr->get_LOR_as_two_points(lor_point_1, lor_point_2, measured_bin); lor_points = record.event().get_LOR(); this->PM_sptr->get_proj_matrix_elems_for_one_bin_with_tof(proj_matrix_row, measured_bin, diff --git a/src/test/test_proj_data_info.cxx b/src/test/test_proj_data_info.cxx index 64c5f47f20..46ebe73813 100644 --- a/src/test/test_proj_data_info.cxx +++ b/src/test/test_proj_data_info.cxx @@ -143,7 +143,7 @@ test_generic_proj_data_info(ProjDataInfo& proj_data_info) tangential_pos_num<=proj_data_info.get_max_tangential_pos_num()-1; tangential_pos_num+=1) { - const Bin org_bin(segment_num,view_num,axial_pos_num,tangential_pos_num, /* value*/1); + const Bin org_bin(segment_num,view_num,axial_pos_num,tangential_pos_num, /* value*/1.f); LORInAxialAndNoArcCorrSinogramCoordinates lor; proj_data_info.get_LOR(lor, org_bin); { @@ -599,10 +599,10 @@ run_tests() cerr << "\nTests with proj_data_info with mashing and axial compression\n\n"; proj_data_info_ptr.reset( ProjDataInfo::ProjDataInfoCTI(scanner_ptr, - /*span*/5, scanner_ptr->get_num_rings()-1, - /*views*/ scanner_ptr->get_num_detectors_per_ring()/2/8, - /*tang_pos*/64, - /*arc_corrected*/ false)); + /*span*/5, scanner_ptr->get_num_rings()-1, + /*views*/ scanner_ptr->get_num_detectors_per_ring()/2/8, + /*tang_pos*/64, + /*arc_corrected*/ false)); test_proj_data_info(dynamic_cast(*proj_data_info_ptr)); } @@ -724,7 +724,7 @@ test_proj_data_info(ProjDataInfoCylindricalNoArcCorr& proj_data_info) // or an LOR parallel to the scanner axis if (det_pos_pair.pos1().tangential_coord() == det_pos_pair.pos2().tangential_coord()) continue; - Bin bin; + Bin bin(0,0,0,0,0,0.0f); DetectionPositionPair<> new_det_pos_pair; const bool there_is_a_bin = proj_data_info.get_bin_for_det_pos_pair(bin, det_pos_pair) == @@ -754,9 +754,9 @@ test_proj_data_info(ProjDataInfoCylindricalNoArcCorr& proj_data_info) cerr << "\n\tTest code for bin -> detector,ring and back conversions. (This might take a while...)"; { - Bin bin; + Bin bin(0,0,0,0,0,0.0f); // set value for comparison later on - bin.set_bin_value(0); + bin.set_bin_value(0.f); for (bin.segment_num() = max(-5,proj_data_info.get_min_segment_num()); bin.segment_num() <= min(5,proj_data_info.get_max_segment_num()); ++bin.segment_num()) @@ -776,7 +776,7 @@ test_proj_data_info(ProjDataInfoCylindricalNoArcCorr& proj_data_info) { // set from for-loop variable bin.tangential_pos_num() = tangential_pos_num; - Bin new_bin; + Bin new_bin(0,0,0,0,0,0.0f); // set value for comparison with bin new_bin.set_bin_value(0); DetectionPositionPair<> det_pos_pair; @@ -809,7 +809,7 @@ test_proj_data_info(ProjDataInfoCylindricalNoArcCorr& proj_data_info) { cerr << "\n\tTest code for bins <-> detectors routines that work with any mashing and axial compression"; - Bin bin; + Bin bin(0,0,0,0,0,0.0f); // set value for comparison later on bin.set_bin_value(0); std::vector > det_pos_pairs; @@ -830,7 +830,7 @@ test_proj_data_info(ProjDataInfoCylindricalNoArcCorr& proj_data_info) ++bin.tangential_pos_num()) { proj_data_info.get_all_det_pos_pairs_for_bin(det_pos_pairs, bin); - Bin new_bin; + Bin new_bin(0,0,0,0,0,0.0f); // set value for comparison with bin new_bin.set_bin_value(0); for (std::vector >::const_iterator det_pos_pair_iter = det_pos_pairs.begin(); diff --git a/src/utilities/create_projdata_template.cxx b/src/utilities/create_projdata_template.cxx index 0364d13682..e33eb399db 100644 --- a/src/utilities/create_projdata_template.cxx +++ b/src/utilities/create_projdata_template.cxx @@ -56,13 +56,18 @@ int main(int argc, char *argv[]) } - shared_ptr proj_data_info_ptr(ProjDataInfo::ask_parameters()); + shared_ptr proj_data_info_sptr(ProjDataInfo::ask_parameters()); const std::string output_file_name = argv[1]; shared_ptr exam_info_sptr(new ExamInfo); // TODO, Currently all stir::Scanner types are PET. exam_info_sptr->imaging_modality = ImagingModality::PT; - shared_ptr proj_data_ptr(new ProjDataInterfile(exam_info_sptr, proj_data_info_ptr, output_file_name)); + // If TOF activated -- No mashing factor will produce surrealistic sinograms + if ( proj_data_info_sptr->get_tof_mash_factor() >1) + shared_ptr proj_data_sptr(new ProjDataInterfile(exam_info_sptr, proj_data_info_sptr, output_file_name, std::ios::out, + ProjDataFromStream::Timing_Segment_View_AxialPos_TangPos)); + else + shared_ptr proj_data_sptr(new ProjDataInterfile(exam_info_sptr, proj_data_info_sptr, output_file_name)); return EXIT_SUCCESS; } From fe31466b608d375117ee9b1b9799deee13935481 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Mon, 6 Feb 2017 09:05:52 +0000 Subject: [PATCH 051/170] LmToProjData is TOF compatible list_projdata_info gives information about each tof bin And there is a test running to validate that the unlisting is performed properly. --- recon_test_pack/root_header.hroot | 5 + recon_test_pack/run_test_time_of_fligh.sh | 106 +++ src/buildblock/ProjData.cxx | 14 +- src/buildblock/ProjDataFromStream.cxx | 678 ++++++++++++------ src/buildblock/ProjDataGEAdvance.cxx | 9 +- src/buildblock/ProjDataInfo.cxx | 6 +- src/buildblock/Scanner.cxx | 117 +-- src/include/stir/Bin.h | 11 +- src/include/stir/IO/InputStreamFromROOTFile.h | 22 +- ...InputStreamFromROOTFileForCylindricalPET.h | 1 + src/include/stir/ProjData.h | 26 +- src/include/stir/ProjDataFromStream.h | 44 +- src/include/stir/ProjDataGEAdvance.h | 10 +- src/include/stir/ProjDataInfo.h | 10 +- src/include/stir/Scanner.h | 4 +- src/include/stir/listmode/CListRecordROOT.h | 22 +- src/include/stir/listmode/LmToProjData.h | 10 + .../stir/recon_buildblock/ProjMatrixByBin.inl | 5 +- src/listmode_buildblock/CListRecordROOT.cxx | 14 +- src/listmode_buildblock/LmToProjData.cxx | 516 +++++++------ src/listmode_utilities/lm_to_projdata.cxx | 13 + src/test/test_proj_data_in_memory.cxx | 52 +- src/test/test_proj_data_info.cxx | 2 + src/test/test_time_of_flight.cxx | 80 +-- src/utilities/list_projdata_info.cxx | 76 +- 25 files changed, 1110 insertions(+), 743 deletions(-) create mode 100755 recon_test_pack/run_test_time_of_fligh.sh diff --git a/recon_test_pack/root_header.hroot b/recon_test_pack/root_header.hroot index 3bada70ad8..f8af3f7559 100644 --- a/recon_test_pack/root_header.hroot +++ b/recon_test_pack/root_header.hroot @@ -8,6 +8,11 @@ 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 +Number of TOF time bins := 410 +Size of timing bin (in picoseconds) := 10.00 +Timing resolution (in picoseconds) := 400.0 + +%TOF mashing factor:= 82 GATE scanner type := GATE_Cylindrical_PET GATE_Cylindrical_PET Parameters := diff --git a/recon_test_pack/run_test_time_of_fligh.sh b/recon_test_pack/run_test_time_of_fligh.sh new file mode 100755 index 0000000000..9d2d5ecd4d --- /dev/null +++ b/recon_test_pack/run_test_time_of_fligh.sh @@ -0,0 +1,106 @@ +#! /bin/sh +# A script to check to see if Time Of Flight data are binned and used properly +# +# Copyright (C) 2016, University of Leeds +# Copyright (C) 2017, University of Hull +# This file is part of STIR. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# This file is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# See STIR/LICENSE.txt for details +# +# Author Nikos Efthimiou +# + +# Scripts should exit with error code when a test fails: +if [ -n "$TRAVIS" ]; then + # The code runs inside Travis + set -e +fi + +echo This script should work with STIR version '>'3.0. If you have +echo a later version, you might have to update your test pack. +echo Please check the web site. +echo + +if [ $# -eq 1 ]; then + echo "Prepending $1 to your PATH for the duration of this script." + PATH=$1:$PATH +fi + +# first need to set this to the C locale, as this is what the STIR utilities use +# otherwise, awk might interpret floating point numbers incorrectly +LC_ALL=C +export LC_ALL + +echo "=== create template sinogram. We'll use a test_scanner which is small and +has TOF info" +template_sino=my_test_scanner_template.hs +cat > 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; +fi + +export INPUT_ROOT_FILE=test_PET_GATE.root +export EXCLUDE_RANDOM=1 +export EXCLUDE_SCATTERED=1 + +INPUT=root_header.hroot TEMPLATE=$template_sino OUT_PROJDATA_FILE=my_tof_sinogram lm_to_projdata --test_timing_positions lm_to_projdata.par > my_write_TOF_values_${template_sino}.log 2>&1 + +if [ $? -ne 0 ]; then + echo "ERROR running lm_to_projdata --test_timing_positions. Check my_write_TOF_values_${template_sino}.log"; exit 1; +fi + +echo "Comparing values in TOF sinogram ..." +list_projdata_info --all my_tof_sinogram_f179g1d0b0.hs > my_sino_values_$template_sino.log 2>&1 +if [ $? -ne 0 ]; then + echo "ERROR running list_projdata_info. Check my_sino_values_$template_sino.log"; + exit 1; +fi + + +TOF_bins=$(grep 'Total number of timing positions' my_sino_values_$template_sino.log | awk -F ':' '{ print $2 }') +echo "Total number of TOF bins:" $TOF_bins + +Timming_Locations=$(grep 'Timing location' my_sino_values_$template_sino.log | awk -F ':' '{ print $2 }') +echo "Timming_Locations:" $Timming_Locations + +Data_mins=$(grep 'Data min' my_sino_values_$template_sino.log | awk -F ':' '{ print $2 }') +echo "Data mins:" $Data_mins + +Data_maxs=$(grep 'Data max' my_sino_values_$template_sino.log | awk -F ':' '{ print $2 }') +echo "Data maxs:" $Data_maxs + +for i in $(seq 5) +do + if [ $(( $(($(($i-1)) - $((TOF_bins/2)))) - $((Data_mins[$i])))) -ne 0 ]; then + echo "Wrong values in TOF sinogram. Error. $(( $(($(($i-1)) - $((TOF_bins/2)))) - $((Data_mins[$i]))))" + exit 1 + fi +done + + +echo +echo '--------------- End of Time-Of-Flight tests -------------' +echo +echo "Everything seems to be fine !" +echo 'You could remove all generated files using "rm -f my_* *.log"' +exit 0 + diff --git a/src/buildblock/ProjData.cxx b/src/buildblock/ProjData.cxx index 416a62804e..c5b1e77e16 100644 --- a/src/buildblock/ProjData.cxx +++ b/src/buildblock/ProjData.cxx @@ -3,6 +3,7 @@ Copyright (C) 2000 - 2010-10-15, Hammersmith Imanet Ltd Copyright (C) 2011-07-01 -2013, Kris Thielemans Copyright (C) 2015, University College London + Copyright (C) 2016, University of Hull This file is part of STIR. This file is free software; you can redistribute it and/or modify @@ -23,6 +24,7 @@ \brief Implementations for non-inline functions of class stir::ProjData + \author Nikos Efthimiou \author Kris Thielemans \author PARAPET project */ @@ -330,24 +332,24 @@ 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_num) const { SegmentBySinogram segment = proj_data_info_ptr->get_empty_segment_by_sinogram(segment_num,false); // 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_num)); 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_num) const { SegmentByView segment = proj_data_info_ptr->get_empty_segment_by_view(segment_num,false); // 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_num)); return segment; } @@ -358,7 +360,7 @@ ProjData::set_segment(const SegmentBySinogram& segment, { for (int view_num = get_min_view_num(); view_num <= get_max_view_num(); ++view_num) { - if(set_viewgram(segment.get_viewgram(view_num)) + if(set_viewgram(segment.get_viewgram(view_num), timing_pos) == Succeeded::no) return Succeeded::no; } @@ -371,7 +373,7 @@ ProjData::set_segment(const SegmentByView& segment, { for (int view_num = get_min_view_num(); view_num <= get_max_view_num(); ++view_num) { - if(set_viewgram(segment.get_viewgram(view_num)) + if(set_viewgram(segment.get_viewgram(view_num), timing_pos) == Succeeded::no) return Succeeded::no; } diff --git a/src/buildblock/ProjDataFromStream.cxx b/src/buildblock/ProjDataFromStream.cxx index c768561476..0639af379f 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 @@ -13,6 +14,7 @@ Copyright (C) 2000 - 2011-12-21, Hammersmith Imanet Ltd Copyright (C) 2011-2012, Kris Thielemans Copyright (C) 2013, University College London + Copyright (C) 2016, University of Hull This file is part of STIR. @@ -71,14 +73,14 @@ 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_ptr, + 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), sino_stream(s), offset(offs), @@ -90,15 +92,40 @@ ProjDataFromStream::ProjDataFromStream(shared_ptr const& exam_info_spt { assert(storage_order != Unsupported); assert(!(data_type == NumericType::UNKNOWN_TYPE)); + + int sum = 0; + for (int segment_num = proj_data_info_ptr->get_min_segment_num(); + segment_num<=proj_data_info_ptr->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 == Timing_Segment_View_AxialPos_TangPos && + proj_data_info_ptr->get_num_timing_poss() > 1) + { + timing_poss_sequence.resize(proj_data_info_ptr->get_num_timing_poss()); + int timing_pos_num; + + for (int i= 0, timing_pos_num = proj_data_info_ptr->get_min_timing_pos_num(); + timing_pos_num<=proj_data_info_ptr->get_max_timing_pos_num(); + ++i, ++timing_pos_num) + { + timing_poss_sequence[i] = timing_pos_num; + } + } } 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_ptr, + 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), sino_stream(s), offset(offs), @@ -112,17 +139,22 @@ ProjDataFromStream::ProjDataFromStream(shared_ptr const& exam_info_spt segment_sequence.resize(proj_data_info_ptr->get_num_segments()); - int segment_num, i; + //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, tmp_add = 0; for (i= 0, segment_num = proj_data_info_ptr->get_min_segment_num(); - segment_num<=proj_data_info_ptr->get_max_segment_num(); + segment_num<=proj_data_info_ptr->get_max_segment_num(); ++i, ++segment_num) { segment_sequence[i] =segment_num; + tmp_add += get_num_axial_poss(segment_num) * get_num_views() * get_num_tangential_poss(); } - // In this case lets start a TOF stream - Similar to segments + offset_3d_data = static_cast (tmp_add * on_disk_data_type.size_in_bytes()); + + // Now, lets initialise a TOF stream - Similarly to segments if (storage_order == Timing_Segment_View_AxialPos_TangPos && - proj_data_info_ptr->get_tof_mash_factor() > 1) + proj_data_info_ptr->get_num_timing_poss() > 1) { timing_poss_sequence.resize(proj_data_info_ptr->get_num_timing_poss()); int timing_pos_num; @@ -133,13 +165,12 @@ ProjDataFromStream::ProjDataFromStream(shared_ptr const& exam_info_spt { timing_poss_sequence[i] = timing_pos_num; } - } } -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 (sino_stream == 0) { @@ -149,16 +180,16 @@ 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); - + + vector offsets = get_offsets(view_num,segment_num, timing_pos); + const streamoff segment_offset = offsets[0]; const streamoff beg_view_offset = offsets[1]; const streamoff intra_views_offset = offsets[2]; - + 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) { error("ProjDataFromStream::get_viewgram: error after seekg\n"); @@ -166,12 +197,12 @@ ProjDataFromStream::get_viewgram(const int view_num, const int segment_num, Viewgram viewgram(proj_data_info_ptr, view_num, segment_num); float scale = float(1); - + 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) error("ProjDataFromStream: error reading data\n"); @@ -182,8 +213,8 @@ ProjDataFromStream::get_viewgram(const int view_num, const int 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) @@ -196,18 +227,18 @@ ProjDataFromStream::get_viewgram(const int view_num, const int segment_num, 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; viewgram.grow( IndexRange2D(get_min_axial_pos_num(segment_num), get_max_axial_pos_num(segment_num), - + get_min_tangential_pos_num(), - new_max_tangential_pos)); - } + new_max_tangential_pos)); + } - return viewgram; + return viewgram; } float @@ -223,7 +254,8 @@ ProjDataFromStream::get_bin_value(const Bin& this_bin) const } vector offsets = get_offsets_bin(this_bin.segment_num(), this_bin.axial_pos_num(), - this_bin.view_num(), this_bin.tangential_pos_num()); + this_bin.view_num(), this_bin.tangential_pos_num(), + this_bin.timing_pos_num()); const streamoff total_offset = offsets[0]; @@ -252,7 +284,8 @@ ProjDataFromStream::get_bin_value(const Bin& this_bin) const vector -ProjDataFromStream::get_offsets(const int view_num, const int segment_num) const +ProjDataFromStream::get_offsets(const int view_num, const int segment_num, + const int timing_num) const { if (!(segment_num >= get_min_segment_num() && @@ -260,9 +293,8 @@ ProjDataFromStream::get_offsets(const int view_num, const int segment_num) const 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); - + 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) - + 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_view_offset = (view_num - get_min_view_num()) *get_num_tangential_poss() * on_disk_data_type.size_in_bytes(); - - const streamoff intra_views_offset = + + 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) + } + 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) + (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; - + } + else if (get_storage_order() == Timing_Segment_View_AxialPos_TangPos) + { + // The timing offset will be added to the segment offset. This approach we minimise the + // changes + if (!(timing_num >= get_min_timing_pos_num() && + timing_num <= get_max_timing_pos_num())) + error("ProjDataFromStream::get_offsets: timing_num out of range : %d", timing_num); + + const int timing_index = static_cast(FIND(timing_poss_sequence.begin(), timing_poss_sequence.end(), timing_num) - + timing_poss_sequence.begin()); + + assert(offset_3d_data > 0); + segment_offset += static_cast(timing_index) * offset_3d_data; + + 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) +ProjDataFromStream::set_viewgram(const Viewgram& v, const int &timing_pos) { if (sino_stream == 0) { @@ -340,23 +398,23 @@ 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_ptr()->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_ptr() != *(v.get_proj_data_info_ptr())) { warning("ProjDataFromStream::set_viewgram: viewgram has incompatible ProjDataInfo member\n" @@ -368,31 +426,31 @@ ProjDataFromStream::set_viewgram(const Viewgram& v) return Succeeded::no; } - int segment_num = v.get_segment_num(); + int segment_num = v.get_segment_num(); int view_num = v.get_view_num(); - - vector offsets = get_offsets(view_num,segment_num); + + vector offsets = get_offsets(view_num,segment_num, timing_pos); const streamoff segment_offset = offsets[0]; const streamoff beg_view_offset = offsets[1]; const streamoff intra_views_offset = offsets[2]; 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 seekg\n"); return Succeeded::no; - } + } float scale = scale_factor; - + 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) + + if (write_data(*sino_stream, v[ax_pos_num], on_disk_data_type, scale, on_disk_byte_order) == Succeeded::no || scale != scale_factor) { @@ -420,9 +478,22 @@ ProjDataFromStream::set_viewgram(const Viewgram& v) } return Succeeded::yes; } + else if (get_storage_order() == Timing_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); + return Succeeded::no; + } + return Succeeded::yes; + } else { - warning("ProjDataFromStream::set_viewgram: unsupported storage order\n"); + warning("ProjDataFromStream::set_viewgram: unsupported storage order\n"); return Succeeded::no; } } @@ -432,7 +503,7 @@ std::vector ProjDataFromStream::get_offsets_bin(const int segment_num, const int ax_pos_num, const int view_num, - const int tang_pos_num) const + const int tang_pos_num, const int timing_pos_num) const { if (!(segment_num >= get_min_segment_num() && @@ -454,7 +525,7 @@ ProjDataFromStream::get_offsets_bin(const int segment_num, 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() * @@ -488,8 +559,45 @@ ProjDataFromStream::get_offsets_bin(const int segment_num, return temp; } - else //if (get_storage_order() == Segment_View_AxialPos_TangPos) + else if (get_storage_order() == Segment_View_AxialPos_TangPos) + { + + // Skip views + const streamoff 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(); + + + // find axial pos + const streamoff ax_pos_offset = + (ax_pos_num - get_min_axial_pos_num(segment_num)) * + get_num_tangential_poss()* + on_disk_data_type.size_in_bytes(); + + // find tang pos + const streamoff tang_offset = + (tang_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; + } + else if (get_storage_order() == Timing_Segment_View_AxialPos_TangPos) { + // The timing offset will be added to the segment offset. This approach we minimise the + // changes + if (!(timing_pos_num >= get_min_timing_pos_num() && + timing_pos_num <= get_max_timing_pos_num())) + error("ProjDataFromStream::get_offsets_bin: timing_num out of range : %d", timing_pos_num); + + const int timing_index = static_cast(FIND(timing_poss_sequence.begin(), timing_poss_sequence.end(), 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 = @@ -514,45 +622,49 @@ ProjDataFromStream::get_offsets_bin(const int segment_num, return temp; } + else + { + error("ProjDataFromStream::get_offsets_bin: unsupported storage order\n"); + } } // get offsets for the sino data vector -ProjDataFromStream::get_offsets_sino(const int ax_pos_num, const int segment_num) const +ProjDataFromStream::get_offsets_sino(const int ax_pos_num, const int segment_num, const int timing_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))) + 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) - + 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_views() * get_num_tangential_poss()* on_disk_data_type.size_in_bytes(); @@ -560,32 +672,62 @@ ProjDataFromStream::get_offsets_sino(const int ax_pos_num, const int segment_num temp[0] = segment_offset; temp[1] = beg_ax_pos_offset; temp[2] = 0; - + return temp; - } - else //if (get_storage_order() == Segment_View_AxialPos_TangPos) + } + 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 = + + 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; - + } + else if (get_storage_order() == Timing_Segment_View_AxialPos_TangPos) + { + // The timing offset will be added to the segment offset. This approach we minimise the + // changes + if (!(timing_num >= get_min_timing_pos_num() && + timing_num <= get_max_timing_pos_num())) + error("ProjDataFromStream::get_offsets: timing_num out of range : %d", timing_num); + + const int timing_index = static_cast(FIND(timing_poss_sequence.begin(), timing_poss_sequence.end(), timing_num) - + timing_poss_sequence.begin()); + + assert(offset_3d_data > 0); + segment_offset += static_cast(timing_index) * offset_3d_data; + + 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; + } + else + { + error("ProjDataFromStream::get_offsets_sino: unsupported storage order\n"); } } 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 (sino_stream == 0) { @@ -595,18 +737,18 @@ ProjDataFromStream::get_sinogram(const int ax_pos_num, const int segment_num, { error("ProjDataFromStream::get_sinogram: error in stream state before reading\n"); } - + // 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); - + vector offsets = get_offsets_sino(ax_pos_num,segment_num, timing_pos); + const streamoff segment_offset = offsets[0]; const streamoff beg_ax_pos_offset = offsets[1]; const streamoff intra_ax_pos_offset = offsets[2]; - + 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) { error("ProjDataFromStream::get_sinogram: error after seekg\n"); @@ -614,17 +756,17 @@ ProjDataFromStream::get_sinogram(const int ax_pos_num, const int segment_num, Sinogram sinogram(proj_data_info_ptr, ax_pos_num, segment_num); float scale = float(1); - + if (get_storage_order() == Segment_AxialPos_View_TangPos) - { + { if(read_data(*sino_stream, sinogram, on_disk_data_type, scale, on_disk_byte_order) == Succeeded::no) error("ProjDataFromStream: error reading data\n"); if(scale != 1) error("ProjDataFromStream: error reading data: scale factor returned by read_data should be 1\n"); } - - + + else if (get_storage_order() == Segment_View_AxialPos_TangPos) { for (int view = get_min_view_num(); view <= get_max_view_num(); view++) @@ -637,7 +779,7 @@ ProjDataFromStream::get_sinogram(const int ax_pos_num, const int segment_num, // 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); - } + } } sinogram *= scale_factor; @@ -648,16 +790,16 @@ ProjDataFromStream::get_sinogram(const int ax_pos_num, const int segment_num, sinogram.grow(IndexRange2D(get_min_view_num(), get_max_view_num(), get_min_tangential_pos_num(), - new_max_tangential_pos)); + new_max_tangential_pos)); } - + return sinogram; - - + + } Succeeded -ProjDataFromStream::set_sinogram(const Sinogram& s) +ProjDataFromStream::set_sinogram(const Sinogram& s, const int &timing_pos) { if (sino_stream == 0) { @@ -674,9 +816,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_ptr() != *(s.get_proj_data_info_ptr())) { warning("ProjDataFromStream::set_sinogram: Sinogram has incompatible ProjDataInfo member.\n" @@ -688,28 +830,28 @@ 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); + + + vector offsets = get_offsets_sino(ax_pos_num,segment_num, timing_pos); const streamoff segment_offset = offsets[0]; const streamoff beg_ax_pos_offset = offsets[1]; const streamoff intra_ax_pos_offset = offsets[2]; - + 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"); return Succeeded::no; - } + } float scale = scale_factor; - - + + 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 @@ -723,7 +865,7 @@ ProjDataFromStream::set_sinogram(const Sinogram& s) return Succeeded::yes; } - + else if (get_storage_order() == Segment_View_AxialPos_TangPos) { for (int view = get_min_view_num();view <= get_max_view_num(); view++) @@ -745,7 +887,7 @@ ProjDataFromStream::set_sinogram(const Sinogram& s) } else { - warning("ProjDataFromStream::set_sinogram: unsupported storage order\n"); + warning("ProjDataFromStream::set_sinogram: unsupported storage order\n"); return Succeeded::no; } } @@ -755,25 +897,53 @@ 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) - + { + 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(FIND(timing_poss_sequence.begin(), timing_poss_sequence.end(), timing_num) - + timing_poss_sequence.begin()); + + assert (timing_num >= get_min_timing_pos_num() && + timing_num <= get_max_timing_pos_num()); + { + if(offset_3d_data < 0 ) // Calculate the full 3D sinogram size - Very slow + { + long long sum = 0; + + for (int segment_num = proj_data_info_ptr->get_min_segment_num(); + segment_num<=proj_data_info_ptr->get_max_segment_num(); + ++segment_num) + { + sum += get_num_axial_poss(segment_num) * get_num_views() * get_num_tangential_poss(); + } + return static_cast (sum * index * on_disk_data_type.size_in_bytes()); + } + else + return static_cast(offset_3d_data * index); + } } @@ -781,7 +951,7 @@ ProjDataFromStream::get_offset_segment(const int segment_num) const // -> 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(sino_stream == 0) { @@ -791,44 +961,44 @@ 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); sino_stream->seekg(segment_offset, ios::beg); if (! *sino_stream) { error("ProjDataFromStream::get_segment_by_sinogram: error after seekg\n"); } - + if (get_storage_order() == Segment_AxialPos_View_TangPos) { SegmentBySinogram segment(proj_data_info_ptr,segment_num); { float scale = float(1); - if(read_data(*sino_stream, segment, on_disk_data_type, scale, on_disk_byte_order) + if(read_data(*sino_stream, segment, on_disk_data_type, scale, on_disk_byte_order) == Succeeded::no) error("ProjDataFromStream: error reading data\n"); if(scale != 1) - error("ProjDataFromStream: error reading data: scale factor returned by read_data should be 1\n"); + error("ProjDataFromStream: error reading data: scale factor returned by read_data should be 1\n"); } - + segment *= scale_factor; - + return segment; - + } else { // TODO rewrite in terms of get_viewgram return SegmentBySinogram (get_segment_by_view(segment_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(sino_stream == 0) { error("ProjDataFromStream::get_segment_by_view: stream ptr is 0\n"); @@ -837,34 +1007,64 @@ 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) { - + streamoff segment_offset = get_offset_segment(segment_num); sino_stream->seekg(segment_offset, ios::beg); - + if (! *sino_stream) { error("ProjDataFromStream::get_segment_by_sinogram: error after seekg\n"); } - + SegmentByView segment(proj_data_info_ptr,segment_num); - + { - float scale = float(1); + float scale = float(1.f); if(read_data(*sino_stream, segment, on_disk_data_type, scale, on_disk_byte_order) == Succeeded::no) error("ProjDataFromStream: error reading data\n"); if(scale != 1) error("ProjDataFromStream: error reading data: scale factor returned by read_data should be 1\n"); } - + segment *= scale_factor; - + return segment; } - else + else if (get_storage_order() == Timing_Segment_View_AxialPos_TangPos) + { + //Get the right segment offset + streamoff segment_offset = get_offset_segment(segment_num); + // Go to the right timing full 3D sinogram + segment_offset += get_offset_timing(timing_pos) ; + + sino_stream->seekg(segment_offset, ios::beg); + + if (! *sino_stream) + { + error("ProjDataFromStream::get_segment_by_sinogram: error after seekg\n"); + } + + SegmentByView segment(proj_data_info_ptr,segment_num); + + { + float scale = float(1.f); + if(read_data(*sino_stream, segment, on_disk_data_type, scale, on_disk_byte_order) + == Succeeded::no) + error("ProjDataFromStream: error reading data\n"); + if(scale != 1) + error("ProjDataFromStream: error reading data: scale factor returned by read_data should be 1\n"); + } + + segment *= scale_factor; + + return segment; + + } + else // TODO rewrite in terms of get_sinogram as this doubles memory temporarily return SegmentByView (get_segment_by_sinogram(segment_num)); } @@ -880,37 +1080,37 @@ 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); - + sino_stream->seekp(segment_offset,ios::beg); - + if (! *sino_stream) { warning("ProjDataFromStream::set_segment: error after seekp\n"); return Succeeded::no; - } - - if (get_storage_order() == Segment_AxialPos_View_TangPos) + } + + if (get_storage_order() == Segment_AxialPos_View_TangPos) { // KT 03/07/2001 handle scale_factor appropriately if (on_disk_data_type.id != NumericType::FLOAT) { warning("ProjDataFromStream::set_segment: non-float output uses original " "scale factor %g which might not be appropriate for the current data\n", - scale_factor); + scale_factor); } float scale = scale_factor; if (write_data(*sino_stream, segmentbysinogram_v, on_disk_data_type, scale, on_disk_byte_order) @@ -925,16 +1125,16 @@ ProjDataFromStream::set_segment(const SegmentBySinogram& segmentbysinogra return Succeeded::yes; } - else + else { // TODO rewrite in terms of set_viewgram const SegmentByView segmentbyview= SegmentByView(segmentbysinogram_v); - set_segment(segmentbyview); + set_segment(segmentbyview); return Succeeded::yes; } - + } Succeeded @@ -945,41 +1145,41 @@ 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); - + sino_stream->seekp(segment_offset,ios::beg); - + if (! *sino_stream) { warning("ProjDataFromStream::set_segment: error after seekp"); return Succeeded::no; - } - - if (get_storage_order() == Segment_View_AxialPos_TangPos) + } + + if (get_storage_order() == Segment_View_AxialPos_TangPos) { // KT 03/07/2001 handle scale_factor appropriately if (on_disk_data_type.id != NumericType::FLOAT) { warning("ProjDataFromStream::set_segment: non-float output uses original " "scale factor %g which might not be appropriate for the current data\n", - scale_factor); + scale_factor); } float scale = scale_factor; if (write_data(*sino_stream, segmentbyview_v, on_disk_data_type, scale, on_disk_byte_order) @@ -994,44 +1194,44 @@ ProjDataFromStream::set_segment(const SegmentByView& segmentbyview_v) return Succeeded::yes; } - else + else { - // TODO rewrite in terms of set_sinogram - const SegmentBySinogram segmentbysinogram = + // TODO rewrite in terms of set_sinogram + const SegmentBySinogram segmentbysinogram = SegmentBySinogram(segmentbyview_v); set_segment(segmentbysinogram); return Succeeded::yes; } - + } #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()) @@ -1042,48 +1242,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; @@ -1092,13 +1292,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; @@ -1110,80 +1310,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 { // -------------------------------------------------------- // -------------------------------------------------------- @@ -304,7 +304,7 @@ get_viewgram(const int view_num, const int segment_num, } -Succeeded ProjDataGEAdvance::set_viewgram(const Viewgram& v) +Succeeded ProjDataGEAdvance::set_viewgram(const Viewgram& v, const int &timing_pos) { // TODO // but this is difficult: how to adjust the scale factors when writing only 1 viewgram ? @@ -312,13 +312,14 @@ 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"); return get_empty_sinogram(ax_pos_num, segment_num);} -Succeeded ProjDataGEAdvance::set_sinogram(const Sinogram& s) +Succeeded ProjDataGEAdvance::set_sinogram(const Sinogram& s, const int &timing_pos) { // TODO warning("ProjDataGEAdvance::set_sinogram not implemented yet\n"); diff --git a/src/buildblock/ProjDataInfo.cxx b/src/buildblock/ProjDataInfo.cxx index 7bae650c39..03fa0e32a7 100644 --- a/src/buildblock/ProjDataInfo.cxx +++ b/src/buildblock/ProjDataInfo.cxx @@ -76,6 +76,8 @@ START_NAMESPACE_STIR float ProjDataInfo::get_k(const Bin& bin) const { + // Probably, This condition should be removed, since I have the check odd number in the + // set_tof_mash_factor(). if (!get_num_timing_poss()%2) return bin.timing_pos_num() * timing_increament_in_mm; else @@ -198,6 +200,7 @@ ProjDataInfo::set_tof_mash_factor(const int new_num) num_tof_bins = max_timing_pos_num - min_timing_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."); @@ -218,6 +221,7 @@ ProjDataInfo::set_tof_mash_factor(const int new_num) timing_bin_boundaries_mm[i].high_lim = cur_high; timing_bin_boundaries_ps[i].low_lim = (timing_bin_boundaries_mm[i].low_lim * 3.33564095198f ) ; timing_bin_boundaries_ps[i].high_lim = ( timing_bin_boundaries_mm[i].high_lim * 3.33564095198f); + // I could imagine a better printing. info(boost::format("Tbin %1%: %2% - %3% mm (%4% - %5% ps) = %6%") %i % timing_bin_boundaries_mm[i].low_lim % timing_bin_boundaries_mm[i].high_lim % timing_bin_boundaries_ps[i].low_lim % timing_bin_boundaries_ps[i].high_lim % get_sampling_in_k(bin)); } @@ -247,7 +251,7 @@ ProjDataInfo::ProjDataInfo(const shared_ptr& scanner_ptr_v, min_timing_pos_num = 0; max_timing_pos_num = 0; timing_increament_in_mm = 0.f; - tof_mash_factor = 1; // zero? + tof_mash_factor = 1; } // TOF version. diff --git a/src/buildblock/Scanner.cxx b/src/buildblock/Scanner.cxx index 4a440aaacc..2c51219d35 100644 --- a/src/buildblock/Scanner.cxx +++ b/src/buildblock/Scanner.cxx @@ -206,113 +206,16 @@ Scanner::Scanner(Type scanner_type) 2, 1, 8, 9, 16, 9, 1 ); // TODO bucket/singles info incorrect? 224 buckets in total, but not sure how distributed break; - case Type_mCT: // dummy - // 8x8 blocks, 1 virtual "crystal", 56 blocks along the ring, 8 blocks in axial direction - // Transaxial blocks have 8 physical crystals and a gap at the - // 9th crystal where the counts are zero. - set_params(Type_mCT, string_list("Type mCT", "tmCT", "t2011"), - 52, 312, 624, - 424.5F, 7.0F, 4.16F, 2.17242F, 0.0F, - 1, 48, 52, 13, 13, 13, 1 ); // TODO bucket/singles info incorrect? 224 buckets in total, but not sure how distributed - break; - - case Type_mCT_TOF: // dummy - // 8x8 blocks, 1 virtual "crystal", 56 blocks along the ring, 8 blocks in axial direction - // Transaxial blocks have 8 physical crystals and a gap at the - // 9th crystal where the counts are zero. - // For TOF scanners the three last types have to be defined to avoid ambiguity. - set_params(Type_mCT_TOF, string_list("Type mCT_TOF", "tmCT_TOF", "t2011_tof"), - 52, 312, 624, - 424.5F, 7.0F, 4.16F, 2.17242F, 0.0F, - 1, 48, 52, 13, 13, 13, 1, - (short int)(410), - (float)(10.0F), - (float)(600.0F)); // TODO bucket/singles info incorrect? 224 buckets in total, but not sure how distributed - break; - - case Type_mCT_TOF_400: // dummy - // 8x8 blocks, 1 virtual "crystal", 56 blocks along the ring, 8 blocks in axial direction - // Transaxial blocks have 8 physical crystals and a gap at the - // 9th crystal where the counts are zero. - // For TOF scanners the three last types have to be defined to avoid ambiguity. - set_params(Type_mCT_TOF_400, string_list("Type mCT_TOF_400", "tmCT_TOF_400", "t2011_tof_400"), - 52, 312, 624, - 424.5F, 7.0F, 4.16F, 2.17242F, 0.0F, - 1, 48, 52, 13, 13, 13, 1, - (short int)(410), - (float)(10.0F), - (float)(400.0F)); // TODO bucket/singles info incorrect? 224 buckets in total, but not sure how distributed - break; - - case Type_mCT_TOF_200: // dummy - // 8x8 blocks, 1 virtual "crystal", 56 blocks along the ring, 8 blocks in axial direction - // Transaxial blocks have 8 physical crystals and a gap at the - // 9th crystal where the counts are zero. - // For TOF scanners the three last types have to be defined to avoid ambiguity. - set_params(Type_mCT_TOF_200, string_list("Type mCT_TOF_200", "tmCT_TOF_200", "t2011_tof_200"), - 52, 312, 624, - 424.5F, 7.0F, 4.16F, 2.17242F, 0.0F, - 1, 48, 52, 13, 13, 13, 1, - (short int)(410), - (float)(10.0F), - (float)(200.0F)); // TODO bucket/singles info incorrect? 224 buckets in total, but not sure how distributed - break; - - case Type_mCT_TOF_100: // dummy - // 8x8 blocks, 1 virtual "crystal", 56 blocks along the ring, 8 blocks in axial direction - // Transaxial blocks have 8 physical crystals and a gap at the - // 9th crystal where the counts are zero. - // For TOF scanners the three last types have to be defined to avoid ambiguity. - set_params(Type_mCT_TOF_100, string_list("Type mCT_TOF_100", "tmCT_TOF_100", "t2011_tof_100"), - 52, 312, 624, - 424.5F, 7.0F, 4.16F, 2.17242F, 0.0F, - 1, 48, 52, 13, 13, 13, 1, - (short int)(820), - (float)(5.00F), - (float)(100.0F)); // TODO bucket/singles info incorrect? 224 buckets in total, but not sure how distributed - break; - - case Type_mCT_TOF_50: // dummy - // 8x8 blocks, 1 virtual "crystal", 56 blocks along the ring, 8 blocks in axial direction - // Transaxial blocks have 8 physical crystals and a gap at the - // 9th crystal where the counts are zero. - // For TOF scanners the three last types have to be defined to avoid ambiguity. - set_params(Type_mCT_TOF_50, string_list("Type mCT_TOF_50", "tmCT_TOF_50", "t2011_tof_50"), - 52, 312, 624, - 424.5F, 7.0F, 4.16F, 2.17242F, 0.0F, - 1, 48, 52, 13, 13, 13, 1, - (short int)(820), - (float)(5.0F), - (float)(50.0F)); // TODO bucket/singles info incorrect? 224 buckets in total, but not sure how distributed - break; - - case Type_mCT_TOF_20: // dummy - // 8x8 blocks, 1 virtual "crystal", 56 blocks along the ring, 8 blocks in axial direction - // Transaxial blocks have 8 physical crystals and a gap at the - // 9th crystal where the counts are zero. - // For TOF scanners the three last types have to be defined to avoid ambiguity. - set_params(Type_mCT_TOF_20, string_list("Type mCT_TOF_20", "tmCT_TOF_20", "t2011_tof_20"), - 52, 312, 624, - 424.5F, 7.0F, 4.16F, 2.17242F, 0.0F, - 1, 48, 52, 13, 13, 13, 1, - (short int)(1025), - (float)(1.F), - (float)(20.0F)); // TODO bucket/singles info incorrect? 224 buckets in total, but not sure how distributed - break; - - case Type_mCT_TOF_10: // dummy - // 8x8 blocks, 1 virtual "crystal", 56 blocks along the ring, 8 blocks in axial direction - // Transaxial blocks have 8 physical crystals and a gap at the - // 9th crystal where the counts are zero. - // For TOF scanners the three last types have to be defined to avoid ambiguity. - set_params(Type_mCT_TOF_10, string_list("Type mCT_TOF_10", "tmCT_TOF_10", "t2011_tof_10"), - 52, 312, 624, - 424.5F, 7.0F, 4.16F, 2.17242F, 0.0F, - 1, 48, 52, 13, 13, 13, 1, - (short int)(1025), - (float)(1.F), - (float)(10.0F)); // TODO bucket/singles info incorrect? 224 buckets in total, but not sure how distributed - break; + case test_scanner: + // This is a relatively small scanner for test purposes. + set_params(test_scanner, string_list("test_scanner"), + 4, 344, 2*252, + 328.0F, 7.0F, 4.0625F, 2.08626F, 0.0F, + 1, 1, 4, 1, 4, 1, 1, + (short int)(410), + (float)(10.0F), + (float)(400.0F) ); + break; case RPT: diff --git a/src/include/stir/Bin.h b/src/include/stir/Bin.h index 1a9377ce93..a383a67d17 100644 --- a/src/include/stir/Bin.h +++ b/src/include/stir/Bin.h @@ -47,14 +47,11 @@ START_NAMESPACE_STIR The timing position reflect the detection time difference between the two events. It is a multiple of the delta t of the least significant clock bit. - \details In order to go around the timing pos which is set only in TOF reconstruction - we set it, by default, to zero. If it is zero in both bins then it is omitted from the - comparison. - \warning Comparison between bin with and without timing position is of high risk! + \warning N.E: Constructors with default values were removed. I faced many problems with ambguity. I had to make + changes to all the framework, when one set a float value, it has to be as 'x.f' - \warning Constructors with default values were removed. - - \warning Temporarily the timing_pos_num is not taken into account when comparing two bins. + \warning Temporarily the timing_pos_num is not taken into account when comparing two bins, + Until were are actually able to cache LORs based on timing location this could be let off. */ class Bin diff --git a/src/include/stir/IO/InputStreamFromROOTFile.h b/src/include/stir/IO/InputStreamFromROOTFile.h index 30e2d5efa7..719119e24b 100644 --- a/src/include/stir/IO/InputStreamFromROOTFile.h +++ b/src/include/stir/IO/InputStreamFromROOTFile.h @@ -115,13 +115,13 @@ class InputStreamFromROOTFile : public RegisteredObject< InputStreamFromROOTFile inline virtual int get_num_axial_crystals_per_block_v() const = 0; //! Get the transaxial number of crystals per module inline virtual int get_num_transaxial_crystals_per_block_v() const = 0; - + //! Get number of axial crystals per singles unit. inline virtual int get_num_axial_crystals_per_singles_unit() const = 0; - + //! Get number of transaxial crystals per singles unit. inline virtual int get_num_trans_crystals_per_singles_unit() const = 0; - + //! Get low energy threshold inline virtual float get_low_energy_thres() const; - + //! Get high energy threshold inline virtual float get_up_energy_thres() const; protected: @@ -151,13 +151,27 @@ class InputStreamFromROOTFile : public RegisteredObject< InputStreamFromROOTFile Float_t energy1, energy2; Int_t comptonphantom1, comptonphantom2; + //! If applied all scattered events will be excluded from processing. + //! \warning Because the exclusion will take place this early, the processing + //! function (e.g. objsective function) will not be aware that the + //! events are skipped. bool exclude_scattered; + + //! If applied all random events will be excluded from processing. + //! \warning Because the exclusion will take place this early, the processing + //! function (e.g. objsective function) will not be aware that the + //! events are skipped. bool exclude_randoms; + //! Low energy window float low_energy_window; + //! High energy window float up_energy_window; + + //! The number of detectors we need to add to aligh GATE orientation with STIR. int offset_dets; + //! This has an effect on the calculation of the singles units. int singles_readout_depth; // This member will try to give to the continuous time register in GATE diff --git a/src/include/stir/IO/InputStreamFromROOTFileForCylindricalPET.h b/src/include/stir/IO/InputStreamFromROOTFileForCylindricalPET.h index a14f652987..11b5f9b9bc 100644 --- a/src/include/stir/IO/InputStreamFromROOTFileForCylindricalPET.h +++ b/src/include/stir/IO/InputStreamFromROOTFileForCylindricalPET.h @@ -6,6 +6,7 @@ \author Nikos Efthimiou */ /* + * Copyright (C) 2016, University of Leeds Copyright (C) 2016, UCL This file is part of STIR. diff --git a/src/include/stir/ProjData.h b/src/include/stir/ProjData.h index 159a32cda3..52acd2af38 100644 --- a/src/include/stir/ProjData.h +++ b/src/include/stir/ProjData.h @@ -2,6 +2,7 @@ Copyright (C) 2000 PARAPET partners Copyright (C) 2000- 2012, Hammersmith Imanet Ltd Copyright (C) 2013, 2015, University College London + Copyright (C) 2016, 2017, University of Hull This file is part of STIR. This file is free software; you can redistribute it and/or modify @@ -21,6 +22,7 @@ \ingroup projdata \brief Declaration of class stir::ProjData + \author Nikos Efthimiou \author Sanida Mustafovic \author Kris Thielemans \author PARAPET project @@ -144,16 +146,22 @@ class ProjData : public ExamData // set_exam_info(ExamInfo const&); //! Get viewgram virtual Viewgram - get_viewgram(const int view, const int segment_num,const bool make_num_tangential_poss_odd = false) const=0; + get_viewgram(const int view, const int segment_num, + const bool make_num_tangential_poss_odd = false, + const int timing_pos = 0) const=0; //! Set viewgram virtual Succeeded - set_viewgram(const Viewgram&) = 0; + set_viewgram(const Viewgram&, + const int& timing_pos = 0) = 0; //! Get sinogram virtual Sinogram - get_sinogram(const int ax_pos_num, const int segment_num,const bool make_num_tangential_poss_odd = false) const=0; + get_sinogram(const int ax_pos_num, const int segment_num, + const bool make_num_tangential_poss_odd = false, + const int timing_pos = 0) const=0; //! Set sinogram virtual Succeeded - set_sinogram(const Sinogram&) = 0; + set_sinogram(const Sinogram&, + const int& timing_pos = 0) = 0; // //! Get Bin value //virtual float get_bin_value(const Bin& this_bin) const = 0; @@ -178,20 +186,22 @@ class ProjData : public ExamData //! Get segment by sinogram virtual SegmentBySinogram - get_segment_by_sinogram(const int segment_num) const; + get_segment_by_sinogram(const int segment_num, + const int timing_num = 0) const; //! Get segment by view virtual SegmentByView - get_segment_by_view(const int segment_num) const; + get_segment_by_view(const int segment_num, + const int timing_num = 0) const; //! Set segment by sinogram //! N.E. Extended to have timging positions. virtual Succeeded set_segment(const SegmentBySinogram&, - const int& timing_pos = 1); + const int& timing_pos = 0); //! Set segment by view //! N.E. Extended to have timing positions. virtual Succeeded set_segment(const SegmentByView&, - const int& timing_pos = 1); + const int& timing_pos = 0); //! Get related viewgrams virtual RelatedViewgrams diff --git a/src/include/stir/ProjDataFromStream.h b/src/include/stir/ProjDataFromStream.h index a0adfd2453..f4e7394e74 100644 --- a/src/include/stir/ProjDataFromStream.h +++ b/src/include/stir/ProjDataFromStream.h @@ -17,6 +17,8 @@ /* Copyright (C) 2000 PARAPET partners Copyright (C) 2000- 2013, Hammersmith Imanet Ltd + Copyright (C) 2016, University of Hull + This file is part of STIR. This file is free software; you can redistribute it and/or modify @@ -54,6 +56,7 @@ START_NAMESPACE_STIR \warning Data have to be contiguous. \warning The parameter make_num_tangential_poss_odd (used in various get_ functions) is temporary and will be removed soon. + \warning Changing the sequence of the timing bins is not supported. */ class ProjDataFromStream : public ProjData @@ -122,17 +125,26 @@ class ProjDataFromStream : public ProjData inline std::vector get_timing_poss_sequence_in_stream() const; //! Get & set viewgram - Viewgram get_viewgram(const int view_num, const int segment_num,const bool make_num_tangential_poss_odd=false) const; - Succeeded set_viewgram(const Viewgram& v); + Viewgram get_viewgram(const int view_num, const int segment_num, + const bool make_num_tangential_poss_odd=false, + const int timing_pos=0) const; + Succeeded set_viewgram(const Viewgram& v, + const int& timing_pos = 0); //! Get & set sinogram - Sinogram get_sinogram(const int ax_pos_num, const int segment_num,const bool make_num_tangential_poss_odd=false) const; - Succeeded set_sinogram(const Sinogram& s); + Sinogram get_sinogram(const int ax_pos_num, const int segment_num, + const bool make_num_tangential_poss_odd=false, + const int timing_pos=0) const; + + Succeeded set_sinogram(const Sinogram& s, + const int& timing_pos = 0); //! Get all sinograms for the given segment - SegmentBySinogram get_segment_by_sinogram(const int segment_num) const; + SegmentBySinogram get_segment_by_sinogram(const int segment_num, + const int timing_num = 0) const; //! Get all viewgrams for the given segment - SegmentByView get_segment_by_view(const int segment_num) const; + SegmentByView get_segment_by_view(const int segment_num, + const int timing_pos = 0) const; //! Set all sinograms for the given segment @@ -153,7 +165,9 @@ class ProjDataFromStream : public ProjData private: //! offset of the whole 3d sinogram in the stream std::streamoff offset; - + //! offset of a complete non-tof sinogram + std::streamoff offset_3d_data; + //!the order in which the segments occur in the stream std::vector segment_sequence; @@ -171,20 +185,30 @@ class ProjDataFromStream : public ProjData // scale_factor is only used when reading data from file. Data are stored in // memory as float, with the scale factor multiplied out float scale_factor; + + //! Calculate the offset of the give timing position + //! \warning N.E: This function might be one the major components of STIR's speeds + std::streamoff get_offset_timing(const int timing_num) const; //! Calculate the offset for the given segmnet + //! \warning This function returns the offset of a segment *WITHING* a timing position + //! If you like to get the offset of a segment from different timing positions it has to + //! be combined with get_offset_timing(). std::streamoff get_offset_segment(const int segment_num) const; //! Calculate offsets for viewgram data - std::vector get_offsets(const int view_num, const int segment_num) const; + std::vector get_offsets(const int view_num, const int segment_num, + const int timing_num = 0) const; //! Calculate offsets for sinogram data - std::vector get_offsets_sino(const int ax_pos_num, const int segment_num) const; + std::vector get_offsets_sino(const int ax_pos_num, const int segment_num, + const int timing_num = 0) const; //! Calculate the offsets for specific bins. std::vector get_offsets_bin(const int segment_num, const int ax_pos_num, const int view_num, - const int tang_pos_num) const; + const int tang_pos_num, + const int timing_pos_num = 0) const; }; diff --git a/src/include/stir/ProjDataGEAdvance.h b/src/include/stir/ProjDataGEAdvance.h index 6901db3806..50a289bdc0 100644 --- a/src/include/stir/ProjDataGEAdvance.h +++ b/src/include/stir/ProjDataGEAdvance.h @@ -66,12 +66,14 @@ class ProjDataGEAdvance : public ProjData ProjDataGEAdvance (std::iostream* s); //! Get & set viewgram - Viewgram get_viewgram(const int view_num, const int segment_num,const bool make_num_tangential_poss_odd=false) const; - Succeeded set_viewgram(const Viewgram& v); + Viewgram get_viewgram(const int view_num, const int segment_num, + const bool make_num_tangential_poss_odd=false, const int timing_pos = 0) const; + Succeeded set_viewgram(const Viewgram& v, const int& timing_pos = 0); //! Get & set sinogram - Sinogram get_sinogram(const int ax_pos_num, const int sergment_num,const bool make_num_tangential_poss_odd=false) const; - Succeeded set_sinogram(const Sinogram& s); + Sinogram get_sinogram(const int ax_pos_num, const int sergment_num, + const bool make_num_tangential_poss_odd=false, const int timing_pos = 0) const; + Succeeded set_sinogram(const Sinogram& s, const int& timing_pos = 0); // float get_bin_value(const Bin& this_bin) const // { diff --git a/src/include/stir/ProjDataInfo.h b/src/include/stir/ProjDataInfo.h index 0d1b9522fb..452b3bff8e 100644 --- a/src/include/stir/ProjDataInfo.h +++ b/src/include/stir/ProjDataInfo.h @@ -4,7 +4,7 @@ Copyright (C) 2000 PARAPET partners Copyright (C) 2000 - 2011-10-14, Hammersmith Imanet Ltd Copyright (C) 2011-07-01 - 2011, Kris Thielemans - Copyright (C) 2016, University of Hull + Copyright (C) 2016-17, University of Hull This file is part of STIR. This file is free software; you can redistribute it and/or modify @@ -72,7 +72,7 @@ class ProjDataInfo ask_parameters(); //! Construct a ProjDataInfo suitable for GE Advance data - //! \warning N.E: TOF mash factor, means no TOF + //! \warning N.E: TOF mash factor = 1, means no TOF static ProjDataInfo* ProjDataInfoGE(const shared_ptr& scanner_ptr, const int max_delta, @@ -84,7 +84,7 @@ class ProjDataInfo /*! \c span is used to denote the amount of axial compression (see CTI doc). It has to be an odd number. */ - //! \warning N.E: TOF mash factor, means no TOF + //! \warning N.E: TOF mash factor = 1, means no TOF static ProjDataInfo* ProjDataInfoCTI(const shared_ptr& scanner_ptr, const int span, const int max_delta, @@ -370,9 +370,9 @@ class ProjDataInfo //! Struct which holds two floating numbers struct Float1Float2 { float low_lim; float high_lim; }; - //! Vector which holds the lower and higher boundary for each timing position, for faster access. + //! Vector which holds the lower and higher boundary for each timing position in mm, for faster access. mutable VectorWithOffset timing_bin_boundaries_mm; - + //! Vector which holds the lower and higher boundary for each timing position in ps`, for faster access. mutable VectorWithOffset timing_bin_boundaries_ps; protected: diff --git a/src/include/stir/Scanner.h b/src/include/stir/Scanner.h index d27c4a0530..5f51f5d52d 100644 --- a/src/include/stir/Scanner.h +++ b/src/include/stir/Scanner.h @@ -112,8 +112,7 @@ class Scanner to flag up an error and do some guess work in trying to recognise the scanner from any given parameters. */ - enum Type {E931, E951, E953, E921, E925, E961, E962, E966, E1080, Siemens_mMR, RPT,HiDAC, - Type_mCT, Type_mCT_TOF, Type_mCT_TOF_10, Type_mCT_TOF_20, Type_mCT_TOF_50, Type_mCT_TOF_100, Type_mCT_TOF_200, Type_mCT_TOF_400, + enum Type {E931, E951, E953, E921, E925, E961, E962, E966, E1080, test_scanner, Siemens_mMR, RPT,HiDAC, Advance, DiscoveryLS, DiscoveryST, DiscoverySTE, DiscoveryRX, Discovery600, HZLR, RATPET, PANDA, HYPERimage, nanoPET, HRRT, Allegro, GeminiTF, User_defined_scanner, Unknown_scanner}; @@ -237,6 +236,7 @@ class Scanner inline Type get_type() const; //! checks consistency /*! Calls warning() with diagnostics when there are problems + * N.E: Should something check be added for TOF information? */ Succeeded check_consistency() const; diff --git a/src/include/stir/listmode/CListRecordROOT.h b/src/include/stir/listmode/CListRecordROOT.h index 658b56b915..62ad478aea 100644 --- a/src/include/stir/listmode/CListRecordROOT.h +++ b/src/include/stir/listmode/CListRecordROOT.h @@ -1,7 +1,7 @@ /* Copyright (C) 2015-2016 University of Leeds Copyright (C) 2016 UCL - Copyright (C) 2016, University of Hull + Copyright (C) 2016-17, University of Hull This file is part of STIR. This file is free software; you can redistribute it and/or modify @@ -113,17 +113,19 @@ class CListTimeROOT : public CListTime return Succeeded::no; } + //! N.E. Sadly I had to comment the original version of this function. It might had been faster. + //! and make more sense under many occations. virtual inline void get_bin(Bin& bin, const ProjDataInfo& proj_data_info) const { -// delta_time >= 0.0 ? -// bin.timing_pos_num() = static_cast ( ( delta_time / proj_data_info.get_num_tof_poss()) + 0.5) -// : bin.timing_pos_num() = static_cast ( ( delta_time / proj_data_info.get_num_tof_poss()) - 0.5); - -// if (bin.timing_pos_num() < proj_data_info.get_min_timing_pos_num() || -// bin.timing_pos_num() > proj_data_info.get_max_timing_pos_num()) -// { -// bin.set_bin_value(-1.f); -// } + // delta_time >= 0.0 ? + // bin.timing_pos_num() = static_cast ( ( delta_time / proj_data_info.get_num_tof_poss()) + 0.5) + // : bin.timing_pos_num() = static_cast ( ( delta_time / proj_data_info.get_num_tof_poss()) - 0.5); + + // if (bin.timing_pos_num() < proj_data_info.get_min_timing_pos_num() || + // bin.timing_pos_num() > proj_data_info.get_max_timing_pos_num()) + // { + // bin.set_bin_value(-1.f); + // } bin.timing_pos_num() = proj_data_info.get_tof_bin(delta_time); } diff --git a/src/include/stir/listmode/LmToProjData.h b/src/include/stir/listmode/LmToProjData.h index 7a97ea2c2e..53ed71fc4c 100644 --- a/src/include/stir/listmode/LmToProjData.h +++ b/src/include/stir/listmode/LmToProjData.h @@ -8,12 +8,14 @@ \brief Declaration of the stir::LmToProjData class which is used to bin listmode data to (3d) sinograms + \author Nikos Efthimiou \author Kris Thielemans \author Sanida Mustafovic */ /* Copyright (C) 2000- 2009, Hammersmith Imanet Ltd + Copyright (C) 2017, University of Hull This file is part of STIR. This file is free software; you can redistribute it and/or modify @@ -177,6 +179,12 @@ class LmToProjData : public ParsingObject //! N.E: In order to keep the ToF functions separate from the non-TOF //! STIR this function just call the appropriate actual_process_data_with(out)_tof(). virtual void process_data(); + + //! A test function for time-of-flight data. At this moment we lack a lot of infrastructure in + //! order to be able to develope a viable test function of class anywhere else. At a future point + //! I should develope a proper test function. This function is going to fill the proj_data with + //! the index number of the respective TOF position, for every TOF position. + void run_tof_test_function(); protected: @@ -198,6 +206,8 @@ class LmToProjData : public ParsingObject (on top of anything done by normalisation_ptr). \todo Would need timing info or so for e.g. time dependent normalisation or angle info for a rotating scanner.*/ + //! \warning N.E: I changed this function _from_event to _from_record, because in + //! TOF unlisting we need the timing information which is stored in the record. virtual void get_bin_from_record(Bin& bin, const CListRecord&) const; //! A function that should return the number of uncompressed bins in the current bin diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl index 08a8120a92..d3ce6147c7 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl +++ b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl @@ -206,8 +206,9 @@ ProjMatrixByBin::apply_tof_kernel(ProjMatrixElemsForOneBin& nonTOF_probabilities high_dist = (proj_data_info_sptr->timing_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].high_lim - m) * r_sqrt2_gauss_sigma; // Cut-off really small values. - if (abs(low_dist) > 5.5 && abs(high_dist) > 5.5) - continue; + // Currently deactivate untill I run all the tests. + //if (abs(low_dist) > 5.5 && abs(high_dist) > 5.5) + //continue; get_tof_value(low_dist, high_dist, new_value); new_value *= element_ptr->get_value(); diff --git a/src/listmode_buildblock/CListRecordROOT.cxx b/src/listmode_buildblock/CListRecordROOT.cxx index 158bf60be1..03d314f767 100644 --- a/src/listmode_buildblock/CListRecordROOT.cxx +++ b/src/listmode_buildblock/CListRecordROOT.cxx @@ -1,6 +1,7 @@ /* Copyright (C) 2015-2016 University of Leeds Copyright (C) 2016 UCL + Copyright (C) 2017, University of Hull This file is part of STIR. This file is free software; you can redistribute it and/or modify @@ -60,19 +61,6 @@ void CListEventROOT::set_detection_position(const DetectionPositionPair<>&) void CListEventROOT::init_from_data(const int& _ring1, const int& _ring2, const int& crystal1, const int& crystal2) { -// if (crystal1 < 0 ) -// det1 = scanner_sptr->get_num_detectors_per_ring() + crystal1; -// else if ( crystal1 >= scanner_sptr->get_num_detectors_per_ring()) -// det1 = crystal1 - scanner_sptr->get_num_detectors_per_ring(); -// else -// det1 = crystal1; - -// if (crystal2 < 0 ) -// det2 = scanner_sptr->get_num_detectors_per_ring() + crystal2; -// else if ( crystal2 >= scanner_sptr->get_num_detectors_per_ring()) -// det2 = crystal2 - scanner_sptr->get_num_detectors_per_ring(); -// else -// det2 = crystal2; // STIR assumes that 0 is on y whill GATE on the x axis det1 = crystal1 + quarter_of_detectors; diff --git a/src/listmode_buildblock/LmToProjData.cxx b/src/listmode_buildblock/LmToProjData.cxx index 73f412020f..983b3beae3 100644 --- a/src/listmode_buildblock/LmToProjData.cxx +++ b/src/listmode_buildblock/LmToProjData.cxx @@ -4,12 +4,14 @@ \brief Implementation of class stir::LmToProjData + \author Nikos Efthimiou \author Kris Thielemans \author Sanida Mustafovic */ /* Copyright (C) 2000 - 2011-12-31, Hammersmith Imanet Ltd Copyright (C) 2013, University College London + Copyright (C) 2017, University of Hull This file is part of STIR. This file is free software; you can redistribute it and/or modify @@ -117,8 +119,8 @@ typedef SegmentByView segment_type; static void allocate_segments(VectorWithOffset& segments, - const int start_segment_index, - const int end_segment_index, + const int start_segment_index, + const int end_segment_index, const ProjDataInfo* proj_data_info_ptr); // In the next 2 functions, the 'output' parameter needs to be passed @@ -754,234 +756,305 @@ void LmToProjData:: actual_process_data_with_tof() { - CPUTimer timer; - timer.start(); + CPUTimer timer; + timer.start(); + + // assume list mode data starts at time 0 + // we have to do this because the first time tag might occur only after a + // few coincidence events (as happens with ECAT scanners) + current_time = 0; + + double time_of_last_stored_event = 0; + long num_stored_events = 0; + VectorWithOffset + segments (template_proj_data_info_ptr->get_min_segment_num(), + template_proj_data_info_ptr->get_max_segment_num()); + + VectorWithOffset + frame_start_positions(1, static_cast(frame_defs.get_num_frames())); + shared_ptr record_sptr = lm_data_ptr->get_empty_record_sptr(); + CListRecord& record = *record_sptr; + + /* Here starts the main loop which will store the listmode data. */ + for (current_frame_num = 1; + current_frame_num<=frame_defs.get_num_frames(); + ++current_frame_num) + { + start_new_time_frame(current_frame_num); + + // construct ExamInfo appropriate for a single projdata with this time frame + ExamInfo this_frame_exam_info(*lm_data_ptr->get_exam_info_ptr()); + { + TimeFrameDefinitions this_time_frame_defs(frame_defs, current_frame_num); + this_frame_exam_info.set_time_frame_definitions(this_time_frame_defs); + } + + // *********** open output file + shared_ptr output; + shared_ptr proj_data_ptr; + + { + char rest[50]; + sprintf(rest, "_f%dg1d0b0", current_frame_num); + const string output_filename = output_filename_prefix + rest; + + proj_data_ptr = + construct_proj_data(output, output_filename, this_frame_exam_info, template_proj_data_info_ptr); + } + + long num_prompts_in_frame = 0; + long num_delayeds_in_frame = 0; + + const double start_time = frame_defs.get_start_time(current_frame_num); + const double end_time = frame_defs.get_end_time(current_frame_num); + + for (int current_timing_pos_index = proj_data_ptr->get_min_timing_pos_num(); + current_timing_pos_index <= proj_data_ptr->get_max_timing_pos_num(); + current_timing_pos_index += 1) + { + /* + For each start_segment_index, we check which events occur in the + segments between start_segment_index and + start_segment_index+num_segments_in_memory. + */ - // assume list mode data starts at time 0 - // we have to do this because the first time tag might occur only after a - // few coincidence events (as happens with ECAT scanners) - current_time = 0; + for (int start_segment_index = proj_data_ptr->get_min_segment_num(); + start_segment_index <= proj_data_ptr->get_max_segment_num(); + start_segment_index += num_segments_in_memory) + { + + const int end_segment_index = + min( proj_data_ptr->get_max_segment_num()+1, start_segment_index + num_segments_in_memory) - 1; + + if (!interactive) + allocate_segments(segments, start_segment_index, end_segment_index, proj_data_ptr->get_proj_data_info_ptr()); + + // the next variable is used to see if there are more events to store for the current segments + // num_events_to_store-more_events will be the number of allowed coincidence events currently seen in the file + // ('allowed' independent on the fact of we have its segment in memory or not) + // When do_time_frame=true, the number of events is irrelevant, so we + // just set more_events to 1, and never change it + unsigned long int more_events = + do_time_frame? 1 : num_events_to_store; + + if (start_segment_index != proj_data_ptr->get_min_segment_num()) + { + // we're going once more through the data (for the next batch of segments) + cerr << "\nProcessing next batch of segments\n"; + // go to the beginning of the listmode data for this frame + lm_data_ptr->set_get_position(frame_start_positions[current_frame_num]); + current_time = start_time; + } + else + { + cerr << "\nProcessing time frame " << current_frame_num << '\n'; + + // Note: we already have current_time from previous frame, so don't + // need to set it. In fact, setting it to start_time would be wrong + // as we first might have to skip some events before we get to start_time. + // So, let's do that now. + while (current_time < start_time && + lm_data_ptr->get_next_record(record) == Succeeded::yes) + { + if (record.is_time()) + current_time = record.time().get_time_in_secs(); + } + // now save position such that we can go back + frame_start_positions[current_frame_num] = + lm_data_ptr->save_get_position(); + } + { + // loop over all events in the listmode file + while (more_events) + { + if (lm_data_ptr->get_next_record(record) == Succeeded::no) + { + // no more events in file for some reason + break; //get out of while loop + } + if (record.is_time() && end_time > 0.01) // Direct comparison within doubles is unsafe. + { + current_time = record.time().get_time_in_secs(); + if (do_time_frame && current_time >= end_time) + break; // get out of while loop + assert(current_time>=start_time); + process_new_time_event(record.time()); + } + // note: could do "else if" here if we would be sure that + // a record can never be both timing and coincidence event + // and there might be a scanner around that has them both combined. + if (record.is_event()) + { + assert(start_time <= current_time); + Bin bin; + // set value in case the event decoder doesn't touch it + // otherwise it would be 0 and all events will be ignored + bin.set_bin_value(1.f); + + get_bin_from_record(bin, record); + + // check if it's inside the range we want to store + if (bin.get_bin_value()>0 + && bin.tangential_pos_num()>= proj_data_ptr->get_min_tangential_pos_num() + && bin.tangential_pos_num()<= proj_data_ptr->get_max_tangential_pos_num() + && bin.axial_pos_num()>=proj_data_ptr->get_min_axial_pos_num(bin.segment_num()) + && bin.axial_pos_num()<=proj_data_ptr->get_max_axial_pos_num(bin.segment_num()) + && bin.timing_pos_num()>=proj_data_ptr->get_min_timing_pos_num() + && bin.timing_pos_num()<=proj_data_ptr->get_max_timing_pos_num() + ) + { + assert(bin.view_num()>=proj_data_ptr->get_min_view_num()); + assert(bin.view_num()<=proj_data_ptr->get_max_view_num()); + + // see if we increment or decrement the value in the sinogram + const int event_increment = + record.event().is_prompt() + ? ( store_prompts ? 1 : 0 ) // it's a prompt + : delayed_increment;//it is a delayed-coincidence event + + if (event_increment==0) + continue; + + if (!do_time_frame) + more_events -= event_increment; + + // Check if the timing position of the bin is the current one. + if (bin.timing_pos_num() == current_timing_pos_index) + { + // now check if we have its segment in memory + if (bin.segment_num() >= start_segment_index && bin.segment_num()<=end_segment_index) + { + do_post_normalisation(bin); + + num_stored_events += event_increment; + if (record.event().is_prompt()) + ++num_prompts_in_frame; + else + ++num_delayeds_in_frame; + + if (num_stored_events%500000L==0) cout << "\r" << num_stored_events << " events stored" << flush; + + if (interactive) + printf("TOFbin %4d Seg %4d view %4d ax_pos %4d tang_pos %4d time %8g stored with incr %d \n", + bin.timing_pos_num(),bin.segment_num(), + bin.view_num(), bin.axial_pos_num(), bin.tangential_pos_num(), + current_time, event_increment); + else + (*segments[bin.segment_num()])[bin.view_num()][bin.axial_pos_num()][bin.tangential_pos_num()] += + bin.get_bin_value() * + event_increment; + } + } + } + else // event is rejected for some reason + { + if (interactive) + printf("TOFbin %4d Seg %4d view %4d ax_pos %4d tang_pos %4d time %8g ignored\n", + bin.timing_pos_num(), bin.segment_num(), bin.view_num(), + bin.axial_pos_num(), bin.tangential_pos_num(), current_time); + } + } // end of spatial event processing + } // end of while loop over all events + + time_of_last_stored_event = + max(time_of_last_stored_event,current_time); + } + + if (!interactive) + save_and_delete_segments(output, segments, + start_segment_index, end_segment_index, + *proj_data_ptr, current_timing_pos_index); + } // end of for loop for segment range + + } // end of for loop for timing positions + cerr << "\nNumber of prompts stored in this time period : " << num_prompts_in_frame + << "\nNumber of delayeds stored in this time period: " << num_delayeds_in_frame + << '\n'; + } // end of loop over frames + + timer.stop(); + + cerr << "Last stored event was recorded before time-tick at " << time_of_last_stored_event << " secs\n"; + if (!do_time_frame && + (num_stored_events<=0 || + /*static_cast*/(num_stored_events) - segments (template_proj_data_info_ptr->get_min_segment_num(), - template_proj_data_info_ptr->get_max_segment_num()); +} - VectorWithOffset - frame_start_positions(1, static_cast(frame_defs.get_num_frames())); - shared_ptr record_sptr = lm_data_ptr->get_empty_record_sptr(); - CListRecord& record = *record_sptr; +void +LmToProjData::run_tof_test_function() +{ + VectorWithOffset + segments (template_proj_data_info_ptr->get_min_segment_num(), + template_proj_data_info_ptr->get_max_segment_num()); - /* Here starts the main loop which will store the listmode data. */ - for (current_frame_num = 1; - current_frame_num<=frame_defs.get_num_frames(); - ++current_frame_num) - { - start_new_time_frame(current_frame_num); - // construct ExamInfo appropriate for a single projdata with this time frame - ExamInfo this_frame_exam_info(*lm_data_ptr->get_exam_info_ptr()); - { - TimeFrameDefinitions this_time_frame_defs(frame_defs, current_frame_num); - this_frame_exam_info.set_time_frame_definitions(this_time_frame_defs); - } + // *********** open output file + shared_ptr output; + shared_ptr proj_data_ptr; - // *********** open output file - shared_ptr output; - shared_ptr proj_data_ptr; + ExamInfo this_frame_exam_info(*lm_data_ptr->get_exam_info_ptr()); + { + TimeFrameDefinitions this_time_frame_defs(frame_defs, current_frame_num); + this_frame_exam_info.set_time_frame_definitions(this_time_frame_defs); + } - { + { char rest[50]; sprintf(rest, "_f%dg1d0b0", current_frame_num); const string output_filename = output_filename_prefix + rest; proj_data_ptr = - construct_proj_data(output, output_filename, this_frame_exam_info, template_proj_data_info_ptr); - } - - long num_prompts_in_frame = 0; - long num_delayeds_in_frame = 0; - - const double start_time = frame_defs.get_start_time(current_frame_num); - const double end_time = frame_defs.get_end_time(current_frame_num); - - for (int current_timing_pos_index = proj_data_ptr->get_min_timing_pos_num(); - current_timing_pos_index <= proj_data_ptr->get_max_timing_pos_num(); - current_timing_pos_index += 1) - { - /* - For each start_segment_index, we check which events occur in the - segments between start_segment_index and - start_segment_index+num_segments_in_memory. - */ - for (int start_segment_index = proj_data_ptr->get_min_segment_num(); - start_segment_index <= proj_data_ptr->get_max_segment_num(); - start_segment_index += num_segments_in_memory) - { - - const int end_segment_index = - min( proj_data_ptr->get_max_segment_num()+1, start_segment_index + num_segments_in_memory) - 1; - - if (!interactive) - allocate_segments(segments, start_segment_index, end_segment_index, proj_data_ptr->get_proj_data_info_ptr()); - - // the next variable is used to see if there are more events to store for the current segments - // num_events_to_store-more_events will be the number of allowed coincidence events currently seen in the file - // ('allowed' independent on the fact of we have its segment in memory or not) - // When do_time_frame=true, the number of events is irrelevant, so we - // just set more_events to 1, and never change it - unsigned long int more_events = - do_time_frame? 1 : num_events_to_store; - - if (start_segment_index != proj_data_ptr->get_min_segment_num()) - { - // we're going once more through the data (for the next batch of segments) - cerr << "\nProcessing next batch of segments\n"; - // go to the beginning of the listmode data for this frame - lm_data_ptr->set_get_position(frame_start_positions[current_frame_num]); - current_time = start_time; - } - else - { - cerr << "\nProcessing time frame " << current_frame_num << '\n'; - - // Note: we already have current_time from previous frame, so don't - // need to set it. In fact, setting it to start_time would be wrong - // as we first might have to skip some events before we get to start_time. - // So, let's do that now. - while (current_time < start_time && - lm_data_ptr->get_next_record(record) == Succeeded::yes) - { - if (record.is_time()) - current_time = record.time().get_time_in_secs(); - } - // now save position such that we can go back - frame_start_positions[current_frame_num] = - lm_data_ptr->save_get_position(); - } - { - // loop over all events in the listmode file - while (more_events) - { - if (lm_data_ptr->get_next_record(record) == Succeeded::no) - { - // no more events in file for some reason - break; //get out of while loop - } - if (record.is_time() && end_time > 0.01) // Direct comparison within doubles is unsafe. - { - current_time = record.time().get_time_in_secs(); - if (do_time_frame && current_time >= end_time) - break; // get out of while loop - assert(current_time>=start_time); - process_new_time_event(record.time()); - } - // note: could do "else if" here if we would be sure that - // a record can never be both timing and coincidence event - // and there might be a scanner around that has them both combined. - if (record.is_event()) - { - assert(start_time <= current_time); - Bin bin; - // set value in case the event decoder doesn't touch it - // otherwise it would be 0 and all events will be ignored - bin.set_bin_value(1.f); - - get_bin_from_record(bin, record); - - // check if it's inside the range we want to store - if (bin.get_bin_value()>0 - && bin.tangential_pos_num()>= proj_data_ptr->get_min_tangential_pos_num() - && bin.tangential_pos_num()<= proj_data_ptr->get_max_tangential_pos_num() - && bin.axial_pos_num()>=proj_data_ptr->get_min_axial_pos_num(bin.segment_num()) - && bin.axial_pos_num()<=proj_data_ptr->get_max_axial_pos_num(bin.segment_num()) - && bin.timing_pos_num()>=proj_data_ptr->get_min_timing_pos_num() - && bin.timing_pos_num()<=proj_data_ptr->get_max_timing_pos_num() - ) - { - assert(bin.view_num()>=proj_data_ptr->get_min_view_num()); - assert(bin.view_num()<=proj_data_ptr->get_max_view_num()); - - // see if we increment or decrement the value in the sinogram - const int event_increment = - record.event().is_prompt() - ? ( store_prompts ? 1 : 0 ) // it's a prompt - : delayed_increment;//it is a delayed-coincidence event - - if (event_increment==0) - continue; - - if (!do_time_frame) - more_events -= event_increment; - - if (bin.timing_pos_num() == current_timing_pos_index) - { - // now check if we have its segment in memory - if (bin.segment_num() >= start_segment_index && bin.segment_num()<=end_segment_index) - { - do_post_normalisation(bin); - - num_stored_events += event_increment; - if (record.event().is_prompt()) - ++num_prompts_in_frame; - else - ++num_delayeds_in_frame; - - if (num_stored_events%500000L==0) cout << "\r" << num_stored_events << " events stored" << flush; - - if (interactive) - printf("TOFbin %4d Seg %4d view %4d ax_pos %4d tang_pos %4d time %8g stored with incr %d \n", - bin.timing_pos_num(),bin.segment_num(), - bin.view_num(), bin.axial_pos_num(), bin.tangential_pos_num(), - current_time, event_increment); - else - (*segments[bin.segment_num()])[bin.view_num()][bin.axial_pos_num()][bin.tangential_pos_num()] += - bin.get_bin_value() * - event_increment; - } - } - } - else // event is rejected for some reason - { - if (interactive) - printf("TOFbin %4d Seg %4d view %4d ax_pos %4d tang_pos %4d time %8g ignored\n", - bin.timing_pos_num(), bin.segment_num(), bin.view_num(), - bin.axial_pos_num(), bin.tangential_pos_num(), current_time); - } - } // end of spatial event processing - } // end of while loop over all events - - time_of_last_stored_event = - max(time_of_last_stored_event,current_time); - } - - if (!interactive) - save_and_delete_segments(output, segments, - start_segment_index, end_segment_index, - *proj_data_ptr, current_timing_pos_index); - } // end of for loop for segment range - - } // end of for loop for timing positions - cerr << "\nNumber of prompts stored in this time period : " << num_prompts_in_frame - << "\nNumber of delayeds stored in this time period: " << num_delayeds_in_frame - << '\n'; - } // end of loop over frames - - timer.stop(); - - cerr << "Last stored event was recorded before time-tick at " << time_of_last_stored_event << " secs\n"; - if (!do_time_frame && - (num_stored_events<=0 || - /*static_cast*/(num_stored_events)get_min_timing_pos_num(); + current_timing_pos_index <= proj_data_ptr->get_max_timing_pos_num(); + current_timing_pos_index += 1) + { + for (int start_segment_index = proj_data_ptr->get_min_segment_num(); + start_segment_index <= proj_data_ptr->get_max_segment_num(); + start_segment_index += 1) + { + + const int end_segment_index = + min( proj_data_ptr->get_max_segment_num()+1, start_segment_index + num_segments_in_memory) - 1; + + if (!interactive) + allocate_segments(segments, start_segment_index, end_segment_index, proj_data_ptr->get_proj_data_info_ptr()); + + for (int ax_num = proj_data_ptr->get_proj_data_info_ptr()->get_min_axial_pos_num(start_segment_index); + ax_num <= proj_data_ptr->get_proj_data_info_ptr()->get_max_axial_pos_num(start_segment_index); + ++ax_num) + { + for (int view_num = proj_data_ptr->get_proj_data_info_ptr()->get_min_view_num(); + view_num <= proj_data_ptr->get_proj_data_info_ptr()->get_max_view_num(); ++view_num) + { + for (int tang_num = proj_data_ptr->get_proj_data_info_ptr()->get_min_tangential_pos_num(); + tang_num <= proj_data_ptr->get_proj_data_info_ptr()->get_max_tangential_pos_num(); + ++tang_num) + { + (*segments[start_segment_index])[view_num][ax_num][tang_num] = current_timing_pos_index; + } + } + } + + if (!interactive) + save_and_delete_segments(output, segments, + start_segment_index, end_segment_index, + *proj_data_ptr, current_timing_pos_index); + } // end of for loop for segment range + + } // end of for loop for timing positions } + /************************* Local helper routines *************************/ @@ -989,20 +1062,20 @@ void allocate_segments( VectorWithOffset& segments, const int start_segment_index, const int end_segment_index, - const ProjDataInfo* proj_data_info_ptr) + const ProjDataInfo* proj_data_info_ptr) { - + for (int seg=start_segment_index ; seg<=end_segment_index; seg++) { #ifdef USE_SegmentByView segments[seg] = new SegmentByView( - proj_data_info_ptr->get_empty_segment_by_view (seg)); + proj_data_info_ptr->get_empty_segment_by_view (seg)); #else - segments[seg] = - new Array<3,elem_type>(IndexRange3D(0, proj_data_info_ptr->get_num_views()-1, - 0, proj_data_info_ptr->get_num_axial_poss(seg)-1, - -(proj_data_info_ptr->get_num_tangential_poss()/2), - proj_data_info_ptr->get_num_tangential_poss()-(proj_data_info_ptr->get_num_tangential_poss()/2)-1)); + segments[seg] = + new Array<3,elem_type>(IndexRange3D(0, proj_data_info_ptr->get_num_views()-1, + 0, proj_data_info_ptr->get_num_axial_poss(seg)-1, + -(proj_data_info_ptr->get_num_tangential_poss()/2), + proj_data_info_ptr->get_num_tangential_poss()-(proj_data_info_ptr->get_num_tangential_poss()/2)-1)); #endif } } @@ -1080,5 +1153,4 @@ construct_proj_data(shared_ptr& output, #endif } - END_NAMESPACE_STIR diff --git a/src/listmode_utilities/lm_to_projdata.cxx b/src/listmode_utilities/lm_to_projdata.cxx index 9fa37abaf0..8804c63a20 100644 --- a/src/listmode_utilities/lm_to_projdata.cxx +++ b/src/listmode_utilities/lm_to_projdata.cxx @@ -59,6 +59,19 @@ int main(int argc, char * argv[]) list_registered_names(cerr); exit(EXIT_SUCCESS); } + + if (strcmp(argv[1], "--test_timing_positions")==0) + { + cerr<<"A test function for TOF data which I could not fit anywhere else right now:\n" + "It is going to fill every segment with the index number of the respective TOF position \n" + "and then stop.\n"; + std::cout< proj_data_info_sptr2 (ProjDataInfo::ProjDataInfoCTI(scanner_sptr, /*span*/1, 8,/*views*/ 96, /*tang_pos*/128, /*arc_corrected*/ true) @@ -116,35 +116,35 @@ run_tests() // construct without filling ProjDataInMemory proj_data2(exam_info_sptr, proj_data_info_sptr2, false); - proj_data2.fill(proj_data); - check_if_equal(proj_data2.get_viewgram(0,0).find_max(), - proj_data.get_viewgram(0,0).find_max(), - "test 1 for copy-constructor and get_viewgram"); - check_if_equal(proj_data2.get_viewgram(1,1).find_max(), - proj_data.get_viewgram(1,1).find_max(), - "test 1 for copy-constructor and get_viewgram"); +// proj_data2.fill(proj_data); +// check_if_equal(proj_data2.get_viewgram(0,0).find_max(), +// proj_data.get_viewgram(0,0).find_max(), +// "test 1 for copy-constructor and get_viewgram"); +// check_if_equal(proj_data2.get_viewgram(1,1).find_max(), +// proj_data.get_viewgram(1,1).find_max(), +// "test 1 for copy-constructor and get_viewgram"); } // test fill with smaller input - { - shared_ptr proj_data_info_sptr2 - (ProjDataInfo::ProjDataInfoCTI(scanner_sptr, - /*span*/1, 12,/*views*/ 96, /*tang_pos*/128, /*arc_corrected*/ true) - ); + { +// shared_ptr proj_data_info_sptr2 +// (ProjDataInfo::ProjDataInfoCTI(scanner_sptr, +// /*span*/1, 12,/*views*/ 96, /*tang_pos*/128, /*arc_corrected*/ true) +// ); - // construct without filling - ProjDataInMemory proj_data2(exam_info_sptr, proj_data_info_sptr2, false); - // this should call error, so we'll catch it - try - { - proj_data2.fill(proj_data); - check(false, "test fill wtih too small proj_data should have thrown"); - } - catch (...) - { - // ok - } +// // construct without filling +// ProjDataInMemory proj_data2(exam_info_sptr, proj_data_info_sptr2, false); +// // this should call error, so we'll catch it +// try +// { +// proj_data2.fill(proj_data); +// check(false, "test fill wtih too small proj_data should have thrown"); +// } +// catch (...) +// { +// // ok +// } } } diff --git a/src/test/test_proj_data_info.cxx b/src/test/test_proj_data_info.cxx index 46ebe73813..45b153c991 100644 --- a/src/test/test_proj_data_info.cxx +++ b/src/test/test_proj_data_info.cxx @@ -604,6 +604,8 @@ run_tests() /*tang_pos*/64, /*arc_corrected*/ false)); test_proj_data_info(dynamic_cast(*proj_data_info_ptr)); + + cerr << "\nTests with proj_data_info with time-of-flight\n\n"; } void diff --git a/src/test/test_time_of_flight.cxx b/src/test/test_time_of_flight.cxx index c9f16d8bdf..12d90769de 100644 --- a/src/test/test_time_of_flight.cxx +++ b/src/test/test_time_of_flight.cxx @@ -126,46 +126,46 @@ class TOF_Tests : public RunTests void TOF_Tests::run_tests() { - // New Scanner - test_scanner_sptr.reset(new Scanner(Scanner::Type_mCT_TOF)); - - // New Proj_Data_Info - const int test_tof_mashing_factor = 6; - test_proj_data_info_sptr.reset(ProjDataInfo::ProjDataInfoCTI(test_scanner_sptr, - 1,test_scanner_sptr->get_num_rings() -1, - test_scanner_sptr->get_num_detectors_per_ring()/2, - test_scanner_sptr->get_max_num_non_arccorrected_bins(), - /* arc_correction*/false)); - test_proj_data_info_sptr->set_tof_mash_factor(test_tof_mashing_factor); - - test_tof_proj_data_info(); -// test_tof_geometry_1(); - - // New Discretised Density - test_discretised_density_sptr.reset( new VoxelsOnCartesianGrid (*test_proj_data_info_sptr, 1.f, - CartesianCoordinate3D(0.f, 0.f, 0.f), - CartesianCoordinate3D(-1, -1, -1))); - // New ProjMatrix - test_proj_matrix_sptr.reset(new ProjMatrixByBinUsingRayTracing()); - dynamic_cast(test_proj_matrix_sptr.get())->set_num_tangential_LORs(1); - dynamic_cast(test_proj_matrix_sptr.get())->set_up(test_proj_data_info_sptr, test_discretised_density_sptr); - test_proj_matrix_sptr->enable_tof(test_proj_data_info_sptr); - - shared_ptr forward_projector_ptr( - new ForwardProjectorByBinUsingProjMatrixByBin(test_proj_matrix_sptr)); - shared_ptr back_projector_ptr( - new BackProjectorByBinUsingProjMatrixByBin(test_proj_matrix_sptr)); - - projector_pair_sptr.reset( - new ProjectorByBinPairUsingSeparateProjectors(forward_projector_ptr, back_projector_ptr)); - projector_pair_sptr->set_up(test_proj_data_info_sptr, test_discretised_density_sptr); - - symmetries_used_sptr.reset(projector_pair_sptr->get_symmetries_used()->clone()); - - // Deactivated it now because it takes a long time to finish. - // test_cache(); - - test_tof_kernel_application(); +// // New Scanner +// test_scanner_sptr.reset(new Scanner(Scanner::Type_mCT_TOF_100)); + +// // New Proj_Data_Info +// const int test_tof_mashing_factor = 6; +// test_proj_data_info_sptr.reset(ProjDataInfo::ProjDataInfoCTI(test_scanner_sptr, +// 1,test_scanner_sptr->get_num_rings() -1, +// test_scanner_sptr->get_num_detectors_per_ring()/2, +// test_scanner_sptr->get_max_num_non_arccorrected_bins(), +// /* arc_correction*/false)); +// test_proj_data_info_sptr->set_tof_mash_factor(test_tof_mashing_factor); + +// test_tof_proj_data_info(); +//// test_tof_geometry_1(); + +// // New Discretised Density +// test_discretised_density_sptr.reset( new VoxelsOnCartesianGrid (*test_proj_data_info_sptr, 1.f, +// CartesianCoordinate3D(0.f, 0.f, 0.f), +// CartesianCoordinate3D(-1, -1, -1))); +// // New ProjMatrix +// test_proj_matrix_sptr.reset(new ProjMatrixByBinUsingRayTracing()); +// dynamic_cast(test_proj_matrix_sptr.get())->set_num_tangential_LORs(1); +// dynamic_cast(test_proj_matrix_sptr.get())->set_up(test_proj_data_info_sptr, test_discretised_density_sptr); +// test_proj_matrix_sptr->enable_tof(test_proj_data_info_sptr); + +// shared_ptr forward_projector_ptr( +// new ForwardProjectorByBinUsingProjMatrixByBin(test_proj_matrix_sptr)); +// shared_ptr back_projector_ptr( +// new BackProjectorByBinUsingProjMatrixByBin(test_proj_matrix_sptr)); + +// projector_pair_sptr.reset( +// new ProjectorByBinPairUsingSeparateProjectors(forward_projector_ptr, back_projector_ptr)); +// projector_pair_sptr->set_up(test_proj_data_info_sptr, test_discretised_density_sptr); + +// symmetries_used_sptr.reset(projector_pair_sptr->get_symmetries_used()->clone()); + +// // Deactivated it now because it takes a long time to finish. +// // test_cache(); + +// test_tof_kernel_application(); } void diff --git a/src/utilities/list_projdata_info.cxx b/src/utilities/list_projdata_info.cxx index 305d57268d..5ac2c87952 100644 --- a/src/utilities/list_projdata_info.cxx +++ b/src/utilities/list_projdata_info.cxx @@ -2,6 +2,7 @@ Copyright (C) 2002 - 2005-06-09, Hammersmith Imanet Ltd Copyright (C) 2011-07-01 - 2012, Kris Thielemans Copyright (C) 2013, University College London + Copyright (C) 2016, University of Hull This file is part of STIR. This file is free software; you can redistribute it and/or modify @@ -30,6 +31,7 @@ Add one or more options to print the exam/geometric/min/max/sum information. If no option is specified, geometric info is printed. + \author Nikos Efthimiou \author Kris Thielemans */ @@ -143,39 +145,47 @@ int main(int argc, char *argv[]) if (print_geom) std::cout << proj_data_sptr->get_proj_data_info_ptr()->parameter_info() << std::endl; - if (print_min || print_max || print_sum) - { + //if (print_min || print_max || print_sum) + { const int min_segment_num = proj_data_sptr->get_min_segment_num(); - const int max_segment_num = proj_data_sptr->get_max_segment_num(); - bool accumulators_initialized = false; - float accum_min=std::numeric_limits::max(); // initialize to very large in case projdata is empty (although that's unlikely) - float accum_max=std::numeric_limits::min(); - double sum=0.; - for (int segment_num = min_segment_num; segment_num<= max_segment_num; ++segment_num) - { - const SegmentByView seg(proj_data_sptr->get_segment_by_view(segment_num)); - const float this_max=seg.find_max(); - const float this_min=seg.find_min(); - sum+=static_cast(seg.sum()); - if(!accumulators_initialized) - { - accum_max=this_max; - accum_min=this_min; - accumulators_initialized=true; - } - else - { - if (accum_maxthis_min) accum_min=this_min; - } - } - if (print_min) - std::cout << "\nData min: " << accum_min; - if (print_max) - std::cout << "\nData max: " << accum_max; - if (print_sum) - std::cout << "\nData sum: " << sum; - std::cout << "\n"; - } + const int max_segment_num = proj_data_sptr->get_max_segment_num(); + const int min_timing_num = proj_data_sptr->get_min_timing_pos_num(); + const int max_timing_num = proj_data_sptr->get_max_timing_pos_num(); + std::cout << "\nTotal number of timing positions: " << proj_data_sptr->get_num_timing_poss(); + + for (int timing_num = min_timing_num; timing_num <= max_timing_num; ++timing_num) + { + std::cout << "\nTiming location: " << timing_num; + bool accumulators_initialized = false; + float accum_min=std::numeric_limits::max(); // initialize to very large in case projdata is empty (although that's unlikely) + float accum_max=std::numeric_limits::min(); + double sum=0.; + for (int segment_num = min_segment_num; segment_num<= max_segment_num; ++segment_num) + { + const SegmentByView seg(proj_data_sptr->get_segment_by_view(segment_num, timing_num)); + const float this_max=seg.find_max(); + const float this_min=seg.find_min(); + sum+=static_cast(seg.sum()); + if(!accumulators_initialized) + { + accum_max=this_max; + accum_min=this_min; + accumulators_initialized=true; + } + else + { + if (accum_maxthis_min) accum_min=this_min; + } + } + if (print_min) + std::cout << "\nData min: " << accum_min; + if (print_max) + std::cout << "\nData max: " << accum_max; + if (print_sum) + std::cout << "\nData sum: " << sum; + std::cout << "\n"; + } + } return EXIT_SUCCESS; } From 58e48946da6ccc4df925abddc4a148e45f83c631 Mon Sep 17 00:00:00 2001 From: Ottavia Date: Wed, 8 Feb 2017 13:28:16 +0000 Subject: [PATCH 052/170] use boost:lexical_cast as opposed to C++11's to:string --- src/test/test_time_of_flight.cxx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test/test_time_of_flight.cxx b/src/test/test_time_of_flight.cxx index 12d90769de..ca6af0b7db 100644 --- a/src/test/test_time_of_flight.cxx +++ b/src/test/test_time_of_flight.cxx @@ -38,6 +38,7 @@ #include "stir/shared_ptr.h" #include "stir/RunTests.h" #include "stir/Scanner.h" +#include "boost/lexical_cast.hpp" #include "stir/info.h" #include "stir/warning.h" @@ -380,7 +381,7 @@ export_lor(ProjMatrixElemsForOneBin& probabilities, const CartesianCoordinate3D& point2, int current_id) { std::ofstream myfile; - std::string file_name = "glor_" + std::to_string(current_id) + ".txt"; + std::string file_name = "glor_" + boost::lexical_cast(current_id) + ".txt"; myfile.open (file_name.c_str()); CartesianCoordinate3D voxel_center; @@ -435,7 +436,7 @@ export_lor(ProjMatrixElemsForOneBin& probabilities, ProjMatrixElemsForOneBin& template_probabilities) { std::ofstream myfile; - std::string file_name = "glor_" + std::to_string(current_id) + ".txt"; + std::string file_name = "glor_" + boost::lexical_cast(current_id) + ".txt"; myfile.open (file_name.c_str()); CartesianCoordinate3D voxel_center; From f6df4d59c4fcb1363b05d11a5875a39726257e27 Mon Sep 17 00:00:00 2001 From: Ottavia Date: Wed, 8 Feb 2017 14:51:15 +0000 Subject: [PATCH 053/170] Added and defined the get_proj_data_info_sptr for GE Signa --- src/include/stir/listmode/CListModeDataGESigna.h | 4 ++++ src/listmode_buildblock/CListModeDataGESigna.cxx | 16 ++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/include/stir/listmode/CListModeDataGESigna.h b/src/include/stir/listmode/CListModeDataGESigna.h index 375e50dd9e..f899014f37 100644 --- a/src/include/stir/listmode/CListModeDataGESigna.h +++ b/src/include/stir/listmode/CListModeDataGESigna.h @@ -36,6 +36,9 @@ class CListModeDataGESigna : public CListModeData, private GEHDF5Data virtual std::string get_name() const; + virtual shared_ptr + get_proj_data_info_sptr() const; + virtual std::time_t get_scan_start_time_in_secs_since_1970() const; @@ -61,6 +64,7 @@ class CListModeDataGESigna : public CListModeData, private GEHDF5Data private: typedef CListRecordGESigna CListRecordT; std::string listmode_filename; + shared_ptr proj_data_info_sptr; shared_ptr > current_lm_data_ptr; float lm_start_time; float lm_duration; diff --git a/src/listmode_buildblock/CListModeDataGESigna.cxx b/src/listmode_buildblock/CListModeDataGESigna.cxx index 5dfb4b1538..3ba7e3ab35 100644 --- a/src/listmode_buildblock/CListModeDataGESigna.cxx +++ b/src/listmode_buildblock/CListModeDataGESigna.cxx @@ -13,6 +13,7 @@ #include "stir/listmode/CListModeDataGESigna.h" #include "stir/Succeeded.h" #include "stir/ExamInfo.h" +#include "stir/ProjDataInfo.h" #include "stir/info.h" #include #include @@ -37,6 +38,13 @@ get_name() const return listmode_filename; } +shared_ptr +CListModeDataGESigna:: +get_proj_data_info_sptr() const +{ + return this->proj_data_info_sptr; +} + std::time_t CListModeDataGESigna:: get_scan_start_time_in_secs_since_1970() const @@ -89,6 +97,14 @@ std::cout << "\n Manufacturer : " << read_str_manufacturer << "\n\n"; #endif CListModeData::scanner_sptr = GEHDF5Data::scanner_sptr; + this->proj_data_info_sptr.reset( + ProjDataInfo::ProjDataInfoCTI(GEHDF5Data::scanner_sptr, + /*span=*/ 1, + GEHDF5Data::scanner_sptr->get_num_rings()-1, + GEHDF5Data::scanner_sptr->get_num_detectors_per_ring()/2, + GEHDF5Data::scanner_sptr->get_max_num_non_arccorrected_bins(), + /*arc_corrected =*/ false, + /*tof_mash_factor = TODO*/ 1)); shared_ptr dataset_list_sptr(new H5::DataSet(this->file.openDataSet("/ListData/listData"))); current_lm_data_ptr. From dcbc02255d0bc9ee3a85f25554ca57282612a149 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Sun, 12 Feb 2017 22:20:30 +0000 Subject: [PATCH 054/170] Revered CListTime, changede the get_tof_mash_factor with get_num_tof_pos Amended the following comments: > You have changed the CListRecord stuff to use get_bin_from_record, apparently because you put the TOF timing information in CListTime. > the meaning of the TOF mashing factor. > The potential overlap of meaning of the TOF keywords --- src/IO/InputStreamFromROOTFileForECATPET.cxx | 4 +- src/IO/interfile.cxx | 2 +- src/buildblock/ProjDataFromStream.cxx | 32 +++++----- src/buildblock/ProjDataInfo.cxx | 55 ++++++++++------- .../ProjDataInfoCylindricalNoArcCorr.cxx | 8 +-- src/include/stir/ProjData.h | 6 +- src/include/stir/ProjData.inl | 12 ++-- src/include/stir/ProjDataInfo.h | 42 ++++++------- src/include/stir/ProjDataInfo.inl | 18 +++--- .../stir/ProjDataInfoCylindricalArcCorr.h | 2 +- .../stir/ProjDataInfoCylindricalNoArcCorr.h | 4 +- ...ylindricalScannerWithDiscreteDetectors.inl | 4 ++ ...calScannerWithViewTangRingRingEncoding.inl | 2 + src/include/stir/listmode/CListRecord.h | 28 +++------ src/include/stir/listmode/CListRecordROOT.h | 61 ++++--------------- src/include/stir/listmode/CListRecordROOT.inl | 3 - .../stir/recon_buildblock/ProjMatrixByBin.inl | 4 +- src/listmode_buildblock/CListEvent.cxx | 2 + src/listmode_buildblock/CListRecordROOT.cxx | 5 +- src/listmode_buildblock/LmToProjData.cxx | 18 +++--- ...MeanAndListModeDataWithProjMatrixByBin.cxx | 8 +-- src/test/test_time_of_flight.cxx | 14 ++--- src/utilities/create_projdata_template.cxx | 8 +-- src/utilities/list_projdata_info.cxx | 8 +-- 24 files changed, 155 insertions(+), 195 deletions(-) diff --git a/src/IO/InputStreamFromROOTFileForECATPET.cxx b/src/IO/InputStreamFromROOTFileForECATPET.cxx index 87efb85d8c..b330346e00 100644 --- a/src/IO/InputStreamFromROOTFileForECATPET.cxx +++ b/src/IO/InputStreamFromROOTFileForECATPET.cxx @@ -102,10 +102,12 @@ get_next_record(CListRecordROOT& record) crystal1 += offset_dets; crystal2 += offset_dets; + double delta_timing_bin = (time2 - time1) * least_significant_clock_bit; + return record.init_from_data(ring1, ring2, crystal1, crystal2, - time1, time2, + time1, delta_timing_bin, event1, event2); } diff --git a/src/IO/interfile.cxx b/src/IO/interfile.cxx index 5e60d8f495..4666f23bf8 100644 --- a/src/IO/interfile.cxx +++ b/src/IO/interfile.cxx @@ -934,7 +934,7 @@ write_basic_interfile_PDFS_header(const string& header_file_name, // IF TOF is supported add this in the header. if (pdfs.get_proj_data_info_ptr()->get_scanner_ptr()->is_tof_ready() && - pdfs.get_proj_data_info_ptr()->get_tof_mash_factor() > 1) + pdfs.get_proj_data_info_ptr()->get_num_tof_poss() > 1) { // Moved in scanner section // output_header << "%number of TOF time bins :=" << diff --git a/src/buildblock/ProjDataFromStream.cxx b/src/buildblock/ProjDataFromStream.cxx index 0639af379f..13190f4362 100644 --- a/src/buildblock/ProjDataFromStream.cxx +++ b/src/buildblock/ProjDataFromStream.cxx @@ -105,13 +105,13 @@ ProjDataFromStream::ProjDataFromStream(shared_ptr const& exam_info_spt // Now, lets initialise a TOF stream - Similarly to segments if (storage_order == Timing_Segment_View_AxialPos_TangPos && - proj_data_info_ptr->get_num_timing_poss() > 1) + proj_data_info_ptr->get_num_tof_poss() > 1) { - timing_poss_sequence.resize(proj_data_info_ptr->get_num_timing_poss()); + timing_poss_sequence.resize(proj_data_info_ptr->get_num_tof_poss()); int timing_pos_num; - for (int i= 0, timing_pos_num = proj_data_info_ptr->get_min_timing_pos_num(); - timing_pos_num<=proj_data_info_ptr->get_max_timing_pos_num(); + for (int i= 0, timing_pos_num = proj_data_info_ptr->get_min_tof_pos_num(); + timing_pos_num<=proj_data_info_ptr->get_max_tof_pos_num(); ++i, ++timing_pos_num) { timing_poss_sequence[i] = timing_pos_num; @@ -154,13 +154,13 @@ ProjDataFromStream::ProjDataFromStream(shared_ptr const& exam_info_spt // Now, lets initialise a TOF stream - Similarly to segments if (storage_order == Timing_Segment_View_AxialPos_TangPos && - proj_data_info_ptr->get_num_timing_poss() > 1) + proj_data_info_ptr->get_num_tof_poss() > 1) { - timing_poss_sequence.resize(proj_data_info_ptr->get_num_timing_poss()); + timing_poss_sequence.resize(proj_data_info_ptr->get_num_tof_poss()); int timing_pos_num; - for (i= 0, timing_pos_num = proj_data_info_ptr->get_min_timing_pos_num(); - timing_pos_num<=proj_data_info_ptr->get_max_timing_pos_num(); + for (i= 0, timing_pos_num = proj_data_info_ptr->get_min_tof_pos_num(); + timing_pos_num<=proj_data_info_ptr->get_max_tof_pos_num(); ++i, ++timing_pos_num) { timing_poss_sequence[i] = timing_pos_num; @@ -354,8 +354,8 @@ ProjDataFromStream::get_offsets(const int view_num, const int segment_num, { // The timing offset will be added to the segment offset. This approach we minimise the // changes - if (!(timing_num >= get_min_timing_pos_num() && - timing_num <= get_max_timing_pos_num())) + if (!(timing_num >= get_min_tof_pos_num() && + timing_num <= get_max_tof_pos_num())) error("ProjDataFromStream::get_offsets: timing_num out of range : %d", timing_num); const int timing_index = static_cast(FIND(timing_poss_sequence.begin(), timing_poss_sequence.end(), timing_num) - @@ -589,8 +589,8 @@ ProjDataFromStream::get_offsets_bin(const int segment_num, { // The timing offset will be added to the segment offset. This approach we minimise the // changes - if (!(timing_pos_num >= get_min_timing_pos_num() && - timing_pos_num <= get_max_timing_pos_num())) + if (!(timing_pos_num >= get_min_tof_pos_num() && + timing_pos_num <= get_max_tof_pos_num())) error("ProjDataFromStream::get_offsets_bin: timing_num out of range : %d", timing_pos_num); const int timing_index = static_cast(FIND(timing_poss_sequence.begin(), timing_poss_sequence.end(), timing_pos_num) - @@ -696,8 +696,8 @@ ProjDataFromStream::get_offsets_sino(const int ax_pos_num, const int segment_num { // The timing offset will be added to the segment offset. This approach we minimise the // changes - if (!(timing_num >= get_min_timing_pos_num() && - timing_num <= get_max_timing_pos_num())) + if (!(timing_num >= get_min_tof_pos_num() && + timing_num <= get_max_tof_pos_num())) error("ProjDataFromStream::get_offsets: timing_num out of range : %d", timing_num); const int timing_index = static_cast(FIND(timing_poss_sequence.begin(), timing_poss_sequence.end(), timing_num) - @@ -926,8 +926,8 @@ ProjDataFromStream::get_offset_timing(const int timing_num) const static_cast(FIND(timing_poss_sequence.begin(), timing_poss_sequence.end(), timing_num) - timing_poss_sequence.begin()); - assert (timing_num >= get_min_timing_pos_num() && - timing_num <= get_max_timing_pos_num()); + assert (timing_num >= get_min_tof_pos_num() && + timing_num <= get_max_tof_pos_num()); { if(offset_3d_data < 0 ) // Calculate the full 3D sinogram size - Very slow { diff --git a/src/buildblock/ProjDataInfo.cxx b/src/buildblock/ProjDataInfo.cxx index 03fa0e32a7..5ea9cd50bd 100644 --- a/src/buildblock/ProjDataInfo.cxx +++ b/src/buildblock/ProjDataInfo.cxx @@ -78,10 +78,10 @@ ProjDataInfo::get_k(const Bin& bin) const { // Probably, This condition should be removed, since I have the check odd number in the // set_tof_mash_factor(). - if (!get_num_timing_poss()%2) - return bin.timing_pos_num() * timing_increament_in_mm; + if (!num_tof_bins%2) + return bin.timing_pos_num() * tof_increament_in_mm; else - return (bin.timing_pos_num() * timing_increament_in_mm) - timing_increament_in_mm/2.f; + return (bin.timing_pos_num() * tof_increament_in_mm) - tof_increament_in_mm/2.f; } float @@ -186,30 +186,30 @@ ProjDataInfo::set_max_tangential_pos_num(const int max_tang_poss) void ProjDataInfo::set_tof_mash_factor(const int new_num) { - if (scanner_ptr->is_tof_ready()) + if (scanner_ptr->is_tof_ready() && new_num > 0 ) { if(tof_mash_factor < 0 || tof_mash_factor > scanner_ptr->get_num_max_of_timing_bins()) error("ProjDataInfo: TOF mashing factor must be positive and smaller or equal than" "the scanner's number of max timing bins. Abort."); tof_mash_factor = new_num; - timing_increament_in_mm = (tof_mash_factor * scanner_ptr->get_size_of_timing_bin() * 0.299792458f); + tof_increament_in_mm = (tof_mash_factor * scanner_ptr->get_size_of_timing_bin() * 0.299792458f); - min_timing_pos_num = - (scanner_ptr->get_num_max_of_timing_bins() / tof_mash_factor)/2; - max_timing_pos_num = min_timing_pos_num + (scanner_ptr->get_num_max_of_timing_bins() / tof_mash_factor) -1; + min_tof_pos_num = - (scanner_ptr->get_num_max_of_timing_bins() / tof_mash_factor)/2; + max_tof_pos_num = min_tof_pos_num + (scanner_ptr->get_num_max_of_timing_bins() / tof_mash_factor) -1; - num_tof_bins = max_timing_pos_num - min_timing_pos_num +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; - timing_bin_boundaries_mm.grow(min_timing_pos_num, max_timing_pos_num); + tof_bin_boundaries_mm.grow(min_tof_pos_num, max_tof_pos_num); - timing_bin_boundaries_ps.grow(min_timing_pos_num, max_timing_pos_num); + tof_bin_boundaries_ps.grow(min_tof_pos_num, max_tof_pos_num); - for (int i = min_timing_pos_num; i <= max_timing_pos_num; ++i ) + for (int i = min_tof_pos_num; i <= max_tof_pos_num; ++i ) { Bin bin; bin.timing_pos_num() = i; @@ -217,15 +217,24 @@ ProjDataInfo::set_tof_mash_factor(const int new_num) float cur_low = get_k(bin); float cur_high = get_k(bin) + get_sampling_in_k(bin); - timing_bin_boundaries_mm[i].low_lim = cur_low; - timing_bin_boundaries_mm[i].high_lim = cur_high; - timing_bin_boundaries_ps[i].low_lim = (timing_bin_boundaries_mm[i].low_lim * 3.33564095198f ) ; - timing_bin_boundaries_ps[i].high_lim = ( timing_bin_boundaries_mm[i].high_lim * 3.33564095198f); + tof_bin_boundaries_mm[i].low_lim = cur_low; + tof_bin_boundaries_mm[i].high_lim = cur_high; + tof_bin_boundaries_ps[i].low_lim = (tof_bin_boundaries_mm[i].low_lim * 3.33564095198f ) ; + tof_bin_boundaries_ps[i].high_lim = ( tof_bin_boundaries_mm[i].high_lim * 3.33564095198f); // I could imagine a better printing. - info(boost::format("Tbin %1%: %2% - %3% mm (%4% - %5% ps) = %6%") %i % timing_bin_boundaries_mm[i].low_lim % timing_bin_boundaries_mm[i].high_lim - % timing_bin_boundaries_ps[i].low_lim % timing_bin_boundaries_ps[i].high_lim % get_sampling_in_k(bin)); + info(boost::format("Tbin %1%: %2% - %3% mm (%4% - %5% ps) = %6%") %i % tof_bin_boundaries_mm[i].low_lim % tof_bin_boundaries_mm[i].high_lim + % tof_bin_boundaries_ps[i].low_lim % tof_bin_boundaries_ps[i].high_lim % get_sampling_in_k(bin)); } } + else if (new_num == 0) // Case new_num = 0, 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; + + // Should I initialise here and the boundaries? + } else error("Not TOF compatible scanner template. Abort."); } @@ -248,10 +257,10 @@ ProjDataInfo::ProjDataInfo(const shared_ptr& scanner_ptr_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_timing_pos_num = 0; - max_timing_pos_num = 0; - timing_increament_in_mm = 0.f; - tof_mash_factor = 1; + min_tof_pos_num = 0; + max_tof_pos_num = 0; + tof_increament_in_mm = 0.f; + tof_mash_factor = 0; } // TOF version. @@ -604,8 +613,8 @@ ProjDataInfo* ProjDataInfo::ask_parameters() ask_num("Mash factor for views",1,16,1); const int tof_mash_factor = scanner_ptr->is_tof_ready() ? - ask_num("Time-of-flight mash factor (1: No TOF):", 1, - scanner_ptr->get_num_max_of_timing_bins(), 1) : 1; + ask_num("Time-of-flight mash factor:", 0, + scanner_ptr->get_num_max_of_timing_bins(), 25) : 0; const bool arc_corrected = ask("Is the data arc-corrected?",true); diff --git a/src/buildblock/ProjDataInfoCylindricalNoArcCorr.cxx b/src/buildblock/ProjDataInfoCylindricalNoArcCorr.cxx index 8018bfc901..83a498ae8a 100644 --- a/src/buildblock/ProjDataInfoCylindricalNoArcCorr.cxx +++ b/src/buildblock/ProjDataInfoCylindricalNoArcCorr.cxx @@ -72,8 +72,7 @@ ProjDataInfoCylindricalNoArcCorr(const shared_ptr scanner_ptr, { uncompressed_view_tangpos_to_det1det2_initialised = false; det1det2_to_uncompressed_view_tangpos_initialised = false; - // If tof_mash_factor == 1 then there is only tof bin ... effectively no TOF - if (tof_mash_factor > 1) + if(scanner_ptr->is_tof_ready()) set_tof_mash_factor(tof_mash_factor); //this->initialise_uncompressed_view_tangpos_to_det1det2(); //this->initialise_det1det2_to_uncompressed_view_tangpos(); @@ -97,9 +96,8 @@ ProjDataInfoCylindricalNoArcCorr(const shared_ptr scanner_ptr, uncompressed_view_tangpos_to_det1det2_initialised = false; det1det2_to_uncompressed_view_tangpos_initialised = false; - // If tof_mash_factor == 1 then there is only tof bin ... effectively no TOF - if (tof_mash_factor > 1) - set_tof_mash_factor(tof_mash_factor); + if(scanner_ptr->is_tof_ready()) + set_tof_mash_factor(tof_mash_factor); //this->initialise_uncompressed_view_tangpos_to_det1det2(); //this->initialise_det1det2_to_uncompressed_view_tangpos(); } diff --git a/src/include/stir/ProjData.h b/src/include/stir/ProjData.h index 52acd2af38..ac9db5f5d3 100644 --- a/src/include/stir/ProjData.h +++ b/src/include/stir/ProjData.h @@ -293,11 +293,11 @@ class ProjData : public ExamData //! Get number of tangential positions inline int get_num_tangential_poss() const; //! Get number of TOF positions - inline int get_num_timing_poss() const; + inline int get_num_tof_poss() const; //! Get the index of the first timing position - inline int get_min_timing_pos_num() const; + inline int get_min_tof_pos_num() const; //! Get the index of the last timgin position. - inline int get_max_timing_pos_num() const; + inline int get_max_tof_pos_num() const; //! Get TOG mash factor inline int get_tof_mash_factor() const; //! Get minimum segment number diff --git a/src/include/stir/ProjData.inl b/src/include/stir/ProjData.inl index c48e200f8a..37f55eb13c 100644 --- a/src/include/stir/ProjData.inl +++ b/src/include/stir/ProjData.inl @@ -68,8 +68,8 @@ int ProjData::get_num_views() const int ProjData::get_num_tangential_poss() const { return proj_data_info_ptr->get_num_tangential_poss(); } -int ProjData::get_num_timing_poss() const -{ return proj_data_info_ptr->get_num_timing_poss(); } +int ProjData::get_num_tof_poss() const +{ return proj_data_info_ptr->get_num_tof_poss(); } int ProjData::get_tof_mash_factor() const { return proj_data_info_ptr->get_tof_mash_factor(); } @@ -98,11 +98,11 @@ int ProjData::get_min_tangential_pos_num() const int ProjData::get_max_tangential_pos_num() const { return proj_data_info_ptr->get_max_tangential_pos_num(); } -int ProjData::get_min_timing_pos_num() const -{ return proj_data_info_ptr->get_min_timing_pos_num(); } +int ProjData::get_min_tof_pos_num() const +{ return proj_data_info_ptr->get_min_tof_pos_num(); } -int ProjData::get_max_timing_pos_num() const -{ return proj_data_info_ptr->get_max_timing_pos_num(); } +int ProjData::get_max_tof_pos_num() const +{ return proj_data_info_ptr->get_max_tof_pos_num(); } int ProjData::get_num_sinograms() const { diff --git a/src/include/stir/ProjDataInfo.h b/src/include/stir/ProjDataInfo.h index 452b3bff8e..1f99ba631e 100644 --- a/src/include/stir/ProjDataInfo.h +++ b/src/include/stir/ProjDataInfo.h @@ -72,25 +72,27 @@ class ProjDataInfo ask_parameters(); //! Construct a ProjDataInfo suitable for GE Advance data - //! \warning N.E: TOF mash factor = 1, means no TOF + //! \warning N.E: TOF mash factor = 1, means possible many TOF bins + //! \warning N.E: TOF mash factor = 0 will produce nonTOF data static ProjDataInfo* ProjDataInfoGE(const shared_ptr& scanner_ptr, const int max_delta, const int num_views, const int num_tangential_poss, const bool arc_corrected = true, - const int tof_mash_factor = 1); + const int tof_mash_factor = 0); //! Construct a ProjDataInfo suitable for CTI data /*! \c span is used to denote the amount of axial compression (see CTI doc). It has to be an odd number. */ - //! \warning N.E: TOF mash factor = 1, means no TOF + //! \warning N.E: TOF mash factor = 1, means possible many TOF bins + //! \warning N.E: TOF mash factor = 0 will produce nonTOF data static ProjDataInfo* ProjDataInfoCTI(const shared_ptr& scanner_ptr, const int span, const int max_delta, const int num_views, const int num_tangential_poss, const bool arc_corrected = true, - const int tof_mash_factor = 1); + const int tof_mash_factor = 0); /************ constructors ***********/ @@ -205,17 +207,15 @@ class ProjDataInfo inline int get_min_tangential_pos_num() const; //! Get maximum tangential position number inline int get_max_tangential_pos_num() const; - //! Get number of TOF positions - inline int get_num_timing_poss() const; //! Get TOF mash factor inline int get_tof_mash_factor() const; - //! Get the index of the first timing position - inline int get_min_timing_pos_num() const; + //! Get the index of the first TOF position + inline int get_min_tof_pos_num() const; //! Get the index of the last timgin position. - inline int get_max_timing_pos_num() const; + inline int get_max_tof_pos_num() const; //! Get the coincide window in pico seconds //! \warning Proposed convension: If the scanner is not TOF ready then - //! the coincidence windowis in the timing bin size. + //! the coincidence windowis in the TOF bin size. inline float get_coincidence_window_in_pico_sec() const; //! Get the total width of the coincide window in mm inline float get_coincidence_window_width() const; @@ -260,7 +260,7 @@ class ProjDataInfo normal to the projection plane */ virtual float get_s(const Bin&) const =0; - //! Get value ot the timing location along the LOR (in mm) + //! Get value ot the TOF location along the LOR (in mm) //! k is a line segment connecting the centers of the two detectors. float get_k(const Bin&) const; @@ -370,10 +370,10 @@ class ProjDataInfo //! Struct which holds two floating numbers struct Float1Float2 { float low_lim; float high_lim; }; - //! Vector which holds the lower and higher boundary for each timing position in mm, for faster access. - mutable VectorWithOffset timing_bin_boundaries_mm; - //! Vector which holds the lower and higher boundary for each timing position in ps`, for faster access. - mutable VectorWithOffset timing_bin_boundaries_ps; + //! Vector which holds the lower and higher boundary for each TOF position in mm, for faster access. + mutable VectorWithOffset tof_bin_boundaries_mm; + //! Vector which holds the lower and higher boundary for each TOF position in ps`, for faster access. + mutable VectorWithOffset tof_bin_boundaries_ps; protected: virtual bool blindly_equals(const root_type * const) const = 0; @@ -384,14 +384,14 @@ class ProjDataInfo int max_view_num; int min_tangential_pos_num; int max_tangential_pos_num; - //! Minimum timing pos - int min_timing_pos_num; - //! Maximum timing pos - int max_timing_pos_num; + //! Minimum TOF pos + int min_tof_pos_num; + //! Maximum TOF pos + int max_tof_pos_num; //! TOF mash factor. int tof_mash_factor; - //! Finally (with any mashing factor) timing bin increament. - float timing_increament_in_mm; + //! Finally (with any mashing factor) TOF bin increament. + float tof_increament_in_mm; //! Number of tof bins (TOF mash factor applied) int num_tof_bins; VectorWithOffset min_axial_pos_per_seg; diff --git a/src/include/stir/ProjDataInfo.inl b/src/include/stir/ProjDataInfo.inl index b3487b6532..588d73b186 100644 --- a/src/include/stir/ProjDataInfo.inl +++ b/src/include/stir/ProjDataInfo.inl @@ -57,10 +57,6 @@ int ProjDataInfo::get_num_tangential_poss() const { return max_tangential_pos_num - min_tangential_pos_num + 1; } -int -ProjDataInfo::get_num_timing_poss() const -{ return max_timing_pos_num - min_timing_pos_num +1; } - int ProjDataInfo::get_num_tof_poss() const { return num_tof_bins; } @@ -68,10 +64,10 @@ ProjDataInfo::get_num_tof_poss() const int ProjDataInfo::get_tof_bin(const double& delta) const { - for (int i = min_timing_pos_num; i < max_timing_pos_num; i++) + for (int i = min_tof_pos_num; i < max_tof_pos_num; i++) { - if ( delta > timing_bin_boundaries_ps[i].low_lim && - delta < timing_bin_boundaries_ps[i].high_lim) + if ( delta > tof_bin_boundaries_ps[i].low_lim && + delta < tof_bin_boundaries_ps[i].high_lim) return i; } } @@ -116,15 +112,15 @@ ProjDataInfo::get_max_tangential_pos_num()const { return max_tangential_pos_num; } int -ProjDataInfo::get_min_timing_pos_num() const +ProjDataInfo::get_min_tof_pos_num() const { - return min_timing_pos_num; + return min_tof_pos_num; } int -ProjDataInfo::get_max_timing_pos_num() const +ProjDataInfo::get_max_tof_pos_num() const { - return max_timing_pos_num; + return max_tof_pos_num; } float diff --git a/src/include/stir/ProjDataInfoCylindricalArcCorr.h b/src/include/stir/ProjDataInfoCylindricalArcCorr.h index 7c6bfc3fdf..be7fb6a14c 100644 --- a/src/include/stir/ProjDataInfoCylindricalArcCorr.h +++ b/src/include/stir/ProjDataInfoCylindricalArcCorr.h @@ -63,7 +63,7 @@ class ProjDataInfoCylindricalArcCorr : public ProjDataInfoCylindrical const VectorWithOffset& min_ring_diff_v, const VectorWithOffset& max_ring_diff_v, const int num_views,const int num_tangential_poss, - const int tof_mash_factor = 1); + const int tof_mash_factor = 0); ProjDataInfo* clone() const; diff --git a/src/include/stir/ProjDataInfoCylindricalNoArcCorr.h b/src/include/stir/ProjDataInfoCylindricalNoArcCorr.h index 4137d4d8d5..99a6ab3c26 100644 --- a/src/include/stir/ProjDataInfoCylindricalNoArcCorr.h +++ b/src/include/stir/ProjDataInfoCylindricalNoArcCorr.h @@ -109,7 +109,7 @@ class ProjDataInfoCylindricalNoArcCorr : public ProjDataInfoCylindrical const VectorWithOffset& min_ring_diff_v, const VectorWithOffset& max_ring_diff_v, const int num_views,const int num_tangential_poss, - const int tof_mash_factor = 1); + const int tof_mash_factor = 0); //! Constructor which gets \a ring_radius and \a angular_increment from the scanner /*! \a angular_increment is determined as Pi divided by the number of detectors in a ring. @@ -119,7 +119,7 @@ class ProjDataInfoCylindricalNoArcCorr : public ProjDataInfoCylindrical const VectorWithOffset& min_ring_diff_v, const VectorWithOffset& max_ring_diff_v, const int num_views,const int num_tangential_poss, - const int tof_mash_factor = 1); + const int tof_mash_factor = 0); ProjDataInfo* clone() const; diff --git a/src/include/stir/listmode/CListEventCylindricalScannerWithDiscreteDetectors.inl b/src/include/stir/listmode/CListEventCylindricalScannerWithDiscreteDetectors.inl index 7baa19f36b..f8a87c416a 100644 --- a/src/include/stir/listmode/CListEventCylindricalScannerWithDiscreteDetectors.inl +++ b/src/include/stir/listmode/CListEventCylindricalScannerWithDiscreteDetectors.inl @@ -84,7 +84,11 @@ get_bin(Bin& bin, const ProjDataInfo& proj_data_info) const get_bin_for_det_pos_pair(bin, det_pos) == Succeeded::no) bin.set_bin_value(0); else + { bin.set_bin_value(1); + if (proj_data_info.get_num_tof_poss() > 1) + bin.timing_pos_num() = proj_data_info.get_tof_bin(delta_time); + } } END_NAMESPACE_STIR diff --git a/src/include/stir/listmode/CListEventCylindricalScannerWithViewTangRingRingEncoding.inl b/src/include/stir/listmode/CListEventCylindricalScannerWithViewTangRingRingEncoding.inl index be0db7deaa..2630ba82f8 100644 --- a/src/include/stir/listmode/CListEventCylindricalScannerWithViewTangRingRingEncoding.inl +++ b/src/include/stir/listmode/CListEventCylindricalScannerWithViewTangRingRingEncoding.inl @@ -110,6 +110,8 @@ get_bin(Bin& bin, const ProjDataInfo& proj_data_info) const get_sinogram_and_ring_coordinates(view_num, tangential_pos_num, ring_a, ring_b); sinogram_coordinates_to_bin(bin, view_num, tangential_pos_num, ring_a, ring_b, static_cast(proj_data_info)); + if (proj_data_info.get_num_tof_poss() > 1) + bin.timing_pos_num() = proj_data_info.get_tof_bin(delta_time); } template diff --git a/src/include/stir/listmode/CListRecord.h b/src/include/stir/listmode/CListRecord.h index 85ed55ef16..2eeb9fb7b5 100644 --- a/src/include/stir/listmode/CListRecord.h +++ b/src/include/stir/listmode/CListRecord.h @@ -109,6 +109,11 @@ class CListEvent void get_bin(Bin& bin, const ProjDataInfo&) const; +protected: + //! The detection time difference, between the two photons. + //! This will work for ROOT files, but not so sure about acquired data. + double delta_time; + }; /*-coincidence event*/ @@ -135,25 +140,12 @@ class CListTime virtual Succeeded set_time_in_millisecs(const unsigned long time_in_millisecs) = 0; inline Succeeded set_time_in_secs(const double time_in_secs) - { + { unsigned long time_in_millisecs; round_to(time_in_millisecs, time_in_secs/1000.); - return set_time_in_millisecs(time_in_millisecs); + return set_time_in_millisecs(time_in_millisecs); } - virtual inline int get_timing_bin() const - { - error("Function CListTime::get_timing_bin() currently is implemented only " - "ROOT data. Abort."); - } - - //! Get the timing component of the bin. - virtual inline void get_bin(Bin&, const ProjDataInfo&) const - { - error("CListTime::get_bin() currently is implemented only " - "ROOT data. Abort."); - } - }; //! A class recording external input to the scanner (normally used for gating) @@ -204,12 +196,6 @@ class CListRecord virtual bool operator==(const CListRecord& e2) const = 0; bool operator!=(const CListRecord& e2) const { return !(*this == e2); } - //! Used in TOF reconstruction to get both the geometric and the timing - //! component of the event - virtual void full_event(Bin&, const ProjDataInfo&) const - {error("CListRecord::full_event() is implemented only for records which " - "hold timing and spatial information.");} - }; class CListRecordWithGatingInput : public CListRecord diff --git a/src/include/stir/listmode/CListRecordROOT.h b/src/include/stir/listmode/CListRecordROOT.h index 62ad478aea..3cee3e0aaa 100644 --- a/src/include/stir/listmode/CListRecordROOT.h +++ b/src/include/stir/listmode/CListRecordROOT.h @@ -52,7 +52,8 @@ class CListEventROOT : public CListEventCylindricalScannerWithDiscreteDetectors //! \details This is the main function which transform GATE coordinates to STIR void init_from_data(const int &_ring1, const int &_ring2, - const int &crystal1, const int &crystal2); + const int &crystal1, const int &crystal2, + const double& _delta_time); inline bool is_prompt() const { return true; } @@ -74,6 +75,7 @@ class CListEventROOT : public CListEventCylindricalScannerWithDiscreteDetectors //! This is the number of detector we have to rotate in order to //! align GATE and STIR. int quarter_of_detectors; + }; //! A class for storing and using a timing 'event' from a listmode file from the ECAT 8_32bit scanner @@ -82,10 +84,9 @@ class CListEventROOT : public CListEventCylindricalScannerWithDiscreteDetectors class CListTimeROOT : public CListTime { public: - void init_from_data(float _timeA, float _delta_time) + void init_from_data(double time1) { - timeA = _timeA; - delta_time = _delta_time; + timeA = time1; } //! Returns always true @@ -94,49 +95,19 @@ class CListTimeROOT : public CListTime //! Returns the detection time of the first photon //! in milliseconds. inline unsigned long get_time_in_millisecs() const - { return timeA * 1e3; } - //! Get the detection time of the first photon - //! in milliseconds - inline unsigned long get_timeA_in_millisecs() const - { return timeA * 1e3; } - //! Get the detection time of the second photon - //! in milliseconds - inline unsigned long get_timeB_in_millisecs() const - { return (delta_time - timeA) * 1e3; } - //! Get the delta Time between the two events - inline unsigned long get_delta_time_in_millisecs() const - { return delta_time * 1e3; } - + { return static_cast (timeA * 1e3); } inline Succeeded set_time_in_millisecs(const unsigned long time_in_millisecs) { warning("set_time_in_millisecs: Not implemented for ROOT files. Aborting."); return Succeeded::no; } - //! N.E. Sadly I had to comment the original version of this function. It might had been faster. - //! and make more sense under many occations. - virtual inline void get_bin(Bin& bin, const ProjDataInfo& proj_data_info) const - { - // delta_time >= 0.0 ? - // bin.timing_pos_num() = static_cast ( ( delta_time / proj_data_info.get_num_tof_poss()) + 0.5) - // : bin.timing_pos_num() = static_cast ( ( delta_time / proj_data_info.get_num_tof_poss()) - 0.5); - - // if (bin.timing_pos_num() < proj_data_info.get_min_timing_pos_num() || - // bin.timing_pos_num() > proj_data_info.get_max_timing_pos_num()) - // { - // bin.set_bin_value(-1.f); - // } - bin.timing_pos_num() = proj_data_info.get_tof_bin(delta_time); - } - private: //! //! \brief timeA //! \details The detection time of the first of the two photons, in seconds double timeA; - - double delta_time; }; //! A class for a general element of a listmode file for a Siemens scanner using the ROOT files @@ -147,8 +118,6 @@ class CListRecordROOT : public CListRecord // currently no gating yet bool inline is_time() const; //! Returns always true bool inline is_event() const; - //! Returns always true - bool inline is_full_event() const; virtual CListEventROOT& event() { @@ -170,12 +139,6 @@ class CListRecordROOT : public CListRecord // currently no gating yet return this->time_data; } - virtual void full_event(Bin& bin, const ProjDataInfo& proj_data_info) const - { - event_data.get_bin(bin, proj_data_info); - time_data.get_bin(bin, proj_data_info); - } - bool operator==(const CListRecord& e2) const { return dynamic_cast(&e2) != 0 && @@ -191,19 +154,17 @@ class CListRecordROOT : public CListRecord // currently no gating yet const int& ring2, const int& crystal1, const int& crystal2, - const double& time1, const double& delta_time, + const double& time1, + const double& delta_time, const int& event1, const int& event2) { /// \warning ROOT data are time and event at the same time. this->event_data.init_from_data(ring1, ring2, - crystal1, crystal2); + crystal1, crystal2, + delta_time); - this->event_data.is_swapped() ? - this->time_data.init_from_data( - time1, -delta_time) : - this->time_data.init_from_data( - time1, delta_time); + this->time_data.init_from_data(time1); // We can make a singature raw based on the two events IDs. // It is pretty unique. diff --git a/src/include/stir/listmode/CListRecordROOT.inl b/src/include/stir/listmode/CListRecordROOT.inl index 26270593c4..d1c0d7de3c 100644 --- a/src/include/stir/listmode/CListRecordROOT.inl +++ b/src/include/stir/listmode/CListRecordROOT.inl @@ -33,7 +33,4 @@ bool CListRecordROOT::is_time() const bool CListRecordROOT::is_event() const { return true; } -bool CListRecordROOT::is_full_event() const -{ return true; } - END_NAMESPACE_STIR diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl index d3ce6147c7..d691e2e4df 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl +++ b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl @@ -202,8 +202,8 @@ ProjMatrixByBin::apply_tof_kernel(ProjMatrixElemsForOneBin& nonTOF_probabilities // (point2.z() - voxel_center.z()) *(point2.z() - voxel_center.z())); float m = (lor_length - d1 - d1) * 0.5f; - low_dist = (proj_data_info_sptr->timing_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].low_lim - m) * r_sqrt2_gauss_sigma; - high_dist = (proj_data_info_sptr->timing_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].high_lim - m) * r_sqrt2_gauss_sigma; + low_dist = (proj_data_info_sptr->tof_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].low_lim - m) * r_sqrt2_gauss_sigma; + high_dist = (proj_data_info_sptr->tof_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].high_lim - m) * r_sqrt2_gauss_sigma; // Cut-off really small values. // Currently deactivate untill I run all the tests. diff --git a/src/listmode_buildblock/CListEvent.cxx b/src/listmode_buildblock/CListEvent.cxx index ad801a2685..3f5f92bbd0 100644 --- a/src/listmode_buildblock/CListEvent.cxx +++ b/src/listmode_buildblock/CListEvent.cxx @@ -46,6 +46,8 @@ CListEvent:: get_bin(Bin& bin, const ProjDataInfo& proj_data_info) const { bin = proj_data_info.get_bin(get_LOR()); + if (proj_data_info.get_num_tof_poss() > 1) + bin.timing_pos_num() = proj_data_info.get_tof_bin(delta_time); } END_NAMESPACE_STIR diff --git a/src/listmode_buildblock/CListRecordROOT.cxx b/src/listmode_buildblock/CListRecordROOT.cxx index 03d314f767..4aa4051dc6 100644 --- a/src/listmode_buildblock/CListRecordROOT.cxx +++ b/src/listmode_buildblock/CListRecordROOT.cxx @@ -59,7 +59,8 @@ void CListEventROOT::set_detection_position(const DetectionPositionPair<>&) } void CListEventROOT::init_from_data(const int& _ring1, const int& _ring2, - const int& crystal1, const int& crystal2) + const int& crystal1, const int& crystal2, + const double& _delta_time) { // STIR assumes that 0 is on y whill GATE on the x axis @@ -84,12 +85,14 @@ void CListEventROOT::init_from_data(const int& _ring1, const int& _ring2, ring1 = _ring2; ring2 = _ring1; + delta_time = -_delta_time; swapped = true; } else { ring1 = _ring1; ring2 = _ring2; + delta_time = _delta_time; swapped = false; } } diff --git a/src/listmode_buildblock/LmToProjData.cxx b/src/listmode_buildblock/LmToProjData.cxx index 983b3beae3..a92dddffef 100644 --- a/src/listmode_buildblock/LmToProjData.cxx +++ b/src/listmode_buildblock/LmToProjData.cxx @@ -426,9 +426,9 @@ get_bin_from_record(Bin& bin, const CListRecord& record) const // template_proj_data_info_ptr->get_bin_from_uncompressed(bin, uncompressed_bin); - if (use_tof) - record.full_event(bin, *template_proj_data_info_ptr); - else +// if (use_tof) +// record.full_event(bin, *template_proj_data_info_ptr); +// else record.event().get_bin(bin, *template_proj_data_info_ptr); bin.set_bin_value(bin_value); @@ -808,8 +808,8 @@ actual_process_data_with_tof() const double start_time = frame_defs.get_start_time(current_frame_num); const double end_time = frame_defs.get_end_time(current_frame_num); - for (int current_timing_pos_index = proj_data_ptr->get_min_timing_pos_num(); - current_timing_pos_index <= proj_data_ptr->get_max_timing_pos_num(); + for (int current_timing_pos_index = proj_data_ptr->get_min_tof_pos_num(); + current_timing_pos_index <= proj_data_ptr->get_max_tof_pos_num(); current_timing_pos_index += 1) { /* @@ -899,8 +899,8 @@ actual_process_data_with_tof() && bin.tangential_pos_num()<= proj_data_ptr->get_max_tangential_pos_num() && bin.axial_pos_num()>=proj_data_ptr->get_min_axial_pos_num(bin.segment_num()) && bin.axial_pos_num()<=proj_data_ptr->get_max_axial_pos_num(bin.segment_num()) - && bin.timing_pos_num()>=proj_data_ptr->get_min_timing_pos_num() - && bin.timing_pos_num()<=proj_data_ptr->get_max_timing_pos_num() + && bin.timing_pos_num()>=proj_data_ptr->get_min_tof_pos_num() + && bin.timing_pos_num()<=proj_data_ptr->get_max_tof_pos_num() ) { assert(bin.view_num()>=proj_data_ptr->get_min_view_num()); @@ -1012,8 +1012,8 @@ LmToProjData::run_tof_test_function() construct_proj_data(output, output_filename, this_frame_exam_info, template_proj_data_info_ptr); } - for (int current_timing_pos_index = proj_data_ptr->get_min_timing_pos_num(); - current_timing_pos_index <= proj_data_ptr->get_max_timing_pos_num(); + for (int current_timing_pos_index = proj_data_ptr->get_min_tof_pos_num(); + current_timing_pos_index <= proj_data_ptr->get_max_tof_pos_num(); current_timing_pos_index += 1) { for (int start_segment_index = proj_data_ptr->get_min_segment_num(); diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx index 67951cf6a3..5a49aca198 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx @@ -476,8 +476,8 @@ compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, { measured_bin.set_bin_value(1.0f); - this->use_tof ? record.full_event(measured_bin, *proj_data_info_cyl_sptr): - record.event().get_bin(measured_bin, *proj_data_info_cyl_sptr); +// this->use_tof ? record.full_event(measured_bin, *proj_data_info_cyl_sptr): + record.event().get_bin(measured_bin, *proj_data_info_cyl_sptr); // In theory we have already done all these checks so we can // remove this if statement. @@ -488,8 +488,8 @@ compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, || measured_bin.tangential_pos_num() > proj_data_info_cyl_sptr->get_max_tangential_pos_num() || measured_bin.axial_pos_num() < proj_data_info_cyl_sptr->get_min_axial_pos_num(measured_bin.segment_num()) || measured_bin.axial_pos_num() > proj_data_info_cyl_sptr->get_max_axial_pos_num(measured_bin.segment_num()) - || measured_bin.timing_pos_num() < proj_data_info_cyl_sptr->get_min_timing_pos_num() - || measured_bin.timing_pos_num() > proj_data_info_cyl_sptr->get_max_timing_pos_num()) + || measured_bin.timing_pos_num() < proj_data_info_cyl_sptr->get_min_tof_pos_num() + || measured_bin.timing_pos_num() > proj_data_info_cyl_sptr->get_max_tof_pos_num()) { continue; } diff --git a/src/test/test_time_of_flight.cxx b/src/test/test_time_of_flight.cxx index 12d90769de..9edc0c587b 100644 --- a/src/test/test_time_of_flight.cxx +++ b/src/test/test_time_of_flight.cxx @@ -185,10 +185,10 @@ TOF_Tests::test_tof_proj_data_info() test_proj_data_info_sptr->get_tof_mash_factor(), "Diffent tof mash factor."); check_if_equal(num_timing_positions, - test_proj_data_info_sptr->get_num_timing_poss(), "Diffent in number of timing positions."); + test_proj_data_info_sptr->get_num_tof_poss(), "Diffent in number of timing positions."); - for (int timing_num = test_proj_data_info_sptr->get_min_timing_pos_num(), counter = 0; - timing_num <= test_proj_data_info_sptr->get_max_timing_pos_num(); ++ timing_num, counter++) + for (int timing_num = test_proj_data_info_sptr->get_min_tof_pos_num(), counter = 0; + timing_num <= test_proj_data_info_sptr->get_max_tof_pos_num(); ++ timing_num, counter++) { Bin bin(0, 0, 0, 0, timing_num, 1.f); @@ -198,8 +198,8 @@ TOF_Tests::test_tof_proj_data_info() static_cast(test_proj_data_info_sptr->get_k(bin)), "Error in get_sampling_in_k()"); } - float total_width = test_proj_data_info_sptr->get_k(Bin(0,0,0,0,test_proj_data_info_sptr->get_max_timing_pos_num(),1.f)) - - test_proj_data_info_sptr->get_k(Bin(0,0,0,0,test_proj_data_info_sptr->get_min_timing_pos_num(),1.f)) + float total_width = test_proj_data_info_sptr->get_k(Bin(0,0,0,0,test_proj_data_info_sptr->get_max_tof_pos_num(),1.f)) + - test_proj_data_info_sptr->get_k(Bin(0,0,0,0,test_proj_data_info_sptr->get_min_tof_pos_num(),1.f)) + test_proj_data_info_sptr->get_sampling_in_k(Bin(0,0,0,0,0,1.f)); set_tolerance(static_cast(0.005)); @@ -340,8 +340,8 @@ TOF_Tests::test_tof_kernel_application() export_lor(proj_matrix_row, lor_point_1, lor_point_2, 5000); - for (int timing_num = test_proj_data_info_sptr->get_min_timing_pos_num(); - timing_num <= test_proj_data_info_sptr->get_max_timing_pos_num(); ++ timing_num) + for (int timing_num = test_proj_data_info_sptr->get_min_tof_pos_num(); + timing_num <= test_proj_data_info_sptr->get_max_tof_pos_num(); ++ timing_num) { ProjMatrixElemsForOneBin new_proj_matrix_row; Bin bin(seg_num, view_num, axial_num, tang_num, timing_num, 1.f); diff --git a/src/utilities/create_projdata_template.cxx b/src/utilities/create_projdata_template.cxx index e33eb399db..bb85ad5575 100644 --- a/src/utilities/create_projdata_template.cxx +++ b/src/utilities/create_projdata_template.cxx @@ -63,10 +63,10 @@ int main(int argc, char *argv[]) // TODO, Currently all stir::Scanner types are PET. exam_info_sptr->imaging_modality = ImagingModality::PT; // If TOF activated -- No mashing factor will produce surrealistic sinograms - if ( proj_data_info_sptr->get_tof_mash_factor() >1) - shared_ptr proj_data_sptr(new ProjDataInterfile(exam_info_sptr, proj_data_info_sptr, output_file_name, std::ios::out, - ProjDataFromStream::Timing_Segment_View_AxialPos_TangPos)); - else + //if ( proj_data_info_sptr->get_num_tof_poss() >1) + // shared_ptr proj_data_sptr(new ProjDataInterfile(exam_info_sptr, proj_data_info_sptr, output_file_name, std::ios::out, + // ProjDataFromStream::Timing_Segment_View_AxialPos_TangPos)); + // else shared_ptr proj_data_sptr(new ProjDataInterfile(exam_info_sptr, proj_data_info_sptr, output_file_name)); return EXIT_SUCCESS; diff --git a/src/utilities/list_projdata_info.cxx b/src/utilities/list_projdata_info.cxx index 5ac2c87952..0b1bdd9e6c 100644 --- a/src/utilities/list_projdata_info.cxx +++ b/src/utilities/list_projdata_info.cxx @@ -145,13 +145,13 @@ int main(int argc, char *argv[]) if (print_geom) std::cout << proj_data_sptr->get_proj_data_info_ptr()->parameter_info() << std::endl; - //if (print_min || print_max || print_sum) + if (print_min || print_max || print_sum) { const int min_segment_num = proj_data_sptr->get_min_segment_num(); const int max_segment_num = proj_data_sptr->get_max_segment_num(); - const int min_timing_num = proj_data_sptr->get_min_timing_pos_num(); - const int max_timing_num = proj_data_sptr->get_max_timing_pos_num(); - std::cout << "\nTotal number of timing positions: " << proj_data_sptr->get_num_timing_poss(); + const int min_timing_num = proj_data_sptr->get_min_tof_pos_num(); + const int max_timing_num = proj_data_sptr->get_max_tof_pos_num(); + std::cout << "\nTotal number of TOF positions: " << proj_data_sptr->get_num_tof_poss(); for (int timing_num = min_timing_num; timing_num <= max_timing_num; ++timing_num) { From c35c701a3ea81a22354e9ee1809855765ce9b667 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Sun, 12 Feb 2017 22:26:24 +0000 Subject: [PATCH 055/170] Amended CListEvent at LmToProjData --- src/include/stir/listmode/LmToProjData.h | 4 +--- src/include/stir/listmode/LmToProjDataBootstrap.h | 2 +- src/listmode_buildblock/LmToProjData.cxx | 15 ++++++--------- src/listmode_buildblock/LmToProjDataBootstrap.cxx | 6 +++--- 4 files changed, 11 insertions(+), 16 deletions(-) diff --git a/src/include/stir/listmode/LmToProjData.h b/src/include/stir/listmode/LmToProjData.h index 53ed71fc4c..d6e4b9f00e 100644 --- a/src/include/stir/listmode/LmToProjData.h +++ b/src/include/stir/listmode/LmToProjData.h @@ -206,9 +206,7 @@ class LmToProjData : public ParsingObject (on top of anything done by normalisation_ptr). \todo Would need timing info or so for e.g. time dependent normalisation or angle info for a rotating scanner.*/ - //! \warning N.E: I changed this function _from_event to _from_record, because in - //! TOF unlisting we need the timing information which is stored in the record. - virtual void get_bin_from_record(Bin& bin, const CListRecord&) const; + virtual void get_bin_from_event(Bin& bin, const CListEvent&) const; //! A function that should return the number of uncompressed bins in the current bin /*! \todo it is not compatiable with e.g. HiDAC doesn't belong here anyway diff --git a/src/include/stir/listmode/LmToProjDataBootstrap.h b/src/include/stir/listmode/LmToProjDataBootstrap.h index ea7cd8c78c..4d98a813eb 100644 --- a/src/include/stir/listmode/LmToProjDataBootstrap.h +++ b/src/include/stir/listmode/LmToProjDataBootstrap.h @@ -87,7 +87,7 @@ class LmToProjDataBootstrap : public LmToProjDataT /*! Initialises a vector with the number of times each event has to be replicated */ virtual void start_new_time_frame(const unsigned int new_frame_num); - virtual void get_bin_from_record(Bin& bin, const CListRecord&) const; + virtual void get_bin_from_event(Bin& bin, const CListEvent&) const; // \name parsing variables diff --git a/src/listmode_buildblock/LmToProjData.cxx b/src/listmode_buildblock/LmToProjData.cxx index a92dddffef..13ac7c7755 100644 --- a/src/listmode_buildblock/LmToProjData.cxx +++ b/src/listmode_buildblock/LmToProjData.cxx @@ -385,12 +385,12 @@ LmToProjData(const char * const par_filename) ***************************************************************/ void LmToProjData:: -get_bin_from_record(Bin& bin, const CListRecord& record) const +get_bin_from_event(Bin& bin, const CListEvent& event) const { if (do_pre_normalisation) { Bin uncompressed_bin; - record.event().get_bin(uncompressed_bin, *proj_data_info_cyl_uncompressed_ptr); + event.get_bin(uncompressed_bin, *proj_data_info_cyl_uncompressed_ptr); if (uncompressed_bin.get_bin_value()<=0) return; // rejected for some strange reason @@ -426,17 +426,14 @@ get_bin_from_record(Bin& bin, const CListRecord& record) const // template_proj_data_info_ptr->get_bin_from_uncompressed(bin, uncompressed_bin); -// if (use_tof) -// record.full_event(bin, *template_proj_data_info_ptr); -// else - record.event().get_bin(bin, *template_proj_data_info_ptr); + event.get_bin(bin, *template_proj_data_info_ptr); bin.set_bin_value(bin_value); } else { - record.event().get_bin(bin, *template_proj_data_info_ptr); + event.get_bin(bin, *template_proj_data_info_ptr); } } @@ -668,7 +665,7 @@ actual_process_data_without_tof() // set value in case the event decoder doesn't touch it // otherwise it would be 0 and all events will be ignored bin.set_bin_value(1); - get_bin_from_record(bin, record); + get_bin_from_event(bin, record.event()); // check if it's inside the range we want to store if (bin.get_bin_value()>0 @@ -891,7 +888,7 @@ actual_process_data_with_tof() // otherwise it would be 0 and all events will be ignored bin.set_bin_value(1.f); - get_bin_from_record(bin, record); + get_bin_from_event(bin, record.event()); // check if it's inside the range we want to store if (bin.get_bin_value()>0 diff --git a/src/listmode_buildblock/LmToProjDataBootstrap.cxx b/src/listmode_buildblock/LmToProjDataBootstrap.cxx index 6327f8b193..6b2752c7e3 100644 --- a/src/listmode_buildblock/LmToProjDataBootstrap.cxx +++ b/src/listmode_buildblock/LmToProjDataBootstrap.cxx @@ -166,7 +166,7 @@ start_new_time_frame(const unsigned int new_frame_num) // set value in case the event decoder doesn't touch it // otherwise it would be 0 and all events will be ignored bin.set_bin_value(1); - base_type::get_bin_from_record(bin, record); + base_type::get_bin_from_event(bin, record.event()); // check if it's inside the range we want to store if (bin.get_bin_value()>0 && bin.tangential_pos_num()>= this->template_proj_data_info_ptr->get_min_tangential_pos_num() @@ -232,12 +232,12 @@ start_new_time_frame(const unsigned int new_frame_num) template void LmToProjDataBootstrap:: -get_bin_from_record(Bin& bin, const CListRecord& record) const +get_bin_from_event(Bin& bin, const CListEvent& event) const { assert(num_times_to_replicate_iter != num_times_to_replicate.end()); if (*num_times_to_replicate_iter > 0) { - base_type::get_bin_from_record(bin, record); + base_type::get_bin_from_event(bin, event); bin.set_bin_value(bin.get_bin_value() * *num_times_to_replicate_iter); } else From 4407f03eb5baa2ea816f53565eba9377dc81cffc Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Thu, 23 Feb 2017 14:43:52 +0000 Subject: [PATCH 056/170] cleaned the hdf5 --- CMakeLists.txt | 5 +++++ src/CMakeLists.txt | 4 ---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a735814efc..aa1d471d73 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,6 +57,7 @@ option(DISABLE_AVW "disable use of AVW library" OFF) option(DISABLE_RDF "disable use of GE RDF library" OFF) option(DISABLE_STIR_LOCAL "disable use of LOCAL extensions to STIR" OFF) option(DISABLE_CERN_ROOT_SUPPORT "disable use of Cern ROOT libraries" OFF) +option(DISABLE_HDF5_SUPPORT "disable use of HDF5 libraries" OFF) if(NOT DISABLE_ITK) # See if we can find a compiled version of ITK (http://www.itk.org/) @@ -71,6 +72,10 @@ if(NOT DISABLE_CERN_ROOT_SUPPORT) find_package(CERN_ROOT) endif() +if(NOT DISABLE_HDF5_SUPPORT) + find_package(HDF5 COMPONENTS CXX) +endif() + if(NOT DISABLE_AVW) find_package(AVW) endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6c4decee14..f08f1381bb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -34,10 +34,6 @@ option(BUILD_EXECUTABLES option(BUILD_SHARED_LIBS "Use shared libraries" OFF) -### Settings for external libraries - -find_package(HDF5 COMPONENTS CXX) - if (LLN_FOUND) set(HAVE_ECAT ON) message(STATUS "ECAT support enabled.") From 1f14f44fbf54e59473a7510a715a3135e7d04768 Mon Sep 17 00:00:00 2001 From: Ottavia Date: Thu, 23 Feb 2017 15:29:52 +0000 Subject: [PATCH 057/170] Added get_tof_bin and evaluation of delta time in picoseconds in CListRecordGESigna.h --- src/include/stir/listmode/CListRecordGESigna.h | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/include/stir/listmode/CListRecordGESigna.h b/src/include/stir/listmode/CListRecordGESigna.h index abb4f2fa84..e8e19f4153 100644 --- a/src/include/stir/listmode/CListRecordGESigna.h +++ b/src/include/stir/listmode/CListRecordGESigna.h @@ -76,7 +76,10 @@ class CListEventDataGESigna { return (eventType==COINC_EVT)/* && eventTypeExt==COINC_COUNT_EVT)*/; } // TODO need to find out how to see if it's a coincidence event - + inline int get_tof_bin() const + { + return static_cast(deltaTime); + } private: #if STIRIsNativeByteOrderBigEndian @@ -315,6 +318,16 @@ dynamic_cast(&e2) != 0 && error("don't know how to byteswap"); ByteOrder::swap_order(this->raw[1]); } + + if (this->is_event()) + { + // set TOF info in ps + this->delta_time = this->event_data.get_tof_bin() * + this-> get_scanner_ptr()->get_size_of_timing_bin(); + } + + + return Succeeded::yes; } From a6b96ed569e672bd9d07944410d096776d2c2f37 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Thu, 23 Feb 2017 15:37:28 +0000 Subject: [PATCH 058/170] Moved TOF initialisation in ProjDataStream new support function --- src/buildblock/ProjDataFromStream.cxx | 73 +++++++++++++-------------- src/include/stir/ProjDataFromStream.h | 2 + 2 files changed, 36 insertions(+), 39 deletions(-) diff --git a/src/buildblock/ProjDataFromStream.cxx b/src/buildblock/ProjDataFromStream.cxx index 13190f4362..2c369ae792 100644 --- a/src/buildblock/ProjDataFromStream.cxx +++ b/src/buildblock/ProjDataFromStream.cxx @@ -93,30 +93,9 @@ ProjDataFromStream::ProjDataFromStream(shared_ptr const& exam_info_spt assert(storage_order != Unsupported); assert(!(data_type == NumericType::UNKNOWN_TYPE)); - int sum = 0; - for (int segment_num = proj_data_info_ptr->get_min_segment_num(); - segment_num<=proj_data_info_ptr->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()); + if (proj_data_info_ptr->get_num_tof_poss() > 1) + activate_TOF(); - // Now, lets initialise a TOF stream - Similarly to segments - if (storage_order == Timing_Segment_View_AxialPos_TangPos && - proj_data_info_ptr->get_num_tof_poss() > 1) - { - timing_poss_sequence.resize(proj_data_info_ptr->get_num_tof_poss()); - int timing_pos_num; - - for (int i= 0, timing_pos_num = proj_data_info_ptr->get_min_tof_pos_num(); - timing_pos_num<=proj_data_info_ptr->get_max_tof_pos_num(); - ++i, ++timing_pos_num) - { - timing_poss_sequence[i] = timing_pos_num; - } - } } ProjDataFromStream::ProjDataFromStream(shared_ptr const& exam_info_sptr, @@ -141,31 +120,47 @@ ProjDataFromStream::ProjDataFromStream(shared_ptr const& exam_info_spt //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, tmp_add = 0; + 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(); ++i, ++segment_num) { segment_sequence[i] =segment_num; - tmp_add += get_num_axial_poss(segment_num) * get_num_views() * get_num_tangential_poss(); } - offset_3d_data = static_cast (tmp_add * on_disk_data_type.size_in_bytes()); + if (proj_data_info_ptr->get_num_tof_poss() > 1) + activate_TOF(); - // Now, lets initialise a TOF stream - Similarly to segments - if (storage_order == Timing_Segment_View_AxialPos_TangPos && - proj_data_info_ptr->get_num_tof_poss() > 1) - { - timing_poss_sequence.resize(proj_data_info_ptr->get_num_tof_poss()); - int timing_pos_num; +} - for (i= 0, timing_pos_num = proj_data_info_ptr->get_min_tof_pos_num(); - timing_pos_num<=proj_data_info_ptr->get_max_tof_pos_num(); - ++i, ++timing_pos_num) - { - timing_poss_sequence[i] = timing_pos_num; - } - } +void +ProjDataFromStream::activate_TOF() +{ + int sum = 0; + for (int segment_num = proj_data_info_ptr->get_min_segment_num(); + segment_num<=proj_data_info_ptr->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 == Timing_Segment_View_AxialPos_TangPos && + proj_data_info_ptr->get_num_tof_poss() > 1) + { + timing_poss_sequence.resize(proj_data_info_ptr->get_num_tof_poss()); + int timing_pos_num; + + for (int i= 0, timing_pos_num = proj_data_info_ptr->get_min_tof_pos_num(); + timing_pos_num<=proj_data_info_ptr->get_max_tof_pos_num(); + ++i, ++timing_pos_num) + { + timing_poss_sequence[i] = timing_pos_num; + } + } } Viewgram diff --git a/src/include/stir/ProjDataFromStream.h b/src/include/stir/ProjDataFromStream.h index f4e7394e74..40b160a71e 100644 --- a/src/include/stir/ProjDataFromStream.h +++ b/src/include/stir/ProjDataFromStream.h @@ -163,6 +163,8 @@ class ProjDataFromStream : public ProjData shared_ptr sino_stream; private: + + void activate_TOF(); //! offset of the whole 3d sinogram in the stream std::streamoff offset; //! offset of a complete non-tof sinogram From ac79a3eff916990c44a8d833397d9b138f5f41f0 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Thu, 23 Feb 2017 15:44:39 +0000 Subject: [PATCH 059/170] fix in activate_TOF --- src/buildblock/ProjDataFromStream.cxx | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/buildblock/ProjDataFromStream.cxx b/src/buildblock/ProjDataFromStream.cxx index 2c369ae792..543b79338d 100644 --- a/src/buildblock/ProjDataFromStream.cxx +++ b/src/buildblock/ProjDataFromStream.cxx @@ -148,19 +148,17 @@ ProjDataFromStream::activate_TOF() 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 == Timing_Segment_View_AxialPos_TangPos && - proj_data_info_ptr->get_num_tof_poss() > 1) - { - timing_poss_sequence.resize(proj_data_info_ptr->get_num_tof_poss()); - int timing_pos_num; + storage_order = Timing_Segment_View_AxialPos_TangPos; - for (int i= 0, timing_pos_num = proj_data_info_ptr->get_min_tof_pos_num(); - timing_pos_num<=proj_data_info_ptr->get_max_tof_pos_num(); - ++i, ++timing_pos_num) - { - timing_poss_sequence[i] = timing_pos_num; - } + timing_poss_sequence.resize(proj_data_info_ptr->get_num_tof_poss()); + + for (int i= 0, timing_pos_num = proj_data_info_ptr->get_min_tof_pos_num(); + timing_pos_num<=proj_data_info_ptr->get_max_tof_pos_num(); + ++i, ++timing_pos_num) + { + timing_poss_sequence[i] = timing_pos_num; } + } Viewgram From d8553d5676bc9d8ecefd624a8fc0af376f5ae626 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Thu, 23 Feb 2017 16:02:56 +0000 Subject: [PATCH 060/170] swapped has been moved to CListEvent --- .../CListEventCylindricalScannerWithDiscreteDetectors.h | 1 + src/include/stir/listmode/CListRecord.h | 8 ++++++++ src/include/stir/listmode/CListRecordROOT.h | 3 +-- src/utilities/create_projdata_template.cxx | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/include/stir/listmode/CListEventCylindricalScannerWithDiscreteDetectors.h b/src/include/stir/listmode/CListEventCylindricalScannerWithDiscreteDetectors.h index caf533641f..2337be4d87 100644 --- a/src/include/stir/listmode/CListEventCylindricalScannerWithDiscreteDetectors.h +++ b/src/include/stir/listmode/CListEventCylindricalScannerWithDiscreteDetectors.h @@ -78,6 +78,7 @@ class CListEventCylindricalScannerWithDiscreteDetectors : public CListEvent } shared_ptr scanner_sptr; + private: shared_ptr uncompressed_proj_data_info_sptr; diff --git a/src/include/stir/listmode/CListRecord.h b/src/include/stir/listmode/CListRecord.h index 2eeb9fb7b5..0497ff549f 100644 --- a/src/include/stir/listmode/CListRecord.h +++ b/src/include/stir/listmode/CListRecord.h @@ -109,11 +109,19 @@ class CListEvent void get_bin(Bin& bin, const ProjDataInfo&) const; + //! Returns true is the delta_time has been swapped. + bool + get_swapped() const + {return swapped;} + protected: //! The detection time difference, between the two photons. //! This will work for ROOT files, but not so sure about acquired data. double delta_time; + //! Indicates if the detectors' order has been swapped. + bool swapped; + }; /*-coincidence event*/ diff --git a/src/include/stir/listmode/CListRecordROOT.h b/src/include/stir/listmode/CListRecordROOT.h index 3cee3e0aaa..6c2b10177e 100644 --- a/src/include/stir/listmode/CListRecordROOT.h +++ b/src/include/stir/listmode/CListRecordROOT.h @@ -70,8 +70,7 @@ class CListEventROOT : public CListEventCylindricalScannerWithDiscreteDetectors int det1; //! Second detector, in order to detector tangestial index int det2; - //! Indicates if swap segments - bool swapped; + //! This is the number of detector we have to rotate in order to //! align GATE and STIR. int quarter_of_detectors; diff --git a/src/utilities/create_projdata_template.cxx b/src/utilities/create_projdata_template.cxx index bb85ad5575..877995452c 100644 --- a/src/utilities/create_projdata_template.cxx +++ b/src/utilities/create_projdata_template.cxx @@ -66,7 +66,7 @@ int main(int argc, char *argv[]) //if ( proj_data_info_sptr->get_num_tof_poss() >1) // shared_ptr proj_data_sptr(new ProjDataInterfile(exam_info_sptr, proj_data_info_sptr, output_file_name, std::ios::out, // ProjDataFromStream::Timing_Segment_View_AxialPos_TangPos)); - // else + //else shared_ptr proj_data_sptr(new ProjDataInterfile(exam_info_sptr, proj_data_info_sptr, output_file_name)); return EXIT_SUCCESS; From 44f317e4593e4afa3482e3bc170ef9b591b6cbdd Mon Sep 17 00:00:00 2001 From: Elise Date: Thu, 23 Feb 2017 17:13:03 +0000 Subject: [PATCH 061/170] Additional fix for HDF5 library before merge with UCL-STIR/tof_sino repo --- src/IO/IO_registries.cxx | 5 +++++ src/listmode_buildblock/CMakeLists.txt | 7 ++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/IO/IO_registries.cxx b/src/IO/IO_registries.cxx index 6cef3d8603..df02a03c1c 100644 --- a/src/IO/IO_registries.cxx +++ b/src/IO/IO_registries.cxx @@ -55,7 +55,10 @@ #endif #include "stir/IO/ECAT8_32bitListmodeInputFileFormat.h" +#ifdef HAVE_HDF5 #include "stir/IO/GESignaListmodeInputFileFormat.h" +#endif + //! Addition for ROOT support - Nikos Efthimiou #ifdef HAVE_CERN_ROOT #include "stir/IO/ROOTListmodeInputFileFormat.h" @@ -123,6 +126,8 @@ static RegisterInputFileFormat LMdu static RegisterInputFileFormat LMdummyECAT962(5); #endif static RegisterInputFileFormat LMdummyECAT8(6); +#ifdef HAVE_HDF5 static RegisterInputFileFormat LMdummyGESigna(7); +#endif END_NAMESPACE_STIR diff --git a/src/listmode_buildblock/CMakeLists.txt b/src/listmode_buildblock/CMakeLists.txt index 9e705fcee8..1f2afd6186 100644 --- a/src/listmode_buildblock/CMakeLists.txt +++ b/src/listmode_buildblock/CMakeLists.txt @@ -11,9 +11,14 @@ set(${dir_LIB_SOURCES} LmToProjDataBootstrap CListModeDataECAT8_32bit CListRecordECAT8_32bit +) + +if (HAVE_HDF5) +list(APPEND ${dir_LIB_SOURCES} CListModeDataGESigna -# CListRecordGESigna + # CListRecordGESigna ) +endif() if (HAVE_ECAT) list(APPEND ${dir_LIB_SOURCES} From 3a26c731252b1493d6bd448dbf840946ed2027d0 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Thu, 23 Feb 2017 17:20:23 +0000 Subject: [PATCH 062/170] Corrected num_events to store in lm_to_proj_data --- src/include/stir/listmode/LmToProjData.h | 2 +- src/listmode_buildblock/LmToProjData.cxx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/include/stir/listmode/LmToProjData.h b/src/include/stir/listmode/LmToProjData.h index d6e4b9f00e..2cd25a678e 100644 --- a/src/include/stir/listmode/LmToProjData.h +++ b/src/include/stir/listmode/LmToProjData.h @@ -242,7 +242,7 @@ class LmToProjData : public ParsingObject bool use_tof; int num_segments_in_memory; // TODO make long (or even unsigned long) but can't do this yet because we can't parse longs yet - unsigned long int num_events_to_store; + int num_events_to_store; int max_segment_num_to_process; //! Toggle readable output on stdout or actual projdata diff --git a/src/listmode_buildblock/LmToProjData.cxx b/src/listmode_buildblock/LmToProjData.cxx index 13ac7c7755..a062a9e89f 100644 --- a/src/listmode_buildblock/LmToProjData.cxx +++ b/src/listmode_buildblock/LmToProjData.cxx @@ -609,7 +609,7 @@ actual_process_data_without_tof() // ('allowed' independent on the fact of we have its segment in memory or not) // When do_time_frame=true, the number of events is irrelevant, so we // just set more_events to 1, and never change it - unsigned long int more_events = + long int more_events = do_time_frame? 1 : num_events_to_store; if (start_segment_index != proj_data_ptr->get_min_segment_num()) From d5f9e50f1baf3d64237f19a5c5c7b10256ced220 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Thu, 23 Feb 2017 17:56:59 +0000 Subject: [PATCH 063/170] Removed get_rof_mash_factor() as trigger, leftovers --- src/IO/interfile.cxx | 2 +- src/listmode_buildblock/LmToProjData.cxx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/IO/interfile.cxx b/src/IO/interfile.cxx index 4666f23bf8..633e11e0f0 100644 --- a/src/IO/interfile.cxx +++ b/src/IO/interfile.cxx @@ -848,7 +848,7 @@ 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_ptr()->get_tof_mash_factor()>1 ? + pdfs.get_proj_data_info_ptr()->get_num_tof_poss()>1 ? output_header << "number of dimensions := 5\n" : output_header << "number of dimensions := 4\n"; diff --git a/src/listmode_buildblock/LmToProjData.cxx b/src/listmode_buildblock/LmToProjData.cxx index a062a9e89f..0dd9a0a262 100644 --- a/src/listmode_buildblock/LmToProjData.cxx +++ b/src/listmode_buildblock/LmToProjData.cxx @@ -1113,7 +1113,7 @@ construct_proj_data(shared_ptr& output, shared_ptr proj_data_sptr; #ifdef USE_SegmentByView // don't need output stream in this case - if (proj_data_info_ptr->get_tof_mash_factor() == 1) + if (proj_data_info_ptr->get_num_tof_poss() == 1) proj_data_sptr.reset(new ProjDataInterfile(exam_info_sptr, proj_data_info_ptr, output_filename, ios::out, ProjDataFromStream::Segment_View_AxialPos_TangPos, From 502743ac9183b0d3fab3c06fe7ef670f21b87950 Mon Sep 17 00:00:00 2001 From: Elise Date: Fri, 3 Mar 2017 10:55:01 +0000 Subject: [PATCH 064/170] Fix to read template header in boundary conditions --- src/buildblock/ProjDataInfo.cxx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/buildblock/ProjDataInfo.cxx b/src/buildblock/ProjDataInfo.cxx index 5ea9cd50bd..cf2f2e0b22 100644 --- a/src/buildblock/ProjDataInfo.cxx +++ b/src/buildblock/ProjDataInfo.cxx @@ -226,17 +226,15 @@ ProjDataInfo::set_tof_mash_factor(const int new_num) % tof_bin_boundaries_ps[i].low_lim % tof_bin_boundaries_ps[i].high_lim % get_sampling_in_k(bin)); } } - else if (new_num == 0) // Case new_num = 0, will produce non-TOF data for a TOF compatible scanner. + 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; - - // Should I initialise here and the boundaries? + // we assume TOF mashing factor = 0 means non-TOF and the projecter won't use any boundary conditions } - else - error("Not TOF compatible scanner template. Abort."); } @@ -261,6 +259,7 @@ ProjDataInfo::ProjDataInfo(const shared_ptr& scanner_ptr_v, max_tof_pos_num = 0; tof_increament_in_mm = 0.f; tof_mash_factor = 0; + num_tof_bins = 1; } // TOF version. From f6a19ff57f6ea101f894fbc4029c6e59a277509e Mon Sep 17 00:00:00 2001 From: Elise Date: Wed, 22 Mar 2017 13:04:10 +0000 Subject: [PATCH 065/170] timing position index added to Segment + forward/back projection for TOF sinograms + TOF recontructions --- src/buildblock/ML_norm.cxx | 123 +++--- src/buildblock/ProjData.cxx | 78 ++-- src/buildblock/ProjDataFromStream.cxx | 77 ++-- src/buildblock/ProjDataGEAdvance.cxx | 4 +- src/buildblock/ProjDataInMemory.cxx | 31 +- src/buildblock/ProjDataInfo.cxx | 35 +- src/buildblock/RelatedViewgrams.cxx | 10 + src/buildblock/Scanner.cxx | 30 +- src/buildblock/Segment.cxx | 10 + src/buildblock/SegmentBySinogram.cxx | 16 +- src/buildblock/SegmentByView.cxx | 14 +- src/buildblock/Sinogram.cxx | 10 + src/buildblock/Viewgram.cxx | 10 + src/buildblock/interpolate_projdata.cxx | 3 +- src/include/stir/DynamicProjData.h | 42 +- .../IO/InputStreamWithRecordsFromHDF5.inl | 6 +- src/include/stir/ProjData.h | 94 +++-- src/include/stir/ProjDataFromStream.h | 6 +- src/include/stir/ProjDataGEAdvance.h | 4 +- src/include/stir/ProjDataInfo.h | 16 +- src/include/stir/ProjDataInfo.inl | 21 + src/include/stir/RelatedViewgrams.h | 3 + src/include/stir/RelatedViewgrams.inl | 8 + src/include/stir/Scanner.h | 2 +- src/include/stir/Segment.h | 5 +- src/include/stir/Segment.inl | 9 +- src/include/stir/SegmentBySinogram.h | 10 +- src/include/stir/SegmentBySinogram.inl | 3 +- src/include/stir/SegmentByView.h | 6 +- src/include/stir/SegmentByView.inl | 3 +- src/include/stir/Sinogram.h | 7 +- src/include/stir/Sinogram.inl | 17 +- src/include/stir/ViewSegmentNumbers.h | 4 +- src/include/stir/ViewSegmentNumbers.inl | 2 - src/include/stir/Viewgram.h | 9 +- src/include/stir/Viewgram.inl | 16 +- src/include/stir/listmode/CListRecord.h | 14 + .../stir/listmode/CListRecordGESigna.h | 3 +- .../recon_buildblock/BackProjectorByBin.h | 15 +- .../BackProjectorByBinUsingInterpolation.h | 3 + .../BackProjectorByBinUsingProjMatrixByBin.h | 11 +- ...ProjectorByBinUsingSquareProjMatrixByBin.h | 3 + .../recon_buildblock/DataSymmetriesForBins.h | 3 +- .../recon_buildblock/ForwardProjectorByBin.h | 28 ++ ...orwardProjectorByBinUsingProjMatrixByBin.h | 3 + .../ForwardProjectorByBinUsingRayTracing.h | 3 + ...orMeanAndListModeDataWithProjMatrixByBin.h | 13 +- .../PostsmoothingBackProjectorByBin.h | 9 + .../PresmoothingForwardProjectorByBin.h | 8 +- .../stir/recon_buildblock/ProjMatrixByBin.inl | 14 + .../recon_buildblock/ProjectorByBinPair.h | 11 +- .../TrivialDataSymmetriesForBins.h | 3 +- src/listmode_buildblock/LmToProjData.cxx | 39 +- src/listmode_utilities/list_lm_events.cxx | 1 + src/local/utilities/fillwithotherprojdata.cxx | 12 +- src/local/utilities/inverse_proj_data.cxx | 101 +++-- src/recon_buildblock/BackProjectorByBin.cxx | 15 +- .../BackProjectorByBinUsingInterpolation.cxx | 8 +- ...BackProjectorByBinUsingProjMatrixByBin.cxx | 35 +- ...ojectorByBinUsingSquareProjMatrixByBin.cxx | 7 + src/recon_buildblock/BinNormalisation.cxx | 55 ++- .../DataSymmetriesForBins.cxx | 10 +- .../ForwardProjectorByBin.cxx | 50 ++- ...wardProjectorByBinUsingProjMatrixByBin.cxx | 47 ++- .../ForwardProjectorByBinUsingRayTracing.cxx | 25 +- ...MeanAndListModeDataWithProjMatrixByBin.cxx | 376 +++++++++++------- .../PostsmoothingBackProjectorByBin.cxx | 39 +- .../PresmoothingForwardProjectorByBin.cxx | 29 +- src/recon_buildblock/ProjMatrixByBin.cxx | 3 + src/recon_buildblock/ProjectorByBinPair.cxx | 175 ++++++++ .../TrivialDataSymmetriesForBins.cxx | 7 +- ...ihoodWithLinearModelForMeanAndProjData.cxx | 81 ++-- src/test/test_proj_data_in_memory.cxx | 139 +++++++ src/utilities/forward_project.cxx | 2 +- src/utilities/poisson_noise.cxx | 45 ++- src/utilities/stir_math.cxx | 46 ++- 76 files changed, 1597 insertions(+), 638 deletions(-) diff --git a/src/buildblock/ML_norm.cxx b/src/buildblock/ML_norm.cxx index 873eacfdd1..a4290eb521 100644 --- a/src/buildblock/ML_norm.cxx +++ b/src/buildblock/ML_norm.cxx @@ -768,25 +768,28 @@ void make_fan_data(FanProjData& fan_data, 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.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()) - { - int ra = 0, a = 0; - int rb = 0, b = 0; - - proj_data_info_ptr->get_det_pair_for_bin(a, ra, b, rb, bin); - - fan_data(ra, a, rb, b) = - fan_data(rb, b, ra, a) = - (*segment_ptr)[bin.axial_pos_num()][bin.view_num()][bin.tangential_pos_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.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; + + proj_data_info_ptr->get_det_pair_for_bin(a, ra, b, rb, bin); + + fan_data(ra, a, rb, b) = + fan_data(rb, b, ra, a) = + (*segment_ptr)[bin.axial_pos_num()][bin.view_num()][bin.tangential_pos_num()]; } + } } } @@ -811,25 +814,28 @@ void set_fan_data(ProjData& proj_data, 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_empty_segment_by_sinogram(bin.segment_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()) - { - int ra = 0, a = 0; - int rb = 0, b = 0; - - proj_data_info_ptr->get_det_pair_for_bin(a, ra, b, rb, bin); - - (*segment_ptr)[bin.axial_pos_num()][bin.view_num()][bin.tangential_pos_num()] = - fan_data(ra, a, rb, b); - } - proj_data.set_segment(*segment_ptr); + 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_empty_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.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; + + proj_data_info_ptr->get_det_pair_for_bin(a, ra, b, rb, bin); + + (*segment_ptr)[bin.axial_pos_num()][bin.view_num()][bin.tangential_pos_num()] = + fan_data(ra, a, rb, b); + } + proj_data.set_segment(*segment_ptr); + } } } @@ -940,26 +946,29 @@ void make_fan_sum_data(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.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()) - { - int ra = 0, a = 0; - int rb = 0, b = 0; - - proj_data_info_ptr->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; - } + 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.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; + + proj_data_info_ptr->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; + } + } } } diff --git a/src/buildblock/ProjData.cxx b/src/buildblock/ProjData.cxx index c5b1e77e16..efa79a7c1c 100644 --- a/src/buildblock/ProjData.cxx +++ b/src/buildblock/ProjData.cxx @@ -222,36 +222,40 @@ read_from_file(const string& filename, 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_ptr->get_empty_viewgram(view_num, segment_num, make_num_tangential_poss_odd); + proj_data_info_ptr->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_ptr->get_empty_sinogram(ax_pos_num, segment_num, make_num_tangential_poss_odd); + proj_data_info_ptr->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_ptr->get_empty_segment_by_sinogram(segment_num, make_num_tangential_poss_odd); + proj_data_info_ptr->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_ptr->get_empty_segment_by_view(segment_num, make_num_tangential_poss_odd); + proj_data_info_ptr->get_empty_segment_by_view(segment_num, make_num_tangential_poss_odd, timing_pos); } @@ -259,23 +263,25 @@ RelatedViewgrams ProjData::get_empty_related_viewgrams(const ViewSegmentNumbers& view_segmnet_num, //const int view_num, const int segment_num, const shared_ptr& symmetries_used, - const bool make_num_tangential_poss_odd) const + const bool make_num_tangential_poss_odd, + const int timing_pos) const { return - proj_data_info_ptr->get_empty_related_viewgrams(view_segmnet_num, symmetries_used, make_num_tangential_poss_odd); + proj_data_info_ptr->get_empty_related_viewgrams(view_segmnet_num, symmetries_used, make_num_tangential_poss_odd, timing_pos); } RelatedViewgrams -ProjData::get_related_viewgrams(const ViewSegmentNumbers& view_segmnet_num, +ProjData::get_related_viewgrams(const ViewSegmentNumbers& view_segment_num, //const int view_num, const int segment_num, const shared_ptr& symmetries_used, - const bool make_num_bins_odd) const + const bool make_num_bins_odd, + const int timing_pos) const { vector pairs; symmetries_used->get_related_view_segment_numbers( pairs, - ViewSegmentNumbers(view_segmnet_num.view_num(),view_segmnet_num.segment_num()) + ViewSegmentNumbers(view_segment_num.view_num(),view_segment_num.segment_num()) ); vector > viewgrams; @@ -285,7 +291,7 @@ ProjData::get_related_viewgrams(const ViewSegmentNumbers& view_segmnet_num, { // TODO optimise to get shared proj_data_info_ptr viewgrams.push_back(get_viewgram(pairs[i].view_num(), - pairs[i].segment_num(), make_num_bins_odd)); + pairs[i].segment_num(), make_num_bins_odd,timing_pos)); } return RelatedViewgrams(viewgrams, symmetries_used); @@ -332,35 +338,32 @@ ProjData::set_related_viewgrams( const RelatedViewgrams& viewgrams) } #endif -SegmentBySinogram ProjData::get_segment_by_sinogram(const int segment_num, const int timing_num) const +SegmentBySinogram ProjData::get_segment_by_sinogram(const int segment_num, const int timing_pos) const { SegmentBySinogram segment = - proj_data_info_ptr->get_empty_segment_by_sinogram(segment_num,false); + proj_data_info_ptr->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, timing_num)); - + 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 int timing_num) const +SegmentByView ProjData::get_segment_by_view(const int segment_num, const int timing_pos) const { SegmentByView segment = - proj_data_info_ptr->get_empty_segment_by_view(segment_num,false); + proj_data_info_ptr->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, timing_num)); - + segment.set_viewgram(get_viewgram(view_num, segment_num, false, timing_pos)); return segment; } Succeeded -ProjData::set_segment(const SegmentBySinogram& segment, - const int& timing_pos) +ProjData::set_segment(const SegmentBySinogram& segment) { for (int view_num = get_min_view_num(); view_num <= get_max_view_num(); ++view_num) { - if(set_viewgram(segment.get_viewgram(view_num), timing_pos) + if(set_viewgram(segment.get_viewgram(view_num)) == Succeeded::no) return Succeeded::no; } @@ -368,12 +371,11 @@ ProjData::set_segment(const SegmentBySinogram& segment, } Succeeded -ProjData::set_segment(const SegmentByView& segment, - const int& timing_pos) +ProjData::set_segment(const SegmentByView& segment) { for (int view_num = get_min_view_num(); view_num <= get_max_view_num(); ++view_num) { - if(set_viewgram(segment.get_viewgram(view_num), timing_pos) + if(set_viewgram(segment.get_viewgram(view_num)) == Succeeded::no) return Succeeded::no; } @@ -386,10 +388,13 @@ ProjData::fill(const float value) { 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)); - segment.fill(value); - if(this->set_segment(segment) == 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) + { + 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"); + } } } @@ -404,9 +409,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"); + } } } diff --git a/src/buildblock/ProjDataFromStream.cxx b/src/buildblock/ProjDataFromStream.cxx index 543b79338d..eda400bf18 100644 --- a/src/buildblock/ProjDataFromStream.cxx +++ b/src/buildblock/ProjDataFromStream.cxx @@ -188,10 +188,10 @@ ProjDataFromStream::get_viewgram(const int view_num, const int segment_num, error("ProjDataFromStream::get_viewgram: error after seekg\n"); } - Viewgram viewgram(proj_data_info_ptr, view_num, segment_num); + Viewgram viewgram(proj_data_info_ptr, view_num, segment_num, timing_pos); float scale = float(1); - if (get_storage_order() == Segment_AxialPos_View_TangPos) + if (get_storage_order() == Segment_AxialPos_View_TangPos) //|| get_storage_order() == Timing_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++) { @@ -208,7 +208,7 @@ ProjDataFromStream::get_viewgram(const int view_num, const int segment_num, } - 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(read_data(*sino_stream, viewgram, on_disk_data_type, scale, on_disk_byte_order) == Succeeded::no) @@ -373,7 +373,7 @@ ProjDataFromStream::get_offsets(const int view_num, const int segment_num, } Succeeded -ProjDataFromStream::set_viewgram(const Viewgram& v, const int &timing_pos) +ProjDataFromStream::set_viewgram(const Viewgram& v) { if (sino_stream == 0) { @@ -421,6 +421,7 @@ ProjDataFromStream::set_viewgram(const Viewgram& v, const int &timing_pos } int segment_num = v.get_segment_num(); int view_num = v.get_view_num(); + int timing_pos = v.get_timing_pos_num(); vector offsets = get_offsets(view_num,segment_num, timing_pos); @@ -747,7 +748,7 @@ ProjDataFromStream::get_sinogram(const int ax_pos_num, const int segment_num, error("ProjDataFromStream::get_sinogram: error after seekg\n"); } - Sinogram sinogram(proj_data_info_ptr, ax_pos_num, segment_num); + Sinogram sinogram(proj_data_info_ptr, ax_pos_num, segment_num, timing_pos); float scale = float(1); if (get_storage_order() == Segment_AxialPos_View_TangPos) @@ -760,7 +761,7 @@ ProjDataFromStream::get_sinogram(const int ax_pos_num, const int segment_num, } - 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) { for (int view = get_min_view_num(); view <= get_max_view_num(); view++) { @@ -792,7 +793,7 @@ ProjDataFromStream::get_sinogram(const int ax_pos_num, const int segment_num, } Succeeded -ProjDataFromStream::set_sinogram(const Sinogram& s, const int &timing_pos) +ProjDataFromStream::set_sinogram(const Sinogram& s) { if (sino_stream == 0) { @@ -825,6 +826,7 @@ ProjDataFromStream::set_sinogram(const Sinogram& s, const int &timing_pos } int segment_num = s.get_segment_num(); int ax_pos_num = s.get_axial_pos_num(); + int timing_pos = s.get_timing_pos_num(); vector offsets = get_offsets_sino(ax_pos_num,segment_num, timing_pos); @@ -859,7 +861,7 @@ ProjDataFromStream::set_sinogram(const Sinogram& s, const int &timing_pos return Succeeded::yes; } - 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) { for (int view = get_min_view_num();view <= get_max_view_num(); view++) { @@ -956,6 +958,9 @@ ProjDataFromStream::get_segment_by_sinogram(const int segment_num, const int tim } streamoff segment_offset = get_offset_segment(segment_num); + // Go to the right timing full 3D sinogram + segment_offset += get_offset_timing(timing_num) ; + sino_stream->seekg(segment_offset, ios::beg); if (! *sino_stream) { @@ -964,7 +969,7 @@ ProjDataFromStream::get_segment_by_sinogram(const int segment_num, const int tim if (get_storage_order() == Segment_AxialPos_View_TangPos) { - SegmentBySinogram segment(proj_data_info_ptr,segment_num); + SegmentBySinogram segment(proj_data_info_ptr,segment_num, timing_num); { float scale = float(1); if(read_data(*sino_stream, segment, on_disk_data_type, scale, on_disk_byte_order) @@ -982,7 +987,7 @@ ProjDataFromStream::get_segment_by_sinogram(const int segment_num, const int tim 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)); } @@ -1001,10 +1006,12 @@ ProjDataFromStream::get_segment_by_view(const int segment_num, const int timing_ 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) { streamoff segment_offset = get_offset_segment(segment_num); + // Go to the right timing full 3D sinogram + segment_offset += get_offset_timing(timing_pos) ; sino_stream->seekg(segment_offset, ios::beg); if (! *sino_stream) @@ -1012,7 +1019,7 @@ ProjDataFromStream::get_segment_by_view(const int segment_num, const int timing_ error("ProjDataFromStream::get_segment_by_sinogram: error after seekg\n"); } - SegmentByView segment(proj_data_info_ptr,segment_num); + SegmentByView segment(proj_data_info_ptr,segment_num, timing_pos); { float scale = float(1.f); @@ -1027,39 +1034,9 @@ ProjDataFromStream::get_segment_by_view(const int segment_num, const int timing_ return segment; } - else if (get_storage_order() == Timing_Segment_View_AxialPos_TangPos) - { - //Get the right segment offset - streamoff segment_offset = get_offset_segment(segment_num); - // Go to the right timing full 3D sinogram - segment_offset += get_offset_timing(timing_pos) ; - - sino_stream->seekg(segment_offset, ios::beg); - - if (! *sino_stream) - { - error("ProjDataFromStream::get_segment_by_sinogram: error after seekg\n"); - } - - SegmentByView segment(proj_data_info_ptr,segment_num); - - { - float scale = float(1.f); - if(read_data(*sino_stream, segment, on_disk_data_type, scale, on_disk_byte_order) - == Succeeded::no) - error("ProjDataFromStream: error reading data\n"); - if(scale != 1) - error("ProjDataFromStream: error reading data: scale factor returned by read_data should be 1\n"); - } - - segment *= scale_factor; - - return segment; - - } 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 @@ -1087,6 +1064,8 @@ ProjDataFromStream::set_segment(const SegmentBySinogram& segmentbysinogra int segment_num = segmentbysinogram_v.get_segment_num(); streamoff segment_offset = get_offset_segment(segment_num); + // Go to the right timing full 3D sinogram + segment_offset += get_offset_timing(segmentbysinogram_v.get_timing_pos_num()) ; sino_stream->seekp(segment_offset,ios::beg); @@ -1110,9 +1089,9 @@ ProjDataFromStream::set_segment(const SegmentBySinogram& segmentbysinogra == Succeeded::no || scale != scale_factor) { - warning("ProjDataFromStream::set_segment: segment (%d)" + warning("ProjDataFromStream::set_segment: segment (%d) tof bin (%d)" " corrupted due to problems with writing or the scale factor \n", - segment_num); + segment_num, segmentbysinogram_v.get_timing_pos_num()); return Succeeded::no; } @@ -1156,6 +1135,8 @@ ProjDataFromStream::set_segment(const SegmentByView& segmentbyview_v) int segment_num = segmentbyview_v.get_segment_num(); streamoff segment_offset = get_offset_segment(segment_num); + // Go to the right timing full 3D sinogram + segment_offset += get_offset_timing(segmentbyview_v.get_timing_pos_num()) ; sino_stream->seekp(segment_offset,ios::beg); @@ -1165,7 +1146,7 @@ ProjDataFromStream::set_segment(const SegmentByView& segmentbyview_v) return Succeeded::no; } - if (get_storage_order() == Segment_View_AxialPos_TangPos) + 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) @@ -1179,9 +1160,9 @@ ProjDataFromStream::set_segment(const SegmentByView& segmentbyview_v) == Succeeded::no || scale != scale_factor) { - warning("ProjDataFromStream::set_segment: segment (%d)" + warning("ProjDataFromStream::set_segment: segment (%d) tof bin (%d)" " corrupted due to problems with writing or the scale factor \n", - segment_num); + segment_num, segmentbyview_v.get_timing_pos_num()); return Succeeded::no; } diff --git a/src/buildblock/ProjDataGEAdvance.cxx b/src/buildblock/ProjDataGEAdvance.cxx index 7bf3c23058..fae969e54d 100644 --- a/src/buildblock/ProjDataGEAdvance.cxx +++ b/src/buildblock/ProjDataGEAdvance.cxx @@ -304,7 +304,7 @@ get_viewgram(const int view_num, const int segment_num, } -Succeeded ProjDataGEAdvance::set_viewgram(const Viewgram& v, const int &timing_pos) +Succeeded ProjDataGEAdvance::set_viewgram(const Viewgram& v) { // TODO // but this is difficult: how to adjust the scale factors when writing only 1 viewgram ? @@ -319,7 +319,7 @@ Sinogram ProjDataGEAdvance::get_sinogram(const int ax_pos_num, const int error("ProjDataGEAdvance::get_sinogram not implemented yet\n"); return get_empty_sinogram(ax_pos_num, segment_num);} -Succeeded ProjDataGEAdvance::set_sinogram(const Sinogram& s, const int &timing_pos) +Succeeded ProjDataGEAdvance::set_sinogram(const Sinogram& s) { // TODO warning("ProjDataGEAdvance::set_sinogram not implemented yet\n"); diff --git a/src/buildblock/ProjDataInMemory.cxx b/src/buildblock/ProjDataInMemory.cxx index 358b7e3c2f..6d7b3e7d3b 100644 --- a/src/buildblock/ProjDataInMemory.cxx +++ b/src/buildblock/ProjDataInMemory.cxx @@ -84,10 +84,14 @@ ProjDataInMemory(shared_ptr const& exam_info_sptr, if (initialise_with_0) { - for (int segment_num = proj_data_info_ptr->get_min_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) + for (int segment_num = proj_data_info_ptr->get_min_segment_num(); segment_num <= proj_data_info_ptr->get_max_segment_num(); ++segment_num) - set_segment(proj_data_info_ptr->get_empty_segment_by_view(segment_num)); + + set_segment(proj_data_info_ptr->get_empty_segment_by_view(segment_num, false, timing_pos_num)); } } @@ -115,10 +119,14 @@ ProjDataInMemory(const ProjData& proj_data) // copy data // (note: cannot use fill(projdata) as that uses virtual functions, which won't work in a constructor - for (int segment_num = proj_data_info_ptr->get_min_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) + for (int segment_num = proj_data_info_ptr->get_min_segment_num(); segment_num <= proj_data_info_ptr->get_max_segment_num(); ++segment_num) - set_segment(proj_data.get_segment_by_view(segment_num)); + + set_segment(proj_data.get_segment_by_view(segment_num, timing_pos_num)); } size_t @@ -134,6 +142,7 @@ get_size_of_buffer() const num_sinograms * proj_data_info_ptr->get_num_views() * proj_data_info_ptr->get_num_tangential_poss() * + proj_data_info_ptr->get_num_tof_poss() * sizeof(float); } @@ -145,17 +154,9 @@ write_to_file(const string& output_filename) const ProjDataInterfile out_projdata(get_exam_info_sptr(), this->proj_data_info_ptr, output_filename, ios::out); - Succeeded success=Succeeded::yes; - for (int segment_num = proj_data_info_ptr->get_min_segment_num(); - segment_num <= proj_data_info_ptr->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); + // if no exception thrown, it succeeded + return Succeeded::yes; } diff --git a/src/buildblock/ProjDataInfo.cxx b/src/buildblock/ProjDataInfo.cxx index cf2f2e0b22..86d2e197b7 100644 --- a/src/buildblock/ProjDataInfo.cxx +++ b/src/buildblock/ProjDataInfo.cxx @@ -219,8 +219,8 @@ ProjDataInfo::set_tof_mash_factor(const int new_num) tof_bin_boundaries_mm[i].low_lim = cur_low; tof_bin_boundaries_mm[i].high_lim = cur_high; - tof_bin_boundaries_ps[i].low_lim = (tof_bin_boundaries_mm[i].low_lim * 3.33564095198f ) ; - tof_bin_boundaries_ps[i].high_lim = ( tof_bin_boundaries_mm[i].high_lim * 3.33564095198f); + tof_bin_boundaries_ps[i].low_lim = (tof_bin_boundaries_mm[i].low_lim / 0.299792458f ) ; + tof_bin_boundaries_ps[i].high_lim = ( tof_bin_boundaries_mm[i].high_lim / 0.299792458f); // I could imagine a better printing. info(boost::format("Tbin %1%: %2% - %3% mm (%4% - %5% ps) = %6%") %i % tof_bin_boundaries_mm[i].low_lim % tof_bin_boundaries_mm[i].high_lim % tof_bin_boundaries_ps[i].low_lim % tof_bin_boundaries_ps[i].high_lim % get_sampling_in_k(bin)); @@ -332,7 +332,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()); @@ -340,7 +341,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; } @@ -348,7 +349,8 @@ ProjDataInfo::get_empty_viewgram(const int view_num, 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()); @@ -356,14 +358,15 @@ 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; } 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()); @@ -374,7 +377,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; } @@ -382,7 +385,8 @@ ProjDataInfo::get_empty_segment_by_sinogram(const int segment_num, 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()); @@ -393,21 +397,22 @@ 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; } RelatedViewgrams -ProjDataInfo::get_empty_related_viewgrams(const ViewSegmentNumbers& view_segmnet_num, +ProjDataInfo::get_empty_related_viewgrams(const ViewSegmentNumbers& view_segment_num, //const int view_num, const int segment_num, 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 { vector pairs; symmetries_used->get_related_view_segment_numbers( pairs, - ViewSegmentNumbers(view_segmnet_num.view_num(),view_segmnet_num.segment_num()) + ViewSegmentNumbers(view_segment_num.view_num(),view_segment_num.segment_num()) ); vector > viewgrams; @@ -417,7 +422,9 @@ ProjDataInfo::get_empty_related_viewgrams(const ViewSegmentNumbers& view_segmnet { // TODO optimise to get shared proj_data_info_ptr viewgrams.push_back(get_empty_viewgram(pairs[i].view_num(), - pairs[i].segment_num(), make_num_tangential_poss_odd)); + pairs[i].segment_num(), + make_num_tangential_poss_odd, + timing_pos_num)); } return RelatedViewgrams(viewgrams, symmetries_used); diff --git a/src/buildblock/RelatedViewgrams.cxx b/src/buildblock/RelatedViewgrams.cxx index d633f46fbe..7abeadcec4 100644 --- a/src/buildblock/RelatedViewgrams.cxx +++ b/src/buildblock/RelatedViewgrams.cxx @@ -139,6 +139,16 @@ has_same_characteristics(self_type const& other, ); return false; } + if (this->get_basic_timing_pos_num() != + other.get_basic_timing_pos_num()) + { + explanation = + str(format("Differing basic timing position index: %1% vs %2%") + % this->get_basic_timing_pos_num() + % other.get_basic_timing_pos_num() + ); + return false; + } return true; } diff --git a/src/buildblock/Scanner.cxx b/src/buildblock/Scanner.cxx index 36d09a5d3c..f7a6b8c882 100644 --- a/src/buildblock/Scanner.cxx +++ b/src/buildblock/Scanner.cxx @@ -302,7 +302,10 @@ Scanner::Scanner(Type scanner_type) 24, 329, 293, 2 * 280, 886.2F/2.F, 8.4F, 6.54F, 2.397F, static_cast(-4.5490*_PI/180),//sign? - 4, 2, 6, 8, 1, 1, 1);// TODO not sure about sign of view_offset + 4, 2, 6, 8, 1, 1, 1, + (short int)(410), + (float)(10.0F), + (float)(400.0F) );// TODO not sure about sign of view_offset break; case DiscoveryRX: @@ -352,7 +355,10 @@ case PETMR_Signa: static_cast(-5.23*_PI/180),//sign? TODO value 5, 4, - 9, 4, 1, 1, 1); + 9, 4, 1, 1, 1, + (short int)(351), + (float)(89.0F/13.0F), //TODO + (float)(390.0F) ); break; case HZLR: @@ -428,6 +434,26 @@ case PETMR_Signa: break; + case Discovery690: + + set_params(Discovery690, string_list("GE Discovery 690", "Discovery 690"), + 24, + 381, + 331, // TODO + 2 * 288, + 405.1F, + 9.4F, + 6.54F, + 2.1306F, + static_cast(-5.021*_PI/180),//sign? TODO value + 4, + 2, + 6, 9, 1, 1, 1, + (short int)(55), + (float)(89.0F), + (float)(550.0F) ); + break; + case User_defined_scanner: // zlong, 08-04-2004, Userdefined support set_params(User_defined_scanner, string_list("Userdefined"), diff --git a/src/buildblock/Segment.cxx b/src/buildblock/Segment.cxx index cf4e385657..c227336b61 100644 --- a/src/buildblock/Segment.cxx +++ b/src/buildblock/Segment.cxx @@ -77,6 +77,16 @@ has_same_characteristics(self_type const& other, ); return false; } + if (this->get_timing_pos_num() != + other.get_timing_pos_num()) + { + explanation = + str(format("Differing timing position index: %1% vs %2%") + % this->get_timing_pos_num() + % other.get_timing_pos_num() + ); + return false; + } return true; } diff --git a/src/buildblock/SegmentBySinogram.cxx b/src/buildblock/SegmentBySinogram.cxx index 3653b69667..1edaa863cb 100644 --- a/src/buildblock/SegmentBySinogram.cxx +++ b/src/buildblock/SegmentBySinogram.cxx @@ -43,9 +43,10 @@ template SegmentBySinogram :: SegmentBySinogram(const Array<3,elemT>& v, const shared_ptr& pdi_ptr, - const int segment_num) + const int segment_num, + const int timing_pos_num) : - Segment(pdi_ptr, segment_num), + Segment(pdi_ptr, segment_num, timing_pos_num), Array<3,elemT>(v) { assert( get_min_view_num() == pdi_ptr->get_min_view_num()); @@ -59,9 +60,10 @@ SegmentBySinogram(const Array<3,elemT>& v, template SegmentBySinogram :: SegmentBySinogram(const shared_ptr& pdi_ptr, - const int segment_num) + const int segment_num, + const int timing_pos_num) : - Segment(pdi_ptr, segment_num), + Segment(pdi_ptr, segment_num, timing_pos_num), Array<3,elemT>(IndexRange3D(pdi_ptr->get_min_axial_pos_num(segment_num), pdi_ptr->get_max_axial_pos_num(segment_num), pdi_ptr->get_min_view_num(), @@ -74,7 +76,7 @@ template SegmentBySinogram:: SegmentBySinogram(const SegmentByView& s_v ) : Segment(s_v.get_proj_data_info_ptr()->create_shared_clone(), - s_v.get_segment_num()), + s_v.get_segment_num(), s_v.get_timing_pos_num()), Array<3,elemT> (IndexRange3D (s_v.get_min_axial_pos_num(), s_v.get_max_axial_pos_num(), s_v.get_min_view_num(), s_v.get_max_view_num(), s_v.get_min_tangential_pos_num(), s_v.get_max_tangential_pos_num())) @@ -107,7 +109,7 @@ SegmentBySinogram::get_viewgram(int view_num) const //KT 9/12 constructed a PETSinogram before... // CL&KT 15/12 added ring_difference stuff return Viewgram(pre_view, this->proj_data_info_ptr->create_shared_clone(), view_num, - this->get_segment_num()); + this->get_segment_num(), this->get_timing_pos_num()); } template @@ -118,8 +120,6 @@ SegmentBySinogram::set_viewgram(const Viewgram& viewgram) Array<3,elemT>::operator[](r)[viewgram.get_view_num()] = viewgram[r]; } - - /*! This makes sure that the new Array dimensions are the same as those in the ProjDataInfo member. diff --git a/src/buildblock/SegmentByView.cxx b/src/buildblock/SegmentByView.cxx index 4f3586e037..0cebf07305 100644 --- a/src/buildblock/SegmentByView.cxx +++ b/src/buildblock/SegmentByView.cxx @@ -41,9 +41,10 @@ template SegmentByView:: SegmentByView(const Array<3,elemT>& v, const shared_ptr& pdi_ptr, - const int segment_num) + const int segment_num, + const int timing_pos_num) : - Segment(pdi_ptr, segment_num), + Segment(pdi_ptr, segment_num, timing_pos_num), Array<3,elemT>(v) { assert( get_min_view_num() == pdi_ptr->get_min_view_num()); @@ -58,9 +59,10 @@ SegmentByView(const Array<3,elemT>& v, template SegmentByView:: SegmentByView(const shared_ptr& pdi_ptr, - const int segment_num) + const int segment_num, + const int timing_pos_num) : - Segment(pdi_ptr, segment_num), + Segment(pdi_ptr, segment_num, timing_pos_num), Array<3,elemT>(IndexRange3D(pdi_ptr->get_min_view_num(), pdi_ptr->get_max_view_num(), pdi_ptr->get_min_axial_pos_num(segment_num), @@ -72,7 +74,7 @@ SegmentByView(const shared_ptr& pdi_ptr, template SegmentByView::SegmentByView(const SegmentBySinogram& s_s) : Segment(s_s.get_proj_data_info_ptr()->create_shared_clone(), - s_s.get_segment_num()), + s_s.get_segment_num(),s_s.get_timing_pos_num()), Array<3,elemT> (IndexRange3D(s_s.get_min_view_num(),s_s.get_max_view_num(), s_s.get_min_axial_pos_num(),s_s.get_max_axial_pos_num(), @@ -105,7 +107,7 @@ SegmentByView::get_sinogram(int axial_pos_num) const pre_sino[v] = Array<3,elemT>::operator[](v)[axial_pos_num]; return Sinogram(pre_sino, this->proj_data_info_ptr, axial_pos_num, - this->get_segment_num()); + this->get_segment_num(), this->get_timing_pos_num()); } template diff --git a/src/buildblock/Sinogram.cxx b/src/buildblock/Sinogram.cxx index ef6c878c59..3c5b937c4a 100644 --- a/src/buildblock/Sinogram.cxx +++ b/src/buildblock/Sinogram.cxx @@ -81,6 +81,16 @@ has_same_characteristics(self_type const& other, ); return false; } + if (this->get_timing_pos_num() != + other.get_timing_pos_num()) + { + explanation = + str(format("Differing timing position index: %1% vs %2%") + % this->get_timing_pos_num() + % other.get_timing_pos_num() + ); + return false; + } return true; } diff --git a/src/buildblock/Viewgram.cxx b/src/buildblock/Viewgram.cxx index 6d0bc53050..84b7abf796 100644 --- a/src/buildblock/Viewgram.cxx +++ b/src/buildblock/Viewgram.cxx @@ -81,6 +81,16 @@ has_same_characteristics(self_type const& other, ); return false; } + if (this->get_timing_pos_num() != + other.get_timing_pos_num()) + { + explanation = + str(format("Differing timing position index: %1% vs %2%") + % this->get_timing_pos_num() + % other.get_timing_pos_num() + ); + return false; + } return true; } diff --git a/src/buildblock/interpolate_projdata.cxx b/src/buildblock/interpolate_projdata.cxx index 7a1fef3245..243485f7d0 100644 --- a/src/buildblock/interpolate_projdata.cxx +++ b/src/buildblock/interpolate_projdata.cxx @@ -154,7 +154,8 @@ namespace detail_interpolate_projdata const SegmentBySinogram& in_segment) { SegmentBySinogram out_segment = - non_interleaved_proj_data_info.get_empty_segment_by_sinogram(in_segment.get_segment_num()); + non_interleaved_proj_data_info.get_empty_segment_by_sinogram(in_segment.get_segment_num(), + in_segment.get_timing_pos_num()); make_non_interleaved_segment(out_segment, in_segment); return out_segment; diff --git a/src/include/stir/DynamicProjData.h b/src/include/stir/DynamicProjData.h index 3900160c6a..f7ec883242 100644 --- a/src/include/stir/DynamicProjData.h +++ b/src/include/stir/DynamicProjData.h @@ -98,14 +98,19 @@ class DynamicProjData : for (int segment_num = (this->_proj_datas[frame_num-1])->get_min_segment_num(); segment_num <= (this->_proj_datas[frame_num-1])->get_max_segment_num(); ++segment_num) { - SegmentByView segment_by_view - ((*(this->_proj_datas[frame_num-1])).get_segment_by_view(segment_num)); - segment_by_view *= cal_factor; - if ((*(this->_proj_datas[frame_num-1])).set_segment(segment_by_view) - ==Succeeded::no) - { - error("DynamicProjData:calibrate_frames failed because set_segment_by_view failed"); - } + for (int timing_pos_num = (this->_proj_datas[frame_num-1])->get_min_tof_pos_num(); + timing_pos_num <= (this->_proj_datas[frame_num-1])->get_max_tof_pos_num(); + ++timing_pos_num) + { + SegmentByView segment_by_view + ((*(this->_proj_datas[frame_num-1])).get_segment_by_view(segment_num,timing_pos_num)); + segment_by_view *= cal_factor; + if ((*(this->_proj_datas[frame_num-1])).set_segment(segment_by_view) + ==Succeeded::no) + { + error("DynamicProjData:calibrate_frames failed because set_segment_by_view failed"); + } + } } } @@ -119,14 +124,19 @@ class DynamicProjData : for (int segment_num = (this->_proj_datas[frame_num-1])->get_min_segment_num(); segment_num <= (this->_proj_datas[frame_num-1])->get_max_segment_num(); ++segment_num) { - SegmentByView segment_by_view = - (*(this->_proj_datas[frame_num-1])).get_segment_by_view(segment_num); - segment_by_view /= static_cast(this->get_time_frame_definitions().get_duration(frame_num)); - if ((*(this->_proj_datas[frame_num-1])).set_segment(segment_by_view) - ==Succeeded::no) - { - error("DynamicProjData:calibrate_frames failed because set_segment_by_view failed"); - } + for (int timing_pos_num = (this->_proj_datas[frame_num-1])->get_min_tof_pos_num(); + timing_pos_num <= (this->_proj_datas[frame_num-1])->get_max_tof_pos_num(); + ++timing_pos_num) + { + SegmentByView segment_by_view = + (*(this->_proj_datas[frame_num-1])).get_segment_by_view(segment_num,timing_pos_num); + segment_by_view /= static_cast(this->get_time_frame_definitions().get_duration(frame_num)); + if ((*(this->_proj_datas[frame_num-1])).set_segment(segment_by_view) + ==Succeeded::no) + { + error("DynamicProjData:calibrate_frames failed because set_segment_by_view failed"); + } + } } } diff --git a/src/include/stir/IO/InputStreamWithRecordsFromHDF5.inl b/src/include/stir/IO/InputStreamWithRecordsFromHDF5.inl index 27f07b54ed..0f9767efac 100644 --- a/src/include/stir/IO/InputStreamWithRecordsFromHDF5.inl +++ b/src/include/stir/IO/InputStreamWithRecordsFromHDF5.inl @@ -68,9 +68,9 @@ get_next_record(RecordT& record) const H5::DataSpace dataspace = dataset_sptr->getSpace(); int rank = dataspace.getSimpleExtentNdims(); - hsize_t dims_out[rank]; - dataspace.getSimpleExtentDims( dims_out, NULL); - uint64_t list_size = dims_out[0]; // should be equal to /HeaderData/ListHeader/sizeOfList + std::vector dims_out(rank); // VS needs a vector + dataspace.getSimpleExtentDims(&dims_out[0],NULL); + uint64_t list_size = dims_out[0]; // should be equal to /HeaderData/ListHeader/sizeOfList if (current_offset > (list_size - this->size_of_record_signature)) return Succeeded::no; diff --git a/src/include/stir/ProjData.h b/src/include/stir/ProjData.h index ac9db5f5d3..7e10f75cd3 100644 --- a/src/include/stir/ProjData.h +++ b/src/include/stir/ProjData.h @@ -148,66 +148,61 @@ class ProjData : public ExamData virtual Viewgram get_viewgram(const int view, const int segment_num, const bool make_num_tangential_poss_odd = false, - const int timing_pos = 0) const=0; + const int timing_pos = 0) const = 0; //! Set viewgram virtual Succeeded - set_viewgram(const Viewgram&, - const int& timing_pos = 0) = 0; + set_viewgram(const Viewgram&) = 0; //! Get sinogram virtual Sinogram get_sinogram(const int ax_pos_num, const int segment_num, const bool make_num_tangential_poss_odd = false, - const int timing_pos = 0) const=0; + const int timing_pos = 0) const = 0; //! Set sinogram virtual Succeeded - set_sinogram(const Sinogram&, - const int& timing_pos = 0) = 0; + set_sinogram(const Sinogram&) = 0; // //! Get Bin value //virtual float get_bin_value(const Bin& this_bin) const = 0; //! Get empty viewgram Viewgram get_empty_viewgram(const int view, const int segment_num, - const bool make_num_tangential_poss_odd = false) const; + const bool make_num_tangential_poss_odd = false, const int timing_pos = 0) const; //! Get empty_sinogram Sinogram get_empty_sinogram(const int ax_pos_num, const int segment_num, - const bool make_num_tangential_poss_odd = false) const; + const bool make_num_tangential_poss_odd = false, const int timing_pos = 0) const; //! Get empty segment sino SegmentByView get_empty_segment_by_view(const int segment_num, - const bool make_num_tangential_poss_odd = false) const; + const bool make_num_tangential_poss_odd = false, + const int timing_pos = 0) const; //! Get empty segment view SegmentBySinogram get_empty_segment_by_sinogram(const int segment_num, - const bool make_num_tangential_poss_odd = false) const; + const bool make_num_tangential_poss_odd = false, + const int timing_pos = 0) const; //! Get segment by sinogram virtual SegmentBySinogram - get_segment_by_sinogram(const int segment_num, - const int timing_num = 0) const; + get_segment_by_sinogram(const int segment_num, const int timing_pos = 0) const; //! Get segment by view virtual SegmentByView - get_segment_by_view(const int segment_num, - const int timing_num = 0) const; + get_segment_by_view(const int segment_num, const int timing_pos = 0) const; //! Set segment by sinogram - //! N.E. Extended to have timging positions. virtual Succeeded - set_segment(const SegmentBySinogram&, - const int& timing_pos = 0); + set_segment(const SegmentBySinogram&); //! Set segment by view - //! N.E. Extended to have timing positions. virtual Succeeded - set_segment(const SegmentByView&, - const int& timing_pos = 0); + set_segment(const SegmentByView&); //! Get related viewgrams virtual RelatedViewgrams get_related_viewgrams(const ViewSegmentNumbers&, const shared_ptr&, - const bool make_num_tangential_poss_odd = false) const; + const bool make_num_tangential_poss_odd = false, + const int timing_pos = 0) const; //! Set related viewgrams virtual Succeeded set_related_viewgrams(const RelatedViewgrams& viewgrams); // //! Get related bin values @@ -219,7 +214,8 @@ class ProjData : public ExamData get_empty_related_viewgrams(const ViewSegmentNumbers& view_segmnet_num, //const int view_num, const int segment_num, const shared_ptr& symmetries_ptr, - const bool make_num_tangential_poss_odd = false) const; + const bool make_num_tangential_poss_odd = false, + const int timing_pos = 0) const; //! set all bins to the same value @@ -243,22 +239,27 @@ class ProjData : public ExamData iterT init_pos = array_iter; for (int s=0; s<= this->get_max_segment_num(); ++s) { - SegmentBySinogram segment = this->get_empty_segment_by_sinogram(s); - // cannot use std::copy sadly as needs end-iterator for range - for (SegmentBySinogram::full_iterator seg_iter = segment.begin_all(); - seg_iter != segment.end_all(); - /*empty*/) - *seg_iter++ = *array_iter++; - this->set_segment(segment); - - if (s!=0) + for (int k=this->get_proj_data_info_ptr()->get_min_tof_pos_num(); + k<=this->get_proj_data_info_ptr()->get_max_tof_pos_num(); + ++k) { - segment = this->get_empty_segment_by_sinogram(-s); - for (SegmentBySinogram::full_iterator seg_iter = segment.begin_all(); - seg_iter != segment.end_all(); - /*empty*/) - *seg_iter++ = *array_iter++; - this->set_segment(segment); + SegmentBySinogram segment = this->get_empty_segment_by_sinogram(s, false, k); + // cannot use std::copy sadly as needs end-iterator for range + for (SegmentBySinogram::full_iterator seg_iter = segment.begin_all(); + seg_iter != segment.end_all(); + /*empty*/) + *seg_iter++ = *array_iter++; + this->set_segment(segment); + + if (s!=0) + { + segment = this->get_empty_segment_by_sinogram(-s,false,k); + for (SegmentBySinogram::full_iterator seg_iter = segment.begin_all(); + seg_iter != segment.end_all(); + /*empty*/) + *seg_iter++ = *array_iter++; + this->set_segment(segment); + } } } return std::distance(init_pos, array_iter); @@ -271,14 +272,19 @@ class ProjData : public ExamData iterT init_pos = array_iter; for (int s=0; s<= this->get_max_segment_num(); ++s) { - SegmentBySinogram segment= this->get_segment_by_sinogram(s); - std::copy(segment.begin_all_const(), segment.end_all_const(), array_iter); - std::advance(array_iter, segment.size_all()); - if (s!=0) + for (int k=this->get_proj_data_info_ptr()->get_min_tof_pos_num(); + k<=this->get_proj_data_info_ptr()->get_max_tof_pos_num(); + ++k) { - segment=this->get_segment_by_sinogram(-s); - std::copy(segment.begin_all_const(), segment.end_all_const(), array_iter); - std::advance(array_iter, segment.size_all()); + SegmentBySinogram segment= this->get_segment_by_sinogram(s,k); + std::copy(segment.begin_all_const(), segment.end_all_const(), array_iter); + std::advance(array_iter, segment.size_all()); + if (s!=0) + { + segment=this->get_segment_by_sinogram(-s,k); + std::copy(segment.begin_all_const(), segment.end_all_const(), array_iter); + std::advance(array_iter, segment.size_all()); + } } } return std::distance(init_pos, array_iter); diff --git a/src/include/stir/ProjDataFromStream.h b/src/include/stir/ProjDataFromStream.h index 40b160a71e..f8c5e4b932 100644 --- a/src/include/stir/ProjDataFromStream.h +++ b/src/include/stir/ProjDataFromStream.h @@ -128,16 +128,14 @@ class ProjDataFromStream : public ProjData Viewgram get_viewgram(const int view_num, const int segment_num, const bool make_num_tangential_poss_odd=false, const int timing_pos=0) const; - Succeeded set_viewgram(const Viewgram& v, - const int& timing_pos = 0); + Succeeded set_viewgram(const Viewgram& v); //! Get & set sinogram Sinogram get_sinogram(const int ax_pos_num, const int segment_num, const bool make_num_tangential_poss_odd=false, const int timing_pos=0) const; - Succeeded set_sinogram(const Sinogram& s, - const int& timing_pos = 0); + Succeeded set_sinogram(const Sinogram& s); //! Get all sinograms for the given segment SegmentBySinogram get_segment_by_sinogram(const int segment_num, diff --git a/src/include/stir/ProjDataGEAdvance.h b/src/include/stir/ProjDataGEAdvance.h index 50a289bdc0..a79a4e6e4d 100644 --- a/src/include/stir/ProjDataGEAdvance.h +++ b/src/include/stir/ProjDataGEAdvance.h @@ -68,12 +68,12 @@ class ProjDataGEAdvance : public ProjData //! Get & set viewgram Viewgram get_viewgram(const int view_num, const int segment_num, const bool make_num_tangential_poss_odd=false, const int timing_pos = 0) const; - Succeeded set_viewgram(const Viewgram& v, const int& timing_pos = 0); + Succeeded set_viewgram(const Viewgram& v); //! Get & set sinogram Sinogram get_sinogram(const int ax_pos_num, const int sergment_num, const bool make_num_tangential_poss_odd=false, const int timing_pos = 0) const; - Succeeded set_sinogram(const Sinogram& s, const int& timing_pos = 0); + Succeeded set_sinogram(const Sinogram& s); // float get_bin_value(const Bin& this_bin) const // { diff --git a/src/include/stir/ProjDataInfo.h b/src/include/stir/ProjDataInfo.h index 1f99ba631e..f7111eb507 100644 --- a/src/include/stir/ProjDataInfo.h +++ b/src/include/stir/ProjDataInfo.h @@ -221,6 +221,9 @@ class ProjDataInfo inline float get_coincidence_window_width() const; //@} + //! Determine if TOF data from tof_mash_factor and num_tof_bins + inline bool is_tof_data() const; + //| \name Functions that return geometrical info for a Bin //@{ //! Get tangent of the co-polar angle of the normal to the projection plane @@ -340,24 +343,27 @@ class ProjDataInfo //! Get empty viewgram Viewgram get_empty_viewgram(const int view_num, const int segment_num, - const bool make_num_tangential_poss_odd = false) const; + const bool make_num_tangential_poss_odd = false, const int timing_pos_num = 0) const; //! Get empty_sinogram Sinogram get_empty_sinogram(const int ax_pos_num, const int segment_num, - const bool make_num_tangential_poss_odd = false) const; + const bool make_num_tangential_poss_odd = false, const int timing_pos_num = 0) const; //! Get empty segment sino SegmentByView get_empty_segment_by_view(const int segment_num, - const bool make_num_tangential_poss_odd = false) const; + const bool make_num_tangential_poss_odd = false, + const int timing_pos_num = 0) const; //! Get empty segment view SegmentBySinogram get_empty_segment_by_sinogram(const int segment_num, - const bool make_num_tangential_poss_odd = false) const; + const bool make_num_tangential_poss_odd = false, + const int timing_pos_num = 0) const; //! Get empty related viewgrams, where the symmetries_ptr specifies the symmetries to use RelatedViewgrams get_empty_related_viewgrams(const ViewSegmentNumbers&, const shared_ptr&, - const bool make_num_tangential_poss_odd = false) const; + const bool make_num_tangential_poss_odd = false, + const int timing_pos_num = 0) const; //@} diff --git a/src/include/stir/ProjDataInfo.inl b/src/include/stir/ProjDataInfo.inl index 588d73b186..462b809aa1 100644 --- a/src/include/stir/ProjDataInfo.inl +++ b/src/include/stir/ProjDataInfo.inl @@ -70,6 +70,7 @@ ProjDataInfo::get_tof_bin(const double& delta) const delta < tof_bin_boundaries_ps[i].high_lim) return i; } + return 0; } int @@ -138,6 +139,26 @@ ProjDataInfo::get_coincidence_window_width() const return get_coincidence_window_in_pico_sec() * 0.299792458f; } +bool +ProjDataInfo::is_tof_data() const +{ + // First case: if tof_mash_factor == 0, scanner is not tof ready and no tof data + if (tof_mash_factor == 0) + { + if (num_tof_bins != 1) + { + error("Non-TOF data with inconsistent Time-of-Flight bin number - Aborted operation."); + } + return false; + } + // Second case: when tof_mash_factor is strictly positive, it means we have TOF data + else if (tof_mash_factor > 0) + { + return true; + } + return false; +} + float ProjDataInfo::get_costheta(const Bin& bin) const { diff --git a/src/include/stir/RelatedViewgrams.h b/src/include/stir/RelatedViewgrams.h index 099801625d..e28a583111 100644 --- a/src/include/stir/RelatedViewgrams.h +++ b/src/include/stir/RelatedViewgrams.h @@ -92,6 +92,9 @@ class RelatedViewgrams //! get 'basic' segment_num /*! see DataSymmetriesForViewSegmentNumbers for definition of 'basic' */ inline int get_basic_segment_num() const; + //! get 'basic' timing_pos_num + /*! see DataSymmetriesForViewSegmentNumbers for definition of 'basic' */ + inline int get_basic_timing_pos_num() const; //! get 'basic' view_segment_num /*! see DataSymmetriesForViewSegmentNumbers for definition of 'basic' */ inline ViewSegmentNumbers get_basic_view_segment_num() const; diff --git a/src/include/stir/RelatedViewgrams.inl b/src/include/stir/RelatedViewgrams.inl index f3a1eab689..50d2ca3faf 100644 --- a/src/include/stir/RelatedViewgrams.inl +++ b/src/include/stir/RelatedViewgrams.inl @@ -78,6 +78,14 @@ int RelatedViewgrams::get_basic_segment_num() const return viewgrams[0].get_segment_num(); } +template +int RelatedViewgrams::get_basic_timing_pos_num() const +{ + assert(viewgrams.size()>0); + check_state(); + return viewgrams[0].get_timing_pos_num(); +} + template ViewSegmentNumbers RelatedViewgrams:: get_basic_view_segment_num() const diff --git a/src/include/stir/Scanner.h b/src/include/stir/Scanner.h index be31c14c10..3095f894e8 100644 --- a/src/include/stir/Scanner.h +++ b/src/include/stir/Scanner.h @@ -114,7 +114,7 @@ class Scanner */ enum Type {E931, E951, E953, E921, E925, E961, E962, E966, E1080, test_scanner, Siemens_mMR, RPT,HiDAC, Advance, DiscoveryLS, DiscoveryST, DiscoverySTE, DiscoveryRX, Discovery600,PETMR_Signa, - HZLR, RATPET, PANDA, HYPERimage, nanoPET, HRRT, Allegro, GeminiTF, User_defined_scanner, + HZLR, RATPET, PANDA, HYPERimage, nanoPET, HRRT, Allegro, GeminiTF, Discovery690, User_defined_scanner, Unknown_scanner}; //! constructor that takes scanner type as an input argument diff --git a/src/include/stir/Segment.h b/src/include/stir/Segment.h index d63df3abb8..8c903e24a4 100644 --- a/src/include/stir/Segment.h +++ b/src/include/stir/Segment.h @@ -74,6 +74,8 @@ class Segment virtual StorageOrder get_storage_order() const = 0; //! Get the segment number inline int get_segment_num() const; + //! Get the timing position index + inline int get_timing_pos_num() const; virtual int get_min_axial_pos_num() const = 0; virtual int get_max_axial_pos_num() const = 0; virtual int get_min_view_num() const = 0; @@ -127,8 +129,9 @@ class Segment protected: shared_ptr proj_data_info_ptr; int segment_num; + int timing_pos_num; - inline Segment(const shared_ptr& proj_data_info_ptr_v,const int s_num); + inline Segment(const shared_ptr& proj_data_info_ptr_v,const int s_num, const int t_num = 0); }; END_NAMESPACE_STIR diff --git a/src/include/stir/Segment.inl b/src/include/stir/Segment.inl index f81d130d8c..314fb16d9c 100644 --- a/src/include/stir/Segment.inl +++ b/src/include/stir/Segment.inl @@ -33,10 +33,11 @@ START_NAMESPACE_STIR template Segment:: -Segment( const shared_ptr& proj_data_info_ptr_v,const int s_num) +Segment( const shared_ptr& proj_data_info_ptr_v,const int s_num, const int t_num) : proj_data_info_ptr(proj_data_info_ptr_v), - segment_num(s_num) + segment_num(s_num), + timing_pos_num(t_num) {} template @@ -44,6 +45,10 @@ int Segment:: get_segment_num() const { return segment_num; } +template +int +Segment:: get_timing_pos_num() const +{ return timing_pos_num; } template const ProjDataInfo* diff --git a/src/include/stir/SegmentBySinogram.h b/src/include/stir/SegmentBySinogram.h index b161c4e5e3..e800e35b40 100644 --- a/src/include/stir/SegmentBySinogram.h +++ b/src/include/stir/SegmentBySinogram.h @@ -66,11 +66,11 @@ class SegmentBySinogram : public Segment, public Array<3,elemT> //! Constructor that sets the data to a given 3d Array SegmentBySinogram(const Array<3,elemT>& v, const shared_ptr& proj_data_info_ptr_v, - const int segment_num); + const int segment_num, const int timing_pos_num = 0); //! Constructor that sets sizes via the ProjDataInfo object, initialising data to 0 SegmentBySinogram(const shared_ptr& proj_data_info_ptr_v, - const int segment_num); + const int segment_num, const int timing_pos_num = 0); //! Conversion from 1 storage order to the other @@ -81,7 +81,7 @@ class SegmentBySinogram : public Segment, public Array<3,elemT> inline int get_num_axial_poss() const; //! Get number of views inline int get_num_views() const; - //! Get number of tangetial positions + //! Get number of tangential positions inline int get_num_tangential_poss() const; //! Get minimum axial position number inline int get_min_axial_pos_num() const; @@ -96,13 +96,13 @@ class SegmentBySinogram : public Segment, public Array<3,elemT> //! Get maximum tangential position number inline int get_max_tangential_pos_num() const; //! Get sinogram - inline Sinogram get_sinogram(int axial_pos_num) const; + inline Sinogram get_sinogram(int axial_pos_num) const; //! Get viewgram Viewgram get_viewgram(int view_num) const; //! Set viewgram void set_viewgram(const Viewgram&); //! Set sinogram - inline void set_sinogram(Sinogram const &s, int axial_pos_num); + inline void set_sinogram(Sinogram const &s, int axial_pos_num); inline void set_sinogram(const Sinogram& s); //! Overloading Array::grow diff --git a/src/include/stir/SegmentBySinogram.inl b/src/include/stir/SegmentBySinogram.inl index a6d862714f..19a469b23c 100644 --- a/src/include/stir/SegmentBySinogram.inl +++ b/src/include/stir/SegmentBySinogram.inl @@ -109,7 +109,8 @@ SegmentBySinogram:: get_sinogram(int axial_pos_num) const { return Sinogram(Array<3,elemT>::operator[](axial_pos_num), Segment::proj_data_info_ptr, axial_pos_num, - Segment::get_segment_num()); } + Segment::get_segment_num(), + Segment::get_timing_pos_num()); } template void diff --git a/src/include/stir/SegmentByView.h b/src/include/stir/SegmentByView.h index 163690a9ff..d9de29042b 100644 --- a/src/include/stir/SegmentByView.h +++ b/src/include/stir/SegmentByView.h @@ -65,11 +65,13 @@ template class SegmentByView : public Segment, public Ar //! Constructor that sets the data to a given 3d Array SegmentByView(const Array<3,elemT>& v, const shared_ptr& proj_data_info_ptr, - const int segment_num); + const int segment_num, + const int timing_pos_num = 0); //! Constructor that sets sizes via the ProjDataInfo object, initialising data to 0 SegmentByView(const shared_ptr& proj_data_info_ptr, - const int segment_num); + const int segment_num, + const int timing_pos_num = 0); //! Conversion from 1 storage order to the other diff --git a/src/include/stir/SegmentByView.inl b/src/include/stir/SegmentByView.inl index 3ecbf127cd..067aa67bcc 100644 --- a/src/include/stir/SegmentByView.inl +++ b/src/include/stir/SegmentByView.inl @@ -107,7 +107,8 @@ SegmentByView::get_viewgram(int view_num) const { return Viewgram(Array<3,elemT>::operator[](view_num), this->proj_data_info_ptr->create_shared_clone(), view_num, - this->get_segment_num()); } + this->get_segment_num(), + this->get_timing_pos_num()); } template void diff --git a/src/include/stir/Sinogram.h b/src/include/stir/Sinogram.h index fc8cc9c584..52229de263 100644 --- a/src/include/stir/Sinogram.h +++ b/src/include/stir/Sinogram.h @@ -70,16 +70,18 @@ class Sinogram : public Array<2,elemT> public: //! Construct sinogram from proj_data_info pointer, ring and segment number. Data are set to 0. inline Sinogram(const shared_ptr& proj_data_info_ptr, - const int ax_pos_num, const int segment_num); + const int ax_pos_num, const int segment_num, const int timing_pos_num = 0); //! Construct sinogram with data set to the array. inline Sinogram(const Array<2,elemT>& p,const shared_ptr& proj_data_info_ptr, - const int ax_pos_num, const int segment_num); + const int ax_pos_num, const int segment_num, const int timing_pos_num = 0); //! Get segment number inline int get_segment_num() const; //! Get number of axial positions inline int get_axial_pos_num() const; + //! Get timing position index + inline int get_timing_pos_num() const; //! Get minimum view number inline int get_min_view_num() const; //! Get maximum view number @@ -145,6 +147,7 @@ class Sinogram : public Array<2,elemT> shared_ptr proj_data_info_ptr; int axial_pos_num; int segment_num; + int timing_pos_num; }; diff --git a/src/include/stir/Sinogram.inl b/src/include/stir/Sinogram.inl index 9ef42b77ef..ab5bc1496a 100644 --- a/src/include/stir/Sinogram.inl +++ b/src/include/stir/Sinogram.inl @@ -46,6 +46,11 @@ int Sinogram::get_axial_pos_num() const { return axial_pos_num; } +template +int +Sinogram::get_timing_pos_num() const +{ return timing_pos_num; } + template int Sinogram::get_min_view_num() const @@ -82,7 +87,7 @@ template Sinogram Sinogram::get_empty_copy(void) const { - Sinogram copy(proj_data_info_ptr, get_axial_pos_num(), get_segment_num()); + Sinogram copy(proj_data_info_ptr, get_axial_pos_num(), get_segment_num(), get_timing_pos_num()); return copy; } @@ -105,12 +110,13 @@ template Sinogram:: Sinogram(const Array<2,elemT>& p, const shared_ptr& pdi_ptr, - const int ax_pos_num, const int s_num) + const int ax_pos_num, const int s_num, const int t_num) : Array<2,elemT>(p), proj_data_info_ptr(pdi_ptr), axial_pos_num(ax_pos_num), - segment_num(s_num) + segment_num(s_num), + timing_pos_num(t_num) { assert(axial_pos_num <= proj_data_info_ptr->get_max_axial_pos_num(segment_num)); assert(axial_pos_num >= proj_data_info_ptr->get_min_axial_pos_num(segment_num)); @@ -127,7 +133,7 @@ Sinogram(const Array<2,elemT>& p, template Sinogram:: Sinogram(const shared_ptr& pdi_ptr, - const int ax_pos_num, const int s_num) + const int ax_pos_num, const int s_num, const int t_num) : Array<2,elemT>(IndexRange2D (pdi_ptr->get_min_view_num(), pdi_ptr->get_max_view_num(), @@ -135,7 +141,8 @@ Sinogram(const shared_ptr& pdi_ptr, pdi_ptr->get_max_tangential_pos_num())), proj_data_info_ptr(pdi_ptr), axial_pos_num(ax_pos_num), - segment_num(s_num) + segment_num(s_num), + timing_pos_num(t_num) { assert(axial_pos_num <= proj_data_info_ptr->get_max_axial_pos_num(segment_num)); assert(axial_pos_num >= proj_data_info_ptr->get_min_axial_pos_num(segment_num)); diff --git a/src/include/stir/ViewSegmentNumbers.h b/src/include/stir/ViewSegmentNumbers.h index 8fb271be85..b358759f4c 100644 --- a/src/include/stir/ViewSegmentNumbers.h +++ b/src/include/stir/ViewSegmentNumbers.h @@ -49,7 +49,7 @@ class ViewSegmentNumbers //! an empty constructor (sets everything to 0) inline ViewSegmentNumbers(); //! constructor taking view and segment number as arguments - inline ViewSegmentNumbers( const int view_num,const int segment_num); + inline ViewSegmentNumbers( const int view_num, const int segment_num); //! get segment number for const objects inline int segment_num() const; @@ -60,6 +60,8 @@ class ViewSegmentNumbers inline int& segment_num(); //! get reference to view number inline int& view_num(); + //! get reference to timing position index + inline int& timing_pos_num(); //! comparison operator, only useful for sorting diff --git a/src/include/stir/ViewSegmentNumbers.inl b/src/include/stir/ViewSegmentNumbers.inl index 198bd32999..c46969865c 100644 --- a/src/include/stir/ViewSegmentNumbers.inl +++ b/src/include/stir/ViewSegmentNumbers.inl @@ -48,11 +48,9 @@ ViewSegmentNumbers::view_num() const { return view;} - int& ViewSegmentNumbers::segment_num() { return segment;} - int& ViewSegmentNumbers::view_num() { return view;} diff --git a/src/include/stir/Viewgram.h b/src/include/stir/Viewgram.h index 23f8e1147c..c824ac25df 100644 --- a/src/include/stir/Viewgram.h +++ b/src/include/stir/Viewgram.h @@ -70,17 +70,19 @@ class Viewgram : public Array<2,elemT> public: //! Construct from proj_data_info pointer, view and segment number. Data are set to 0. inline Viewgram(const shared_ptr& proj_data_info_ptr, - const int v_num, const int s_num); + const int v_num, const int s_num, const int t_num = 0); //! Construct with data set to the array. inline Viewgram(const Array<2,elemT>& p,const shared_ptr& proj_data_info_ptr, - const int v_num, const int s_num); + const int v_num, const int s_num, const int t_num = 0); //! Get segment number inline int get_segment_num() const; //! Get number of views inline int get_view_num() const; + //! Get timing position index + inline int get_timing_pos_num() const; //! Get minimum number of axial positions inline int get_min_axial_pos_num() const; //! Get maximum number of axial positions @@ -143,7 +145,8 @@ class Viewgram : public Array<2,elemT> shared_ptr proj_data_info_ptr; int view_num; - int segment_num; + int segment_num; + int timing_pos_num; }; END_NAMESPACE_STIR diff --git a/src/include/stir/Viewgram.inl b/src/include/stir/Viewgram.inl index 5094ca3b36..610b0e9675 100644 --- a/src/include/stir/Viewgram.inl +++ b/src/include/stir/Viewgram.inl @@ -45,6 +45,11 @@ int Viewgram::get_view_num() const { return view_num; } +template +int +Viewgram::get_timing_pos_num() const +{ return timing_pos_num; } + template int Viewgram::get_min_axial_pos_num() const @@ -82,7 +87,7 @@ template Viewgram Viewgram::get_empty_copy(void) const { - Viewgram copy(proj_data_info_ptr, get_view_num(), get_segment_num()); + Viewgram copy(proj_data_info_ptr, get_view_num(), get_segment_num(), get_timing_pos_num()); return copy; } @@ -105,10 +110,10 @@ template Viewgram:: Viewgram(const Array<2,elemT>& p, const shared_ptr& pdi_ptr, - const int v_num, const int s_num) + const int v_num, const int s_num, const int t_num) : Array<2,elemT>(p), proj_data_info_ptr(pdi_ptr), - view_num(v_num), segment_num(s_num) + view_num(v_num), segment_num(s_num), timing_pos_num(t_num) { assert(view_num <= proj_data_info_ptr->get_max_view_num()); assert(view_num >= proj_data_info_ptr->get_min_view_num()); @@ -123,7 +128,7 @@ Viewgram(const Array<2,elemT>& p, template Viewgram:: Viewgram(const shared_ptr& pdi_ptr, - const int v_num, const int s_num) + const int v_num, const int s_num, const int t_num) : Array<2,elemT>(IndexRange2D (pdi_ptr->get_min_axial_pos_num(s_num), pdi_ptr->get_max_axial_pos_num(s_num), @@ -131,7 +136,8 @@ Viewgram(const shared_ptr& pdi_ptr, pdi_ptr->get_max_tangential_pos_num())), proj_data_info_ptr(pdi_ptr), view_num(v_num), - segment_num(s_num) + segment_num(s_num), + timing_pos_num(t_num) { assert(view_num <= proj_data_info_ptr->get_max_view_num()); assert(view_num >= proj_data_info_ptr->get_min_view_num()); diff --git a/src/include/stir/listmode/CListRecord.h b/src/include/stir/listmode/CListRecord.h index 0497ff549f..c46bb63c42 100644 --- a/src/include/stir/listmode/CListRecord.h +++ b/src/include/stir/listmode/CListRecord.h @@ -114,6 +114,7 @@ class CListEvent get_swapped() const {return swapped;} + double get_delta_time() const { return delta_time; } protected: //! The detection time difference, between the two photons. //! This will work for ROOT files, but not so sure about acquired data. @@ -154,6 +155,13 @@ class CListTime return set_time_in_millisecs(time_in_millisecs); } + //! Get the timing component of the bin. + virtual inline void get_bin(Bin&, const ProjDataInfo&) const + { + error("CListTime::get_bin() currently is implemented only " + "ROOT data. Abort."); + } + }; //! A class recording external input to the scanner (normally used for gating) @@ -203,6 +211,12 @@ class CListRecord virtual bool operator==(const CListRecord& e2) const = 0; bool operator!=(const CListRecord& e2) const { return !(*this == e2); } + + //! Used in TOF reconstruction to get both the geometric and the timing + //! component of the event + virtual void full_event(Bin&, const ProjDataInfo&) const + {error("CListRecord::full_event() is implemented only for records which " + "hold timing and spatial information.");} }; diff --git a/src/include/stir/listmode/CListRecordGESigna.h b/src/include/stir/listmode/CListRecordGESigna.h index e8e19f4153..f54cbc023b 100644 --- a/src/include/stir/listmode/CListRecordGESigna.h +++ b/src/include/stir/listmode/CListRecordGESigna.h @@ -322,8 +322,7 @@ dynamic_cast(&e2) != 0 && if (this->is_event()) { // set TOF info in ps - this->delta_time = this->event_data.get_tof_bin() * - this-> get_scanner_ptr()->get_size_of_timing_bin(); + this->delta_time = this->event_data.get_tof_bin() *this-> get_scanner_ptr()->get_size_of_timing_bin(); } diff --git a/src/include/stir/recon_buildblock/BackProjectorByBin.h b/src/include/stir/recon_buildblock/BackProjectorByBin.h index f7bcad25d2..ed5fa5c4d6 100644 --- a/src/include/stir/recon_buildblock/BackProjectorByBin.h +++ b/src/include/stir/recon_buildblock/BackProjectorByBin.h @@ -34,6 +34,8 @@ #include "stir/RegisteredObject.h" #include "stir/TimedObject.h" #include "stir/shared_ptr.h" +#include "stir/Bin.h" +#include "stir/recon_buildblock/ProjMatrixElemsForOneBin.h" START_NAMESPACE_STIR @@ -105,6 +107,9 @@ class BackProjectorByBin : const int min_axial_pos_num, const int max_axial_pos_num, const int min_tangential_pos_num, const int max_tangential_pos_num); + void back_project(DiscretisedDensity<3,float>&, + const Bin&); + protected: @@ -112,13 +117,21 @@ class BackProjectorByBin : const RelatedViewgrams&, const int min_axial_pos_num, const int max_axial_pos_num, const int min_tangential_pos_num, const int max_tangential_pos_num) = 0; + + virtual void actual_back_project(DiscretisedDensity<3,float>&, + const Bin&) = 0; + + //! True if TOF has been activated. + bool tof_enabled; + private: void do_segments(DiscretisedDensity<3,float>& image, const ProjData& proj_data_org, const int start_segment_num, const int end_segment_num, const int start_axial_pos_num, const int end_axial_pos_num, const int start_tang_pos_num,const int end_tang_pos_num, - const int start_view, const int end_view); + const int start_view, const int end_view, + const int start_timing_pos_num = 0, const int end_timing_pos_num = 0); }; diff --git a/src/include/stir/recon_buildblock/BackProjectorByBinUsingInterpolation.h b/src/include/stir/recon_buildblock/BackProjectorByBinUsingInterpolation.h index 7f01569856..63c82e45d5 100644 --- a/src/include/stir/recon_buildblock/BackProjectorByBinUsingInterpolation.h +++ b/src/include/stir/recon_buildblock/BackProjectorByBinUsingInterpolation.h @@ -228,6 +228,9 @@ struct ProjDataForIntBP const int min_axial_pos_num, const int max_axial_pos_num, const int min_tangential_pos_num, const int max_tangential_pos_num); + void actual_back_project(DiscretisedDensity<3,float>&, + const Bin&); + virtual void back_project_all_symmetries( VoxelsOnCartesianGrid& image, diff --git a/src/include/stir/recon_buildblock/BackProjectorByBinUsingProjMatrixByBin.h b/src/include/stir/recon_buildblock/BackProjectorByBinUsingProjMatrixByBin.h index f4958e1420..728ba92281 100644 --- a/src/include/stir/recon_buildblock/BackProjectorByBinUsingProjMatrixByBin.h +++ b/src/include/stir/recon_buildblock/BackProjectorByBinUsingProjMatrixByBin.h @@ -86,17 +86,26 @@ class BackProjectorByBinUsingProjMatrixByBin: shared_ptr & get_proj_matrix_sptr(){ return proj_matrix_ptr ;} - + + + BackProjectorByBin* get_original_back_projector() const; + + void enable_tof(ProjMatrixElemsForOneBin* ); protected: shared_ptr proj_matrix_ptr; + void actual_back_project(DiscretisedDensity<3,float>& image, + const Bin& bin); + private: virtual void set_defaults(); virtual void initialise_keymap(); virtual bool post_processing(); + ProjMatrixElemsForOneBin* tof_row; + }; diff --git a/src/include/stir/recon_buildblock/BackProjectorByBinUsingSquareProjMatrixByBin.h b/src/include/stir/recon_buildblock/BackProjectorByBinUsingSquareProjMatrixByBin.h index 487202e144..79a21fb766 100644 --- a/src/include/stir/recon_buildblock/BackProjectorByBinUsingSquareProjMatrixByBin.h +++ b/src/include/stir/recon_buildblock/BackProjectorByBinUsingSquareProjMatrixByBin.h @@ -79,6 +79,9 @@ class BackProjectorByBinUsingSquareProjMatrixByBin: shared_ptr proj_matrix_ptr; + void actual_back_project(DiscretisedDensity<3,float>& image, + const Bin& bin); + private: virtual void set_defaults(); virtual void initialise_keymap(); diff --git a/src/include/stir/recon_buildblock/DataSymmetriesForBins.h b/src/include/stir/recon_buildblock/DataSymmetriesForBins.h index e75fe52234..66d05083a5 100644 --- a/src/include/stir/recon_buildblock/DataSymmetriesForBins.h +++ b/src/include/stir/recon_buildblock/DataSymmetriesForBins.h @@ -115,7 +115,8 @@ class DataSymmetriesForBins : public DataSymmetriesForViewSegmentNumbers virtual void get_related_bins(std::vector&, const Bin& b, const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) const; + const int min_tangential_pos_num, const int max_tangential_pos_num, + const int min_timing_pos_num = 0, const int max_timing_pos_num = 0) const; //! fills in a vector with the axial and tangential position numbers related to this bin /*! range for axial_pos_num and tangential_pos_num is taken from the ProjDataInfo object diff --git a/src/include/stir/recon_buildblock/ForwardProjectorByBin.h b/src/include/stir/recon_buildblock/ForwardProjectorByBin.h index 1fc37ee1c4..0e0cfdbc71 100644 --- a/src/include/stir/recon_buildblock/ForwardProjectorByBin.h +++ b/src/include/stir/recon_buildblock/ForwardProjectorByBin.h @@ -36,6 +36,8 @@ #include "stir/TimedObject.h" #include "stir/VoxelsOnCartesianGrid.h" #include "stir/shared_ptr.h" +#include "stir/Bin.h" +#include "stir/recon_buildblock/ProjMatrixElemsForOneBin.h" START_NAMESPACE_STIR @@ -101,6 +103,21 @@ virtual void set_up( const int min_axial_pos_num, const int max_axial_pos_num, const int min_tangential_pos_num, const int max_tangential_pos_num); + //! Overloaded function mainly used in ListMode reconstruction. + void forward_project(Bin&, + const DiscretisedDensity<3,float>&); + + //! This is a really ungly way to sort the extra bit of information which are needed for + //! the TOF reconstruction. In this base class we are going to store a pointer the the current + //! ProjMatrixElements row and the two detection points. Which the are going to be accessed by the + //! child class in case that tof_enabled. + //! The common ProjMatrixElems row will provide a 'bridge' between forward and back projection as, + //! we'll be able to keep it and not calculate it again. + void set_tof_data(const CartesianCoordinate3D*, + const CartesianCoordinate3D*); + + ProjMatrixElemsForOneBin* get_tof_row() const; + virtual ~ForwardProjectorByBin(); protected: @@ -110,6 +127,17 @@ virtual void set_up( const int min_axial_pos_num, const int max_axial_pos_num, const int min_tangential_pos_num, const int max_tangential_pos_num) = 0; + //! This virtual function has to be implemented by the derived class. + virtual void actual_forward_project(Bin&, + const DiscretisedDensity<3,float>&) = 0; + + //! True if TOF has been activated. + bool tof_enabled; + + shared_ptr tof_probabilities; + const CartesianCoordinate3D* point1; + const CartesianCoordinate3D* point2; + }; END_NAMESPACE_STIR diff --git a/src/include/stir/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.h b/src/include/stir/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.h index c0743e306d..2dd75e7b7d 100644 --- a/src/include/stir/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.h +++ b/src/include/stir/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.h @@ -79,6 +79,7 @@ class ForwardProjectorByBinUsingProjMatrixByBin: const DataSymmetriesForViewSegmentNumbers * get_symmetries_used() const; + void enable_tof(const shared_ptr& _proj_data_info_sptr, const bool v); private: shared_ptr proj_matrix_ptr; @@ -89,6 +90,8 @@ class ForwardProjectorByBinUsingProjMatrixByBin: const int min_axial_pos_num, const int max_axial_pos_num, const int min_tangential_pos_num, const int max_tangential_pos_num); + void actual_forward_project(Bin&, const DiscretisedDensity<3,float>&); + virtual void set_defaults(); virtual void initialise_keymap(); virtual bool post_processing(); diff --git a/src/include/stir/recon_buildblock/ForwardProjectorByBinUsingRayTracing.h b/src/include/stir/recon_buildblock/ForwardProjectorByBinUsingRayTracing.h index ad723fad18..96af693885 100644 --- a/src/include/stir/recon_buildblock/ForwardProjectorByBinUsingRayTracing.h +++ b/src/include/stir/recon_buildblock/ForwardProjectorByBinUsingRayTracing.h @@ -113,6 +113,9 @@ class ForwardProjectorByBinUsingRayTracing : const int min_axial_pos_num, const int max_axial_pos_num, const int min_tangential_pos_num, const int max_tangential_pos_num); + void actual_forward_project(Bin&, + const DiscretisedDensity<3,float>&); + // KT 20/06/2001 changed type from 'const DataSymmetriesForViewSegmentNumbers *' shared_ptr symmetries_ptr; diff --git a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h index a596588603..5a224a7ae2 100644 --- a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h +++ b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h @@ -37,6 +37,10 @@ #include "stir/ProjDataInMemory.h" #include "stir/recon_buildblock/ProjectorByBinPair.h" #include "stir/ExamInfo.h" +#include "stir/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.h" +#include "stir/recon_buildblock/BackProjectorByBinUsingProjMatrixByBin.h" +#include "stir/recon_buildblock/ProjMatrixByBinUsingRayTracing.h" +#include "stir/recon_buildblock/ProjectorByBinPairUsingSeparateProjectors.h" START_NAMESPACE_STIR @@ -69,7 +73,7 @@ typedef RegisteredParsingObject(); @@ -101,7 +105,10 @@ typedef RegisteredParsingObject PM_sptr; @@ -126,7 +133,7 @@ typedef RegisteredParsingObject&); + + void init_filtered_density_image(DiscretisedDensity<3, float> &); + + BackProjectorByBin* get_original_back_projector_ptr() const; private: @@ -86,6 +91,10 @@ class PostsmoothingBackProjectorByBin : const int min_axial_pos_num, const int max_axial_pos_num, const int min_tangential_pos_num, const int max_tangential_pos_num); + void actual_back_project(DiscretisedDensity<3,float>& density, + const Bin& bin); + + shared_ptr > filtered_density_sptr; virtual void set_defaults(); diff --git a/src/include/stir/recon_buildblock/PresmoothingForwardProjectorByBin.h b/src/include/stir/recon_buildblock/PresmoothingForwardProjectorByBin.h index 7eb1caee2a..6c82636dc3 100644 --- a/src/include/stir/recon_buildblock/PresmoothingForwardProjectorByBin.h +++ b/src/include/stir/recon_buildblock/PresmoothingForwardProjectorByBin.h @@ -55,6 +55,8 @@ class PresmoothingForwardProjectorByBin : PresmoothingForwardProjectorByBin(); ~ PresmoothingForwardProjectorByBin(); + + void update_filtered_density_image(const DiscretisedDensity<3,float>&); //! Stores all necessary geometric info /*! Note that the density_info_ptr is not stored in this object. It's only used to get some info on sizes etc. @@ -75,18 +77,22 @@ class PresmoothingForwardProjectorByBin : // class has other behaviour). const DataSymmetriesForViewSegmentNumbers * get_symmetries_used() const; + ForwardProjectorByBin* get_original_forward_projector_ptr() const; private: shared_ptr original_forward_projector_ptr; shared_ptr > > image_processor_ptr; + shared_ptr > filtered_density_sptr; + void actual_forward_project(RelatedViewgrams&, const DiscretisedDensity<3,float>&, const int min_axial_pos_num, const int max_axial_pos_num, const int min_tangential_pos_num, const int max_tangential_pos_num); - + void actual_forward_project(Bin&, + const DiscretisedDensity<3,float>&); virtual void set_defaults(); virtual void initialise_keymap(); diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl index d691e2e4df..72e2ced515 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl +++ b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl @@ -61,6 +61,19 @@ get_proj_matrix_elems_for_one_bin( // set to empty probabilities.erase(); + if (proj_data_info_sptr && proj_data_info_sptr->is_tof_data()) + { + LORInAxialAndNoArcCorrSinogramCoordinates lor; + proj_data_info_sptr->get_LOR(lor, bin); + LORAs2Points lor2(lor); + this->get_proj_matrix_elems_for_one_bin_with_tof( + probabilities, + bin, + lor2.p1(), + lor2.p2()) ; + return; + } + if (cache_stores_only_basic_bins) { // find basic bin @@ -128,6 +141,7 @@ get_proj_matrix_elems_for_one_bin_with_tof( if (!tof_enabled) error("The function get_proj_matrix_elems_for_one_bin_with_tof() needs proper timing " "initialisation. Abort."); + // set to empty probabilities.erase(); ProjMatrixElemsForOneBin tmp_probabilities; diff --git a/src/include/stir/recon_buildblock/ProjectorByBinPair.h b/src/include/stir/recon_buildblock/ProjectorByBinPair.h index 6245607c51..9579f7cb4d 100644 --- a/src/include/stir/recon_buildblock/ProjectorByBinPair.h +++ b/src/include/stir/recon_buildblock/ProjectorByBinPair.h @@ -33,6 +33,7 @@ #include "stir/recon_buildblock/BackProjectorByBin.h" #include "stir/ParsingObject.h" #include "stir/shared_ptr.h" +#include "stir/recon_buildblock/ProjMatrixElemsForOneBin.h" START_NAMESPACE_STIR @@ -81,7 +82,15 @@ public ParsingObject //BackProjectorByBin const * const shared_ptr get_back_projector_sptr() const; - + + ProjMatrixElemsForOneBin* get_current_tof_row() const; + + void enable_tof(const shared_ptr& _proj_data_info_sptr, const bool v); + + void + set_tof_data(const CartesianCoordinate3D* _point1, + const CartesianCoordinate3D* _point2); + //! Provide access to the (minimal) symmetries used by the projectors /*! It is expected that the forward and back projector can handle the same diff --git a/src/include/stir/recon_buildblock/TrivialDataSymmetriesForBins.h b/src/include/stir/recon_buildblock/TrivialDataSymmetriesForBins.h index 22ba01fdc7..8b03c2b8ef 100644 --- a/src/include/stir/recon_buildblock/TrivialDataSymmetriesForBins.h +++ b/src/include/stir/recon_buildblock/TrivialDataSymmetriesForBins.h @@ -56,7 +56,8 @@ class TrivialDataSymmetriesForBins : public DataSymmetriesForBins virtual void get_related_bins(std::vector&, const Bin& b, const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) const; + const int min_tangential_pos_num, const int max_tangential_pos_num, + const int min_timing_pos_num = 0, const int max_timing_pos_num = 0) const; virtual void get_related_bins_factorised(std::vector&, const Bin& b, diff --git a/src/listmode_buildblock/LmToProjData.cxx b/src/listmode_buildblock/LmToProjData.cxx index 0dd9a0a262..1d85a97d94 100644 --- a/src/listmode_buildblock/LmToProjData.cxx +++ b/src/listmode_buildblock/LmToProjData.cxx @@ -121,7 +121,8 @@ static void allocate_segments(VectorWithOffset& segments, const int start_segment_index, const int end_segment_index, - const ProjDataInfo* proj_data_info_ptr); + const ProjDataInfo* proj_data_info_ptr, + const int timing_pos_num = 0); // In the next 2 functions, the 'output' parameter needs to be passed // because save_and_delete_segments needs it when we're not using SegmentByView @@ -134,7 +135,7 @@ save_and_delete_segments(shared_ptr& output, VectorWithOffset& segments, const int start_segment_index, const int end_segment_index, - ProjData& proj_data, const int timing_pos = 1); + ProjData& proj_data); static shared_ptr construct_proj_data(shared_ptr& output, @@ -519,7 +520,7 @@ process_data() { assert(!is_null_ptr(template_proj_data_info_ptr)); - if (template_proj_data_info_ptr->get_num_tof_poss() > 1) + if (template_proj_data_info_ptr->is_tof_data()) { use_tof = true; actual_process_data_with_tof(); @@ -597,7 +598,6 @@ actual_process_data_without_tof() start_segment_index <= proj_data_ptr->get_max_segment_num(); start_segment_index += num_segments_in_memory) { - const int end_segment_index = min( proj_data_ptr->get_max_segment_num()+1, start_segment_index + num_segments_in_memory) - 1; @@ -763,9 +763,6 @@ actual_process_data_with_tof() double time_of_last_stored_event = 0; long num_stored_events = 0; - VectorWithOffset - segments (template_proj_data_info_ptr->get_min_segment_num(), - template_proj_data_info_ptr->get_max_segment_num()); VectorWithOffset frame_start_positions(1, static_cast(frame_defs.get_num_frames())); @@ -815,6 +812,10 @@ actual_process_data_with_tof() start_segment_index+num_segments_in_memory. */ + VectorWithOffset + segments (template_proj_data_info_ptr->get_min_segment_num(), + template_proj_data_info_ptr->get_max_segment_num()); + for (int start_segment_index = proj_data_ptr->get_min_segment_num(); start_segment_index <= proj_data_ptr->get_max_segment_num(); start_segment_index += num_segments_in_memory) @@ -824,7 +825,7 @@ actual_process_data_with_tof() min( proj_data_ptr->get_max_segment_num()+1, start_segment_index + num_segments_in_memory) - 1; if (!interactive) - allocate_segments(segments, start_segment_index, end_segment_index, proj_data_ptr->get_proj_data_info_ptr()); + allocate_segments(segments, start_segment_index, end_segment_index, proj_data_ptr->get_proj_data_info_ptr(),current_timing_pos_index); // the next variable is used to see if there are more events to store for the current segments // num_events_to_store-more_events will be the number of allowed coincidence events currently seen in the file @@ -834,7 +835,7 @@ actual_process_data_with_tof() unsigned long int more_events = do_time_frame? 1 : num_events_to_store; - if (start_segment_index != proj_data_ptr->get_min_segment_num()) + if (start_segment_index != proj_data_ptr->get_min_segment_num() || current_timing_pos_index > proj_data_ptr->get_min_tof_pos_num()) { // we're going once more through the data (for the next batch of segments) cerr << "\nProcessing next batch of segments\n"; @@ -960,7 +961,7 @@ actual_process_data_with_tof() if (!interactive) save_and_delete_segments(output, segments, start_segment_index, end_segment_index, - *proj_data_ptr, current_timing_pos_index); + *proj_data_ptr); } // end of for loop for segment range } // end of for loop for timing positions @@ -1022,7 +1023,7 @@ LmToProjData::run_tof_test_function() min( proj_data_ptr->get_max_segment_num()+1, start_segment_index + num_segments_in_memory) - 1; if (!interactive) - allocate_segments(segments, start_segment_index, end_segment_index, proj_data_ptr->get_proj_data_info_ptr()); + allocate_segments(segments, start_segment_index, end_segment_index, proj_data_ptr->get_proj_data_info_ptr(), current_timing_pos_index); for (int ax_num = proj_data_ptr->get_proj_data_info_ptr()->get_min_axial_pos_num(start_segment_index); ax_num <= proj_data_ptr->get_proj_data_info_ptr()->get_max_axial_pos_num(start_segment_index); @@ -1043,7 +1044,7 @@ LmToProjData::run_tof_test_function() if (!interactive) save_and_delete_segments(output, segments, start_segment_index, end_segment_index, - *proj_data_ptr, current_timing_pos_index); + *proj_data_ptr); } // end of for loop for segment range } // end of for loop for timing positions @@ -1059,14 +1060,15 @@ void allocate_segments( VectorWithOffset& segments, const int start_segment_index, const int end_segment_index, - const ProjDataInfo* proj_data_info_ptr) + const ProjDataInfo* proj_data_info_ptr, + const int timing_pos_num) { for (int seg=start_segment_index ; seg<=end_segment_index; seg++) { #ifdef USE_SegmentByView segments[seg] = new SegmentByView( - proj_data_info_ptr->get_empty_segment_by_view (seg)); + proj_data_info_ptr->get_empty_segment_by_view (seg, false, timing_pos_num)); #else segments[seg] = new Array<3,elem_type>(IndexRange3D(0, proj_data_info_ptr->get_num_views()-1, @@ -1082,15 +1084,14 @@ save_and_delete_segments(shared_ptr& output, VectorWithOffset& segments, const int start_segment_index, const int end_segment_index, - ProjData& proj_data, - const int timing_pos) + ProjData& proj_data) { for (int seg=start_segment_index; seg<=end_segment_index; seg++) { { #ifdef USE_SegmentByView - proj_data.set_segment(*segments[seg], timing_pos); + proj_data.set_segment(*segments[seg]); #else (*segments[seg]).write_data(*output); #endif @@ -1113,8 +1114,8 @@ construct_proj_data(shared_ptr& output, shared_ptr proj_data_sptr; #ifdef USE_SegmentByView // don't need output stream in this case - if (proj_data_info_ptr->get_num_tof_poss() == 1) - proj_data_sptr.reset(new ProjDataInterfile(exam_info_sptr, + if (!proj_data_info_ptr->is_tof_data()) + proj_data_sptr.reset(new ProjDataInterfile(exam_info_sptr, proj_data_info_ptr, output_filename, ios::out, ProjDataFromStream::Segment_View_AxialPos_TangPos, OUTPUTNumericType)); diff --git a/src/listmode_utilities/list_lm_events.cxx b/src/listmode_utilities/list_lm_events.cxx index 33ee97d4c1..a234b495be 100644 --- a/src/listmode_utilities/list_lm_events.cxx +++ b/src/listmode_utilities/list_lm_events.cxx @@ -159,6 +159,7 @@ int main(int argc, char *argv[]) << ",r:" << det_pos.pos2().axial_coord() << ",l:" << det_pos.pos2().radial_coord() << ")"; + cout << " delta time: " << event_ptr->get_delta_time(); listed = true; } } diff --git a/src/local/utilities/fillwithotherprojdata.cxx b/src/local/utilities/fillwithotherprojdata.cxx index 585f80748f..8b17ad52cf 100644 --- a/src/local/utilities/fillwithotherprojdata.cxx +++ b/src/local/utilities/fillwithotherprojdata.cxx @@ -59,10 +59,14 @@ int main(int argc, char *argv[]) segment_num<=out_projdata_ptr->get_max_segment_num(); ++segment_num) { - SegmentByView segment = in_projdata_ptr->get_segment_by_view(segment_num); - if (out_projdata_ptr->set_segment(segment) == Succeeded::no) - return EXIT_FAILURE; - + for (int timing_pos_num=out_projdata_ptr->get_min_tof_pos_num(); + timing_pos_num<=out_projdata_ptr->get_max_tof_pos_num(); + ++timing_pos_num) + { + SegmentByView segment = in_projdata_ptr->get_segment_by_view(segment_num,timing_pos_num); + if (out_projdata_ptr->set_segment(segment) == Succeeded::no) + return EXIT_FAILURE; + } } return EXIT_SUCCESS; diff --git a/src/local/utilities/inverse_proj_data.cxx b/src/local/utilities/inverse_proj_data.cxx index da2120acf9..ecb5316cda 100644 --- a/src/local/utilities/inverse_proj_data.cxx +++ b/src/local/utilities/inverse_proj_data.cxx @@ -77,60 +77,75 @@ find_inverse( ProjData* proj_data_ptr_out, const ProjData* proj_data_ptr_in) int max_segment_num = ask_num("Maximum segment number to invert", min_segment_num,proj_data_info_ptr->get_max_segment_num(), min_segment_num); - + int min_timing_pos_num = 0; // Default cases of non-TOF data + int max_timing_pos_num = 0; + if (proj_data_info_ptr->is_tof_data()) + { + int min_timing_pos_num = ask_num("Minimum timing position index to invert", + proj_data_info_ptr->get_min_tof_pos_num(), proj_data_info_ptr->get_max_tof_pos_num(), 0); + int max_timing_pos_num = ask_num("Maximum timing position index to invert", + min_timing_pos_num,proj_data_info_ptr->get_max_tof_pos_num(), + max_timing_pos_num); + } float max_in_viewgram =0.F; for (int segment_num = min_segment_num; segment_num<= max_segment_num; segment_num++) { - SegmentByView segment_by_view = - projdatafromstream_in->get_segment_by_view(segment_num); - const float current_max_in_viewgram = segment_by_view.find_max(); - if ( current_max_in_viewgram >= max_in_viewgram) - max_in_viewgram = current_max_in_viewgram ; - else - continue; + for (int timing_pos_num = min_timing_pos_num; timing_pos_num<= max_timing_pos_num; + timing_pos_num++) + { + SegmentByView segment_by_view = + projdatafromstream_in->get_segment_by_view(segment_num,timing_pos_num); + const float current_max_in_viewgram = segment_by_view.find_max(); + if ( current_max_in_viewgram >= max_in_viewgram) + max_in_viewgram = current_max_in_viewgram ; + else + continue; + } } info(boost::format("Max number in viewgram is: %1%") % max_in_viewgram); for (int segment_num = min_segment_num; segment_num<= max_segment_num; segment_num++) - for ( int view_num = proj_data_info_ptr->get_min_view_num(); - view_num<=proj_data_info_ptr->get_max_view_num(); view_num++) - { - Viewgram viewgram_in = projdatafromstream_in->get_viewgram(view_num,segment_num); - Viewgram viewgram_out = proj_data_info_ptr_out->get_empty_viewgram(view_num,segment_num); - - // the following const was found in the ls_cyl.hs and the same value will - // be used for thresholding both kappa_0 and kappa_1. - // threshold = 10^-4/max_in_sinogram - // TODO - find out batter way of finding the threshold - // const float max_in_viewgram = 54.0F; - - //segment_by_view.find_max(); - - const float threshold = 0.0001F*max_in_viewgram; - - for (int i= viewgram_in.get_min_axial_pos_num(); i<=viewgram_in.get_max_axial_pos_num();i++) - for (int j= viewgram_in.get_min_tangential_pos_num(); j<=viewgram_in.get_max_tangential_pos_num();j++) - { - bin= viewgram_in[i][j]; - - if (bin >= threshold) - { - inv = 1.F/bin; - viewgram_out[i][j] = inv; - } - else - { - inv =1/threshold; - viewgram_out[i][j] = inv; - } - - } - projdatafromstream_out->set_viewgram(viewgram_out); - } + for (int timing_pos_num = min_timing_pos_num; timing_pos_num<= max_timing_pos_num; + timing_pos_num++) + for ( int view_num = proj_data_info_ptr->get_min_view_num(); + view_num<=proj_data_info_ptr->get_max_view_num(); view_num++) + { + Viewgram viewgram_in = projdatafromstream_in->get_viewgram(view_num,segment_num,false,timing_pos_num); + Viewgram viewgram_out = proj_data_info_ptr_out->get_empty_viewgram(view_num,segment_num,false,timing_pos_num); + + // the following const was found in the ls_cyl.hs and the same value will + // be used for thresholding both kappa_0 and kappa_1. + // threshold = 10^-4/max_in_sinogram + // TODO - find out batter way of finding the threshold + // const float max_in_viewgram = 54.0F; + + //segment_by_view.find_max(); + + const float threshold = 0.0001F*max_in_viewgram; + + for (int i= viewgram_in.get_min_axial_pos_num(); i<=viewgram_in.get_max_axial_pos_num();i++) + for (int j= viewgram_in.get_min_tangential_pos_num(); j<=viewgram_in.get_max_tangential_pos_num();j++) + { + bin= viewgram_in[i][j]; + + if (bin >= threshold) + { + inv = 1.F/bin; + viewgram_out[i][j] = inv; + } + else + { + inv =1/threshold; + viewgram_out[i][j] = inv; + } + + } + projdatafromstream_out->set_viewgram(viewgram_out); + } } diff --git a/src/recon_buildblock/BackProjectorByBin.cxx b/src/recon_buildblock/BackProjectorByBin.cxx index a10644722f..f606ab72d1 100644 --- a/src/recon_buildblock/BackProjectorByBin.cxx +++ b/src/recon_buildblock/BackProjectorByBin.cxx @@ -80,13 +80,18 @@ BackProjectorByBin::back_project(DiscretisedDensity<3,float>& image, for (int i=0; i(vs_nums_to_process.size()); ++i) { const ViewSegmentNumbers vs=vs_nums_to_process[i]; + + for (int k=proj_data.get_proj_data_info_ptr()->get_min_tof_pos_num(); + k<=proj_data.get_proj_data_info_ptr()->get_max_tof_pos_num(); + ++k) + { #ifdef STIR_OPENMP RelatedViewgrams viewgrams; #pragma omp critical (BACKPROJECTORBYBIN_GETVIEWGRAMS) viewgrams = proj_data.get_related_viewgrams(vs, symmetries_sptr); #else const RelatedViewgrams viewgrams = - proj_data.get_related_viewgrams(vs, symmetries_sptr); + proj_data.get_related_viewgrams(vs, symmetries_sptr, false, k); #endif #ifdef STIR_OPENMP const int thread_num=omp_get_thread_num(); @@ -97,6 +102,7 @@ BackProjectorByBin::back_project(DiscretisedDensity<3,float>& image, #else back_project(image, viewgrams); #endif + } } } #ifdef STIR_OPENMP @@ -172,5 +178,12 @@ back_project(DiscretisedDensity<3,float>& density, stop_timers(); } +void +BackProjectorByBin:: +back_project(DiscretisedDensity<3, float>& density, + const Bin &bin) +{ + actual_back_project(density, bin); +} END_NAMESPACE_STIR diff --git a/src/recon_buildblock/BackProjectorByBinUsingInterpolation.cxx b/src/recon_buildblock/BackProjectorByBinUsingInterpolation.cxx index e534282735..3345e9bce9 100644 --- a/src/recon_buildblock/BackProjectorByBinUsingInterpolation.cxx +++ b/src/recon_buildblock/BackProjectorByBinUsingInterpolation.cxx @@ -440,7 +440,13 @@ actual_back_project(DiscretisedDensity<3,float>& density, } - +void +BackProjectorByBinUsingInterpolation:: +actual_back_project(DiscretisedDensity<3,float>&, + const Bin&) +{ + error("BackProjectorByBinUsingInterpolation is not supported for list-mode reconstruction. Abort."); +} #if 0 diff --git a/src/recon_buildblock/BackProjectorByBinUsingProjMatrixByBin.cxx b/src/recon_buildblock/BackProjectorByBinUsingProjMatrixByBin.cxx index 0709594e33..78842daff6 100644 --- a/src/recon_buildblock/BackProjectorByBinUsingProjMatrixByBin.cxx +++ b/src/recon_buildblock/BackProjectorByBinUsingProjMatrixByBin.cxx @@ -139,6 +139,7 @@ actual_back_project(DiscretisedDensity<3,float>& image, const Viewgram& viewgram = *r_viewgrams_iter; const int view_num = viewgram.get_view_num(); const int segment_num = viewgram.get_segment_num(); + const int timing_num = viewgram.get_timing_pos_num(); for ( int tang_pos = min_tangential_pos_num ;tang_pos <= max_tangential_pos_num ;++tang_pos) for ( int ax_pos = min_axial_pos_num; ax_pos <= max_axial_pos_num ;++ax_pos) @@ -146,7 +147,7 @@ actual_back_project(DiscretisedDensity<3,float>& image, // KT 21/02/2002 added check on 0 if (viewgram[ax_pos][tang_pos] == 0) continue; - Bin bin(segment_num, view_num, ax_pos, tang_pos, viewgram[ax_pos][tang_pos]); + Bin bin(segment_num, view_num, ax_pos, tang_pos, timing_num, viewgram[ax_pos][tang_pos]); proj_matrix_ptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, bin); proj_matrix_row.back_project(image, bin); } @@ -175,7 +176,8 @@ actual_back_project(DiscretisedDensity<3,float>& image, Bin basic_bin(viewgrams.get_basic_segment_num(), viewgrams.get_basic_view_num(), ax_pos, - tang_pos); + tang_pos, + viewgrams.get_basic_timing_pos_num()); symmetries->find_basic_bin(basic_bin); proj_matrix_ptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, basic_bin); @@ -216,6 +218,7 @@ actual_back_project(DiscretisedDensity<3,float>& image, viewgram_iter->get_view_num(), axial_pos_tmp, tang_pos_tmp, + viewgram_iter->get_timing_pos_num(), (*viewgram_iter)[axial_pos_tmp][tang_pos_tmp]); auto_ptr symm_op_ptr = @@ -225,6 +228,7 @@ actual_back_project(DiscretisedDensity<3,float>& image, assert(bin.view_num() == basic_bin.view_num()); assert(bin.axial_pos_num() == basic_bin.axial_pos_num()); assert(bin.tangential_pos_num() == basic_bin.tangential_pos_num()); + assert(bin.timing_pos_num() == basic_bin.timing_pos_num()); symm_op_ptr->transform_proj_matrix_elems_for_one_bin(proj_matrix_row_copy); proj_matrix_row_copy.back_project(image, bin); @@ -239,5 +243,32 @@ actual_back_project(DiscretisedDensity<3,float>& image, } +void +BackProjectorByBinUsingProjMatrixByBin:: +actual_back_project(DiscretisedDensity<3,float>& image, + const Bin& bin) +{ + if (proj_matrix_ptr->is_cache_enabled() && !tof_enabled) + { + ProjMatrixElemsForOneBin proj_matrix_row; + proj_matrix_ptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, bin); + proj_matrix_row.back_project(image, bin); + } + else if (proj_matrix_ptr->is_cache_enabled() && tof_enabled) + { + tof_row->back_project(image, bin); + } + else + error("BackProjectorByBinUsingProjMatrixByBin: Symmetries should be handled by ProjMatrix. Abort. "); + +} + +void +BackProjectorByBinUsingProjMatrixByBin:: +enable_tof(ProjMatrixElemsForOneBin * for_row) +{ + tof_row = for_row; + tof_enabled = true; +} END_NAMESPACE_STIR diff --git a/src/recon_buildblock/BackProjectorByBinUsingSquareProjMatrixByBin.cxx b/src/recon_buildblock/BackProjectorByBinUsingSquareProjMatrixByBin.cxx index 8c624419be..5ccd4f7745 100644 --- a/src/recon_buildblock/BackProjectorByBinUsingSquareProjMatrixByBin.cxx +++ b/src/recon_buildblock/BackProjectorByBinUsingSquareProjMatrixByBin.cxx @@ -111,6 +111,13 @@ actual_back_project(DiscretisedDensity<3,float>& image, } } +void +BackProjectorByBinUsingSquareProjMatrixByBin:: +actual_back_project(DiscretisedDensity<3, float> &image, + const Bin& bin) +{ + error("BackProjectorByBinUsingSquareProjMatrixByBin is not supported for list-mode reconstruction. Abort."); +} void BackProjectorByBinUsingSquareProjMatrixByBin:: diff --git a/src/recon_buildblock/BinNormalisation.cxx b/src/recon_buildblock/BinNormalisation.cxx index 94a974161c..494207fb06 100644 --- a/src/recon_buildblock/BinNormalisation.cxx +++ b/src/recon_buildblock/BinNormalisation.cxx @@ -57,7 +57,7 @@ BinNormalisation::apply(RelatedViewgrams& viewgrams, { for (RelatedViewgrams::iterator iter = viewgrams.begin(); iter != viewgrams.end(); ++iter) { - Bin bin(iter->get_segment_num(),iter->get_view_num(), 0,0); + Bin bin(iter->get_segment_num(),iter->get_view_num(), 0,0,iter->get_timing_pos_num()); for (bin.axial_pos_num()= iter->get_min_axial_pos_num(); bin.axial_pos_num()<=iter->get_max_axial_pos_num(); ++bin.axial_pos_num()) @@ -75,7 +75,7 @@ undo(RelatedViewgrams& viewgrams,const double start_time, const double en { for (RelatedViewgrams::iterator iter = viewgrams.begin(); iter != viewgrams.end(); ++iter) { - Bin bin(iter->get_segment_num(),iter->get_view_num(), 0,0); + Bin bin(iter->get_segment_num(),iter->get_view_num(), 0,0,iter->get_timing_pos_num()); for (bin.axial_pos_num()= iter->get_min_axial_pos_num(); bin.axial_pos_num()<=iter->get_max_axial_pos_num(); ++bin.axial_pos_num()) @@ -109,26 +109,32 @@ apply(ProjData& proj_data,const double start_time, const double end_time, { const ViewSegmentNumbers vs=vs_nums_to_process[i]; - RelatedViewgrams viewgrams; + for (int k=proj_data.get_proj_data_info_ptr()->get_min_tof_pos_num(); + k<=proj_data.get_proj_data_info_ptr()->get_max_tof_pos_num(); + ++k) + { + + RelatedViewgrams viewgrams; #ifdef STIR_OPENMP - // reading/writing to streams is not safe in multi-threaded code - // so protect with a critical section - // note that the name of the section has to be same for the get/set - // function as they're reading from/writing to the same stream + // reading/writing to streams is not safe in multi-threaded code + // so protect with a critical section + // note that the name of the section has to be same for the get/set + // function as they're reading from/writing to the same stream #pragma omp critical (BINNORMALISATION_APPLY__VIEWGRAMS) #endif - { - viewgrams = - proj_data.get_related_viewgrams(vs, symmetries_sptr); - } + { + viewgrams = + proj_data.get_related_viewgrams(vs, symmetries_sptr, false, k); + } - this->apply(viewgrams, start_time, end_time); + this->apply(viewgrams, start_time, end_time); #ifdef STIR_OPENMP #pragma omp critical (BINNORMALISATION_APPLY__VIEWGRAMS) #endif - { - proj_data.set_related_viewgrams(viewgrams); + { + proj_data.set_related_viewgrams(viewgrams); + } } } } @@ -154,22 +160,27 @@ undo(ProjData& proj_data,const double start_time, const double end_time, { const ViewSegmentNumbers vs=vs_nums_to_process[i]; - RelatedViewgrams viewgrams; + for (int k=proj_data.get_proj_data_info_ptr()->get_min_tof_pos_num(); + k<=proj_data.get_proj_data_info_ptr()->get_max_tof_pos_num(); + ++k) + { + RelatedViewgrams viewgrams; #ifdef STIR_OPENMP #pragma omp critical (BINNORMALISATION_UNDO__VIEWGRAMS) #endif - { - viewgrams = - proj_data.get_related_viewgrams(vs, symmetries_sptr); - } + { + viewgrams = + proj_data.get_related_viewgrams(vs, symmetries_sptr,false,k); + } - this->undo(viewgrams, start_time, end_time); + this->undo(viewgrams, start_time, end_time); #ifdef STIR_OPENMP #pragma omp critical (BINNORMALISATION_UNDO__VIEWGRAMS) #endif - { - proj_data.set_related_viewgrams(viewgrams); + { + proj_data.set_related_viewgrams(viewgrams); + } } } } diff --git a/src/recon_buildblock/DataSymmetriesForBins.cxx b/src/recon_buildblock/DataSymmetriesForBins.cxx index ec694365d1..9d4e81be29 100644 --- a/src/recon_buildblock/DataSymmetriesForBins.cxx +++ b/src/recon_buildblock/DataSymmetriesForBins.cxx @@ -93,7 +93,8 @@ void DataSymmetriesForBins:: get_related_bins(vector& rel_b, const Bin& b, const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) const + const int min_tangential_pos_num, const int max_tangential_pos_num, + const int min_timing_pos_num, const int max_timing_pos_num) const { #ifndef NDEBUG Bin bin_copy = b; @@ -130,8 +131,11 @@ get_related_bins(vector& rel_b, const Bin& b, ax_tang_pos_ptr != ax_tang_poss.end(); ++ax_tang_pos_ptr) { - rel_b.push_back(Bin(view_seg_ptr->segment_num(), view_seg_ptr->view_num(), - (*ax_tang_pos_ptr)[1], (*ax_tang_pos_ptr)[2])); + for (int k=min_timing_pos_num;k<=max_timing_pos_num;++k) + { + rel_b.push_back(Bin(view_seg_ptr->segment_num(), view_seg_ptr->view_num(), + (*ax_tang_pos_ptr)[1], (*ax_tang_pos_ptr)[2],k)); + } } } } diff --git a/src/recon_buildblock/ForwardProjectorByBin.cxx b/src/recon_buildblock/ForwardProjectorByBin.cxx index 7abb00eda6..4a5525bf98 100644 --- a/src/recon_buildblock/ForwardProjectorByBin.cxx +++ b/src/recon_buildblock/ForwardProjectorByBin.cxx @@ -49,6 +49,7 @@ START_NAMESPACE_STIR ForwardProjectorByBin::ForwardProjectorByBin() { + tof_enabled = false; } ForwardProjectorByBin::~ForwardProjectorByBin() @@ -62,7 +63,7 @@ ForwardProjectorByBin::forward_project(ProjData& proj_data, // this->set_up(proj_data_ptr->get_proj_data_info_ptr()->clone(), // image_sptr); - + shared_ptr symmetries_sptr(this->get_symmetries_used()->clone()); @@ -77,18 +78,24 @@ ForwardProjectorByBin::forward_project(ProjData& proj_data, for (int i=0; i(vs_nums_to_process.size()); ++i) { const ViewSegmentNumbers vs=vs_nums_to_process[i]; - - info(boost::format("Processing view %1% of segment %2%") % vs.view_num() % vs.segment_num()); - - RelatedViewgrams viewgrams = - proj_data.get_empty_related_viewgrams(vs, symmetries_sptr); - forward_project(viewgrams, image); + for (int k=proj_data.get_proj_data_info_ptr()->get_min_tof_pos_num(); + k<=proj_data.get_proj_data_info_ptr()->get_max_tof_pos_num(); + ++k) + { + if (proj_data.get_proj_data_info_ptr()->is_tof_data()) + info(boost::format("Processing view %1% of segment %2% of TOF bin %3%") % vs.view_num() % vs.segment_num() % k); + else + info(boost::format("Processing view %1% of segment %2%") % vs.view_num() % vs.segment_num()); + RelatedViewgrams viewgrams = + proj_data.get_empty_related_viewgrams(vs, symmetries_sptr, false, k); + forward_project(viewgrams, image); #ifdef STIR_OPENMP #pragma omp critical (FORWARDPROJ_SETVIEWGRAMS) #endif - { - if (!(proj_data.set_related_viewgrams(viewgrams) == Succeeded::yes)) - error("Error set_related_viewgrams in forward projecting"); + { + if (!(proj_data.set_related_viewgrams(viewgrams) == Succeeded::yes)) + error("Error set_related_viewgrams in forward projecting"); + } } } @@ -156,5 +163,28 @@ forward_project(RelatedViewgrams& viewgrams, stop_timers(); } +void +ForwardProjectorByBin::forward_project(Bin& this_bin, + const DiscretisedDensity<3, float> & this_image) +{ + actual_forward_project(this_bin, + this_image); +} + +void +ForwardProjectorByBin:: +set_tof_data(const CartesianCoordinate3D* _point1, + const CartesianCoordinate3D* _point2) +{ + point1 = _point1; + point2 = _point2; +} + +ProjMatrixElemsForOneBin* +ForwardProjectorByBin:: +get_tof_row() const +{ + return tof_probabilities.get(); +} END_NAMESPACE_STIR diff --git a/src/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.cxx b/src/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.cxx index 3405dbcfe3..f81486ace0 100644 --- a/src/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.cxx +++ b/src/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.cxx @@ -128,9 +128,9 @@ ForwardProjectorByBinUsingProjMatrixByBin:: // straightforward version which relies on ProjMatrixByBin to sort out all // symmetries // would be slow if there's no caching at all, but is very fast if everything is cached - + ProjMatrixElemsForOneBin proj_matrix_row; - + RelatedViewgrams::iterator r_viewgrams_iter = viewgrams.begin(); while( r_viewgrams_iter!=viewgrams.end()) @@ -138,11 +138,12 @@ ForwardProjectorByBinUsingProjMatrixByBin:: Viewgram& viewgram = *r_viewgrams_iter; const int view_num = viewgram.get_view_num(); const int segment_num = viewgram.get_segment_num(); + const int timing_num = viewgram.get_timing_pos_num(); for ( int tang_pos = min_tangential_pos_num ;tang_pos <= max_tangential_pos_num ;++tang_pos) for ( int ax_pos = min_axial_pos_num; ax_pos <= max_axial_pos_num ;++ax_pos) { - Bin bin(segment_num, view_num, ax_pos, tang_pos, 0.f); + Bin bin(segment_num, view_num, ax_pos, tang_pos, timing_num, 0.f); proj_matrix_ptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, bin); proj_matrix_row.forward_project(bin,image); viewgram[ax_pos][tang_pos] = bin.get_bin_value(); @@ -152,6 +153,7 @@ ForwardProjectorByBinUsingProjMatrixByBin:: } else { + error("Need to do TOF stuff here"); // Complicated version which handles the symmetries explicitly. // Faster when no caching is performed, about just as fast when there is caching, // but of only basic bins. @@ -170,9 +172,10 @@ ForwardProjectorByBinUsingProjMatrixByBin:: if (already_processed[ax_pos][tang_pos]) continue; - Bin basic_bin(viewgrams.get_basic_segment_num(),viewgrams.get_basic_view_num(),ax_pos,tang_pos); + Bin basic_bin(viewgrams.get_basic_segment_num(),viewgrams.get_basic_view_num(),ax_pos,tang_pos, + viewgrams.get_basic_timing_pos_num()); symmetries->find_basic_bin(basic_bin); - + proj_matrix_ptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, basic_bin); vector r_ax_poss; @@ -208,7 +211,8 @@ ForwardProjectorByBinUsingProjMatrixByBin:: Bin bin(viewgram_iter->get_segment_num(), viewgram_iter->get_view_num(), axial_pos_tmp, - tang_pos_tmp); + tang_pos_tmp, + viewgram_iter->get_timing_pos_num()); auto_ptr symm_op_ptr = symmetries->find_symmetry_operation_from_basic_bin(bin); @@ -227,4 +231,35 @@ ForwardProjectorByBinUsingProjMatrixByBin:: } } +void +ForwardProjectorByBinUsingProjMatrixByBin:: + actual_forward_project(Bin& this_bin, + const DiscretisedDensity<3, float> &density) +{ + + if (proj_matrix_ptr->is_cache_enabled() && !tof_enabled) + { + ProjMatrixElemsForOneBin proj_matrix_row; + + proj_matrix_ptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, this_bin); + proj_matrix_row.forward_project(this_bin,density); + } + else if (proj_matrix_ptr->is_cache_enabled() && tof_enabled) + { + proj_matrix_ptr->get_proj_matrix_elems_for_one_bin_with_tof(*tof_probabilities, this_bin, *point1, *point2); + tof_probabilities->forward_project(this_bin,density); + } + else + error("ForwardProjectorByBinUsingProjMatrixByBin: Symmetries should be handled by ProjMatrix. Abort. "); +} + +void +ForwardProjectorByBinUsingProjMatrixByBin:: +enable_tof(const shared_ptr& _proj_data_info_sptr, const bool v) +{ + proj_matrix_ptr->enable_tof(_proj_data_info_sptr, v); + tof_enabled = v; + tof_probabilities.reset(new ProjMatrixElemsForOneBin()); +} + END_NAMESPACE_STIR diff --git a/src/recon_buildblock/ForwardProjectorByBinUsingRayTracing.cxx b/src/recon_buildblock/ForwardProjectorByBinUsingRayTracing.cxx index 5af0934820..9bf0c7e040 100644 --- a/src/recon_buildblock/ForwardProjectorByBinUsingRayTracing.cxx +++ b/src/recon_buildblock/ForwardProjectorByBinUsingRayTracing.cxx @@ -152,12 +152,18 @@ set_up(const shared_ptr& proj_data_info_ptr, segment_num <= proj_data_info_ptr->get_max_segment_num(); ++segment_num) { - const float num_planes_per_axial_pos = - symmetries_ptr->get_num_planes_per_axial_pos(segment_num); - if (fabs(round(num_planes_per_axial_pos) - num_planes_per_axial_pos) > 1.E-4) - error("ForwardProjectorByBinUsingRayTracing: the number of image planes " - "per axial_pos (which is %g for segment %d) should be an integer\n", - num_planes_per_axial_pos, segment_num); +// for (int k = proj_data_info_ptr->get_min_tof_pos_num(); +// k <= proj_data_info_ptr->get_max_tof_pos_num(); +// ++k) +// { + //TODO Check symmetries here, should we add TOF? + const float num_planes_per_axial_pos = + symmetries_ptr->get_num_planes_per_axial_pos(segment_num); + if (fabs(round(num_planes_per_axial_pos) - num_planes_per_axial_pos) > 1.E-4) + error("ForwardProjectorByBinUsingRayTracing: the number of image planes " + "per axial_pos (which is %g for segment %d) should be an integer\n", + num_planes_per_axial_pos, segment_num); +// } } @@ -1445,5 +1451,12 @@ forward_project_all_symmetries_2D(Viewgram & pos_view, stop_timers(); } +void +ForwardProjectorByBinUsingRayTracing:: + actual_forward_project(Bin& this_bin, + const DiscretisedDensity<3,float>& density) +{ + error("ForwardProjectorByBinUsingRayTracing is not supported for list-mode data. Abort."); +} END_NAMESPACE_STIR diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx index 5a49aca198..928f706a61 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx @@ -54,6 +54,9 @@ #include "stir/recon_buildblock/ProjMatrixByBinUsingRayTracing.h" #include "stir/recon_buildblock/ProjectorByBinPairUsingSeparateProjectors.h" +#include "stir/recon_buildblock/PresmoothingForwardProjectorByBin.h" +#include "stir/recon_buildblock/PostsmoothingBackProjectorByBin.h" + #ifdef STIR_MPI #include "stir/recon_buildblock/distributed_functions.h" #endif @@ -78,16 +81,17 @@ PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin() template void PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin:: -set_defaults() -{ +set_defaults() +{ base_type::set_defaults(); this->additive_proj_data_sptr.reset(); - this->additive_projection_data_filename ="0"; + this->additive_projection_data_filename ="0"; this->max_ring_difference_num_to_process =-1; this->PM_sptr.reset(new ProjMatrixByBinUsingRayTracing()); this->normalisation_sptr.reset(new TrivialBinNormalisation); this->do_time_frame = false; + this->use_projectors = false; } template @@ -99,8 +103,10 @@ initialise_keymap() this->parser.add_start_key("PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin Parameters"); this->parser.add_stop_key("End PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin Parameters"); this->parser.add_key("max ring difference num to process", &this->max_ring_difference_num_to_process); - this->parser.add_parsing_key("Matrix type", &this->PM_sptr); - this->parser.add_key("additive sinogram",&this->additive_projection_data_filename); + this->parser.add_parsing_key("Matrix type", &this->PM_sptr); + this->parser.add_parsing_key("Projector pair type", &this->projector_pair_ptr); + this->parser.add_key("use projectors", &use_projectors); + this->parser.add_key("additive sinogram",&this->additive_projection_data_filename); this->parser.add_key("num_events_to_store",&this->num_events_to_store); } @@ -128,39 +134,46 @@ actual_subsets_are_approximately_balanced(std::string& warning_message) const for (int subset_num=0; subset_numnum_subsets; ++subset_num) { - for (int segment_num = -this->max_ring_difference_num_to_process; - segment_num <= this->max_ring_difference_num_to_process; ++segment_num) - { - for (int axial_num = proj_data_info_cyl_sptr->get_min_axial_pos_num(segment_num); - axial_num < proj_data_info_cyl_sptr->get_max_axial_pos_num(segment_num); - axial_num ++) - { - // For debugging. - // std::cout <get_min_tangential_pos_num(); - tang_num < proj_data_info_cyl_sptr->get_max_tangential_pos_num(); - tang_num ++ ) - { - for(int view_num = proj_data_info_cyl_sptr->get_min_view_num() + subset_num; - view_num <= proj_data_info_cyl_sptr->get_max_view_num(); - view_num += this->num_subsets) - { - const Bin tmp_bin(segment_num, - view_num, - axial_num, - tang_num, 1); - - if (!this->PM_sptr->get_symmetries_ptr()->is_basic(tmp_bin) ) - continue; - - num_bins_in_subset[subset_num] += - symmetries.num_related_bins(tmp_bin); - - } - } - } - } + for (int timing_pos_num = proj_data_info_cyl_sptr->get_min_tof_pos_num(); + timing_pos_num <= proj_data_info_cyl_sptr->get_max_tof_pos_num(); + ++timing_pos_num) + { + for (int segment_num = -this->max_ring_difference_num_to_process; + segment_num <= this->max_ring_difference_num_to_process; ++segment_num) + { + for (int axial_num = proj_data_info_cyl_sptr->get_min_axial_pos_num(segment_num); + axial_num < proj_data_info_cyl_sptr->get_max_axial_pos_num(segment_num); + axial_num ++) + { + // For debugging. + // std::cout <get_min_tangential_pos_num(); + tang_num < proj_data_info_cyl_sptr->get_max_tangential_pos_num(); + tang_num ++ ) + { + for(int view_num = proj_data_info_cyl_sptr->get_min_view_num() + subset_num; + view_num <= proj_data_info_cyl_sptr->get_max_view_num(); + view_num += this->num_subsets) + { + const Bin tmp_bin(segment_num, + view_num, + axial_num, + tang_num, + timing_pos_num, + 1); + + if (!this->PM_sptr->get_symmetries_ptr()->is_basic(tmp_bin) ) + continue; + + num_bins_in_subset[subset_num] += + symmetries.num_related_bins(tmp_bin); + + } + } + } + } + } } for (int subset_num=1; subset_numnum_subsets; ++subset_num) @@ -196,20 +209,28 @@ set_up_before_sensitivity(shared_ptr const& target_sptr) distributed::send_int_value(100, -1); #endif + // Check if we have listmode projectors + if (!use_projectors) + { + // set projector to be used for the calculations + this->PM_sptr->set_up(proj_data_info_cyl_sptr->create_shared_clone(),target_sptr); - // set projector to be used for the calculations - this->PM_sptr->set_up(proj_data_info_cyl_sptr->create_shared_clone(),target_sptr); - - this->PM_sptr->enable_tof(proj_data_info_cyl_sptr->create_shared_clone(), this->use_tof); + this->PM_sptr->enable_tof(proj_data_info_cyl_sptr->create_shared_clone(), this->use_tof); - shared_ptr forward_projector_ptr(new ForwardProjectorByBinUsingProjMatrixByBin(this->PM_sptr)); - shared_ptr back_projector_ptr(new BackProjectorByBinUsingProjMatrixByBin(this->PM_sptr)); + shared_ptr forward_projector_ptr(new ForwardProjectorByBinUsingProjMatrixByBin(this->PM_sptr)); + shared_ptr back_projector_ptr(new BackProjectorByBinUsingProjMatrixByBin(this->PM_sptr)); - this->projector_pair_ptr.reset( - new ProjectorByBinPairUsingSeparateProjectors(forward_projector_ptr, back_projector_ptr)); + this->projector_pair_ptr.reset( + new ProjectorByBinPairUsingSeparateProjectors(forward_projector_ptr, back_projector_ptr)); - this->projector_pair_ptr->set_up(proj_data_info_cyl_sptr->create_shared_clone(),target_sptr); + this->projector_pair_ptr->set_up(proj_data_info_cyl_sptr->create_shared_clone(),target_sptr); + } + else + { + this->projector_pair_ptr->set_up(proj_data_info_cyl_sptr->create_shared_clone(),target_sptr); + this->projector_pair_ptr->enable_tof(proj_data_info_cyl_sptr->create_shared_clone(), this->use_tof); + } if (is_null_ptr(this->normalisation_sptr)) { @@ -235,21 +256,21 @@ set_up_before_sensitivity(shared_ptr const& target_sptr) } return Succeeded::yes; -} - - -template -bool +} + + +template +bool PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin::post_processing() -{ +{ + + if (base_type::post_processing() == true) + return true; - if (base_type::post_processing() == true) - return true; - #if 1 - if (is_null_ptr(this->PM_sptr)) + if (is_null_ptr(this->PM_sptr)) - { warning("You need to specify a projection matrix"); return true; } + { warning("You need to specify a projection matrix"); return true; } #else if(is_null_ptr(this->projector_pair_sptr->get_forward_projector_sptr())) @@ -266,7 +287,7 @@ PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBinmax_ring_difference_num_to_process == -1) { - this->max_ring_difference_num_to_process = + this->max_ring_difference_num_to_process = scanner_sptr->get_num_rings()-1; } @@ -306,7 +327,9 @@ PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin= proj.get_max_segment_num(); + add_proj.get_max_segment_num() >= proj.get_max_segment_num() && + add_proj.get_min_tof_pos_num() <= proj.get_min_tof_pos_num() && + add_proj.get_max_tof_pos_num() >= proj.get_max_tof_pos_num() ; for (int segment_num=proj.get_min_segment_num(); ok && segment_num<=proj.get_max_segment_num(); @@ -334,10 +357,10 @@ warning("PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrix return true; } - return false; + return false; + +} -} - template void PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin:: @@ -346,33 +369,38 @@ add_subset_sensitivity(TargetT& sensitivity, const int subset_num) const const int min_segment_num = proj_data_info_cyl_sptr->get_min_segment_num(); const int max_segment_num = proj_data_info_cyl_sptr->get_max_segment_num(); + const int min_timing_pos_num = proj_data_info_cyl_sptr->get_min_tof_pos_num(); + const int max_timing_pos_num = proj_data_info_cyl_sptr->get_max_tof_pos_num(); // warning: has to be same as subset scheme used as in distributable_computation - for (int segment_num = min_segment_num; segment_num <= max_segment_num; ++segment_num) + for (int timing_pos_num = min_timing_pos_num; timing_pos_num <= min_timing_pos_num; ++timing_pos_num) { - for (int view = proj_data_info_cyl_sptr->get_min_view_num() + subset_num; - view <= proj_data_info_cyl_sptr->get_max_view_num(); - view += this->num_subsets) - { - const ViewSegmentNumbers view_segment_num(view, segment_num); - - if (! this->projector_pair_ptr->get_symmetries_used()->is_basic(view_segment_num)) - continue; - this->add_view_seg_to_sensitivity(sensitivity, view_segment_num); - } + for (int segment_num = min_segment_num; segment_num <= max_segment_num; ++segment_num) + { + for (int view = proj_data_info_cyl_sptr->get_min_view_num() + subset_num; + view <= proj_data_info_cyl_sptr->get_max_view_num(); + view += this->num_subsets) + { + const ViewSegmentNumbers view_segment_num(view, segment_num); + + if (! this->projector_pair_ptr->get_symmetries_used()->is_basic(view_segment_num)) + continue; + this->add_view_seg_to_sensitivity(sensitivity, view_segment_num, timing_pos_num); + } + } } } template void PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin:: -add_view_seg_to_sensitivity(TargetT& sensitivity, const ViewSegmentNumbers& view_seg_nums) const +add_view_seg_to_sensitivity(TargetT& sensitivity, const ViewSegmentNumbers& view_seg_nums, const int timing_pos_num) const { shared_ptr symmetries_used (this->projector_pair_ptr->get_symmetries_used()->clone()); RelatedViewgrams viewgrams = - proj_data_info_cyl_sptr->get_empty_related_viewgrams(view_seg_nums,symmetries_used); + proj_data_info_cyl_sptr->get_empty_related_viewgrams(view_seg_nums,symmetries_used, false, timing_pos_num); viewgrams.fill(1.F); // find efficiencies @@ -401,26 +429,26 @@ PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin (*proj_data_info_cyl_sptr, - static_cast(this->zoom), - CartesianCoordinate3D(static_cast(this->Zoffset), - static_cast(this->Yoffset), - static_cast(this->Xoffset)), - CartesianCoordinate3D(this->output_image_size_z, - this->output_image_size_xy, - this->output_image_size_xy) - ); + static_cast(this->zoom), + CartesianCoordinate3D(static_cast(this->Zoffset), + static_cast(this->Yoffset), + static_cast(this->Xoffset)), + CartesianCoordinate3D(this->output_image_size_z, + this->output_image_size_xy, + this->output_image_size_xy) + ); -} - -template -void -PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin:: -compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, - const TargetT ¤t_estimate, - const int subset_num) -{ +} + +template +void +PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin:: +compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, + const TargetT ¤t_estimate, + const int subset_num) +{ assert(subset_num>=0); assert(subset_numnum_subsets); @@ -446,13 +474,35 @@ compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, // TODO implement function that will do this for a random time this->list_mode_data_sptr->reset(); double current_time = 0.; - ProjMatrixElemsForOneBin proj_matrix_row; + ProjMatrixElemsForOneBin proj_matrix_row; - shared_ptr record_sptr = this->list_mode_data_sptr->get_empty_record_sptr(); - CListRecord& record = *record_sptr; + shared_ptr record_sptr = this->list_mode_data_sptr->get_empty_record_sptr(); + CListRecord& record = *record_sptr; unsigned long int more_events = - this->do_time_frame? 1 : this->num_events_to_store; + this->do_time_frame? 1 : (this->num_events_to_store / this->num_subsets); + + if (use_projectors) + { + + PresmoothingForwardProjectorByBin* forward= + dynamic_cast (this->projector_pair_ptr->get_forward_projector_sptr().get()); + + if (!is_null_ptr(forward)) + forward->update_filtered_density_image(current_estimate); + + PostsmoothingBackProjectorByBin* back= + dynamic_cast (this->projector_pair_ptr->get_back_projector_sptr().get()); + + if (!is_null_ptr(back)) + back->init_filtered_density_image(gradient); + + if (this->use_tof) + { + projector_pair_ptr->set_tof_data(&lor_points.p1(), &lor_points.p2()); + } + + } while (more_events)//this->list_mode_data_sptr->get_next_record(record) == Succeeded::yes) { @@ -472,12 +522,12 @@ compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, continue; } - if (record.is_event() && record.event().is_prompt()) - { + if (record.is_event() && record.event().is_prompt()) + { measured_bin.set_bin_value(1.0f); // this->use_tof ? record.full_event(measured_bin, *proj_data_info_cyl_sptr): - record.event().get_bin(measured_bin, *proj_data_info_cyl_sptr); + record.event().get_bin(measured_bin, *proj_data_info_cyl_sptr); // In theory we have already done all these checks so we can // remove this if statement. @@ -497,6 +547,7 @@ compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, measured_bin.set_bin_value(1.0f); // If more than 1 subsets, check if the current bin belongs to // the current. + if (this->num_subsets > 1) { Bin basic_bin = measured_bin; @@ -509,46 +560,93 @@ compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, } } - if(this->use_tof) - { - lor_points = record.event().get_LOR(); - this->PM_sptr->get_proj_matrix_elems_for_one_bin_with_tof(proj_matrix_row, - measured_bin, - lor_points.p1(), lor_points.p2()); - } - else - this->PM_sptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, measured_bin); - - //in_the_range++; - fwd_bin.set_bin_value(0.0f); - proj_matrix_row.forward_project(fwd_bin,current_estimate); - // additive sinogram - if (!is_null_ptr(this->additive_proj_data_sptr)) - { - float add_value = this->additive_proj_data_sptr->get_bin_value(measured_bin); - float value= fwd_bin.get_bin_value()+add_value; - fwd_bin.set_bin_value(value); - } - float measured_div_fwd = 0.0f; - - if(!this->do_time_frame) - more_events -=1 ; - - num_stored_events += 1; + if(!use_projectors) + { + if(this->use_tof) + { + lor_points = record.event().get_LOR(); + this->PM_sptr->get_proj_matrix_elems_for_one_bin_with_tof(proj_matrix_row, + measured_bin, + lor_points.p1(), lor_points.p2()); + } + else + this->PM_sptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, measured_bin); + + //in_the_range++; + fwd_bin.set_bin_value(0.0f); + proj_matrix_row.forward_project(fwd_bin,current_estimate); + // additive sinogram + if (!is_null_ptr(this->additive_proj_data_sptr)) + { + float add_value = this->additive_proj_data_sptr->get_bin_value(measured_bin); + float value= fwd_bin.get_bin_value()+add_value; + fwd_bin.set_bin_value(value); + } + float measured_div_fwd = 0.0f; + + if(!this->do_time_frame) + more_events -=1 ; + + num_stored_events += 1; + + if (num_stored_events%200000L==0) + info( boost::format("Stored Events: %1% ") % num_stored_events); + + if ( measured_bin.get_bin_value() <= max_quotient *fwd_bin.get_bin_value()) + measured_div_fwd = 1.0f /fwd_bin.get_bin_value(); + else + continue; + + measured_bin.set_bin_value(measured_div_fwd); + proj_matrix_row.back_project(gradient, measured_bin); + } + else + { + measured_bin.set_bin_value(0.0f); + + + if(this->use_tof) + lor_points = record.event().get_LOR(); + + projector_pair_ptr->get_forward_projector_sptr()->forward_project(measured_bin, + current_estimate); + + if (!is_null_ptr(this->additive_proj_data_sptr)) + { + float add_value = this->additive_proj_data_sptr->get_bin_value(measured_bin); + float value= measured_bin.get_bin_value()+add_value; + measured_bin.set_bin_value(value); + } + + float measured_div_fwd = 0.0f; + + if(!this->do_time_frame) + more_events -=1 ; + + num_stored_events += 1; + + if (num_stored_events%200000L==0) + info( boost::format("Stored Events: %1% ") % num_stored_events); + + if ( measured_bin.get_bin_value() <= max_quotient *measured_bin.get_bin_value()) + measured_div_fwd = 1.0f /measured_bin.get_bin_value(); + else + continue; + + measured_bin.set_bin_value(measured_div_fwd); + projector_pair_ptr->get_back_projector_sptr()->back_project(gradient, measured_bin); + } + } + } + if (use_projectors) + { + PostsmoothingBackProjectorByBin* back= + dynamic_cast (this->projector_pair_ptr->get_back_projector_sptr().get()); - if (num_stored_events%200000L==0) - info( boost::format("Stored Events: %1% ") % num_stored_events); + if (!is_null_ptr(back)) + back->update_filtered_density_image(gradient); - if ( measured_bin.get_bin_value() <= max_quotient *fwd_bin.get_bin_value()) - measured_div_fwd = 1.0f /fwd_bin.get_bin_value(); - else - continue; - - measured_bin.set_bin_value(measured_div_fwd); - proj_matrix_row.back_project(gradient, measured_bin); - - } - } + } info(boost::format("Number of used events: %1%") % num_stored_events); } diff --git a/src/recon_buildblock/PostsmoothingBackProjectorByBin.cxx b/src/recon_buildblock/PostsmoothingBackProjectorByBin.cxx index a2695c91f3..696f1f8229 100644 --- a/src/recon_buildblock/PostsmoothingBackProjectorByBin.cxx +++ b/src/recon_buildblock/PostsmoothingBackProjectorByBin.cxx @@ -71,6 +71,28 @@ PostsmoothingBackProjectorByBin:: set_defaults(); } +void PostsmoothingBackProjectorByBin:: +update_filtered_density_image(DiscretisedDensity<3, float> &density) +{ + image_processor_ptr->apply(*filtered_density_sptr); + density += *filtered_density_sptr; + filtered_density_sptr->fill(0.f); +} + +BackProjectorByBin* +PostsmoothingBackProjectorByBin::get_original_back_projector_ptr() const +{ + return original_back_projector_ptr.get(); +} + +void PostsmoothingBackProjectorByBin:: +init_filtered_density_image(DiscretisedDensity<3, float> &density) +{ + filtered_density_sptr.reset( + density.get_empty_discretised_density()); + assert(density.get_index_range() == filtered_density_sptr->get_index_range()); +} + PostsmoothingBackProjectorByBin:: PostsmoothingBackProjectorByBin( const shared_ptr& original_back_projector_ptr, @@ -126,7 +148,22 @@ actual_back_project(DiscretisedDensity<3,float>& density, min_tangential_pos_num, max_tangential_pos_num); } } - +void +PostsmoothingBackProjectorByBin:: +actual_back_project(DiscretisedDensity<3, float> &density, + const Bin& bin) +{ + if (!is_null_ptr(image_processor_ptr)) + { +// + original_back_projector_ptr->back_project(*filtered_density_sptr, bin); + + } + else + { + original_back_projector_ptr->back_project(density, bin); + } +} END_NAMESPACE_STIR diff --git a/src/recon_buildblock/PresmoothingForwardProjectorByBin.cxx b/src/recon_buildblock/PresmoothingForwardProjectorByBin.cxx index 29cbd64c0a..97a6f19c1a 100644 --- a/src/recon_buildblock/PresmoothingForwardProjectorByBin.cxx +++ b/src/recon_buildblock/PresmoothingForwardProjectorByBin.cxx @@ -94,6 +94,12 @@ set_up(const shared_ptr& proj_data_info_ptr, image_processor_ptr->set_up(*image_info_ptr); } +ForwardProjectorByBin* +PresmoothingForwardProjectorByBin::get_original_forward_projector_ptr() const +{ + return original_forward_projector_ptr.get(); +} + const DataSymmetriesForViewSegmentNumbers * PresmoothingForwardProjectorByBin:: get_symmetries_used() const @@ -101,6 +107,14 @@ get_symmetries_used() const return original_forward_projector_ptr->get_symmetries_used(); } +void PresmoothingForwardProjectorByBin:: +update_filtered_density_image(const DiscretisedDensity<3,float>& density) +{ + filtered_density_sptr.reset(density.get_empty_discretised_density()); + image_processor_ptr->apply(*filtered_density_sptr, density); + assert(density.get_index_range() == filtered_density_sptr->get_index_range()); +} + void PresmoothingForwardProjectorByBin:: actual_forward_project(RelatedViewgrams& viewgrams, @@ -125,6 +139,19 @@ actual_forward_project(RelatedViewgrams& viewgrams, } } - +void +PresmoothingForwardProjectorByBin:: +actual_forward_project(Bin& this_bin, + const DiscretisedDensity<3,float>& density) +{ + if (!is_null_ptr(image_processor_ptr)) + { + original_forward_projector_ptr->forward_project(this_bin, *filtered_density_sptr); + } + else + { + original_forward_projector_ptr->forward_project(this_bin, density); + } +} END_NAMESPACE_STIR diff --git a/src/recon_buildblock/ProjMatrixByBin.cxx b/src/recon_buildblock/ProjMatrixByBin.cxx index 951859650b..d25ef353f2 100644 --- a/src/recon_buildblock/ProjMatrixByBin.cxx +++ b/src/recon_buildblock/ProjMatrixByBin.cxx @@ -146,6 +146,9 @@ set_up( const int min_segment_num = proj_data_info_sptr->get_min_segment_num(); const int max_segment_num = proj_data_info_sptr->get_max_segment_num(); + if (proj_data_info_sptr->is_tof_data()) + enable_tof(proj_data_info_sptr,true); + this->cache_collection.recycle(); this->cache_collection.resize(min_view_num, max_view_num); #ifdef STIR_OPENMP diff --git a/src/recon_buildblock/ProjectorByBinPair.cxx b/src/recon_buildblock/ProjectorByBinPair.cxx index 106152a2cf..fed8902369 100644 --- a/src/recon_buildblock/ProjectorByBinPair.cxx +++ b/src/recon_buildblock/ProjectorByBinPair.cxx @@ -29,6 +29,12 @@ #include "stir/recon_buildblock/ProjectorByBinPair.h" #include "stir/Succeeded.h" +#include "stir/recon_buildblock/PresmoothingForwardProjectorByBin.h" +#include "stir/recon_buildblock/PostsmoothingBackProjectorByBin.h" +#include "stir/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.h" +#include "stir/recon_buildblock/BackProjectorByBinUsingProjMatrixByBin.h" +#include "stir/is_null_ptr.h" + START_NAMESPACE_STIR @@ -50,6 +56,175 @@ set_up(const shared_ptr& proj_data_info_ptr, return Succeeded::yes; } +void +ProjectorByBinPair:: +enable_tof(const shared_ptr& _proj_data_info_sptr, const bool v) +{ + // Check if it is PresmoothingForwardProjectorByBin + PresmoothingForwardProjectorByBin* forward= + dynamic_cast (forward_projector_sptr.get()); + + if (!is_null_ptr(forward)) + { + ForwardProjectorByBinUsingProjMatrixByBin* original_forward = + dynamic_cast (forward->get_original_forward_projector_ptr()); + + if (is_null_ptr(original_forward)) + error("Currently only ForwardProjectorByBinUsingProjMatrixByBin supports TOF reconstruction. Abort."); + else + original_forward->enable_tof(_proj_data_info_sptr, v); + } + else // if is ForwardProjectorByBinUsingProjMatrixByBin directly. + { + ForwardProjectorByBinUsingProjMatrixByBin* original_forward = + dynamic_cast (forward_projector_sptr.get()); + + if (is_null_ptr(original_forward)) + error("Currently only ForwardProjectorByBinUsingProjMatrixByBin supports TOF reconstruction. Abort."); + else + original_forward->enable_tof(_proj_data_info_sptr, v); + } + +// // Check if it is PostSmoothingBackProjectorByBin is used. +// PostsmoothingBackProjectorByBin* back= +// dynamic_cast (back_projector_sptr.get()); + +// if (!is_null_ptr(back)) +// { +// BackProjectorByBinUsingProjMatrixByBin* original_back = +// dynamic_cast (back->get_original_back_projector_ptr()); + +// if (is_null_ptr(original_back)) +// error("Currently only ForwardProjectorByBinUsingProjMatrixByBin supports TOF reconstruction. Abort."); +// else +// original_back->enable_tof(_proj_data_info_sptr, v); +// } +// else // if is ForwardProjectorByBinUsingProjMatrixByBin directly. +// { +// BackProjectorByBinUsingProjMatrixByBin* original_back = +// dynamic_cast (back_projector_sptr.get()); + +// if (is_null_ptr(original_back)) +// error("Currently only BackProjectorByBinUsingProjMatrixByBin supports TOF reconstruction. Abort."); +// else +// original_back->enable_tof(_proj_data_info_sptr, v); +// } + +} + +void +ProjectorByBinPair:: +set_tof_data(const CartesianCoordinate3D* _point1, + const CartesianCoordinate3D* _point2) +{ + // Check if it is PresmoothingForwardProjectorByBin + PresmoothingForwardProjectorByBin* forward= + dynamic_cast (forward_projector_sptr.get()); + + if (!is_null_ptr(forward)) + { + ForwardProjectorByBinUsingProjMatrixByBin* original_forward = + dynamic_cast (forward->get_original_forward_projector_ptr()); + + if (is_null_ptr(original_forward)) + error("Currently only ForwardProjectorByBinUsingProjMatrixByBin supports TOF reconstruction. Abort."); + else + { + original_forward->set_tof_data(_point1, _point2); + + PostsmoothingBackProjectorByBin* back= + dynamic_cast (back_projector_sptr.get()); + + if (!is_null_ptr(back)) + { + BackProjectorByBinUsingProjMatrixByBin* original_back = + dynamic_cast (back->get_original_back_projector_ptr()); + + if (is_null_ptr(original_back)) + error("Currently only ForwardProjectorByBinUsingProjMatrixByBin supports TOF reconstruction. Abort."); + else + original_back->enable_tof(original_forward->get_tof_row()); + } + else // if is ForwardProjectorByBinUsingProjMatrixByBin directly. + { + BackProjectorByBinUsingProjMatrixByBin* original_back = + dynamic_cast (back_projector_sptr.get()); + + if (is_null_ptr(original_back)) + error("Currently only BackProjectorByBinUsingProjMatrixByBin supports TOF reconstruction. Abort."); + else + original_back->enable_tof(original_forward->get_tof_row()); + } + } + } + else // if is ForwardProjectorByBinUsingProjMatrixByBin directly. + { + ForwardProjectorByBinUsingProjMatrixByBin* original_forward = + dynamic_cast (forward_projector_sptr.get()); + + if (is_null_ptr(original_forward)) + error("Currently only ForwardProjectorByBinUsingProjMatrixByBin supports TOF reconstruction. Abort."); + else + { + original_forward->set_tof_data(_point1, _point2); + + PostsmoothingBackProjectorByBin* back= + dynamic_cast (back_projector_sptr.get()); + + if (!is_null_ptr(back)) + { + BackProjectorByBinUsingProjMatrixByBin* original_back = + dynamic_cast (back->get_original_back_projector_ptr()); + + if (is_null_ptr(original_back)) + error("Currently only ForwardProjectorByBinUsingProjMatrixByBin supports TOF reconstruction. Abort."); + else + original_back->enable_tof(original_forward->get_tof_row()); + } + else // if is ForwardProjectorByBinUsingProjMatrixByBin directly. + { + BackProjectorByBinUsingProjMatrixByBin* original_back = + dynamic_cast (back_projector_sptr.get()); + + if (is_null_ptr(original_back)) + error("Currently only BackProjectorByBinUsingProjMatrixByBin supports TOF reconstruction. Abort."); + else + original_back->enable_tof(original_forward->get_tof_row()); + } + } + } +} + +ProjMatrixElemsForOneBin* +ProjectorByBinPair:: +get_current_tof_row() const +{ + // Check if it is PresmoothingForwardProjectorByBin + PresmoothingForwardProjectorByBin* forward= + dynamic_cast (forward_projector_sptr.get()); + + if (!is_null_ptr(forward)) + { + ForwardProjectorByBinUsingProjMatrixByBin* original_forward = + dynamic_cast (forward->get_original_forward_projector_ptr()); + + if (is_null_ptr(original_forward)) + error("Currently only ForwardProjectorByBinUsingProjMatrixByBin supports TOF reconstruction. Abort."); + else + return original_forward->get_tof_row(); + } + else // if is ForwardProjectorByBinUsingProjMatrixByBin directly. + { + ForwardProjectorByBinUsingProjMatrixByBin* original_forward = + dynamic_cast (forward_projector_sptr.get()); + + if (is_null_ptr(original_forward)) + error("Currently only ForwardProjectorByBinUsingProjMatrixByBin supports TOF reconstruction. Abort."); + else + return original_forward->get_tof_row(); + } +} + //ForwardProjectorByBin const * const shared_ptr ProjectorByBinPair:: diff --git a/src/recon_buildblock/TrivialDataSymmetriesForBins.cxx b/src/recon_buildblock/TrivialDataSymmetriesForBins.cxx index a394fe7973..258451a323 100644 --- a/src/recon_buildblock/TrivialDataSymmetriesForBins.cxx +++ b/src/recon_buildblock/TrivialDataSymmetriesForBins.cxx @@ -99,12 +99,15 @@ void TrivialDataSymmetriesForBins:: get_related_bins(vector& rel_b, const Bin& b, const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) const + const int min_tangential_pos_num, const int max_tangential_pos_num, + const int min_timing_pos_num, const int max_timing_pos_num) const { if (b.axial_pos_num() >= min_axial_pos_num && b.axial_pos_num() <= max_axial_pos_num && b.tangential_pos_num() >= min_tangential_pos_num && - b.tangential_pos_num() <= max_tangential_pos_num) + b.tangential_pos_num() <= max_tangential_pos_num && + b.timing_pos_num() >= min_timing_pos_num && + b.timing_pos_num() <= max_timing_pos_num) { rel_b.resize(1); rel_b[0] = b; diff --git a/src/recon_test/test_PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx b/src/recon_test/test_PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx index 88b0321c14..9aa649eb5b 100644 --- a/src/recon_test/test_PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx +++ b/src/recon_test/test_PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx @@ -187,17 +187,22 @@ construct_input_data(shared_ptr& density_sptr) seg_num<=proj_data_sptr->get_max_segment_num(); ++seg_num) { - SegmentByView segment = proj_data_sptr->get_empty_segment_by_view(seg_num); - // fill in some crazy values - float value=0; - for (SegmentByView::full_iterator iter = segment.begin_all(); - iter != segment.end_all(); - ++iter) - { - value = float(fabs((seg_num+.1)*value - 5)); // needs to be positive for Poisson - *iter = value; - } - proj_data_sptr->set_segment(segment); + for (int timing_pos_num = proj_data_sptr->get_min_tof_pos_num(); + timing_pos_num <= proj_data_sptr->get_max_tof_pos_num(); + ++timing_pos_num) + { + SegmentByView segment = proj_data_sptr->get_empty_segment_by_view(seg_num); + // fill in some crazy values + float value=0; + for (SegmentByView::full_iterator iter = segment.begin_all(); + iter != segment.end_all(); + ++iter) + { + value = float(fabs((seg_num+.1)*value - 5)); // needs to be positive for Poisson + *iter = value; + } + proj_data_sptr->set_segment(segment); + } } } else @@ -254,17 +259,22 @@ construct_input_data(shared_ptr& density_sptr) seg_num<=proj_data_sptr->get_max_segment_num(); ++seg_num) { - SegmentByView segment = proj_data_sptr->get_empty_segment_by_view(seg_num); - // fill in some crazy values - float value =0; - for (SegmentByView::full_iterator iter = segment.begin_all(); - iter != segment.end_all(); - ++iter) - { - value = float(fabs(seg_num*value - .2)); // needs to be positive for Poisson - *iter = value; - } - mult_proj_data_sptr->set_segment(segment); + for (int timing_pos_num = proj_data_sptr->get_min_tof_pos_num(); + timing_pos_num <= proj_data_sptr->get_max_tof_pos_num(); + ++timing_pos_num) + { + SegmentByView segment = proj_data_sptr->get_empty_segment_by_view(seg_num, false, timing_pos_num); + // fill in some crazy values + float value =0; + for (SegmentByView::full_iterator iter = segment.begin_all(); + iter != segment.end_all(); + ++iter) + { + value = float(fabs(seg_num*value - .2)); // needs to be positive for Poisson + *iter = value; + } + mult_proj_data_sptr->set_segment(segment); + } } bin_norm_sptr.reset(new BinNormalisationFromProjData(mult_proj_data_sptr)); } @@ -278,17 +288,22 @@ proj_data_sptr->get_proj_data_info_ptr()->create_shared_clone())); seg_num<=proj_data_sptr->get_max_segment_num(); ++seg_num) { - SegmentByView segment = proj_data_sptr->get_empty_segment_by_view(seg_num); - // fill in some crazy values - float value =0; - for (SegmentByView::full_iterator iter = segment.begin_all(); - iter != segment.end_all(); - ++iter) - { - value = float(fabs(seg_num*value - .3)); // needs to be positive for Poisson - *iter = value; - } - add_proj_data_sptr->set_segment(segment); + for (int timing_pos_num = proj_data_sptr->get_min_tof_pos_num(); + timing_pos_num <= proj_data_sptr->get_max_tof_pos_num(); + ++timing_pos_num) + { + SegmentByView segment = proj_data_sptr->get_empty_segment_by_view(seg_num, false, timing_pos_num); + // fill in some crazy values + float value =0; + for (SegmentByView::full_iterator iter = segment.begin_all(); + iter != segment.end_all(); + ++iter) + { + value = float(fabs(seg_num*value - .3)); // needs to be positive for Poisson + *iter = value; + } + add_proj_data_sptr->set_segment(segment); + } } } diff --git a/src/test/test_proj_data_in_memory.cxx b/src/test/test_proj_data_in_memory.cxx index 9f906c50af..024885b394 100644 --- a/src/test/test_proj_data_in_memory.cxx +++ b/src/test/test_proj_data_in_memory.cxx @@ -47,11 +47,22 @@ class ProjDataInMemoryTests: public RunTests { public: void run_tests(); + void run_tests_no_tof(); + void run_tests_tof(); }; + void ProjDataInMemoryTests:: run_tests() +{ + this->run_tests_no_tof(); + this->run_tests_tof(); +} + +void +ProjDataInMemoryTests:: +run_tests_no_tof() { std::cerr << "-------- Testing ProjDataInMemory --------\n"; shared_ptr scanner_sptr(new Scanner(Scanner::E953)); @@ -133,6 +144,134 @@ run_tests() // ); +// // construct without filling +// ProjDataInMemory proj_data2(exam_info_sptr, proj_data_info_sptr2, false); +// // this should call error, so we'll catch it +// try +// { +// proj_data2.fill(proj_data); +// check(false, "test fill wtih too small proj_data should have thrown"); +// } +// catch (...) +// { +// // ok +// } + } +} + +void +ProjDataInMemoryTests:: +run_tests_tof() +{ + std::cerr << "-------- Testing ProjDataInMemory --------\n"; + shared_ptr scanner_sptr(new Scanner(Scanner::PETMR_Signa)); + + shared_ptr proj_data_info_sptr + (ProjDataInfo::ProjDataInfoCTI(scanner_sptr, + /*span*/1, 10,/*views*/ 96, /*tang_pos*/128, /*arc_corrected*/ true, 11) + ); + shared_ptr exam_info_sptr(new ExamInfo); + + + // construct with filling to 0 + ProjDataInMemory proj_data(exam_info_sptr, proj_data_info_sptr); + { + check_if_equal( proj_data.get_sinogram(0,0,false,-2).get_timing_pos_num(), + -2, + "test get_sinogram timing position index"); + Sinogram sinogram = proj_data.get_sinogram(0,0,false,-2); + check_if_equal(sinogram.get_timing_pos_num(), + -2, + "test constructor and get_sinogram timing position index"); + check_if_equal(sinogram.find_min(), + 0.F, + "test constructor and get_sinogram"); + } + + const float value = 1.2F; + // test fill(float) + { + proj_data.fill(value); + Viewgram viewgram = proj_data.get_viewgram(0,0,false,-2); + check_if_equal(viewgram.get_timing_pos_num(), + -2, + "test constructor and get_viewgram timing position index"); + check_if_equal(viewgram.find_min(), + value, + "test fill(float) and get_viewgram"); + } + + // test set_viewgram + { + Viewgram viewgram = proj_data.get_empty_viewgram(1,1,false,-2); + viewgram.fill(value*2); + check(proj_data.set_viewgram(viewgram) == Succeeded::yes, + "test set_viewgram succeeded"); + + Viewgram viewgram2 = proj_data.get_viewgram(1,1,false,-2); + check_if_equal(viewgram2.get_timing_pos_num(), + -2, + "test set/get_viewgram timing position index"); + check_if_equal(viewgram2.find_min(), + viewgram.find_min(), + "test set/get_viewgram"); + } + // test set_segment_by_view + { + SegmentByView segment = proj_data.get_empty_segment_by_view(1,false,-2); + segment.fill(value*2); + check(proj_data.set_segment(segment) == Succeeded::yes, + "test set_segment succeeded"); + + SegmentByView segment2 = proj_data.get_segment_by_view(1,-2); + check_if_equal(segment2.get_timing_pos_num(), + -2, + "test set/get_segment_by_view timing position index"); + check_if_equal(segment2.find_min(), + segment.find_min(), + "test set/get_segment_by_view"); + } + // test making a copy + { + ProjDataInMemory proj_data2(proj_data); + check_if_equal(proj_data2.get_viewgram(0,0,false,-2).find_max(), + proj_data.get_viewgram(0,0,false,-2).find_max(), + "test 1 for copy-constructor and get_viewgram"); + check_if_equal(proj_data2.get_viewgram(1,1,false,-2).find_max(), + proj_data.get_viewgram(1,1,false,-2).find_max(), + "test 1 for copy-constructor and get_viewgram"); + check_if_equal(proj_data2.get_viewgram(1,1,false,-2).get_timing_pos_num(), + -2, + "test 2 for copy-constructor and get_viewgram"); + } + + // test fill with larger input + { + shared_ptr proj_data_info_sptr2 + (ProjDataInfo::ProjDataInfoCTI(scanner_sptr, + /*span*/1, 8,/*views*/ 96, /*tang_pos*/128, /*arc_corrected*/ true) + ); + + + // construct without filling + ProjDataInMemory proj_data2(exam_info_sptr, proj_data_info_sptr2, false); +// proj_data2.fill(proj_data); +// check_if_equal(proj_data2.get_viewgram(0,0).find_max(), +// proj_data.get_viewgram(0,0).find_max(), +// "test 1 for copy-constructor and get_viewgram"); +// check_if_equal(proj_data2.get_viewgram(1,1).find_max(), +// proj_data.get_viewgram(1,1).find_max(), +// "test 1 for copy-constructor and get_viewgram"); + } + + // test fill with smaller input + { +// shared_ptr proj_data_info_sptr2 +// (ProjDataInfo::ProjDataInfoCTI(scanner_sptr, +// /*span*/1, 12,/*views*/ 96, /*tang_pos*/128, /*arc_corrected*/ true) +// ); + + // // construct without filling // ProjDataInMemory proj_data2(exam_info_sptr, proj_data_info_sptr2, false); // // this should call error, so we'll catch it diff --git a/src/utilities/forward_project.cxx b/src/utilities/forward_project.cxx index b681b8c6ef..b9b5eb312b 100644 --- a/src/utilities/forward_project.cxx +++ b/src/utilities/forward_project.cxx @@ -109,7 +109,7 @@ main (int argc, char * argv[]) ProjDataInterfile output_projdata(template_proj_data_sptr->get_exam_info_sptr(), template_proj_data_sptr->get_proj_data_info_ptr()->create_shared_clone(), output_filename); - + forw_projector_sptr->forward_project(output_projdata, *image_density_sptr); return EXIT_SUCCESS; diff --git a/src/utilities/poisson_noise.cxx b/src/utilities/poisson_noise.cxx index 10bea5d513..094bdef660 100644 --- a/src/utilities/poisson_noise.cxx +++ b/src/utilities/poisson_noise.cxx @@ -167,26 +167,31 @@ poisson_noise(ProjData& output_projdata, seg<=input_projdata.get_max_segment_num(); seg++) { - SegmentByView seg_input= input_projdata.get_segment_by_view(seg); - SegmentByView seg_output= - output_projdata.get_empty_segment_by_view(seg,false); - - cerr << "Segment " << seg << endl; - - for(int view=seg_input.get_min_view_num();view<=seg_input.get_max_view_num();view++) - for(int ax_pos=seg_input.get_min_axial_pos_num();ax_pos<=seg_input.get_max_axial_pos_num();ax_pos++) - for(int tang_pos=seg_input.get_min_tangential_pos_num();tang_pos<=seg_input.get_max_tangential_pos_num();tang_pos++) - { - const float bin = seg_input[view][ax_pos][tang_pos]; - const int random_poisson = generate_poisson_random(bin*scaling_factor); - seg_output[view][ax_pos][tang_pos] = - preserve_mean ? - random_poisson / scaling_factor - : - static_cast(random_poisson); - } - if (output_projdata.set_segment(seg_output) == Succeeded::no) - exit(EXIT_FAILURE); + 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_input= input_projdata.get_segment_by_view(seg, timing_pos_num); + SegmentByView seg_output= + output_projdata.get_empty_segment_by_view(seg,false, timing_pos_num); + + cerr << "Segment " << seg << endl; + + for(int view=seg_input.get_min_view_num();view<=seg_input.get_max_view_num();view++) + for(int ax_pos=seg_input.get_min_axial_pos_num();ax_pos<=seg_input.get_max_axial_pos_num();ax_pos++) + for(int tang_pos=seg_input.get_min_tangential_pos_num();tang_pos<=seg_input.get_max_tangential_pos_num();tang_pos++) + { + const float bin = seg_input[view][ax_pos][tang_pos]; + const int random_poisson = generate_poisson_random(bin*scaling_factor); + seg_output[view][ax_pos][tang_pos] = + preserve_mean ? + random_poisson / scaling_factor + : + static_cast(random_poisson); + } + if (output_projdata.set_segment(seg_output) == Succeeded::no) + exit(EXIT_FAILURE); + } } } diff --git a/src/utilities/stir_math.cxx b/src/utilities/stir_math.cxx index d342c8a148..522d086610 100644 --- a/src/utilities/stir_math.cxx +++ b/src/utilities/stir_math.cxx @@ -568,26 +568,32 @@ main(int argc, char **argv) segment_num <= out_proj_data_ptr->get_max_segment_num(); ++segment_num) { - if (verbose) - cout << "Processing segment num " << segment_num << " for all files" << endl; - SegmentByView segment_by_view = - (*all_proj_data[0]).get_segment_by_view(segment_num); - if (!no_math_on_data && !except_first ) - in_place_apply_function(segment_by_view, pow_times_add_object); - for (int i=1; i current_segment_by_view = - (*all_proj_data[i]).get_segment_by_view(segment_num); - if (!no_math_on_data) - in_place_apply_function(current_segment_by_view, pow_times_add_object); - if(do_add) - segment_by_view += current_segment_by_view; - else - segment_by_view *= current_segment_by_view; - } - - if (!(out_proj_data_ptr->set_segment(segment_by_view) == Succeeded::yes)) - warning("Error set_segment %d\n", segment_num); + if (verbose) + cout << "Processing segment num " << segment_num << " for all files" << endl; + + for (int k=out_proj_data_ptr->get_min_tof_pos_num(); + k<=out_proj_data_ptr->get_max_tof_pos_num(); + ++k) + { + SegmentByView segment_by_view = + (*all_proj_data[0]).get_segment_by_view(segment_num,k); + if (!no_math_on_data && !except_first ) + in_place_apply_function(segment_by_view, pow_times_add_object); + for (int i=1; i current_segment_by_view = + (*all_proj_data[i]).get_segment_by_view(segment_num,k); + if (!no_math_on_data) + in_place_apply_function(current_segment_by_view, pow_times_add_object); + if(do_add) + segment_by_view += current_segment_by_view; + else + segment_by_view *= current_segment_by_view; + } + + if (!(out_proj_data_ptr->set_segment(segment_by_view) == Succeeded::yes)) + warning("Error set_segment %d\n", segment_num); + } } } return EXIT_SUCCESS; From dcd3194bfc549e39859ab65084c0b4bc6f0472fc Mon Sep 17 00:00:00 2001 From: Elise Date: Wed, 22 Mar 2017 14:44:51 +0000 Subject: [PATCH 066/170] Fourier Rebinning: throws error for TOF data --- src/recon_buildblock/FourierRebinning.cxx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/recon_buildblock/FourierRebinning.cxx b/src/recon_buildblock/FourierRebinning.cxx index 634acc6118..1f6e69a02e 100644 --- a/src/recon_buildblock/FourierRebinning.cxx +++ b/src/recon_buildblock/FourierRebinning.cxx @@ -121,6 +121,12 @@ FourierRebinning:: rebin() { + if (proj_data_sptr->get_proj_data_info_ptr()->is_tof_data()) + { + error("FORE Rebinning :: Not supported for TOF data. Aborted"); + return Succeeded::no; + } + start_timers(); CPUTimer timer; timer.start(); From 6f61824efc83f046aee521ed9e1bd66644e8383e Mon Sep 17 00:00:00 2001 From: Elise Date: Fri, 31 Mar 2017 15:02:00 +0100 Subject: [PATCH 067/170] Fixes for TOF OSEM reconstructions + modified some test and utility functions for TOF --- src/buildblock/ArcCorrection.cxx | 37 +-- .../ProjDataInfoCylindricalArcCorr.cxx | 3 +- src/buildblock/RelatedViewgrams.cxx | 2 +- src/buildblock/SSRB.cxx | 3 + src/buildblock/zoom.cxx | 9 +- src/include/stir/Bin.inl | 2 +- .../DataSymmetriesForBins.inl | 4 +- ...elihoodWithLinearModelForMeanAndProjData.h | 8 +- .../stir/recon_buildblock/distributable.h | 3 +- .../distributableMPICacheEnabled.h | 3 +- src/recon_buildblock/BackProjectorByBin.cxx | 2 +- .../BackProjectorByBinUsingInterpolation.cxx | 4 +- ...ojectorByBinUsingSquareProjMatrixByBin.cxx | 3 +- .../BinNormalisationFromProjData.cxx | 12 +- .../ForwardProjectorByBinUsingRayTracing.cxx | 12 +- ...ihoodWithLinearModelForMeanAndProjData.cxx | 237 ++++++++++------- .../ProjMatrixByBinUsingRayTracing.cxx | 2 + .../ProjMatrixElemsForOneBin.cxx | 4 +- src/recon_buildblock/distributable.cxx | 39 +-- .../distributableMPICacheEnabled.cxx | 116 +++++---- .../distributed_functions.cxx | 12 +- .../distributed_test_functions.cxx | 2 + src/recon_test/bcktest.cxx | 163 ++++++------ src/recon_test/fwdtest.cxx | 96 ++++--- ...ihoodWithLinearModelForMeanAndProjData.cxx | 2 +- src/test/test_ArcCorrection.cxx | 42 ++- src/utilities/correct_projdata.cxx | 246 +++++++++--------- 27 files changed, 607 insertions(+), 461 deletions(-) diff --git a/src/buildblock/ArcCorrection.cxx b/src/buildblock/ArcCorrection.cxx index 57acefe493..ac1a5322f8 100644 --- a/src/buildblock/ArcCorrection.cxx +++ b/src/buildblock/ArcCorrection.cxx @@ -251,6 +251,7 @@ do_arc_correction(Sinogram& out, const Sinogram& in) const assert(*out.get_proj_data_info_ptr() == *_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]); } @@ -261,7 +262,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; } @@ -274,6 +276,7 @@ do_arc_correction(Viewgram& out, const Viewgram& in) const assert(*out.get_proj_data_info_ptr() == *_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]); } @@ -284,7 +287,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; } @@ -305,7 +309,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; } @@ -317,6 +321,7 @@ do_arc_correction(SegmentBySinogram& out, const SegmentBySinogram& assert(*in.get_proj_data_info_ptr() == *_noarc_corr_proj_data_info_sptr); assert(*out.get_proj_data_info_ptr() == *_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]); @@ -327,7 +332,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; } @@ -340,6 +345,7 @@ do_arc_correction(SegmentByView& out, const SegmentByView& in) con assert(*in.get_proj_data_info_ptr() == *_noarc_corr_proj_data_info_sptr); assert(*out.get_proj_data_info_ptr() == *_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]); @@ -351,7 +357,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; } @@ -366,16 +372,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/ProjDataInfoCylindricalArcCorr.cxx b/src/buildblock/ProjDataInfoCylindricalArcCorr.cxx index ac84029f32..15cdb8ef9f 100644 --- a/src/buildblock/ProjDataInfoCylindricalArcCorr.cxx +++ b/src/buildblock/ProjDataInfoCylindricalArcCorr.cxx @@ -66,8 +66,7 @@ ProjDataInfoCylindricalArcCorr:: ProjDataInfoCylindricalArcCorr(const shared_ptr bin_size(bin_size_v) { - // If tof_mash_factor == 1 then there is only tof bin ... effectively no TOF - if (tof_mash_factor > 1) + if (scanner_ptr->is_tof_ready()) set_tof_mash_factor(tof_mash_factor); } diff --git a/src/buildblock/RelatedViewgrams.cxx b/src/buildblock/RelatedViewgrams.cxx index 7abeadcec4..129e8449f8 100644 --- a/src/buildblock/RelatedViewgrams.cxx +++ b/src/buildblock/RelatedViewgrams.cxx @@ -374,7 +374,7 @@ grow(const IndexRange<2>& range) { iter->grow(range); *iter = Viewgram(*iter, pdi_shared_ptr, - iter->get_view_num(), iter->get_segment_num()); + iter->get_view_num(), iter->get_segment_num(),iter->get_timing_pos_num()); } check_state(); diff --git a/src/buildblock/SSRB.cxx b/src/buildblock/SSRB.cxx index 3a56290429..e096516e97 100644 --- a/src/buildblock/SSRB.cxx +++ b/src/buildblock/SSRB.cxx @@ -192,6 +192,9 @@ SSRB(ProjData& out_proj_data, const bool do_norm ) { + if (in_proj_data.get_proj_data_info_ptr()->is_tof_data()) + error("SSRB for TOF data is not currently implemented.\n"); + const ProjDataInfoCylindrical * const in_proj_data_info_ptr = dynamic_cast (in_proj_data.get_proj_data_info_ptr()); diff --git a/src/buildblock/zoom.cxx b/src/buildblock/zoom.cxx index cd80afc760..0f668004bc 100644 --- a/src/buildblock/zoom.cxx +++ b/src/buildblock/zoom.cxx @@ -133,7 +133,7 @@ zoom_viewgrams (RelatedViewgrams& in_viewgrams, out_viewgrams = new_proj_data_info_arccorr_ptr-> get_empty_related_viewgrams(in_viewgrams.get_basic_view_segment_num(), - symmetries_sptr); + symmetries_sptr,false,in_viewgrams.get_basic_timing_pos_num()); { RelatedViewgrams::iterator out_iter = out_viewgrams.begin(); @@ -176,7 +176,9 @@ zoom_viewgram (Viewgram& in_view, out_view = new_proj_data_info_arccorr_ptr-> get_empty_viewgram( in_view.get_view_num(), - in_view.get_segment_num()); + in_view.get_segment_num(), + false, + in_view.get_timing_pos_num()); zoom_viewgram(out_view, in_view, x_offset_in_mm, y_offset_in_mm); @@ -196,6 +198,9 @@ zoom_viewgram (Viewgram& out_view, assert(in_view.get_proj_data_info_ptr()->get_num_segments() == out_view.get_proj_data_info_ptr()->get_num_segments()); assert(in_view.get_segment_num() == out_view.get_segment_num()); + assert(in_view.get_proj_data_info_ptr()->get_num_tof_poss() == + out_view.get_proj_data_info_ptr()->get_num_tof_poss()); + assert(in_view.get_timing_pos_num() == out_view.get_timing_pos_num()); assert(in_view.get_min_axial_pos_num() == out_view.get_min_axial_pos_num()); assert(in_view.get_max_axial_pos_num() == out_view.get_max_axial_pos_num()); diff --git a/src/include/stir/Bin.inl b/src/include/stir/Bin.inl index 9b3872d145..d476a065a7 100644 --- a/src/include/stir/Bin.inl +++ b/src/include/stir/Bin.inl @@ -137,7 +137,7 @@ Bin::operator==(const Bin& bin2) const return segment == bin2.segment && view == bin2.view && axial_pos == bin2.axial_pos && tangential_pos == bin2.tangential_pos && -// && timing_pos == bin2.timing_pos + timing_pos == bin2.timing_pos && bin_value == bin2.bin_value; } diff --git a/src/include/stir/recon_buildblock/DataSymmetriesForBins.inl b/src/include/stir/recon_buildblock/DataSymmetriesForBins.inl index 9f56c6e730..ef3a739d4e 100644 --- a/src/include/stir/recon_buildblock/DataSymmetriesForBins.inl +++ b/src/include/stir/recon_buildblock/DataSymmetriesForBins.inl @@ -41,7 +41,9 @@ get_related_bins(std::vector& rel_b, const Bin& b) const proj_data_info_ptr->get_min_axial_pos_num(b.segment_num()), proj_data_info_ptr->get_max_axial_pos_num(b.segment_num()), proj_data_info_ptr->get_min_tangential_pos_num(), - proj_data_info_ptr->get_max_tangential_pos_num()); + proj_data_info_ptr->get_max_tangential_pos_num(), + proj_data_info_ptr->get_min_tof_pos_num(), + proj_data_info_ptr->get_max_tof_pos_num()); } diff --git a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.h b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.h index 15d2f30f7d..153420d90a 100644 --- a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.h +++ b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.h @@ -185,6 +185,7 @@ public RegisteredParsingObject& get_proj_data_sptr() const; const int get_max_segment_num_to_process() const; + const int get_max_timing_pos_num_to_process() const; const bool get_zero_seg0_end_planes() const; const ProjData& get_additive_proj_data() const; const shared_ptr& get_additive_proj_data_sptr() const; @@ -206,6 +207,7 @@ public RegisteredParsingObject&); void set_max_segment_num_to_process(const int); + void set_max_timing_pos_num_to_process(const int); void set_zero_seg0_end_planes(const bool); //N.E. Changed to ExamData virtual void set_additive_proj_data_sptr(const shared_ptr&); @@ -279,6 +281,10 @@ public RegisteredParsingObject symmetries_sptr; void - add_view_seg_to_sensitivity(TargetT& sensitivity, const ViewSegmentNumbers& view_seg_nums) const; + add_view_seg_to_sensitivity(TargetT& sensitivity, const ViewSegmentNumbers& view_seg_nums, const int timing_pos_num = 0) const; }; #ifdef STIR_MPI diff --git a/src/include/stir/recon_buildblock/distributable.h b/src/include/stir/recon_buildblock/distributable.h index 4b34075e48..ca3f3ede75 100644 --- a/src/include/stir/recon_buildblock/distributable.h +++ b/src/include/stir/recon_buildblock/distributable.h @@ -181,7 +181,8 @@ void distributable_computation( const double start_time_of_frame, const double end_time_of_frame, RPC_process_related_viewgrams_type * RPC_process_related_viewgrams, - DistributedCachingInformation* caching_info_ptr); + DistributedCachingInformation* caching_info_ptr, + int min_timing_pos_num = 0, int max_timing_pos_num = 0); /*! \name Tag-names currently used by stir::distributable_computation and related functions0 diff --git a/src/include/stir/recon_buildblock/distributableMPICacheEnabled.h b/src/include/stir/recon_buildblock/distributableMPICacheEnabled.h index fc0ecd046b..eee7658863 100644 --- a/src/include/stir/recon_buildblock/distributableMPICacheEnabled.h +++ b/src/include/stir/recon_buildblock/distributableMPICacheEnabled.h @@ -73,7 +73,8 @@ void distributable_computation_cache_enabled( const double start_time_of_frame, const double end_time_of_frame, RPC_process_related_viewgrams_type * RPC_process_related_viewgrams, - DistributedCachingInformation* caching_info_ptr + DistributedCachingInformation* caching_info_ptr, + int min_timing_pos_num = 0, int max_timing_pos_num = 0 ); diff --git a/src/recon_buildblock/BackProjectorByBin.cxx b/src/recon_buildblock/BackProjectorByBin.cxx index f606ab72d1..5789bb3ea1 100644 --- a/src/recon_buildblock/BackProjectorByBin.cxx +++ b/src/recon_buildblock/BackProjectorByBin.cxx @@ -88,7 +88,7 @@ BackProjectorByBin::back_project(DiscretisedDensity<3,float>& image, #ifdef STIR_OPENMP RelatedViewgrams viewgrams; #pragma omp critical (BACKPROJECTORBYBIN_GETVIEWGRAMS) - viewgrams = proj_data.get_related_viewgrams(vs, symmetries_sptr); + viewgrams = proj_data.get_related_viewgrams(vs, symmetries_sptr, false, k); #else const RelatedViewgrams viewgrams = proj_data.get_related_viewgrams(vs, symmetries_sptr, false, k); diff --git a/src/recon_buildblock/BackProjectorByBinUsingInterpolation.cxx b/src/recon_buildblock/BackProjectorByBinUsingInterpolation.cxx index 3345e9bce9..0ee3950e3d 100644 --- a/src/recon_buildblock/BackProjectorByBinUsingInterpolation.cxx +++ b/src/recon_buildblock/BackProjectorByBinUsingInterpolation.cxx @@ -677,7 +677,7 @@ can only handle arc-corrected data (cast to ProjDataInfoCylindricalArcCorr)!\n") Array<4, float > Proj2424(IndexRange4D(0, 1, 0, 3, 0, 1, 1, 4)); // a variable which will be used in the loops over s to get s_in_mm - Bin bin(pos_view.get_segment_num(), pos_view.get_view_num(),min_axial_pos_num,0); + Bin bin(pos_view.get_segment_num(), pos_view.get_view_num(),min_axial_pos_num,pos_view.get_timing_pos_num(),0); // KT 20/06/2001 rewrite using get_phi const float cphi = cos(proj_data_info_cyl_ptr->get_phi(bin)); @@ -881,7 +881,7 @@ can only handle arc-corrected data (cast to ProjDataInfoCylindricalArcCorr)!\n") Array<4, float > Proj2424(IndexRange4D(0, 1, 0, 3, 0, 1, 1, 4)); // a variable which will be used in the loops over s to get s_in_mm - Bin bin(pos_view.get_segment_num(), pos_view.get_view_num(),min_axial_pos_num,0); + Bin bin(pos_view.get_segment_num(), pos_view.get_view_num(),min_axial_pos_num,0,pos_view.get_timing_pos_num()); // compute cos(phi) and sin(phi) /* KT included special cases for phi=0 and 90 degrees to try to avoid rounding problems diff --git a/src/recon_buildblock/BackProjectorByBinUsingSquareProjMatrixByBin.cxx b/src/recon_buildblock/BackProjectorByBinUsingSquareProjMatrixByBin.cxx index 5ccd4f7745..2f91dcc9c1 100644 --- a/src/recon_buildblock/BackProjectorByBinUsingSquareProjMatrixByBin.cxx +++ b/src/recon_buildblock/BackProjectorByBinUsingSquareProjMatrixByBin.cxx @@ -87,12 +87,13 @@ actual_back_project(DiscretisedDensity<3,float>& image, const Viewgram& viewgram = *r_viewgrams_iter; const int view_num = viewgram.get_view_num(); const int segment_num = viewgram.get_segment_num(); + const int timing_pos_num = viewgram.get_timing_pos_num(); for ( int tang_pos = min_tangential_pos_num ;tang_pos <= max_tangential_pos_num ;++tang_pos) for ( int ax_pos = min_axial_pos_num; ax_pos <= max_axial_pos_num ;++ax_pos) { - Bin bin(segment_num, view_num, ax_pos, tang_pos, viewgram[ax_pos][tang_pos]); + Bin bin(segment_num, view_num, ax_pos, tang_pos, timing_pos_num, viewgram[ax_pos][tang_pos]); proj_matrix_ptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, bin); ProjMatrixElemsForOneBin::iterator element_ptr = proj_matrix_row.begin(); diff --git a/src/recon_buildblock/BinNormalisationFromProjData.cxx b/src/recon_buildblock/BinNormalisationFromProjData.cxx index 57449e6346..8e8c38e45e 100644 --- a/src/recon_buildblock/BinNormalisationFromProjData.cxx +++ b/src/recon_buildblock/BinNormalisationFromProjData.cxx @@ -98,7 +98,11 @@ set_up(const shared_ptr& proj_data_info_ptr) (norm_proj.get_min_tangential_pos_num() ==proj.get_min_tangential_pos_num())&& (norm_proj.get_max_tangential_pos_num() ==proj.get_max_tangential_pos_num()) && norm_proj.get_min_segment_num() <= proj.get_min_segment_num() && - norm_proj.get_max_segment_num() >= proj.get_max_segment_num(); + norm_proj.get_max_segment_num() >= proj.get_max_segment_num() && + ((norm_proj.get_min_tof_pos_num() <= proj.get_min_tof_pos_num() && + norm_proj.get_max_tof_pos_num() >= proj.get_max_tof_pos_num()) + || + !norm_proj.is_tof_data()) ; for (int segment_num=proj.get_min_segment_num(); ok && segment_num<=proj.get_max_segment_num(); @@ -147,9 +151,10 @@ void BinNormalisationFromProjData::apply(RelatedViewgrams& viewgrams,const double start_time, const double end_time) const { const ViewSegmentNumbers vs_num=viewgrams.get_basic_view_segment_num(); + const int timing_pos_num = norm_proj_data_ptr->get_proj_data_info_sptr()->is_tof_data() ? viewgrams.get_basic_timing_pos_num() : 0; shared_ptr symmetries_sptr(viewgrams.get_symmetries_ptr()->clone()); viewgrams *= - norm_proj_data_ptr->get_related_viewgrams(vs_num,symmetries_sptr, false); + norm_proj_data_ptr->get_related_viewgrams(vs_num,symmetries_sptr, false,timing_pos_num); } void @@ -157,9 +162,10 @@ BinNormalisationFromProjData:: undo(RelatedViewgrams& viewgrams,const double start_time, const double end_time) const { const ViewSegmentNumbers vs_num=viewgrams.get_basic_view_segment_num(); + const int timing_pos_num = norm_proj_data_ptr->get_proj_data_info_sptr()->is_tof_data() ? viewgrams.get_basic_timing_pos_num() : 0; shared_ptr symmetries_sptr(viewgrams.get_symmetries_ptr()->clone()); viewgrams /= - norm_proj_data_ptr->get_related_viewgrams(vs_num,symmetries_sptr, false); + norm_proj_data_ptr->get_related_viewgrams(vs_num,symmetries_sptr, false, timing_pos_num); } diff --git a/src/recon_buildblock/ForwardProjectorByBinUsingRayTracing.cxx b/src/recon_buildblock/ForwardProjectorByBinUsingRayTracing.cxx index 9bf0c7e040..4bcf659fdc 100644 --- a/src/recon_buildblock/ForwardProjectorByBinUsingRayTracing.cxx +++ b/src/recon_buildblock/ForwardProjectorByBinUsingRayTracing.cxx @@ -152,18 +152,12 @@ set_up(const shared_ptr& proj_data_info_ptr, segment_num <= proj_data_info_ptr->get_max_segment_num(); ++segment_num) { -// for (int k = proj_data_info_ptr->get_min_tof_pos_num(); -// k <= proj_data_info_ptr->get_max_tof_pos_num(); -// ++k) -// { - //TODO Check symmetries here, should we add TOF? const float num_planes_per_axial_pos = symmetries_ptr->get_num_planes_per_axial_pos(segment_num); if (fabs(round(num_planes_per_axial_pos) - num_planes_per_axial_pos) > 1.E-4) error("ForwardProjectorByBinUsingRayTracing: the number of image planes " "per axial_pos (which is %g for segment %d) should be an integer\n", num_planes_per_axial_pos, segment_num); -// } } @@ -427,6 +421,7 @@ forward_project_all_symmetries( const int nviews = pos_view.get_proj_data_info_ptr()->get_num_views(); const int segment_num = pos_view.get_segment_num(); + const int timing_pos_num = pos_view.get_timing_pos_num(); const float delta = proj_data_info_ptr->get_average_ring_difference(segment_num); const int view = pos_view.get_view_num(); @@ -460,7 +455,7 @@ forward_project_all_symmetries( const float R = proj_data_info_ptr->get_ring_radius(); // a variable which will be used in the loops over tang_pos_num to get s_in_mm - Bin bin(pos_view.get_segment_num(), pos_view.get_view_num(),min_ax_pos_num,0); + Bin bin(pos_view.get_segment_num(), pos_view.get_view_num(),min_ax_pos_num,0,pos_view.get_timing_pos_num()); // KT 20/06/2001 rewrote using get_phi const float cphi = cos(proj_data_info_ptr->get_phi(bin)); @@ -1130,6 +1125,7 @@ forward_project_all_symmetries_2D(Viewgram & pos_view, const int nviews = pos_view.get_proj_data_info_ptr()->get_num_views(); const int segment_num = pos_view.get_segment_num(); + const int timing_pos_num = pos_view.get_timing_pos_num(); const float delta = proj_data_info_ptr->get_average_ring_difference(segment_num); const int view = pos_view.get_view_num(); @@ -1158,7 +1154,7 @@ forward_project_all_symmetries_2D(Viewgram & pos_view, const float R = proj_data_info_ptr->get_ring_radius(); // a variable which will be used in the loops over tang_pos_num to get s_in_mm - Bin bin(pos_view.get_segment_num(), pos_view.get_view_num(),min_axial_pos_num,0); + Bin bin(pos_view.get_segment_num(), pos_view.get_view_num(),min_axial_pos_num,0,pos_view.get_timing_pos_num()); // KT 20/06/2001 rewrote using get_phi const float cphi = cos(proj_data_info_ptr->get_phi(bin)); diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx index 8575180304..64ae469621 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx @@ -96,6 +96,7 @@ set_defaults() this->input_filename=""; this->max_segment_num_to_process=-1; + this->max_timing_pos_num_to_process=0; // KT 20/06/2001 disabled //num_views_to_add=1; this->proj_data_sptr.reset(); //MJ added @@ -113,7 +114,7 @@ set_defaults() shared_ptr PM(new ProjMatrixByBinUsingRayTracing()); // PM->set_num_tangential_LORs(5); shared_ptr forward_projector_ptr(new ForwardProjectorByBinUsingProjMatrixByBin(PM)); - shared_ptr back_projector_ptr(new BackProjectorByBinUsingProjMatrixByBin(PM)); + shared_ptr back_projector_ptr(new BackProjectorByBinUsingProjMatrixByBin(PM)); #endif this->projector_pair_ptr.reset( @@ -356,6 +357,12 @@ PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: get_max_segment_num_to_process() const { return this->max_segment_num_to_process; } +template +const int +PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: +get_max_timing_pos_num_to_process() const +{ return this->max_timing_pos_num_to_process; } + template const bool PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: @@ -439,7 +446,14 @@ PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: set_max_segment_num_to_process(const int arg) { this->max_segment_num_to_process = arg; +} +template +void +PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: +set_max_timing_pos_num_to_process(const int arg) +{ + this->max_timing_pos_num_to_process = arg; } template @@ -518,15 +532,15 @@ actual_subsets_are_approximately_balanced(std::string& warning_message) const for (int segment_num = -this->max_segment_num_to_process; segment_num <= this->max_segment_num_to_process; ++segment_num) - for (int view_num = this->proj_data_sptr->get_min_view_num() + subset_num; - view_num <= this->proj_data_sptr->get_max_view_num(); - view_num += this->num_subsets) - { - const ViewSegmentNumbers view_segment_num(view_num, segment_num); - if (!symmetries.is_basic(view_segment_num)) - continue; - num_vs_in_subset[subset_num] += - symmetries.num_related_view_segment_numbers(view_segment_num); + for (int view_num = this->proj_data_sptr->get_min_view_num() + subset_num; + view_num <= this->proj_data_sptr->get_max_view_num(); + view_num += this->num_subsets) + { + const ViewSegmentNumbers view_segment_num(view_num, segment_num); + if (!symmetries.is_basic(view_segment_num)) + continue; + num_vs_in_subset[subset_num] += + symmetries.num_related_view_segment_numbers(view_segment_num); } } for (int subset_num=1; subset_numnum_subsets; ++subset_num) @@ -571,12 +585,15 @@ set_up_before_sensitivity(shared_ptr const& target_sptr) return Succeeded::no; } + this->max_timing_pos_num_to_process = + this->proj_data_sptr->get_max_tof_pos_num(); + shared_ptr proj_data_info_sptr(this->proj_data_sptr->get_proj_data_info_ptr()->clone()); proj_data_info_sptr-> reduce_segment_range(-this->max_segment_num_to_process, +this->max_segment_num_to_process); - + if (is_null_ptr(this->projector_pair_ptr)) { warning("You need to specify a projector pair"); return Succeeded::no; } @@ -659,7 +676,9 @@ compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, this->zero_seg0_end_planes!=0, NULL, this->additive_proj_data_sptr - , caching_info_ptr + , caching_info_ptr, + -this->max_timing_pos_num_to_process, + this->max_timing_pos_num_to_process ); @@ -687,7 +706,9 @@ actual_compute_objective_function_without_penalty(const TargetT& current_estimat this->normalisation_sptr, this->get_time_frame_definitions().get_start_time(this->get_time_frame_num()), this->get_time_frame_definitions().get_end_time(this->get_time_frame_num()), - this->caching_info_ptr + this->caching_info_ptr, + -this->max_timing_pos_num_to_process, + this->max_timing_pos_num_to_process ); @@ -703,29 +724,32 @@ sum_projection_data() const float counts=0.0F; - for (int segment_num = -max_segment_num_to_process; segment_num <= max_segment_num_to_process; segment_num++) + for (int segment_num = -max_segment_num_to_process; segment_num <= max_segment_num_to_process; ++segment_num) { - for (int view_num = proj_data_sptr->get_min_view_num(); - view_num <= proj_data_sptr->get_max_view_num(); - ++view_num) - { - - Viewgram viewgram=proj_data_sptr->get_viewgram(view_num,segment_num); - - //first adjust data - - // KT 05/07/2000 made parameters.zero_seg0_end_planes int - if(segment_num==0 && zero_seg0_end_planes!=0) - { - viewgram[viewgram.get_min_axial_pos_num()].fill(0); - viewgram[viewgram.get_max_axial_pos_num()].fill(0); - } - - truncate_rim(viewgram,rim_truncation_sino); - - //now take totals - counts+=viewgram.sum(); - } + for (int timing_pos_num = -max_timing_pos_num_to_process; timing_pos_num <= max_timing_pos_num_to_process; ++timing_pos_num) + { + for (int view_num = proj_data_sptr->get_min_view_num(); + view_num <= proj_data_sptr->get_max_view_num(); + ++view_num) + { + + Viewgram viewgram=proj_data_sptr->get_viewgram(view_num,segment_num,false,timing_pos_num); + + //first adjust data + + // KT 05/07/2000 made parameters.zero_seg0_end_planes int + if(segment_num==0 && zero_seg0_end_planes!=0) + { + viewgram[viewgram.get_min_axial_pos_num()].fill(0); + viewgram[viewgram.get_max_axial_pos_num()].fill(0); + } + + truncate_rim(viewgram,rim_truncation_sino); + + //now take totals + counts+=viewgram.sum(); + } + } } return counts; @@ -741,24 +765,29 @@ add_subset_sensitivity(TargetT& sensitivity, const int subset_num) const { const int min_segment_num = -this->max_segment_num_to_process; const int max_segment_num = this->max_segment_num_to_process; + const int min_timing_pos_num = -this->max_timing_pos_num_to_process; + const int max_timing_pos_num = this->max_timing_pos_num_to_process; // warning: has to be same as subset scheme used as in distributable_computation - for (int segment_num = min_segment_num; segment_num <= max_segment_num; ++segment_num) + for (int timing_pos_num = min_timing_pos_num; timing_pos_num <= max_timing_pos_num; ++timing_pos_num) { - //CPUTimer timer; - //timer.start(); - - for (int view = this->proj_data_sptr->get_min_view_num() + subset_num; - view <= this->proj_data_sptr->get_max_view_num(); - view += this->num_subsets) - { - const ViewSegmentNumbers view_segment_num(view, segment_num); - - if (!symmetries_sptr->is_basic(view_segment_num)) - continue; - this->add_view_seg_to_sensitivity(sensitivity, view_segment_num); - } - // cerr<proj_data_sptr->get_min_view_num() + subset_num; + view <= this->proj_data_sptr->get_max_view_num(); + view += this->num_subsets) + { + const ViewSegmentNumbers view_segment_num(view, segment_num); + + if (!symmetries_sptr->is_basic(view_segment_num)) + continue; + this->add_view_seg_to_sensitivity(sensitivity, view_segment_num, timing_pos_num); + } + // cerr< void PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -add_view_seg_to_sensitivity(TargetT& sensitivity, const ViewSegmentNumbers& view_seg_nums) const +add_view_seg_to_sensitivity(TargetT& sensitivity, const ViewSegmentNumbers& view_seg_nums , const int timing_pos_num) const { - RelatedViewgrams viewgrams = - this->proj_data_sptr->get_empty_related_viewgrams(view_seg_nums, - this->symmetries_sptr); + RelatedViewgrams viewgrams = + this->proj_data_sptr->get_empty_related_viewgrams(view_seg_nums, + this->symmetries_sptr, + false, + timing_pos_num); viewgrams.fill(1.F); // find efficiencies { @@ -788,9 +819,8 @@ add_view_seg_to_sensitivity(TargetT& sensitivity, const ViewSegmentNumbers& view const int max_ax_pos_num = viewgrams.get_max_axial_pos_num() - range_to_zero; - this->projector_pair_ptr->get_back_projector_sptr()-> - back_project(sensitivity, viewgrams, - min_ax_pos_num, max_ax_pos_num); + projector_pair_ptr->get_back_projector_sptr()->back_project(sensitivity, viewgrams, + min_ax_pos_num, max_ax_pos_num); } } @@ -827,44 +857,49 @@ actual_add_multiplication_with_approximate_sub_Hessian_without_penalty(TargetT& for (int segment_num = -this->get_max_segment_num_to_process(); segment_num<= this->get_max_segment_num_to_process(); ++segment_num) - { - for (int view = this->get_proj_data().get_min_view_num() + subset_num; - view <= this->get_proj_data().get_max_view_num(); - view += this->num_subsets) - { - const ViewSegmentNumbers view_segment_num(view, segment_num); - - if (!symmetries_sptr->is_basic(view_segment_num)) - continue; - - // first compute data-term: y*norm^2 - RelatedViewgrams viewgrams = - this->get_proj_data().get_related_viewgrams(view_segment_num, symmetries_sptr); - // TODO add 1 for 1/(y+1) approximation - - this->get_normalisation().apply(viewgrams, start_time, end_time); - - // smooth TODO - - this->get_normalisation().apply(viewgrams, start_time, end_time); - - RelatedViewgrams tmp_viewgrams; - // set tmp_viewgrams to geometric forward projection of input - { - tmp_viewgrams = this->get_proj_data().get_empty_related_viewgrams(view_segment_num, symmetries_sptr); - this->get_projector_pair().get_forward_projector_sptr()-> - forward_project(tmp_viewgrams, input); - } - - // now divide by the data term - { - int tmp1=0, tmp2=0;// ignore counters returned by divide_and_truncate - divide_and_truncate(tmp_viewgrams, viewgrams, 0, tmp1, tmp2); - } - - // back-project - this->get_projector_pair().get_back_projector_sptr()-> - back_project(output, tmp_viewgrams); + { + for (int timing_pos_num = -this->get_max_timing_pos_num_to_process(); + timing_pos_num<= this->get_max_timing_pos_num_to_process(); + ++timing_pos_num) + { + for (int view = this->get_proj_data().get_min_view_num() + subset_num; + view <= this->get_proj_data().get_max_view_num(); + view += this->num_subsets) + { + const ViewSegmentNumbers view_segment_num(view, segment_num); + + if (!symmetries_sptr->is_basic(view_segment_num)) + continue; + + // first compute data-term: y*norm^2 + RelatedViewgrams viewgrams = + this->get_proj_data().get_related_viewgrams(view_segment_num, symmetries_sptr, false, timing_pos_num); + // TODO add 1 for 1/(y+1) approximation + + this->get_normalisation().apply(viewgrams, start_time, end_time); + + // smooth TODO + + this->get_normalisation().apply(viewgrams, start_time, end_time); + + RelatedViewgrams tmp_viewgrams; + // set tmp_viewgrams to geometric forward projection of input + { + tmp_viewgrams = this->get_proj_data().get_empty_related_viewgrams(view_segment_num, symmetries_sptr, false, timing_pos_num); + this->get_projector_pair().get_forward_projector_sptr()-> + forward_project(tmp_viewgrams, input); + } + + // now divide by the data term + { + int tmp1=0, tmp2=0;// ignore counters returned by divide_and_truncate + divide_and_truncate(tmp_viewgrams, viewgrams, 0, tmp1, tmp2); + } + + // back-project + this->get_projector_pair().get_back_projector_sptr()-> + back_project(output, tmp_viewgrams); + } } } // end of loop over segments @@ -902,7 +937,8 @@ void distributable_compute_gradient(const shared_ptr& for bool zero_seg0_end_planes, double* log_likelihood_ptr, shared_ptr const& additive_binwise_correction, - DistributedCachingInformation* caching_info_ptr + DistributedCachingInformation* caching_info_ptr, + int min_timing_pos_num, int max_timing_pos_num ) { @@ -918,7 +954,8 @@ void distributable_compute_gradient(const shared_ptr& for additive_binwise_correction, /* normalisation info to be ignored */ shared_ptr(), 0., 0., &RPC_process_related_viewgrams_gradient, - caching_info_ptr + caching_info_ptr, + min_timing_pos_num, max_timing_pos_num ); } @@ -937,7 +974,8 @@ void distributable_accumulate_loglikelihood( shared_ptr const& normalisation_sptr, const double start_time_of_frame, const double end_time_of_frame, - DistributedCachingInformation* caching_info_ptr + DistributedCachingInformation* caching_info_ptr, + int min_timing_pos_num, int max_timing_pos_num ) { @@ -955,7 +993,8 @@ void distributable_accumulate_loglikelihood( start_time_of_frame, end_time_of_frame, &RPC_process_related_viewgrams_accumulate_loglikelihood, - caching_info_ptr + caching_info_ptr, + min_timing_pos_num, max_timing_pos_num ); } diff --git a/src/recon_buildblock/ProjMatrixByBinUsingRayTracing.cxx b/src/recon_buildblock/ProjMatrixByBinUsingRayTracing.cxx index 0702526c1f..1935bcb8ad 100644 --- a/src/recon_buildblock/ProjMatrixByBinUsingRayTracing.cxx +++ b/src/recon_buildblock/ProjMatrixByBinUsingRayTracing.cxx @@ -542,6 +542,8 @@ calculate_proj_matrix_elems_for_one_bin( } const Bin bin = lor.get_bin(); + assert(bin.timing_pos_num() >= proj_data_info_ptr->get_min_tof_pos_num()); + assert(bin.timing_pos_num() <= proj_data_info_ptr->get_max_tof_pos_num()); assert(bin.segment_num() >= proj_data_info_ptr->get_min_segment_num()); assert(bin.segment_num() <= proj_data_info_ptr->get_max_segment_num()); diff --git a/src/recon_buildblock/ProjMatrixElemsForOneBin.cxx b/src/recon_buildblock/ProjMatrixElemsForOneBin.cxx index 5bb369b30f..81c94279c5 100644 --- a/src/recon_buildblock/ProjMatrixElemsForOneBin.cxx +++ b/src/recon_buildblock/ProjMatrixElemsForOneBin.cxx @@ -128,9 +128,9 @@ Succeeded ProjMatrixElemsForOneBin::check_state() const { if (value_type::coordinates_equal(*lor_iter, *(lor_iter+1))) { - warning("ProjMatrixElemsForOneBin: coordinates occur more than once %d,%d,%d for bin s=%d, v=%d, a=%d, t=%d\n", + warning("ProjMatrixElemsForOneBin: coordinates occur more than once %d,%d,%d for bin s=%d, tofbin=%d, v=%d, a=%d, t=%d\n", lor_iter->coord1(), lor_iter->coord2(), lor_iter->coord3(), - bin.segment_num(), bin.view_num(), + bin.segment_num(), bin.timing_pos_num(), bin.view_num(), bin.axial_pos_num(), bin.tangential_pos_num()); #if 0 const_iterator iter = begin(); diff --git a/src/recon_buildblock/distributable.cxx b/src/recon_buildblock/distributable.cxx index f5a2e79d63..5df9a65805 100644 --- a/src/recon_buildblock/distributable.cxx +++ b/src/recon_buildblock/distributable.cxx @@ -173,7 +173,8 @@ void get_viewgrams(shared_ptr >& y, const double start_time_of_frame, const double end_time_of_frame, const shared_ptr& symmetries_ptr, - const ViewSegmentNumbers& view_segment_num + const ViewSegmentNumbers& view_segment_num, + const int timing_pos_num ) { if (!is_null_ptr(binwise_correction)) @@ -184,10 +185,10 @@ void get_viewgrams(shared_ptr >& y, #if !defined(_MSC_VER) || _MSC_VER>1300 additive_binwise_correction_viewgrams.reset( new RelatedViewgrams - (binwise_correction->get_related_viewgrams(view_segment_num, symmetries_ptr))); + (binwise_correction->get_related_viewgrams(view_segment_num, symmetries_ptr, false, timing_pos_num))); #else RelatedViewgrams tmp(binwise_correction-> - get_related_viewgrams(view_segment_num, symmetries_ptr)); + get_related_viewgrams(view_segment_num, symmetries_ptr, false, timing_pos_num)); additive_binwise_correction_viewgrams.reset(new RelatedViewgrams(tmp)); #endif } @@ -199,25 +200,25 @@ void get_viewgrams(shared_ptr >& y, #endif #if !defined(_MSC_VER) || _MSC_VER>1300 y.reset(new RelatedViewgrams - (proj_dat_ptr->get_related_viewgrams(view_segment_num, symmetries_ptr))); + (proj_dat_ptr->get_related_viewgrams(view_segment_num, symmetries_ptr, false, timing_pos_num))); #else // workaround VC++ 6.0 bug RelatedViewgrams tmp(proj_dat_ptr-> - get_related_viewgrams(view_segment_num, symmetries_ptr)); + get_related_viewgrams(view_segment_num, symmetries_ptr, false, timing_pos_num)); y.reset(new RelatedViewgrams(tmp)); #endif } else { y.reset(new RelatedViewgrams - (proj_dat_ptr->get_empty_related_viewgrams(view_segment_num, symmetries_ptr))); + (proj_dat_ptr->get_empty_related_viewgrams(view_segment_num, symmetries_ptr, false, timing_pos_num))); } // multiplicative correction if (!is_null_ptr(normalisation_sptr) && !normalisation_sptr->is_trivial()) { mult_viewgrams_sptr.reset( - new RelatedViewgrams(proj_dat_ptr->get_empty_related_viewgrams(view_segment_num, symmetries_ptr))); + new RelatedViewgrams(proj_dat_ptr->get_empty_related_viewgrams(view_segment_num, symmetries_ptr, false, timing_pos_num))); mult_viewgrams_sptr->fill(1.F); #ifdef STIR_OPENMP #pragma omp critical(MULT) @@ -240,6 +241,7 @@ void send_viewgrams(const shared_ptr >& y, const int next_receiver) { distributed::send_view_segment_numbers( y->get_basic_view_segment_num(), NEW_VIEWGRAM_TAG, next_receiver); + distributed::send_int_value( y->get_timing_pos_num(), next_receiver); #ifndef NDEBUG //test sending related viegrams @@ -294,7 +296,8 @@ void distributable_computation( const double start_time_of_frame, const double end_time_of_frame, RPC_process_related_viewgrams_type * RPC_process_related_viewgrams, - DistributedCachingInformation* caching_info_ptr) + DistributedCachingInformation* caching_info_ptr, + int min_timing_pos_num, int max_timing_pos_num) { #ifdef STIR_MPI @@ -334,7 +337,8 @@ void distributable_computation( start_time_of_frame, end_time_of_frame, RPC_process_related_viewgrams, - caching_info_ptr); + caching_info_ptr, + min_timing_pos_num, max_timing_pos_num); return; } @@ -374,6 +378,7 @@ void distributable_computation( wall_clock_timer.start(); assert(min_segment_num <= max_segment_num); + assert(min_timing_pos_num <= max_timing_pos_num); assert(subset_num >=0); assert(subset_num < num_subsets); @@ -423,6 +428,9 @@ void distributable_computation( } #pragma omp for schedule(runtime) #endif + + for (int timing_pos_num = min_timing_pos_num; timing_pos_num <= max_timing_pos_num; ++timing_pos_num) + { // note: older versions of openmp need an int as loop for (int i=0; i(vs_nums_to_process.size()); ++i) { @@ -437,7 +445,7 @@ void distributable_computation( zero_seg0_end_planes, binwise_correction, normalisation_sptr, start_time_of_frame, end_time_of_frame, - symmetries_ptr, view_segment_num); + symmetries_ptr, view_segment_num, timing_pos_num); #ifdef STIR_MPI //send viewgrams, the slave will immediatelly start calculation @@ -465,12 +473,12 @@ void distributable_computation( #ifdef STIR_OPENMP const int thread_num=omp_get_thread_num(); - info(boost::format("Thread %d/%d calculating segment_num: %d, view_num: %d") + info(boost::format("Thread %d/%d calculating segment_num: %d, view_num: %d, timing_pos_num: %d") % thread_num % omp_get_num_threads() - % view_segment_num.segment_num() % view_segment_num.view_num()); + % view_segment_num.segment_num() % view_segment_num.view_num() % timing_pos_num); #else - info(boost::format("calculating segment_num: %d, view_num: %d") - % view_segment_num.segment_num() % view_segment_num.view_num()); + info(boost::format("calculating segment_num: %d, view_num: %d, timing_pos_num: %d") + % view_segment_num.segment_num() % view_segment_num.view_num() % timing_pos_num); #endif #ifdef STIR_OPENMP if (output_image_ptr != NULL) @@ -495,7 +503,8 @@ void distributable_computation( mult_viewgrams_sptr.get()); #endif // OPENMP #endif // MPI - } // end of for-loop + } // end of for-loop + } // end of for-loop over timing_pos_num } // end of parallel section of openmp #ifdef STIR_OPENMP diff --git a/src/recon_buildblock/distributableMPICacheEnabled.cxx b/src/recon_buildblock/distributableMPICacheEnabled.cxx index b89b117734..c3343f7fcc 100644 --- a/src/recon_buildblock/distributableMPICacheEnabled.cxx +++ b/src/recon_buildblock/distributableMPICacheEnabled.cxx @@ -92,7 +92,8 @@ void get_viewgrams(shared_ptr >& y, const double start_time_of_frame, const double end_time_of_frame, const shared_ptr& symmetries_ptr, - const ViewSegmentNumbers& view_segment_num + const ViewSegmentNumbers& view_segment_num, + const int timing_pos_num ) { if (!is_null_ptr(binwise_correction)) @@ -100,10 +101,10 @@ void get_viewgrams(shared_ptr >& y, #if !defined(_MSC_VER) || _MSC_VER>1300 additive_binwise_correction_viewgrams.reset( new RelatedViewgrams - (binwise_correction->get_related_viewgrams(view_segment_num, symmetries_ptr))); + (binwise_correction->get_related_viewgrams(view_segment_num, symmetries_ptr, false, timing_pos_num))); #else RelatedViewgrams tmp(binwise_correction-> - get_related_viewgrams(view_segment_num, symmetries_ptr)); + get_related_viewgrams(view_segment_num, symmetries_ptr, false, timing_pos_num)); additive_binwise_correction_viewgrams = new RelatedViewgrams(tmp); #endif } @@ -112,25 +113,25 @@ void get_viewgrams(shared_ptr >& y, { #if !defined(_MSC_VER) || _MSC_VER>1300 y.reset(new RelatedViewgrams - (proj_dat_ptr->get_related_viewgrams(view_segment_num, symmetries_ptr))); + (proj_dat_ptr->get_related_viewgrams(view_segment_num, symmetries_ptr, false, timing_pos_num))); #else // workaround VC++ 6.0 bug RelatedViewgrams tmp(proj_dat_ptr-> - get_related_viewgrams(view_segment_num, symmetries_ptr)); + get_related_viewgrams(view_segment_num, symmetries_ptr, false, timing_pos_num)); y = new RelatedViewgrams(tmp); #endif } else { y.reset(new RelatedViewgrams - (proj_dat_ptr->get_empty_related_viewgrams(view_segment_num, symmetries_ptr))); + (proj_dat_ptr->get_empty_related_viewgrams(view_segment_num, symmetries_ptr, false, timing_pos_num))); } // multiplicative correction if (!is_null_ptr(normalisation_sptr) && !normalisation_sptr->is_trivial()) { mult_viewgrams_sptr.reset( - new RelatedViewgrams(proj_dat_ptr->get_empty_related_viewgrams(view_segment_num, symmetries_ptr))); + new RelatedViewgrams(proj_dat_ptr->get_empty_related_viewgrams(view_segment_num, symmetries_ptr, false, timing_pos_num))); mult_viewgrams_sptr->fill(1.F); normalisation_sptr->undo(*mult_viewgrams_sptr,start_time_of_frame,end_time_of_frame); } @@ -149,6 +150,7 @@ static void send_viewgrams(const shared_ptr >& y, const int next_receiver) { distributed::send_view_segment_numbers( y->get_basic_view_segment_num(), NEW_VIEWGRAM_TAG, next_receiver); + distributed::send_int_value( y->get_basic_timing_pos_num(), next_receiver); #ifndef NDEBUG //test sending related viewgrams @@ -202,7 +204,8 @@ void distributable_computation_cache_enabled( const double start_time_of_frame, const double end_time_of_frame, RPC_process_related_viewgrams_type * RPC_process_related_viewgrams, - DistributedCachingInformation* caching_info_ptr + DistributedCachingInformation* caching_info_ptr, + int min_timing_pos_num, int max_timing_pos_num ) { //test distributed functions (see DistributedTestFunctions.h for details) @@ -232,6 +235,7 @@ void distributable_computation_cache_enabled( distributed::send_bool_value(!is_null_ptr(output_image_ptr),USE_OUTPUT_IMAGE_ARG_TAG,-1); assert(min_segment_num <= max_segment_num); + assert(min_timing_pos_num <= max_timing_pos_num); assert(subset_num >=0); assert(subset_num < num_subsets); @@ -269,56 +273,60 @@ void distributable_computation_cache_enabled( for (std::size_t processed_count = 1; processed_count <= num_vs; ++processed_count) { ViewSegmentNumbers view_segment_num; - - //check whether the slave will receive a new or an already cached viewgram - const bool new_viewgrams = - caching_info_ptr->get_unprocessed_vs_num(view_segment_num, next_receiver); - // view_segment_num = vs_nums_to_process[processed_count-1]; - - //the slave has not yet processed this vs_num, so the viewgrams have to be sent - if (new_viewgrams==true) - { - info(boost::format("Sending segment %1%, view %2% to slave %3%\n") % view_segment_num.segment_num() % view_segment_num.view_num() % next_receiver); - shared_ptr > y; - shared_ptr > additive_binwise_correction_viewgrams; - shared_ptr > mult_viewgrams_sptr; + //check whether the slave will receive a new or an already cached viewgram + const bool new_viewgrams = + caching_info_ptr->get_unprocessed_vs_num(view_segment_num, next_receiver); + // view_segment_num = vs_nums_to_process[processed_count-1]; - get_viewgrams(y, additive_binwise_correction_viewgrams, mult_viewgrams_sptr, - proj_dat_ptr, read_from_proj_dat, - zero_seg0_end_planes, - binwise_correction, - normalise_sptr, start_time_of_frame, end_time_of_frame, - symmetries_ptr, view_segment_num); + for (int timing_pos_num = min_timing_pos_num; timing_pos_num <= max_timing_pos_num; ++timing_pos_num) + { - //send viewgrams, the slave will immediatelly start calculation - send_viewgrams(y, additive_binwise_correction_viewgrams, mult_viewgrams_sptr, - next_receiver); - } // if(new_viewgram) - else - { - info(boost::format("Re-using segment %1%, view %2% to slave %3%\n") % view_segment_num.segment_num() % view_segment_num.view_num() % next_receiver); - //send vs_num with reuse-tag, the slave will immediatelly start calculation - distributed::send_view_segment_numbers( view_segment_num, REUSE_VIEWGRAM_TAG, next_receiver); - } + //the slave has not yet processed this vs_num, so the viewgrams have to be sent + if (new_viewgrams==true) + { + info(boost::format("Sending segment %1%, view %2%, TOF bin %3% to slave %4%\n") % view_segment_num.segment_num() % view_segment_num.view_num() % timing_pos_num % next_receiver); - working_slaves_count++; - - if (int(processed_count) > y; + shared_ptr > additive_binwise_correction_viewgrams; + shared_ptr > mult_viewgrams_sptr; + + get_viewgrams(y, additive_binwise_correction_viewgrams, mult_viewgrams_sptr, + proj_dat_ptr, read_from_proj_dat, + zero_seg0_end_planes, + binwise_correction, + normalise_sptr, start_time_of_frame, end_time_of_frame, + symmetries_ptr, view_segment_num, timing_pos_num); + + //send viewgrams, the slave will immediatelly start calculation + send_viewgrams(y, additive_binwise_correction_viewgrams, mult_viewgrams_sptr, + next_receiver); + } // if(new_viewgram) + else + { + info(boost::format("Re-using segment %1%, view %2%, TOF bin %3% to slave %4%\n") % view_segment_num.segment_num() % view_segment_num.view_num() % timing_pos_num % next_receiver); + //send vs_num with reuse-tag, the slave will immediatelly start calculation + distributed::send_view_segment_numbers( view_segment_num, REUSE_VIEWGRAM_TAG, next_receiver); + } + + working_slaves_count++; + + if (int(processed_count) (proj_data_info_ptr, v_num, s_num); + viewgram_ptr= new stir::Viewgram(proj_data_info_ptr, v_num, s_num, t_num); //allocate receive-buffer const int buffer_size=(viewgram_values[1]-viewgram_values[0]+1)*(viewgram_values[3]-viewgram_values[2]+1); diff --git a/src/recon_buildblock/distributed_test_functions.cxx b/src/recon_buildblock/distributed_test_functions.cxx index a3a3d08ce3..a0344d22c9 100644 --- a/src/recon_buildblock/distributed_test_functions.cxx +++ b/src/recon_buildblock/distributed_test_functions.cxx @@ -68,6 +68,8 @@ namespace distributed if (vg->get_view_num()!=viewgram.get_view_num()) printf("-----Test sending viewgram failed!!!!-------------\n"); assert(vg->get_segment_num()==viewgram.get_segment_num()); if (vg->get_segment_num()!=viewgram.get_segment_num()) printf("-----Test sending viewgram failed!!!!-------------\n"); + assert(vg->get_timing_pos_num()==viewgram.get_timing_pos_num()); + if (vg->get_timing_pos_num()!=viewgram.get_timing_pos_num()) printf("-----Test sending viewgram failed!!!!-------------\n"); delete vg; printf("\n-----Test sending viewgram done-----------\n"); diff --git a/src/recon_test/bcktest.cxx b/src/recon_test/bcktest.cxx index 3b077f5436..4841d31a24 100644 --- a/src/recon_test/bcktest.cxx +++ b/src/recon_test/bcktest.cxx @@ -88,6 +88,7 @@ START_NAMESPACE_STIR void do_segments(DiscretisedDensity<3,float>& image, ProjData& proj_data_org, + const int start_timing_num, const int end_timing_num, const int start_segment_num, const int end_segment_num, const int start_axial_pos_num, const int end_axial_pos_num, const int start_tang_pos_num,const int end_tang_pos_num, @@ -101,81 +102,85 @@ do_segments(DiscretisedDensity<3,float>& image, list already_processed; - - for (int segment_num = start_segment_num; segment_num <= end_segment_num; ++segment_num) - for (int view= start_view; view<=end_view; view++) - { - ViewSegmentNumbers vs(view, segment_num); - symmetries_sptr->find_basic_view_segment_numbers(vs); - if (find(already_processed.begin(), already_processed.end(), vs) - != already_processed.end()) - continue; - - already_processed.push_back(vs); - - cerr << "Processing view " << vs.view_num() - << " of segment " < viewgrams_empty= - proj_data_org.get_empty_related_viewgrams(vs, symmetries_sptr); - //proj_data_org.get_empty_related_viewgrams(vs.view_num(),vs.segment_num(), symmetries_sptr); - - RelatedViewgrams::iterator r_viewgrams_iter = viewgrams_empty.begin(); - while(r_viewgrams_iter!=viewgrams_empty.end()) - { - Viewgram& single_viewgram = *r_viewgrams_iter; - if (start_view <= single_viewgram.get_view_num() && - single_viewgram.get_view_num() <= end_view && - single_viewgram.get_segment_num() >= start_segment_num && - single_viewgram.get_segment_num() <= end_segment_num) - { - single_viewgram.fill(1.F); - } - r_viewgrams_iter++; - } - - back_projector_ptr->back_project(image,viewgrams_empty, - std::max(start_axial_pos_num, viewgrams_empty.get_min_axial_pos_num()), - std::min(end_axial_pos_num, viewgrams_empty.get_max_axial_pos_num()), - start_tang_pos_num, end_tang_pos_num); - } - else - { - RelatedViewgrams viewgrams = - proj_data_org.get_related_viewgrams(vs, - //proj_data_org.get_related_viewgrams(vs.view_num(),vs.segment_num(), - symmetries_sptr); - RelatedViewgrams::iterator r_viewgrams_iter = viewgrams.begin(); - - while(r_viewgrams_iter!=viewgrams.end()) - { - Viewgram& single_viewgram = *r_viewgrams_iter; - { - if (start_view <= single_viewgram.get_view_num() && - single_viewgram.get_view_num() <= end_view && - single_viewgram.get_segment_num() >= start_segment_num && - single_viewgram.get_segment_num() <= end_segment_num) - { - // ok - } - else - { - // set to 0 to prevent it being backprojected - single_viewgram.fill(0); - } - } - ++r_viewgrams_iter; - } - - back_projector_ptr->back_project(image,viewgrams, - std::max(start_axial_pos_num, viewgrams.get_min_axial_pos_num()), - std::min(end_axial_pos_num, viewgrams.get_max_axial_pos_num()), - start_tang_pos_num, end_tang_pos_num); - } // fill - } // for view_num, segment_num + for (int timing_num = start_timing_num; timing_num <= end_timing_num; ++timing_num) + { + already_processed.clear(); + for (int segment_num = start_segment_num; segment_num <= end_segment_num; ++segment_num) + for (int view = start_view; view <= end_view; view++) + { + ViewSegmentNumbers vs(view, segment_num); + symmetries_sptr->find_basic_view_segment_numbers(vs); + if (find(already_processed.begin(), already_processed.end(), vs) + != already_processed.end()) + continue; + + already_processed.push_back(vs); + + cerr << "Processing view " << vs.view_num() + << " of segment " << vs.segment_num() + << " of timing position index " << timing_num + << endl; + + if (fill_with_1) + { + RelatedViewgrams viewgrams_empty = + proj_data_org.get_empty_related_viewgrams(vs, symmetries_sptr, false, timing_num); + //proj_data_org.get_empty_related_viewgrams(vs.view_num(),vs.segment_num(), symmetries_sptr); + + RelatedViewgrams::iterator r_viewgrams_iter = viewgrams_empty.begin(); + while (r_viewgrams_iter != viewgrams_empty.end()) + { + Viewgram& single_viewgram = *r_viewgrams_iter; + if (start_view <= single_viewgram.get_view_num() && + single_viewgram.get_view_num() <= end_view && + single_viewgram.get_segment_num() >= start_segment_num && + single_viewgram.get_segment_num() <= end_segment_num) + { + single_viewgram.fill(1.F); + } + r_viewgrams_iter++; + } + + back_projector_ptr->back_project(image, viewgrams_empty, + std::max(start_axial_pos_num, viewgrams_empty.get_min_axial_pos_num()), + std::min(end_axial_pos_num, viewgrams_empty.get_max_axial_pos_num()), + start_tang_pos_num, end_tang_pos_num); + } + else + { + RelatedViewgrams viewgrams = + proj_data_org.get_related_viewgrams(vs, + //proj_data_org.get_related_viewgrams(vs.view_num(),vs.segment_num(), + symmetries_sptr, false, timing_num); + RelatedViewgrams::iterator r_viewgrams_iter = viewgrams.begin(); + + while (r_viewgrams_iter != viewgrams.end()) + { + Viewgram& single_viewgram = *r_viewgrams_iter; + { + if (start_view <= single_viewgram.get_view_num() && + single_viewgram.get_view_num() <= end_view && + single_viewgram.get_segment_num() >= start_segment_num && + single_viewgram.get_segment_num() <= end_segment_num) + { + // ok + } + else + { + // set to 0 to prevent it being backprojected + single_viewgram.fill(0); + } + } + ++r_viewgrams_iter; + } + + back_projector_ptr->back_project(image, viewgrams, + std::max(start_axial_pos_num, viewgrams.get_min_axial_pos_num()), + std::min(end_axial_pos_num, viewgrams.get_max_axial_pos_num()), + start_tang_pos_num, end_tang_pos_num); + } // fill + } // for view_num, segment_num + } // for timing_pos_num } @@ -274,7 +279,12 @@ main(int argc, char **argv) do { - + int min_timing_num = ask_num("Minimum timing position index to backproject", + proj_data_info_ptr->get_min_tof_pos_num(), proj_data_info_ptr->get_max_tof_pos_num(), + proj_data_info_ptr->get_min_tof_pos_num()); + int max_timing_num = ask_num("Maximum timing position index to backproject", + min_timing_num, proj_data_info_ptr->get_max_tof_pos_num(), + min_timing_num); int min_segment_num = ask_num("Minimum segment number to backproject", proj_data_info_ptr->get_min_segment_num(), proj_data_info_ptr->get_max_segment_num(), 0); int max_segment_num = ask_num("Maximum segment number to backproject", @@ -343,6 +353,7 @@ main(int argc, char **argv) do_segments(*image_sptr, *proj_data_ptr, + min_timing_num, max_timing_num, min_segment_num, max_segment_num, start_axial_pos_num,end_axial_pos_num, start_tang_pos_num,end_tang_pos_num, diff --git a/src/recon_test/fwdtest.cxx b/src/recon_test/fwdtest.cxx index 655e207d4c..ebf53cc1b0 100644 --- a/src/recon_test/fwdtest.cxx +++ b/src/recon_test/fwdtest.cxx @@ -81,6 +81,7 @@ USING_NAMESPACE_STIR static void do_segments(const VoxelsOnCartesianGrid& image, ProjData& s3d, + const int start_timing_pos_num, const int end_timing_pos_num, const int start_segment_num, const int end_segment_num, const int start_view, const int end_view, const int start_tangential_pos_num, const int end_tangential_pos_num, @@ -248,6 +249,7 @@ main(int argc, char *argv[]) do_segments(*vox_image_ptr, *proj_data_ptr, + proj_data_ptr->get_min_tof_pos_num(), proj_data_ptr->get_max_tof_pos_num(), proj_data_ptr->get_min_segment_num(), proj_data_ptr->get_max_segment_num(), proj_data_ptr->get_min_view_num(), proj_data_ptr->get_max_view_num(), @@ -268,14 +270,17 @@ main(int argc, char *argv[]) // first set all data to 0 cerr << "Filling output file with 0\n"; + for (int timing_pos_num = proj_data_ptr->get_min_tof_pos_num(); + timing_pos_num <= proj_data_ptr->get_max_tof_pos_num(); + ++timing_pos_num) for (int segment_num = proj_data_ptr->get_min_segment_num(); segment_num <= proj_data_ptr->get_max_segment_num(); ++segment_num) { const SegmentByView segment = - proj_data_ptr->get_empty_segment_by_view(segment_num, false); + proj_data_ptr->get_empty_segment_by_view(segment_num, false,timing_pos_num); if (!(proj_data_ptr->set_segment(segment) == Succeeded::yes)) - warning("Error set_segment %d\n", segment_num); + warning("Error set_segment %d of timing position index %d\n", segment_num,timing_pos_num); } do { @@ -283,6 +288,11 @@ main(int argc, char *argv[]) timer.reset(); timer.start(); + const int timing_pos_num = + ask_num("Timing position index to forward project", + proj_data_ptr->get_min_tof_pos_num(), + proj_data_ptr->get_max_tof_pos_num(), + 0); const int segment_num = ask_num("Segment number to forward project (related segments will be done as well)", proj_data_ptr->get_min_segment_num(), @@ -305,6 +315,7 @@ main(int argc, char *argv[]) ask_num("End tangential_pos_num ", start_tangential_pos_num, max_tangential_pos_num, max_tangential_pos_num); do_segments(*vox_image_ptr,*proj_data_ptr, + timing_pos_num, timing_pos_num, segment_num,segment_num, start_view, end_view, start_tangential_pos_num, end_tangential_pos_num, @@ -324,45 +335,50 @@ main(int argc, char *argv[]) /******************* Implementation local functions *******************/ void -do_segments(const VoxelsOnCartesianGrid& image, - ProjData& proj_data, - const int start_segment_num, const int end_segment_num, - const int start_view, const int end_view, - const int start_tangential_pos_num, const int end_tangential_pos_num, - ForwardProjectorByBin& forw_projector, - const bool disp) +do_segments(const VoxelsOnCartesianGrid& image, + ProjData& proj_data, + const int start_timing_pos_num, const int end_timing_pos_num, + const int start_segment_num, const int end_segment_num, + const int start_view, const int end_view, + const int start_tangential_pos_num, const int end_tangential_pos_num, + ForwardProjectorByBin& forw_projector, + const bool disp) { - shared_ptr - symmetries_sptr(forw_projector.get_symmetries_used()->clone()); - - std::list already_processed; - - for (int segment_num = start_segment_num; segment_num <= end_segment_num; ++segment_num) - for (int view= start_view; view<=end_view; view++) - { - ViewSegmentNumbers vs(view, segment_num); - symmetries_sptr->find_basic_view_segment_numbers(vs); - if (find(already_processed.begin(), already_processed.end(), vs) - != already_processed.end()) - continue; - - already_processed.push_back(vs); - - cerr << "Processing view " << vs.view_num() - << " of segment " < viewgrams = - proj_data.get_empty_related_viewgrams(vs, symmetries_sptr,false); - forw_projector.forward_project(viewgrams, image, - viewgrams.get_min_axial_pos_num(), - viewgrams.get_max_axial_pos_num(), - start_tangential_pos_num, end_tangential_pos_num); - if (disp) - display(viewgrams, viewgrams.find_max()); - if (!(proj_data.set_related_viewgrams(viewgrams) == Succeeded::yes)) - error("Error set_related_viewgrams\n"); - } + shared_ptr + symmetries_sptr(forw_projector.get_symmetries_used()->clone()); + + std::list already_processed; + for (int timing_pos_num = start_timing_pos_num; timing_pos_num <= end_timing_pos_num; ++timing_pos_num) + { + already_processed.clear(); + for (int segment_num = start_segment_num; segment_num <= end_segment_num; ++segment_num) + for (int view = start_view; view <= end_view; view++) + { + ViewSegmentNumbers vs(view, segment_num); + symmetries_sptr->find_basic_view_segment_numbers(vs); + if (find(already_processed.begin(), already_processed.end(), vs) + != already_processed.end()) + continue; + + already_processed.push_back(vs); + + cerr << "Processing view " << vs.view_num() + << " of segment " << vs.segment_num() + << " of timing position index " << timing_pos_num + << endl; + + RelatedViewgrams viewgrams = + proj_data.get_empty_related_viewgrams(vs, symmetries_sptr, false,timing_pos_num); + forw_projector.forward_project(viewgrams, image, + viewgrams.get_min_axial_pos_num(), + viewgrams.get_max_axial_pos_num(), + start_tangential_pos_num, end_tangential_pos_num); + if (disp) + display(viewgrams, viewgrams.find_max()); + if (!(proj_data.set_related_viewgrams(viewgrams) == Succeeded::yes)) + error("Error set_related_viewgrams\n"); + } + } } diff --git a/src/recon_test/test_PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx b/src/recon_test/test_PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx index 9aa649eb5b..2320cd54c1 100644 --- a/src/recon_test/test_PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx +++ b/src/recon_test/test_PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx @@ -191,7 +191,7 @@ construct_input_data(shared_ptr& density_sptr) timing_pos_num <= proj_data_sptr->get_max_tof_pos_num(); ++timing_pos_num) { - SegmentByView segment = proj_data_sptr->get_empty_segment_by_view(seg_num); + SegmentByView segment = proj_data_sptr->get_empty_segment_by_view(seg_num,false,timing_pos_num); // fill in some crazy values float value=0; for (SegmentByView::full_iterator iter = segment.begin_all(); diff --git a/src/test/test_ArcCorrection.cxx b/src/test/test_ArcCorrection.cxx index 17ef8e33c5..ea9885bb0d 100644 --- a/src/test/test_ArcCorrection.cxx +++ b/src/test/test_ArcCorrection.cxx @@ -61,6 +61,7 @@ class ArcCorrectionTests: public RunTests { public: void run_tests(); + void run_tests_tof(); protected: void run_tests_for_specific_proj_data_info(const ArcCorrection&); }; @@ -76,24 +77,27 @@ run_tests_for_specific_proj_data_info(const ArcCorrection& arc_correction) const float sampling_in_s = proj_data_info_arc_corr.get_tangential_sampling(); + for (int timing_pos_num = proj_data_info_noarc_corr.get_min_tof_pos_num(); + timing_pos_num <= proj_data_info_noarc_corr.get_max_tof_pos_num(); + ++timing_pos_num) for (int segment_num=proj_data_info_noarc_corr.get_min_segment_num(); segment_num<=proj_data_info_noarc_corr.get_max_segment_num(); ++segment_num) { const int axial_pos_num = 0; Sinogram noarccorr_sinogram = - proj_data_info_noarc_corr.get_empty_sinogram(axial_pos_num, segment_num); + proj_data_info_noarc_corr.get_empty_sinogram(axial_pos_num, segment_num, false, timing_pos_num); Sinogram arccorr_sinogram = - proj_data_info_arc_corr.get_empty_sinogram(axial_pos_num, segment_num); + proj_data_info_arc_corr.get_empty_sinogram(axial_pos_num, segment_num, false, timing_pos_num); for (int view_num=proj_data_info_noarc_corr.get_min_view_num(); view_num<=proj_data_info_noarc_corr.get_max_view_num(); view_num+=3) { Viewgram noarccorr_viewgram = - proj_data_info_noarc_corr.get_empty_viewgram(view_num, segment_num); + proj_data_info_noarc_corr.get_empty_viewgram(view_num, segment_num, false, timing_pos_num); Viewgram arccorr_viewgram = - proj_data_info_arc_corr.get_empty_viewgram(view_num, segment_num); + proj_data_info_arc_corr.get_empty_viewgram(view_num, segment_num, false, timing_pos_num); // test geometry by checking if single non-zero value gets put in the right bin { for (int tangential_pos_num=proj_data_info_noarc_corr.get_min_tangential_pos_num(); @@ -113,10 +117,10 @@ run_tests_for_specific_proj_data_info(const ArcCorrection& arc_correction) const float noarccorr_s = proj_data_info_noarc_corr. - get_s(Bin(segment_num,view_num,axial_pos_num,tangential_pos_num)); + get_s(Bin(segment_num,view_num,axial_pos_num,tangential_pos_num,timing_pos_num)); const float arccorr_s = proj_data_info_arc_corr. - get_s(Bin(segment_num,view_num,axial_pos_num,arccorr_tangential_pos_num_at_max)); + get_s(Bin(segment_num,view_num,axial_pos_num,arccorr_tangential_pos_num_at_max,timing_pos_num)); check((arccorr_s - noarccorr_s)/sampling_in_s < 1.1, "correspondence in location of maximum after arc-correction"); } @@ -176,6 +180,31 @@ ArcCorrectionTests::run_tests() } } +void +ArcCorrectionTests::run_tests_tof() +{ + cerr << "-------- Testing ArcCorrection for TOF scanner --------\n"; + ArcCorrection arc_correction; + shared_ptr scanner_ptr(new Scanner(Scanner::PETMR_Signa)); + + shared_ptr proj_data_info_ptr( + ProjDataInfo::ProjDataInfoGE(scanner_ptr, + /*max_delta*/ 10,/*views*/ 224, /*tang_pos*/ 357, /*arc_corrected*/ false, /*tof_mashing_factor*/ 39)); + + cerr << "Using default range and bin-size\n"; + { + arc_correction.set_up(proj_data_info_ptr); + run_tests_for_specific_proj_data_info(arc_correction); + } + cerr << "Using non-default range and bin-size\n"; + { + arc_correction.set_up(proj_data_info_ptr, + 357, + scanner_ptr->get_default_bin_size() * 2); + run_tests_for_specific_proj_data_info(arc_correction); + } +} + END_NAMESPACE_STIR @@ -185,5 +214,6 @@ int main() { ArcCorrectionTests tests; tests.run_tests(); + tests.run_tests_tof(); return tests.main_return_value(); } diff --git a/src/utilities/correct_projdata.cxx b/src/utilities/correct_projdata.cxx index db1b14c161..86e7b1a9d8 100644 --- a/src/utilities/correct_projdata.cxx +++ b/src/utilities/correct_projdata.cxx @@ -243,133 +243,133 @@ run() const : forward_projector_ptr->get_symmetries_used()->clone() ); - - for (int segment_num = output_projdata.get_min_segment_num(); segment_num <= output_projdata.get_max_segment_num() ; segment_num++) - { - cerr<is_basic(view_seg_nums)) - continue; + for (int timing_pos_num = output_projdata.get_min_tof_pos_num(); timing_pos_num <= output_projdata.get_max_tof_pos_num(); timing_pos_num++) + for (int segment_num = output_projdata.get_min_segment_num(); segment_num <= output_projdata.get_max_segment_num() ; segment_num++) + { + cerr<is_basic(view_seg_nums)) + continue; - // ** first fill in the data ** - RelatedViewgrams - viewgrams = input_projdata.get_empty_related_viewgrams(view_seg_nums, - symmetries_ptr); - if (use_data_or_set_to_1) - { - viewgrams += - input_projdata.get_related_viewgrams(view_seg_nums, - symmetries_ptr); - } - else - { - viewgrams.fill(1.F); - } + // ** first fill in the data ** + RelatedViewgrams + viewgrams = input_projdata.get_empty_related_viewgrams(view_seg_nums,symmetries_ptr, + false,timing_pos_num); + if (use_data_or_set_to_1) + { + viewgrams += + input_projdata.get_related_viewgrams(view_seg_nums, + symmetries_ptr,false,timing_pos_num); + } + else + { + viewgrams.fill(1.F); + } - if (do_arc_correction && !apply_or_undo_correction) - { - error("Cannot undo arc-correction yet. Sorry."); - // TODO - //arc_correction_sptr->undo_arc_correction(output_viewgrams, viewgrams); - } - - if (do_scatter && !apply_or_undo_correction) - { - viewgrams += - scatter_projdata_ptr->get_related_viewgrams(view_seg_nums, - symmetries_ptr); - } - - if (do_randoms && apply_or_undo_correction) - { - viewgrams -= - randoms_projdata_ptr->get_related_viewgrams(view_seg_nums, - symmetries_ptr); - } -#if 0 - if (frame_num==-1) - { - int num_frames = frame_def.get_num_frames(); - for ( int i = 1; i<=num_frames; i++) - { - //cerr << "Doing frame " << i << endl; - const double start_frame = frame_def.get_start_time(i); - const double end_frame = frame_def.get_end_time(i); - //cerr << "Start time " << start_frame << endl; - //cerr << " End time " << end_frame << endl; - // ** normalisation ** - if (apply_or_undo_correction) - { - normalisation_ptr->apply(viewgrams,start_frame,end_frame); - } - else - { - normalisation_ptr->undo(viewgrams,start_frame,end_frame); - } - } - } - - - - else -#endif - { - const double start_frame = frame_defs.get_start_time(frame_num); - const double end_frame = frame_defs.get_end_time(frame_num); - if (apply_or_undo_correction) - { - normalisation_ptr->apply(viewgrams,start_frame,end_frame); - } - else - { - normalisation_ptr->undo(viewgrams,start_frame,end_frame); - } - } - if (do_scatter && apply_or_undo_correction) - { - viewgrams -= - scatter_projdata_ptr->get_related_viewgrams(view_seg_nums, - symmetries_ptr); - } - - if (do_randoms && !apply_or_undo_correction) - { - viewgrams += - randoms_projdata_ptr->get_related_viewgrams(view_seg_nums, - symmetries_ptr); - } - - if (do_arc_correction && apply_or_undo_correction) - { - viewgrams = arc_correction_sptr->do_arc_correction(viewgrams); - } - - // output - { - // Unfortunately, segment range in output_projdata and input_projdata can be - // different. - // Hence, output_projdata.set_related_viewgrams(viewgrams) would not work. - // So, we need an extra viewgrams object to take this into account. - // The trick relies on calling Array::operator+= instead of - // RelatedViewgrams::operator= - RelatedViewgrams - output_viewgrams = - output_projdata.get_empty_related_viewgrams(view_seg_nums, - symmetries_ptr); - output_viewgrams += viewgrams; - - if (!(output_projdata.set_related_viewgrams(viewgrams) == Succeeded::yes)) - { - warning("CorrectProjData: Error set_related_viewgrams\n"); - return Succeeded::no; - } - } + if (do_arc_correction && !apply_or_undo_correction) + { + error("Cannot undo arc-correction yet. Sorry."); + // TODO + //arc_correction_sptr->undo_arc_correction(output_viewgrams, viewgrams); + } + + if (do_scatter && !apply_or_undo_correction) + { + viewgrams += + scatter_projdata_ptr->get_related_viewgrams(view_seg_nums, + symmetries_ptr,false,timing_pos_num); + } + + if (do_randoms && apply_or_undo_correction) + { + viewgrams -= + randoms_projdata_ptr->get_related_viewgrams(view_seg_nums, + symmetries_ptr,false,timing_pos_num); + } + #if 0 + if (frame_num==-1) + { + int num_frames = frame_def.get_num_frames(); + for ( int i = 1; i<=num_frames; i++) + { + //cerr << "Doing frame " << i << endl; + const double start_frame = frame_def.get_start_time(i); + const double end_frame = frame_def.get_end_time(i); + //cerr << "Start time " << start_frame << endl; + //cerr << " End time " << end_frame << endl; + // ** normalisation ** + if (apply_or_undo_correction) + { + normalisation_ptr->apply(viewgrams,start_frame,end_frame); + } + else + { + normalisation_ptr->undo(viewgrams,start_frame,end_frame); + } + } + } + + + + else + #endif + { + const double start_frame = frame_defs.get_start_time(frame_num); + const double end_frame = frame_defs.get_end_time(frame_num); + if (apply_or_undo_correction) + { + normalisation_ptr->apply(viewgrams,start_frame,end_frame); + } + else + { + normalisation_ptr->undo(viewgrams,start_frame,end_frame); + } + } + if (do_scatter && apply_or_undo_correction) + { + viewgrams -= + scatter_projdata_ptr->get_related_viewgrams(view_seg_nums, + symmetries_ptr,false,timing_pos_num); + } + + if (do_randoms && !apply_or_undo_correction) + { + viewgrams += + randoms_projdata_ptr->get_related_viewgrams(view_seg_nums, + symmetries_ptr,false,timing_pos_num); + } + + if (do_arc_correction && apply_or_undo_correction) + { + viewgrams = arc_correction_sptr->do_arc_correction(viewgrams); + } + + // output + { + // Unfortunately, segment range in output_projdata and input_projdata can be + // different. + // Hence, output_projdata.set_related_viewgrams(viewgrams) would not work. + // So, we need an extra viewgrams object to take this into account. + // The trick relies on calling Array::operator+= instead of + // RelatedViewgrams::operator= + RelatedViewgrams + output_viewgrams = + output_projdata.get_empty_related_viewgrams(view_seg_nums, + symmetries_ptr,false,timing_pos_num); + output_viewgrams += viewgrams; + + if (!(output_projdata.set_related_viewgrams(viewgrams) == Succeeded::yes)) + { + warning("CorrectProjData: Error set_related_viewgrams\n"); + return Succeeded::no; + } + } - } + } - } + } return Succeeded::yes; } From 1cda37c4d583ffab32c8ab5add449c31cb983926 Mon Sep 17 00:00:00 2001 From: Elise Date: Tue, 11 Apr 2017 12:43:14 +0100 Subject: [PATCH 068/170] Fix for sensitivity calculation (use of non-TOF backprojector) and calculate_attenuation_coefficients (will use non-TOF template if a TOF template is provided as input + warning) --- src/include/stir/ProjDataInfo.h | 3 ++ src/include/stir/ProjDataInfo.inl | 9 ++++ .../recon_buildblock/BackProjectorByBin.h | 2 + .../BackProjectorByBinUsingInterpolation.h | 2 + .../BackProjectorByBinUsingProjMatrixByBin.h | 5 +- ...elihoodWithLinearModelForMeanAndProjData.h | 5 +- .../PostsmoothingBackProjectorByBin.h | 2 + .../BackProjectorByBinUsingInterpolation.cxx | 6 +++ ...BackProjectorByBinUsingProjMatrixByBin.cxx | 7 +++ ...ihoodWithLinearModelForMeanAndProjData.cxx | 52 +++++++++---------- .../PostsmoothingBackProjectorByBin.cxx | 6 +++ .../calculate_attenuation_coefficients.cxx | 9 +++- 12 files changed, 74 insertions(+), 34 deletions(-) diff --git a/src/include/stir/ProjDataInfo.h b/src/include/stir/ProjDataInfo.h index f7111eb507..0a8002fe4b 100644 --- a/src/include/stir/ProjDataInfo.h +++ b/src/include/stir/ProjDataInfo.h @@ -125,6 +125,9 @@ class ProjDataInfo //! Like clone() but return a shared_ptr inline shared_ptr create_shared_clone() const; + //! Similar to create_shared_clone() but returns a non-tof version of ProjDataInfo setting tof mashing factor = 0 + inline shared_ptr create_non_tof_clone() const; + //! Destructor virtual ~ProjDataInfo() {} diff --git a/src/include/stir/ProjDataInfo.inl b/src/include/stir/ProjDataInfo.inl index 462b809aa1..812e3fc172 100644 --- a/src/include/stir/ProjDataInfo.inl +++ b/src/include/stir/ProjDataInfo.inl @@ -40,6 +40,15 @@ create_shared_clone() const return sptr; } +shared_ptr +ProjDataInfo:: +create_non_tof_clone() const +{ + shared_ptr sptr(this->clone()); + sptr->set_tof_mash_factor(0); // tof mashing factor = 0 is a trigger for non-tof data + return sptr; +} + int ProjDataInfo::get_num_segments() const { return (max_axial_pos_per_seg.get_length());} diff --git a/src/include/stir/recon_buildblock/BackProjectorByBin.h b/src/include/stir/recon_buildblock/BackProjectorByBin.h index ed5fa5c4d6..5f955435c6 100644 --- a/src/include/stir/recon_buildblock/BackProjectorByBin.h +++ b/src/include/stir/recon_buildblock/BackProjectorByBin.h @@ -110,6 +110,8 @@ class BackProjectorByBin : void back_project(DiscretisedDensity<3,float>&, const Bin&); + virtual BackProjectorByBin* clone() const =0; + protected: diff --git a/src/include/stir/recon_buildblock/BackProjectorByBinUsingInterpolation.h b/src/include/stir/recon_buildblock/BackProjectorByBinUsingInterpolation.h index 63c82e45d5..8dce8fb6a5 100644 --- a/src/include/stir/recon_buildblock/BackProjectorByBinUsingInterpolation.h +++ b/src/include/stir/recon_buildblock/BackProjectorByBinUsingInterpolation.h @@ -180,6 +180,8 @@ class BackProjectorByBinUsingInterpolation : */ void use_piecewise_linear_interpolation(const bool use_piecewise_linear_interpolation); + BackProjectorByBinUsingInterpolation* clone() const; + private: // KT 20/06/2001 changed type to enable use of more methods diff --git a/src/include/stir/recon_buildblock/BackProjectorByBinUsingProjMatrixByBin.h b/src/include/stir/recon_buildblock/BackProjectorByBinUsingProjMatrixByBin.h index 728ba92281..9b1d7cf9ee 100644 --- a/src/include/stir/recon_buildblock/BackProjectorByBinUsingProjMatrixByBin.h +++ b/src/include/stir/recon_buildblock/BackProjectorByBinUsingProjMatrixByBin.h @@ -87,10 +87,9 @@ class BackProjectorByBinUsingProjMatrixByBin: shared_ptr & get_proj_matrix_sptr(){ return proj_matrix_ptr ;} - - BackProjectorByBin* get_original_back_projector() const; - void enable_tof(ProjMatrixElemsForOneBin* ); + + BackProjectorByBinUsingProjMatrixByBin* clone() const; protected: diff --git a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.h b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.h index 153420d90a..ec7c8a33be 100644 --- a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.h +++ b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.h @@ -316,6 +316,9 @@ public RegisteredParsingObject projector_pair_ptr; + //! Backprojector used for sensitivity computation + shared_ptr sens_backprojector_sptr; + //! signals whether to zero the data in the end planes of the projection data bool zero_seg0_end_planes; @@ -364,7 +367,7 @@ public RegisteredParsingObject symmetries_sptr; void - add_view_seg_to_sensitivity(TargetT& sensitivity, const ViewSegmentNumbers& view_seg_nums, const int timing_pos_num = 0) const; + add_view_seg_to_sensitivity(TargetT& sensitivity, const ViewSegmentNumbers& view_seg_nums) const; }; #ifdef STIR_MPI diff --git a/src/include/stir/recon_buildblock/PostsmoothingBackProjectorByBin.h b/src/include/stir/recon_buildblock/PostsmoothingBackProjectorByBin.h index c2e68e7105..629845b5bc 100644 --- a/src/include/stir/recon_buildblock/PostsmoothingBackProjectorByBin.h +++ b/src/include/stir/recon_buildblock/PostsmoothingBackProjectorByBin.h @@ -81,6 +81,8 @@ class PostsmoothingBackProjectorByBin : BackProjectorByBin* get_original_back_projector_ptr() const; + PostsmoothingBackProjectorByBin* clone() const; + private: shared_ptr original_back_projector_ptr; diff --git a/src/recon_buildblock/BackProjectorByBinUsingInterpolation.cxx b/src/recon_buildblock/BackProjectorByBinUsingInterpolation.cxx index 0ee3950e3d..f58c2b5962 100644 --- a/src/recon_buildblock/BackProjectorByBinUsingInterpolation.cxx +++ b/src/recon_buildblock/BackProjectorByBinUsingInterpolation.cxx @@ -183,6 +183,12 @@ use_exact_Jacobian(const bool use_exact_Jacobian) use_exact_Jacobian_now = use_exact_Jacobian; } +BackProjectorByBinUsingInterpolation* +BackProjectorByBinUsingInterpolation:: +clone() const +{ + return new BackProjectorByBinUsingInterpolation(*this); +} void BackProjectorByBinUsingInterpolation:: diff --git a/src/recon_buildblock/BackProjectorByBinUsingProjMatrixByBin.cxx b/src/recon_buildblock/BackProjectorByBinUsingProjMatrixByBin.cxx index 78842daff6..4e1837264a 100644 --- a/src/recon_buildblock/BackProjectorByBinUsingProjMatrixByBin.cxx +++ b/src/recon_buildblock/BackProjectorByBinUsingProjMatrixByBin.cxx @@ -271,4 +271,11 @@ enable_tof(ProjMatrixElemsForOneBin * for_row) tof_enabled = true; } +BackProjectorByBinUsingProjMatrixByBin* +BackProjectorByBinUsingProjMatrixByBin:: +clone() const +{ + return new BackProjectorByBinUsingProjMatrixByBin(*this); +} + END_NAMESPACE_STIR diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx index 64ae469621..17d74351e0 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx @@ -620,6 +620,10 @@ set_up_before_sensitivity(shared_ptr const& target_sptr) this->projector_pair_ptr->set_up(proj_data_info_sptr, target_sptr); + + // sets non-tof backprojector for sensitivity calculation (clone of the back_projector + set projdatainfo to non-tof) + this->sens_backprojector_sptr.reset(projector_pair_ptr->get_back_projector_sptr()->clone()); + this->sens_backprojector_sptr->set_up(proj_data_info_sptr->create_non_tof_clone(), target_sptr); // TODO check compatibility between symmetries for forward and backprojector this->symmetries_sptr.reset( @@ -765,43 +769,36 @@ add_subset_sensitivity(TargetT& sensitivity, const int subset_num) const { const int min_segment_num = -this->max_segment_num_to_process; const int max_segment_num = this->max_segment_num_to_process; - const int min_timing_pos_num = -this->max_timing_pos_num_to_process; - const int max_timing_pos_num = this->max_timing_pos_num_to_process; // warning: has to be same as subset scheme used as in distributable_computation - for (int timing_pos_num = min_timing_pos_num; timing_pos_num <= max_timing_pos_num; ++timing_pos_num) - { - for (int segment_num = min_segment_num; segment_num <= max_segment_num; ++segment_num) - { - //CPUTimer timer; - //timer.start(); - - for (int view = this->proj_data_sptr->get_min_view_num() + subset_num; - view <= this->proj_data_sptr->get_max_view_num(); - view += this->num_subsets) - { - const ViewSegmentNumbers view_segment_num(view, segment_num); - - if (!symmetries_sptr->is_basic(view_segment_num)) - continue; - this->add_view_seg_to_sensitivity(sensitivity, view_segment_num, timing_pos_num); - } - // cerr<proj_data_sptr->get_min_view_num() + subset_num; + view <= this->proj_data_sptr->get_max_view_num(); + view += this->num_subsets) + { + const ViewSegmentNumbers view_segment_num(view, segment_num); + + if (!symmetries_sptr->is_basic(view_segment_num)) + continue; + this->add_view_seg_to_sensitivity(sensitivity, view_segment_num); + } + // cerr< void PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -add_view_seg_to_sensitivity(TargetT& sensitivity, const ViewSegmentNumbers& view_seg_nums , const int timing_pos_num) const +add_view_seg_to_sensitivity(TargetT& sensitivity, const ViewSegmentNumbers& view_seg_nums) const { RelatedViewgrams viewgrams = this->proj_data_sptr->get_empty_related_viewgrams(view_seg_nums, - this->symmetries_sptr, - false, - timing_pos_num); + this->symmetries_sptr); viewgrams.fill(1.F); // find efficiencies { @@ -819,8 +816,7 @@ add_view_seg_to_sensitivity(TargetT& sensitivity, const ViewSegmentNumbers& view const int max_ax_pos_num = viewgrams.get_max_axial_pos_num() - range_to_zero; - projector_pair_ptr->get_back_projector_sptr()->back_project(sensitivity, viewgrams, - min_ax_pos_num, max_ax_pos_num); + this->sens_backprojector_sptr->back_project(sensitivity, viewgrams, min_ax_pos_num, max_ax_pos_num); } } diff --git a/src/recon_buildblock/PostsmoothingBackProjectorByBin.cxx b/src/recon_buildblock/PostsmoothingBackProjectorByBin.cxx index 696f1f8229..5f3dce67a6 100644 --- a/src/recon_buildblock/PostsmoothingBackProjectorByBin.cxx +++ b/src/recon_buildblock/PostsmoothingBackProjectorByBin.cxx @@ -85,6 +85,12 @@ PostsmoothingBackProjectorByBin::get_original_back_projector_ptr() const return original_back_projector_ptr.get(); } +PostsmoothingBackProjectorByBin* +PostsmoothingBackProjectorByBin::clone() const +{ + return new PostsmoothingBackProjectorByBin(*this); +} + void PostsmoothingBackProjectorByBin:: init_filtered_density_image(DiscretisedDensity<3, float> &density) { diff --git a/src/utilities/calculate_attenuation_coefficients.cxx b/src/utilities/calculate_attenuation_coefficients.cxx index 1c637a6220..c4f0715476 100644 --- a/src/utilities/calculate_attenuation_coefficients.cxx +++ b/src/utilities/calculate_attenuation_coefficients.cxx @@ -126,11 +126,16 @@ main (int argc, char * argv[]) cerr << "\n\nForward projector used:\n" << forw_projector_ptr->parameter_info(); + if (template_proj_data_ptr->get_proj_data_info_ptr()->is_tof_data()) + { + warning("The scanner template provided contains timing information. The calculation of the attenuation coefficients will not take them into consideration.\n"); + } + const std::string output_file_name = argv[1]; shared_ptr out_proj_data_ptr( new ProjDataInterfile(template_proj_data_ptr->get_exam_info_sptr(),// TODO this should possibly come from the image, or say it's an ACF File - template_proj_data_ptr->get_proj_data_info_ptr()->create_shared_clone(), + template_proj_data_ptr->get_proj_data_info_ptr()->create_non_tof_clone(), output_file_name, std::ios::in|std::ios::out|std::ios::trunc)); @@ -143,7 +148,7 @@ main (int argc, char * argv[]) forw_projector_ptr)); if ( - normalisation_ptr->set_up(template_proj_data_ptr->get_proj_data_info_ptr()->create_shared_clone()) + normalisation_ptr->set_up(template_proj_data_ptr->get_proj_data_info_ptr()->create_non_tof_clone()) != Succeeded::yes) { warning("calculate_attenuation_coefficients: set-up of normalisation failed\n"); From 3d21216bcdc5aa2af4d1b32a28b07f157a8c2f18 Mon Sep 17 00:00:00 2001 From: Elise Date: Tue, 11 Apr 2017 18:58:19 +0100 Subject: [PATCH 069/170] Fix for sensitivity calculation 2 --- src/recon_buildblock/ProjMatrixByBin.cxx | 5 +++++ src/recon_buildblock/ProjMatrixByBinUsingRayTracing.cxx | 2 -- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/recon_buildblock/ProjMatrixByBin.cxx b/src/recon_buildblock/ProjMatrixByBin.cxx index d25ef353f2..554cbc1f67 100644 --- a/src/recon_buildblock/ProjMatrixByBin.cxx +++ b/src/recon_buildblock/ProjMatrixByBin.cxx @@ -148,6 +148,11 @@ set_up( if (proj_data_info_sptr->is_tof_data()) enable_tof(proj_data_info_sptr,true); + else if (this->tof_enabled) // this case happens when we transform a TOF projector into a non-TOF projector + { + tof_enabled = false; + this->proj_data_info_sptr = proj_data_info_sptr; + } this->cache_collection.recycle(); this->cache_collection.resize(min_view_num, max_view_num); diff --git a/src/recon_buildblock/ProjMatrixByBinUsingRayTracing.cxx b/src/recon_buildblock/ProjMatrixByBinUsingRayTracing.cxx index 1935bcb8ad..0702526c1f 100644 --- a/src/recon_buildblock/ProjMatrixByBinUsingRayTracing.cxx +++ b/src/recon_buildblock/ProjMatrixByBinUsingRayTracing.cxx @@ -542,8 +542,6 @@ calculate_proj_matrix_elems_for_one_bin( } const Bin bin = lor.get_bin(); - assert(bin.timing_pos_num() >= proj_data_info_ptr->get_min_tof_pos_num()); - assert(bin.timing_pos_num() <= proj_data_info_ptr->get_max_tof_pos_num()); assert(bin.segment_num() >= proj_data_info_ptr->get_min_segment_num()); assert(bin.segment_num() <= proj_data_info_ptr->get_max_segment_num()); From 22dbe615bd22097404968fb22420783c7bc24c30 Mon Sep 17 00:00:00 2001 From: Elise Date: Thu, 13 Apr 2017 21:17:52 +0100 Subject: [PATCH 070/170] Fix for clone() + clean-up + removed duplicate of proj_data_info in ProjMatrixByBinUsingRayTracing --- ...orMeanAndListModeDataWithProjMatrixByBin.h | 5 ++- .../stir/recon_buildblock/ProjMatrixByBin.h | 4 +- .../ProjMatrixByBinFromFile.h | 2 + .../recon_buildblock/ProjMatrixByBinSPECTUB.h | 2 + .../ProjMatrixByBinUsingInterpolation.h | 2 + .../ProjMatrixByBinUsingRayTracing.h | 5 +-- ...BackProjectorByBinUsingProjMatrixByBin.cxx | 4 +- ...MeanAndListModeDataWithProjMatrixByBin.cxx | 38 ++++++++-------- src/recon_buildblock/ProjMatrixByBin.cxx | 2 +- .../ProjMatrixByBinFromFile.cxx | 6 +++ .../ProjMatrixByBinSPECTUB.cxx | 6 +++ .../ProjMatrixByBinUsingInterpolation.cxx | 7 +++ .../ProjMatrixByBinUsingRayTracing.cxx | 45 ++++++++++--------- 13 files changed, 81 insertions(+), 47 deletions(-) diff --git a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h index 5a224a7ae2..25606ea8df 100644 --- a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h +++ b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h @@ -115,6 +115,9 @@ typedef RegisteredParsingObject projector_pair_ptr; + //! Backprojector used for sensitivity computation + shared_ptr sens_backprojector_sptr; + //! points to the additive projection data shared_ptr additive_proj_data_sptr; @@ -133,7 +136,7 @@ typedef RegisteredParsingObject >& density_info_ptr // TODO should be Info only ) = 0; + virtual ProjMatrixByBin* clone() const = 0; + //! get a pointer to an object encoding all symmetries that are used by this ProjMatrixByBin inline const DataSymmetriesForBins* get_symmetries_ptr() const; //! get a shared_ptr to an object encoding all symmetries that are used by this ProjMatrixByBin @@ -229,7 +231,7 @@ class ProjMatrixByBin : //! cartesian coordinates of each voxel. shared_ptr > image_info_sptr; - //! We need a local copy of the proj_data_info to get the integration boundaries. + //! We need a local copy of the proj_data_info to get the integration boundaries and RayTracing shared_ptr proj_data_info_sptr; //! The method to store data in the cache. diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBinFromFile.h b/src/include/stir/recon_buildblock/ProjMatrixByBinFromFile.h index 5332fe8368..12e6c38621 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBinFromFile.h +++ b/src/include/stir/recon_buildblock/ProjMatrixByBinFromFile.h @@ -108,6 +108,8 @@ static Succeeded const shared_ptr >& density_info_ptr // TODO should be Info only ); + virtual ProjMatrixByBinFromFile* clone() const; + private: std::string parsed_version; diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBinSPECTUB.h b/src/include/stir/recon_buildblock/ProjMatrixByBinSPECTUB.h index 5ad639418d..2e9739d076 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBinSPECTUB.h +++ b/src/include/stir/recon_buildblock/ProjMatrixByBinSPECTUB.h @@ -107,6 +107,8 @@ class ProjMatrixByBinSPECTUB : const shared_ptr >& density_info_ptr // TODO should be Info only ); + virtual ProjMatrixByBinSPECTUB* clone() const; + private: // parameters that will be parsed diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBinUsingInterpolation.h b/src/include/stir/recon_buildblock/ProjMatrixByBinUsingInterpolation.h index 62a9da20a3..1c567fe76f 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBinUsingInterpolation.h +++ b/src/include/stir/recon_buildblock/ProjMatrixByBinUsingInterpolation.h @@ -78,6 +78,8 @@ public : const shared_ptr >& density_info_ptr // TODO should be Info only ); + virtual ProjMatrixByBinUsingInterpolation* clone() const; + private: bool do_symmetry_90degrees_min_phi; bool do_symmetry_180degrees_min_phi; diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBinUsingRayTracing.h b/src/include/stir/recon_buildblock/ProjMatrixByBinUsingRayTracing.h index a274c2e6bf..cad31ddc3f 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBinUsingRayTracing.h +++ b/src/include/stir/recon_buildblock/ProjMatrixByBinUsingRayTracing.h @@ -140,6 +140,8 @@ public : const shared_ptr >& density_info_sptr_v // TODO should be Info only ); + virtual ProjMatrixByBinUsingRayTracing* clone() const; + //! \name If a cylindrical FOV or the whole image will be handled //!@{ bool get_restrict_to_cylindrical_FOV() const; @@ -199,9 +201,6 @@ public : CartesianCoordinate3D min_index; CartesianCoordinate3D max_index; - shared_ptr proj_data_info_ptr; - - virtual void calculate_proj_matrix_elems_for_one_bin( ProjMatrixElemsForOneBin&) const; diff --git a/src/recon_buildblock/BackProjectorByBinUsingProjMatrixByBin.cxx b/src/recon_buildblock/BackProjectorByBinUsingProjMatrixByBin.cxx index 4e1837264a..08a0f53021 100644 --- a/src/recon_buildblock/BackProjectorByBinUsingProjMatrixByBin.cxx +++ b/src/recon_buildblock/BackProjectorByBinUsingProjMatrixByBin.cxx @@ -275,7 +275,9 @@ BackProjectorByBinUsingProjMatrixByBin* BackProjectorByBinUsingProjMatrixByBin:: clone() const { - return new BackProjectorByBinUsingProjMatrixByBin(*this); + BackProjectorByBinUsingProjMatrixByBin* sptr(new BackProjectorByBinUsingProjMatrixByBin(*this)); + sptr->proj_matrix_ptr.reset(this->proj_matrix_ptr->clone()); + return sptr; } END_NAMESPACE_STIR diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx index 928f706a61..40dd8208b4 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx @@ -232,6 +232,10 @@ set_up_before_sensitivity(shared_ptr const& target_sptr) this->projector_pair_ptr->enable_tof(proj_data_info_cyl_sptr->create_shared_clone(), this->use_tof); } + // sets non-tof backprojector for sensitivity calculation (clone of the back_projector + set projdatainfo to non-tof) + this->sens_backprojector_sptr.reset(projector_pair_ptr->get_back_projector_sptr()->clone()); + this->sens_backprojector_sptr->set_up(proj_data_info_cyl_sptr->create_non_tof_clone(), target_sptr); + if (is_null_ptr(this->normalisation_sptr)) { warning("Invalid normalisation object"); @@ -369,38 +373,33 @@ add_subset_sensitivity(TargetT& sensitivity, const int subset_num) const const int min_segment_num = proj_data_info_cyl_sptr->get_min_segment_num(); const int max_segment_num = proj_data_info_cyl_sptr->get_max_segment_num(); - const int min_timing_pos_num = proj_data_info_cyl_sptr->get_min_tof_pos_num(); - const int max_timing_pos_num = proj_data_info_cyl_sptr->get_max_tof_pos_num(); // warning: has to be same as subset scheme used as in distributable_computation - for (int timing_pos_num = min_timing_pos_num; timing_pos_num <= min_timing_pos_num; ++timing_pos_num) - { - for (int segment_num = min_segment_num; segment_num <= max_segment_num; ++segment_num) + for (int segment_num = min_segment_num; segment_num <= max_segment_num; ++segment_num) + { + for (int view = proj_data_info_cyl_sptr->get_min_view_num() + subset_num; + view <= proj_data_info_cyl_sptr->get_max_view_num(); + view += this->num_subsets) { - for (int view = proj_data_info_cyl_sptr->get_min_view_num() + subset_num; - view <= proj_data_info_cyl_sptr->get_max_view_num(); - view += this->num_subsets) - { - const ViewSegmentNumbers view_segment_num(view, segment_num); - - if (! this->projector_pair_ptr->get_symmetries_used()->is_basic(view_segment_num)) - continue; - this->add_view_seg_to_sensitivity(sensitivity, view_segment_num, timing_pos_num); - } + const ViewSegmentNumbers view_segment_num(view, segment_num); + + if (! this->projector_pair_ptr->get_symmetries_used()->is_basic(view_segment_num)) + continue; + this->add_view_seg_to_sensitivity(sensitivity, view_segment_num); } - } + } } template void PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin:: -add_view_seg_to_sensitivity(TargetT& sensitivity, const ViewSegmentNumbers& view_seg_nums, const int timing_pos_num) const +add_view_seg_to_sensitivity(TargetT& sensitivity, const ViewSegmentNumbers& view_seg_nums) const { shared_ptr symmetries_used (this->projector_pair_ptr->get_symmetries_used()->clone()); RelatedViewgrams viewgrams = - proj_data_info_cyl_sptr->get_empty_related_viewgrams(view_seg_nums,symmetries_used, false, timing_pos_num); + proj_data_info_cyl_sptr->get_empty_related_viewgrams(view_seg_nums,symmetries_used); viewgrams.fill(1.F); // find efficiencies @@ -416,8 +415,7 @@ add_view_seg_to_sensitivity(TargetT& sensitivity, const ViewSegmentNumbers& view const int max_ax_pos_num = viewgrams.get_max_axial_pos_num(); - this->projector_pair_ptr->get_back_projector_sptr()-> - back_project(sensitivity, viewgrams, + this->sens_backprojector_sptr->back_project(sensitivity, viewgrams, min_ax_pos_num, max_ax_pos_num); } diff --git a/src/recon_buildblock/ProjMatrixByBin.cxx b/src/recon_buildblock/ProjMatrixByBin.cxx index 554cbc1f67..28ffb96d35 100644 --- a/src/recon_buildblock/ProjMatrixByBin.cxx +++ b/src/recon_buildblock/ProjMatrixByBin.cxx @@ -148,7 +148,7 @@ set_up( if (proj_data_info_sptr->is_tof_data()) enable_tof(proj_data_info_sptr,true); - else if (this->tof_enabled) // this case happens when we transform a TOF projector into a non-TOF projector + else { tof_enabled = false; this->proj_data_info_sptr = proj_data_info_sptr; diff --git a/src/recon_buildblock/ProjMatrixByBinFromFile.cxx b/src/recon_buildblock/ProjMatrixByBinFromFile.cxx index fec274a423..6e60899418 100644 --- a/src/recon_buildblock/ProjMatrixByBinFromFile.cxx +++ b/src/recon_buildblock/ProjMatrixByBinFromFile.cxx @@ -241,6 +241,12 @@ set_up( error("Something wrong reading the matrix from file. Exiting."); } +ProjMatrixByBinFromFile* +ProjMatrixByBinFromFile::clone() const +{ + return new ProjMatrixByBinFromFile(*this); +} + // anonymous namespace for local functions namespace { diff --git a/src/recon_buildblock/ProjMatrixByBinSPECTUB.cxx b/src/recon_buildblock/ProjMatrixByBinSPECTUB.cxx index 833f2b89a5..4ce6ef8c94 100644 --- a/src/recon_buildblock/ProjMatrixByBinSPECTUB.cxx +++ b/src/recon_buildblock/ProjMatrixByBinSPECTUB.cxx @@ -575,6 +575,12 @@ set_up( this->already_setup= true; } +ProjMatrixByBinSPECTUB* +ProjMatrixByBinSPECTUB::clone() const +{ + return new ProjMatrixByBinSPECTUB(*this); +} + ProjMatrixByBinSPECTUB:: ~ProjMatrixByBinSPECTUB() { diff --git a/src/recon_buildblock/ProjMatrixByBinUsingInterpolation.cxx b/src/recon_buildblock/ProjMatrixByBinUsingInterpolation.cxx index fe4bf6e3b6..24696971fc 100644 --- a/src/recon_buildblock/ProjMatrixByBinUsingInterpolation.cxx +++ b/src/recon_buildblock/ProjMatrixByBinUsingInterpolation.cxx @@ -161,6 +161,13 @@ set_up( } } } + +ProjMatrixByBinUsingInterpolation* +ProjMatrixByBinUsingInterpolation::clone() const +{ + return new ProjMatrixByBinUsingInterpolation(*this); +} + // point should be w.r.t. middle of the scanner! static inline void diff --git a/src/recon_buildblock/ProjMatrixByBinUsingRayTracing.cxx b/src/recon_buildblock/ProjMatrixByBinUsingRayTracing.cxx index 0702526c1f..8e45163295 100644 --- a/src/recon_buildblock/ProjMatrixByBinUsingRayTracing.cxx +++ b/src/recon_buildblock/ProjMatrixByBinUsingRayTracing.cxx @@ -275,8 +275,7 @@ set_up( { ProjMatrixByBin::set_up(proj_data_info_sptr_v, density_info_sptr_v); - proj_data_info_ptr= proj_data_info_sptr_v; - image_info_sptr.reset( + image_info_sptr.reset( dynamic_cast* > (density_info_sptr_v->clone() )); // const VoxelsOnCartesianGrid * image_info_ptr = // dynamic_cast*> (density_info_ptr.get()); @@ -289,7 +288,7 @@ set_up( image_info_sptr->get_regular_range(min_index, max_index); symmetries_sptr.reset( - new DataSymmetriesForBins_PET_CartesianGrid(proj_data_info_ptr, + new DataSymmetriesForBins_PET_CartesianGrid(proj_data_info_sptr, density_info_sptr_v, do_symmetry_90degrees_min_phi, do_symmetry_180degrees_min_phi, @@ -297,7 +296,7 @@ set_up( do_symmetry_swap_s, do_symmetry_shift_z)); const float sampling_distance_of_adjacent_LORs_xy = - proj_data_info_ptr->get_sampling_in_s(Bin(0,0,0,0)); + proj_data_info_sptr->get_sampling_in_s(Bin(0,0,0,0)); if(sampling_distance_of_adjacent_LORs_xy/num_tangential_LORs > voxel_size.x() + 1.E-3 || sampling_distance_of_adjacent_LORs_xy/num_tangential_LORs > voxel_size.y() + 1.E-3) @@ -313,7 +312,7 @@ set_up( if (use_actual_detector_boundaries) { const ProjDataInfoCylindricalNoArcCorr * proj_data_info_cyl_ptr = - dynamic_cast(proj_data_info_ptr.get()); + dynamic_cast(proj_data_info_sptr.get()); if (proj_data_info_cyl_ptr== 0) { warning("ProjMatrixByBinUsingRayTracing: use_actual_detector_boundaries" @@ -364,6 +363,12 @@ set_up( this->clear_cache(); }; +ProjMatrixByBinUsingRayTracing* +ProjMatrixByBinUsingRayTracing::clone() const +{ + return new ProjMatrixByBinUsingRayTracing(*this); +} + /* this is used when (tantheta==0 && sampling_distance_of_adjacent_LORs_z==2*voxel_size.z()) it adds two adjacents z with their half value @@ -542,13 +547,13 @@ calculate_proj_matrix_elems_for_one_bin( } const Bin bin = lor.get_bin(); - assert(bin.segment_num() >= proj_data_info_ptr->get_min_segment_num()); - assert(bin.segment_num() <= proj_data_info_ptr->get_max_segment_num()); + assert(bin.segment_num() >= proj_data_info_sptr->get_min_segment_num()); + assert(bin.segment_num() <= proj_data_info_sptr->get_max_segment_num()); assert(lor.size() == 0); float phi; - float s_in_mm = proj_data_info_ptr->get_s(bin); + float s_in_mm = proj_data_info_sptr->get_s(bin); /* Implementation note. KT initialised s_in_mm above instead of in the if because this meant that gcc 3.0.1 generated identical results to the previous version of this file. @@ -562,19 +567,19 @@ calculate_proj_matrix_elems_for_one_bin( */ if (!use_actual_detector_boundaries) { - phi = proj_data_info_ptr->get_phi(bin); - //s_in_mm = proj_data_info_ptr->get_s(bin); + phi = proj_data_info_sptr->get_phi(bin); + //s_in_mm = proj_data_info_sptr->get_s(bin); } else { // can be static_cast later on const ProjDataInfoCylindricalNoArcCorr& proj_data_info_noarccor = - dynamic_cast(*proj_data_info_ptr); + dynamic_cast(*proj_data_info_sptr); // TODO check on 180 degrees for views const int num_detectors = - proj_data_info_ptr->get_scanner_ptr()->get_num_detectors_per_ring(); + proj_data_info_sptr->get_scanner_ptr()->get_num_detectors_per_ring(); const float ring_radius = - proj_data_info_ptr->get_scanner_ptr()->get_effective_ring_radius(); + proj_data_info_sptr->get_scanner_ptr()->get_effective_ring_radius(); int det_num1=0, det_num2=0; proj_data_info_noarccor. @@ -583,13 +588,13 @@ calculate_proj_matrix_elems_for_one_bin( bin.view_num(), bin.tangential_pos_num()); phi = static_cast((det_num1+det_num2)*_PI/num_detectors-_PI/2); - const float old_phi=proj_data_info_ptr->get_phi(bin); + const float old_phi=proj_data_info_sptr->get_phi(bin); if (fabs(phi-old_phi)>2*_PI/num_detectors) warning("view %d old_phi %g new_phi %g\n",bin.view_num(), old_phi, phi); s_in_mm = static_cast(ring_radius*sin((det_num1-det_num2)*_PI/num_detectors+_PI/2)); - const float old_s_in_mm=proj_data_info_ptr->get_s(bin); - if (fabs(s_in_mm-old_s_in_mm)>proj_data_info_ptr->get_sampling_in_s(bin)*.0001) + const float old_s_in_mm=proj_data_info_sptr->get_s(bin); + if (fabs(s_in_mm-old_s_in_mm)>proj_data_info_sptr->get_sampling_in_s(bin)*.0001) warning("tangential_pos_num %d old_s_in_mm %g new_s_in_mm %g\n",bin.tangential_pos_num(), old_s_in_mm, s_in_mm); } @@ -597,12 +602,12 @@ calculate_proj_matrix_elems_for_one_bin( const float cphi = cos(phi); const float sphi = sin(phi); - const float tantheta = proj_data_info_ptr->get_tantheta(bin); + const float tantheta = proj_data_info_sptr->get_tantheta(bin); const float costheta = 1/sqrt(1+square(tantheta)); - const float t_in_mm = proj_data_info_ptr->get_t(bin); + const float t_in_mm = proj_data_info_sptr->get_t(bin); const float sampling_distance_of_adjacent_LORs_z = - proj_data_info_ptr->get_sampling_in_t(bin)/costheta; + proj_data_info_sptr->get_sampling_in_t(bin)/costheta; // find number of LORs we have to take, such that we don't miss voxels @@ -697,7 +702,7 @@ calculate_proj_matrix_elems_for_one_bin( // interleaved case has a sampling which is twice as high const float s_inc = (!use_actual_detector_boundaries ? 1 : 2) * - proj_data_info_ptr->get_sampling_in_s(bin)/num_tangential_LORs; + proj_data_info_sptr->get_sampling_in_s(bin)/num_tangential_LORs; float current_s_in_mm = s_in_mm - s_inc*(num_tangential_LORs-1)/2.F; for (int s_LOR_num=1; s_LOR_num<=num_tangential_LORs; ++s_LOR_num, current_s_in_mm+=s_inc) From a177b6012d36b590be72a76b4a19a2a3fa26eea5 Mon Sep 17 00:00:00 2001 From: Elise Date: Sat, 6 May 2017 19:03:23 +0100 Subject: [PATCH 071/170] Fix to add the possibility to use a TOF template to calculate sensitivity images if specified in the recon par file --- ...orMeanAndListModeDataWithProjMatrixByBin.h | 3 ++ ...elihoodWithLinearModelForMeanAndProjData.h | 3 ++ ...MeanAndListModeDataWithProjMatrixByBin.cxx | 51 ++++++++++-------- ...ihoodWithLinearModelForMeanAndProjData.cxx | 53 +++++++++++-------- src/utilities/poisson_noise.cxx | 2 +- 5 files changed, 67 insertions(+), 45 deletions(-) diff --git a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h index 25606ea8df..ea82529356 100644 --- a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h +++ b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h @@ -106,6 +106,9 @@ typedef RegisteredParsingObjectnormalisation_sptr.reset(new TrivialBinNormalisation); this->do_time_frame = false; + this->use_tofsens = false; this->use_projectors = false; } @@ -102,6 +103,7 @@ initialise_keymap() base_type::initialise_keymap(); this->parser.add_start_key("PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin Parameters"); this->parser.add_stop_key("End PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin Parameters"); + this->parser.add_key("use time-of-flight sensitivities", &this->use_tofsens); this->parser.add_key("max ring difference num to process", &this->max_ring_difference_num_to_process); this->parser.add_parsing_key("Matrix type", &this->PM_sptr); this->parser.add_parsing_key("Projector pair type", &this->projector_pair_ptr); @@ -234,7 +236,8 @@ set_up_before_sensitivity(shared_ptr const& target_sptr) // sets non-tof backprojector for sensitivity calculation (clone of the back_projector + set projdatainfo to non-tof) this->sens_backprojector_sptr.reset(projector_pair_ptr->get_back_projector_sptr()->clone()); - this->sens_backprojector_sptr->set_up(proj_data_info_cyl_sptr->create_non_tof_clone(), target_sptr); + if (!this->use_tofsens) + this->sens_backprojector_sptr->set_up(proj_data_info_cyl_sptr->create_non_tof_clone(), target_sptr); if (is_null_ptr(this->normalisation_sptr)) { @@ -395,29 +398,35 @@ void PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin:: add_view_seg_to_sensitivity(TargetT& sensitivity, const ViewSegmentNumbers& view_seg_nums) const { - shared_ptr symmetries_used - (this->projector_pair_ptr->get_symmetries_used()->clone()); + for (int timing_pos_num = this->proj_data_info_cyl_sptr->get_min_tof_pos_num(); + timing_pos_num <= this->proj_data_info_cyl_sptr->get_max_tof_pos_num(); + ++timing_pos_num) + { + shared_ptr symmetries_used + (this->projector_pair_ptr->get_symmetries_used()->clone()); - RelatedViewgrams viewgrams = - proj_data_info_cyl_sptr->get_empty_related_viewgrams(view_seg_nums,symmetries_used); + RelatedViewgrams viewgrams = + proj_data_info_cyl_sptr->get_empty_related_viewgrams( + view_seg_nums, symmetries_used, false, timing_pos_num); - viewgrams.fill(1.F); - // find efficiencies - { - const double start_frame = this->frame_defs.get_start_time(this->current_frame_num); - const double end_frame = this->frame_defs.get_end_time(this->current_frame_num); - this->normalisation_sptr->undo(viewgrams,start_frame,end_frame); - } - // backproject - { - const int min_ax_pos_num = - viewgrams.get_min_axial_pos_num(); - const int max_ax_pos_num = - viewgrams.get_max_axial_pos_num(); + viewgrams.fill(1.F); + // find efficiencies + { + const double start_frame = this->frame_defs.get_start_time(this->current_frame_num); + const double end_frame = this->frame_defs.get_end_time(this->current_frame_num); + this->normalisation_sptr->undo(viewgrams, start_frame, end_frame); + } + // backproject + { + const int min_ax_pos_num = + viewgrams.get_min_axial_pos_num(); + const int max_ax_pos_num = + viewgrams.get_max_axial_pos_num(); - this->sens_backprojector_sptr->back_project(sensitivity, viewgrams, - min_ax_pos_num, max_ax_pos_num); - } + this->sens_backprojector_sptr->back_project(sensitivity, viewgrams, + min_ax_pos_num, max_ax_pos_num); + } + } } diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx index 17d74351e0..192ddb02d4 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx @@ -101,6 +101,7 @@ set_defaults() //num_views_to_add=1; this->proj_data_sptr.reset(); //MJ added this->zero_seg0_end_planes = 0; + this->use_tofsens = false; this->additive_projection_data_filename = "0"; this->additive_proj_data_sptr.reset(); @@ -155,6 +156,7 @@ initialise_keymap() base_type::initialise_keymap(); this->parser.add_start_key("PoissonLogLikelihoodWithLinearModelForMeanAndProjData Parameters"); this->parser.add_stop_key("End PoissonLogLikelihoodWithLinearModelForMeanAndProjData Parameters"); + this->parser.add_key("use time-of-flight sensitivities", &this->use_tofsens); this->parser.add_key("input file",&this->input_filename); // KT 20/06/2001 disabled //parser.add_key("mash x views", &num_views_to_add); @@ -623,7 +625,8 @@ set_up_before_sensitivity(shared_ptr const& target_sptr) // sets non-tof backprojector for sensitivity calculation (clone of the back_projector + set projdatainfo to non-tof) this->sens_backprojector_sptr.reset(projector_pair_ptr->get_back_projector_sptr()->clone()); - this->sens_backprojector_sptr->set_up(proj_data_info_sptr->create_non_tof_clone(), target_sptr); + if (!this->use_tofsens) + this->sens_backprojector_sptr->set_up(proj_data_info_sptr->create_non_tof_clone(), target_sptr); // TODO check compatibility between symmetries for forward and backprojector this->symmetries_sptr.reset( @@ -796,28 +799,32 @@ void PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: add_view_seg_to_sensitivity(TargetT& sensitivity, const ViewSegmentNumbers& view_seg_nums) const { - RelatedViewgrams viewgrams = - this->proj_data_sptr->get_empty_related_viewgrams(view_seg_nums, - this->symmetries_sptr); - viewgrams.fill(1.F); - // find efficiencies - { - const double start_frame = this->frame_defs.get_start_time(this->frame_num); - const double end_frame = this->frame_defs.get_end_time(this->frame_num); - this->normalisation_sptr->undo(viewgrams,start_frame,end_frame); - } - // backproject - { - const int range_to_zero = - view_seg_nums.segment_num() == 0 && this->zero_seg0_end_planes - ? 1 : 0; - const int min_ax_pos_num = - viewgrams.get_min_axial_pos_num() + range_to_zero; - const int max_ax_pos_num = - viewgrams.get_max_axial_pos_num() - range_to_zero; - - this->sens_backprojector_sptr->back_project(sensitivity, viewgrams, min_ax_pos_num, max_ax_pos_num); - } + for (int timing_pos_num = -this->max_timing_pos_num_to_process; + timing_pos_num <= this->max_timing_pos_num_to_process; ++ timing_pos_num) + { + RelatedViewgrams viewgrams = + this->proj_data_sptr->get_empty_related_viewgrams(view_seg_nums, + this->symmetries_sptr, false, timing_pos_num); + viewgrams.fill(1.F); + // find efficiencies + { + const double start_frame = this->frame_defs.get_start_time(this->frame_num); + const double end_frame = this->frame_defs.get_end_time(this->frame_num); + this->normalisation_sptr->undo(viewgrams, start_frame, end_frame); + } + // backproject + { + const int range_to_zero = + view_seg_nums.segment_num() == 0 && this->zero_seg0_end_planes + ? 1 : 0; + const int min_ax_pos_num = + viewgrams.get_min_axial_pos_num() + range_to_zero; + const int max_ax_pos_num = + viewgrams.get_max_axial_pos_num() - range_to_zero; + + this->sens_backprojector_sptr->back_project(sensitivity, viewgrams, min_ax_pos_num, max_ax_pos_num); + } + } } diff --git a/src/utilities/poisson_noise.cxx b/src/utilities/poisson_noise.cxx index 094bdef660..2c7cf7b567 100644 --- a/src/utilities/poisson_noise.cxx +++ b/src/utilities/poisson_noise.cxx @@ -175,7 +175,7 @@ poisson_noise(ProjData& output_projdata, SegmentByView seg_output= output_projdata.get_empty_segment_by_view(seg,false, timing_pos_num); - cerr << "Segment " << seg << endl; + cerr << "Segment " << seg << " Timing position index " << timing_pos_num << endl; for(int view=seg_input.get_min_view_num();view<=seg_input.get_max_view_num();view++) for(int ax_pos=seg_input.get_min_axial_pos_num();ax_pos<=seg_input.get_max_axial_pos_num();ax_pos++) From 623e88bee38d9847c70d724ae6cfc028c7bf078b Mon Sep 17 00:00:00 2001 From: Elise Date: Sun, 7 May 2017 08:49:37 +0100 Subject: [PATCH 072/170] Fix for last commit (min & max timing_pos_num correctly set depending on the value of the flag) --- ...LinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx | 6 +++--- ...oissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx index 9dd4725346..ffd4736cd8 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx @@ -398,9 +398,9 @@ void PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin:: add_view_seg_to_sensitivity(TargetT& sensitivity, const ViewSegmentNumbers& view_seg_nums) const { - for (int timing_pos_num = this->proj_data_info_cyl_sptr->get_min_tof_pos_num(); - timing_pos_num <= this->proj_data_info_cyl_sptr->get_max_tof_pos_num(); - ++timing_pos_num) + int min_timing_pos_num = use_tofsens ? this->proj_data_info_cyl_sptr->get_min_tof_pos_num() : 0; + int max_timing_pos_num = use_tofsens ? this->proj_data_info_cyl_sptr->get_max_tof_pos_num() : 0; + for (int timing_pos_num = min_timing_pos_num; timing_pos_num <= max_timing_pos_num; ++timing_pos_num) { shared_ptr symmetries_used (this->projector_pair_ptr->get_symmetries_used()->clone()); diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx index 192ddb02d4..37e8578eb7 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx @@ -799,8 +799,9 @@ void PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: add_view_seg_to_sensitivity(TargetT& sensitivity, const ViewSegmentNumbers& view_seg_nums) const { - for (int timing_pos_num = -this->max_timing_pos_num_to_process; - timing_pos_num <= this->max_timing_pos_num_to_process; ++ timing_pos_num) + int min_timing_pos_num = use_tofsens ? -this->max_timing_pos_num_to_process : 0; + int max_timing_pos_num = use_tofsens ? this->max_timing_pos_num_to_process : 0; + for (int timing_pos_num = min_timing_pos_num; timing_pos_num <= min_timing_pos_num; ++ timing_pos_num) { RelatedViewgrams viewgrams = this->proj_data_sptr->get_empty_related_viewgrams(view_seg_nums, From f9cb151845f210ec3c28b57ccc414dc19c77eaa0 Mon Sep 17 00:00:00 2001 From: Elise Date: Sun, 7 May 2017 08:58:38 +0100 Subject: [PATCH 073/170] Fix2 --- .../PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx index 37e8578eb7..a81c87284c 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx @@ -801,7 +801,7 @@ add_view_seg_to_sensitivity(TargetT& sensitivity, const ViewSegmentNumbers& view { int min_timing_pos_num = use_tofsens ? -this->max_timing_pos_num_to_process : 0; int max_timing_pos_num = use_tofsens ? this->max_timing_pos_num_to_process : 0; - for (int timing_pos_num = min_timing_pos_num; timing_pos_num <= min_timing_pos_num; ++ timing_pos_num) + for (int timing_pos_num = min_timing_pos_num; timing_pos_num <= max_timing_pos_num; ++timing_pos_num) { RelatedViewgrams viewgrams = this->proj_data_sptr->get_empty_related_viewgrams(view_seg_nums, From c23a6044868ca79725a382ab6b30bd88e9bd4548 Mon Sep 17 00:00:00 2001 From: Elise Date: Wed, 31 May 2017 19:20:36 +0100 Subject: [PATCH 074/170] Fix for Swig --- src/display/CMakeLists.txt | 2 +- src/swig/stir.i | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/display/CMakeLists.txt b/src/display/CMakeLists.txt index adbfbd1a3b..2dcc3ed41e 100644 --- a/src/display/CMakeLists.txt +++ b/src/display/CMakeLists.txt @@ -29,7 +29,7 @@ if( "${GRAPHICS}" STREQUAL "X") elseif("${GRAPHICS}" STREQUAL "PGM") ADD_DEFINITIONS(-DSTIR_PGM) -else() +#else() # TODO #ifeq "$(GRAPHICS)" "MATHLINK" # this presumably needs a Mathlink library, depends on your system though diff --git a/src/swig/stir.i b/src/swig/stir.i index 05ee21e48f..769046d350 100644 --- a/src/swig/stir.i +++ b/src/swig/stir.i @@ -1291,6 +1291,7 @@ namespace stir { %ignore stir::Bin::axial_pos_num(); %ignore stir::Bin::view_num(); %ignore stir::Bin::tangential_pos_num(); +%ignore stir::Bin::timing_pos_num(); %include "stir/Bin.h" %newobject stir::ProjDataInfo::ProjDataInfoGE; %newobject stir::ProjDataInfo::ProjDataInfoCTI; From 4dc60a96eb057b91b6a875971e4fb571b5453427 Mon Sep 17 00:00:00 2001 From: Elise Date: Mon, 19 Jun 2017 18:46:08 +0100 Subject: [PATCH 075/170] Fix for GCC-c++11 in test_time_of_flight --- src/test/test_time_of_flight.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/test_time_of_flight.cxx b/src/test/test_time_of_flight.cxx index 24e9ec45d5..77ba7ba42b 100644 --- a/src/test/test_time_of_flight.cxx +++ b/src/test/test_time_of_flight.cxx @@ -83,7 +83,7 @@ class cache_index{ // Helper class. class FloatFloat{ public: - FloatFloat() { float1 = 0f; float2 = 0f;} + FloatFloat() { float1 = 0.f; float2 = 0.f;} float float1; float float2; }; From 61cf1ec000bc67f4f3da8d6ce6160d1cb4d5b508 Mon Sep 17 00:00:00 2001 From: Ottavia Date: Tue, 27 Jun 2017 17:37:24 +0100 Subject: [PATCH 076/170] needed to add forward_project for a single bin, but currently just exits with error --- .../recon_buildblock/PostsmoothingForwardProjectorByBin.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/include/local/stir/recon_buildblock/PostsmoothingForwardProjectorByBin.h b/src/include/local/stir/recon_buildblock/PostsmoothingForwardProjectorByBin.h index 8a6b0fd212..26dd3253dd 100644 --- a/src/include/local/stir/recon_buildblock/PostsmoothingForwardProjectorByBin.h +++ b/src/include/local/stir/recon_buildblock/PostsmoothingForwardProjectorByBin.h @@ -79,6 +79,10 @@ class PostsmoothingForwardProjectorByBin : const int min_axial_pos_num, const int max_axial_pos_num, const int min_tangential_pos_num, const int max_tangential_pos_num); + virtual void actual_forward_project(Bin&, + const DiscretisedDensity<3,float>&) + { error("Postsmoothing forward projector currently doesn't support projection by Bin"); } + void smooth(Viewgram&, const int min_axial_pos_num, const int max_axial_pos_num, const int min_tangential_pos_num, const int max_tangential_pos_num) const; From a897f197367b0c8bb49ebd7a69fc84b9b7e9ddc0 Mon Sep 17 00:00:00 2001 From: Elise Date: Wed, 19 Jul 2017 18:10:55 +0100 Subject: [PATCH 077/170] Fix for TOF unlisting (tested on ROOT files and for Signa listmode) - make test should fail though --- src/buildblock/ProjDataInfo.cxx | 2 +- src/buildblock/ProjDataInfoCylindrical.cxx | 18 +++- .../ProjDataInfoCylindricalArcCorr.cxx | 7 +- .../ProjDataInfoCylindricalNoArcCorr.cxx | 9 +- src/buildblock/Scanner.cxx | 5 +- src/include/stir/DetectionPositionPair.h | 6 +- src/include/stir/DetectionPositionPair.inl | 21 +++- src/include/stir/LORCoordinates.h | 13 ++- src/include/stir/LORCoordinates.inl | 10 +- src/include/stir/ProjDataInfo.h | 2 +- src/include/stir/ProjDataInfoCylindrical.h | 9 +- .../stir/ProjDataInfoCylindricalArcCorr.h | 2 +- .../stir/ProjDataInfoCylindricalNoArcCorr.h | 15 +-- .../stir/ProjDataInfoCylindricalNoArcCorr.inl | 31 ++++-- src/include/stir/Scanner.inl | 2 +- ...ylindricalScannerWithDiscreteDetectors.inl | 5 +- ...calScannerWithViewTangRingRingEncoding.inl | 1 + .../stir/listmode/CListRecordGESigna.h | 20 +++- src/listmode_buildblock/CListEvent.cxx | 4 +- src/listmode_buildblock/CListModeDataROOT.cxx | 14 +-- src/listmode_buildblock/CListRecordROOT.cxx | 23 +---- src/listmode_buildblock/LmToProjData.cxx | 3 +- ...ataSymmetriesForBins_PET_CartesianGrid.cxx | 47 ++++++--- src/test/test_proj_data_info.cxx | 99 +++++++++++-------- 24 files changed, 230 insertions(+), 138 deletions(-) diff --git a/src/buildblock/ProjDataInfo.cxx b/src/buildblock/ProjDataInfo.cxx index 86d2e197b7..646a19094a 100644 --- a/src/buildblock/ProjDataInfo.cxx +++ b/src/buildblock/ProjDataInfo.cxx @@ -78,7 +78,7 @@ ProjDataInfo::get_k(const Bin& bin) const { // Probably, This condition should be removed, since I have the check odd number in the // set_tof_mash_factor(). - if (!num_tof_bins%2) + if (!(num_tof_bins%2)) return bin.timing_pos_num() * tof_increament_in_mm; else return (bin.timing_pos_num() * tof_increament_in_mm) - tof_increament_in_mm/2.f; diff --git a/src/buildblock/ProjDataInfoCylindrical.cxx b/src/buildblock/ProjDataInfoCylindrical.cxx index 0d54ea6587..37b2cc2bf7 100644 --- a/src/buildblock/ProjDataInfoCylindrical.cxx +++ b/src/buildblock/ProjDataInfoCylindrical.cxx @@ -547,7 +547,8 @@ get_LOR(LORInAxialAndNoArcCorrSinogramCoordinates& lor, LORInAxialAndNoArcCorrSinogramCoordinates(z1, z2, phi, asin(s_in_mm/get_ring_radius()), - get_ring_radius()); + get_ring_radius(), + bin.timing_pos_num()>=0); } void @@ -578,16 +579,20 @@ get_LOR_as_two_points(CartesianCoordinate3D& coord_1, coord_2.x() = s_in_mm*cos(phi) + max_a*sin(phi); coord_2.y() = s_in_mm*sin(phi) - min_a*cos(phi); coord_2.z() = m_in_mm - min_a*tantheta; + + if (bin.timing_pos_num()<0) + std::swap(coord_1, coord_2); } void ProjDataInfoCylindrical:: get_LOR_as_two_points_alt(CartesianCoordinate3D& coord_1, CartesianCoordinate3D& coord_2, - const int& det1, - const int& det2, - const int& ring1, - const int& ring2) const + const int det1, + const int det2, + const int ring1, + const int ring2, + const int timing_pos) const { const int num_detectors_per_ring = get_scanner_ptr()->get_num_detectors_per_ring(); @@ -612,6 +617,9 @@ get_LOR_as_two_points_alt(CartesianCoordinate3D& coord_1, LORAs2Points lor(cyl_coords); coord_1 = lor.p1(); coord_2 = lor.p2(); + + if (timing_pos<0) + std::swap(coord_1, coord_2); } string diff --git a/src/buildblock/ProjDataInfoCylindricalArcCorr.cxx b/src/buildblock/ProjDataInfoCylindricalArcCorr.cxx index 15cdb8ef9f..5df18fec42 100644 --- a/src/buildblock/ProjDataInfoCylindricalArcCorr.cxx +++ b/src/buildblock/ProjDataInfoCylindricalArcCorr.cxx @@ -124,9 +124,14 @@ ProjDataInfoCylindricalArcCorr::parameter_info() const Bin ProjDataInfoCylindricalArcCorr:: -get_bin(const LOR& lor) const +get_bin(const LOR& lor,const double delta_time) const { + if (delta_time != 0) + { + error("TODO NO TOF YET"); + } + Bin bin; LORInAxialAndSinogramCoordinates lor_coords; if (lor.change_representation(lor_coords, get_ring_radius()) == Succeeded::no) diff --git a/src/buildblock/ProjDataInfoCylindricalNoArcCorr.cxx b/src/buildblock/ProjDataInfoCylindricalNoArcCorr.cxx index 83a498ae8a..191d49040f 100644 --- a/src/buildblock/ProjDataInfoCylindricalNoArcCorr.cxx +++ b/src/buildblock/ProjDataInfoCylindricalNoArcCorr.cxx @@ -363,6 +363,7 @@ get_all_det_pos_pairs_for_bin(vector >& dps, dps[current_dp_num].pos1().axial_coord() = rings_iter->first; dps[current_dp_num].pos2().tangential_coord() = det2_num; dps[current_dp_num].pos2().axial_coord() = rings_iter->second; + dps[current_dp_num].timing_pos() = bin.timing_pos_num(); ++current_dp_num; } } @@ -579,7 +580,7 @@ find_bin_given_cartesian_coordinates_of_detection(Bin& bin, Bin ProjDataInfoCylindricalNoArcCorr:: -get_bin(const LOR& lor) const +get_bin(const LOR& lor,const double delta_time) const { Bin bin; #ifndef STIR_DEVEL @@ -607,7 +608,7 @@ get_bin(const LOR& lor) const if (ring1 >=0 && ring1=0 && ring2= get_min_tangential_pos_num() && bin.tangential_pos_num() <= get_max_tangential_pos_num()) { @@ -681,6 +682,10 @@ get_bin(const LOR& lor) const #else // find nearest segment { + if (delta_time!=0) + { + error("TODO TOF"); + } const float delta = (swap_direction ? lor_coords.z1()-lor_coords.z2() diff --git a/src/buildblock/Scanner.cxx b/src/buildblock/Scanner.cxx index f7a6b8c882..8ff31e3d02 100644 --- a/src/buildblock/Scanner.cxx +++ b/src/buildblock/Scanner.cxx @@ -1064,7 +1064,10 @@ if (!close_enough(energy_resolution, scanner.energy_resolution) && (num_transaxial_crystals_per_block == scanner.num_transaxial_crystals_per_block) && (num_detector_layers == scanner.num_detector_layers) && (num_axial_crystals_per_singles_unit == scanner.num_axial_crystals_per_singles_unit) && - (num_transaxial_crystals_per_singles_unit == scanner.num_transaxial_crystals_per_singles_unit); + (num_transaxial_crystals_per_singles_unit == scanner.num_transaxial_crystals_per_singles_unit) && + (max_num_of_timing_bins == scanner.max_num_of_timing_bins) && + close_enough(size_timing_bin, scanner.size_timing_bin) && + close_enough(timing_resolution, scanner.timing_resolution); } diff --git a/src/include/stir/DetectionPositionPair.h b/src/include/stir/DetectionPositionPair.h index b3359bb8f2..6bd6046c9e 100644 --- a/src/include/stir/DetectionPositionPair.h +++ b/src/include/stir/DetectionPositionPair.h @@ -44,12 +44,15 @@ class DetectionPositionPair inline DetectionPositionPair(); inline DetectionPositionPair(const DetectionPosition&, - const DetectionPosition&); + const DetectionPosition&, + const coordT timing_pos = static_cast(0)); inline const DetectionPosition& pos1() const; inline const DetectionPosition& pos2() const; + inline const coordT timing_pos() const; inline DetectionPosition& pos1(); inline DetectionPosition& pos2(); + inline coordT& timing_pos(); //! comparison operators inline bool operator==(const DetectionPositionPair&) const; inline bool operator!=(const DetectionPositionPair&) const; @@ -57,6 +60,7 @@ class DetectionPositionPair private : DetectionPosition p1; DetectionPosition p2; + coordT _timing_pos; }; END_NAMESPACE_STIR diff --git a/src/include/stir/DetectionPositionPair.inl b/src/include/stir/DetectionPositionPair.inl index 73b0776715..5391eeb19c 100644 --- a/src/include/stir/DetectionPositionPair.inl +++ b/src/include/stir/DetectionPositionPair.inl @@ -33,8 +33,9 @@ DetectionPositionPair() template DetectionPositionPair:: DetectionPositionPair(const DetectionPosition& pos1, - const DetectionPosition& pos2) - : p1(pos1), p2(pos2) + const DetectionPosition& pos2, + const coordT timing_pos) + : p1(pos1), p2(pos2), _timing_pos(timing_pos) {} template @@ -49,6 +50,12 @@ DetectionPositionPair:: pos2() const { return p2; } +template +const coordT +DetectionPositionPair:: +timing_pos() const +{ return _timing_pos; } + template DetectionPosition& DetectionPositionPair:: @@ -61,6 +68,12 @@ DetectionPositionPair:: pos2() { return p2; } +template +coordT& +DetectionPositionPair:: +timing_pos() +{ return _timing_pos; } + //! comparison operators template bool @@ -68,8 +81,8 @@ DetectionPositionPair:: operator==(const DetectionPositionPair& p) const { return - (pos1() == p.pos1() && pos2() == p.pos2()) || - (pos1() == p.pos2() && pos2() == p.pos1()) ; + (pos1() == p.pos1() && pos2() == p.pos2() && timing_pos() == p.timing_pos()) || + (pos1() == p.pos2() && pos2() == p.pos1() && timing_pos() == -p.timing_pos()) ; } template diff --git a/src/include/stir/LORCoordinates.h b/src/include/stir/LORCoordinates.h index 3c62f99136..cbbf4e0137 100644 --- a/src/include/stir/LORCoordinates.h +++ b/src/include/stir/LORCoordinates.h @@ -346,7 +346,8 @@ class LORInAxialAndSinogramCoordinates coordT& s() { check_state(); return _s; } coordT beta() const { check_state(); return asin(_s/private_base_type::_radius); } - + bool is_swapped() const { check_state(); return _swapped; } + bool is_swapped() { check_state(); return _swapped; } inline explicit LORInAxialAndSinogramCoordinates(const coordT radius = 1); @@ -360,7 +361,8 @@ class LORInAxialAndSinogramCoordinates const coordT z2, const coordT phi, const coordT s, - const coordT radius =1); + const coordT radius =1, + const bool swapped =false); inline LORInAxialAndSinogramCoordinates(const LORInCylinderCoordinates&); @@ -422,6 +424,7 @@ class LORInAxialAndSinogramCoordinates private: coordT _phi; coordT _s; + bool _swapped; }; @@ -455,6 +458,8 @@ class LORInAxialAndNoArcCorrSinogramCoordinates coordT& phi() { check_state(); return _phi; } coordT beta() const { check_state(); return _beta; } coordT& beta() { check_state(); return _beta; } + bool is_swapped() const { check_state(); return _swapped; } + bool is_swapped() { check_state(); return _swapped; } coordT s() const { check_state(); return private_base_type::_radius*sin(_beta); } @@ -491,7 +496,8 @@ class LORInAxialAndNoArcCorrSinogramCoordinates const coordT z2, const coordT phi, const coordT beta, - const coordT radius =1); + const coordT radius =1, + const bool swapped =false); inline LORInAxialAndNoArcCorrSinogramCoordinates(const LORInCylinderCoordinates&); @@ -532,6 +538,7 @@ class LORInAxialAndNoArcCorrSinogramCoordinates private: coordT _phi; coordT _beta; + bool _swapped; }; /*! \ingroup LOR diff --git a/src/include/stir/LORCoordinates.inl b/src/include/stir/LORCoordinates.inl index 6c84a0947d..9304e733f1 100644 --- a/src/include/stir/LORCoordinates.inl +++ b/src/include/stir/LORCoordinates.inl @@ -88,10 +88,11 @@ LORInAxialAndSinogramCoordinates(const coordT z1, const coordT z2, const coordT phi, const coordT s, - const coordT radius) + const coordT radius, + const bool swapped) : LORCylindricalCoordinates_z_and_radius(z1, z2, radius), - _phi(phi), _s(s) + _phi(phi), _s(s), _swapped(swapped) { check_state(); } @@ -112,9 +113,10 @@ LORInAxialAndNoArcCorrSinogramCoordinates(const coordT z1, const coordT z2, const coordT phi, const coordT beta, - const coordT radius) + const coordT radius, + const bool swapped) : LORCylindricalCoordinates_z_and_radius(z1, z2, radius), - _phi(phi), _beta(beta) + _phi(phi), _beta(beta), _swapped(swapped) { check_state(); diff --git a/src/include/stir/ProjDataInfo.h b/src/include/stir/ProjDataInfo.h index 0a8002fe4b..7fc5221954 100644 --- a/src/include/stir/ProjDataInfo.h +++ b/src/include/stir/ProjDataInfo.h @@ -331,7 +331,7 @@ class ProjDataInfo */ virtual Bin - get_bin(const LOR&) const = 0; + get_bin(const LOR&,const double delta_time = 0.0) const = 0; //! \name Equality of ProjDataInfo objects //@{ diff --git a/src/include/stir/ProjDataInfoCylindrical.h b/src/include/stir/ProjDataInfoCylindrical.h index 8604973aa6..26043bbfd0 100644 --- a/src/include/stir/ProjDataInfoCylindrical.h +++ b/src/include/stir/ProjDataInfoCylindrical.h @@ -112,10 +112,11 @@ class ProjDataInfoCylindrical: public ProjDataInfo void get_LOR_as_two_points_alt(CartesianCoordinate3D& coord_1, CartesianCoordinate3D& coord_2, - const int& det1, - const int& det2, - const int& ring1, - const int& ring2) const; + const int det1, + const int det2, + const int ring1, + const int ring2, + const int timing_pos) const; void set_azimuthal_angle_sampling(const float angle); diff --git a/src/include/stir/ProjDataInfoCylindricalArcCorr.h b/src/include/stir/ProjDataInfoCylindricalArcCorr.h index be7fb6a14c..a7e620f147 100644 --- a/src/include/stir/ProjDataInfoCylindricalArcCorr.h +++ b/src/include/stir/ProjDataInfoCylindricalArcCorr.h @@ -79,7 +79,7 @@ class ProjDataInfoCylindricalArcCorr : public ProjDataInfoCylindrical virtual Bin - get_bin(const LOR&) const; + get_bin(const LOR&, const double delta_time = 0.0) const; virtual std::string parameter_info() const; private: diff --git a/src/include/stir/ProjDataInfoCylindricalNoArcCorr.h b/src/include/stir/ProjDataInfoCylindricalNoArcCorr.h index 99a6ab3c26..8399b7dc99 100644 --- a/src/include/stir/ProjDataInfoCylindricalNoArcCorr.h +++ b/src/include/stir/ProjDataInfoCylindricalNoArcCorr.h @@ -241,27 +241,18 @@ class ProjDataInfoCylindricalNoArcCorr : public ProjDataInfoCylindrical inline Succeeded get_bin_for_det_pair(Bin&, const int det1_num, const int ring1_num, - const int det2_num, const int ring2_num) const; - + const int det2_num, const int ring2_num, + const int timing_pos_num = 0) const; //! This routine gets the detector pair corresponding to a bin. - /*! - \see get_det_pair_for_view_tangential_pos_num() for - restrictions. In addition, this routine only works for span=1 data, - i.e. no axial compression. - \obsolete */ inline void - get_det_pair_for_bin( - int& det1_num, int& ring1_num, - int& det2_num, int& ring2_num, - const Bin&) const; //@} virtual Bin - get_bin(const LOR&) const; + get_bin(const LOR&,const double delta_time) const; //! \name set of obsolete functions to go between bins<->LORs (will disappear!) diff --git a/src/include/stir/ProjDataInfoCylindricalNoArcCorr.inl b/src/include/stir/ProjDataInfoCylindricalNoArcCorr.inl index 10442913e0..1b9894d8e6 100644 --- a/src/include/stir/ProjDataInfoCylindricalNoArcCorr.inl +++ b/src/include/stir/ProjDataInfoCylindricalNoArcCorr.inl @@ -30,6 +30,7 @@ #include "stir/Bin.h" #include "stir/Succeeded.h" +#include "stir/round.h" #include START_NAMESPACE_STIR @@ -135,12 +136,19 @@ Succeeded ProjDataInfoCylindricalNoArcCorr:: get_bin_for_det_pair(Bin& bin, const int det_num1, const int ring_num1, - const int det_num2, const int ring_num2) const + const int det_num2, const int ring_num2, + const int timing_pos_num) const { if (get_view_tangential_pos_num_for_det_num_pair(bin.view_num(), bin.tangential_pos_num(), det_num1, det_num2)) - return get_segment_axial_pos_num_for_ring_pair(bin.segment_num(), bin.axial_pos_num(), ring_num1, ring_num2); + { + bin.timing_pos_num() = timing_pos_num; + return get_segment_axial_pos_num_for_ring_pair(bin.segment_num(), bin.axial_pos_num(), ring_num1, ring_num2); + } else + { + bin.timing_pos_num() = -timing_pos_num; return get_segment_axial_pos_num_for_ring_pair(bin.segment_num(), bin.axial_pos_num(), ring_num2, ring_num1); + } } Succeeded @@ -148,12 +156,14 @@ ProjDataInfoCylindricalNoArcCorr:: get_bin_for_det_pos_pair(Bin& bin, const DetectionPositionPair<>& dp) const { + assert(this->get_tof_mash_factor()>0); return get_bin_for_det_pair(bin, dp.pos1().tangential_coord(), dp.pos1().axial_coord(), - dp.pos2().tangential_coord(), - dp.pos2().axial_coord()); + dp.pos2().tangential_coord(), + dp.pos2().axial_coord(), + stir::round((float)dp.timing_pos()/this->get_tof_mash_factor())); } void ProjDataInfoCylindricalNoArcCorr:: @@ -162,8 +172,16 @@ get_det_pair_for_bin( int& det_num2, int& ring_num2, const Bin& bin) const { - get_det_num_pair_for_view_tangential_pos_num(det_num1, det_num2, bin.view_num(), bin.tangential_pos_num()); - get_ring_pair_for_segment_axial_pos_num( ring_num1, ring_num2, bin.segment_num(), bin.axial_pos_num()); + if (bin.timing_pos_num()>=0) + { + get_det_num_pair_for_view_tangential_pos_num(det_num1, det_num2, bin.view_num(), bin.tangential_pos_num()); + get_ring_pair_for_segment_axial_pos_num( ring_num1, ring_num2, bin.segment_num(), bin.axial_pos_num()); + } + else + { + get_det_num_pair_for_view_tangential_pos_num(det_num2, det_num1, bin.view_num(), bin.tangential_pos_num()); + get_ring_pair_for_segment_axial_pos_num( ring_num2, ring_num1, bin.segment_num(), bin.axial_pos_num()); + } } void @@ -183,6 +201,7 @@ get_det_pos_pair_for_bin( dp.pos1().axial_coord()=a1; dp.pos2().tangential_coord()=t2; dp.pos2().axial_coord()=a2; + dp.timing_pos() = std::abs(bin.timing_pos_num())*this->get_tof_mash_factor(); #else diff --git a/src/include/stir/Scanner.inl b/src/include/stir/Scanner.inl index 952dce0db1..4dcb149cba 100644 --- a/src/include/stir/Scanner.inl +++ b/src/include/stir/Scanner.inl @@ -247,7 +247,7 @@ float Scanner::get_timing_resolution() const bool Scanner::is_tof_ready() const { return (max_num_of_timing_bins > 0 - && timing_resolution > 0.0f + && size_timing_bin > 0.0f && timing_resolution > 0.0f); } diff --git a/src/include/stir/listmode/CListEventCylindricalScannerWithDiscreteDetectors.inl b/src/include/stir/listmode/CListEventCylindricalScannerWithDiscreteDetectors.inl index d6d337b072..6f0b3f45ea 100644 --- a/src/include/stir/listmode/CListEventCylindricalScannerWithDiscreteDetectors.inl +++ b/src/include/stir/listmode/CListEventCylindricalScannerWithDiscreteDetectors.inl @@ -40,7 +40,8 @@ CListEventCylindricalScannerWithDiscreteDetectors(const shared_ptr& sca 1, scanner_sptr->get_num_rings()-1, scanner_sptr->get_num_detectors_per_ring()/2, scanner_sptr->get_default_num_arccorrected_bins(), - false))); + false, + /*TOF mashing factor*/1))); } LORAs2Points @@ -86,8 +87,6 @@ get_bin(Bin& bin, const ProjDataInfo& proj_data_info) const else { bin.set_bin_value(1); - if (proj_data_info.get_num_tof_poss() > 1) - bin.timing_pos_num() = proj_data_info.get_tof_bin(delta_time); } } diff --git a/src/include/stir/listmode/CListEventCylindricalScannerWithViewTangRingRingEncoding.inl b/src/include/stir/listmode/CListEventCylindricalScannerWithViewTangRingRingEncoding.inl index c599375c5f..14bb23fbf2 100644 --- a/src/include/stir/listmode/CListEventCylindricalScannerWithViewTangRingRingEncoding.inl +++ b/src/include/stir/listmode/CListEventCylindricalScannerWithViewTangRingRingEncoding.inl @@ -47,6 +47,7 @@ get_detection_position(DetectionPositionPair<>& det_pos) const view_num, tangential_pos_num); det_pos.pos1().tangential_coord() = det_num_1; det_pos.pos2().tangential_coord() = det_num_2; + det_pos.timing_pos() = this->get_uncompressed_proj_data_info_sptr()->get_tof_bin(delta_time); } template diff --git a/src/include/stir/listmode/CListRecordGESigna.h b/src/include/stir/listmode/CListRecordGESigna.h index f54cbc023b..d6b35efc82 100644 --- a/src/include/stir/listmode/CListRecordGESigna.h +++ b/src/include/stir/listmode/CListRecordGESigna.h @@ -67,10 +67,22 @@ class CListEventDataGESigna } inline void get_detection_position(DetectionPositionPair<>& det_pos) const { - det_pos.pos1().tangential_coord() = loXtalTransAxID; - det_pos.pos1().axial_coord() = loXtalAxialID; - det_pos.pos2().tangential_coord() = hiXtalTransAxID; - det_pos.pos2().axial_coord() = hiXtalAxialID; + if (deltaTime<0) + { + det_pos.pos1().tangential_coord() = hiXtalTransAxID; + det_pos.pos1().axial_coord() = hiXtalAxialID; + det_pos.pos2().tangential_coord() = loXtalTransAxID; + det_pos.pos2().axial_coord() = loXtalAxialID; + det_pos.timing_pos() = -get_tof_bin(); + } + else + { + det_pos.pos1().tangential_coord() = loXtalTransAxID; + det_pos.pos1().axial_coord() = loXtalAxialID; + det_pos.pos2().tangential_coord() = hiXtalTransAxID; + det_pos.pos2().axial_coord() = hiXtalAxialID; + det_pos.timing_pos() = get_tof_bin(); + } } inline bool is_event() const { diff --git a/src/listmode_buildblock/CListEvent.cxx b/src/listmode_buildblock/CListEvent.cxx index 3f5f92bbd0..5edcf8b2d3 100644 --- a/src/listmode_buildblock/CListEvent.cxx +++ b/src/listmode_buildblock/CListEvent.cxx @@ -45,9 +45,7 @@ void CListEvent:: get_bin(Bin& bin, const ProjDataInfo& proj_data_info) const { - bin = proj_data_info.get_bin(get_LOR()); - if (proj_data_info.get_num_tof_poss() > 1) - bin.timing_pos_num() = proj_data_info.get_tof_bin(delta_time); + bin = proj_data_info.get_bin(get_LOR(),delta_time); } END_NAMESPACE_STIR diff --git a/src/listmode_buildblock/CListModeDataROOT.cxx b/src/listmode_buildblock/CListModeDataROOT.cxx index 5fe8ead23a..7c3899f86e 100644 --- a/src/listmode_buildblock/CListModeDataROOT.cxx +++ b/src/listmode_buildblock/CListModeDataROOT.cxx @@ -54,7 +54,7 @@ CListModeDataROOT(const std::string& listmode_filename) max_num_timing_bins = -1; size_timing_bin = -1.f; timing_resolution = -1.f; - tof_mash_factor = -1; + tof_mash_factor = 1; // Scanner related & Physical dimentions. this->parser.add_key("originating system", &this->originating_system); @@ -76,8 +76,8 @@ CListModeDataROOT(const std::string& listmode_filename) // this->parser.add_key("%number_of_segments", &number_of_segments); this->parser.add_key("number of TOF time bins", &this->max_num_timing_bins); - this->parser.add_key("Size of timing bin (in picoseconds)", &this->size_timing_bin); - this->parser.add_key("Timing resolution (in picoseconds)", &this->timing_resolution); + this->parser.add_key("Size of timing bin (ps)", &this->size_timing_bin); + this->parser.add_key("Timing resolution (ps)", &this->timing_resolution); this->parser.add_key("%TOF mashing factor", &this->tof_mash_factor); // @@ -160,9 +160,11 @@ CListModeDataROOT(const std::string& listmode_filename) num_rings-1, num_detectors_per_ring/2, max_num_non_arccorrected_bins, - /* arc_correction*/false)); - if (tof_mash_factor > 0) - this->proj_data_info_sptr->set_tof_mash_factor(tof_mash_factor); + /* arc_correction*/false, + tof_mash_factor)); + + if (tof_mash_factor != 1) + error("TOF mashing factor for ROOT different from 1 not implemented yet."); } std::string diff --git a/src/listmode_buildblock/CListRecordROOT.cxx b/src/listmode_buildblock/CListRecordROOT.cxx index 4aa4051dc6..8711fa3ae9 100644 --- a/src/listmode_buildblock/CListRecordROOT.cxx +++ b/src/listmode_buildblock/CListRecordROOT.cxx @@ -51,6 +51,7 @@ void CListEventROOT::get_detection_position(DetectionPositionPair<>& _det_pos) c _det_pos.pos1() = det1; _det_pos.pos2() = det2; + _det_pos.timing_pos() = this->get_uncompressed_proj_data_info_sptr()->get_tof_bin(delta_time); } void CListEventROOT::set_detection_position(const DetectionPositionPair<>&) @@ -77,24 +78,10 @@ void CListEventROOT::init_from_data(const int& _ring1, const int& _ring2, else if ( det2 >= scanner_sptr->get_num_detectors_per_ring()) det2 = det2 - scanner_sptr->get_num_detectors_per_ring(); - if (det1 > det2) - { - int tmp = det1; - det1 = det2; - det2 = tmp; - - ring1 = _ring2; - ring2 = _ring1; - delta_time = -_delta_time; - swapped = true; - } - else - { - ring1 = _ring1; - ring2 = _ring2; - delta_time = _delta_time; - swapped = false; - } + ring1 = _ring1; + ring2 = _ring2; + delta_time = _delta_time; + swapped = false; } END_NAMESPACE_STIR diff --git a/src/listmode_buildblock/LmToProjData.cxx b/src/listmode_buildblock/LmToProjData.cxx index 81c01d7131..0fc7b8a2e7 100644 --- a/src/listmode_buildblock/LmToProjData.cxx +++ b/src/listmode_buildblock/LmToProjData.cxx @@ -316,7 +316,8 @@ post_processing() 1, scanner_ptr->get_num_rings()-1, scanner_ptr->get_num_detectors_per_ring()/2, scanner_ptr->get_default_num_arccorrected_bins(), - false))); + false, + 1))); if ( normalisation_ptr->set_up(proj_data_info_cyl_uncompressed_ptr) != Succeeded::yes) diff --git a/src/recon_test/test_DataSymmetriesForBins_PET_CartesianGrid.cxx b/src/recon_test/test_DataSymmetriesForBins_PET_CartesianGrid.cxx index 25206f03c8..1ca8acba3d 100644 --- a/src/recon_test/test_DataSymmetriesForBins_PET_CartesianGrid.cxx +++ b/src/recon_test/test_DataSymmetriesForBins_PET_CartesianGrid.cxx @@ -138,15 +138,16 @@ run_tests_2_proj_matrices_1_bin(const ProjMatrixByBin& proj_matrix_no_symm, cerr << "Current bin: segment = " << bin.segment_num() << ", axial pos " << bin.axial_pos_num() << ", view = " << bin.view_num() - << ", tangential_pos_num = " << bin.tangential_pos_num() << "\n"; + << ", tangential_pos_num = " << bin.tangential_pos_num() + << ", timing position index = " << bin.timing_pos_num() << "\n"; ProjMatrixElemsForOneBin::const_iterator no_sym_iter= elems_no_sym.begin(); ProjMatrixElemsForOneBin::const_iterator with_sym_iter = elems_with_sym.begin(); while (no_sym_iter!= elems_no_sym.end() || with_sym_iter!=elems_with_sym.end()) - { + { if (no_sym_iter==elems_no_sym.end() || with_sym_iter==elems_with_sym.end() || no_sym_iter->get_coords()!= with_sym_iter->get_coords() || - fabs(no_sym_iter->get_value()/with_sym_iter->get_value() -1) > .0002) + fabs(no_sym_iter->get_value()/with_sym_iter->get_value() -1) > .01) { bool inc_no_sym_iter = false; if (no_sym_iter!=elems_no_sym.end() && @@ -195,16 +196,19 @@ run_tests_2_proj_matrices(const ProjMatrixByBin& proj_matrix_no_symm, for (int v=proj_data_info_sptr->get_min_view_num(); v <= proj_data_info_sptr->get_max_view_num(); ++v) - for (int a=proj_data_info_sptr->get_min_axial_pos_num(s); - a <= proj_data_info_sptr->get_max_axial_pos_num(s); - ++a) - for (int t=-6; t<=6; t+=3) - { - const Bin bin(s,v,a,t); - //SYM if (proj_matrix_with_symm.get_symmetries_ptr()->is_basic(bin)) - run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, - proj_matrix_with_symm, bin); - } + for (int timing_pos=proj_data_info_sptr->get_min_tof_pos_num(); + timing_pos<=proj_data_info_sptr->get_max_tof_pos_num(); + ++timing_pos) + for (int a=proj_data_info_sptr->get_min_axial_pos_num(s); + a <= proj_data_info_sptr->get_max_axial_pos_num(s); + ++a) + for (int t=-6; t<=6; t+=3) + { + const Bin bin(s,v,a,t,timing_pos); + //SYM if (proj_matrix_with_symm.get_symmetries_ptr()->is_basic(bin)) + run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, + proj_matrix_with_symm, bin); + } #else const int oblique_seg_num = proj_data_info_sptr->get_max_segment_num(); @@ -711,6 +715,23 @@ DataSymmetriesForBins_PET_CartesianGridTests::run_tests() run_tests_for_1_projdata(proj_data_info_sptr); } + { + cerr << "Testing with proj_data_info with time-of-flight"; + // warning: make sure that parameters are ok such that hard-wired + // bins above are fine (e.g. segment 3 should be allowed) + shared_ptr scanner_sptr(new Scanner(Scanner::PETMR_Signa)); + proj_data_info_sptr.reset( + ProjDataInfo::ProjDataInfoCTI(scanner_sptr, + /*span=*/11, + /*max_delta=*/scanner_sptr->get_num_rings()-1, + /*num_views=*/scanner_sptr->get_num_detectors_per_ring()/8, + /*num_tang_poss=*/64, + /*arc_corrected*/false, + /*tof_mashing*/39)); + + + run_tests_for_1_projdata(proj_data_info_sptr); + } } else { diff --git a/src/test/test_proj_data_info.cxx b/src/test/test_proj_data_info.cxx index 45b153c991..e159b49a2e 100644 --- a/src/test/test_proj_data_info.cxx +++ b/src/test/test_proj_data_info.cxx @@ -594,7 +594,7 @@ run_tests() /*views*/ scanner_ptr->get_num_detectors_per_ring()/2, /*tang_pos*/64, /*arc_corrected*/ false)); - test_proj_data_info(dynamic_cast(*proj_data_info_ptr)); + test_proj_data_info(dynamic_cast(*proj_data_info_ptr)); cerr << "\nTests with proj_data_info with mashing and axial compression\n\n"; proj_data_info_ptr.reset( @@ -603,9 +603,18 @@ run_tests() /*views*/ scanner_ptr->get_num_detectors_per_ring()/2/8, /*tang_pos*/64, /*arc_corrected*/ false)); - test_proj_data_info(dynamic_cast(*proj_data_info_ptr)); + test_proj_data_info(dynamic_cast(*proj_data_info_ptr)); cerr << "\nTests with proj_data_info with time-of-flight\n\n"; + shared_ptr scanner_tof_ptr(new Scanner(Scanner::PETMR_Signa)); + proj_data_info_ptr.reset( + ProjDataInfo::ProjDataInfoCTI(scanner_tof_ptr, + /*span*/11, scanner_tof_ptr->get_num_rings()-1, + /*views*/ scanner_tof_ptr->get_num_detectors_per_ring()/2, + /*tang_pos*/64, + /*arc_corrected*/ false, + /*tof_mashing*/39)); + test_proj_data_info(dynamic_cast(*proj_data_info_ptr)); } void @@ -759,50 +768,54 @@ test_proj_data_info(ProjDataInfoCylindricalNoArcCorr& proj_data_info) Bin bin(0,0,0,0,0,0.0f); // set value for comparison later on bin.set_bin_value(0.f); - for (bin.segment_num() = max(-5,proj_data_info.get_min_segment_num()); - bin.segment_num() <= min(5,proj_data_info.get_max_segment_num()); - ++bin.segment_num()) - for (bin.axial_pos_num() = proj_data_info.get_min_axial_pos_num(bin.segment_num()); - bin.axial_pos_num() <= proj_data_info.get_max_axial_pos_num(bin.segment_num()); - ++bin.axial_pos_num()) + for (bin.timing_pos_num() = proj_data_info.get_min_tof_pos_num(); + bin.timing_pos_num() <= proj_data_info.get_max_tof_pos_num(); + ++bin.timing_pos_num()) + for (bin.segment_num() = max(-5,proj_data_info.get_min_segment_num()); + bin.segment_num() <= min(5,proj_data_info.get_max_segment_num()); + ++bin.segment_num()) + for (bin.axial_pos_num() = proj_data_info.get_min_axial_pos_num(bin.segment_num()); + bin.axial_pos_num() <= proj_data_info.get_max_axial_pos_num(bin.segment_num()); + ++bin.axial_pos_num()) #ifdef STIR_OPENMP - // insert a parallel for here for testing. - // we do it at this level to avoid too much overhead for the thread creation, while still having enough jobs to do - // Note that the omp construct needs an int loop variable + // insert a parallel for here for testing. + // we do it at this level to avoid too much overhead for the thread creation, while still having enough jobs to do // Note that the omp construct needs an int loop variable #pragma omp parallel for firstprivate(bin) #endif - for (int tangential_pos_num = -(num_detectors/2)+1; - tangential_pos_num < num_detectors/2; - ++tangential_pos_num) - for (bin.view_num() = 0; bin.view_num() < num_detectors/2; ++bin.view_num()) - { - // set from for-loop variable - bin.tangential_pos_num() = tangential_pos_num; - Bin new_bin(0,0,0,0,0,0.0f); - // set value for comparison with bin - new_bin.set_bin_value(0); - DetectionPositionPair<> det_pos_pair; - proj_data_info.get_det_pos_pair_for_bin(det_pos_pair, bin); - - const bool there_is_a_bin = - proj_data_info.get_bin_for_det_pos_pair(new_bin, - det_pos_pair) == - Succeeded::yes; - if (!check(there_is_a_bin, "checking if there is a bin for this det_pos_pair") || - !check(bin == new_bin, "checking if we round-trip to the same bin")) - { - cerr << "Problem at segment = " << bin.segment_num() - << ", axial pos " << bin.axial_pos_num() - << ", view = " << bin.view_num() - << ", tangential_pos_num = " << bin.tangential_pos_num() << "\n"; - if (there_is_a_bin) - cerr << " bin -> dets -> bin, gives new numbers:\n\t" - << "segment = " << new_bin.segment_num() - << ", axial pos " << new_bin.axial_pos_num() - << ", view = " << new_bin.view_num() - << ", tangential_pos_num = " << new_bin.tangential_pos_num() - << endl; - } + for (int tangential_pos_num = -(num_detectors/2)+1; + tangential_pos_num < num_detectors/2; + ++tangential_pos_num) + for (bin.view_num() = 0; bin.view_num() < num_detectors/2; ++bin.view_num()) + { + // set from for-loop variable + bin.tangential_pos_num() = tangential_pos_num; + Bin new_bin(0,0,0,0,0,0.0f); + // set value for comparison with bin + new_bin.set_bin_value(0); + DetectionPositionPair<> det_pos_pair; + proj_data_info.get_det_pos_pair_for_bin(det_pos_pair, bin); + + const bool there_is_a_bin = + proj_data_info.get_bin_for_det_pos_pair(new_bin, + det_pos_pair) == + Succeeded::yes; + if (!check(there_is_a_bin, "checking if there is a bin for this det_pos_pair") || + !check(bin == new_bin, "checking if we round-trip to the same bin")) + { + cerr << "Problem at segment = " << bin.segment_num() + << ", axial pos " << bin.axial_pos_num() + << ", view = " << bin.view_num() + << ", tangential_pos_num = " << bin.tangential_pos_num() + << ", timing pos num = " << bin.timing_pos_num() << "\n"; + if (there_is_a_bin) + cerr << " bin -> dets -> bin, gives new numbers:\n\t" + << "segment = " << new_bin.segment_num() + << ", axial pos " << new_bin.axial_pos_num() + << ", view = " << new_bin.view_num() + << ", tangential_pos_num = " << new_bin.tangential_pos_num() + << ", timing pos num = " << new_bin.timing_pos_num() + << endl; + } } // end of get_det_pos_pair_for_bin and back code } From d2f39d994882f63eccdd47072f9d09c0664e5a17 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Wed, 19 Jul 2017 20:24:21 +0100 Subject: [PATCH 078/170] New improved and cleaner apply_tof_kernel() and minor corrections On tests on individula lors I found tha this method of application works a bit better and is much cleaner. --- .../stir/ProjDataInfoCylindricalNoArcCorr.h | 6 +++- .../stir/ProjDataInfoCylindricalNoArcCorr.inl | 18 +++++------ .../stir/recon_buildblock/ProjMatrixByBin.inl | 32 ++++++++----------- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/include/stir/ProjDataInfoCylindricalNoArcCorr.h b/src/include/stir/ProjDataInfoCylindricalNoArcCorr.h index 8399b7dc99..2e5e758eb9 100644 --- a/src/include/stir/ProjDataInfoCylindricalNoArcCorr.h +++ b/src/include/stir/ProjDataInfoCylindricalNoArcCorr.h @@ -245,8 +245,12 @@ class ProjDataInfoCylindricalNoArcCorr : public ProjDataInfoCylindrical const int timing_pos_num = 0) const; //! This routine gets the detector pair corresponding to a bin. - */ + inline void + get_det_pair_for_bin( + int& det_num1, int& ring_num1, + int& det_num2, int& ring_num2, + const Bin& bin) const; //@} diff --git a/src/include/stir/ProjDataInfoCylindricalNoArcCorr.inl b/src/include/stir/ProjDataInfoCylindricalNoArcCorr.inl index 1b9894d8e6..c08e3214c5 100644 --- a/src/include/stir/ProjDataInfoCylindricalNoArcCorr.inl +++ b/src/include/stir/ProjDataInfoCylindricalNoArcCorr.inl @@ -168,18 +168,18 @@ get_bin_for_det_pos_pair(Bin& bin, void ProjDataInfoCylindricalNoArcCorr:: get_det_pair_for_bin( - int& det_num1, int& ring_num1, - int& det_num2, int& ring_num2, - const Bin& bin) const + int& det_num1, int& ring_num1, + int& det_num2, int& ring_num2, + const Bin& bin) const { if (bin.timing_pos_num()>=0) { - get_det_num_pair_for_view_tangential_pos_num(det_num1, det_num2, bin.view_num(), bin.tangential_pos_num()); + get_det_num_pair_for_view_tangential_pos_num(det_num1, det_num2, bin.view_num(), bin.tangential_pos_num()); get_ring_pair_for_segment_axial_pos_num( ring_num1, ring_num2, bin.segment_num(), bin.axial_pos_num()); } else { - get_det_num_pair_for_view_tangential_pos_num(det_num2, det_num1, bin.view_num(), bin.tangential_pos_num()); + get_det_num_pair_for_view_tangential_pos_num(det_num2, det_num1, bin.view_num(), bin.tangential_pos_num()); get_ring_pair_for_segment_axial_pos_num( ring_num2, ring_num1, bin.segment_num(), bin.axial_pos_num()); } } @@ -187,12 +187,12 @@ get_det_pair_for_bin( void ProjDataInfoCylindricalNoArcCorr:: get_det_pos_pair_for_bin( - DetectionPositionPair<>& dp, - const Bin& bin) const + DetectionPositionPair<>& dp, + const Bin& bin) const { //lousy work around because types don't match TODO remove! #if 1 - int t1=dp.pos1().tangential_coord(), + int t1=dp.pos1().tangential_coord(), a1=dp.pos1().axial_coord(), t2=dp.pos2().tangential_coord(), a2=dp.pos2().axial_coord(); @@ -207,7 +207,7 @@ get_det_pos_pair_for_bin( get_det_pair_for_bin(dp.pos1().tangential_coord(), dp.pos1().axial_coord(), - dp.pos2().tangential_coord(), + dp.pos2().tangential_coord(), dp.pos2().axial_coord(), bin); #endif diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl index 72e2ced515..5bfb35b068 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl +++ b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl @@ -193,9 +193,13 @@ ProjMatrixByBin::apply_tof_kernel(ProjMatrixElemsForOneBin& nonTOF_probabilities float low_dist = 0.f; float high_dist = 0.f; - float lor_length = std::sqrt((point1.x() - point2.x()) *(point1.x() - point2.x()) + + float lor_length = 1 / (0.5 * std::sqrt((point1.x() - point2.x()) *(point1.x() - point2.x()) + (point1.y() - point2.y()) *(point1.y() - point2.y()) + - (point1.z() - point2.z()) *(point1.z() - point2.z())); + (point1.z() - point2.z()) *(point1.z() - point2.z()))); + + // THe direction can be from 1 -> 2 depending on the bin sign. + const CartesianCoordinate3D middle = (point1 + point2)/2.f; + const CartesianCoordinate3D difference = point2 - middle; for (ProjMatrixElemsForOneBin::iterator element_ptr = nonTOF_probabilities.begin(); element_ptr != nonTOF_probabilities.end(); ++element_ptr) @@ -205,30 +209,22 @@ ProjMatrixByBin::apply_tof_kernel(ProjMatrixElemsForOneBin& nonTOF_probabilities project_point_on_a_line(point1, point2, voxel_center ); - float d1 = std::sqrt((point1.x() - voxel_center.x()) *(point1.x() - voxel_center.x()) + - (point1.y() - voxel_center.y()) *(point1.y() - voxel_center.y()) + - (point1.z() - voxel_center.z()) *(point1.z() - voxel_center.z())); - - // This might be risky. - // The advantage is significant speed up. - // float d2 = std::sqrt( (point2.x() - voxel_center.x()) *(point2.x() - voxel_center.x()) + - // (point2.y() - voxel_center.y()) *(point2.y() - voxel_center.y()) + - // (point2.z() - voxel_center.z()) *(point2.z() - voxel_center.z())); + CartesianCoordinate3D x = voxel_center - middle; - float m = (lor_length - d1 - d1) * 0.5f; - low_dist = (proj_data_info_sptr->tof_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].low_lim - m) * r_sqrt2_gauss_sigma; - high_dist = (proj_data_info_sptr->tof_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].high_lim - m) * r_sqrt2_gauss_sigma; + float d1 = - inner_product(x, difference) * lor_length; - // Cut-off really small values. - // Currently deactivate untill I run all the tests. - //if (abs(low_dist) > 5.5 && abs(high_dist) > 5.5) - //continue; + low_dist = (proj_data_info_sptr->tof_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].low_lim -d1) * r_sqrt2_gauss_sigma; + high_dist = (proj_data_info_sptr->tof_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].high_lim -d1) * r_sqrt2_gauss_sigma; get_tof_value(low_dist, high_dist, new_value); +// std::cout <get_value(); + //if (new_value <= 0.000001F) // Too small to bother? + // continue; tof_probabilities.push_back(ProjMatrixElemsForOneBin::value_type(element_ptr->get_coords(), new_value)); } +// int nikos= 0; } void From 915ce908dd6162edef7d61b13f29a0a5978f83d9 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Wed, 19 Jul 2017 20:26:10 +0100 Subject: [PATCH 079/170] Forgot to remove some silly comments. --- src/include/stir/recon_buildblock/ProjMatrixByBin.inl | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl index 5bfb35b068..77b0612ae8 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl +++ b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl @@ -198,7 +198,7 @@ ProjMatrixByBin::apply_tof_kernel(ProjMatrixElemsForOneBin& nonTOF_probabilities (point1.z() - point2.z()) *(point1.z() - point2.z()))); // THe direction can be from 1 -> 2 depending on the bin sign. - const CartesianCoordinate3D middle = (point1 + point2)/2.f; + const CartesianCoordinate3D middle = (point1 + point2)*0.5f; const CartesianCoordinate3D difference = point2 - middle; for (ProjMatrixElemsForOneBin::iterator element_ptr = nonTOF_probabilities.begin(); @@ -217,14 +217,12 @@ ProjMatrixByBin::apply_tof_kernel(ProjMatrixElemsForOneBin& nonTOF_probabilities high_dist = (proj_data_info_sptr->tof_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].high_lim -d1) * r_sqrt2_gauss_sigma; get_tof_value(low_dist, high_dist, new_value); -// std::cout <get_value(); - //if (new_value <= 0.000001F) // Too small to bother? - // continue; + tof_probabilities.push_back(ProjMatrixElemsForOneBin::value_type(element_ptr->get_coords(), new_value)); } -// int nikos= 0; } void From 2945feefbd6ab897590ce4c5ba0fa428317164c5 Mon Sep 17 00:00:00 2001 From: Elise Date: Thu, 20 Jul 2017 10:25:56 +0100 Subject: [PATCH 080/170] Fix of previous commit --- src/include/stir/ProjDataInfoCylindricalNoArcCorr.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/include/stir/ProjDataInfoCylindricalNoArcCorr.h b/src/include/stir/ProjDataInfoCylindricalNoArcCorr.h index 8399b7dc99..bb3bf952f3 100644 --- a/src/include/stir/ProjDataInfoCylindricalNoArcCorr.h +++ b/src/include/stir/ProjDataInfoCylindricalNoArcCorr.h @@ -245,8 +245,17 @@ class ProjDataInfoCylindricalNoArcCorr : public ProjDataInfoCylindrical const int timing_pos_num = 0) const; //! This routine gets the detector pair corresponding to a bin. + /*! + \see get_det_pair_for_view_tangential_pos_num() for + restrictions. In addition, this routine only works for span=1 data, + i.e. no axial compression. + \obsolete */ inline void + get_det_pair_for_bin( + int& det1_num, int& ring1_num, + int& det2_num, int& ring2_num, + const Bin&) const; //@} From fa6e5bb34dc7aab2daffaefaeeaac574c9a8ac3f Mon Sep 17 00:00:00 2001 From: Kris Thielemans Date: Sat, 9 Sep 2017 16:46:12 +0100 Subject: [PATCH 081/170] Bug fixes for TOF This commit fixes some bugs found with Elise Emond and Nikos Efthimiou: - Fixed bug in get_tof_bin (a delta in the last bin was assigned to bin 0). Also, let it return 0 if non-tof such that it is always usable. - Fixed bugs in conversion from mm to TOF-time and vice versa (factor 2). This also needed a fix in the ROOT support (as the same factor 2 was present). - Removed "minimum timing step (in picoseconds)" keyword from ROOT header as this should be hard-wired. - introduced mm_to_tof_delta_time, tof_delta_time_to_mm and get_tof_delta_time in ProjDataInfo - fixed (probably irrelevant) bug in get_bin_for_det_pos_pair for non-TOF data, where timing_pos was set undefined (and an assert was thrown) - let DetectionPositionPair constructors set timing_pos to 0 if not specified as this is what non-TOF would have expected (this fixes an assert in the test code). - Add TOF-loops to test_proj_data_info Tests still fail on swapping of the TOF-bin. --- src/IO/InputStreamFromROOTFile.cxx | 5 +- src/buildblock/ProjDataInfo.cxx | 14 +- .../ProjDataInfoCylindricalNoArcCorr.cxx | 2 +- src/include/stir/DetectionPositionPair.h | 7 +- src/include/stir/DetectionPositionPair.inl | 12 +- src/include/stir/ProjDataInfo.h | 15 +- src/include/stir/ProjDataInfo.inl | 34 ++- .../stir/ProjDataInfoCylindricalNoArcCorr.inl | 5 +- src/recon_buildblock/ProjMatrixByBin.cxx | 2 +- src/test/test_proj_data_info.cxx | 287 ++++++++++-------- src/test/test_time_of_flight.cxx | 6 +- 11 files changed, 242 insertions(+), 147 deletions(-) diff --git a/src/IO/InputStreamFromROOTFile.cxx b/src/IO/InputStreamFromROOTFile.cxx index 3e78e1244b..b8e38dc7a0 100644 --- a/src/IO/InputStreamFromROOTFile.cxx +++ b/src/IO/InputStreamFromROOTFile.cxx @@ -28,6 +28,7 @@ InputStreamFromROOTFile() { starting_stream_position = 0; reset(); + least_significant_clock_bit = 1.0e+12; // TODO remove cst or rename } @@ -42,13 +43,13 @@ InputStreamFromROOTFile(std::string filename, low_energy_window(low_energy_window), up_energy_window(up_energy_window), offset_dets(offset_dets) { starting_stream_position = 0; + least_significant_clock_bit = 1.0e+12; reset(); } void InputStreamFromROOTFile::set_defaults() { - least_significant_clock_bit = 1.0; } void @@ -62,7 +63,6 @@ InputStreamFromROOTFile::initialise_keymap() this->parser.add_key("offset (num of detectors)", &this->offset_dets); this->parser.add_key("low energy window (keV)", &this->low_energy_window); this->parser.add_key("upper energy window (keV)", &this->up_energy_window); - this->parser.add_key("minimum timing step (in picoseconds)", &this->least_significant_clock_bit); } bool @@ -88,7 +88,6 @@ InputStreamFromROOTFile::post_processing() stream_ptr->SetBranchAddress("comptonPhantom1", &comptonphantom1); stream_ptr->SetBranchAddress("comptonPhantom2", &comptonphantom2); - least_significant_clock_bit = 5.0e+11;// / least_significant_clock_bit; return false; } diff --git a/src/buildblock/ProjDataInfo.cxx b/src/buildblock/ProjDataInfo.cxx index 167d40929b..a7e622813a 100644 --- a/src/buildblock/ProjDataInfo.cxx +++ b/src/buildblock/ProjDataInfo.cxx @@ -75,14 +75,18 @@ START_NAMESPACE_STIR float ProjDataInfo::get_k(const Bin& bin) const { - // Probably, This condition should be removed, since I have the check odd number in the - // set_tof_mash_factor(). if (!(num_tof_bins%2)) return bin.timing_pos_num() * tof_increament_in_mm; else return (bin.timing_pos_num() * tof_increament_in_mm) - tof_increament_in_mm/2.f; } +double +ProjDataInfo::get_tof_delta_time(const Bin& bin) const +{ + return mm_to_tof_delta_time(get_k(bin) + tof_increament_in_mm / 2.f); // get_k gives "left" edge +} + float ProjDataInfo::get_sampling_in_k(const Bin& bin) const { @@ -192,7 +196,7 @@ ProjDataInfo::set_tof_mash_factor(const int new_num) "the scanner's number of max timing bins. Abort."); tof_mash_factor = new_num; - tof_increament_in_mm = (tof_mash_factor * scanner_ptr->get_size_of_timing_bin() * 0.299792458f); + tof_increament_in_mm = tof_delta_time_to_mm(tof_mash_factor * scanner_ptr->get_size_of_timing_bin()); min_tof_pos_num = - (scanner_ptr->get_num_max_of_timing_bins() / tof_mash_factor)/2; max_tof_pos_num = min_tof_pos_num + (scanner_ptr->get_num_max_of_timing_bins() / tof_mash_factor) -1; @@ -218,8 +222,8 @@ ProjDataInfo::set_tof_mash_factor(const int new_num) tof_bin_boundaries_mm[i].low_lim = cur_low; tof_bin_boundaries_mm[i].high_lim = cur_high; - tof_bin_boundaries_ps[i].low_lim = (tof_bin_boundaries_mm[i].low_lim / 0.299792458f ) ; - tof_bin_boundaries_ps[i].high_lim = ( tof_bin_boundaries_mm[i].high_lim / 0.299792458f); + tof_bin_boundaries_ps[i].low_lim = static_cast(mm_to_tof_delta_time(tof_bin_boundaries_mm[i].low_lim)); + tof_bin_boundaries_ps[i].high_lim = static_cast(mm_to_tof_delta_time(tof_bin_boundaries_mm[i].high_lim)); // I could imagine a better printing. info(boost::format("Tbin %1%: %2% - %3% mm (%4% - %5% ps) = %6%") %i % tof_bin_boundaries_mm[i].low_lim % tof_bin_boundaries_mm[i].high_lim % tof_bin_boundaries_ps[i].low_lim % tof_bin_boundaries_ps[i].high_lim % get_sampling_in_k(bin)); diff --git a/src/buildblock/ProjDataInfoCylindricalNoArcCorr.cxx b/src/buildblock/ProjDataInfoCylindricalNoArcCorr.cxx index 191d49040f..9afcad10fa 100644 --- a/src/buildblock/ProjDataInfoCylindricalNoArcCorr.cxx +++ b/src/buildblock/ProjDataInfoCylindricalNoArcCorr.cxx @@ -608,7 +608,7 @@ get_bin(const LOR& lor,const double delta_time) const if (ring1 >=0 && ring1=0 && ring2= get_min_tangential_pos_num() && bin.tangential_pos_num() <= get_max_tangential_pos_num()) { diff --git a/src/include/stir/DetectionPositionPair.h b/src/include/stir/DetectionPositionPair.h index 6bd6046c9e..4387ad40bc 100644 --- a/src/include/stir/DetectionPositionPair.h +++ b/src/include/stir/DetectionPositionPair.h @@ -6,9 +6,11 @@ \ingroup projdata \brief Declaration of class stir::DetectionPositionPair \author Kris Thielemans + \author Elise Emond */ /* Copyright (C) 2002- 2009, Hammersmith Imanet Ltd + Copyright 2017, University College London This file is part of STIR. This file is free software; you can redistribute it and/or modify @@ -32,7 +34,8 @@ START_NAMESPACE_STIR /*! \ingroup projdata \brief - A class for storing 2 coordinates-sets of a detection, as suitable for PET. + A class for storing 2 coordinates-sets of a detection, together with a timing-position index (for TOF), + as suitable for PET. \see DetectionPosition for details on what we mean with a Detector Position */ @@ -41,6 +44,8 @@ template class DetectionPositionPair { public: + //! default constructor + /*! sets TOF bin to 0, but leaves others coordinates undefined*/ inline DetectionPositionPair(); inline DetectionPositionPair(const DetectionPosition&, diff --git a/src/include/stir/DetectionPositionPair.inl b/src/include/stir/DetectionPositionPair.inl index 5391eeb19c..379de77c8a 100644 --- a/src/include/stir/DetectionPositionPair.inl +++ b/src/include/stir/DetectionPositionPair.inl @@ -6,9 +6,11 @@ \ingroup projdata \brief Implementation of inline methods of class stir::DetectionPositionPair \author Kris Thielemans + \author Elise Emond */ /* Copyright (C) 2002- 2009, Hammersmith Imanet Ltd + Copyright 2017, University College London This file is part of STIR. This file is free software; you can redistribute it and/or modify @@ -28,6 +30,7 @@ START_NAMESPACE_STIR template DetectionPositionPair:: DetectionPositionPair() + : _timing_pos(static_cast(0)) {} template @@ -80,9 +83,16 @@ bool DetectionPositionPair:: operator==(const DetectionPositionPair& p) const { + // Slightly complicated as we need to be able to cope with reverse order of detectors. If so, + // the TOF bin should swap as well. However, currently, coordT is unsigned, so timing_pos is + // always positive so sign reversal can never occur. Below implementation is ok, but + // generates a compiler warning on many compilers for unsigned. + // For an unsigned type, we should check + // timing_pos() == coordT(0) && p.timing_pos() == coordT(0) + // TODO. differentiate between types return (pos1() == p.pos1() && pos2() == p.pos2() && timing_pos() == p.timing_pos()) || - (pos1() == p.pos2() && pos2() == p.pos1() && timing_pos() == -p.timing_pos()) ; + (pos1() == p.pos2() && pos2() == p.pos1() && timing_pos() == -p.timing_pos()); } template diff --git a/src/include/stir/ProjDataInfo.h b/src/include/stir/ProjDataInfo.h index a38b1f09d8..7c6eb5a544 100644 --- a/src/include/stir/ProjDataInfo.h +++ b/src/include/stir/ProjDataInfo.h @@ -5,6 +5,7 @@ Copyright (C) 2000 - 2011-10-14, Hammersmith Imanet Ltd Copyright (C) 2011-07-01 - 2011, Kris Thielemans Copyright (C) 2016-17, University of Hull + Copyright (C) 2017, University College London This file is part of STIR. This file is free software; you can redistribute it and/or modify @@ -27,6 +28,7 @@ \author Nikos Efthimiou \author Sanida Mustafovic \author Kris Thielemans + \author Elise Emond \author PARAPET project */ @@ -112,6 +114,14 @@ class ProjDataInfo const int num_views, const int num_tangential_poss, const bool arc_corrected = true, const int tof_mash_factor = 0); + //! \name Conversion functions between TOF delta_time and mm + //@{ + inline static double + mm_to_tof_delta_time(const float dist); + inline static float + tof_delta_time_to_mm(const double delta_time); + //@} + /************ constructors ***********/ // TODO should probably be protected @@ -283,10 +293,13 @@ class ProjDataInfo normal to the projection plane */ virtual float get_s(const Bin&) const =0; - //! Get value ot the TOF location along the LOR (in mm) + //! Get value of the TOF location along the LOR (in mm) //! k is a line segment connecting the centers of the two detectors. float get_k(const Bin&) const; + //! Get the value of the TOF timing difference (in ps) + double get_tof_delta_time(const Bin&) const; + //! Get LOR corresponding to a given bin /*! \see get_bin() diff --git a/src/include/stir/ProjDataInfo.inl b/src/include/stir/ProjDataInfo.inl index 812e3fc172..c9ebc75bab 100644 --- a/src/include/stir/ProjDataInfo.inl +++ b/src/include/stir/ProjDataInfo.inl @@ -5,6 +5,7 @@ Copyright (C) 2000 - 2011-10-14, Hammersmith Imanet Ltd Copyright (C) 2011-07-01 - 2011, Kris Thielemans Copyright (C) 2016, University of Hull + Copyright (C) 2017, University College London This file is part of STIR. This file is free software; you can redistribute it and/or modify @@ -26,11 +27,24 @@ \author Nikos Efthimiou \author Sanida Mustafovic \author Kris Thielemans + \author Elise Emond \author PARAPET project */ +#include "boost/format.hpp" + START_NAMESPACE_STIR +double +ProjDataInfo::mm_to_tof_delta_time(const float dist) +{ + return dist / (0.299792458 / 2); +} +float +ProjDataInfo::tof_delta_time_to_mm(const double delta_time) +{ + return static_cast(delta_time * (0.299792458 / 2)); +} shared_ptr ProjDataInfo:: @@ -73,13 +87,18 @@ ProjDataInfo::get_num_tof_poss() const int ProjDataInfo::get_tof_bin(const double& delta) const { - for (int i = min_tof_pos_num; i < max_tof_pos_num; i++) - { - if ( delta > tof_bin_boundaries_ps[i].low_lim && - delta < tof_bin_boundaries_ps[i].high_lim) - return i; - } + if (!is_tof_data()) return 0; + + for (int i = min_tof_pos_num; i <= max_tof_pos_num; i++) + { + if (delta >= tof_bin_boundaries_ps[i].low_lim && + delta < tof_bin_boundaries_ps[i].high_lim) + return i; + } + // TODO handle differently + error(boost::format("TOF delta time %g out of range") % delta); + return 0; } int @@ -144,8 +163,7 @@ ProjDataInfo::get_coincidence_window_in_pico_sec() const float ProjDataInfo::get_coincidence_window_width() const { - // Speed of light 0.299792458 mm / psec. - return get_coincidence_window_in_pico_sec() * 0.299792458f; + return tof_delta_time_to_mm(get_coincidence_window_in_pico_sec()); } bool diff --git a/src/include/stir/ProjDataInfoCylindricalNoArcCorr.inl b/src/include/stir/ProjDataInfoCylindricalNoArcCorr.inl index c08e3214c5..db68876db0 100644 --- a/src/include/stir/ProjDataInfoCylindricalNoArcCorr.inl +++ b/src/include/stir/ProjDataInfoCylindricalNoArcCorr.inl @@ -156,14 +156,15 @@ ProjDataInfoCylindricalNoArcCorr:: get_bin_for_det_pos_pair(Bin& bin, const DetectionPositionPair<>& dp) const { - assert(this->get_tof_mash_factor()>0); return get_bin_for_det_pair(bin, dp.pos1().tangential_coord(), dp.pos1().axial_coord(), dp.pos2().tangential_coord(), dp.pos2().axial_coord(), - stir::round((float)dp.timing_pos()/this->get_tof_mash_factor())); + this->get_tof_mash_factor()==0 + ? 0 // use timing_pos==0 in the nonTOF case + : stir::round((float)dp.timing_pos()/this->get_tof_mash_factor())); } void ProjDataInfoCylindricalNoArcCorr:: diff --git a/src/recon_buildblock/ProjMatrixByBin.cxx b/src/recon_buildblock/ProjMatrixByBin.cxx index 28ffb96d35..f666d48af1 100644 --- a/src/recon_buildblock/ProjMatrixByBin.cxx +++ b/src/recon_buildblock/ProjMatrixByBin.cxx @@ -83,7 +83,7 @@ enable_tof(const shared_ptr& _proj_data_info_sptr, const bool v) { tof_enabled = true; proj_data_info_sptr = _proj_data_info_sptr; - gauss_sigma_in_mm = (proj_data_info_sptr->get_scanner_ptr()->get_timing_resolution() * 0.299792458f) / 2.355f; + gauss_sigma_in_mm = ProjDataInfo::tof_delta_time_to_mm(proj_data_info_sptr->get_scanner_ptr()->get_timing_resolution()) / 2.355f; r_sqrt2_gauss_sigma = 1.0f/ (gauss_sigma_in_mm * static_cast(sqrt(2.0))); } } diff --git a/src/test/test_proj_data_info.cxx b/src/test/test_proj_data_info.cxx index a31720aa0b..0373af5bb1 100644 --- a/src/test/test_proj_data_info.cxx +++ b/src/test/test_proj_data_info.cxx @@ -108,6 +108,7 @@ test_generic_proj_data_info(ProjDataInfo& proj_data_info) int max_diff_view_num=0; int max_diff_axial_pos_num=0; int max_diff_tangential_pos_num=0; + int max_diff_timing_pos_num = 0; #ifdef STIR_OPENMP #pragma omp parallel for schedule(dynamic) #endif @@ -118,7 +119,7 @@ test_generic_proj_data_info(ProjDataInfo& proj_data_info) for (int view_num=proj_data_info.get_min_view_num(); view_num<=proj_data_info.get_max_view_num(); view_num+=3) - { + { // loop over axial_positions. Avoid using first and last positions, as // if there is axial compression, the central LOR of a bin might actually not // fall within the scanner. In this case, the get_bin(get_LOR(org_bin)) code @@ -143,100 +144,130 @@ test_generic_proj_data_info(ProjDataInfo& proj_data_info) tangential_pos_num<=proj_data_info.get_max_tangential_pos_num()-1; tangential_pos_num+=1) { - const Bin org_bin(segment_num,view_num,axial_pos_num,tangential_pos_num, /* value*/1.f); - LORInAxialAndNoArcCorrSinogramCoordinates lor; - proj_data_info.get_LOR(lor, org_bin); - { - const Bin new_bin = proj_data_info.get_bin(lor); + for (int timing_pos_num = proj_data_info.get_min_tof_pos_num(); + timing_pos_num <= proj_data_info.get_max_tof_pos_num(); + timing_pos_num += std::max(1,(proj_data_info.get_max_tof_pos_num() - proj_data_info.get_min_tof_pos_num()) / 2))// take 3 or 1 steps, always going through 0 + { + const Bin org_bin(segment_num,view_num,axial_pos_num,tangential_pos_num, timing_pos_num, /* value*/1.f); + const double delta_time = proj_data_info.get_tof_delta_time(org_bin); + LORInAxialAndNoArcCorrSinogramCoordinates lor; + proj_data_info.get_LOR(lor, org_bin); + { + const Bin new_bin = proj_data_info.get_bin(lor, delta_time); +#ifdef STIR_TOF_DEBUG + std::cerr << "T:" << org_bin.timing_pos_num() << ", swapped=" << lor.is_swapped() + << ", z1=" << lor.z1() << ", z2=" << lor.z2() << ", phi=" << lor.phi() << ", beta=" << lor.beta() << std::endl; +#endif #if 1 - const int diff_segment_num = - intabs(org_bin.segment_num() - new_bin.segment_num()); - const int diff_view_num = - intabs(org_bin.view_num() - new_bin.view_num()); - const int diff_axial_pos_num = - intabs(org_bin.axial_pos_num() - new_bin.axial_pos_num()); - const int diff_tangential_pos_num = - intabs(org_bin.tangential_pos_num() - new_bin.tangential_pos_num()); - if (new_bin.get_bin_value()>0) - { - if (diff_segment_num>max_diff_segment_num) - max_diff_segment_num=diff_segment_num; - if (diff_view_num>max_diff_view_num) - max_diff_view_num=diff_view_num; - if (diff_axial_pos_num>max_diff_axial_pos_num) - max_diff_axial_pos_num=diff_axial_pos_num; - if (diff_tangential_pos_num>max_diff_tangential_pos_num) - max_diff_tangential_pos_num=diff_tangential_pos_num; - } - if (!check(org_bin.get_bin_value() == new_bin.get_bin_value(), "round-trip get_LOR then get_bin: value") || - !check(diff_segment_num<=0, "round-trip get_LOR then get_bin: segment") || - !check(diff_view_num<=1, "round-trip get_LOR then get_bin: view") || - !check(diff_axial_pos_num<=1, "round-trip get_LOR then get_bin: axial_pos") || - !check(diff_tangential_pos_num<=1, "round-trip get_LOR then get_bin: tangential_pos")) + const int diff_segment_num = + intabs(org_bin.segment_num() - new_bin.segment_num()); + const int diff_view_num = + intabs(org_bin.view_num() - new_bin.view_num()); + const int diff_axial_pos_num = + intabs(org_bin.axial_pos_num() - new_bin.axial_pos_num()); + const int diff_tangential_pos_num = + intabs(org_bin.tangential_pos_num() - new_bin.tangential_pos_num()); + const int diff_timing_pos_num = + intabs(org_bin.timing_pos_num() - new_bin.timing_pos_num()); + if (new_bin.get_bin_value()>0) + { + if (diff_segment_num>max_diff_segment_num) + max_diff_segment_num=diff_segment_num; + if (diff_view_num>max_diff_view_num) + max_diff_view_num=diff_view_num; + if (diff_axial_pos_num>max_diff_axial_pos_num) + max_diff_axial_pos_num=diff_axial_pos_num; + if (diff_tangential_pos_num>max_diff_tangential_pos_num) + max_diff_tangential_pos_num=diff_tangential_pos_num; + if (diff_timing_pos_num>max_diff_timing_pos_num) + max_diff_timing_pos_num = diff_timing_pos_num; + } + if (!check(org_bin.get_bin_value() == new_bin.get_bin_value(), "round-trip get_LOR then get_bin: value") || + !check(diff_segment_num<=0, "round-trip get_LOR then get_bin: segment") || + !check(diff_view_num<=1, "round-trip get_LOR then get_bin: view") || + !check(diff_axial_pos_num<=1, "round-trip get_LOR then get_bin: axial_pos") || + !check(diff_tangential_pos_num<=1, "round-trip get_LOR then get_bin: tangential_pos") || + !check(diff_timing_pos_num == 0, "round-trip get_LOR then get_bin: timing_pos")) #else - if (!check(org_bin == new_bin, "round-trip get_LOR then get_bin")) + if (!check(org_bin == new_bin, "round-trip get_LOR then get_bin")) #endif - { - cerr << "\tProblem at segment = " << org_bin.segment_num() - << ", axial pos " << org_bin.axial_pos_num() - << ", view = " << org_bin.view_num() - << ", tangential_pos_num = " << org_bin.tangential_pos_num() << "\n"; - if (new_bin.get_bin_value()>0) - cerr << "\tround-trip to segment = " << new_bin.segment_num() - << ", axial pos " << new_bin.axial_pos_num() - << ", view = " << new_bin.view_num() - << ", tangential_pos_num = " << new_bin.tangential_pos_num() - <<'\n'; - } - } - // repeat test but with different type of LOR - { - LORAs2Points lor_as_points; - lor.get_intersections_with_cylinder(lor_as_points, lor.radius()); - const Bin new_bin = proj_data_info.get_bin(lor_as_points); + { + cerr << "\tProblem at segment = " << org_bin.segment_num() + << ", axial pos " << org_bin.axial_pos_num() + << ", view = " << org_bin.view_num() + << ", tangential_pos = " << org_bin.tangential_pos_num() + << ", timing_pos = " << org_bin.timing_pos_num() << "\n"; + if (new_bin.get_bin_value()>0) + cerr << "\tround-trip to segment = " << new_bin.segment_num() + << ", axial pos " << new_bin.axial_pos_num() + << ", view = " << new_bin.view_num() + << ", tangential_pos = " << new_bin.tangential_pos_num() + << ", timing_pos = " << new_bin.timing_pos_num() + <<'\n'; + } + } + // repeat test but with different type of LOR + { + LORAs2Points lor_as_points; + lor.get_intersections_with_cylinder(lor_as_points, lor.radius()); +#ifdef STIR_TOF_DEBUG + std::cerr + << " z1=" << lor_as_points.p1().z() << ", y1=" << lor_as_points.p1().y() << ", x1=" << lor_as_points.p1().x() + << "\n z2=" << lor_as_points.p2().z() << ", y2=" << lor_as_points.p2().y() << ", x2=" << lor_as_points.p2().x() + << std::endl; +#endif + const Bin new_bin = proj_data_info.get_bin(lor_as_points, proj_data_info.get_tof_delta_time(org_bin)); #if 1 - const int diff_segment_num = - intabs(org_bin.segment_num() - new_bin.segment_num()); - const int diff_view_num = - intabs(org_bin.view_num() - new_bin.view_num()); - const int diff_axial_pos_num = - intabs(org_bin.axial_pos_num() - new_bin.axial_pos_num()); - const int diff_tangential_pos_num = - intabs(org_bin.tangential_pos_num() - new_bin.tangential_pos_num()); - if (new_bin.get_bin_value()>0) - { - if (diff_segment_num>max_diff_segment_num) - max_diff_segment_num=diff_segment_num; - if (diff_view_num>max_diff_view_num) - max_diff_view_num=diff_view_num; - if (diff_axial_pos_num>max_diff_axial_pos_num) - max_diff_axial_pos_num=diff_axial_pos_num; - if (diff_tangential_pos_num>max_diff_tangential_pos_num) - max_diff_tangential_pos_num=diff_tangential_pos_num; - } - if (!check(org_bin.get_bin_value() == new_bin.get_bin_value(), "round-trip get_LOR then get_bin (LORAs2Points): value") || - !check(diff_segment_num<=0, "round-trip get_LOR then get_bin (LORAs2Points): segment") || - !check(diff_view_num<=1, "round-trip get_LOR then get_bin (LORAs2Points): view") || - !check(diff_axial_pos_num<=1, "round-trip get_LOR then get_bin (LORAs2Points): axial_pos") || - !check(diff_tangential_pos_num<=1, "round-trip get_LOR then get_bin (LORAs2Points): tangential_pos")) + const int diff_segment_num = + intabs(org_bin.segment_num() - new_bin.segment_num()); + const int diff_view_num = + intabs(org_bin.view_num() - new_bin.view_num()); + const int diff_axial_pos_num = + intabs(org_bin.axial_pos_num() - new_bin.axial_pos_num()); + const int diff_tangential_pos_num = + intabs(org_bin.tangential_pos_num() - new_bin.tangential_pos_num()); + const int diff_timing_pos_num = + intabs(org_bin.timing_pos_num() - new_bin.timing_pos_num()); + if (new_bin.get_bin_value()>0) + { + if (diff_segment_num>max_diff_segment_num) + max_diff_segment_num=diff_segment_num; + if (diff_view_num>max_diff_view_num) + max_diff_view_num=diff_view_num; + if (diff_axial_pos_num>max_diff_axial_pos_num) + max_diff_axial_pos_num=diff_axial_pos_num; + if (diff_tangential_pos_num>max_diff_tangential_pos_num) + max_diff_tangential_pos_num=diff_tangential_pos_num; + if (diff_timing_pos_num>max_diff_timing_pos_num) + max_diff_timing_pos_num = diff_timing_pos_num; + } + if (!check(org_bin.get_bin_value() == new_bin.get_bin_value(), "round-trip get_LOR then get_bin (LORAs2Points): value") || + !check(diff_segment_num<=0, "round-trip get_LOR then get_bin (LORAs2Points): segment") || + !check(diff_view_num<=1, "round-trip get_LOR then get_bin (LORAs2Points): view") || + !check(diff_axial_pos_num<=1, "round-trip get_LOR then get_bin (LORAs2Points): axial_pos") || + !check(diff_tangential_pos_num<=1, "round-trip get_LOR then get_bin (LORAs2Points): tangential_pos") || + !check(diff_timing_pos_num == 0, "round-trip get_LOR then get_bin (LORAs2Points): timing_pos")) #else - if (!check(org_bin == new_bin, "round-trip get_LOR then get_bin")) + if (!check(org_bin == new_bin, "round-trip get_LOR then get_bin")) #endif - { - cerr << "\tProblem at segment = " << org_bin.segment_num() - << ", axial pos " << org_bin.axial_pos_num() - << ", view = " << org_bin.view_num() - << ", tangential_pos_num = " << org_bin.tangential_pos_num() << "\n"; - if (new_bin.get_bin_value()>0) - cerr << "\tround-trip to segment = " << new_bin.segment_num() - << ", axial pos " << new_bin.axial_pos_num() - << ", view = " << new_bin.view_num() - << ", tangential_pos_num = " << new_bin.tangential_pos_num() - <<'\n'; - } - } + { + cerr << "\tProblem at segment = " << org_bin.segment_num() + << ", axial pos " << org_bin.axial_pos_num() + << ", view = " << org_bin.view_num() + << ", tangential_pos_num = " << org_bin.tangential_pos_num() + << ", timing_pos = " << org_bin.timing_pos_num() << "\n"; + if (new_bin.get_bin_value()>0) + cerr << "\tround-trip to segment = " << new_bin.segment_num() + << ", axial pos " << new_bin.axial_pos_num() + << ", view = " << new_bin.view_num() + << ", tangential_pos_num = " << new_bin.tangential_pos_num() + << ", timing_pos = " << new_bin.timing_pos_num() + <<'\n'; + } + } + } } } } @@ -244,7 +275,8 @@ test_generic_proj_data_info(ProjDataInfo& proj_data_info) cerr << "Max Deviation: segment = " << max_diff_segment_num << ", axial pos " << max_diff_axial_pos_num << ", view = " << max_diff_view_num - << ", tangential_pos_num = " << max_diff_tangential_pos_num << "\n"; + << ", tangential_pos_num = " << max_diff_tangential_pos_num + << ", timing_pos_num = " << max_diff_timing_pos_num << "\n"; } @@ -608,7 +640,8 @@ run_tests() /*views*/ scanner_ptr->get_num_detectors_per_ring()/2, /*tang_pos*/64, /*arc_corrected*/ false)); - test_proj_data_info(dynamic_cast(*proj_data_info_ptr)); +#ifndef STIR_TOF_DEBUG // disable these for speed of testing + test_proj_data_info(dynamic_cast(*proj_data_info_ptr)); cerr << "\nTests with proj_data_info with mashing and axial compression (span 5)\n\n"; proj_data_info_ptr = @@ -627,7 +660,7 @@ run_tests() /*tang_pos*/64, /*arc_corrected*/ false); test_proj_data_info(dynamic_cast(*proj_data_info_ptr)); - +#endif // STIR_TOF_DEBUG cerr << "\nTests with proj_data_info with time-of-flight\n\n"; shared_ptr scanner_tof_ptr(new Scanner(Scanner::PETMR_Signa)); proj_data_info_ptr = @@ -751,49 +784,54 @@ test_proj_data_info(ProjDataInfoCylindricalNoArcCorr& proj_data_info) for (det_pos_pair.pos2().tangential_coord() = 0; det_pos_pair.pos2().tangential_coord() < (unsigned)num_detectors; det_pos_pair.pos2().tangential_coord()++) - { - // set from for-loop variable - det_pos_pair.pos1().tangential_coord() = (unsigned)tangential_coord1; - // skip case of equal detector numbers (as this is either a singular LOR) - // or an LOR parallel to the scanner axis - if (det_pos_pair.pos1().tangential_coord() == det_pos_pair.pos2().tangential_coord()) - continue; - Bin bin(0,0,0,0,0,0.0f); - DetectionPositionPair<> new_det_pos_pair; - const bool there_is_a_bin = - proj_data_info.get_bin_for_det_pos_pair(bin, det_pos_pair) == + for (det_pos_pair.timing_pos() = 0; // currently unsigned so start from 0 + det_pos_pair.timing_pos() <= (unsigned)proj_data_info.get_max_tof_pos_num(); + det_pos_pair.timing_pos() += (unsigned)std::max(1,proj_data_info.get_max_tof_pos_num())) + { + + // set from for-loop variable + det_pos_pair.pos1().tangential_coord() = (unsigned)tangential_coord1; + // skip case of equal detector numbers (as this is either a singular LOR) + // or an LOR parallel to the scanner axis + if (det_pos_pair.pos1().tangential_coord() == det_pos_pair.pos2().tangential_coord()) + continue; + Bin bin(0,0,0,0,0,0.0f); + DetectionPositionPair<> new_det_pos_pair; + const bool there_is_a_bin = + proj_data_info.get_bin_for_det_pos_pair(bin, det_pos_pair) == Succeeded::yes; - if (there_is_a_bin) - proj_data_info.get_det_pos_pair_for_bin(new_det_pos_pair, bin); - if (!check(there_is_a_bin, "checking if there is a bin for this det_pos_pair") || - !check(det_pos_pair == new_det_pos_pair, "checking if we round-trip to the same detection positions")) + if (there_is_a_bin) + proj_data_info.get_det_pos_pair_for_bin(new_det_pos_pair, bin); + if (!check(there_is_a_bin, "checking if there is a bin for this det_pos_pair") || + !check(det_pos_pair == new_det_pos_pair, "checking if we round-trip to the same detection positions")) { cerr << "Problem at det1 = " << det_pos_pair.pos1().tangential_coord() << ", det2 = " << det_pos_pair.pos2().tangential_coord() << ", ring1 = " << det_pos_pair.pos1().axial_coord() << ", ring2 = " << det_pos_pair.pos2().axial_coord() + << ", timing_pos = " << det_pos_pair.timing_pos() << endl; if (there_is_a_bin) - cerr << " dets,rings -> bin -> dets,rings, gives new numbers:\n\t" - << "det1 = " << new_det_pos_pair.pos1().tangential_coord() + cerr << " dets,rings -> bin -> dets,rings, gives new numbers:\n\t" + << "det1 = " << new_det_pos_pair.pos1().tangential_coord() - << ", det2 = " << new_det_pos_pair.pos2().tangential_coord() - << ", ring1 = " << new_det_pos_pair.pos1().axial_coord() - << ", ring2 = " << new_det_pos_pair.pos2().axial_coord() - << endl; - } + << ", det2 = " << new_det_pos_pair.pos2().tangential_coord() + << ", ring1 = " << new_det_pos_pair.pos1().axial_coord() + << ", ring2 = " << new_det_pos_pair.pos2().axial_coord() + << ", timing_pos = " << det_pos_pair.timing_pos() + << endl; + } - } // end of get_bin_for_det_pos_pair and vice versa code + } // end of get_bin_for_det_pos_pair and vice versa code cerr << "\n\tTest code for bin -> detector,ring and back conversions. (This might take a while...)"; - { Bin bin(0,0,0,0,0,0.0f); // set value for comparison later on bin.set_bin_value(0.f); for (bin.timing_pos_num() = proj_data_info.get_min_tof_pos_num(); bin.timing_pos_num() <= proj_data_info.get_max_tof_pos_num(); - ++bin.timing_pos_num()) + bin.timing_pos_num() += std::max(1,(proj_data_info.get_max_tof_pos_num() - proj_data_info.get_min_tof_pos_num()) / 2))// take 3 or 1 steps, always going through 0) for (bin.segment_num() = max(-5,proj_data_info.get_min_segment_num()); bin.segment_num() <= min(5,proj_data_info.get_max_segment_num()); ++bin.segment_num()) @@ -828,15 +866,15 @@ test_proj_data_info(ProjDataInfoCylindricalNoArcCorr& proj_data_info) cerr << "Problem at segment = " << bin.segment_num() << ", axial pos " << bin.axial_pos_num() << ", view = " << bin.view_num() - << ", tangential_pos_num = " << bin.tangential_pos_num() - << ", timing pos num = " << bin.timing_pos_num() << "\n"; + << ", tangential_pos_num = " << bin.tangential_pos_num() + << ", timing pos num = " << bin.timing_pos_num() << "\n"; if (there_is_a_bin) cerr << " bin -> dets -> bin, gives new numbers:\n\t" << "segment = " << new_bin.segment_num() << ", axial pos " << new_bin.axial_pos_num() << ", view = " << new_bin.view_num() << ", tangential_pos_num = " << new_bin.tangential_pos_num() - << ", timing pos num = " << new_bin.timing_pos_num() + << ", timing pos num = " << new_bin.timing_pos_num() << endl; } @@ -866,6 +904,9 @@ test_proj_data_info(ProjDataInfoCylindricalNoArcCorr& proj_data_info) for (bin.tangential_pos_num() = proj_data_info.get_min_tangential_pos_num(); bin.tangential_pos_num() <= proj_data_info.get_max_tangential_pos_num(); ++bin.tangential_pos_num()) + for (bin.timing_pos_num() = proj_data_info.get_min_tof_pos_num(); + bin.timing_pos_num() <= proj_data_info.get_max_tof_pos_num(); + bin.timing_pos_num() += std::max(1,(proj_data_info.get_max_tof_pos_num() - proj_data_info.get_min_tof_pos_num()) / 2))// take 3 or 1 steps, always going through 0) { proj_data_info.get_all_det_pos_pairs_for_bin(det_pos_pairs, bin); Bin new_bin(0,0,0,0,0,0.0f); @@ -885,14 +926,16 @@ test_proj_data_info(ProjDataInfoCylindricalNoArcCorr& proj_data_info) cerr << "Problem at segment = " << bin.segment_num() << ", axial pos " << bin.axial_pos_num() << ", view = " << bin.view_num() - << ", tangential_pos_num = " << bin.tangential_pos_num() << "\n"; + << ", tangential_pos = " << bin.tangential_pos_num() + << ", timing_pos - " << bin.timing_pos_num() << "\n"; if (there_is_a_bin) cerr << " bin -> dets -> bin, gives new numbers:\n\t" << "segment = " << new_bin.segment_num() << ", axial pos " << new_bin.axial_pos_num() << ", view = " << new_bin.view_num() << ", tangential_pos_num = " << new_bin.tangential_pos_num() - << endl; + << ", timing_pos - " << new_bin.timing_pos_num() + << endl; } } // end of iteration of det_pos_pairs } // end of loop over all bins @@ -961,9 +1004,11 @@ int main() { set_default_num_threads(); +#ifndef STIR_TOF_DEBUG // disable for speed of testing ProjDataInfoCylindricalArcCorrTests tests; tests.run_tests(); +#endif ProjDataInfoCylindricalNoArcCorrTests tests1; tests1.run_tests(); - return tests.main_return_value(); + return tests1.main_return_value(); } diff --git a/src/test/test_time_of_flight.cxx b/src/test/test_time_of_flight.cxx index 77ba7ba42b..c70ce53322 100644 --- a/src/test/test_time_of_flight.cxx +++ b/src/test/test_time_of_flight.cxx @@ -175,9 +175,9 @@ TOF_Tests::test_tof_proj_data_info() const int correct_tof_mashing_factor = 39; const int num_timing_positions = 9; float correct_width_of_tof_bin = test_scanner_sptr->get_size_of_timing_bin() * - test_proj_data_info_sptr->get_tof_mash_factor() * 0.299792458f; - float correct_timing_locations[num_timing_positions] = {-360.201f, -280.156f, -200.111f, -120.067f, -40.022f, 40.022f, - 120.067f, 200.111f, 280.156f}; + test_proj_data_info_sptr->get_tof_mash_factor() * 0.299792458f/2; + float correct_timing_locations[num_timing_positions] = {-360.201f/2, -280.156f/2, -200.111f/2, -120.067f/2, -40.022f/2, 40.022f/2, + 120.067f/2, 200.111f/2, 280.156f/2}; check_if_equal(correct_tof_mashing_factor, test_proj_data_info_sptr->get_tof_mash_factor(), "Different TOF mashing factor."); From 8241496a9a1b4457ca3eb83f5b5d6931f2e1aaf7 Mon Sep 17 00:00:00 2001 From: Kris Thielemans Date: Sat, 9 Sep 2017 21:57:41 +0100 Subject: [PATCH 082/170] More TOF fixes - fix LOR and derived classes to take direction into account - fix ProjDataInfo::get_bin for TOF swapping - add all TOF bins to ProjDataInfoCylindricalNoArcCorr:get_all_det_pos_pairs_for_bin - reduce items in some loops in test_proj_data_info and switch to Discovery 690 (as opposed to Signa) to save some time --- src/buildblock/ProjDataInfoCylindrical.cxx | 2 +- .../ProjDataInfoCylindricalNoArcCorr.cxx | 46 +++++++++++++------ src/include/stir/LORCoordinates.h | 27 ++++++++++- src/include/stir/LORCoordinates.inl | 37 ++++++++++----- src/test/test_proj_data_info.cxx | 22 +++++---- 5 files changed, 95 insertions(+), 39 deletions(-) diff --git a/src/buildblock/ProjDataInfoCylindrical.cxx b/src/buildblock/ProjDataInfoCylindrical.cxx index 37b2cc2bf7..5431c00d98 100644 --- a/src/buildblock/ProjDataInfoCylindrical.cxx +++ b/src/buildblock/ProjDataInfoCylindrical.cxx @@ -548,7 +548,7 @@ get_LOR(LORInAxialAndNoArcCorrSinogramCoordinates& lor, phi, asin(s_in_mm/get_ring_radius()), get_ring_radius(), - bin.timing_pos_num()>=0); + false);// needs to set "swapped" to false given above code } void diff --git a/src/buildblock/ProjDataInfoCylindricalNoArcCorr.cxx b/src/buildblock/ProjDataInfoCylindricalNoArcCorr.cxx index 9afcad10fa..f0688de474 100644 --- a/src/buildblock/ProjDataInfoCylindricalNoArcCorr.cxx +++ b/src/buildblock/ProjDataInfoCylindricalNoArcCorr.cxx @@ -35,7 +35,7 @@ #include "stir/CartesianCoordinate3D.h" #include "stir/LORCoordinates.h" #include "stir/round.h" - +#include #ifdef BOOST_NO_STRINGSTREAM #include #else @@ -327,7 +327,8 @@ get_num_det_pos_pairs_for_bin(const Bin& bin) const return get_num_ring_pairs_for_segment_axial_pos_num(bin.segment_num(), bin.axial_pos_num())* - get_view_mashing_factor(); + get_view_mashing_factor()* + std::max(1,get_tof_mash_factor()); } void @@ -344,7 +345,8 @@ get_all_det_pos_pairs_for_bin(vector >& dps, bin.axial_pos_num()); // not sure how to handle mashing with non-zero view offset... assert(get_min_view_num()==0); - + // not sure how to handle even tof mashing + assert(!is_tof_data() || (get_tof_mash_factor() % 2 == 1)); unsigned int current_dp_num=0; for (int uncompressed_view_num=bin.view_num()*get_view_mashing_factor(); uncompressed_view_num<(bin.view_num()+1)*get_view_mashing_factor(); @@ -355,17 +357,31 @@ get_all_det_pos_pairs_for_bin(vector >& dps, const int det2_num = uncompressed_view_tangpos_to_det1det2[uncompressed_view_num][bin.tangential_pos_num()].det2_num; for (ProjDataInfoCylindrical::RingNumPairs::const_iterator rings_iter = ring_pairs.begin(); - rings_iter != ring_pairs.end(); - ++rings_iter) - { - assert(current_dp_num < get_num_det_pos_pairs_for_bin(bin)); - dps[current_dp_num].pos1().tangential_coord() = det1_num; - dps[current_dp_num].pos1().axial_coord() = rings_iter->first; - dps[current_dp_num].pos2().tangential_coord() = det2_num; - dps[current_dp_num].pos2().axial_coord() = rings_iter->second; - dps[current_dp_num].timing_pos() = bin.timing_pos_num(); - ++current_dp_num; - } + rings_iter != ring_pairs.end(); + ++rings_iter) + { + for (int uncompressed_timing_pos_num = bin.timing_pos_num()*get_tof_mash_factor() - (get_tof_mash_factor() / 2); + uncompressed_timing_pos_num <= bin.timing_pos_num()*get_tof_mash_factor() + (get_tof_mash_factor() / 2); + ++uncompressed_timing_pos_num) + { + assert(current_dp_num < get_num_det_pos_pairs_for_bin(bin)); + dps[current_dp_num].pos1().tangential_coord() = det1_num; + dps[current_dp_num].pos1().axial_coord() = rings_iter->first; + dps[current_dp_num].pos2().tangential_coord() = det2_num; + dps[current_dp_num].pos2().axial_coord() = rings_iter->second; + // need to keep dp.timing_pos positive + if (uncompressed_timing_pos_num > 0) + { + dps[current_dp_num].timing_pos() = static_cast(uncompressed_timing_pos_num); + } + else + { + std::swap(dps[current_dp_num].pos1(), dps[current_dp_num].pos2()); + dps[current_dp_num].timing_pos() = static_cast(-uncompressed_timing_pos_num); + } + ++current_dp_num; + } + } } assert(current_dp_num == get_num_det_pos_pairs_for_bin(bin)); } @@ -608,7 +624,7 @@ get_bin(const LOR& lor,const double delta_time) const if (ring1 >=0 && ring1=0 && ring2= get_min_tangential_pos_num() && bin.tangential_pos_num() <= get_max_tangential_pos_num()) { diff --git a/src/include/stir/LORCoordinates.h b/src/include/stir/LORCoordinates.h index cbbf4e0137..882493cdec 100644 --- a/src/include/stir/LORCoordinates.h +++ b/src/include/stir/LORCoordinates.h @@ -53,6 +53,10 @@ template class LORAs2Points; a line in several ways, each if which is more convenient for some application. This class provides a common base for all of these. + Note that we take direction of the line into account (since after STIR 3.0). This is + necessary for TOF support for instance. This is currently done by providing the is_swapped() + member. + \warning This is all preliminary and likely to change. */ template @@ -64,6 +68,10 @@ class LOR virtual LOR * clone() const = 0; + //! Return if the LOR direction is opposite from normal. + virtual + bool is_swapped() const = 0; + virtual Succeeded change_representation(LORInCylinderCoordinates&, @@ -178,7 +186,15 @@ class LORInCylinderCoordinates : public LOR const PointOnCylinder& p2() const { return _p2; } PointOnCylinder& p2() { return _p2; } - void reset(coordT radius=1) + //! \copybrief LOR::is_swapped() + /*! In this class, this currently always return \c false. You can swap the points if + you want to swap the direction of the LOR. + */ + bool is_swapped() const + { + return false; + } + void reset(coordT radius = 1) { // set psi such that the new LOR does intersect that cylinder _p1.psi()=0; _p2.psi()=static_cast(_PI); _radius=radius; @@ -291,6 +307,15 @@ class LORAs2Points : public LOR LOR* #endif clone() const { return new self_type(*this); } + + //! \copybrief LOR::is_swapped() + /*! In this class, this currently always return \c false. You can swap the points if + you want to swap the direction of the LOR. + */ + bool is_swapped() const + { + return false; + } virtual Succeeded diff --git a/src/include/stir/LORCoordinates.inl b/src/include/stir/LORCoordinates.inl index 9304e733f1..ec13b6fe13 100644 --- a/src/include/stir/LORCoordinates.inl +++ b/src/include/stir/LORCoordinates.inl @@ -77,7 +77,7 @@ template LORInAxialAndSinogramCoordinates:: LORInAxialAndSinogramCoordinates(const coordT radius) : LORCylindricalCoordinates_z_and_radius(radius), - _phi(0),_s(0) // set _phi,_s to value to avoid assert + _phi(0),_s(0), _swapped(false) // set _phi,_s to value to avoid assert { check_state(); } @@ -101,7 +101,7 @@ template LORInAxialAndNoArcCorrSinogramCoordinates:: LORInAxialAndNoArcCorrSinogramCoordinates(const coordT radius) : LORCylindricalCoordinates_z_and_radius(radius), - _phi(0),_beta(0) // set _phi,_beta to value to avoid assert + _phi(0),_beta(0), _swapped(false) // set _phi,_beta to value to avoid assert { check_state(); @@ -135,6 +135,8 @@ LORInCylinderCoordinates(const LORInAxialAndNoArcCorrSinogramCoordinates _p2.z() = na_coords.z2(); _p1.psi() = to_0_2pi(na_coords.phi() + na_coords.beta()); _p2.psi() = to_0_2pi(na_coords.phi() - na_coords.beta() + static_cast(_PI)); + if (na_coords.is_swapped()) + std::swap(_p1, _p2); check_state(); } @@ -147,6 +149,8 @@ LORInCylinderCoordinates(const LORInAxialAndSinogramCoordinates& coords) _p2.z() = coords.z2(); _p1.psi() = to_0_2pi(coords.phi() + coords.beta()); _p2.psi() = to_0_2pi(coords.phi() - coords.beta() + static_cast(_PI)); + if (coords.is_swapped()) + std::swap(_p1, _p2); check_state(); } @@ -156,6 +160,7 @@ get_sino_coords( coordT& _z1, coordT& _z2, coordT& _phi, coordT& _beta, + bool& _swapped, const LORInCylinderCoordinates& cyl_coords) { _beta = to_0_2pi((cyl_coords.p1().psi() - cyl_coords.p2().psi() + static_cast(_PI))/2); @@ -170,20 +175,23 @@ get_sino_coords( coordT& _z1, if (_beta >= static_cast(_PI)/2) { _beta = static_cast(_PI) - _beta; - _z2 = cyl_coords.p1().z(); - _z1 = cyl_coords.p2().z(); + _z2 = cyl_coords.p1().z(); + _z1 = cyl_coords.p2().z(); + _swapped = true; } else if (_beta < -static_cast(_PI)/2) { _beta = -static_cast(_PI) - _beta; - _z2 = cyl_coords.p1().z(); - _z1 = cyl_coords.p2().z(); + _z2 = cyl_coords.p1().z(); + _z1 = cyl_coords.p2().z(); + _swapped = false; } else { _z1 = cyl_coords.p1().z(); _z2 = cyl_coords.p2().z(); + _swapped = false; } } else @@ -195,18 +203,21 @@ get_sino_coords( coordT& _z1, _beta -= static_cast(_PI); _z1 = cyl_coords.p1().z(); _z2 = cyl_coords.p2().z(); + _swapped = false; } else if (_beta < -static_cast(_PI)/2) { _beta += static_cast(_PI); _z1 = cyl_coords.p1().z(); _z2 = cyl_coords.p2().z(); + _swapped = true; } else { _beta *= -1; _z2 = cyl_coords.p1().z(); _z1 = cyl_coords.p2().z(); + _swapped = true; } } assert(_phi>=0); @@ -225,7 +236,7 @@ LORInAxialAndNoArcCorrSinogramCoordinates(const LORInCylinderCoordinates _phi=0; _beta=0; #endif - get_sino_coords(z1(), z2(), _phi, _beta, + get_sino_coords(z1(), z2(), _phi, _beta, _swapped, cyl_coords); check_state(); } @@ -241,7 +252,7 @@ LORInAxialAndSinogramCoordinates(const LORInCylinderCoordinates& cyl_coo _phi=0; _s=0; #endif - get_sino_coords(z1(), z2(), _phi, beta, + get_sino_coords(z1(), z2(), _phi, beta, _swapped, cyl_coords); _s = this->_radius*sin(beta); check_state(); @@ -252,7 +263,8 @@ LORInAxialAndSinogramCoordinates:: LORInAxialAndSinogramCoordinates(const LORInAxialAndNoArcCorrSinogramCoordinates& coords) : LORCylindricalCoordinates_z_and_radius(coords.z1(), coords.z2(), coords.radius()), _phi(coords.phi()), - _s(coords.s()) + _s(coords.s()), + _swapped(coords.is_swapped()) { check_state(); } @@ -262,7 +274,8 @@ LORInAxialAndNoArcCorrSinogramCoordinates:: LORInAxialAndNoArcCorrSinogramCoordinates(const LORInAxialAndSinogramCoordinates& coords) : LORCylindricalCoordinates_z_and_radius(coords.z1(), coords.z2(), coords.radius()), _phi(coords.phi()), - _beta(coords.beta()) + _beta(coords.beta()), + _swapped(coords.is_swapped()) { check_state(); } @@ -338,8 +351,8 @@ find_LOR_intersections_with_cylinder(LORAs2Points& intersection_coords, return Succeeded::no; // LOR is outside detector radius const coordT2 root = static_cast(sqrt(argsqrt)); - const coordT2 l1 = static_cast((- (d.x()*c1.x() + d.y()*c1.y())+root)/dxy2); - const coordT2 l2 = static_cast((- (d.x()*c1.x() + d.y()*c1.y())-root)/dxy2); + const coordT2 l2 = static_cast((- (d.x()*c1.x() + d.y()*c1.y())+root)/dxy2); + const coordT2 l1 = static_cast((- (d.x()*c1.x() + d.y()*c1.y())-root)/dxy2); // TODO won't work when coordT1!=coordT2 intersection_coords.p1() = d*l1 + c1; intersection_coords.p2() = d*l2 + c1; diff --git a/src/test/test_proj_data_info.cxx b/src/test/test_proj_data_info.cxx index 0373af5bb1..543c48136e 100644 --- a/src/test/test_proj_data_info.cxx +++ b/src/test/test_proj_data_info.cxx @@ -52,6 +52,8 @@ using std::max; using std::size_t; #endif +//#define STIR_TOF_DEBUG 1 + START_NAMESPACE_STIR static inline @@ -154,7 +156,7 @@ test_generic_proj_data_info(ProjDataInfo& proj_data_info) proj_data_info.get_LOR(lor, org_bin); { const Bin new_bin = proj_data_info.get_bin(lor, delta_time); -#ifdef STIR_TOF_DEBUG +#if STIR_TOF_DEBUG>1 std::cerr << "T:" << org_bin.timing_pos_num() << ", swapped=" << lor.is_swapped() << ", z1=" << lor.z1() << ", z2=" << lor.z2() << ", phi=" << lor.phi() << ", beta=" << lor.beta() << std::endl; #endif @@ -211,7 +213,7 @@ test_generic_proj_data_info(ProjDataInfo& proj_data_info) { LORAs2Points lor_as_points; lor.get_intersections_with_cylinder(lor_as_points, lor.radius()); -#ifdef STIR_TOF_DEBUG +#if STIR_TOF_DEBUG>1 std::cerr << " z1=" << lor_as_points.p1().z() << ", y1=" << lor_as_points.p1().y() << ", x1=" << lor_as_points.p1().x() << "\n z2=" << lor_as_points.p2().z() << ", y2=" << lor_as_points.p2().y() << ", x2=" << lor_as_points.p2().x() @@ -662,14 +664,14 @@ run_tests() test_proj_data_info(dynamic_cast(*proj_data_info_ptr)); #endif // STIR_TOF_DEBUG cerr << "\nTests with proj_data_info with time-of-flight\n\n"; - shared_ptr scanner_tof_ptr(new Scanner(Scanner::PETMR_Signa)); + shared_ptr scanner_tof_ptr(new Scanner(Scanner::Discovery690)); proj_data_info_ptr = ProjDataInfo::construct_proj_data_info(scanner_tof_ptr, /*span*/11, scanner_tof_ptr->get_num_rings()-1, /*views*/ scanner_tof_ptr->get_num_detectors_per_ring()/2, /*tang_pos*/64, /*arc_corrected*/ false, - /*tof_mashing*/39); + /*tof_mashing*/5); test_proj_data_info(dynamic_cast(*proj_data_info_ptr)); } @@ -837,7 +839,7 @@ test_proj_data_info(ProjDataInfoCylindricalNoArcCorr& proj_data_info) ++bin.segment_num()) for (bin.axial_pos_num() = proj_data_info.get_min_axial_pos_num(bin.segment_num()); bin.axial_pos_num() <= proj_data_info.get_max_axial_pos_num(bin.segment_num()); - ++bin.axial_pos_num()) + bin.axial_pos_num() += std::min(3, proj_data_info.get_num_axial_poss(bin.segment_num()))) #ifdef STIR_OPENMP // insert a parallel for here for testing. // we do it at this level to avoid too much overhead for the thread creation, while still having enough jobs to do // Note that the omp construct needs an int loop variable @@ -894,16 +896,16 @@ test_proj_data_info(ProjDataInfoCylindricalNoArcCorr& proj_data_info) #endif for (bin.segment_num() = proj_data_info.get_min_segment_num(); bin.segment_num() <= proj_data_info.get_max_segment_num(); - ++bin.segment_num()) + bin.segment_num() += std::max(1, proj_data_info.get_num_segments()/2)) for (bin.axial_pos_num() = proj_data_info.get_min_axial_pos_num(bin.segment_num()); bin.axial_pos_num() <= proj_data_info.get_max_axial_pos_num(bin.segment_num()); - ++bin.axial_pos_num()) + bin.axial_pos_num()+=std::min(3, proj_data_info.get_num_axial_poss(bin.segment_num()))) for (bin.view_num() = proj_data_info.get_min_view_num(); - bin.view_num() <= proj_data_info.get_max_view_num(); - ++bin.view_num()) + bin.view_num() <= proj_data_info.get_max_view_num(); + bin.view_num()+= std::min(5, proj_data_info.get_num_views())) for (bin.tangential_pos_num() = proj_data_info.get_min_tangential_pos_num(); bin.tangential_pos_num() <= proj_data_info.get_max_tangential_pos_num(); - ++bin.tangential_pos_num()) + bin.tangential_pos_num()+= std::min(7, proj_data_info.get_num_tangential_poss())) for (bin.timing_pos_num() = proj_data_info.get_min_tof_pos_num(); bin.timing_pos_num() <= proj_data_info.get_max_tof_pos_num(); bin.timing_pos_num() += std::max(1,(proj_data_info.get_max_tof_pos_num() - proj_data_info.get_min_tof_pos_num()) / 2))// take 3 or 1 steps, always going through 0) From 5c68b72181d12ecf95351c71cbebf57a7e684180 Mon Sep 17 00:00:00 2001 From: Kris Thielemans Date: Sun, 10 Sep 2017 00:56:56 +0100 Subject: [PATCH 083/170] replace auto_ptr with unique_ptr --- src/include/stir/IO/GESignaListmodeInputFileFormat.h | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/include/stir/IO/GESignaListmodeInputFileFormat.h b/src/include/stir/IO/GESignaListmodeInputFileFormat.h index cd9500cabf..1ee6846597 100644 --- a/src/include/stir/IO/GESignaListmodeInputFileFormat.h +++ b/src/include/stir/IO/GESignaListmodeInputFileFormat.h @@ -89,19 +89,17 @@ std::cout << "\n Manufacturer : " << read_str_manufacturer << "\n\n"; } } public: - virtual std::auto_ptr + virtual unique_ptr read_from_file(std::istream& input) const { warning("read_from_file for GESigna listmode data with istream not implemented %s:%s. Sorry", __FILE__, __LINE__); - return - std::auto_ptr - (0); + return unique_ptr(); } - virtual std::auto_ptr + virtual unique_ptr read_from_file(const std::string& filename) const { - return std::auto_ptr(new CListModeDataGESigna(filename)); + return unique_ptr(new CListModeDataGESigna(filename)); } }; From b7f5729e7fb2fab44c9b86270298b3ec6de0f9b1 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Mon, 11 Sep 2017 09:26:24 +0100 Subject: [PATCH 084/170] Patch to make TOF reconstruction work DetectorPositionPair can accomondate negative TOF bins. LOR Cartesian coordinates flip correctly --- .../ProjDataInfoCylindricalNoArcCorr.cxx | 27 ++++++++++++++++--- src/include/stir/DetectionPositionPair.h | 4 +-- src/include/stir/DetectionPositionPair.inl | 2 +- .../stir/ProjDataInfoCylindricalNoArcCorr.inl | 16 +++++------ 4 files changed, 34 insertions(+), 15 deletions(-) diff --git a/src/buildblock/ProjDataInfoCylindricalNoArcCorr.cxx b/src/buildblock/ProjDataInfoCylindricalNoArcCorr.cxx index f0688de474..2ad4a440ea 100644 --- a/src/buildblock/ProjDataInfoCylindricalNoArcCorr.cxx +++ b/src/buildblock/ProjDataInfoCylindricalNoArcCorr.cxx @@ -484,6 +484,25 @@ find_cartesian_coordinates_given_scanner_coordinates (CartesianCoordinate3Dget_num_detectors_per_ring(); + int d1, d2, r1, r2; + + this->initialise_det1det2_to_uncompressed_view_tangpos_if_not_done_yet(); + + if (!det1det2_to_uncompressed_view_tangpos[det1][det2].swap_detectors) + { + d1 = det2; + d2 = det1; + r1 = Ring_B; + r2 = Ring_A; + } + else + { + d1 = det1; + d2 = det2; + r1 = Ring_A; + r2 = Ring_B; + } + #if 0 const float df1 = (2.*_PI/num_detectors_per_ring)*(det1); const float df2 = (2.*_PI/num_detectors_per_ring)*(det2); @@ -510,10 +529,10 @@ find_cartesian_coordinates_given_scanner_coordinates (CartesianCoordinate3D cyl_coords(get_scanner_ptr()->get_effective_ring_radius()); - cyl_coords.p1().psi() = static_cast((2.*_PI/num_detectors_per_ring)*(det1)); - cyl_coords.p2().psi() = static_cast((2.*_PI/num_detectors_per_ring)*(det2)); - cyl_coords.p1().z() = Ring_A*get_scanner_ptr()->get_ring_spacing(); - cyl_coords.p2().z() = Ring_B*get_scanner_ptr()->get_ring_spacing(); + cyl_coords.p1().psi() = static_cast((2.*_PI/num_detectors_per_ring)*(d1)); + cyl_coords.p2().psi() = static_cast((2.*_PI/num_detectors_per_ring)*(d2)); + cyl_coords.p1().z() = r1*get_scanner_ptr()->get_ring_spacing(); + cyl_coords.p2().z() = r2*get_scanner_ptr()->get_ring_spacing(); LORAs2Points lor(cyl_coords); coord_1 = lor.p1(); coord_2 = lor.p2(); diff --git a/src/include/stir/DetectionPositionPair.h b/src/include/stir/DetectionPositionPair.h index 4387ad40bc..d419f47af6 100644 --- a/src/include/stir/DetectionPositionPair.h +++ b/src/include/stir/DetectionPositionPair.h @@ -57,7 +57,7 @@ class DetectionPositionPair inline const coordT timing_pos() const; inline DetectionPosition& pos1(); inline DetectionPosition& pos2(); - inline coordT& timing_pos(); + inline int& timing_pos(); //! comparison operators inline bool operator==(const DetectionPositionPair&) const; inline bool operator!=(const DetectionPositionPair&) const; @@ -65,7 +65,7 @@ class DetectionPositionPair private : DetectionPosition p1; DetectionPosition p2; - coordT _timing_pos; + int _timing_pos; }; END_NAMESPACE_STIR diff --git a/src/include/stir/DetectionPositionPair.inl b/src/include/stir/DetectionPositionPair.inl index 379de77c8a..dbca441c81 100644 --- a/src/include/stir/DetectionPositionPair.inl +++ b/src/include/stir/DetectionPositionPair.inl @@ -72,7 +72,7 @@ pos2() { return p2; } template -coordT& +int& DetectionPositionPair:: timing_pos() { return _timing_pos; } diff --git a/src/include/stir/ProjDataInfoCylindricalNoArcCorr.inl b/src/include/stir/ProjDataInfoCylindricalNoArcCorr.inl index db68876db0..b4ffa53c20 100644 --- a/src/include/stir/ProjDataInfoCylindricalNoArcCorr.inl +++ b/src/include/stir/ProjDataInfoCylindricalNoArcCorr.inl @@ -173,16 +173,16 @@ get_det_pair_for_bin( int& det_num2, int& ring_num2, const Bin& bin) const { - if (bin.timing_pos_num()>=0) - { + //if (bin.timing_pos_num()>=0) + // { get_det_num_pair_for_view_tangential_pos_num(det_num1, det_num2, bin.view_num(), bin.tangential_pos_num()); get_ring_pair_for_segment_axial_pos_num( ring_num1, ring_num2, bin.segment_num(), bin.axial_pos_num()); - } - else - { - get_det_num_pair_for_view_tangential_pos_num(det_num2, det_num1, bin.view_num(), bin.tangential_pos_num()); - get_ring_pair_for_segment_axial_pos_num( ring_num2, ring_num1, bin.segment_num(), bin.axial_pos_num()); - } + //} + //else + //{ + // get_det_num_pair_for_view_tangential_pos_num(det_num2, det_num1, bin.view_num(), bin.tangential_pos_num()); + // get_ring_pair_for_segment_axial_pos_num( ring_num2, ring_num1, bin.segment_num(), bin.axial_pos_num()); + // } } void From cabd3ab5cd2a6157e0c771661e7f92ae95c965f0 Mon Sep 17 00:00:00 2001 From: Ottavia Date: Tue, 12 Sep 2017 17:57:23 +0100 Subject: [PATCH 085/170] Add facility to LmToProjData to process multiple TOF bins in one go --- src/include/stir/listmode/LmToProjData.h | 4 +- src/listmode_buildblock/LmToProjData.cxx | 134 ++++++++++++++--------- 2 files changed, 83 insertions(+), 55 deletions(-) diff --git a/src/include/stir/listmode/LmToProjData.h b/src/include/stir/listmode/LmToProjData.h index 2cd25a678e..e268dd5401 100644 --- a/src/include/stir/listmode/LmToProjData.h +++ b/src/include/stir/listmode/LmToProjData.h @@ -110,7 +110,8 @@ class CListTime; ; 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 - + ; same for TOF bins + num_TOF_bins_in_memory := 1 End := \endverbatim @@ -241,6 +242,7 @@ class LmToProjData : public ParsingObject //! Use TOF information bool use_tof; int num_segments_in_memory; + int num_timing_poss_in_memory; // TODO make long (or even unsigned long) but can't do this yet because we can't parse longs yet int num_events_to_store; int max_segment_num_to_process; diff --git a/src/listmode_buildblock/LmToProjData.cxx b/src/listmode_buildblock/LmToProjData.cxx index 0fc7b8a2e7..7f6da73a27 100644 --- a/src/listmode_buildblock/LmToProjData.cxx +++ b/src/listmode_buildblock/LmToProjData.cxx @@ -118,11 +118,12 @@ typedef SegmentByView segment_type; static void -allocate_segments(VectorWithOffset& segments, - const int start_segment_index, - const int end_segment_index, - const ProjDataInfo* proj_data_info_ptr, - const int timing_pos_num = 0); +allocate_segments(VectorWithOffset >& segments, + const int start_timing_pos_index, + const int end_timing_pos_index, + const int start_segment_index, + const int end_segment_index, + const ProjDataInfo* proj_data_info_ptr); // In the next 2 functions, the 'output' parameter needs to be passed // because save_and_delete_segments needs it when we're not using SegmentByView @@ -132,7 +133,9 @@ allocate_segments(VectorWithOffset& segments, */ static void save_and_delete_segments(shared_ptr& output, - VectorWithOffset& segments, + VectorWithOffset >& segments, + const int start_timing_pos_index, + const int end_timing_pos_index, const int start_segment_index, const int end_segment_index, ProjData& proj_data); @@ -155,6 +158,7 @@ set_defaults() store_delayeds = true; interactive=false; num_segments_in_memory = -1; + num_timing_poss_in_memory = 1; normalisation_ptr.reset(new TrivialBinNormalisation); post_normalisation_ptr.reset(new TrivialBinNormalisation); do_pre_normalisation =0; @@ -176,6 +180,7 @@ initialise_keymap() parser.add_parsing_key("Bin Normalisation type for post-normalisation", &post_normalisation_ptr); parser.add_key("maximum absolute segment number to process", &max_segment_num_to_process); parser.add_key("do pre normalisation ", &do_pre_normalisation); + parser.add_key("num_TOF_bins_in_memory", &num_timing_poss_in_memory); parser.add_key("num_segments_in_memory", &num_segments_in_memory); //if (lm_data_ptr->has_delayeds()) TODO we haven't read the CListModeData yet, so cannot access has_delayeds() yet @@ -548,9 +553,13 @@ actual_process_data_without_tof() double time_of_last_stored_event = 0; long num_stored_events = 0; - VectorWithOffset - segments (template_proj_data_info_ptr->get_min_segment_num(), - template_proj_data_info_ptr->get_max_segment_num()); + VectorWithOffset > + segments (0,0); + for (int timing_pos_num=segments.get_min_index(); timing_pos_num<=segments.get_max_index(); ++timing_pos_num) + { + segments[timing_pos_num].resize(template_proj_data_info_ptr->get_min_segment_num(), + template_proj_data_info_ptr->get_max_segment_num()); + } VectorWithOffset frame_start_positions(1, static_cast(frame_defs.get_num_frames())); @@ -607,7 +616,7 @@ actual_process_data_without_tof() min( proj_data_ptr->get_max_segment_num()+1, start_segment_index + num_segments_in_memory) - 1; if (!interactive) - allocate_segments(segments, start_segment_index, end_segment_index, proj_data_ptr->get_proj_data_info_ptr()); + allocate_segments(segments, 0,0,start_segment_index, end_segment_index, proj_data_ptr->get_proj_data_info_ptr()); // the next variable is used to see if there are more events to store for the current segments // num_events_to_store-more_events will be the number of allowed coincidence events currently seen in the file @@ -713,7 +722,7 @@ actual_process_data_without_tof() bin.segment_num(), bin.view_num(), bin.axial_pos_num(), bin.tangential_pos_num(), current_time, event_increment); else - (*segments[bin.segment_num()])[bin.view_num()][bin.axial_pos_num()][bin.tangential_pos_num()] += + (*segments[0][bin.segment_num()])[bin.view_num()][bin.axial_pos_num()][bin.tangential_pos_num()] += bin.get_bin_value() * event_increment; } @@ -733,7 +742,7 @@ actual_process_data_without_tof() if (!interactive) save_and_delete_segments(output, segments, - start_segment_index, end_segment_index, + 0,0, start_segment_index, end_segment_index, *proj_data_ptr); } // end of for loop for segment range cerr << "\nNumber of prompts stored in this time period : " << num_prompts_in_frame @@ -810,20 +819,27 @@ actual_process_data_with_tof() const double start_time = frame_defs.get_start_time(current_frame_num); const double end_time = frame_defs.get_end_time(current_frame_num); - for (int current_timing_pos_index = proj_data_ptr->get_min_tof_pos_num(); - current_timing_pos_index <= proj_data_ptr->get_max_tof_pos_num(); - current_timing_pos_index += 1) + VectorWithOffset > + segments (template_proj_data_info_ptr->get_min_tof_pos_num(), + template_proj_data_info_ptr->get_max_tof_pos_num()); + for (int timing_pos_num=segments.get_min_index(); timing_pos_num<=segments.get_max_index(); ++timing_pos_num) + { + segments[timing_pos_num].resize(template_proj_data_info_ptr->get_min_segment_num(), + template_proj_data_info_ptr->get_max_segment_num()); + } + for (int start_timing_pos_index = proj_data_ptr->get_min_tof_pos_num(); + start_timing_pos_index <= proj_data_ptr->get_max_tof_pos_num(); + start_timing_pos_index += num_timing_poss_in_memory) { - /* - For each start_segment_index, we check which events occur in the - segments between start_segment_index and - start_segment_index+num_segments_in_memory. - */ - - VectorWithOffset - segments (template_proj_data_info_ptr->get_min_segment_num(), - template_proj_data_info_ptr->get_max_segment_num()); - + const int end_timing_pos_index = + min( proj_data_ptr->get_max_tof_pos_num()+1, + start_timing_pos_index + num_timing_poss_in_memory) - 1; + + /* + For each start_segment_index, we check which events occur in the + segments between start_segment_index and + start_segment_index+num_segments_in_memory. + */ for (int start_segment_index = proj_data_ptr->get_min_segment_num(); start_segment_index <= proj_data_ptr->get_max_segment_num(); start_segment_index += num_segments_in_memory) @@ -833,7 +849,10 @@ actual_process_data_with_tof() min( proj_data_ptr->get_max_segment_num()+1, start_segment_index + num_segments_in_memory) - 1; if (!interactive) - allocate_segments(segments, start_segment_index, end_segment_index, proj_data_ptr->get_proj_data_info_ptr(),current_timing_pos_index); + allocate_segments(segments, + start_timing_pos_index,end_timing_pos_index, + start_segment_index, end_segment_index, + proj_data_ptr->get_proj_data_info_ptr()); // the next variable is used to see if there are more events to store for the current segments // num_events_to_store-more_events will be the number of allowed coincidence events currently seen in the file @@ -843,10 +862,10 @@ actual_process_data_with_tof() unsigned long int more_events = do_time_frame? 1 : num_events_to_store; - if (start_segment_index != proj_data_ptr->get_min_segment_num() || current_timing_pos_index > proj_data_ptr->get_min_tof_pos_num()) + if (start_segment_index != proj_data_ptr->get_min_segment_num() || start_timing_pos_index > proj_data_ptr->get_min_tof_pos_num()) { // we're going once more through the data (for the next batch of segments) - cerr << "\nProcessing next batch of segments\n"; + cerr << "\nProcessing next batch of segments for start TOF bin " << start_timing_pos_index <<"\n"; // go to the beginning of the listmode data for this frame lm_data_ptr->set_get_position(frame_start_positions[current_frame_num]); current_time = start_time; @@ -924,8 +943,8 @@ actual_process_data_with_tof() if (!do_time_frame) more_events -= event_increment; - // Check if the timing position of the bin is the current one. - if (bin.timing_pos_num() == current_timing_pos_index) + // Check if the timing position of the bin is in the range + if (bin.timing_pos_num() >= start_timing_pos_index && bin.timing_pos_num()<=end_timing_pos_index) { // now check if we have its segment in memory if (bin.segment_num() >= start_segment_index && bin.segment_num()<=end_segment_index) @@ -946,7 +965,7 @@ actual_process_data_with_tof() bin.view_num(), bin.axial_pos_num(), bin.tangential_pos_num(), current_time, event_increment); else - (*segments[bin.segment_num()])[bin.view_num()][bin.axial_pos_num()][bin.tangential_pos_num()] += + (*segments[bin.timing_pos_num()][bin.segment_num()])[bin.view_num()][bin.axial_pos_num()][bin.tangential_pos_num()] += bin.get_bin_value() * event_increment; } @@ -968,7 +987,8 @@ actual_process_data_with_tof() if (!interactive) save_and_delete_segments(output, segments, - start_segment_index, end_segment_index, + start_timing_pos_index,end_timing_pos_index, + start_segment_index, end_segment_index, *proj_data_ptr); } // end of for loop for segment range @@ -994,7 +1014,10 @@ actual_process_data_with_tof() void LmToProjData::run_tof_test_function() { - VectorWithOffset +#if 1 + error("TOF test function disabled"); +#else + VectorWithOffset > segments (template_proj_data_info_ptr->get_min_segment_num(), template_proj_data_info_ptr->get_max_segment_num()); @@ -1031,7 +1054,9 @@ LmToProjData::run_tof_test_function() min( proj_data_ptr->get_max_segment_num()+1, start_segment_index + num_segments_in_memory) - 1; if (!interactive) - allocate_segments(segments, start_segment_index, end_segment_index, proj_data_ptr->get_proj_data_info_ptr(), current_timing_pos_index); + allocate_segments(segments, start_segment_index, end_segment_index, + start_timing_pos_index, end_timing_pos_index, + proj_data_ptr->get_proj_data_info_ptr(), current_timing_pos_index); for (int ax_num = proj_data_ptr->get_proj_data_info_ptr()->get_min_axial_pos_num(start_segment_index); ax_num <= proj_data_ptr->get_proj_data_info_ptr()->get_max_axial_pos_num(start_segment_index); @@ -1056,7 +1081,7 @@ LmToProjData::run_tof_test_function() } // end of for loop for segment range } // end of for loop for timing positions - +#endif } @@ -1065,20 +1090,22 @@ LmToProjData::run_tof_test_function() void -allocate_segments( VectorWithOffset& segments, +allocate_segments(VectorWithOffset > & segments, + const int start_timing_pos_index, + const int end_timing_pos_index, const int start_segment_index, const int end_segment_index, - const ProjDataInfo* proj_data_info_ptr, - const int timing_pos_num) + const ProjDataInfo* proj_data_info_ptr) { - for (int seg=start_segment_index ; seg<=end_segment_index; seg++) + for (int timing_pos_num=start_timing_pos_index ; timing_pos_num<=end_timing_pos_index; timing_pos_num++) + for (int seg=start_segment_index ; seg<=end_segment_index; seg++) { #ifdef USE_SegmentByView - segments[seg] = new SegmentByView( + segments[timing_pos_num][seg] = new SegmentByView( proj_data_info_ptr->get_empty_segment_by_view (seg, false, timing_pos_num)); #else - segments[seg] = + segments[timing_pos_num][seg] = new Array<3,elem_type>(IndexRange3D(0, proj_data_info_ptr->get_num_views()-1, 0, proj_data_info_ptr->get_num_axial_poss(seg)-1, -(proj_data_info_ptr->get_num_tangential_poss()/2), @@ -1089,24 +1116,23 @@ allocate_segments( VectorWithOffset& segments, void save_and_delete_segments(shared_ptr& output, - VectorWithOffset& segments, + VectorWithOffset >& segments, + const int start_timing_pos_index, + const int end_timing_pos_index, const int start_segment_index, - const int end_segment_index, - ProjData& proj_data) + const int end_segment_index, + ProjData& proj_data) { - - for (int seg=start_segment_index; seg<=end_segment_index; seg++) - { - { + for (int timing_pos_num=start_timing_pos_index ; timing_pos_num<=end_timing_pos_index; timing_pos_num++) + for (int seg=start_segment_index; seg<=end_segment_index; seg++) + { #ifdef USE_SegmentByView - proj_data.set_segment(*segments[seg]); + proj_data.set_segment(*segments[timing_pos_num][seg]); #else - (*segments[seg]).write_data(*output); + (*segments[timing_pos_num][seg]).write_data(*output); #endif - delete segments[seg]; - } - - } + delete segments[timing_pos_num][seg]; + } } From 74e2de4f40866a97fbde30c3bf75eaa8a7acd943 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Wed, 13 Sep 2017 16:56:38 +0100 Subject: [PATCH 086/170] Minor edits --- src/include/stir/ProjDataInfo.inl | 2 +- ...ithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/include/stir/ProjDataInfo.inl b/src/include/stir/ProjDataInfo.inl index c9ebc75bab..77ab6bad3b 100644 --- a/src/include/stir/ProjDataInfo.inl +++ b/src/include/stir/ProjDataInfo.inl @@ -97,7 +97,7 @@ ProjDataInfo::get_tof_bin(const double& delta) const return i; } // TODO handle differently - error(boost::format("TOF delta time %g out of range") % delta); + warning(boost::format("TOF delta time %g out of range") % delta); return 0; } diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx index ffd4736cd8..56087b3662 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx @@ -133,6 +133,9 @@ actual_subsets_are_approximately_balanced(std::string& warning_message) const Array<1,int> num_bins_in_subset(this->num_subsets); num_bins_in_subset.fill(0); + if (this->num_subsets == 1) + return true; + for (int subset_num=0; subset_numnum_subsets; ++subset_num) { From b091861efcb1c0446e41734b11bce30398175599 Mon Sep 17 00:00:00 2001 From: Elise Date: Fri, 15 Sep 2017 17:53:48 +0100 Subject: [PATCH 087/170] Adds a unit test to check the consistency between root listmode unlisting and STIR TOF projector. Also small fix to be able to use User_defined_scanners with TOF. --- src/IO/InterfileHeader.cxx | 3 +- src/recon_test/CMakeLists.txt | 13 + src/recon_test/test_consistency_root.cxx | 318 +++++++++++++++++++++++ 3 files changed, 333 insertions(+), 1 deletion(-) create mode 100644 src/recon_test/test_consistency_root.cxx diff --git a/src/IO/InterfileHeader.cxx b/src/IO/InterfileHeader.cxx index c556f75614..c7aa645add 100644 --- a/src/IO/InterfileHeader.cxx +++ b/src/IO/InterfileHeader.cxx @@ -1283,7 +1283,7 @@ bool InterfilePDFSHeader::post_processing() // data from the Interfile header (or the guessed scanner). shared_ptr scanner_sptr_from_file; - if (!guessed_scanner_ptr->is_tof_ready()) + if (false) // !guessed_scanner_ptr->is_tof_ready()) { scanner_sptr_from_file.reset( new Scanner(guessed_scanner_ptr->get_type(), @@ -1309,6 +1309,7 @@ bool InterfilePDFSHeader::post_processing() } else { + warning("ENERGY WINDOW INFO IGNORED"); scanner_sptr_from_file.reset( new Scanner(guessed_scanner_ptr->get_type(), get_exam_info_ptr()->originating_system, diff --git a/src/recon_test/CMakeLists.txt b/src/recon_test/CMakeLists.txt index 86852dcc28..497921ef48 100644 --- a/src/recon_test/CMakeLists.txt +++ b/src/recon_test/CMakeLists.txt @@ -33,6 +33,19 @@ set(${dir_INVOLVED_TEST_EXE_SOURCES} recontest ) +if (HAVE_CERN_ROOT) + list(APPEND ${dir_INVOLVED_TEST_EXE_SOURCES} test_consistency_root) + foreach(n RANGE 1 12 1) + set(test_name test_consistency_root_${n}) + ADD_TEST(NAME ${test_name} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/root_files_generation + COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test_consistency_root + ${CMAKE_CURRENT_SOURCE_DIR}/root_files_generation/root_header_test${n}.hroot + ${CMAKE_CURRENT_SOURCE_DIR}/root_files_generation/stir_image${n}.hv + ) + endforeach() +endif() + include(stir_test_exe_targets) # a test that uses MPI diff --git a/src/recon_test/test_consistency_root.cxx b/src/recon_test/test_consistency_root.cxx new file mode 100644 index 0000000000..5e9970f281 --- /dev/null +++ b/src/recon_test/test_consistency_root.cxx @@ -0,0 +1,318 @@ +/* + Copyright (C) 2017, UCL + This file is part of STIR. + + This file is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + See STIR/LICENSE.txt for details +*/ +/*! + \ingroup recon_test + Implementation of stir::test_consistency_root +*/ +#include "stir/ProjDataInfoCylindricalNoArcCorr.h" +#include "stir/recon_buildblock/ProjMatrixByBin.h" +#include "stir/recon_buildblock/ProjMatrixByBinUsingRayTracing.h" +#include "stir/recon_buildblock/ProjMatrixElemsForOneBin.h" +#include "stir/centre_of_gravity.h" +#include "stir/listmode/LmToProjData.h" +#include "stir/listmode/CListModeDataROOT.h" +#include "stir/listmode/CListRecord.h" +#include "stir/IO/read_from_file.h" +#include "stir/HighResWallClockTimer.h" +#include "stir/DiscretisedDensity.h" +#include "stir/VoxelsOnCartesianGrid.h" +#include "stir/Succeeded.h" +#include "stir/shared_ptr.h" +#include "stir/RunTests.h" +#include "boost/lexical_cast.hpp" + +#include "stir/info.h" +#include "stir/warning.h" + +#include + +using std::cerr; +using std::ifstream; + +START_NAMESPACE_STIR + +/*! + * \ingroup recon_test + * \brief Test class to check the consistency between ROOT listmode and STIR backprojection for high resolution TOF + * \author Elise Emond + * + * This test currently uses Root listmodes of single point sources. Scatters are not + * considered. It could be extended to actual scanner data, for which we would need + * to exclude scatter events. A way to do so would be to compute the distance between + * the bin LOR and the (non-TOF?) LOR calculated from the original data and exclude + * the events for which the distance would be more than a chosen threshold. + * + */ + +class ROOTconsistency_Tests : public RunTests +{ +public: + ROOTconsistency_Tests(std::string in, std::string image) + : root_header_filename(in), image_filename(image) + {} + void run_tests(); + + // Class to store the coordinates and weights of the maxima of the Lines-of-Response + // used to calculate the centre of gravity (see below). + class LORMax{ + public: + LORMax() { voxel_centre = CartesianCoordinate3D(0.f,0.f,0.f); value = 0.f;} + CartesianCoordinate3D voxel_centre; + float value; + }; + +private: + //! Reads listmode event by event, computes the ProjMatrixElemsForOneBin (probabilities + //! along a bin LOR) and stores in a vector the coordinates and weights of the + //! LOR maxima (vector::LORMax) prior to computing the centre of mass of those. + void construct_list_of_LOR_max( + const shared_ptr >& test_discretised_density_sptr); + + //! Selects and stores the highest probability elements of ProjMatrixElemsForOneBin. + void get_LOR_of_max(const ProjMatrixElemsForOneBin& probabilities, + const shared_ptr >& test_discretised_density_sptr); + + //! Given a vector::LORMax, computes the centre of mass. + CartesianCoordinate3D compute_centre_of_mass(); + + //! Checks if original and calculated coordinates are close enough. + void compare_original_and_calculated_coordinates( + const CartesianCoordinate3D& original_coords, + const CartesianCoordinate3D& centre_of_mass, + const BasicCoordinate<3, float>& grid_spacing); + + //! Modified version of check_if_equal for this test + bool check_if_almost_equal(const double a, const double b, std::string str, const double tolerance); + + std::string root_header_filename; + std::string image_filename; + std::vector max_lor; +}; + +void +ROOTconsistency_Tests::run_tests() +{ + // DiscretisedDensity for original image + shared_ptr > discretised_density_sptr(DiscretisedDensity<3,float>::read_from_file(image_filename)); + + // needs to be cast to VoxelsOnCartesianGrid to be able to calculate the centre of gravity, + // hence the location of the original source, stored in test_original_coords. + const VoxelsOnCartesianGrid& discretised_cartesian_grid = + dynamic_cast &>(*discretised_density_sptr); + CartesianCoordinate3D original_coords = find_centre_of_gravity_in_mm(discretised_cartesian_grid); + + construct_list_of_LOR_max(discretised_density_sptr); + + CartesianCoordinate3D centreofmass = compute_centre_of_mass(); + + compare_original_and_calculated_coordinates(original_coords,centreofmass,discretised_cartesian_grid.get_grid_spacing()); +} + +void +ROOTconsistency_Tests:: +construct_list_of_LOR_max(const shared_ptr >& discretised_density_sptr) +{ + shared_ptr lm_data_sptr(read_from_file(root_header_filename)); + + shared_ptr proj_matrix_sptr(new ProjMatrixByBinUsingRayTracing()); + + proj_matrix_sptr.get()->set_up(lm_data_sptr->get_proj_data_info_sptr(), + discretised_density_sptr); + proj_matrix_sptr->enable_tof(lm_data_sptr->get_proj_data_info_sptr()); + + ProjMatrixElemsForOneBin proj_matrix_row; + + { + // loop over all events in the listmode file + shared_ptr record_sptr = lm_data_sptr->get_empty_record_sptr(); + CListRecord& record = *record_sptr; + while (lm_data_sptr->get_next_record(record) == Succeeded::yes) + { + // only stores prompts + if (record.is_event() && record.event().is_prompt()) + { + Bin bin; + bin.set_bin_value(1.f); + // gets the bin corresponding to the event + record.event().get_bin(bin, *lm_data_sptr->get_proj_data_info_sptr()); + if ( bin.get_bin_value()>0 ) + { + // computes the TOF probabilities along the bin LOR + proj_matrix_sptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, bin); + // adds coordinates and weights of the elements with highest probability along LOR + get_LOR_of_max(proj_matrix_row, discretised_density_sptr); + } + } + } + } + +} + +void +ROOTconsistency_Tests::get_LOR_of_max(const ProjMatrixElemsForOneBin& probabilities, const shared_ptr >& test_discretised_density_sptr) +{ + std::stack tmp_max_lor; + + CartesianCoordinate3D voxel_centre; + + float maxLOR = 0; + + ProjMatrixElemsForOneBin::const_iterator element_ptr = probabilities.begin(); + // iterative calculation of highest probability and corresponding elements along the LOR + while (element_ptr != probabilities.end()) + { + if (element_ptr->get_value() >= maxLOR) + { + maxLOR = element_ptr->get_value(); + voxel_centre = + test_discretised_density_sptr->get_physical_coordinates_for_indices(element_ptr->get_coords()); + LORMax tmp; + tmp.value = element_ptr->get_value(); + tmp.voxel_centre = voxel_centre; + tmp_max_lor.push(tmp); + } + ++element_ptr; + } + + // only selects the elements on top of the stack, corresponding to the highest probability + if (maxLOR !=0) + { + while(!tmp_max_lor.empty()) + { + if (tmp_max_lor.top().value == maxLOR) + { + max_lor.push_back(tmp_max_lor.top()); + tmp_max_lor.pop(); + } + else break; + } + } +} + +CartesianCoordinate3D ROOTconsistency_Tests::compute_centre_of_mass() +{ + + // creation of a file with all LOR maxima, to be able to plot them + std::ofstream myfile; + std::string file_name = image_filename.substr(0,image_filename.size()-3) + ".txt"; + myfile.open (file_name.c_str()); + + LORMax centreofmass; + + // computes centre of mass + for (std::vector::iterator lor_element_ptr=max_lor.begin(); + lor_element_ptr != max_lor.end();++lor_element_ptr) + { + centreofmass.voxel_centre.x() += lor_element_ptr->voxel_centre.x()*lor_element_ptr->value; + centreofmass.voxel_centre.y() += lor_element_ptr->voxel_centre.y()*lor_element_ptr->value; + centreofmass.voxel_centre.z() += lor_element_ptr->voxel_centre.z()*lor_element_ptr->value; + centreofmass.value += lor_element_ptr->value; + + myfile << lor_element_ptr->voxel_centre.x() << " " + << lor_element_ptr->voxel_centre.y() << " " + << lor_element_ptr->voxel_centre.z() << " " + << lor_element_ptr->value << std::endl; + + } + + // needs to divide by the weights + if (centreofmass.value != 0) + { + centreofmass.voxel_centre.x()=centreofmass.voxel_centre.x()/centreofmass.value; + centreofmass.voxel_centre.y()=centreofmass.voxel_centre.y()/centreofmass.value; + centreofmass.voxel_centre.z()=centreofmass.voxel_centre.z()/centreofmass.value; + } + else + { + warning("Total weight of the centre of mass equal to 0. Please check your data."); + centreofmass.voxel_centre.x()=0; + centreofmass.voxel_centre.y()=0; + centreofmass.voxel_centre.z()=0; + } + + cerr << "Centre of gravity coordinates: " << centreofmass.voxel_centre.x() << " " + << centreofmass.voxel_centre.y() << " " << centreofmass.voxel_centre.z() << std::endl; + + myfile.close(); + + return centreofmass.voxel_centre; + +} + +// TODO change this +void ROOTconsistency_Tests::compare_original_and_calculated_coordinates(const CartesianCoordinate3D& original_coords, + const CartesianCoordinate3D& centre_of_mass, const BasicCoordinate<3, float>& grid_spacing) +{ + check_if_almost_equal(static_cast(original_coords.x()),static_cast(centre_of_mass.x()),"x",grid_spacing[1]); + check_if_almost_equal(static_cast(original_coords.y()),static_cast(centre_of_mass.y()),"y",grid_spacing[2]); + check_if_almost_equal(static_cast(original_coords.z()),static_cast(centre_of_mass.z()),"z",grid_spacing[3]); + + cerr << "Original coordinates: " << original_coords.x() << " " + << original_coords.y() << " " << original_coords.z() << std::endl; +} + +bool +ROOTconsistency_Tests::check_if_almost_equal(const double a, const double b, std::string str, const double tolerance) +{ + if ((fabs(a-b) > tolerance)) + { + std::cerr << "Error : unequal values are " << a << " and " << b + << ". " << str << std::endl; + everything_ok = false; + return false; + } + else + return true; +} + +END_NAMESPACE_STIR + +int main(int argc, char **argv) +{ + USING_NAMESPACE_STIR + + if (argc != 3) + { + cerr << "Usage : " << argv[1] << " filename " + << argv[2] << "original image \n" + << "See source file for the format of this file.\n\n"; + return EXIT_FAILURE; + } + + + ifstream in(argv[1]); + if (!in) + { + cerr << argv[0] + << ": Error opening root file " << argv[1] << "\nExiting.\n"; + + return EXIT_FAILURE; + } + ifstream in2(argv[2]); + if (!in2) + { + cerr << argv[0] + << ": Error opening original image " << argv[2] << "\nExiting.\n"; + + return EXIT_FAILURE; + } + std::string filename(argv[1]); + std::string image(argv[2]); + ROOTconsistency_Tests tests(filename,image); + tests.run_tests(); + return tests.main_return_value(); +} From 89fc82b655c60fb7e2c84594b630a507ab4136a2 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Tue, 19 Sep 2017 12:01:40 +0100 Subject: [PATCH 088/170] Removed code from LM objective function for separate projectors Kris said that this should move to a new class. --- ...oodWithLinearModelForMeanAndListModeData.h | 2 +- ...orMeanAndListModeDataWithProjMatrixByBin.h | 3 - ...MeanAndListModeDataWithProjMatrixByBin.cxx | 171 +++++------------- 3 files changed, 45 insertions(+), 131 deletions(-) diff --git a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeData.h b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeData.h index 41a051045c..69b6e076a3 100644 --- a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeData.h +++ b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeData.h @@ -122,7 +122,7 @@ public PoissonLogLikelihoodWithLinearModelForMean //! \author Nikos Efthimiou //! \details This is part of some functionality I transfer from lm_to_projdata. //! The total number of events to be *STORED* not *PROCESSED*. - unsigned long int num_events_to_store; + int num_events_to_store; //! //! \brief do_time_frame diff --git a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h index ea82529356..98ea3d6ecf 100644 --- a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h +++ b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h @@ -108,9 +108,6 @@ typedef RegisteredParsingObject PM_sptr; diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx index 56087b3662..126e8ca96b 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx @@ -92,7 +92,6 @@ set_defaults() this->normalisation_sptr.reset(new TrivialBinNormalisation); this->do_time_frame = false; this->use_tofsens = false; - this->use_projectors = false; } template @@ -106,8 +105,6 @@ initialise_keymap() this->parser.add_key("use time-of-flight sensitivities", &this->use_tofsens); this->parser.add_key("max ring difference num to process", &this->max_ring_difference_num_to_process); this->parser.add_parsing_key("Matrix type", &this->PM_sptr); - this->parser.add_parsing_key("Projector pair type", &this->projector_pair_ptr); - this->parser.add_key("use projectors", &use_projectors); this->parser.add_key("additive sinogram",&this->additive_projection_data_filename); this->parser.add_key("num_events_to_store",&this->num_events_to_store); @@ -214,28 +211,18 @@ set_up_before_sensitivity(shared_ptr const& target_sptr) distributed::send_int_value(100, -1); #endif - // Check if we have listmode projectors - if (!use_projectors) - { - // set projector to be used for the calculations - this->PM_sptr->set_up(proj_data_info_cyl_sptr->create_shared_clone(),target_sptr); + // set projector to be used for the calculations + this->PM_sptr->set_up(proj_data_info_cyl_sptr->create_shared_clone(),target_sptr); - this->PM_sptr->enable_tof(proj_data_info_cyl_sptr->create_shared_clone(), this->use_tof); + this->PM_sptr->enable_tof(proj_data_info_cyl_sptr->create_shared_clone(), this->use_tof); - shared_ptr forward_projector_ptr(new ForwardProjectorByBinUsingProjMatrixByBin(this->PM_sptr)); - shared_ptr back_projector_ptr(new BackProjectorByBinUsingProjMatrixByBin(this->PM_sptr)); + shared_ptr forward_projector_ptr(new ForwardProjectorByBinUsingProjMatrixByBin(this->PM_sptr)); + shared_ptr back_projector_ptr(new BackProjectorByBinUsingProjMatrixByBin(this->PM_sptr)); - this->projector_pair_ptr.reset( - new ProjectorByBinPairUsingSeparateProjectors(forward_projector_ptr, back_projector_ptr)); + this->projector_pair_ptr.reset( + new ProjectorByBinPairUsingSeparateProjectors(forward_projector_ptr, back_projector_ptr)); - this->projector_pair_ptr->set_up(proj_data_info_cyl_sptr->create_shared_clone(),target_sptr); - } - else - { - this->projector_pair_ptr->set_up(proj_data_info_cyl_sptr->create_shared_clone(),target_sptr); - - this->projector_pair_ptr->enable_tof(proj_data_info_cyl_sptr->create_shared_clone(), this->use_tof); - } + this->projector_pair_ptr->set_up(proj_data_info_cyl_sptr->create_shared_clone(),target_sptr); // sets non-tof backprojector for sensitivity calculation (clone of the back_projector + set projdatainfo to non-tof) this->sens_backprojector_sptr.reset(projector_pair_ptr->get_back_projector_sptr()->clone()); @@ -489,31 +476,9 @@ compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, shared_ptr record_sptr = this->list_mode_data_sptr->get_empty_record_sptr(); CListRecord& record = *record_sptr; - unsigned long int more_events = + long int more_events = this->do_time_frame? 1 : (this->num_events_to_store / this->num_subsets); - if (use_projectors) - { - - PresmoothingForwardProjectorByBin* forward= - dynamic_cast (this->projector_pair_ptr->get_forward_projector_sptr().get()); - - if (!is_null_ptr(forward)) - forward->update_filtered_density_image(current_estimate); - - PostsmoothingBackProjectorByBin* back= - dynamic_cast (this->projector_pair_ptr->get_back_projector_sptr().get()); - - if (!is_null_ptr(back)) - back->init_filtered_density_image(gradient); - - if (this->use_tof) - { - projector_pair_ptr->set_tof_data(&lor_points.p1(), &lor_points.p2()); - } - - } - while (more_events)//this->list_mode_data_sptr->get_next_record(record) == Succeeded::yes) { @@ -536,8 +501,7 @@ compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, { measured_bin.set_bin_value(1.0f); -// this->use_tof ? record.full_event(measured_bin, *proj_data_info_cyl_sptr): - record.event().get_bin(measured_bin, *proj_data_info_cyl_sptr); + record.event().get_bin(measured_bin, *proj_data_info_cyl_sptr); // In theory we have already done all these checks so we can // remove this if statement. @@ -570,94 +534,47 @@ compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, } } - if(!use_projectors) - { - if(this->use_tof) - { - lor_points = record.event().get_LOR(); - this->PM_sptr->get_proj_matrix_elems_for_one_bin_with_tof(proj_matrix_row, - measured_bin, - lor_points.p1(), lor_points.p2()); - } - else - this->PM_sptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, measured_bin); - - //in_the_range++; - fwd_bin.set_bin_value(0.0f); - proj_matrix_row.forward_project(fwd_bin,current_estimate); - // additive sinogram - if (!is_null_ptr(this->additive_proj_data_sptr)) - { - float add_value = this->additive_proj_data_sptr->get_bin_value(measured_bin); - float value= fwd_bin.get_bin_value()+add_value; - fwd_bin.set_bin_value(value); - } - float measured_div_fwd = 0.0f; - - if(!this->do_time_frame) - more_events -=1 ; - - num_stored_events += 1; - - if (num_stored_events%200000L==0) - info( boost::format("Stored Events: %1% ") % num_stored_events); - - if ( measured_bin.get_bin_value() <= max_quotient *fwd_bin.get_bin_value()) - measured_div_fwd = 1.0f /fwd_bin.get_bin_value(); - else - continue; - - measured_bin.set_bin_value(measured_div_fwd); - proj_matrix_row.back_project(gradient, measured_bin); - } - else - { - measured_bin.set_bin_value(0.0f); - - - if(this->use_tof) - lor_points = record.event().get_LOR(); - - projector_pair_ptr->get_forward_projector_sptr()->forward_project(measured_bin, - current_estimate); - - if (!is_null_ptr(this->additive_proj_data_sptr)) - { - float add_value = this->additive_proj_data_sptr->get_bin_value(measured_bin); - float value= measured_bin.get_bin_value()+add_value; - measured_bin.set_bin_value(value); - } - - float measured_div_fwd = 0.0f; - - if(!this->do_time_frame) - more_events -=1 ; + if(this->use_tof) + { + lor_points = record.event().get_LOR(); + this->PM_sptr->get_proj_matrix_elems_for_one_bin_with_tof(proj_matrix_row, + measured_bin, + lor_points.p1(), lor_points.p2()); + } + else + this->PM_sptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, measured_bin); + + //in_the_range++; + fwd_bin.set_bin_value(0.0f); + proj_matrix_row.forward_project(fwd_bin,current_estimate); + // additive sinogram + if (!is_null_ptr(this->additive_proj_data_sptr)) + { + float add_value = this->additive_proj_data_sptr->get_bin_value(measured_bin); + float value= fwd_bin.get_bin_value()+add_value; + fwd_bin.set_bin_value(value); + } + float measured_div_fwd = 0.0f; - num_stored_events += 1; + if(!this->do_time_frame) + more_events -=1 ; - if (num_stored_events%200000L==0) - info( boost::format("Stored Events: %1% ") % num_stored_events); + num_stored_events += 1; - if ( measured_bin.get_bin_value() <= max_quotient *measured_bin.get_bin_value()) - measured_div_fwd = 1.0f /measured_bin.get_bin_value(); - else - continue; + if (num_stored_events%200000L==0) + info( boost::format("Stored Events: %1% ") % num_stored_events); - measured_bin.set_bin_value(measured_div_fwd); - projector_pair_ptr->get_back_projector_sptr()->back_project(gradient, measured_bin); - } - } - } - if (use_projectors) - { - PostsmoothingBackProjectorByBin* back= - dynamic_cast (this->projector_pair_ptr->get_back_projector_sptr().get()); + if ( measured_bin.get_bin_value() <= max_quotient *fwd_bin.get_bin_value()) + measured_div_fwd = 1.0f /fwd_bin.get_bin_value(); + else + continue; - if (!is_null_ptr(back)) - back->update_filtered_density_image(gradient); + measured_bin.set_bin_value(measured_div_fwd); + proj_matrix_row.back_project(gradient, measured_bin); + } } - info(boost::format("Number of used events: %1%") % num_stored_events); + info(boost::format("Number of used events: %1%") % num_stored_events); } # ifdef _MSC_VER From ba61f34771a71287e383dd7df7a187659bd79ec8 Mon Sep 17 00:00:00 2001 From: NikEfth Date: Tue, 19 Sep 2017 14:45:54 +0000 Subject: [PATCH 089/170] Changes to be able to reconstruct both TOF proj and lm data from ROOT files. --- src/listmode_buildblock/CListModeDataROOT.cxx | 4 ++-- src/recon_buildblock/BinNormalisationFromProjData.cxx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/listmode_buildblock/CListModeDataROOT.cxx b/src/listmode_buildblock/CListModeDataROOT.cxx index 8e28a823b0..484c38fac3 100644 --- a/src/listmode_buildblock/CListModeDataROOT.cxx +++ b/src/listmode_buildblock/CListModeDataROOT.cxx @@ -167,8 +167,8 @@ CListModeDataROOT(const std::string& hroot_filename) /* arc_correction*/false, tof_mash_factor)); - if (tof_mash_factor != 1) - error("TOF mashing factor for ROOT different from 1 not implemented yet."); +// if (tof_mash_factor != 1) + // error("TOF mashing factor for ROOT different from 1 not implemented yet."); } std::string diff --git a/src/recon_buildblock/BinNormalisationFromProjData.cxx b/src/recon_buildblock/BinNormalisationFromProjData.cxx index 8e8c38e45e..77734eb250 100644 --- a/src/recon_buildblock/BinNormalisationFromProjData.cxx +++ b/src/recon_buildblock/BinNormalisationFromProjData.cxx @@ -109,8 +109,8 @@ set_up(const shared_ptr& proj_data_info_ptr) ++segment_num) { ok = - norm_proj.get_min_axial_pos_num(segment_num) == proj.get_min_axial_pos_num(segment_num) && - norm_proj.get_max_axial_pos_num(segment_num) == proj.get_max_axial_pos_num(segment_num); + norm_proj.get_min_axial_pos_num(segment_num) <= proj.get_min_axial_pos_num(segment_num) && + norm_proj.get_max_axial_pos_num(segment_num) >= proj.get_max_axial_pos_num(segment_num); } if (ok) return Succeeded::yes; From 031e347ab79f29da620dd763fe9db9e4aadf1ae6 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Fri, 22 Sep 2017 17:36:49 +0100 Subject: [PATCH 090/170] Important bug fix and change in CListRecord and CListEventCylindricalScannerWithDiscreteDetectors Now they are initialised using ProjDataInfo because otherwise the tof_mash_factor was wrong! --- ...tCylindricalScannerWithDiscreteDetectors.h | 5 +- ...ylindricalScannerWithDiscreteDetectors.inl | 10 +- ...ricalScannerWithViewTangRingRingEncoding.h | 4 +- .../stir/listmode/CListRecordGESigna.h | 102 +++++++++--------- src/include/stir/listmode/CListRecordROOT.h | 6 +- ...orMeanAndListModeDataWithProjMatrixByBin.h | 2 + .../CListModeDataGESigna.cxx | 2 +- src/listmode_buildblock/CListModeDataROOT.cxx | 2 +- .../CListRecordECAT8_32bit.cxx | 12 +-- src/listmode_buildblock/CListRecordROOT.cxx | 14 +-- ...MeanAndListModeDataWithProjMatrixByBin.cxx | 4 +- 11 files changed, 80 insertions(+), 83 deletions(-) diff --git a/src/include/stir/listmode/CListEventCylindricalScannerWithDiscreteDetectors.h b/src/include/stir/listmode/CListEventCylindricalScannerWithDiscreteDetectors.h index c802f6575d..6125b4f728 100644 --- a/src/include/stir/listmode/CListEventCylindricalScannerWithDiscreteDetectors.h +++ b/src/include/stir/listmode/CListEventCylindricalScannerWithDiscreteDetectors.h @@ -43,10 +43,10 @@ class CListEventCylindricalScannerWithDiscreteDetectors : public CListEvent { public: inline explicit - CListEventCylindricalScannerWithDiscreteDetectors(const shared_ptr& scanner_sptr); + CListEventCylindricalScannerWithDiscreteDetectors(const shared_ptr& proj_data_info); const Scanner * get_scanner_ptr() const - { return this->scanner_sptr.get(); } + { return this->uncompressed_proj_data_info_sptr->get_scanner_ptr(); } //! This routine returns the corresponding detector pair virtual void get_detection_position(DetectionPositionPair<>&) const = 0; @@ -86,7 +86,6 @@ class CListEventCylindricalScannerWithDiscreteDetectors : public CListEvent shared_ptr scanner_sptr; - private: shared_ptr uncompressed_proj_data_info_sptr; diff --git a/src/include/stir/listmode/CListEventCylindricalScannerWithDiscreteDetectors.inl b/src/include/stir/listmode/CListEventCylindricalScannerWithDiscreteDetectors.inl index 6f0b3f45ea..640611db97 100644 --- a/src/include/stir/listmode/CListEventCylindricalScannerWithDiscreteDetectors.inl +++ b/src/include/stir/listmode/CListEventCylindricalScannerWithDiscreteDetectors.inl @@ -30,18 +30,12 @@ START_NAMESPACE_STIR CListEventCylindricalScannerWithDiscreteDetectors:: -CListEventCylindricalScannerWithDiscreteDetectors(const shared_ptr& scanner_sptr) - : scanner_sptr(scanner_sptr) +CListEventCylindricalScannerWithDiscreteDetectors(const shared_ptr& proj_data_info) { this->uncompressed_proj_data_info_sptr.reset (dynamic_cast ( - ProjDataInfo::ProjDataInfoCTI(scanner_sptr, - 1, scanner_sptr->get_num_rings()-1, - scanner_sptr->get_num_detectors_per_ring()/2, - scanner_sptr->get_default_num_arccorrected_bins(), - false, - /*TOF mashing factor*/1))); + proj_data_info.get())); } LORAs2Points diff --git a/src/include/stir/listmode/CListEventCylindricalScannerWithViewTangRingRingEncoding.h b/src/include/stir/listmode/CListEventCylindricalScannerWithViewTangRingRingEncoding.h index a0827e7c4e..9ec3f79955 100644 --- a/src/include/stir/listmode/CListEventCylindricalScannerWithViewTangRingRingEncoding.h +++ b/src/include/stir/listmode/CListEventCylindricalScannerWithViewTangRingRingEncoding.h @@ -71,8 +71,8 @@ class CListEventCylindricalScannerWithViewTangRingRingEncoding : public CListEventCylindricalScannerWithDiscreteDetectors { public: - CListEventCylindricalScannerWithViewTangRingRingEncoding(const shared_ptr& scanner_sptr) : - CListEventCylindricalScannerWithDiscreteDetectors(scanner_sptr) + CListEventCylindricalScannerWithViewTangRingRingEncoding(const shared_ptr& proj_data_info) : + CListEventCylindricalScannerWithDiscreteDetectors(proj_data_info) {} //! This routine returns the corresponding detector pair diff --git a/src/include/stir/listmode/CListRecordGESigna.h b/src/include/stir/listmode/CListRecordGESigna.h index d6b35efc82..146282e447 100644 --- a/src/include/stir/listmode/CListRecordGESigna.h +++ b/src/include/stir/listmode/CListRecordGESigna.h @@ -9,7 +9,7 @@ This file is based on GE proprietary information and can therefore not be distributed outside UCL without approval from GE. - + \author Kris Thielemans */ @@ -58,11 +58,11 @@ enum ExtendedEvtType */ class CListEventDataGESigna { - public: + public: inline bool is_prompt() const { return true; } // TODO - inline Succeeded set_prompt(const bool prompt = true) - { - //if (prompt) random=1; else random=0; return Succeeded::yes; + inline Succeeded set_prompt(const bool prompt = true) + { + //if (prompt) random=1; else random=0; return Succeeded::yes; return Succeeded::no; } inline void get_detection_position(DetectionPositionPair<>& det_pos) const @@ -85,13 +85,13 @@ class CListEventDataGESigna } } inline bool is_event() const - { - return (eventType==COINC_EVT)/* && eventTypeExt==COINC_COUNT_EVT)*/; + { + return (eventType==COINC_EVT)/* && eventTypeExt==COINC_COUNT_EVT)*/; } // TODO need to find out how to see if it's a coincidence event inline int get_tof_bin() const - { - return static_cast(deltaTime); - } + { + return static_cast(deltaTime); + } private: #if STIRIsNativeByteOrderBigEndian @@ -123,17 +123,17 @@ class CListTimeDataGESigna inline unsigned long get_time_in_millisecs() const { return (time_hi()<<16) | time_lo(); } inline Succeeded set_time_in_millisecs(const unsigned long time_in_millisecs) - { - data.timeMarkerLS = ((1UL<<16)-1) & (time_in_millisecs); - data.timeMarkerMS = (time_in_millisecs) >> 16; + { + data.timeMarkerLS = ((1UL<<16)-1) & (time_in_millisecs); + data.timeMarkerMS = (time_in_millisecs) >> 16; // TODO return more useful value return Succeeded::yes; } inline bool is_time() const { // TODO need to find out how to see if it's a timing event - return (data.eventType==EXTENDED_EVT) && (data.eventTypeExt==TIME_MARKER_EVT); + return (data.eventType==EXTENDED_EVT) && (data.eventTypeExt==TIME_MARKER_EVT); }// TODO - + private: typedef union{ struct { @@ -151,7 +151,7 @@ class CListTimeDataGESigna boost::uint16_t timeMarkerLS:16; /* Least Significant 16 bits of 32-bit Time Marker */ boost::uint16_t timeMarkerMS:16; /* Most Significant 16 bits of 32-bitTime Marker */ #endif - }; + }; } data_t; data_t data; @@ -173,9 +173,9 @@ class CListGatingDataGESigna inline unsigned long get_time_in_millisecs() const { return (time_hi()<<24) | time_lo(); } inline Succeeded set_time_in_millisecs(const unsigned long time_in_millisecs) - { - words[0].value = ((1UL<<24)-1) & (time_in_millisecs); - words[1].value = (time_in_millisecs) >> 24; + { + words[0].value = ((1UL<<24)-1) & (time_in_millisecs); + words[1].value = (time_in_millisecs) >> 24; // TODO return more useful value return Succeeded::yes; } @@ -184,9 +184,9 @@ class CListGatingDataGESigna { return (words[0].signature==21) && (words[1].signature==29); } inline unsigned int get_gating() const { return words[0].reserved; } // return "reserved" bits. might be something in there - inline Succeeded set_gating(unsigned int g) + inline Succeeded set_gating(unsigned int g) { words[0].reserved = g&7; return Succeeded::yes; } - + private: typedef union{ struct { @@ -199,7 +199,7 @@ class CListGatingDataGESigna boost::uint32_t reserved : 3; boost::uint32_t signature : 5; #endif - }; + }; boost::uint32_t raw; } oneword_t; oneword_t words[2]; @@ -213,7 +213,7 @@ class CListGatingDataGESigna This class essentially just forwards the work to the "basic" classes. A complication for GE Dimension data is that not all events are the same size: - coincidence events are 4 bytes, and others are 8 bytes. + coincidence events are 4 bytes, and others are 8 bytes. \todo Currently we always assume the data is from a DSTE. We should really read this from the RDF header. */ @@ -224,13 +224,13 @@ class CListRecordGESigna : public CListRecord, public CListTime, // public CList typedef CListTimeDataGESigna TimeType; //typedef CListGatingDataGESigna GatingType; - public: - CListRecordGESigna() : - CListEventCylindricalScannerWithDiscreteDetectors(shared_ptr(new Scanner(Scanner::PETMR_Signa))) + public: + CListRecordGESigna(const shared_ptr& proj_data_info_sptr) : + CListEventCylindricalScannerWithDiscreteDetectors(proj_data_info_sptr) {} bool is_time() const - { + { return this->time_data.is_time(); } #if 0 @@ -242,7 +242,7 @@ class CListRecordGESigna : public CListRecord, public CListTime, // public CList bool is_event() const { return this->event_data.is_event(); } - virtual CListEvent& event() + virtual CListEvent& event() { return *this; } virtual const CListEvent& event() const { return *this; } @@ -265,22 +265,22 @@ dynamic_cast(&e2) != 0 && raw[0] == static_cast(e2).raw[0] && (this->is_event() || (raw[1] == static_cast(e2).raw[1])); #endif - } + } - // time - inline unsigned long get_time_in_millisecs() const + // time + inline unsigned long get_time_in_millisecs() const { return time_data.get_time_in_millisecs(); } inline Succeeded set_time_in_millisecs(const unsigned long time_in_millisecs) { return time_data.set_time_in_millisecs(time_in_millisecs); } #if 0 inline unsigned int get_gating() const { return gating_data.get_gating(); } - inline Succeeded set_gating(unsigned int g) + inline Succeeded set_gating(unsigned int g) { return gating_data.set_gating(g); } #endif // event inline bool is_prompt() const { return event_data.is_prompt(); } - inline Succeeded set_prompt(const bool prompt = true) + inline Succeeded set_prompt(const bool prompt = true) { return event_data.set_prompt(prompt); } virtual void get_detection_position(DetectionPositionPair<>& det_pos) const @@ -292,15 +292,15 @@ dynamic_cast(&e2) != 0 && error("TODO"); } - virtual std::size_t size_of_record_at_ptr(const char * const data_ptr, const std::size_t /*size*/, + virtual std::size_t size_of_record_at_ptr(const char * const data_ptr, const std::size_t /*size*/, const bool do_byte_swap) const - { + { // TODO: get size of record from the file, whereas here I have hard-coded as being 6bytes (I know it's the case for the Orsay data) OtB 15/09 return std::size_t(6); // std::size_t(data_ptr[0]&0x80); } - virtual Succeeded init_from_data_ptr(const char * const data_ptr, + virtual Succeeded init_from_data_ptr(const char * const data_ptr, const std::size_t #ifndef NDEBUG size // only used within assert, so don't define otherwise to avoid compiler warning @@ -321,38 +321,38 @@ dynamic_cast(&e2) != 0 && { // std::cout << "This is an event \n" ; assert(size >= 6); - + std::copy(data_ptr+6, data_ptr+6, reinterpret_cast(&this->raw[1])); // std::cout << "after assert an event \n" ; } if (do_byte_swap) { - error("don't know how to byteswap"); + error("don't know how to byteswap"); ByteOrder::swap_order(this->raw[1]); } - - if (this->is_event()) - { - // set TOF info in ps - this->delta_time = this->event_data.get_tof_bin() *this-> get_scanner_ptr()->get_size_of_timing_bin(); - } - - - + + if (this->is_event()) + { + // set TOF info in ps + this->delta_time = this->event_data.get_tof_bin() *this-> get_scanner_ptr()->get_size_of_timing_bin(); + } + + + return Succeeded::yes; } private: union { DataType event_data; - TimeType time_data; + TimeType time_data; //GatingType gating_data; boost::int32_t raw[2]; }; BOOST_STATIC_ASSERT(sizeof(boost::int32_t)==4); - BOOST_STATIC_ASSERT(sizeof(DataType)==6); - BOOST_STATIC_ASSERT(sizeof(TimeType)==6); - //BOOST_STATIC_ASSERT(sizeof(GatingType)==8); + BOOST_STATIC_ASSERT(sizeof(DataType)==6); + BOOST_STATIC_ASSERT(sizeof(TimeType)==6); + //BOOST_STATIC_ASSERT(sizeof(GatingType)==8); }; diff --git a/src/include/stir/listmode/CListRecordROOT.h b/src/include/stir/listmode/CListRecordROOT.h index 6c2b10177e..173e1282e9 100644 --- a/src/include/stir/listmode/CListRecordROOT.h +++ b/src/include/stir/listmode/CListRecordROOT.h @@ -42,7 +42,7 @@ class CListEventROOT : public CListEventCylindricalScannerWithDiscreteDetectors { public: - CListEventROOT(const shared_ptr& scanner_sptr); + CListEventROOT(const shared_ptr& proj_data_info); //! This routine returns the corresponding detector pair virtual void get_detection_position(DetectionPositionPair<>&) const; @@ -145,8 +145,8 @@ class CListRecordROOT : public CListRecord // currently no gating yet raw[1] == dynamic_cast(e2).raw[1]; } - CListRecordROOT(const shared_ptr& scanner_sptr) : - event_data(scanner_sptr) + CListRecordROOT(const shared_ptr& proj_data_info_sptr) : + event_data(proj_data_info_sptr) {} virtual Succeeded init_from_data( const int& ring1, diff --git a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h index 98ea3d6ecf..272463805e 100644 --- a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h +++ b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h @@ -125,6 +125,8 @@ typedef RegisteredParsingObject proj_data_info_cyl_sptr; + shared_ptr record_sptr; + //! sets any default values /*! Has to be called by set_defaults in the leaf-class */ virtual void set_defaults(); diff --git a/src/listmode_buildblock/CListModeDataGESigna.cxx b/src/listmode_buildblock/CListModeDataGESigna.cxx index 3ba7e3ab35..36c8ce6a8e 100644 --- a/src/listmode_buildblock/CListModeDataGESigna.cxx +++ b/src/listmode_buildblock/CListModeDataGESigna.cxx @@ -57,7 +57,7 @@ shared_ptr CListModeDataGESigna:: get_empty_record_sptr() const { - shared_ptr sptr(new CListRecordT); + shared_ptr sptr(new CListRecordT(this->proj_data_info_sptr)); return sptr; } diff --git a/src/listmode_buildblock/CListModeDataROOT.cxx b/src/listmode_buildblock/CListModeDataROOT.cxx index 484c38fac3..35be59d5cf 100644 --- a/src/listmode_buildblock/CListModeDataROOT.cxx +++ b/src/listmode_buildblock/CListModeDataROOT.cxx @@ -182,7 +182,7 @@ shared_ptr CListModeDataROOT:: get_empty_record_sptr() const { - shared_ptr sptr(new CListRecordROOT(this->scanner_sptr)); + shared_ptr sptr(new CListRecordROOT(this->proj_data_info_sptr)); return sptr; } diff --git a/src/listmode_buildblock/CListRecordECAT8_32bit.cxx b/src/listmode_buildblock/CListRecordECAT8_32bit.cxx index 274e08ff43..7b1857fdbc 100644 --- a/src/listmode_buildblock/CListRecordECAT8_32bit.cxx +++ b/src/listmode_buildblock/CListRecordECAT8_32bit.cxx @@ -36,16 +36,16 @@ namespace ecat { CListEventECAT8_32bit:: CListEventECAT8_32bit(const shared_ptr& proj_data_info_sptr) : - CListEventCylindricalScannerWithDiscreteDetectors(shared_ptr(new Scanner(*proj_data_info_sptr->get_scanner_ptr()))) + CListEventCylindricalScannerWithDiscreteDetectors(proj_data_info_sptr) { const ProjDataInfoCylindricalNoArcCorr * const proj_data_info_ptr = dynamic_cast(proj_data_info_sptr.get()); if (proj_data_info_ptr == 0) error("CListEventECAT8_32bit can only be initialised with cylindrical projection data without arc-correction"); - const int num_rings = this->scanner_sptr->get_num_rings(); - const int max_ring_diff=proj_data_info_ptr->get_max_ring_difference(proj_data_info_ptr->get_max_segment_num()); - if (proj_data_info_ptr->get_max_ring_difference(0) != proj_data_info_ptr->get_min_ring_difference(0)) + const int num_rings = this->uncompressed_proj_data_info_sptr->get_scanner_ptr()->get_num_rings(); + const int max_ring_diff=this->uncompressed_proj_data_info_sptr->get_max_ring_difference(proj_data_info_ptr->get_max_segment_num()); + if (this->uncompressed_proj_data_info_sptr->get_max_ring_difference(0) != this->uncompressed_proj_data_info_sptr->get_min_ring_difference(0)) error("CListEventECAT8_32bit can only handle axial compression==1"); this->segment_sequence.resize(2*max_ring_diff+1); this->sizes.resize(2*max_ring_diff+1); @@ -65,8 +65,8 @@ CListEventECAT8_32bit:: get_detection_position(DetectionPositionPair<>& det_pos) const { /* data is organised by segment, axial coordinate, view, tangential */ - const int num_tangential_poss = this->scanner_sptr->get_default_num_arccorrected_bins(); - const int num_views = this->scanner_sptr->get_num_detectors_per_ring()/2; + const int num_tangential_poss = this->uncompressed_proj_data_info_sptr->get_scanner_ptr()->get_default_num_arccorrected_bins(); + const int num_views = this->uncompressed_proj_data_info_sptr->get_scanner_ptr()->get_num_detectors_per_ring()/2; const int tang_pos_num = this->data.offset % num_tangential_poss;//(this->num_sinograms * this-> num_views); const int rest = this->data.offset / num_tangential_poss; diff --git a/src/listmode_buildblock/CListRecordROOT.cxx b/src/listmode_buildblock/CListRecordROOT.cxx index 8711fa3ae9..abbd3c4709 100644 --- a/src/listmode_buildblock/CListRecordROOT.cxx +++ b/src/listmode_buildblock/CListRecordROOT.cxx @@ -32,10 +32,10 @@ START_NAMESPACE_STIR CListEventROOT:: -CListEventROOT(const shared_ptr& scanner_sptr) : - CListEventCylindricalScannerWithDiscreteDetectors(scanner_sptr) +CListEventROOT(const shared_ptr &proj_data_info) : + CListEventCylindricalScannerWithDiscreteDetectors(proj_data_info) { - quarter_of_detectors = static_cast(scanner_sptr->get_num_detectors_per_ring()/4.f); + quarter_of_detectors = static_cast(proj_data_info->get_scanner_ptr()->get_num_detectors_per_ring()/4.f); } //! @@ -70,13 +70,13 @@ void CListEventROOT::init_from_data(const int& _ring1, const int& _ring2, if (det1 < 0 ) det1 = scanner_sptr->get_num_detectors_per_ring() + det1; - else if ( det1 >= scanner_sptr->get_num_detectors_per_ring()) - det1 = det1 - scanner_sptr->get_num_detectors_per_ring(); + else if ( det1 >= this->uncompressed_proj_data_info_sptr->get_scanner_ptr()->get_num_detectors_per_ring()) + det1 = det1 - this->uncompressed_proj_data_info_sptr->get_scanner_ptr()->get_num_detectors_per_ring(); if (det2 < 0 ) det2 = scanner_sptr->get_num_detectors_per_ring() + det2; - else if ( det2 >= scanner_sptr->get_num_detectors_per_ring()) - det2 = det2 - scanner_sptr->get_num_detectors_per_ring(); + else if ( det2 >= this->uncompressed_proj_data_info_sptr->get_scanner_ptr()->get_num_detectors_per_ring()) + det2 = det2 - this->uncompressed_proj_data_info_sptr->get_scanner_ptr()->get_num_detectors_per_ring(); ring1 = _ring1; ring2 = _ring2; diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx index 126e8ca96b..9b0a38f635 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx @@ -252,6 +252,8 @@ set_up_before_sensitivity(shared_ptr const& target_sptr) return Succeeded::no; } + record_sptr = this->list_mode_data_sptr->get_empty_record_sptr(); + return Succeeded::yes; } @@ -473,7 +475,7 @@ compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, double current_time = 0.; ProjMatrixElemsForOneBin proj_matrix_row; - shared_ptr record_sptr = this->list_mode_data_sptr->get_empty_record_sptr(); + CListRecord& record = *record_sptr; long int more_events = From c75d71c8b460d6c25bdf98a3e61d6b5845e6ca7e Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Fri, 22 Sep 2017 18:57:26 +0100 Subject: [PATCH 091/170] Bug fix ProjDataInfoCylindricalNoArcCorr: scanner_sptr should be deleted --- ...ylindricalScannerWithDiscreteDetectors.inl | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/include/stir/listmode/CListEventCylindricalScannerWithDiscreteDetectors.inl b/src/include/stir/listmode/CListEventCylindricalScannerWithDiscreteDetectors.inl index 640611db97..f26d81dc4f 100644 --- a/src/include/stir/listmode/CListEventCylindricalScannerWithDiscreteDetectors.inl +++ b/src/include/stir/listmode/CListEventCylindricalScannerWithDiscreteDetectors.inl @@ -4,9 +4,9 @@ \file \ingroup listmode \brief Implementations of class stir::CListEventCylindricalScannerWithDiscreteDetectors - + \author Kris Thielemans - + */ /* Copyright (C) 2003- 2011, Hammersmith Imanet Ltd @@ -36,6 +36,9 @@ CListEventCylindricalScannerWithDiscreteDetectors(const shared_ptr (dynamic_cast ( proj_data_info.get())); + + if (is_null_ptr(this->uncompressed_proj_data_info_sptr)) + error("CListEventCylindricalScannerWithDiscreteDetectors takes only ProjDataInfoCylindricalNoArcCorr. Abord."); } LORAs2Points @@ -51,7 +54,7 @@ get_LOR() const this->get_detection_position(det_pos); assert(det_pos.pos1().radial_coord()==0); assert(det_pos.pos2().radial_coord()==0); - + // TODO we're using an obsolete function here which uses a different coordinate system this->get_uncompressed_proj_data_info_sptr()-> find_cartesian_coordinates_given_scanner_coordinates(coord_1, coord_2, @@ -60,15 +63,15 @@ get_LOR() const det_pos.pos1().tangential_coord(), det_pos.pos2().tangential_coord()); // find shift in z - const float shift = this->scanner_sptr->get_ring_spacing()* - (this->scanner_sptr->get_num_rings()-1)/2.F; + const float shift = this->get_uncompressed_proj_data_info_sptr()->get_ring_spacing()* + (this->get_uncompressed_proj_data_info_sptr()->get_scanner_ptr()->get_num_rings()-1)/2.F; coord_1.z() -= shift; coord_2.z() -= shift; - + return lor; } -void +void CListEventCylindricalScannerWithDiscreteDetectors:: get_bin(Bin& bin, const ProjDataInfo& proj_data_info) const { @@ -88,10 +91,10 @@ bool CListEventCylindricalScannerWithDiscreteDetectors:: is_valid_template(const ProjDataInfo& proj_data_info) const { - if (dynamic_cast(&proj_data_info)!= 0) - return true; + if (dynamic_cast(&proj_data_info)!= 0) + return true; - return false; + return false; } END_NAMESPACE_STIR From aee5de758c8836a5088603f22b85acc1b0fe8078 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Wed, 27 Sep 2017 02:29:54 +0100 Subject: [PATCH 092/170] Remove tof_mash_factor from get_bin_for_det_pos_pair as ... applied in get_tof_bin() . --- src/include/stir/ProjDataInfoCylindricalNoArcCorr.inl | 2 +- .../CListEventCylindricalScannerWithDiscreteDetectors.h | 2 +- src/listmode_buildblock/CListRecordROOT.cxx | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/include/stir/ProjDataInfoCylindricalNoArcCorr.inl b/src/include/stir/ProjDataInfoCylindricalNoArcCorr.inl index b4ffa53c20..69f30f0522 100644 --- a/src/include/stir/ProjDataInfoCylindricalNoArcCorr.inl +++ b/src/include/stir/ProjDataInfoCylindricalNoArcCorr.inl @@ -164,7 +164,7 @@ get_bin_for_det_pos_pair(Bin& bin, dp.pos2().axial_coord(), this->get_tof_mash_factor()==0 ? 0 // use timing_pos==0 in the nonTOF case - : stir::round((float)dp.timing_pos()/this->get_tof_mash_factor())); + : dp.timing_pos()); } void ProjDataInfoCylindricalNoArcCorr:: diff --git a/src/include/stir/listmode/CListEventCylindricalScannerWithDiscreteDetectors.h b/src/include/stir/listmode/CListEventCylindricalScannerWithDiscreteDetectors.h index 6125b4f728..b5535c40eb 100644 --- a/src/include/stir/listmode/CListEventCylindricalScannerWithDiscreteDetectors.h +++ b/src/include/stir/listmode/CListEventCylindricalScannerWithDiscreteDetectors.h @@ -84,7 +84,7 @@ class CListEventCylindricalScannerWithDiscreteDetectors : public CListEvent return uncompressed_proj_data_info_sptr; } - shared_ptr scanner_sptr; +// shared_ptr scanner_sptr; shared_ptr uncompressed_proj_data_info_sptr; diff --git a/src/listmode_buildblock/CListRecordROOT.cxx b/src/listmode_buildblock/CListRecordROOT.cxx index abbd3c4709..5a526bb5e3 100644 --- a/src/listmode_buildblock/CListRecordROOT.cxx +++ b/src/listmode_buildblock/CListRecordROOT.cxx @@ -69,12 +69,12 @@ void CListEventROOT::init_from_data(const int& _ring1, const int& _ring2, det2 = crystal2 + quarter_of_detectors; if (det1 < 0 ) - det1 = scanner_sptr->get_num_detectors_per_ring() + det1; + det1 = this->uncompressed_proj_data_info_sptr->get_scanner_ptr()->get_num_detectors_per_ring() + det1; else if ( det1 >= this->uncompressed_proj_data_info_sptr->get_scanner_ptr()->get_num_detectors_per_ring()) det1 = det1 - this->uncompressed_proj_data_info_sptr->get_scanner_ptr()->get_num_detectors_per_ring(); if (det2 < 0 ) - det2 = scanner_sptr->get_num_detectors_per_ring() + det2; + det2 = this->uncompressed_proj_data_info_sptr->get_scanner_ptr()->get_num_detectors_per_ring() + det2; else if ( det2 >= this->uncompressed_proj_data_info_sptr->get_scanner_ptr()->get_num_detectors_per_ring()) det2 = det2 - this->uncompressed_proj_data_info_sptr->get_scanner_ptr()->get_num_detectors_per_ring(); From fc84e85d8247a9768fb644594cf1d2ab6ae02261 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Wed, 27 Sep 2017 18:03:12 +0100 Subject: [PATCH 093/170] fix for distributable computation at send_viewgrams() Error on the timing position. --- src/recon_buildblock/distributable.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/recon_buildblock/distributable.cxx b/src/recon_buildblock/distributable.cxx index 4ed12d0fe1..a256903aa0 100644 --- a/src/recon_buildblock/distributable.cxx +++ b/src/recon_buildblock/distributable.cxx @@ -241,7 +241,7 @@ void send_viewgrams(const shared_ptr >& y, const int next_receiver) { distributed::send_view_segment_numbers( y->get_basic_view_segment_num(), NEW_VIEWGRAM_TAG, next_receiver); - distributed::send_int_value( y->get_timing_pos_num(), next_receiver); + distributed::send_int_value( y->get_basic_timing_pos_num(), next_receiver); #ifndef NDEBUG //test sending related viegrams From 3dda018086273674153dacff3e71e0741628e8dc Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Fri, 29 Sep 2017 19:14:18 +0100 Subject: [PATCH 094/170] Workaround for incomparable results between lm and proj recon. The TOF kernels from CListEventCylindricalScannerWithDiscreteDetectors::get_LOR() and ProjDataInfo::get_LOR() are shifted. The difference is small to be observed with simple numeric tests but the reconstructed images have distict differences and artifacts. In most cases they are not the same (by compare_image). --- src/include/stir/recon_buildblock/ProjMatrixByBin.inl | 3 ++- ...ModelForMeanAndListModeDataWithProjMatrixByBin.cxx | 11 +++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl index 9171ea05d2..4ad5875445 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl +++ b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl @@ -61,7 +61,8 @@ get_proj_matrix_elems_for_one_bin( // set to empty probabilities.erase(); - if (proj_data_info_sptr && proj_data_info_sptr->is_tof_data()) + if (proj_data_info_sptr && (proj_data_info_sptr->is_tof_data() || + this->tof_enabled)) { LORInAxialAndNoArcCorrSinogramCoordinates lor; proj_data_info_sptr->get_LOR(lor, bin); diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx index 9b0a38f635..2c09474d9d 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx @@ -538,10 +538,13 @@ compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, if(this->use_tof) { - lor_points = record.event().get_LOR(); - this->PM_sptr->get_proj_matrix_elems_for_one_bin_with_tof(proj_matrix_row, - measured_bin, - lor_points.p1(), lor_points.p2()); +// lor_points = record.event().get_LOR(); +// this->PM_sptr->get_proj_matrix_elems_for_one_bin_with_tof(proj_matrix_row, +// measured_bin, +// lor_points.p1(), lor_points.p2()); + + this->PM_sptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, + measured_bin); } else this->PM_sptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, measured_bin); From 96a30a9daa49bccff60f0e59bb5b2a97381e031a Mon Sep 17 00:00:00 2001 From: Elise Date: Sat, 30 Sep 2017 18:05:58 +0100 Subject: [PATCH 095/170] Fix calculation of sensitivity for TOF projection data With the added use of distributable computation for sensitivity within the reconstruction, there was a inconsistency in the code when calculating non-TOF sensitivity for TOF projection data, which was previously using a TOF backprojector in one part and a non-TOF backprojector in another part, ending with the calculation of wrong sensitivity images. --- .../PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx index 23bd65cf48..637fdf2a10 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx @@ -778,7 +778,7 @@ add_subset_sensitivity(TargetT& sensitivity, const int subset_num) const sens_proj_data_sptr->fill(1.0F); distributable_sensitivity_computation(this->projector_pair_ptr->get_forward_projector_sptr(), - this->projector_pair_ptr->get_back_projector_sptr(), + this->sens_backprojector_sptr, this->symmetries_sptr, *sensitivity_this_subset_sptr, sensitivity, From e2647236cfa305a5c93613b603e93b30c5137c1f Mon Sep 17 00:00:00 2001 From: Elise Date: Tue, 3 Oct 2017 20:03:56 +0100 Subject: [PATCH 096/170] Fix segmentation fault due to double deletion of shared_ptr in CListEventCylindricalScannerWithDiscreteDetectors The pointer was deleted twice in list_lm_events or lm_to_projdata when reading the listmode data, which was causing a segmentation fault. A dynamic_pointer_cast replaced proj_data_info.get(). --- .../CListEventCylindricalScannerWithDiscreteDetectors.inl | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/include/stir/listmode/CListEventCylindricalScannerWithDiscreteDetectors.inl b/src/include/stir/listmode/CListEventCylindricalScannerWithDiscreteDetectors.inl index f26d81dc4f..1847be7cea 100644 --- a/src/include/stir/listmode/CListEventCylindricalScannerWithDiscreteDetectors.inl +++ b/src/include/stir/listmode/CListEventCylindricalScannerWithDiscreteDetectors.inl @@ -30,12 +30,10 @@ START_NAMESPACE_STIR CListEventCylindricalScannerWithDiscreteDetectors:: -CListEventCylindricalScannerWithDiscreteDetectors(const shared_ptr& proj_data_info) +CListEventCylindricalScannerWithDiscreteDetectors(const shared_ptr& proj_data_info_sptr) { - this->uncompressed_proj_data_info_sptr.reset - (dynamic_cast - ( - proj_data_info.get())); + this->uncompressed_proj_data_info_sptr = + dynamic_pointer_cast(proj_data_info_sptr); if (is_null_ptr(this->uncompressed_proj_data_info_sptr)) error("CListEventCylindricalScannerWithDiscreteDetectors takes only ProjDataInfoCylindricalNoArcCorr. Abord."); From a740037db62ea5aa536998ba433f863c9ef5d6d4 Mon Sep 17 00:00:00 2001 From: Elise Date: Wed, 4 Oct 2017 16:17:15 +0100 Subject: [PATCH 097/170] Rolling back to previous version-ProjData recons All the tests are successful with this version of the code and there should not be any reason to remove the tof mashing factor here for now. To be discussed. --- src/include/stir/ProjDataInfoCylindricalNoArcCorr.inl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/include/stir/ProjDataInfoCylindricalNoArcCorr.inl b/src/include/stir/ProjDataInfoCylindricalNoArcCorr.inl index 69f30f0522..b4ffa53c20 100644 --- a/src/include/stir/ProjDataInfoCylindricalNoArcCorr.inl +++ b/src/include/stir/ProjDataInfoCylindricalNoArcCorr.inl @@ -164,7 +164,7 @@ get_bin_for_det_pos_pair(Bin& bin, dp.pos2().axial_coord(), this->get_tof_mash_factor()==0 ? 0 // use timing_pos==0 in the nonTOF case - : dp.timing_pos()); + : stir::round((float)dp.timing_pos()/this->get_tof_mash_factor())); } void ProjDataInfoCylindricalNoArcCorr:: From 29dc9b45a53bdd0f311241c1cea830fc9f919b5d Mon Sep 17 00:00:00 2001 From: Elise Date: Thu, 5 Oct 2017 19:21:47 +0100 Subject: [PATCH 098/170] Fix for TOF sensitivity distributable computation Following change to using distributable computation for sensitivity calculation. This should allow the calculation of sensitivity for both TOF and non-TOF backprojectors. --- ...ogLikelihoodWithLinearModelForMeanAndProjData.cxx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx index 637fdf2a10..4f0277732b 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx @@ -793,8 +793,10 @@ add_subset_sensitivity(TargetT& sensitivity, const int subset_num) const this->normalisation_sptr, this->get_time_frame_definitions().get_start_time(this->get_time_frame_num()), this->get_time_frame_definitions().get_end_time(this->get_time_frame_num()), - this->caching_info_ptr - ); + this->caching_info_ptr, + use_tofsens ? sens_proj_data_sptr->get_min_tof_pos_num() : 0, + use_tofsens ? sens_proj_data_sptr->get_max_tof_pos_num() : 0); + std::transform(sensitivity.begin_all(), sensitivity.end_all(), sensitivity_this_subset_sptr->begin_all(), sensitivity.begin_all(), std::plus()); @@ -1052,7 +1054,8 @@ void distributable_sensitivity_computation( shared_ptr const& normalisation_sptr, const double start_time_of_frame, const double end_time_of_frame, - DistributedCachingInformation* caching_info_ptr + DistributedCachingInformation* caching_info_ptr, + int min_timing_pos_num, int max_timing_pos_num ) { @@ -1070,7 +1073,8 @@ void distributable_sensitivity_computation( start_time_of_frame, end_time_of_frame, &RPC_process_related_viewgrams_sensitivity_computation, - caching_info_ptr + caching_info_ptr, + min_timing_pos_num, max_timing_pos_num ); } From 995bbdbbb885147ff7acf96aa5a75bbf3993cc5e Mon Sep 17 00:00:00 2001 From: Elise Date: Thu, 5 Oct 2017 22:54:13 +0100 Subject: [PATCH 099/170] Modification of last commit --- .../PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx index 4f0277732b..abcf46680b 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx @@ -794,8 +794,8 @@ add_subset_sensitivity(TargetT& sensitivity, const int subset_num) const this->get_time_frame_definitions().get_start_time(this->get_time_frame_num()), this->get_time_frame_definitions().get_end_time(this->get_time_frame_num()), this->caching_info_ptr, - use_tofsens ? sens_proj_data_sptr->get_min_tof_pos_num() : 0, - use_tofsens ? sens_proj_data_sptr->get_max_tof_pos_num() : 0); + use_tofsens ? -this->max_timing_pos_num_to_process : 0, + use_tofsens ? this->max_timing_pos_num_to_process : 0); std::transform(sensitivity.begin_all(), sensitivity.end_all(), sensitivity_this_subset_sptr->begin_all(), sensitivity.begin_all(), From e23620719b45de056f9a15e15d39890335f0518f Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Tue, 10 Oct 2017 15:25:23 +0100 Subject: [PATCH 100/170] Display TOF sinograms simple patch on this --- src/utilities/display_projdata.cxx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/utilities/display_projdata.cxx b/src/utilities/display_projdata.cxx index f7f378f03f..09002a6b80 100644 --- a/src/utilities/display_projdata.cxx +++ b/src/utilities/display_projdata.cxx @@ -58,16 +58,19 @@ int main(int argc, char *argv[]) int segment_num = ask_num("Which segment number do you want to display", s3d->get_min_segment_num(), s3d->get_max_segment_num(), 0); + int tof_num = ask_num("Which timing pos number do you want to display", + s3d->get_min_tof_pos_num(), s3d->get_max_tof_pos_num(), 0); + if(ask_num("Display as SegmentByView (0) or BySinogram (1)?", 0,1,0)==0) { - SegmentByView segment= s3d->get_segment_by_view(segment_num); + SegmentByView segment= s3d->get_segment_by_view(segment_num, tof_num); const float maxi = ask_num("Maximum in color scale (default is actual max)",0.F,2*segment.find_max(),segment.find_max()); display(segment,maxi); } else { - SegmentBySinogram segment = s3d->get_segment_by_sinogram(segment_num); + SegmentBySinogram segment = s3d->get_segment_by_sinogram(segment_num, tof_num); const float maxi = ask_num("Maximum in color scale (default is actual max)",0.F,2*segment.find_max(),segment.find_max()); display(segment,maxi); From 11e84a74858f51809b5e8ae4d8cdd536470df9ad Mon Sep 17 00:00:00 2001 From: Elise Date: Wed, 11 Oct 2017 10:20:32 +0100 Subject: [PATCH 101/170] Allows the use of 4D sinograms with Swig --- src/buildblock/ProjData.cxx | 4 ++-- src/include/stir/ProjData.h | 8 ++++---- src/swig/stir.i | 36 ++++++++++++++++++++---------------- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/src/buildblock/ProjData.cxx b/src/buildblock/ProjData.cxx index 91ad1489c0..e6afdb8976 100644 --- a/src/buildblock/ProjData.cxx +++ b/src/buildblock/ProjData.cxx @@ -386,9 +386,9 @@ 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) { - 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 = 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); diff --git a/src/include/stir/ProjData.h b/src/include/stir/ProjData.h index 174f98fde7..5c5237173d 100644 --- a/src/include/stir/ProjData.h +++ b/src/include/stir/ProjData.h @@ -280,11 +280,11 @@ class ProjData : public ExamData std::size_t copy_to(iterT array_iter) const { iterT init_pos = array_iter; - for (int s=0; s<= this->get_max_segment_num(); ++s) + for (int k = this->get_proj_data_info_ptr()->get_min_tof_pos_num(); + k <= this->get_proj_data_info_ptr()->get_max_tof_pos_num(); + ++k) { - for (int k=this->get_proj_data_info_ptr()->get_min_tof_pos_num(); - k<=this->get_proj_data_info_ptr()->get_max_tof_pos_num(); - ++k) + for (int s = 0; s <= this->get_max_segment_num(); ++s) { SegmentBySinogram segment= this->get_segment_by_sinogram(s,k); std::copy(segment.begin_all_const(), segment.end_all_const(), array_iter); diff --git a/src/swig/stir.i b/src/swig/stir.i index 34b0ac662c..1cd34b9f77 100644 --- a/src/swig/stir.i +++ b/src/swig/stir.i @@ -63,6 +63,7 @@ #include "stir/CartesianCoordinate3D.h" #include "stir/IndexRange.h" #include "stir/IndexRange3D.h" +#include "stir/IndexRange4D.h" #include "stir/Array.h" #include "stir/DiscretisedDensity.h" #include "stir/DiscretisedDensityOnCartesianGrid.h" @@ -607,7 +608,7 @@ namespace std { #endif - static Array<3,float> create_array_for_proj_data(const ProjData& proj_data) + static Array<4,float> create_array_for_proj_data(const ProjData& proj_data) { // int num_sinos=proj_data.get_num_axial_poss(0); // for (int s=1; s<= proj_data.get_max_segment_num(); ++s) @@ -616,15 +617,15 @@ namespace std { // } int num_sinos = proj_data.get_num_sinograms(); - Array<3,float> array(IndexRange3D(num_sinos, proj_data.get_num_views(), proj_data.get_num_tangential_poss())); + Array<4,float> array(IndexRange4D(proj_data.get_num_tof_poss(),num_sinos, proj_data.get_num_views(), proj_data.get_num_tangential_poss())); return array; } - // a function for converting ProjData to a 3D array as that's what is easy to use - static Array<3,float> projdata_to_3D(const ProjData& proj_data) + // a function for converting ProjData to a 4D array as that's what is easy to use + static Array<4,float> projdata_to_4D(const ProjData& proj_data) { - Array<3,float> array = create_array_for_proj_data(proj_data); - Array<3,float>::full_iterator array_iter = array.begin_all(); + Array<4,float> array = create_array_for_proj_data(proj_data); + Array<4,float>::full_iterator array_iter = array.begin_all(); // for (int s=0; s<= proj_data.get_max_segment_num(); ++s) // { // SegmentBySinogram segment=proj_data.get_segment_by_sinogram(s); @@ -642,7 +643,7 @@ namespace std { } // inverse of the above function - void fill_proj_data_from_3D(ProjData& proj_data, const Array<3,float>& array) + void fill_proj_data_from_4D(ProjData& proj_data, const Array<4,float>& array) { // int num_sinos=proj_data.get_num_axial_poss(0); // for (int s=1; s<= proj_data.get_max_segment_num(); ++s) @@ -655,7 +656,7 @@ namespace std { // { // throw std::runtime_error("Incorrect size for filling this projection data"); // } - Array<3,float>::const_full_iterator array_iter = array.begin_all(); + Array<4,float>::const_full_iterator array_iter = array.begin_all(); // // for (int s=0; s<= proj_data.get_max_segment_num(); ++s) // { @@ -828,6 +829,7 @@ namespace std { //%shared_ptr(stir::Array<1,float>); %shared_ptr(stir::Array<2,float>); %shared_ptr(stir::Array<3,float>); +%shared_ptr(stir::Array<4,float>); %shared_ptr(stir::DiscretisedDensity<3,float>); %shared_ptr(stir::DiscretisedDensityOnCartesianGrid<3,float>); %shared_ptr(stir::VoxelsOnCartesianGrid); @@ -1109,6 +1111,7 @@ namespace stir { %template(IndexRange2D) IndexRange<2>; //%template(IndexRange2DVectorWithOffset) VectorWithOffset >; %template(IndexRange3D) IndexRange<3>; + %template(IndexRange4D) IndexRange<4>; %ADD_indexaccess(int,T,VectorWithOffset); %template(FloatVectorWithOffset) VectorWithOffset; @@ -1254,6 +1257,7 @@ namespace stir { // TODO name %template (FloatNumericVectorWithOffset3D) stir::NumericVectorWithOffset, float>; %template(FloatArray3D) stir::Array<3,float>; + %template(FloatArray4D) stir::Array<4,float>; #if 0 %ADD_indexaccess(int,%arg(stir::Array<2,float>),%arg(stir::Array<3,float>)); #endif @@ -1351,11 +1355,11 @@ namespace stir { %extend ProjData { #ifdef SWIGPYTHON - %feature("autodoc", "create a stir 3D Array from the projection data (internal)") to_array; + %feature("autodoc", "create a stir 4D Array from the projection data (internal)") to_array; %newobject to_array; - Array<3,float> to_array() + Array<4,float> to_array() { - Array<3,float> array = swigstir::projdata_to_3D(*$self); + Array<4,float> array = swigstir::projdata_to_4D(*$self); return array; } @@ -1364,9 +1368,9 @@ namespace stir { { if (PyIter_Check(arg)) { - Array<3,float> array = swigstir::create_array_for_proj_data(*$self); + Array<4,float> array = swigstir::create_array_for_proj_data(*$self); swigstir::fill_Array_from_Python_iterator(&array, arg); - swigstir::fill_proj_data_from_3D(*$self, array); + swigstir::fill_proj_data_from_4D(*$self, array); } else { @@ -1381,15 +1385,15 @@ namespace stir { %newobject to_matlab; mxArray * to_matlab() { - Array<3,float> array = swigstir::projdata_to_3D(*$self); + Array<4,float> array = swigstir::projdata_to_4D(*$self); return swigstir::Array_to_matlab(array); } void fill(const mxArray *pm) { - Array<3,float> array; + Array<4,float> array; swigstir::fill_Array_from_matlab(array, pm, true); - swigstir::fill_proj_data_from_3D(*$self, array); + swigstir::fill_proj_data_from_4D(*$self, array); } #endif } From 69651e54492f465e87c9fb459ca6509ac2ab6ec9 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Mon, 15 Oct 2018 15:52:49 +0100 Subject: [PATCH 102/170] Corrections of errors from merging --- ...putStreamFromROOTFileForCylindricalPET.cxx | 2 +- src/IO/InputStreamFromROOTFileForECATPET.cxx | 2 +- src/buildblock/ProjDataFromStream.cxx | 18 +++--- .../stir/listmode/CListModeDataECAT8_32bit.h | 2 - src/include/stir/listmode/CListModeDataROOT.h | 4 +- .../stir/listmode/CListModeDataSAFIR.h | 3 - ...orMeanAndListModeDataWithProjMatrixByBin.h | 11 ++-- .../CListModeDataECAT8_32bit.cxx | 8 --- src/listmode_buildblock/CListModeDataROOT.cxx | 49 +++------------- .../CListModeDataSAFIR.cxx | 9 --- ...MeanAndListModeDataWithProjMatrixByBin.cxx | 58 +++++++++---------- 11 files changed, 54 insertions(+), 112 deletions(-) diff --git a/src/IO/InputStreamFromROOTFileForCylindricalPET.cxx b/src/IO/InputStreamFromROOTFileForCylindricalPET.cxx index a4a60269c1..c8417af6ae 100644 --- a/src/IO/InputStreamFromROOTFileForCylindricalPET.cxx +++ b/src/IO/InputStreamFromROOTFileForCylindricalPET.cxx @@ -124,7 +124,7 @@ get_next_record(CListRecordROOT& record) record.init_from_data(ring1, ring2, crystal1, crystal2, time1, delta_timing_bin, - event1, event2); + eventID1, eventID2); } std::string diff --git a/src/IO/InputStreamFromROOTFileForECATPET.cxx b/src/IO/InputStreamFromROOTFileForECATPET.cxx index e9a218dbc8..6028fdc1ca 100644 --- a/src/IO/InputStreamFromROOTFileForECATPET.cxx +++ b/src/IO/InputStreamFromROOTFileForECATPET.cxx @@ -113,7 +113,7 @@ get_next_record(CListRecordROOT& record) record.init_from_data(ring1, ring2, crystal1, crystal2, time1, delta_timing_bin, - event1, event2); + eventID1, eventID2); } std::string diff --git a/src/buildblock/ProjDataFromStream.cxx b/src/buildblock/ProjDataFromStream.cxx index 3cf916a808..851e7a1a2e 100644 --- a/src/buildblock/ProjDataFromStream.cxx +++ b/src/buildblock/ProjDataFromStream.cxx @@ -542,7 +542,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() * @@ -606,11 +606,11 @@ ProjDataFromStream::get_offsets_bin(const Bin this_bin) const { // The timing offset will be added to the segment offset. This approach we minimise the // changes - if (!(timing_pos_num >= get_min_tof_pos_num() && - timing_pos_num <= get_max_tof_pos_num())) - error("ProjDataFromStream::get_offsets_bin: timing_num out of range : %d", timing_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_offsets_bin: timing_num out of range : %d", this_bin.timing_pos_num()); - const int timing_index = static_cast(FIND(timing_poss_sequence.begin(), timing_poss_sequence.end(), timing_pos_num) - + 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); @@ -618,21 +618,21 @@ ProjDataFromStream::get_offsets_bin(const Bin this_bin) const // Skip views const streamoff view_offset = - (view_num - get_min_view_num())* - get_num_axial_poss(segment_num) * + (this_bin.view_num() - get_min_view_num())* + get_num_axial_poss(this_bin.segment_num()) * get_num_tangential_poss()* on_disk_data_type.size_in_bytes(); // find axial pos const streamoff ax_pos_offset = - (ax_pos_num - get_min_axial_pos_num(segment_num)) * + (this_bin.axial_pos_num() - get_min_axial_pos_num(this_bin.segment_num())) * get_num_tangential_poss()* on_disk_data_type.size_in_bytes(); // find tang pos const streamoff tang_offset = - (tang_pos_num - get_min_tangential_pos_num()) * on_disk_data_type.size_in_bytes(); + (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; diff --git a/src/include/stir/listmode/CListModeDataECAT8_32bit.h b/src/include/stir/listmode/CListModeDataECAT8_32bit.h index 280e872298..97fc66ee7d 100644 --- a/src/include/stir/listmode/CListModeDataECAT8_32bit.h +++ b/src/include/stir/listmode/CListModeDataECAT8_32bit.h @@ -74,8 +74,6 @@ class CListModeDataECAT8_32bit : public CListModeData /*! \todo this might depend on the acquisition parameters */ virtual bool has_delayeds() const { return true; } - virtual - shared_ptr get_proj_data_info_sptr() const; private: typedef CListRecordECAT8_32bit CListRecordT; std::string listmode_filename; diff --git a/src/include/stir/listmode/CListModeDataROOT.h b/src/include/stir/listmode/CListModeDataROOT.h index 635534a373..f8a6926925 100644 --- a/src/include/stir/listmode/CListModeDataROOT.h +++ b/src/include/stir/listmode/CListModeDataROOT.h @@ -175,11 +175,13 @@ class CListModeDataROOT : public CListModeData float ring_spacing; //! Bin size, set in the hroot file (optional) float bin_size; -//@} int max_num_timing_bins; + float size_timing_bin; + float timing_resolution; +//@} int tof_mash_factor; diff --git a/src/include/stir/listmode/CListModeDataSAFIR.h b/src/include/stir/listmode/CListModeDataSAFIR.h index 0e79332540..3d2117ebc6 100644 --- a/src/include/stir/listmode/CListModeDataSAFIR.h +++ b/src/include/stir/listmode/CListModeDataSAFIR.h @@ -85,9 +85,6 @@ template class CListModeDataSAFIR : public CListModeData */ virtual bool has_delayeds() const { return false; } - virtual - shared_ptr get_proj_data_info_sptr() const; - private: std::string listmode_filename; mutable shared_ptr > current_lm_data_ptr; diff --git a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h index 272463805e..0fa1a6bb3a 100644 --- a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h +++ b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h @@ -35,12 +35,9 @@ #include "stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeData.h" #include "stir/recon_buildblock/ProjMatrixByBin.h" #include "stir/ProjDataInMemory.h" -#include "stir/recon_buildblock/ProjectorByBinPair.h" +#include "stir/recon_buildblock/ProjectorByBinPairUsingProjMatrixByBin.h" #include "stir/ExamInfo.h" -#include "stir/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.h" -#include "stir/recon_buildblock/BackProjectorByBinUsingProjMatrixByBin.h" -#include "stir/recon_buildblock/ProjMatrixByBinUsingRayTracing.h" -#include "stir/recon_buildblock/ProjectorByBinPairUsingSeparateProjectors.h" + START_NAMESPACE_STIR @@ -113,7 +110,7 @@ typedef RegisteredParsingObject PM_sptr; //! Stores the projectors that are used for the computations - shared_ptr projector_pair_ptr; + shared_ptr projector_pair_sptr; //! Backprojector used for sensitivity computation shared_ptr sens_backprojector_sptr; @@ -123,7 +120,7 @@ typedef RegisteredParsingObject proj_data_info_cyl_sptr; + shared_ptr proj_data_info_sptr; shared_ptr record_sptr; diff --git a/src/listmode_buildblock/CListModeDataECAT8_32bit.cxx b/src/listmode_buildblock/CListModeDataECAT8_32bit.cxx index ad25817442..4cfbc92350 100644 --- a/src/listmode_buildblock/CListModeDataECAT8_32bit.cxx +++ b/src/listmode_buildblock/CListModeDataECAT8_32bit.cxx @@ -141,13 +141,5 @@ set_get_position(const CListModeDataECAT8_32bit::SavedPosition& pos) current_lm_data_ptr->set_get_position(pos); } -shared_ptr -CListModeDataECAT8_32bit:: -get_proj_data_info_sptr() const -{ - assert(!is_null_ptr(proj_data_info_sptr)); - return proj_data_info_sptr; -} - } // namespace ecat END_NAMESPACE_STIR diff --git a/src/listmode_buildblock/CListModeDataROOT.cxx b/src/listmode_buildblock/CListModeDataROOT.cxx index c99a96cb63..4562660f0c 100644 --- a/src/listmode_buildblock/CListModeDataROOT.cxx +++ b/src/listmode_buildblock/CListModeDataROOT.cxx @@ -1,7 +1,7 @@ /* Copyright (C) 2015, 2016 University of Leeds Copyright (C) 2016, 2017 University College London - Copyright (C) 2018 University of Hull + Copyright (C) 2017, 2018 University of Hull This file is part of STIR. This file is free software; you can redistribute it and/or modify @@ -47,18 +47,6 @@ CListModeDataROOT(const std::string& hroot_filename) this->parser.add_start_key("ROOT header"); this->parser.add_stop_key("End ROOT header"); - // N.E.: Compression on ROOT listmode data is commented out until further testing is done. - // axial_compression = -1; - // maximum_ring_difference = -1; - // number_of_projections = -1; - // number_of_views = -1; - // number_of_segments = -1; - - max_num_timing_bins = -1; - size_timing_bin = -1.f; - timing_resolution = -1.f; - tof_mash_factor = 1; - // Scanner related & Physical dimensions. this->parser.add_key("originating system", &this->originating_system); @@ -71,14 +59,6 @@ CListModeDataROOT(const std::string& hroot_filename) this->parser.add_key("Maximum number of non-arc-corrected bins", &this->max_num_non_arccorrected_bins); // end Scanner and physical dimensions. - // Acquisition related - // N.E.: Compression on ROOT listmode data has been commented out until further testing is done. - // this->parser.add_key("%axial_compression", &axial_compression); - // this->parser.add_key("%maximum_ring_difference", &maximum_ring_difference); - // this->parser.add_key("%number_of_projections", &number_of_projections); - // this->parser.add_key("%number_of_views", &number_of_views); - // this->parser.add_key("%number_of_segments", &number_of_segments); - this->parser.add_key("number of TOF time bins", &this->max_num_timing_bins); this->parser.add_key("Size of timing bin (ps)", &this->size_timing_bin); this->parser.add_key("Timing resolution (ps)", &this->timing_resolution); @@ -163,8 +143,11 @@ CListModeDataROOT(const std::string& hroot_filename) /*num_transaxial_crystals_per_singles_unit_v*/ this->root_file_sptr->get_num_trans_crystals_per_singles_unit(), /*num_detector_layers_v*/ 1, + /* maximum number of timing bins */ max_num_timing_bins, + /* size of basic TOF bin */ size_timing_bin, + /* Scanner's timing resolution */ timing_resolution)); } @@ -179,31 +162,13 @@ CListModeDataROOT(const std::string& hroot_filename) this_scanner_sptr->get_num_rings()-1, this_scanner_sptr->get_num_detectors_per_ring()/2, this_scanner_sptr->get_max_num_non_arccorrected_bins(), - /* arc_correction*/false)); + /* arc_correction*/false, + tof_mash_factor)); this->set_proj_data_info_sptr(tmp); if (this->open_lm_file() == Succeeded::no) error("CListModeDataROOT: error opening ROOT file for filename '%s'", hroot_filename.c_str()); - - // N.E.: Compression on ROOT listmode data has been commented out until further testing is done. - // this->proj_data_info_sptr.reset(ProjDataInfo::ProjDataInfoCTI(this->scanner_sptr, - // std::max(axial_compression, 1), - // std::max(maximum_ring_difference, num_rings-1), - // std::max(number_of_views, num_detectors_per_ring/2), - // std::max(number_of_projections, max_num_non_arccorrected_bins), - // /* arc_correction*/false)); - - this->proj_data_info_sptr.reset(ProjDataInfo::ProjDataInfoCTI(this->scanner_sptr, - 1, - num_rings-1, - num_detectors_per_ring/2, - max_num_non_arccorrected_bins, - /* arc_correction*/false, - tof_mash_factor)); - -// if (tof_mash_factor != 1) - // error("TOF mashing factor for ROOT different from 1 not implemented yet."); } std::string @@ -217,7 +182,7 @@ shared_ptr CListModeDataROOT:: get_empty_record_sptr() const { - shared_ptr sptr(new CListRecordROOT(this->get_proj_data_info_sptr()->get_scanner_sptr())); + shared_ptr sptr(new CListRecordROOT(this->get_proj_data_info_sptr())); return sptr; } diff --git a/src/listmode_buildblock/CListModeDataSAFIR.cxx b/src/listmode_buildblock/CListModeDataSAFIR.cxx index 05365cb903..09de319fc1 100644 --- a/src/listmode_buildblock/CListModeDataSAFIR.cxx +++ b/src/listmode_buildblock/CListModeDataSAFIR.cxx @@ -119,15 +119,6 @@ open_lm_file() const ByteOrder::little_endian !=ByteOrder::get_native_order())); return Succeeded::yes; } - -template -shared_ptr -CListModeDataSAFIR:: -get_proj_data_info_sptr() const -{ - assert(!is_null_ptr(proj_data_info_sptr)); - return proj_data_info_sptr; -} template class CListModeDataSAFIR; diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx index d5dd0168cc..f1a900fe7b 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx @@ -135,26 +135,26 @@ actual_subsets_are_approximately_balanced(std::string& warning_message) const for (int subset_num=0; subset_numnum_subsets; ++subset_num) { - for (int timing_pos_num = proj_data_info_cyl_sptr->get_min_tof_pos_num(); - timing_pos_num <= proj_data_info_cyl_sptr->get_max_tof_pos_num(); + for (int timing_pos_num = proj_data_info_sptr->get_min_tof_pos_num(); + timing_pos_num <= proj_data_info_sptr->get_max_tof_pos_num(); ++timing_pos_num) { for (int segment_num = -this->max_ring_difference_num_to_process; segment_num <= this->max_ring_difference_num_to_process; ++segment_num) { - for (int axial_num = proj_data_info_cyl_sptr->get_min_axial_pos_num(segment_num); - axial_num < proj_data_info_cyl_sptr->get_max_axial_pos_num(segment_num); + for (int axial_num = proj_data_info_sptr->get_min_axial_pos_num(segment_num); + axial_num < proj_data_info_sptr->get_max_axial_pos_num(segment_num); axial_num ++) { // For debugging. // std::cout <get_min_tangential_pos_num(); - tang_num < proj_data_info_cyl_sptr->get_max_tangential_pos_num(); + for (int tang_num= proj_data_info_sptr->get_min_tangential_pos_num(); + tang_num < proj_data_info_sptr->get_max_tangential_pos_num(); tang_num ++ ) { - for(int view_num = proj_data_info_cyl_sptr->get_min_view_num() + subset_num; - view_num <= proj_data_info_cyl_sptr->get_max_view_num(); + for(int view_num = proj_data_info_sptr->get_min_view_num() + subset_num; + view_num <= proj_data_info_sptr->get_max_view_num(); view_num += this->num_subsets) { const Bin tmp_bin(segment_num, @@ -187,11 +187,11 @@ actual_subsets_are_approximately_balanced(std::string& warning_message) const << num_bins_in_subset << "\nEither reduce the number of symmetries used by the projector, or\n" "change the number of subsets. It usually should be a divisor of\n" - << proj_data_info_cyl_sptr->get_num_views() + << proj_data_info_sptr->get_num_views() << "/4 (or if that's not an integer, a divisor of " - << proj_data_info_cyl_sptr->get_num_views() + << proj_data_info_sptr->get_num_views() << "/2 or " - << proj_data_info_cyl_sptr->get_num_views() + << proj_data_info_sptr->get_num_views() << ").\n"; warning_message = str.str(); return false; @@ -213,7 +213,7 @@ set_up_before_sensitivity(shared_ptr const& target_sptr) // set projector to be used for the calculations this->PM_sptr->set_up(proj_data_info_sptr->create_shared_clone(),target_sptr); - this->PM_sptr->enable_tof(proj_data_info_cyl_sptr->create_shared_clone(), this->use_tof); + this->PM_sptr->enable_tof(proj_data_info_sptr->create_shared_clone(), this->use_tof); shared_ptr forward_projector_ptr(new ForwardProjectorByBinUsingProjMatrixByBin(this->PM_sptr)); shared_ptr back_projector_ptr(new BackProjectorByBinUsingProjMatrixByBin(this->PM_sptr)); @@ -223,9 +223,9 @@ set_up_before_sensitivity(shared_ptr const& target_sptr) this->projector_pair_sptr->set_up(proj_data_info_sptr->create_shared_clone(),target_sptr); // sets non-tof backprojector for sensitivity calculation (clone of the back_projector + set projdatainfo to non-tof) - this->sens_backprojector_sptr.reset(projector_pair_ptr->get_back_projector_sptr()->clone()); + this->sens_backprojector_sptr.reset(projector_pair_sptr->get_back_projector_sptr()->clone()); if (!this->use_tofsens) - this->sens_backprojector_sptr->set_up(proj_data_info_cyl_sptr->create_non_tof_clone(), target_sptr); + this->sens_backprojector_sptr->set_up(proj_data_info_sptr->create_non_tof_clone(), target_sptr); if (is_null_ptr(this->normalisation_sptr)) { @@ -394,15 +394,15 @@ void PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin:: add_view_seg_to_sensitivity(TargetT& sensitivity, const ViewSegmentNumbers& view_seg_nums) const { - int min_timing_pos_num = use_tofsens ? this->proj_data_info_cyl_sptr->get_min_tof_pos_num() : 0; - int max_timing_pos_num = use_tofsens ? this->proj_data_info_cyl_sptr->get_max_tof_pos_num() : 0; + int min_timing_pos_num = use_tofsens ? this->proj_data_info_sptr->get_min_tof_pos_num() : 0; + int max_timing_pos_num = use_tofsens ? this->proj_data_info_sptr->get_max_tof_pos_num() : 0; for (int timing_pos_num = min_timing_pos_num; timing_pos_num <= max_timing_pos_num; ++timing_pos_num) { shared_ptr symmetries_used - (this->projector_pair_ptr->get_symmetries_used()->clone()); + (this->projector_pair_sptr->get_symmetries_used()->clone()); RelatedViewgrams viewgrams = - proj_data_info_cyl_sptr->get_empty_related_viewgrams( + proj_data_info_sptr->get_empty_related_viewgrams( view_seg_nums, symmetries_used, false, timing_pos_num); viewgrams.fill(1.F); @@ -433,7 +433,7 @@ construct_target_ptr() const { return - new VoxelsOnCartesianGrid (*proj_data_info_cyl_sptr, + new VoxelsOnCartesianGrid (*proj_data_info_sptr, static_cast(this->zoom), CartesianCoordinate3D(static_cast(this->Zoffset), static_cast(this->Yoffset), @@ -476,7 +476,7 @@ compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, CListRecord& record = *record_sptr; long int more_events = - this->do_time_frame? 1 : (this->num_events_to_store / this->num_subsets); + this->do_time_frame? 1 : (this->num_events_to_use / this->num_subsets); while (more_events)//this->list_mode_data_sptr->get_next_record(record) == Succeeded::yes) { @@ -500,19 +500,19 @@ compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, { measured_bin.set_bin_value(1.0f); - record.event().get_bin(measured_bin, *proj_data_info_cyl_sptr); + record.event().get_bin(measured_bin, *proj_data_info_sptr); // In theory we have already done all these checks so we can // remove this if statement. if (measured_bin.get_bin_value() != 1.0f - || measured_bin.segment_num() < proj_data_info_cyl_sptr->get_min_segment_num() - || measured_bin.segment_num() > proj_data_info_cyl_sptr->get_max_segment_num() - || measured_bin.tangential_pos_num() < proj_data_info_cyl_sptr->get_min_tangential_pos_num() - || measured_bin.tangential_pos_num() > proj_data_info_cyl_sptr->get_max_tangential_pos_num() - || measured_bin.axial_pos_num() < proj_data_info_cyl_sptr->get_min_axial_pos_num(measured_bin.segment_num()) - || measured_bin.axial_pos_num() > proj_data_info_cyl_sptr->get_max_axial_pos_num(measured_bin.segment_num()) - || measured_bin.timing_pos_num() < proj_data_info_cyl_sptr->get_min_tof_pos_num() - || measured_bin.timing_pos_num() > proj_data_info_cyl_sptr->get_max_tof_pos_num()) + || measured_bin.segment_num() < proj_data_info_sptr->get_min_segment_num() + || measured_bin.segment_num() > proj_data_info_sptr->get_max_segment_num() + || measured_bin.tangential_pos_num() < proj_data_info_sptr->get_min_tangential_pos_num() + || measured_bin.tangential_pos_num() > proj_data_info_sptr->get_max_tangential_pos_num() + || measured_bin.axial_pos_num() < proj_data_info_sptr->get_min_axial_pos_num(measured_bin.segment_num()) + || measured_bin.axial_pos_num() > proj_data_info_sptr->get_max_axial_pos_num(measured_bin.segment_num()) + || measured_bin.timing_pos_num() < proj_data_info_sptr->get_min_tof_pos_num() + || measured_bin.timing_pos_num() > proj_data_info_sptr->get_max_tof_pos_num()) { continue; } From b26607d1a0cd1ad1f80abf3f06a07037cfb6ba13 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Wed, 17 Oct 2018 03:47:07 +0100 Subject: [PATCH 103/170] Significant speed-up on the application of the kernel Now the symmetries and the TOF kernel are applied with one loop --- src/include/stir/geometry/line_distances.h | 5 +- src/include/stir/listmode/CListModeDataROOT.h | 3 -- .../stir/recon_buildblock/ProjMatrixByBin.h | 6 +-- .../stir/recon_buildblock/ProjMatrixByBin.inl | 52 +++++++++---------- .../CListModeDataGESigna.cxx | 1 - 5 files changed, 31 insertions(+), 36 deletions(-) diff --git a/src/include/stir/geometry/line_distances.h b/src/include/stir/geometry/line_distances.h index 76331eb52f..0a89a103b4 100644 --- a/src/include/stir/geometry/line_distances.h +++ b/src/include/stir/geometry/line_distances.h @@ -178,14 +178,13 @@ project_point_on_a_line( CartesianCoordinate3D& r1 ) { - const CartesianCoordinate3D& r0 = p1; const CartesianCoordinate3D difference = p2 - p1; - const CartesianCoordinate3D r10 = r1-r0; + const CartesianCoordinate3D r10 = r1 - p1; float inner_prod = inner_product(difference, difference); - const coordT u = inner_product(r10,difference) / inner_prod ; + const float u = inner_product(r10, difference) / inner_prod ; r1.x() = p1.x() + u * difference.x(); r1.y() = p1.y() + u * difference.y(); diff --git a/src/include/stir/listmode/CListModeDataROOT.h b/src/include/stir/listmode/CListModeDataROOT.h index f8a6926925..f439d8a83c 100644 --- a/src/include/stir/listmode/CListModeDataROOT.h +++ b/src/include/stir/listmode/CListModeDataROOT.h @@ -140,9 +140,6 @@ class CListModeDataROOT : public CListModeData unsigned long int get_total_number_of_events() const ; - virtual - shared_ptr get_proj_data_info_sptr() const; - private: //! Check if the hroot contains a full scanner description Succeeded check_scanner_definition(std::string& ret); diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBin.h b/src/include/stir/recon_buildblock/ProjMatrixByBin.h index 6e0b4faa33..d25688cd77 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBin.h +++ b/src/include/stir/recon_buildblock/ProjMatrixByBin.h @@ -269,10 +269,10 @@ class ProjMatrixByBin : float r_sqrt2_gauss_sigma; //! The function which actually applies the TOF kernel on the LOR. - inline void apply_tof_kernel( ProjMatrixElemsForOneBin& nonTOF_probabilities, - ProjMatrixElemsForOneBin& tof_probabilities, + inline void apply_tof_kernel( ProjMatrixElemsForOneBin& tof_probabilities, const CartesianCoordinate3D& point1, - const CartesianCoordinate3D& point2) STIR_MUTABLE_CONST; + const CartesianCoordinate3D& point2, + const shared_ptr symm_ptr) STIR_MUTABLE_CONST; diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl index 4ad5875445..5606981e41 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl +++ b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl @@ -34,7 +34,7 @@ #include "stir/Succeeded.h" #include "stir/recon_buildblock/SymmetryOperation.h" #include "stir/geometry/line_distances.h" -#include "stir/numerics/erf.h" +//#include "stir/numerics/erf.h" START_NAMESPACE_STIR @@ -145,35 +145,32 @@ get_proj_matrix_elems_for_one_bin_with_tof( // set to empty probabilities.erase(); - ProjMatrixElemsForOneBin tmp_probabilities; if (cache_stores_only_basic_bins) { // find basic bin Bin basic_bin = bin; - unique_ptr symm_ptr = + shared_ptr symm_ptr = symmetries_sptr->find_symmetry_operation_from_basic_bin(basic_bin); - tmp_probabilities.set_bin(basic_bin); - probabilities.set_bin(bin); + probabilities.set_bin(basic_bin); // check if basic bin is in cache - if (get_cached_proj_matrix_elems_for_one_bin(tmp_probabilities) == + if (get_cached_proj_matrix_elems_for_one_bin(probabilities) == Succeeded::no) { // call 'calculate' just for the basic bin - calculate_proj_matrix_elems_for_one_bin(tmp_probabilities); + calculate_proj_matrix_elems_for_one_bin(probabilities); #ifndef NDEBUG - tmp_probabilities.check_state(); + probabilities.check_state(); #endif - cache_proj_matrix_elems_for_one_bin(tmp_probabilities); + cache_proj_matrix_elems_for_one_bin(probabilities); } -// else -// int nikos = 0; -// tmp_probabilities.set_bin(bin); // now transform to original bin - symm_ptr->transform_proj_matrix_elems_for_one_bin(tmp_probabilities); - apply_tof_kernel(tmp_probabilities, probabilities, point1, point2); + // NE: I moved this operation in the apply_tof_kernel. This should increase the speed + //symm_ptr->transform_proj_matrix_elems_for_one_bin(tmp_probabilities); + apply_tof_kernel(probabilities, point1, point2, + symm_ptr); } else // !cache_stores_only_basic_bins { @@ -183,10 +180,10 @@ get_proj_matrix_elems_for_one_bin_with_tof( } void -ProjMatrixByBin::apply_tof_kernel(ProjMatrixElemsForOneBin& nonTOF_probabilities, - ProjMatrixElemsForOneBin& tof_probabilities, +ProjMatrixByBin::apply_tof_kernel(ProjMatrixElemsForOneBin& tof_probabilities, const CartesianCoordinate3D& point1, - const CartesianCoordinate3D& point2) STIR_MUTABLE_CONST + const CartesianCoordinate3D& point2, + const shared_ptr symm_ptr) STIR_MUTABLE_CONST { CartesianCoordinate3D voxel_center; @@ -194,7 +191,7 @@ ProjMatrixByBin::apply_tof_kernel(ProjMatrixElemsForOneBin& nonTOF_probabilities float low_dist = 0.f; float high_dist = 0.f; - float lor_length = 1 / (0.5 * std::sqrt((point1.x() - point2.x()) *(point1.x() - point2.x()) + + float lor_length = 1.f / (0.5f * std::sqrt((point1.x() - point2.x()) *(point1.x() - point2.x()) + (point1.y() - point2.y()) *(point1.y() - point2.y()) + (point1.z() - point2.z()) *(point1.z() - point2.z()))); @@ -202,26 +199,29 @@ ProjMatrixByBin::apply_tof_kernel(ProjMatrixElemsForOneBin& nonTOF_probabilities const CartesianCoordinate3D middle = (point1 + point2)*0.5f; const CartesianCoordinate3D difference = point2 - middle; - for (ProjMatrixElemsForOneBin::iterator element_ptr = nonTOF_probabilities.begin(); - element_ptr != nonTOF_probabilities.end(); ++element_ptr) + for (ProjMatrixElemsForOneBin::iterator element_ptr = tof_probabilities.begin(); + element_ptr != tof_probabilities.end(); ++element_ptr) { + Coordinate3D c(element_ptr->get_coords()); + symm_ptr->transform_image_coordinates(c); + voxel_center = - image_info_sptr->get_physical_coordinates_for_indices (element_ptr->get_coords()); + image_info_sptr->get_physical_coordinates_for_indices (c); - project_point_on_a_line(point1, point2, voxel_center ); + project_point_on_a_line(point1, point2, voxel_center); CartesianCoordinate3D x = voxel_center - middle; - float d1 = - inner_product(x, difference) * lor_length; + float d1 = inner_product(x, difference) * lor_length; - low_dist = (proj_data_info_sptr->tof_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].low_lim -d1) * r_sqrt2_gauss_sigma; - high_dist = (proj_data_info_sptr->tof_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].high_lim -d1) * r_sqrt2_gauss_sigma; + low_dist = (proj_data_info_sptr->tof_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].low_lim + d1) * r_sqrt2_gauss_sigma; + high_dist = (proj_data_info_sptr->tof_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].high_lim + d1) * r_sqrt2_gauss_sigma; get_tof_value(low_dist, high_dist, new_value); new_value *= element_ptr->get_value(); - tof_probabilities.push_back(ProjMatrixElemsForOneBin::value_type(element_ptr->get_coords(), new_value)); + *element_ptr = ProjMatrixElemsForOneBin::value_type(c, new_value); } } diff --git a/src/listmode_buildblock/CListModeDataGESigna.cxx b/src/listmode_buildblock/CListModeDataGESigna.cxx index 36c8ce6a8e..eaf5c28e3a 100644 --- a/src/listmode_buildblock/CListModeDataGESigna.cxx +++ b/src/listmode_buildblock/CListModeDataGESigna.cxx @@ -95,7 +95,6 @@ dataset2.read(read_str_manufacturer,vlst); std::cout << "\n Manufacturer : " << read_str_manufacturer << "\n\n"; #endif - CListModeData::scanner_sptr = GEHDF5Data::scanner_sptr; this->proj_data_info_sptr.reset( ProjDataInfo::ProjDataInfoCTI(GEHDF5Data::scanner_sptr, From 84e963fa2bb0471c56a62591034f26ade97bcb8d Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Wed, 17 Oct 2018 18:41:56 +0100 Subject: [PATCH 104/170] Further speed up, by optimisation and cache * Avoid recacalculation of erf by caching. * Optimisation on geometric calculations. --- src/include/stir/geometry/line_distances.h | 23 +++++++ .../stir/recon_buildblock/ProjMatrixByBin.h | 4 +- .../stir/recon_buildblock/ProjMatrixByBin.inl | 65 ++++++++++++++----- src/recon_buildblock/ProjMatrixByBin.cxx | 11 ++++ 4 files changed, 85 insertions(+), 18 deletions(-) diff --git a/src/include/stir/geometry/line_distances.h b/src/include/stir/geometry/line_distances.h index 0a89a103b4..b54d686fe0 100644 --- a/src/include/stir/geometry/line_distances.h +++ b/src/include/stir/geometry/line_distances.h @@ -192,4 +192,27 @@ project_point_on_a_line( } +template +inline void +project_point_on_a_line2( + const CartesianCoordinate3D& p1, + const CartesianCoordinate3D& p2, + CartesianCoordinate3D& r1, + bool& sign) +{ + + const CartesianCoordinate3D difference = p2 - p1; + + const CartesianCoordinate3D r10 = r1 - p1; + + const float u = inner_product(r10, difference) / + inner_product(difference, difference); + + r1[3] = u * difference[3]; + r1[2] = u * difference[2]; + r1[1] = u * difference[1]; + + sign = u > 0 ? true : false; +} + END_NAMESPACE_STIR diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBin.h b/src/include/stir/recon_buildblock/ProjMatrixByBin.h index d25688cd77..5177673f79 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBin.h +++ b/src/include/stir/recon_buildblock/ProjMatrixByBin.h @@ -268,6 +268,8 @@ class ProjMatrixByBin : //! 1/(2*sigma_in_mm) float r_sqrt2_gauss_sigma; + Array<1, float> cache_erf; + //! The function which actually applies the TOF kernel on the LOR. inline void apply_tof_kernel( ProjMatrixElemsForOneBin& tof_probabilities, const CartesianCoordinate3D& point1, @@ -277,7 +279,7 @@ class ProjMatrixByBin : //! Get the interal value erf(m - v_j) - erf(m -v_j) - inline void get_tof_value(float& d1, float& d2, float& val) const; + inline void get_tof_value(const float& d1, const float& d2, float& val) const; }; diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl index 5606981e41..fc2ac314ec 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl +++ b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl @@ -191,13 +191,16 @@ ProjMatrixByBin::apply_tof_kernel(ProjMatrixElemsForOneBin& tof_probabilities, float low_dist = 0.f; float high_dist = 0.f; - float lor_length = 1.f / (0.5f * std::sqrt((point1.x() - point2.x()) *(point1.x() - point2.x()) + - (point1.y() - point2.y()) *(point1.y() - point2.y()) + - (point1.z() - point2.z()) *(point1.z() - point2.z()))); + bool neg_sgn; + float d1; + float step = 100000.f / 8.f; // THe direction can be from 1 -> 2 depending on the bin sign. const CartesianCoordinate3D middle = (point1 + point2)*0.5f; - const CartesianCoordinate3D difference = point2 - middle; + // const CartesianCoordinate3D difference = point2 - middle; + // float lor_length = 2.f / (std::sqrt((point1.x() - point2.x()) *(point1.x() - point2.x()) + + // (point1.y() - point2.y()) *(point1.y() - point2.y()) + + // (point1.z() - point2.z()) *(point1.z() - point2.z()))); for (ProjMatrixElemsForOneBin::iterator element_ptr = tof_probabilities.begin(); element_ptr != tof_probabilities.end(); ++element_ptr) @@ -208,29 +211,57 @@ ProjMatrixByBin::apply_tof_kernel(ProjMatrixElemsForOneBin& tof_probabilities, voxel_center = image_info_sptr->get_physical_coordinates_for_indices (c); + /* + * Original method: + * project_point_on_a_line(point1, point2, voxel_center); - CartesianCoordinate3D x = voxel_center - middle; - - float d1 = inner_product(x, difference) * lor_length; - - low_dist = (proj_data_info_sptr->tof_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].low_lim + d1) * r_sqrt2_gauss_sigma; - high_dist = (proj_data_info_sptr->tof_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].high_lim + d1) * r_sqrt2_gauss_sigma; - - get_tof_value(low_dist, high_dist, new_value); - + const CartesianCoordinate3D x = voxel_center - middle; + + const float d1 = inner_product(x, difference) * lor_length; + */ + + // The following is the optimisation of the previous: + { + project_point_on_a_line2(middle, point2, voxel_center, + neg_sgn); + + if(neg_sgn) + d1 = - sqrt(voxel_center.x()*voxel_center.x() + + voxel_center.y()*voxel_center.y() + + voxel_center.z()*voxel_center.z() ); + else + d1 = sqrt(voxel_center.x()*voxel_center.x() + + voxel_center.y()*voxel_center.y() + + voxel_center.z()*voxel_center.z() ); + } + + low_dist = ((proj_data_info_sptr->tof_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].low_lim - d1) * r_sqrt2_gauss_sigma) + 4.f; + high_dist =((proj_data_info_sptr->tof_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].high_lim - d1) * r_sqrt2_gauss_sigma) + 4.f; + + int p1 = low_dist * step; + int p2 = high_dist * step; + + if (p1 < 0 || p2 < 0 || + p1 > 100000 || p2 > 100000) + { + *element_ptr = ProjMatrixElemsForOneBin::value_type(c, 0.0); + continue; + } + + //get_tof_value(low_dist, high_dist, new_value); + new_value = cache_erf[p2] - cache_erf[p1]; new_value *= element_ptr->get_value(); - *element_ptr = ProjMatrixElemsForOneBin::value_type(c, new_value); - } } void ProjMatrixByBin:: -get_tof_value(float& d1, float& d2, float& val) const +//get_tof_value(const float& d1, const float& d2, float& val) const +get_tof_value(const float& d1, const float& d2, float& val) const { - val = ( erf(d2) - erf(d1)) * 0.5f; + val = 0.5f * (erf(d2) - erf(d1)); } END_NAMESPACE_STIR diff --git a/src/recon_buildblock/ProjMatrixByBin.cxx b/src/recon_buildblock/ProjMatrixByBin.cxx index f666d48af1..06cdb4f857 100644 --- a/src/recon_buildblock/ProjMatrixByBin.cxx +++ b/src/recon_buildblock/ProjMatrixByBin.cxx @@ -85,6 +85,17 @@ enable_tof(const shared_ptr& _proj_data_info_sptr, const bool v) proj_data_info_sptr = _proj_data_info_sptr; gauss_sigma_in_mm = ProjDataInfo::tof_delta_time_to_mm(proj_data_info_sptr->get_scanner_ptr()->get_timing_resolution()) / 2.355f; r_sqrt2_gauss_sigma = 1.0f/ (gauss_sigma_in_mm * static_cast(sqrt(2.0))); + + cache_erf.resize(0, 100000); + + float step = 8.f / 100000.f; + // cache erf with reasonable sampling + for (int i = 0 ;i < 100000; ++i) + { + float d = -4.0f + i * step; + cache_erf[i] = 0.5f * erf(d); + int nikos = 0; + } } } From 711b96681fd33ca1e3010cd73cbf20c7c3b38d43 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Wed, 17 Oct 2018 22:53:28 +0100 Subject: [PATCH 105/170] Further optimisation and speed up * Apply_tof_kernel() uses only one inner product in the loop. * Temporarily reduced the number of samples for the erf to 1000. Discussion needed on this. --- .../stir/recon_buildblock/ProjMatrixByBin.inl | 35 +++++++++++-------- src/recon_buildblock/ProjMatrixByBin.cxx | 7 ++-- src/recon_test/CMakeLists.txt | 24 ++++++------- 3 files changed, 35 insertions(+), 31 deletions(-) diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl index fc2ac314ec..0180d3e35c 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl +++ b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl @@ -188,16 +188,17 @@ ProjMatrixByBin::apply_tof_kernel(ProjMatrixElemsForOneBin& tof_probabilities, CartesianCoordinate3D voxel_center; float new_value = 0.f; - float low_dist = 0.f; - float high_dist = 0.f; + //float low_dist = 0.f; + //float high_dist = 0.f; - bool neg_sgn; float d1; - float step = 100000.f / 8.f; + float step = 1000.f / 8.f; + int p1, p2; // THe direction can be from 1 -> 2 depending on the bin sign. const CartesianCoordinate3D middle = (point1 + point2)*0.5f; - // const CartesianCoordinate3D difference = point2 - middle; + const CartesianCoordinate3D difference = point2 - middle; + const float denom = 1.f / inner_product(difference, difference); // float lor_length = 2.f / (std::sqrt((point1.x() - point2.x()) *(point1.x() - point2.x()) + // (point1.y() - point2.y()) *(point1.y() - point2.y()) + // (point1.z() - point2.z()) *(point1.z() - point2.z()))); @@ -223,10 +224,15 @@ ProjMatrixByBin::apply_tof_kernel(ProjMatrixElemsForOneBin& tof_probabilities, // The following is the optimisation of the previous: { - project_point_on_a_line2(middle, point2, voxel_center, - neg_sgn); + const CartesianCoordinate3D r10 = voxel_center - middle; - if(neg_sgn) + const float u = inner_product(r10, difference) * denom; + + voxel_center[3] = u * difference[3]; + voxel_center[2] = u * difference[2]; + voxel_center[1] = u * difference[1]; + + if(u < 0.f) d1 = - sqrt(voxel_center.x()*voxel_center.x() + voxel_center.y()*voxel_center.y() + voxel_center.z()*voxel_center.z() ); @@ -236,22 +242,21 @@ ProjMatrixByBin::apply_tof_kernel(ProjMatrixElemsForOneBin& tof_probabilities, voxel_center.z()*voxel_center.z() ); } - low_dist = ((proj_data_info_sptr->tof_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].low_lim - d1) * r_sqrt2_gauss_sigma) + 4.f; - high_dist =((proj_data_info_sptr->tof_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].high_lim - d1) * r_sqrt2_gauss_sigma) + 4.f; + //low_dist = ((proj_data_info_sptr->tof_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].low_lim - d1) * r_sqrt2_gauss_sigma) + 4.f; + //high_dist = ((proj_data_info_sptr->tof_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].high_lim - d1) * r_sqrt2_gauss_sigma) + 4.f; - int p1 = low_dist * step; - int p2 = high_dist * step; + p1 = (((proj_data_info_sptr->tof_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].low_lim - d1) * r_sqrt2_gauss_sigma) + 4.f) * step; + p2 = (((proj_data_info_sptr->tof_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].high_lim - d1) * r_sqrt2_gauss_sigma) + 4.f) * step; if (p1 < 0 || p2 < 0 || - p1 > 100000 || p2 > 100000) + p1 > 1000 || p2 > 1000) { *element_ptr = ProjMatrixElemsForOneBin::value_type(c, 0.0); continue; } //get_tof_value(low_dist, high_dist, new_value); - new_value = cache_erf[p2] - cache_erf[p1]; - new_value *= element_ptr->get_value(); + new_value = (cache_erf[p2] - cache_erf[p1]) * element_ptr->get_value(); *element_ptr = ProjMatrixElemsForOneBin::value_type(c, new_value); } } diff --git a/src/recon_buildblock/ProjMatrixByBin.cxx b/src/recon_buildblock/ProjMatrixByBin.cxx index 06cdb4f857..3e2cc9130b 100644 --- a/src/recon_buildblock/ProjMatrixByBin.cxx +++ b/src/recon_buildblock/ProjMatrixByBin.cxx @@ -86,15 +86,14 @@ enable_tof(const shared_ptr& _proj_data_info_sptr, const bool v) gauss_sigma_in_mm = ProjDataInfo::tof_delta_time_to_mm(proj_data_info_sptr->get_scanner_ptr()->get_timing_resolution()) / 2.355f; r_sqrt2_gauss_sigma = 1.0f/ (gauss_sigma_in_mm * static_cast(sqrt(2.0))); - cache_erf.resize(0, 100000); + cache_erf.resize(0, 1000); - float step = 8.f / 100000.f; + float step = 8.f / 1000.f; // cache erf with reasonable sampling - for (int i = 0 ;i < 100000; ++i) + for (int i = 0 ;i < 1000; ++i) { float d = -4.0f + i * step; cache_erf[i] = 0.5f * erf(d); - int nikos = 0; } } } diff --git a/src/recon_test/CMakeLists.txt b/src/recon_test/CMakeLists.txt index 497921ef48..5c5bffb406 100644 --- a/src/recon_test/CMakeLists.txt +++ b/src/recon_test/CMakeLists.txt @@ -33,18 +33,18 @@ set(${dir_INVOLVED_TEST_EXE_SOURCES} recontest ) -if (HAVE_CERN_ROOT) - list(APPEND ${dir_INVOLVED_TEST_EXE_SOURCES} test_consistency_root) - foreach(n RANGE 1 12 1) - set(test_name test_consistency_root_${n}) - ADD_TEST(NAME ${test_name} - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/root_files_generation - COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test_consistency_root - ${CMAKE_CURRENT_SOURCE_DIR}/root_files_generation/root_header_test${n}.hroot - ${CMAKE_CURRENT_SOURCE_DIR}/root_files_generation/stir_image${n}.hv - ) - endforeach() -endif() +#if (HAVE_CERN_ROOT) +# list(APPEND ${dir_INVOLVED_TEST_EXE_SOURCES} test_consistency_root) +# foreach(n RANGE 1 12 1) +# set(test_name test_consistency_root_${n}) +# ADD_TEST(NAME ${test_name} +# WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/root_files_generation +# COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test_consistency_root +# ${CMAKE_CURRENT_SOURCE_DIR}/root_files_generation/root_header_test${n}.hroot +# ${CMAKE_CURRENT_SOURCE_DIR}/root_files_generation/stir_image${n}.hv +# ) +# endforeach() +#endif() include(stir_test_exe_targets) From 34dedf2f026a4efd94d4a5baf99fea779a97c1da Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Wed, 17 Oct 2018 23:19:12 +0100 Subject: [PATCH 106/170] Don't pass the symm operations in the apply_tof_kernel() --- src/include/stir/recon_buildblock/ProjMatrixByBin.h | 7 +++---- src/include/stir/recon_buildblock/ProjMatrixByBin.inl | 11 ++++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBin.h b/src/include/stir/recon_buildblock/ProjMatrixByBin.h index 5177673f79..79c1145c7f 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBin.h +++ b/src/include/stir/recon_buildblock/ProjMatrixByBin.h @@ -271,10 +271,9 @@ class ProjMatrixByBin : Array<1, float> cache_erf; //! The function which actually applies the TOF kernel on the LOR. - inline void apply_tof_kernel( ProjMatrixElemsForOneBin& tof_probabilities, - const CartesianCoordinate3D& point1, - const CartesianCoordinate3D& point2, - const shared_ptr symm_ptr) STIR_MUTABLE_CONST; + inline void apply_tof_kernel(ProjMatrixElemsForOneBin& tof_probabilities, + const CartesianCoordinate3D& point1, + const CartesianCoordinate3D& point2) STIR_MUTABLE_CONST; diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl index 0180d3e35c..198770a1b5 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl +++ b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl @@ -150,7 +150,7 @@ get_proj_matrix_elems_for_one_bin_with_tof( { // find basic bin Bin basic_bin = bin; - shared_ptr symm_ptr = + unique_ptr symm_ptr = symmetries_sptr->find_symmetry_operation_from_basic_bin(basic_bin); probabilities.set_bin(basic_bin); @@ -169,8 +169,7 @@ get_proj_matrix_elems_for_one_bin_with_tof( // now transform to original bin // NE: I moved this operation in the apply_tof_kernel. This should increase the speed //symm_ptr->transform_proj_matrix_elems_for_one_bin(tmp_probabilities); - apply_tof_kernel(probabilities, point1, point2, - symm_ptr); + apply_tof_kernel(probabilities, point1, point2); } else // !cache_stores_only_basic_bins { @@ -182,10 +181,12 @@ get_proj_matrix_elems_for_one_bin_with_tof( void ProjMatrixByBin::apply_tof_kernel(ProjMatrixElemsForOneBin& tof_probabilities, const CartesianCoordinate3D& point1, - const CartesianCoordinate3D& point2, - const shared_ptr symm_ptr) STIR_MUTABLE_CONST + const CartesianCoordinate3D& point2) STIR_MUTABLE_CONST { + unique_ptr symm_ptr = + symmetries_sptr->find_symmetry_operation_from_basic_bin(basic_bin); + CartesianCoordinate3D voxel_center; float new_value = 0.f; //float low_dist = 0.f; From f9835391f12c944c8ebba4c471077db76d5fa6bf Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Thu, 18 Oct 2018 01:16:53 +0100 Subject: [PATCH 107/170] Close github issues * closes #6 merge non-tof and tof-specific code * closes #7 make ProjMatrixByBin::enable_tof private * closes #9 rename Scanner::get_num_max_of_timing_bins --- src/IO/InterfileHeader.cxx | 8 +- src/buildblock/ProjDataInfo.cxx | 10 +- src/buildblock/Scanner.cxx | 48 ++-- src/include/stir/ProjDataInfo.inl | 6 +- src/include/stir/Scanner.h | 57 ++--- src/include/stir/Scanner.inl | 20 +- .../stir/listmode/CListRecordGESigna.h | 2 +- src/include/stir/listmode/LmToProjData.h | 8 +- .../recon_buildblock/ForwardProjectorByBin.h | 18 -- ...orwardProjectorByBinUsingProjMatrixByBin.h | 2 - .../stir/recon_buildblock/ProjMatrixByBin.h | 33 +-- .../stir/recon_buildblock/ProjMatrixByBin.inl | 107 +++----- .../recon_buildblock/ProjectorByBinPair.h | 9 - src/listmode_buildblock/LmToProjData.cxx | 242 ------------------ .../ForwardProjectorByBin.cxx | 17 -- ...wardProjectorByBinUsingProjMatrixByBin.cxx | 16 +- ...MeanAndListModeDataWithProjMatrixByBin.cxx | 5 +- src/recon_buildblock/ProjectorByBinPair.cxx | 169 ------------ src/test/test_time_of_flight.cxx | 25 +- 19 files changed, 125 insertions(+), 677 deletions(-) diff --git a/src/IO/InterfileHeader.cxx b/src/IO/InterfileHeader.cxx index fc0d80e05f..3d75991781 100644 --- a/src/IO/InterfileHeader.cxx +++ b/src/IO/InterfileHeader.cxx @@ -1257,16 +1257,16 @@ bool InterfilePDFSHeader::post_processing() if (guessed_scanner_ptr->is_tof_ready()) { - if (max_num_timing_poss != guessed_scanner_ptr->get_num_max_of_timing_bins()) + if (max_num_timing_poss != guessed_scanner_ptr->get_num_max_of_timing_poss()) { warning("Interfile warning: 'Number of TOF time bins' (%d) is expected to be %d.", - max_num_timing_poss, guessed_scanner_ptr->get_num_max_of_timing_bins()); + max_num_timing_poss, guessed_scanner_ptr->get_num_max_of_timing_poss()); mismatch_between_header_and_guess = true; } - if (size_of_timing_pos != guessed_scanner_ptr->get_size_of_timing_bin()) + 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_bin()); + 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()) diff --git a/src/buildblock/ProjDataInfo.cxx b/src/buildblock/ProjDataInfo.cxx index 04bcc5fdc2..f2b838d42f 100644 --- a/src/buildblock/ProjDataInfo.cxx +++ b/src/buildblock/ProjDataInfo.cxx @@ -193,15 +193,15 @@ ProjDataInfo::set_tof_mash_factor(const int new_num) { if (scanner_ptr->is_tof_ready() && new_num > 0 ) { - if(tof_mash_factor < 0 || tof_mash_factor > scanner_ptr->get_num_max_of_timing_bins()) + if(tof_mash_factor < 0 || tof_mash_factor > scanner_ptr->get_num_max_of_timing_poss()) error("ProjDataInfo: TOF mashing factor must be positive and smaller or equal than" "the scanner's number of max timing bins. Abort."); tof_mash_factor = new_num; - tof_increament_in_mm = tof_delta_time_to_mm(tof_mash_factor * scanner_ptr->get_size_of_timing_bin()); + tof_increament_in_mm = tof_delta_time_to_mm(tof_mash_factor * scanner_ptr->get_size_of_timing_pos()); - min_tof_pos_num = - (scanner_ptr->get_num_max_of_timing_bins() / tof_mash_factor)/2; - max_tof_pos_num = min_tof_pos_num + (scanner_ptr->get_num_max_of_timing_bins() / tof_mash_factor) -1; + min_tof_pos_num = - (scanner_ptr->get_num_max_of_timing_poss() / tof_mash_factor)/2; + max_tof_pos_num = min_tof_pos_num + (scanner_ptr->get_num_max_of_timing_poss() / tof_mash_factor) -1; num_tof_bins = max_tof_pos_num - min_tof_pos_num +1 ; @@ -649,7 +649,7 @@ ProjDataInfo* ProjDataInfo::ask_parameters() const int tof_mash_factor = scanner_ptr->is_tof_ready() ? ask_num("Time-of-flight mash factor:", 0, - scanner_ptr->get_num_max_of_timing_bins(), 25) : 0; + scanner_ptr->get_num_max_of_timing_poss(), 25) : 0; const bool arc_corrected = ask("Is the data arc-corrected?",true); diff --git a/src/buildblock/Scanner.cxx b/src/buildblock/Scanner.cxx index c77355f621..4344c973f6 100644 --- a/src/buildblock/Scanner.cxx +++ b/src/buildblock/Scanner.cxx @@ -544,8 +544,8 @@ Scanner::Scanner(Type type_v, const list& list_of_names_v, int num_axial_crystals_per_singles_unit_v, int num_transaxial_crystals_per_singles_unit_v, int num_detector_layers_v, - short int max_num_of_timing_bins_v, - float size_timing_bin_v, + short int max_num_of_timing_poss_v, + float size_timing_pos_v, float timing_resolution_v) { set_params(type_v, list_of_names_v, num_rings_v, @@ -560,8 +560,8 @@ Scanner::Scanner(Type type_v, const list& list_of_names_v, num_axial_crystals_per_singles_unit_v, num_transaxial_crystals_per_singles_unit_v, num_detector_layers_v, - max_num_of_timing_bins_v, - size_timing_bin_v, + max_num_of_timing_poss_v, + size_timing_pos_v, timing_resolution_v); } @@ -633,8 +633,8 @@ Scanner::Scanner(Type type_v, const string& name, int num_axial_crystals_per_singles_unit_v, int num_transaxial_crystals_per_singles_unit_v, int num_detector_layers_v, - short int max_num_of_timing_bins_v, - float size_timing_bin_v, + short int max_num_of_timing_poss_v, + float size_timing_pos_v, float timing_resolution_v ) { @@ -650,8 +650,8 @@ Scanner::Scanner(Type type_v, const string& name, num_axial_crystals_per_singles_unit_v, num_transaxial_crystals_per_singles_unit_v, num_detector_layers_v, - max_num_of_timing_bins_v, - size_timing_bin_v, + max_num_of_timing_poss_v, + size_timing_pos_v, timing_resolution_v); } @@ -734,8 +734,8 @@ set_params(Type type_v,const list& list_of_names_v, int num_axial_crystals_per_singles_unit_v, int num_transaxial_crystals_per_singles_unit_v, int num_detector_layers_v, - short int max_num_of_timing_bins_v, - float size_timing_bin_v, + short int max_num_of_timing_poss_v, + float size_timing_pos_v, float timing_resolution_v) { set_params(type_v, list_of_names_v, num_rings_v, @@ -750,8 +750,8 @@ set_params(Type type_v,const list& list_of_names_v, num_axial_crystals_per_singles_unit_v, num_transaxial_crystals_per_singles_unit_v, num_detector_layers_v, - max_num_of_timing_bins_v, - size_timing_bin_v, + max_num_of_timing_poss_v, + size_timing_pos_v, timing_resolution_v); } @@ -793,8 +793,8 @@ set_params(Type type_v,const list& list_of_names_v, energy_resolution = -1.f; reference_energy = -1.f; - max_num_of_timing_bins = -1; - size_timing_bin = -1.f; + max_num_of_timing_poss = -1; + size_timing_pos = -1.f; timing_resolution = -1.f; } @@ -839,8 +839,8 @@ set_params(Type type_v,const list& list_of_names_v, energy_resolution = energy_resolution_v; reference_energy = reference_energy_v; - max_num_of_timing_bins = -1; - size_timing_bin = -1.f; + max_num_of_timing_poss = -1; + size_timing_pos = -1.f; timing_resolution = -1.f; } @@ -862,8 +862,8 @@ set_params(Type type_v,const list& list_of_names_v, int num_axial_crystals_per_singles_unit_v, int num_transaxial_crystals_per_singles_unit_v, int num_detector_layers_v, - short int max_num_of_timing_bins_v, - float size_timing_bin_v, + short int max_num_of_timing_poss_v, + float size_timing_pos_v, float timing_resolution_v) { type = type_v; @@ -885,8 +885,8 @@ set_params(Type type_v,const list& list_of_names_v, num_transaxial_crystals_per_singles_unit = num_transaxial_crystals_per_singles_unit_v; num_detector_layers = num_detector_layers_v; - max_num_of_timing_bins = max_num_of_timing_bins_v; - size_timing_bin = size_timing_bin_v; + max_num_of_timing_poss = max_num_of_timing_poss_v; + size_timing_pos = size_timing_pos_v; timing_resolution = timing_resolution_v; energy_resolution = -1.f; reference_energy = -1.f; @@ -1065,8 +1065,8 @@ if (!close_enough(energy_resolution, scanner.energy_resolution) && (num_detector_layers == scanner.num_detector_layers) && (num_axial_crystals_per_singles_unit == scanner.num_axial_crystals_per_singles_unit) && (num_transaxial_crystals_per_singles_unit == scanner.num_transaxial_crystals_per_singles_unit) && - (max_num_of_timing_bins == scanner.max_num_of_timing_bins) && - close_enough(size_timing_bin, scanner.size_timing_bin) && + (max_num_of_timing_poss == scanner.max_num_of_timing_poss) && + close_enough(size_timing_pos, scanner.size_timing_pos) && close_enough(timing_resolution, scanner.timing_resolution); } @@ -1120,8 +1120,8 @@ Scanner::parameter_info() const if (is_tof_ready()) { - s << "Number of TOF time bins :=" << get_num_max_of_timing_bins() << "\n"; - s << "Size of timing bin (ps) :=" << get_size_of_timing_bin() << "\n"; + s << "Number of TOF time bins :=" << get_num_max_of_timing_poss() << "\n"; + s << "Size of timing bin (ps) :=" << get_size_of_timing_pos() << "\n"; s << "Timing resolution (ps) :=" << get_timing_resolution() << "\n"; } diff --git a/src/include/stir/ProjDataInfo.inl b/src/include/stir/ProjDataInfo.inl index ce12b02d9d..fa4cf80266 100644 --- a/src/include/stir/ProjDataInfo.inl +++ b/src/include/stir/ProjDataInfo.inl @@ -155,9 +155,9 @@ ProjDataInfo::get_max_tof_pos_num() const float ProjDataInfo::get_coincidence_window_in_pico_sec() const { - return scanner_ptr->is_tof_ready()? (scanner_ptr->get_num_max_of_timing_bins() * - scanner_ptr->get_size_of_timing_bin()) - :(scanner_ptr->get_size_of_timing_bin()); + return scanner_ptr->is_tof_ready()? (scanner_ptr->get_num_max_of_timing_poss() * + scanner_ptr->get_size_of_timing_pos()) + :(scanner_ptr->get_size_of_timing_pos()); } float diff --git a/src/include/stir/Scanner.h b/src/include/stir/Scanner.h index 3095f894e8..e502989ff7 100644 --- a/src/include/stir/Scanner.h +++ b/src/include/stir/Scanner.h @@ -165,8 +165,8 @@ class Scanner int num_axial_crystals_per_singles_unit_v, int num_transaxial_crystals_per_singles_unit_v, int num_detector_layers_v, - short int max_num_of_timing_bins, - float size_timing_bin, + short int max_num_of_timing_poss, + float size_timing_pos, float timing_resolution); //! constructor ( a single name) @@ -213,8 +213,8 @@ class Scanner int num_axial_crystals_per_singles_unit_v, int num_transaxial_crystals_per_singles_unit_v, int num_detector_layers_v, - short int max_num_of_timing_bins, - float size_timing_bin, + short int max_num_of_timing_poss, + float size_timing_pos, float timing_resolution); @@ -325,9 +325,9 @@ class Scanner /* inline int get_num_layers_singles_units() const; */ inline int get_num_singles_units() const; //! Get the maximum number of TOF bins. - inline int get_num_max_of_timing_bins() const; + inline int get_num_max_of_timing_poss() const; //! Get the delta t which correspnds to the max number of TOF bins in picosecs. - inline float get_size_of_timing_bin() const; + inline float get_size_of_timing_pos() const; //! Get the timing resolution of the scanner. inline float get_timing_resolution() const; @@ -395,9 +395,9 @@ class Scanner //! A negative value indicates, unknown || not set inline void set_reference_energy(const float new_num); //! Set the maximum number of TOF bins. - inline void set_num_max_of_timing_bins(int new_num); + inline void set_num_max_of_timing_poss(int new_num); //! Set the delta t which correspnds to the max number of TOF bins. - inline void set_size_of_timing_bin(float new_num); + inline void set_size_of_timing_poss(float new_num); //! Set timing resolution inline void set_timing_resolution(float new_num_in_ps); //@} (end of set info) @@ -443,38 +443,19 @@ class Scanner int num_axial_crystals_per_singles_unit; int num_transaxial_crystals_per_singles_unit; - //! - //! \brief energy_resolution - //! \author Nikos Efthimiou - //! \details This is the energy resolution of the system. + //! This is the energy resolution of the system. //! A negative value indicates, unknown. //! This value is dominated by the material of the scintilation crystal float energy_resolution; - - //! - //! \brief reference_energy - //! \author Nikos Efthimiou - //! \details In PET application this should always be 511 keV. + //! In PET application this should always be 511 keV. //! A negative value indicates, unknown. float reference_energy; - - //! - //! \brief timing_resolution - //! \author Nikos Efthimiou - //! \details The timing resolution of the scanner, in psec. + //! The timing resolution of the scanner, in psec. float timing_resolution; - - //! - //! \brief num_tof_bins - //! \author Nikos Efthimiou - //! \details The number of TOF bins. Without any mash factors - int max_num_of_timing_bins; - - //! - //! \brief size_timing_bin - //! \author Nikos Efthimiou - //! \details This number corresponds the the least significant clock digit. - float size_timing_bin; + //! The number of TOF bins. Without any mash factors + int max_num_of_timing_poss; + //! This number corresponds the the least significant clock digit. + float size_timing_pos; //! set all parameters, case where default_num_arccorrected_bins==max_num_non_arccorrected_bins @@ -522,8 +503,8 @@ class Scanner int num_axial_crystals_per_singles_unit_v, int num_transaxial_crystals_per_singles_unit_v, int num_detector_layers_v, - short int max_num_of_timing_bins_v, - float size_timing_bin_v, + short int max_num_of_timing_poss_v, + float size_timing_pos_v, float timing_resolution_v); // ! set all parameters @@ -574,8 +555,8 @@ class Scanner int num_axial_crystals_per_singles_unit_v, int num_transaxial_crystals_per_singles_unit_v, int num_detector_layers_v, - short int max_num_of_timing_bins_v, - float size_timing_bin_v, + short int max_num_of_timing_poss_v, + float size_timing_pos_v, float timing_resolution_v); diff --git a/src/include/stir/Scanner.inl b/src/include/stir/Scanner.inl index 4dcb149cba..bc5480ea25 100644 --- a/src/include/stir/Scanner.inl +++ b/src/include/stir/Scanner.inl @@ -229,14 +229,14 @@ Scanner::get_reference_energy() const return reference_energy; } -int Scanner::get_num_max_of_timing_bins() const +int Scanner::get_num_max_of_timing_poss() const { - return max_num_of_timing_bins; + return max_num_of_timing_poss; } -float Scanner::get_size_of_timing_bin() const +float Scanner::get_size_of_timing_pos() const { - return size_timing_bin; + return size_timing_pos; } float Scanner::get_timing_resolution() const @@ -246,8 +246,8 @@ float Scanner::get_timing_resolution() const bool Scanner::is_tof_ready() const { - return (max_num_of_timing_bins > 0 - && size_timing_bin > 0.0f + return (max_num_of_timing_poss > 0 + && size_timing_pos > 0.0f && timing_resolution > 0.0f); } @@ -356,14 +356,14 @@ Scanner::set_reference_energy(const float new_num) reference_energy = new_num; } -void Scanner::set_num_max_of_timing_bins(const int new_num) +void Scanner::set_num_max_of_timing_poss(const int new_num) { - max_num_of_timing_bins = new_num; + max_num_of_timing_poss = new_num; } -void Scanner::set_size_of_timing_bin(const float new_num) +void Scanner::set_size_of_timing_poss(const float new_num) { - size_timing_bin = new_num; + size_timing_pos = new_num; } void Scanner::set_timing_resolution(const float new_num_in_ps) diff --git a/src/include/stir/listmode/CListRecordGESigna.h b/src/include/stir/listmode/CListRecordGESigna.h index 146282e447..d3c7a8f82b 100644 --- a/src/include/stir/listmode/CListRecordGESigna.h +++ b/src/include/stir/listmode/CListRecordGESigna.h @@ -334,7 +334,7 @@ dynamic_cast(&e2) != 0 && if (this->is_event()) { // set TOF info in ps - this->delta_time = this->event_data.get_tof_bin() *this-> get_scanner_ptr()->get_size_of_timing_bin(); + this->delta_time = this->event_data.get_tof_bin() *this-> get_scanner_ptr()->get_size_of_timing_pos(); } diff --git a/src/include/stir/listmode/LmToProjData.h b/src/include/stir/listmode/LmToProjData.h index 6387168b11..6f2fd44791 100644 --- a/src/include/stir/listmode/LmToProjData.h +++ b/src/include/stir/listmode/LmToProjData.h @@ -188,11 +188,6 @@ class LmToProjData : public ParsingObject void run_tof_test_function(); protected: - - //! This function does the non-TOF work - virtual void actual_process_data_without_tof(); - //! This function does the TOF work - virtual void actual_process_data_with_tof(); //! will be called when a new time frame starts /*! The frame numbers start from 1. */ @@ -239,8 +234,7 @@ class LmToProjData : public ParsingObject bool do_pre_normalisation; bool store_prompts; bool store_delayeds; - //! Use TOF information - bool use_tof; + int num_segments_in_memory; int num_timing_poss_in_memory; long int num_events_to_store; diff --git a/src/include/stir/recon_buildblock/ForwardProjectorByBin.h b/src/include/stir/recon_buildblock/ForwardProjectorByBin.h index 9c2b877eb5..aaa6c739e0 100644 --- a/src/include/stir/recon_buildblock/ForwardProjectorByBin.h +++ b/src/include/stir/recon_buildblock/ForwardProjectorByBin.h @@ -114,17 +114,6 @@ virtual void set_up( void forward_project(Bin&, const DiscretisedDensity<3,float>&); - //! This is a really ungly way to sort the extra bit of information which are needed for - //! the TOF reconstruction. In this base class we are going to store a pointer the the current - //! ProjMatrixElements row and the two detection points. Which the are going to be accessed by the - //! child class in case that tof_enabled. - //! The common ProjMatrixElems row will provide a 'bridge' between forward and back projection as, - //! we'll be able to keep it and not calculate it again. - void set_tof_data(const CartesianCoordinate3D*, - const CartesianCoordinate3D*); - - ProjMatrixElemsForOneBin* get_tof_row() const; - virtual ~ForwardProjectorByBin(); protected: @@ -138,13 +127,6 @@ virtual void set_up( virtual void actual_forward_project(Bin&, const DiscretisedDensity<3,float>&) = 0; - //! True if TOF has been activated. - bool tof_enabled; - - shared_ptr tof_probabilities; - const CartesianCoordinate3D* point1; - const CartesianCoordinate3D* point2; - //! check if the argument is the same as what was used for set_up() /*! calls error() if anything is wrong. diff --git a/src/include/stir/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.h b/src/include/stir/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.h index 2dd75e7b7d..28ed510e4d 100644 --- a/src/include/stir/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.h +++ b/src/include/stir/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.h @@ -79,8 +79,6 @@ class ForwardProjectorByBinUsingProjMatrixByBin: const DataSymmetriesForViewSegmentNumbers * get_symmetries_used() const; - void enable_tof(const shared_ptr& _proj_data_info_sptr, const bool v); - private: shared_ptr proj_matrix_ptr; diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBin.h b/src/include/stir/recon_buildblock/ProjMatrixByBin.h index 79c1145c7f..7eefb4460c 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBin.h +++ b/src/include/stir/recon_buildblock/ProjMatrixByBin.h @@ -123,25 +123,15 @@ class ProjMatrixByBin : The implementation is inline as it just gets it in terms of the cached_proj_matrix_elems_for_one_bin or - calculate_proj_matrix_elems_for_one_bin.*/ + calculate_proj_matrix_elems_for_one_bin. + + N.E: Updated to accomondate TOF information. +*/ inline void get_proj_matrix_elems_for_one_bin( ProjMatrixElemsForOneBin&, const Bin&) STIR_MUTABLE_CONST; - //! Returns a LOR with elements after application of the TOF - //! kernel. The central_point of the LOR is needed in order to - //! correlate the physical position of the LOR elements with the - //! timing bin dimentions which have as reference the center of the LOR. - //! \warning Currently, first it calculates a non-TOF LOR and then - //! kernel is applied. Which is slow. - inline void - get_proj_matrix_elems_for_one_bin_with_tof( - ProjMatrixElemsForOneBin&, - const Bin&, - const CartesianCoordinate3D& point1, - const CartesianCoordinate3D& point2) STIR_MUTABLE_CONST; - #if 0 // TODO /*! \brief Facility to write the 'independent' part of the matrix to file. @@ -171,11 +161,6 @@ class ProjMatrixByBin : // void reserve_num_elements_in_cache(const std::size_t); //! Remove all elements from the cache void clear_cache() STIR_MUTABLE_CONST; - - //! Activates the application of the timing kernel to the LOR - //! and performs initial set_up(). - //! \warning Must be called after set_up() - void enable_tof(const shared_ptr& proj_data_info_sptr,const bool v = true); protected: shared_ptr symmetries_sptr; @@ -263,6 +248,11 @@ class ProjMatrixByBin : // KT 15/05/2002 not static anymore as it uses cache_stores_only_basic_bins CacheKey cache_key(const Bin& bin) const; + //! Activates the application of the timing kernel to the LOR + //! and performs initial set_up(). + //! \warning Must be called after set_up() + void enable_tof(const shared_ptr& proj_data_info_sptr,const bool v = true); + //! A local copy of the scanner's time resolution in mm. float gauss_sigma_in_mm; //! 1/(2*sigma_in_mm) @@ -271,9 +261,10 @@ class ProjMatrixByBin : Array<1, float> cache_erf; //! The function which actually applies the TOF kernel on the LOR. - inline void apply_tof_kernel(ProjMatrixElemsForOneBin& tof_probabilities, + inline void apply_tof_kernel_and_symm_transformation(ProjMatrixElemsForOneBin& tof_probabilities, const CartesianCoordinate3D& point1, - const CartesianCoordinate3D& point2) STIR_MUTABLE_CONST; + const CartesianCoordinate3D& point2, + const unique_ptr& symm_ptr) STIR_MUTABLE_CONST; diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl index 198770a1b5..eb0c3ed304 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl +++ b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl @@ -57,24 +57,10 @@ get_proj_matrix_elems_for_one_bin( const Bin& bin) STIR_MUTABLE_CONST { // start_timers(); TODO, can't do this in a const member - + // set to empty probabilities.erase(); - if (proj_data_info_sptr && (proj_data_info_sptr->is_tof_data() || - this->tof_enabled)) - { - LORInAxialAndNoArcCorrSinogramCoordinates lor; - proj_data_info_sptr->get_LOR(lor, bin); - LORAs2Points lor2(lor); - this->get_proj_matrix_elems_for_one_bin_with_tof( - probabilities, - bin, - lor2.p1(), - lor2.p2()) ; - return; - } - if (cache_stores_only_basic_bins) { // find basic bin @@ -94,9 +80,21 @@ get_proj_matrix_elems_for_one_bin( #endif cache_proj_matrix_elems_for_one_bin(probabilities); } - - // now transform to original bin - symm_ptr->transform_proj_matrix_elems_for_one_bin(probabilities); + if ( proj_data_info_sptr->is_tof_data() && + this->tof_enabled) + { + LORInAxialAndNoArcCorrSinogramCoordinates lor; + proj_data_info_sptr->get_LOR(lor, bin); + LORAs2Points lor2(lor); + + // now apply TOF kernel and transform to original bin + apply_tof_kernel_and_symm_transformation(probabilities, lor2.p1(), lor2.p2(), symm_ptr); + } + else + { + // now transform to original bin + symm_ptr->transform_proj_matrix_elems_for_one_bin(probabilities); + } } else // !cache_stores_only_basic_bins { @@ -122,71 +120,34 @@ get_proj_matrix_elems_for_one_bin( #endif cache_proj_matrix_elems_for_one_bin(probabilities); } - symm_ptr->transform_proj_matrix_elems_for_one_bin(probabilities); + if ( proj_data_info_sptr->is_tof_data() && + this->tof_enabled) + { + LORInAxialAndNoArcCorrSinogramCoordinates lor; + proj_data_info_sptr->get_LOR(lor, bin); + LORAs2Points lor2(lor); + + // now apply TOF kernel and transform to original bin + apply_tof_kernel_and_symm_transformation(probabilities, lor2.p1(), lor2.p2(), symm_ptr); + } + else + { + // now transform to original bin + symm_ptr->transform_proj_matrix_elems_for_one_bin(probabilities); + } cache_proj_matrix_elems_for_one_bin(probabilities); } } // stop_timers(); TODO, can't do this in a const member } -inline void -ProjMatrixByBin:: -get_proj_matrix_elems_for_one_bin_with_tof( - ProjMatrixElemsForOneBin& probabilities, - const Bin& bin, - const CartesianCoordinate3D& point1, - const CartesianCoordinate3D& point2) STIR_MUTABLE_CONST -{ - // start_timers(); TODO, can't do this in a const member - - if (!tof_enabled) - error("The function get_proj_matrix_elems_for_one_bin_with_tof() needs proper timing " - "initialisation. Abort."); - - // set to empty - probabilities.erase(); - - if (cache_stores_only_basic_bins) - { - // find basic bin - Bin basic_bin = bin; - unique_ptr symm_ptr = - symmetries_sptr->find_symmetry_operation_from_basic_bin(basic_bin); - - probabilities.set_bin(basic_bin); - // check if basic bin is in cache - if (get_cached_proj_matrix_elems_for_one_bin(probabilities) == - Succeeded::no) - { - // call 'calculate' just for the basic bin - calculate_proj_matrix_elems_for_one_bin(probabilities); -#ifndef NDEBUG - probabilities.check_state(); -#endif - cache_proj_matrix_elems_for_one_bin(probabilities); - } - - // now transform to original bin - // NE: I moved this operation in the apply_tof_kernel. This should increase the speed - //symm_ptr->transform_proj_matrix_elems_for_one_bin(tmp_probabilities); - apply_tof_kernel(probabilities, point1, point2); - } - else // !cache_stores_only_basic_bins - { - error("This option has been deactivated as the amount of memory required is not realistic. Abort."); - } - // stop_timers(); TODO, can't do this in a const member -} - void -ProjMatrixByBin::apply_tof_kernel(ProjMatrixElemsForOneBin& tof_probabilities, +ProjMatrixByBin::apply_tof_kernel_and_symm_transformation(ProjMatrixElemsForOneBin& tof_probabilities, const CartesianCoordinate3D& point1, - const CartesianCoordinate3D& point2) STIR_MUTABLE_CONST + const CartesianCoordinate3D& point2, + const unique_ptr& symm_ptr) STIR_MUTABLE_CONST { - unique_ptr symm_ptr = - symmetries_sptr->find_symmetry_operation_from_basic_bin(basic_bin); - CartesianCoordinate3D voxel_center; float new_value = 0.f; //float low_dist = 0.f; diff --git a/src/include/stir/recon_buildblock/ProjectorByBinPair.h b/src/include/stir/recon_buildblock/ProjectorByBinPair.h index 42cabbc705..e9d04bac6b 100644 --- a/src/include/stir/recon_buildblock/ProjectorByBinPair.h +++ b/src/include/stir/recon_buildblock/ProjectorByBinPair.h @@ -84,15 +84,6 @@ public ParsingObject const shared_ptr get_back_projector_sptr() const; - ProjMatrixElemsForOneBin* get_current_tof_row() const; - - void enable_tof(const shared_ptr& _proj_data_info_sptr, const bool v); - - void - set_tof_data(const CartesianCoordinate3D* _point1, - const CartesianCoordinate3D* _point2); - - //! Provide access to the (minimal) symmetries used by the projectors /*! It is expected that the forward and back projector can handle the same symmetries. diff --git a/src/listmode_buildblock/LmToProjData.cxx b/src/listmode_buildblock/LmToProjData.cxx index f3c30d62bd..59eda4e8ea 100644 --- a/src/listmode_buildblock/LmToProjData.cxx +++ b/src/listmode_buildblock/LmToProjData.cxx @@ -163,7 +163,6 @@ set_defaults() post_normalisation_ptr.reset(new TrivialBinNormalisation); do_pre_normalisation =0; num_events_to_store = 0L; - use_tof = false; do_time_frame = false; } @@ -527,247 +526,6 @@ process_data() { assert(!is_null_ptr(template_proj_data_info_ptr)); - if (template_proj_data_info_ptr->is_tof_data()) - { - use_tof = true; - actual_process_data_with_tof(); - } - else - { - use_tof = false; - actual_process_data_without_tof(); - } -} - - -void -LmToProjData:: -actual_process_data_without_tof() -{ - CPUTimer timer; - timer.start(); - - // assume list mode data starts at time 0 - // we have to do this because the first time tag might occur only after a - // few coincidence events (as happens with ECAT scanners) - current_time = 0; - - double time_of_last_stored_event = 0; - long num_stored_events = 0; - VectorWithOffset > - segments (0,0); - for (int timing_pos_num=segments.get_min_index(); timing_pos_num<=segments.get_max_index(); ++timing_pos_num) - { - segments[timing_pos_num].resize(template_proj_data_info_ptr->get_min_segment_num(), - template_proj_data_info_ptr->get_max_segment_num()); - } - - VectorWithOffset - frame_start_positions(1, static_cast(frame_defs.get_num_frames())); - shared_ptr record_sptr = lm_data_ptr->get_empty_record_sptr(); - CListRecord& record = *record_sptr; - - if (!record.event().is_valid_template(*template_proj_data_info_ptr)) - error("The scanner template is not valid for LmToProjData. This might be because of unsupported arc correction."); - - - /* Here starts the main loop which will store the listmode data. */ - for (current_frame_num = 1; - current_frame_num<=frame_defs.get_num_frames(); - ++current_frame_num) - { - start_new_time_frame(current_frame_num); - - // construct ExamInfo appropriate for a single projdata with this time frame - ExamInfo this_frame_exam_info(*lm_data_ptr->get_exam_info_ptr()); - { - TimeFrameDefinitions this_time_frame_defs(frame_defs, current_frame_num); - this_frame_exam_info.set_time_frame_definitions(this_time_frame_defs); - } - - // *********** open output file - shared_ptr output; - shared_ptr proj_data_ptr; - - { - char rest[50]; - sprintf(rest, "_f%dg1d0b0", current_frame_num); - const string output_filename = output_filename_prefix + rest; - - proj_data_ptr = - construct_proj_data(output, output_filename, this_frame_exam_info, template_proj_data_info_ptr); - } - - long num_prompts_in_frame = 0; - long num_delayeds_in_frame = 0; - - const double start_time = frame_defs.get_start_time(current_frame_num); - const double end_time = frame_defs.get_end_time(current_frame_num); - - /* - For each start_segment_index, we check which events occur in the - segments between start_segment_index and - start_segment_index+num_segments_in_memory. - */ - for (int start_segment_index = proj_data_ptr->get_min_segment_num(); - start_segment_index <= proj_data_ptr->get_max_segment_num(); - start_segment_index += num_segments_in_memory) - { - const int end_segment_index = - min( proj_data_ptr->get_max_segment_num()+1, start_segment_index + num_segments_in_memory) - 1; - - if (!interactive) - allocate_segments(segments, 0,0,start_segment_index, end_segment_index, proj_data_ptr->get_proj_data_info_ptr()); - - // the next variable is used to see if there are more events to store for the current segments - // num_events_to_store-more_events will be the number of allowed coincidence events currently seen in the file - // ('allowed' independent on the fact of we have its segment in memory or not) - // When do_time_frame=true, the number of events is irrelevant, so we - // just set more_events to 1, and never change it - long more_events = - do_time_frame? 1 : num_events_to_store; - - if (start_segment_index != proj_data_ptr->get_min_segment_num()) - { - // we're going once more through the data (for the next batch of segments) - cerr << "\nProcessing next batch of segments\n"; - // go to the beginning of the listmode data for this frame - lm_data_ptr->set_get_position(frame_start_positions[current_frame_num]); - current_time = start_time; - } - else - { - cerr << "\nProcessing time frame " << current_frame_num << '\n'; - - // Note: we already have current_time from previous frame, so don't - // need to set it. In fact, setting it to start_time would be wrong - // as we first might have to skip some events before we get to start_time. - // So, let's do that now. - while (current_time < start_time && - lm_data_ptr->get_next_record(record) == Succeeded::yes) - { - if (record.is_time()) - current_time = record.time().get_time_in_secs(); - } - // now save position such that we can go back - frame_start_positions[current_frame_num] = - lm_data_ptr->save_get_position(); - } - { - // loop over all events in the listmode file - while (more_events) - { - if (lm_data_ptr->get_next_record(record) == Succeeded::no) - { - // no more events in file for some reason - break; //get out of while loop - } - if (record.is_time() && end_time > 0.01) // Direct comparison within doubles is unsafe. - { - current_time = record.time().get_time_in_secs(); - if (do_time_frame && current_time >= end_time) - break; // get out of while loop - assert(current_time>=start_time); - process_new_time_event(record.time()); - } - // note: could do "else if" here if we would be sure that - // a record can never be both timing and coincidence event - // and there might be a scanner around that has them both combined. - if (record.is_event()) - { - assert(start_time <= current_time); - Bin bin; - // set value in case the event decoder doesn't touch it - // otherwise it would be 0 and all events will be ignored - bin.set_bin_value(1); - get_bin_from_event(bin, record.event()); - - // check if it's inside the range we want to store - if (bin.get_bin_value()>0 - && bin.tangential_pos_num()>= proj_data_ptr->get_min_tangential_pos_num() - && bin.tangential_pos_num()<= proj_data_ptr->get_max_tangential_pos_num() - && bin.axial_pos_num()>=proj_data_ptr->get_min_axial_pos_num(bin.segment_num()) - && bin.axial_pos_num()<=proj_data_ptr->get_max_axial_pos_num(bin.segment_num()) - ) - { - assert(bin.view_num()>=proj_data_ptr->get_min_view_num()); - assert(bin.view_num()<=proj_data_ptr->get_max_view_num()); - - // see if we increment or decrement the value in the sinogram - const int event_increment = - record.event().is_prompt() - ? ( store_prompts ? 1 : 0 ) // it's a prompt - : delayed_increment;//it is a delayed-coincidence event - - if (event_increment==0) - continue; - - if (!do_time_frame) - more_events -= event_increment; - - // now check if we have its segment in memory - if (bin.segment_num() >= start_segment_index && bin.segment_num()<=end_segment_index) - { - do_post_normalisation(bin); - - num_stored_events += event_increment; - if (record.event().is_prompt()) - ++num_prompts_in_frame; - else - ++num_delayeds_in_frame; - - if (num_stored_events%500000L==0) cout << "\r" << num_stored_events << " events stored" << flush; - - if (interactive) - printf("Seg %4d view %4d ax_pos %4d tang_pos %4d time %8g stored with incr %d \n", - bin.segment_num(), bin.view_num(), bin.axial_pos_num(), bin.tangential_pos_num(), - current_time, event_increment); - else - (*segments[0][bin.segment_num()])[bin.view_num()][bin.axial_pos_num()][bin.tangential_pos_num()] += - bin.get_bin_value() * - event_increment; - } - } - else // event is rejected for some reason - { - if (interactive) - printf("Seg %4d view %4d ax_pos %4d tang_pos %4d time %8g ignored\n", - bin.segment_num(), bin.view_num(), bin.axial_pos_num(), bin.tangential_pos_num(), current_time); - } - } // end of spatial event processing - } // end of while loop over all events - - time_of_last_stored_event = - max(time_of_last_stored_event,current_time); - } - - if (!interactive) - save_and_delete_segments(output, segments, - 0,0, start_segment_index, end_segment_index, - *proj_data_ptr); - } // end of for loop for segment range - cerr << "\nNumber of prompts stored in this time period : " << num_prompts_in_frame - << "\nNumber of delayeds stored in this time period: " << num_delayeds_in_frame - << '\n'; - } // end of loop over frames - - timer.stop(); - - cerr << "Last stored event was recorded before time-tick at " << time_of_last_stored_event << " secs\n"; - if (!do_time_frame && - (num_stored_events<=0 || - /*static_cast*/(num_stored_events)* _point1, - const CartesianCoordinate3D* _point2) -{ - point1 = _point1; - point2 = _point2; -} - -ProjMatrixElemsForOneBin* -ForwardProjectorByBin:: -get_tof_row() const -{ - return tof_probabilities.get(); -} - END_NAMESPACE_STIR diff --git a/src/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.cxx b/src/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.cxx index 53a0806dc4..71604b6e98 100644 --- a/src/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.cxx +++ b/src/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.cxx @@ -239,29 +239,15 @@ ForwardProjectorByBinUsingProjMatrixByBin:: const DiscretisedDensity<3, float> &density) { - if (proj_matrix_ptr->is_cache_enabled() && !tof_enabled) + if (proj_matrix_ptr->is_cache_enabled()) { ProjMatrixElemsForOneBin proj_matrix_row; proj_matrix_ptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, this_bin); proj_matrix_row.forward_project(this_bin,density); } - else if (proj_matrix_ptr->is_cache_enabled() && tof_enabled) - { - proj_matrix_ptr->get_proj_matrix_elems_for_one_bin_with_tof(*tof_probabilities, this_bin, *point1, *point2); - tof_probabilities->forward_project(this_bin,density); - } else error("ForwardProjectorByBinUsingProjMatrixByBin: Symmetries should be handled by ProjMatrix. Abort. "); } -void -ForwardProjectorByBinUsingProjMatrixByBin:: -enable_tof(const shared_ptr& _proj_data_info_sptr, const bool v) -{ - proj_matrix_ptr->enable_tof(_proj_data_info_sptr, v); - tof_enabled = v; - tof_probabilities.reset(new ProjMatrixElemsForOneBin()); -} - END_NAMESPACE_STIR diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx index f1a900fe7b..c096621df6 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx @@ -213,7 +213,7 @@ set_up_before_sensitivity(shared_ptr const& target_sptr) // set projector to be used for the calculations this->PM_sptr->set_up(proj_data_info_sptr->create_shared_clone(),target_sptr); - this->PM_sptr->enable_tof(proj_data_info_sptr->create_shared_clone(), this->use_tof); + //this->PM_sptr->enable_tof(proj_data_info_sptr->create_shared_clone(), this->use_tof); shared_ptr forward_projector_ptr(new ForwardProjectorByBinUsingProjMatrixByBin(this->PM_sptr)); shared_ptr back_projector_ptr(new BackProjectorByBinUsingProjMatrixByBin(this->PM_sptr)); @@ -533,11 +533,8 @@ compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, } } - if(this->use_tof) this->PM_sptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, measured_bin); - else - this->PM_sptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, measured_bin); //in_the_range++; fwd_bin.set_bin_value(0.0f); diff --git a/src/recon_buildblock/ProjectorByBinPair.cxx b/src/recon_buildblock/ProjectorByBinPair.cxx index 5575adee14..10fc37ac8f 100644 --- a/src/recon_buildblock/ProjectorByBinPair.cxx +++ b/src/recon_buildblock/ProjectorByBinPair.cxx @@ -62,175 +62,6 @@ set_up(const shared_ptr& proj_data_info_sptr, return Succeeded::yes; } -void -ProjectorByBinPair:: -enable_tof(const shared_ptr& _proj_data_info_sptr, const bool v) -{ - // Check if it is PresmoothingForwardProjectorByBin - PresmoothingForwardProjectorByBin* forward= - dynamic_cast (forward_projector_sptr.get()); - - if (!is_null_ptr(forward)) - { - ForwardProjectorByBinUsingProjMatrixByBin* original_forward = - dynamic_cast (forward->get_original_forward_projector_ptr()); - - if (is_null_ptr(original_forward)) - error("Currently only ForwardProjectorByBinUsingProjMatrixByBin supports TOF reconstruction. Abort."); - else - original_forward->enable_tof(_proj_data_info_sptr, v); - } - else // if is ForwardProjectorByBinUsingProjMatrixByBin directly. - { - ForwardProjectorByBinUsingProjMatrixByBin* original_forward = - dynamic_cast (forward_projector_sptr.get()); - - if (is_null_ptr(original_forward)) - error("Currently only ForwardProjectorByBinUsingProjMatrixByBin supports TOF reconstruction. Abort."); - else - original_forward->enable_tof(_proj_data_info_sptr, v); - } - -// // Check if it is PostSmoothingBackProjectorByBin is used. -// PostsmoothingBackProjectorByBin* back= -// dynamic_cast (back_projector_sptr.get()); - -// if (!is_null_ptr(back)) -// { -// BackProjectorByBinUsingProjMatrixByBin* original_back = -// dynamic_cast (back->get_original_back_projector_ptr()); - -// if (is_null_ptr(original_back)) -// error("Currently only ForwardProjectorByBinUsingProjMatrixByBin supports TOF reconstruction. Abort."); -// else -// original_back->enable_tof(_proj_data_info_sptr, v); -// } -// else // if is ForwardProjectorByBinUsingProjMatrixByBin directly. -// { -// BackProjectorByBinUsingProjMatrixByBin* original_back = -// dynamic_cast (back_projector_sptr.get()); - -// if (is_null_ptr(original_back)) -// error("Currently only BackProjectorByBinUsingProjMatrixByBin supports TOF reconstruction. Abort."); -// else -// original_back->enable_tof(_proj_data_info_sptr, v); -// } - -} - -void -ProjectorByBinPair:: -set_tof_data(const CartesianCoordinate3D* _point1, - const CartesianCoordinate3D* _point2) -{ - // Check if it is PresmoothingForwardProjectorByBin - PresmoothingForwardProjectorByBin* forward= - dynamic_cast (forward_projector_sptr.get()); - - if (!is_null_ptr(forward)) - { - ForwardProjectorByBinUsingProjMatrixByBin* original_forward = - dynamic_cast (forward->get_original_forward_projector_ptr()); - - if (is_null_ptr(original_forward)) - error("Currently only ForwardProjectorByBinUsingProjMatrixByBin supports TOF reconstruction. Abort."); - else - { - original_forward->set_tof_data(_point1, _point2); - - PostsmoothingBackProjectorByBin* back= - dynamic_cast (back_projector_sptr.get()); - - if (!is_null_ptr(back)) - { - BackProjectorByBinUsingProjMatrixByBin* original_back = - dynamic_cast (back->get_original_back_projector_ptr()); - - if (is_null_ptr(original_back)) - error("Currently only ForwardProjectorByBinUsingProjMatrixByBin supports TOF reconstruction. Abort."); - else - original_back->enable_tof(original_forward->get_tof_row()); - } - else // if is ForwardProjectorByBinUsingProjMatrixByBin directly. - { - BackProjectorByBinUsingProjMatrixByBin* original_back = - dynamic_cast (back_projector_sptr.get()); - - if (is_null_ptr(original_back)) - error("Currently only BackProjectorByBinUsingProjMatrixByBin supports TOF reconstruction. Abort."); - else - original_back->enable_tof(original_forward->get_tof_row()); - } - } - } - else // if is ForwardProjectorByBinUsingProjMatrixByBin directly. - { - ForwardProjectorByBinUsingProjMatrixByBin* original_forward = - dynamic_cast (forward_projector_sptr.get()); - - if (is_null_ptr(original_forward)) - error("Currently only ForwardProjectorByBinUsingProjMatrixByBin supports TOF reconstruction. Abort."); - else - { - original_forward->set_tof_data(_point1, _point2); - - PostsmoothingBackProjectorByBin* back= - dynamic_cast (back_projector_sptr.get()); - - if (!is_null_ptr(back)) - { - BackProjectorByBinUsingProjMatrixByBin* original_back = - dynamic_cast (back->get_original_back_projector_ptr()); - - if (is_null_ptr(original_back)) - error("Currently only ForwardProjectorByBinUsingProjMatrixByBin supports TOF reconstruction. Abort."); - else - original_back->enable_tof(original_forward->get_tof_row()); - } - else // if is ForwardProjectorByBinUsingProjMatrixByBin directly. - { - BackProjectorByBinUsingProjMatrixByBin* original_back = - dynamic_cast (back_projector_sptr.get()); - - if (is_null_ptr(original_back)) - error("Currently only BackProjectorByBinUsingProjMatrixByBin supports TOF reconstruction. Abort."); - else - original_back->enable_tof(original_forward->get_tof_row()); - } - } - } -} - -ProjMatrixElemsForOneBin* -ProjectorByBinPair:: -get_current_tof_row() const -{ - // Check if it is PresmoothingForwardProjectorByBin - PresmoothingForwardProjectorByBin* forward= - dynamic_cast (forward_projector_sptr.get()); - - if (!is_null_ptr(forward)) - { - ForwardProjectorByBinUsingProjMatrixByBin* original_forward = - dynamic_cast (forward->get_original_forward_projector_ptr()); - - if (is_null_ptr(original_forward)) - error("Currently only ForwardProjectorByBinUsingProjMatrixByBin supports TOF reconstruction. Abort."); - else - return original_forward->get_tof_row(); - } - else // if is ForwardProjectorByBinUsingProjMatrixByBin directly. - { - ForwardProjectorByBinUsingProjMatrixByBin* original_forward = - dynamic_cast (forward_projector_sptr.get()); - - if (is_null_ptr(original_forward)) - error("Currently only ForwardProjectorByBinUsingProjMatrixByBin supports TOF reconstruction. Abort."); - else - return original_forward->get_tof_row(); - } -} - void ProjectorByBinPair:: check(const ProjDataInfo& proj_data_info, const DiscretisedDensity<3,float>& density_info) const diff --git a/src/test/test_time_of_flight.cxx b/src/test/test_time_of_flight.cxx index c70ce53322..128160a0b9 100644 --- a/src/test/test_time_of_flight.cxx +++ b/src/test/test_time_of_flight.cxx @@ -150,7 +150,7 @@ TOF_Tests::run_tests() test_proj_matrix_sptr.reset(new ProjMatrixByBinUsingRayTracing()); dynamic_cast(test_proj_matrix_sptr.get())->set_num_tangential_LORs(1); dynamic_cast(test_proj_matrix_sptr.get())->set_up(test_proj_data_info_sptr, test_discretised_density_sptr); - test_proj_matrix_sptr->enable_tof(test_proj_data_info_sptr); +// test_proj_matrix_sptr->enable_tof(test_proj_data_info_sptr); shared_ptr forward_projector_ptr( new ForwardProjectorByBinUsingProjMatrixByBin(test_proj_matrix_sptr)); @@ -174,7 +174,7 @@ TOF_Tests::test_tof_proj_data_info() { const int correct_tof_mashing_factor = 39; const int num_timing_positions = 9; - float correct_width_of_tof_bin = test_scanner_sptr->get_size_of_timing_bin() * + float correct_width_of_tof_bin = test_scanner_sptr->get_size_of_timing_pos() * test_proj_data_info_sptr->get_tof_mash_factor() * 0.299792458f/2; float correct_timing_locations[num_timing_positions] = {-360.201f/2, -280.156f/2, -200.111f/2, -120.067f/2, -40.022f/2, 40.022f/2, 120.067f/2, 200.111f/2, 280.156f/2}; @@ -317,7 +317,7 @@ TOF_Tests::test_tof_kernel_application() int view_num = 0; int axial_num = 0; int tang_num = 0; - CartesianCoordinate3D lor_point_1, lor_point_2; + ProjMatrixElemsForOneBin proj_matrix_row; HighResWallClockTimer t; std::vector times_of_tofing; @@ -326,17 +326,13 @@ TOF_Tests::test_tof_kernel_application() dynamic_cast (test_proj_data_info_sptr.get()); Bin this_bin(seg_num, view_num, axial_num, tang_num, 1.f); - proj_data_ptr->get_LOR_as_two_points(lor_point_1, lor_point_2, this_bin); - - std::cerr<< lor_point_1.x() << " " << lor_point_1.y() << " " << lor_point_1.z() << " " << - lor_point_2.x() << " " << lor_point_2.y() << " " << lor_point_2.z() << std::endl; t.reset(); t.start(); test_proj_matrix_sptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, this_bin); t.stop(); std::cerr<<"Execution time for nonTOF: "<get_min_tof_pos_num(); timing_num <= test_proj_data_info_sptr->get_max_tof_pos_num(); ++ timing_num) @@ -345,14 +341,13 @@ TOF_Tests::test_tof_kernel_application() Bin bin(seg_num, view_num, axial_num, tang_num, timing_num, 1.f); t.reset(); t.start(); - test_proj_matrix_sptr->get_proj_matrix_elems_for_one_bin_with_tof(new_proj_matrix_row, - bin, - lor_point_1, lor_point_2); + test_proj_matrix_sptr->get_proj_matrix_elems_for_one_bin(new_proj_matrix_row, + bin); t.stop(); times_of_tofing.push_back(t.value()); - export_lor(new_proj_matrix_row, - lor_point_1, lor_point_2, timing_num, - proj_matrix_row); +// export_lor(new_proj_matrix_row, +// lor_point_1, lor_point_2, timing_num, +// proj_matrix_row); } double mean = 0.0; From c996e126eb2b9544bcf5c03c20d1cd846900af62 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Thu, 18 Oct 2018 02:00:58 +0100 Subject: [PATCH 108/170] Reduce test time in test_DataSYmmetriesForBins_PET_CartesianGrid increase the tof mashing factor until the test run on Travis --- .../stir/recon_buildblock/ProjMatrixByBin.inl | 12 ++++++------ .../test_DataSymmetriesForBins_PET_CartesianGrid.cxx | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl index eb0c3ed304..1f27e9620c 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl +++ b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl @@ -195,13 +195,13 @@ ProjMatrixByBin::apply_tof_kernel_and_symm_transformation(ProjMatrixElemsForOneB voxel_center[1] = u * difference[1]; if(u < 0.f) - d1 = - sqrt(voxel_center.x()*voxel_center.x() + - voxel_center.y()*voxel_center.y() + - voxel_center.z()*voxel_center.z() ); + d1 = - sqrt( voxel_center[3] * voxel_center[3] + + voxel_center[2] * voxel_center[2] + + voxel_center[1] * voxel_center[1]); else - d1 = sqrt(voxel_center.x()*voxel_center.x() + - voxel_center.y()*voxel_center.y() + - voxel_center.z()*voxel_center.z() ); + d1 = sqrt( voxel_center[3] * voxel_center[3] + + voxel_center[2] * voxel_center[2] + + voxel_center[1] * voxel_center[1]); } //low_dist = ((proj_data_info_sptr->tof_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].low_lim - d1) * r_sqrt2_gauss_sigma) + 4.f; diff --git a/src/recon_test/test_DataSymmetriesForBins_PET_CartesianGrid.cxx b/src/recon_test/test_DataSymmetriesForBins_PET_CartesianGrid.cxx index 1ca8acba3d..e6edd0f879 100644 --- a/src/recon_test/test_DataSymmetriesForBins_PET_CartesianGrid.cxx +++ b/src/recon_test/test_DataSymmetriesForBins_PET_CartesianGrid.cxx @@ -727,7 +727,7 @@ DataSymmetriesForBins_PET_CartesianGridTests::run_tests() /*num_views=*/scanner_sptr->get_num_detectors_per_ring()/8, /*num_tang_poss=*/64, /*arc_corrected*/false, - /*tof_mashing*/39)); + /*tof_mashing*/100)); run_tests_for_1_projdata(proj_data_info_sptr); From 81d2f507849153cee8ba246f0c870f088b63fcf9 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Sat, 20 Oct 2018 02:17:41 +0100 Subject: [PATCH 109/170] Revert kernel caching, ProjDataInfo comparison improvement * Comprare the timing positions of the ProjDataInfo only if both are tof_enabled --- src/buildblock/ProjDataInfo.cxx | 2 +- .../stir/recon_buildblock/ProjMatrixByBin.inl | 32 +++++++++++-------- src/recon_buildblock/ProjMatrixByBin.cxx | 18 +++++------ 3 files changed, 29 insertions(+), 23 deletions(-) diff --git a/src/buildblock/ProjDataInfo.cxx b/src/buildblock/ProjDataInfo.cxx index f2b838d42f..c15d97f087 100644 --- a/src/buildblock/ProjDataInfo.cxx +++ b/src/buildblock/ProjDataInfo.cxx @@ -746,7 +746,7 @@ operator>=(const ProjDataInfo& proj_data_info) const proj_data_info.get_min_tangential_pos_num() < larger_proj_data_info.get_min_tangential_pos_num() || ((proj_data_info.get_min_tof_pos_num() < larger_proj_data_info.get_min_tof_pos_num() || proj_data_info.get_max_tof_pos_num() > larger_proj_data_info.get_max_tof_pos_num()) && - proj_data_info.is_tof_data())) + (proj_data_info.is_tof_data() && larger_proj_data_info.is_tof_data()))) return false; for (int segment_num=proj_data_info.get_min_segment_num(); diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl index 1f27e9620c..8b1288facd 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl +++ b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl @@ -34,7 +34,6 @@ #include "stir/Succeeded.h" #include "stir/recon_buildblock/SymmetryOperation.h" #include "stir/geometry/line_distances.h" -//#include "stir/numerics/erf.h" START_NAMESPACE_STIR @@ -154,7 +153,7 @@ ProjMatrixByBin::apply_tof_kernel_and_symm_transformation(ProjMatrixElemsForOneB //float high_dist = 0.f; float d1; - float step = 1000.f / 8.f; + //float step = 100000.f / 8.f; int p1, p2; // THe direction can be from 1 -> 2 depending on the bin sign. @@ -195,30 +194,37 @@ ProjMatrixByBin::apply_tof_kernel_and_symm_transformation(ProjMatrixElemsForOneB voxel_center[1] = u * difference[1]; if(u < 0.f) - d1 = - sqrt( voxel_center[3] * voxel_center[3] + + d1 = std::sqrt( voxel_center[3] * voxel_center[3] + voxel_center[2] * voxel_center[2] + voxel_center[1] * voxel_center[1]); else - d1 = sqrt( voxel_center[3] * voxel_center[3] + + d1 = -std::sqrt( voxel_center[3] * voxel_center[3] + voxel_center[2] * voxel_center[2] + voxel_center[1] * voxel_center[1]); } - //low_dist = ((proj_data_info_sptr->tof_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].low_lim - d1) * r_sqrt2_gauss_sigma) + 4.f; - //high_dist = ((proj_data_info_sptr->tof_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].high_lim - d1) * r_sqrt2_gauss_sigma) + 4.f; + float low_dist = ((proj_data_info_sptr->tof_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].low_lim - d1) * r_sqrt2_gauss_sigma); + float high_dist = ((proj_data_info_sptr->tof_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].high_lim - d1) * r_sqrt2_gauss_sigma); - p1 = (((proj_data_info_sptr->tof_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].low_lim - d1) * r_sqrt2_gauss_sigma) + 4.f) * step; - p2 = (((proj_data_info_sptr->tof_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].high_lim - d1) * r_sqrt2_gauss_sigma) + 4.f) * step; + //p1 = (((proj_data_info_sptr->tof_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].low_lim - d1) * r_sqrt2_gauss_sigma) + 4.f) * step; + //p2 = (((proj_data_info_sptr->tof_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].high_lim - d1) * r_sqrt2_gauss_sigma) + 4.f) * step; - if (p1 < 0 || p2 < 0 || - p1 > 1000 || p2 > 1000) +// if (p1 < 0 || p2 < 0 || +// p1 >= 100000 || p2 >= 100000) +// { +// *element_ptr = ProjMatrixElemsForOneBin::value_type(c, 0.0f); +// continue; +// } + + if (low_dist >= 4.f || high_dist >= 4.f || + low_dist <= -4.f || high_dist <= -4.f) { - *element_ptr = ProjMatrixElemsForOneBin::value_type(c, 0.0); + *element_ptr = ProjMatrixElemsForOneBin::value_type(c, 0.0f); continue; } - //get_tof_value(low_dist, high_dist, new_value); - new_value = (cache_erf[p2] - cache_erf[p1]) * element_ptr->get_value(); + get_tof_value(low_dist, high_dist, new_value); + new_value *= element_ptr->get_value();//*(cache_erf[p2] - cache_erf[p1]); // *element_ptr = ProjMatrixElemsForOneBin::value_type(c, new_value); } } diff --git a/src/recon_buildblock/ProjMatrixByBin.cxx b/src/recon_buildblock/ProjMatrixByBin.cxx index 3e2cc9130b..c62d78de87 100644 --- a/src/recon_buildblock/ProjMatrixByBin.cxx +++ b/src/recon_buildblock/ProjMatrixByBin.cxx @@ -86,15 +86,15 @@ enable_tof(const shared_ptr& _proj_data_info_sptr, const bool v) gauss_sigma_in_mm = ProjDataInfo::tof_delta_time_to_mm(proj_data_info_sptr->get_scanner_ptr()->get_timing_resolution()) / 2.355f; r_sqrt2_gauss_sigma = 1.0f/ (gauss_sigma_in_mm * static_cast(sqrt(2.0))); - cache_erf.resize(0, 1000); - - float step = 8.f / 1000.f; - // cache erf with reasonable sampling - for (int i = 0 ;i < 1000; ++i) - { - float d = -4.0f + i * step; - cache_erf[i] = 0.5f * erf(d); - } +// cache_erf.resize(0, 100000); + +// float step = 8.f / 100000.f; +// // cache erf with reasonable sampling +// for (int i = 0 ;i < 100000; ++i) +// { +// float d = -4.0f + i * step; +// cache_erf[i] = 0.5f * erf(d); +// } } } From 460173d28b594caf070df0a24ce019ce3a1d3462 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Sat, 20 Oct 2018 09:54:34 +0100 Subject: [PATCH 110/170] correction of the LOR bin after symmetry --- .../stir/recon_buildblock/ProjMatrixByBin.h | 2 +- .../stir/recon_buildblock/ProjMatrixByBin.inl | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBin.h b/src/include/stir/recon_buildblock/ProjMatrixByBin.h index 7eefb4460c..bb54907502 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBin.h +++ b/src/include/stir/recon_buildblock/ProjMatrixByBin.h @@ -269,7 +269,7 @@ class ProjMatrixByBin : //! Get the interal value erf(m - v_j) - erf(m -v_j) - inline void get_tof_value(const float& d1, const float& d2, float& val) const; + inline void get_tof_value(const float d1, const float d2, float& val) const; }; diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl index 8b1288facd..93cdb6ff6f 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl +++ b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl @@ -86,6 +86,10 @@ get_proj_matrix_elems_for_one_bin( proj_data_info_sptr->get_LOR(lor, bin); LORAs2Points lor2(lor); + Bin fbin = probabilities.get_bin(); + symm_ptr->transform_bin_coordinates(fbin); + probabilities.set_bin(fbin); + // now apply TOF kernel and transform to original bin apply_tof_kernel_and_symm_transformation(probabilities, lor2.p1(), lor2.p2(), symm_ptr); } @@ -127,6 +131,10 @@ get_proj_matrix_elems_for_one_bin( LORAs2Points lor2(lor); // now apply TOF kernel and transform to original bin + + Bin fbin = probabilities.get_bin(); + symm_ptr->transform_bin_coordinates(fbin); + probabilities.set_bin(fbin); apply_tof_kernel_and_symm_transformation(probabilities, lor2.p1(), lor2.p2(), symm_ptr); } else @@ -154,7 +162,7 @@ ProjMatrixByBin::apply_tof_kernel_and_symm_transformation(ProjMatrixElemsForOneB float d1; //float step = 100000.f / 8.f; - int p1, p2; + //int p1, p2; // THe direction can be from 1 -> 2 depending on the bin sign. const CartesianCoordinate3D middle = (point1 + point2)*0.5f; @@ -223,8 +231,8 @@ ProjMatrixByBin::apply_tof_kernel_and_symm_transformation(ProjMatrixElemsForOneB continue; } - get_tof_value(low_dist, high_dist, new_value); - new_value *= element_ptr->get_value();//*(cache_erf[p2] - cache_erf[p1]); // +// get_tof_value(low_dist, high_dist, new_value); + new_value = element_ptr->get_value() * 0.5f * (erf(high_dist) - erf(low_dist));//*(cache_erf[p2] - cache_erf[p1]); // *element_ptr = ProjMatrixElemsForOneBin::value_type(c, new_value); } } @@ -232,7 +240,7 @@ ProjMatrixByBin::apply_tof_kernel_and_symm_transformation(ProjMatrixElemsForOneB void ProjMatrixByBin:: //get_tof_value(const float& d1, const float& d2, float& val) const -get_tof_value(const float& d1, const float& d2, float& val) const +get_tof_value(const float d1, const float d2, float& val) const { val = 0.5f * (erf(d2) - erf(d1)); } From 2056815a7562c7d1ea0be3deaea6daf64dfd172e Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Wed, 24 Oct 2018 01:02:16 +0100 Subject: [PATCH 111/170] Revert some changes made in previous commits: Some modifications in 711b96681fd33ca1e3010cd73cbf20c7c3b38d43 and 711b96681fd33ca1e3010cd73cbf20c7c3b38d43 led to small benefits and potential problems. --- .../stir/recon_buildblock/ProjMatrixByBin.h | 2 +- .../stir/recon_buildblock/ProjMatrixByBin.inl | 90 +++++++++---------- 2 files changed, 42 insertions(+), 50 deletions(-) diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBin.h b/src/include/stir/recon_buildblock/ProjMatrixByBin.h index bb54907502..8a438121eb 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBin.h +++ b/src/include/stir/recon_buildblock/ProjMatrixByBin.h @@ -261,7 +261,7 @@ class ProjMatrixByBin : Array<1, float> cache_erf; //! The function which actually applies the TOF kernel on the LOR. - inline void apply_tof_kernel_and_symm_transformation(ProjMatrixElemsForOneBin& tof_probabilities, + inline void apply_tof_kernel_and_symm_transformation(ProjMatrixElemsForOneBin& probabilities, const CartesianCoordinate3D& point1, const CartesianCoordinate3D& point2, const unique_ptr& symm_ptr) STIR_MUTABLE_CONST; diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl index 93cdb6ff6f..79c607d155 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl +++ b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl @@ -59,7 +59,7 @@ get_proj_matrix_elems_for_one_bin( // set to empty probabilities.erase(); - + if (cache_stores_only_basic_bins) { // find basic bin @@ -77,7 +77,7 @@ get_proj_matrix_elems_for_one_bin( #ifndef NDEBUG probabilities.check_state(); #endif - cache_proj_matrix_elems_for_one_bin(probabilities); + cache_proj_matrix_elems_for_one_bin(probabilities); } if ( proj_data_info_sptr->is_tof_data() && this->tof_enabled) @@ -85,11 +85,7 @@ get_proj_matrix_elems_for_one_bin( LORInAxialAndNoArcCorrSinogramCoordinates lor; proj_data_info_sptr->get_LOR(lor, bin); LORAs2Points lor2(lor); - - Bin fbin = probabilities.get_bin(); - symm_ptr->transform_bin_coordinates(fbin); - probabilities.set_bin(fbin); - + probabilities.set_bin(bin); // now apply TOF kernel and transform to original bin apply_tof_kernel_and_symm_transformation(probabilities, lor2.p1(), lor2.p2(), symm_ptr); } @@ -129,13 +125,10 @@ get_proj_matrix_elems_for_one_bin( LORInAxialAndNoArcCorrSinogramCoordinates lor; proj_data_info_sptr->get_LOR(lor, bin); LORAs2Points lor2(lor); - + probabilities.set_bin(bin); // now apply TOF kernel and transform to original bin - - Bin fbin = probabilities.get_bin(); - symm_ptr->transform_bin_coordinates(fbin); - probabilities.set_bin(fbin); apply_tof_kernel_and_symm_transformation(probabilities, lor2.p1(), lor2.p2(), symm_ptr); + } else { @@ -149,7 +142,7 @@ get_proj_matrix_elems_for_one_bin( } void -ProjMatrixByBin::apply_tof_kernel_and_symm_transformation(ProjMatrixElemsForOneBin& tof_probabilities, +ProjMatrixByBin::apply_tof_kernel_and_symm_transformation(ProjMatrixElemsForOneBin& probabilities, const CartesianCoordinate3D& point1, const CartesianCoordinate3D& point2, const unique_ptr& symm_ptr) STIR_MUTABLE_CONST @@ -157,8 +150,8 @@ ProjMatrixByBin::apply_tof_kernel_and_symm_transformation(ProjMatrixElemsForOneB CartesianCoordinate3D voxel_center; float new_value = 0.f; - //float low_dist = 0.f; - //float high_dist = 0.f; + float low_dist = 0.f; + float high_dist = 0.f; float d1; //float step = 100000.f / 8.f; @@ -166,53 +159,53 @@ ProjMatrixByBin::apply_tof_kernel_and_symm_transformation(ProjMatrixElemsForOneB // THe direction can be from 1 -> 2 depending on the bin sign. const CartesianCoordinate3D middle = (point1 + point2)*0.5f; - const CartesianCoordinate3D difference = point2 - middle; + const CartesianCoordinate3D difference = point2 - point1; + +// const CartesianCoordinate3D diff = point2 - middle; + const float denom = 1.f / inner_product(difference, difference); - // float lor_length = 2.f / (std::sqrt((point1.x() - point2.x()) *(point1.x() - point2.x()) + - // (point1.y() - point2.y()) *(point1.y() - point2.y()) + - // (point1.z() - point2.z()) *(point1.z() - point2.z()))); - for (ProjMatrixElemsForOneBin::iterator element_ptr = tof_probabilities.begin(); - element_ptr != tof_probabilities.end(); ++element_ptr) + const float lor_length = 1.f / (std::sqrt(difference.x() * difference.x() + + difference.y() * difference.y() + + difference.z() * difference.z())); + + for (ProjMatrixElemsForOneBin::iterator element_ptr = probabilities.begin(); + element_ptr != probabilities.end(); ++element_ptr) { Coordinate3D c(element_ptr->get_coords()); symm_ptr->transform_image_coordinates(c); - voxel_center = - image_info_sptr->get_physical_coordinates_for_indices (c); +// voxel_center = +// image_info_sptr->get_physical_coordinates_for_indices (c); + +// project_point_on_a_line(point1, point2, voxel_center); - /* - * Original method: - * - project_point_on_a_line(point1, point2, voxel_center); +// const CartesianCoordinate3D x = voxel_center - middle; - const CartesianCoordinate3D x = voxel_center - middle; +// const float d2 = -inner_product(x, diff) * lor_length; - const float d1 = inner_product(x, difference) * lor_length; - */ // The following is the optimisation of the previous: { - const CartesianCoordinate3D r10 = voxel_center - middle; - - const float u = inner_product(r10, difference) * denom; - - voxel_center[3] = u * difference[3]; - voxel_center[2] = u * difference[2]; - voxel_center[1] = u * difference[1]; - - if(u < 0.f) - d1 = std::sqrt( voxel_center[3] * voxel_center[3] + - voxel_center[2] * voxel_center[2] + - voxel_center[1] * voxel_center[1]); - else - d1 = -std::sqrt( voxel_center[3] * voxel_center[3] + - voxel_center[2] * voxel_center[2] + - voxel_center[1] * voxel_center[1]); + voxel_center = + image_info_sptr->get_physical_coordinates_for_indices (c); + + const CartesianCoordinate3D x = point2 - voxel_center; + + const float u = inner_product(x, difference) * denom; + + voxel_center[3] = point1[3] + u * difference[3]; + voxel_center[2] = point1[2] + u * difference[2]; + voxel_center[1] = point1[1] + u * difference[1]; + + const CartesianCoordinate3D x_dim = voxel_center - middle; + + d1 = inner_product(x_dim, difference) * lor_length; + } - float low_dist = ((proj_data_info_sptr->tof_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].low_lim - d1) * r_sqrt2_gauss_sigma); - float high_dist = ((proj_data_info_sptr->tof_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].high_lim - d1) * r_sqrt2_gauss_sigma); + low_dist = ((proj_data_info_sptr->tof_bin_boundaries_mm[probabilities.get_bin_ptr()->timing_pos_num()].low_lim - d1) * r_sqrt2_gauss_sigma); + high_dist = ((proj_data_info_sptr->tof_bin_boundaries_mm[probabilities.get_bin_ptr()->timing_pos_num()].high_lim - d1) * r_sqrt2_gauss_sigma); //p1 = (((proj_data_info_sptr->tof_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].low_lim - d1) * r_sqrt2_gauss_sigma) + 4.f) * step; //p2 = (((proj_data_info_sptr->tof_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].high_lim - d1) * r_sqrt2_gauss_sigma) + 4.f) * step; @@ -239,7 +232,6 @@ ProjMatrixByBin::apply_tof_kernel_and_symm_transformation(ProjMatrixElemsForOneB void ProjMatrixByBin:: -//get_tof_value(const float& d1, const float& d2, float& val) const get_tof_value(const float d1, const float d2, float& val) const { val = 0.5f * (erf(d2) - erf(d1)); From ccb6a24054a490ea5c0d4cf6db469ef0d49011e8 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Mon, 12 Nov 2018 09:13:09 +1100 Subject: [PATCH 112/170] Bug fixes close #16 : BinNormalisationFromProjData::is_trivial needs TOF loop close #13 : use member record_sptr in PoissonLogLikelihoodWithLinearModelForMeanAndListModeData close #5: consistent naming of timing/tof bin/pos close #4: change get_k to be centre of bin. rename to be consistent in TOF variable --- src/buildblock/ProjDataInfo.cxx | 26 +++++++++---------- ...orMeanAndListModeDataWithProjMatrixByBin.h | 2 -- .../stir/recon_buildblock/ProjMatrixByBin.inl | 8 +++--- .../BinNormalisationFromProjData.cxx | 7 ++++- ...MeanAndListModeDataWithProjMatrixByBin.cxx | 14 +++++----- 5 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/buildblock/ProjDataInfo.cxx b/src/buildblock/ProjDataInfo.cxx index c15d97f087..117a671ff8 100644 --- a/src/buildblock/ProjDataInfo.cxx +++ b/src/buildblock/ProjDataInfo.cxx @@ -78,15 +78,15 @@ float ProjDataInfo::get_k(const Bin& bin) const { if (!(num_tof_bins%2)) - return bin.timing_pos_num() * tof_increament_in_mm; + 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) - tof_increament_in_mm/2.f; + 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) + tof_increament_in_mm / 2.f); // get_k gives "left" edge + return mm_to_tof_delta_time(get_k(bin)); // get_k gives "left" edge N.E: corrected returns the center. } float @@ -214,21 +214,21 @@ ProjDataInfo::set_tof_mash_factor(const int new_num) tof_bin_boundaries_ps.grow(min_tof_pos_num, max_tof_pos_num); - for (int i = min_tof_pos_num; i <= max_tof_pos_num; ++i ) + for (int k = min_tof_pos_num; k <= max_tof_pos_num; ++k ) { Bin bin; - bin.timing_pos_num() = i; + bin.timing_pos_num() = k; - float cur_low = get_k(bin); - float cur_high = get_k(bin) + get_sampling_in_k(bin); + 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[i].low_lim = cur_low; - tof_bin_boundaries_mm[i].high_lim = cur_high; - tof_bin_boundaries_ps[i].low_lim = static_cast(mm_to_tof_delta_time(tof_bin_boundaries_mm[i].low_lim)); - tof_bin_boundaries_ps[i].high_lim = static_cast(mm_to_tof_delta_time(tof_bin_boundaries_mm[i].high_lim)); + 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%") %i % tof_bin_boundaries_mm[i].low_lim % tof_bin_boundaries_mm[i].high_lim - % tof_bin_boundaries_ps[i].low_lim % tof_bin_boundaries_ps[i].high_lim % get_sampling_in_k(bin)); + 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) diff --git a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h index 0fa1a6bb3a..4628cb4005 100644 --- a/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h +++ b/src/include/stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h @@ -122,8 +122,6 @@ typedef RegisteredParsingObject proj_data_info_sptr; - shared_ptr record_sptr; - //! sets any default values /*! Has to be called by set_defaults in the leaf-class */ virtual void set_defaults(); diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl index 79c607d155..41c7351036 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl +++ b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl @@ -165,7 +165,7 @@ ProjMatrixByBin::apply_tof_kernel_and_symm_transformation(ProjMatrixElemsForOneB const float denom = 1.f / inner_product(difference, difference); - const float lor_length = 1.f / (std::sqrt(difference.x() * difference.x() + + const float lor_length = 2.f / (std::sqrt(difference.x() * difference.x() + difference.y() * difference.y() + difference.z() * difference.z())); @@ -200,7 +200,7 @@ ProjMatrixByBin::apply_tof_kernel_and_symm_transformation(ProjMatrixElemsForOneB const CartesianCoordinate3D x_dim = voxel_center - middle; - d1 = inner_product(x_dim, difference) * lor_length; + d1 = -inner_product(x_dim, difference) * lor_length; } @@ -224,8 +224,8 @@ ProjMatrixByBin::apply_tof_kernel_and_symm_transformation(ProjMatrixElemsForOneB continue; } -// get_tof_value(low_dist, high_dist, new_value); - new_value = element_ptr->get_value() * 0.5f * (erf(high_dist) - erf(low_dist));//*(cache_erf[p2] - cache_erf[p1]); // + get_tof_value(low_dist, high_dist, new_value); + //new_value = element_ptr->get_value() *(cache_erf[p2] - cache_erf[p1]); *element_ptr = ProjMatrixElemsForOneBin::value_type(c, new_value); } } diff --git a/src/recon_buildblock/BinNormalisationFromProjData.cxx b/src/recon_buildblock/BinNormalisationFromProjData.cxx index 59aaa52d13..fa4b503fa6 100644 --- a/src/recon_buildblock/BinNormalisationFromProjData.cxx +++ b/src/recon_buildblock/BinNormalisationFromProjData.cxx @@ -113,6 +113,10 @@ bool BinNormalisationFromProjData:: is_trivial() const { + for (int tof_pos = this->norm_proj_data_ptr->get_min_tof_pos_num(); + tof_pos <= this->norm_proj_data_ptr->get_max_tof_pos_num(); + ++tof_pos) + { // check if all data is 1 (up to a tolerance of 1e-4) for (int segment_num = this->norm_proj_data_ptr->get_min_segment_num(); segment_num <= this->norm_proj_data_ptr->get_max_segment_num(); @@ -123,11 +127,12 @@ is_trivial() const ++view_num) { const Viewgram viewgram = - this->norm_proj_data_ptr->get_viewgram(view_num, segment_num); + this->norm_proj_data_ptr->get_viewgram(view_num, segment_num, tof_pos); if (fabs(viewgram.find_min()-1)>.0001 || fabs(viewgram.find_max()-1)>.0001) return false; // return from function as we know not all data is 1 } } + } // if we get here. they were all 1 return true; } diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx index c096621df6..4f7637b6fa 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx @@ -250,8 +250,6 @@ set_up_before_sensitivity(shared_ptr const& target_sptr) return Succeeded::no; } - record_sptr = this->list_mode_data_sptr->get_empty_record_sptr(); - return Succeeded::yes; } @@ -473,7 +471,7 @@ compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, double current_time = 0.; ProjMatrixElemsForOneBin proj_matrix_row; - CListRecord& record = *record_sptr; + shared_ptr record_sptr = this->list_mode_data_sptr->get_empty_record_sptr(); long int more_events = this->do_time_frame? 1 : (this->num_events_to_use / this->num_subsets); @@ -481,26 +479,26 @@ compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, while (more_events)//this->list_mode_data_sptr->get_next_record(record) == Succeeded::yes) { - if (this->list_mode_data_sptr->get_next_record(record) == Succeeded::no) + if (this->list_mode_data_sptr->get_next_record(*record_sptr) == Succeeded::no) { info("End of file!"); break; //get out of while loop } - if(record.is_time() && end_time > 0.01) + if(record_sptr->is_time() && end_time > 0.01) { - current_time = record.time().get_time_in_secs(); + current_time = record_sptr->time().get_time_in_secs(); if (this->do_time_frame && current_time >= end_time) break; // get out of while loop if (current_time < start_time) continue; } - if (record.is_event() && record.event().is_prompt()) + if (record_sptr->is_event() && record_sptr->event().is_prompt()) { measured_bin.set_bin_value(1.0f); - record.event().get_bin(measured_bin, *proj_data_info_sptr); + record_sptr->event().get_bin(measured_bin, *proj_data_info_sptr); // In theory we have already done all these checks so we can // remove this if statement. From b2778ed062496766745fb2c6c134d4bca6d71239 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Mon, 12 Nov 2018 11:17:59 +1100 Subject: [PATCH 113/170] Keep only two set_params and constructors for Scanner With the addition of the energy resolution and timing resolution the initialisation of the scanner became very ambiguous. I unified all version in two cases in which all parameters have to set by the user, even if they are not used. --- src/IO/InterfileHeader.cxx | 74 ++-- src/IO/InterfilePDFSHeaderSPECT.cxx | 46 ++- src/buildblock/Scanner.cxx | 595 +++++++--------------------- src/include/stir/Scanner.h | 153 +------ 4 files changed, 202 insertions(+), 666 deletions(-) diff --git a/src/IO/InterfileHeader.cxx b/src/IO/InterfileHeader.cxx index 3d75991781..a6c3ce0896 100644 --- a/src/IO/InterfileHeader.cxx +++ b/src/IO/InterfileHeader.cxx @@ -1321,57 +1321,31 @@ bool InterfilePDFSHeader::post_processing() // data from the Interfile header (or the guessed scanner). shared_ptr scanner_sptr_from_file; - if (false) // !guessed_scanner_ptr->is_tof_ready()) - { - scanner_sptr_from_file.reset( - new Scanner(guessed_scanner_ptr->get_type(), - get_exam_info_ptr()->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, - num_transaxial_crystals_per_singles_unit, - num_detector_layers, - energy_resolution, - reference_energy)); - } - else - { - warning("ENERGY WINDOW INFO IGNORED"); - scanner_sptr_from_file.reset( - new Scanner(guessed_scanner_ptr->get_type(), - get_exam_info_ptr()->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, - num_transaxial_crystals_per_singles_unit, - num_detector_layers, - max_num_timing_poss, - size_of_timing_pos, - timing_resolution)); - } + scanner_sptr_from_file.reset( + new Scanner(guessed_scanner_ptr->get_type(), + get_exam_info_ptr()->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, + num_transaxial_crystals_per_singles_unit, + num_detector_layers, + energy_resolution, + reference_energy, + max_num_timing_poss, + size_of_timing_pos, + timing_resolution)); bool is_consistent = scanner_sptr_from_file->check_consistency() == Succeeded::yes; diff --git a/src/IO/InterfilePDFSHeaderSPECT.cxx b/src/IO/InterfilePDFSHeaderSPECT.cxx index bc043232d1..c13392ba18 100644 --- a/src/IO/InterfilePDFSHeaderSPECT.cxx +++ b/src/IO/InterfilePDFSHeaderSPECT.cxx @@ -163,27 +163,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_ptr()->originating_system)); shared_ptr scanner_ptr_from_file( - new Scanner(guessed_scanner_ptr->get_type(), - get_exam_info_ptr()->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_ptr()->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/buildblock/Scanner.cxx b/src/buildblock/Scanner.cxx index 9bb456fe6d..756ee8e0d3 100644 --- a/src/buildblock/Scanner.cxx +++ b/src/buildblock/Scanner.cxx @@ -120,9 +120,11 @@ Scanner::Scanner(Type scanner_type) // KT 25/01/2002 corrected ring_spacing set_params(E931, string_list("ECAT 931"), - 8, 192, 2 * 256, + 8, 192, 2 * 256, 2*256, 510.0F, 7.0F, 13.5F, 3.129F, 0.0F, - 2, 4, 4, 8, 4, 8 * 4, 1); + 2, 4, 4, 8, 4, 8 * 4, 1, + 0.0F, 511.F, + 1, 1.F, 1.F); // 16 BUCKETS per ring in TWO rings - i.e. 32 buckets in total break; @@ -130,66 +132,82 @@ Scanner::Scanner(Type scanner_type) case E951: set_params(E951, string_list("ECAT 951"), - 16, 192, 2 * 256, + 16, 192, 2 * 256, 2*256, 510.0F, 7.0F, 6.75F, 3.12932F, 0.0F, - 1, 4, 8, 8, 8, 8 * 4, 1); + 1, 4, 8, 8, 8, 8 * 4, 1, + 0.0F, 511.F, + 1, 1.F, 1.F); break; case E953: set_params(E953, string_list("ECAT 953"), - 16, 160, 2 * 192, + 16, 160, 2 * 192, 2*192, 382.5F, 7.0F, 6.75F, 3.12932F, static_cast(15.*_PI/180), - 1, 4, 8, 8, 8, 8 * 4, 1); + 1, 4, 8, 8, 8, 8 * 4, 1, + 0.0F, 511.F, + 1, 1.F, 1.F); break; case E921: set_params(E921, string_list("ECAT 921", "ECAT EXACT", "EXACT"), - 24, 192, 2* 192, + 24, 192, 2* 192, 2 * 192, 412.5F, 7.0F, 6.75F, 3.375F, static_cast(15.*_PI/180), - 1, 4, 8, 8, 8, 8 * 4, 1); + 1, 4, 8, 8, 8, 8 * 4, 1, + 0.0F, 511.F, + 1, 1.F, 1.F); break; case E925: set_params(E925, string_list("ECAT 925", "ECAT ART"), - 24, 192, 2* 192, + 24, 192, 2* 192, 2* 192, 412.5F, 7.0F, 6.75F, 3.375F, static_cast(15.*_PI/180), - 3, 4, 8, 8, 8, 8 * 4, 1); + 3, 4, 8, 8, 8, 8 * 4, 1, + 0.0F, 511.F, + 1, 1.F, 1.F); break; case E961: set_params(E961,string_list("ECAT 961", "ECAT HR"), - 24, 336, 2* 392, + 24, 336, 2* 392, 2*392, 412.0F, 7.0F, 6.25F, 1.650F, static_cast(13.*_PI/180), - 1, 8, 8, 7, 8, 7 * 8, 1); + 1, 8, 8, 7, 8, 7 * 8, 1, + 0.0F, 511.F, + 1, 1.F, 1.F); break; case E962: set_params(E962,string_list("ECAT 962","ECAT HR+"), - 32, 288, 2* 288, + 32, 288, 2* 288, 2*288, 412.0F, 7.0F, 4.85F, 2.25F, 0.0F, - 4, 3, 8, 8, 8, 8 * 3, 1); + 4, 3, 8, 8, 8, 8 * 3, 1, + 0.0F, 511.F, + 1, 1.F, 1.F); break; case E966: set_params(E966, string_list("ECAT EXACT 3D", "EXACT 3D", "ECAT HR++","ECAT 966"), - 48, 288, 2* 288, + 48, 288, 2* 288, 2*288, 412.0F, 7.0F, 4.850F, 2.250F, 0.0, - 6, 2, 8, 8, 2 * 8, 8 * 2, 1); + 6, 2, 8, 8, 2 * 8, 8 * 2, 1, + 0.0F, 511.F, + 1, 1.F, 1.F); break; case E1080: // data added by Robert Barnett, Westmead Hospital, Sydney set_params(E1080, string_list("ECAT 1080", "Biograph 16", "1080"), - 41, 336, 2* 336, + 41, 336, 2* 336, 2*336, 412.0F, 7.0F, 4.0F, 2.000F, 0.0F, - 1, 2, 41, 14, 41, 14, 1); + 1, 2, 41, 14, 41, 14, 1, + 0.0F, 511.F, + 1, 1.F, 1.F); // Transaxial blocks have 13 physical crystals and a gap at the // 140th crystal where the counts are zero. // There are 39 rings with 13 axial crystals per block. Two virtual @@ -201,17 +219,20 @@ Scanner::Scanner(Type scanner_type) // Transaxial blocks have 8 physical crystals and a gap at the // 9th crystal where the counts are zero. set_params(Siemens_mMR, string_list("Siemens mMR", "mMR", "2008"), - 64, 344, 2* 252, + 64, 344, 2* 252, 2*252, 328.0F, 7.0F, 4.0625F, 2.08626F, 0.0F, - 2, 1, 8, 9, 16, 9, 1 ); // TODO bucket/singles info incorrect? 224 buckets in total, but not sure how distributed + 2, 1, 8, 9, 16, 9, 1, + 0.0F, 511.F, + 1, 1.F, 1.F ); // TODO bucket/singles info incorrect? 224 buckets in total, but not sure how distributed break; case test_scanner: // This is a relatively small scanner for test purposes. set_params(test_scanner, string_list("test_scanner"), - 4, 344, 2*252, + 4, 344, 2*252,2*252, 328.0F, 7.0F, 4.0625F, 2.08626F, 0.0F, 1, 1, 4, 1, 4, 1, 1, + 0.0F, 511.F, (short int)(410), (float)(10.0F), (float)(400.0F) ); @@ -220,9 +241,11 @@ Scanner::Scanner(Type scanner_type) case RPT: set_params(RPT, string_list("PRT-1", "RPT"), - 16, 128, 2 * 192, + 16, 128, 2 * 192, 2*192, 380.0F - 7.0F, 7.0F, 6.75F, 3.1088F, 0.0F, - 1, 4, 8, 8, 8, 32, 1); + 1, 4, 8, 8, 8, 32, 1, + 0.0F, 511.F, + 1, 1.F, 1.F); // Default 7.0mm average interaction depth. // This 7mm taken off the inner ring radius so that the effective radius remains 380mm @@ -231,9 +254,11 @@ Scanner::Scanner(Type scanner_type) case RATPET: set_params(RATPET, string_list("RATPET"), - 8, 56, 2 * 56, + 8, 56, 2 * 56, 2*56, 115 / 2.F, 7.0F, 6.25F, 1.65F, 0.0F, - 1, 16, 8, 7, 8, 0, 1); // HR block, 4 buckets per ring + 1, 16, 8, 7, 8, 0, 1, + 0.0F, 511.F, + 1, 1.F, 1.F); // HR block, 4 buckets per ring // Default 7.0mm average interaction depth. // 8 x 0 crystals per singles unit because not known @@ -245,7 +270,9 @@ Scanner::Scanner(Type scanner_type) set_params(PANDA, string_list("PANDA"), 1 /*NumRings*/, 512 /*MaxBinsNonArcCor*/, 512 /*MaxBinsArcCor*/, 2048 /*NumDetPerRing*/, /*MeanInnerRadius*/ 75.5/2.F, /*AverageDoI*/ 10.F, /*Ring Spacing*/ 3.F, /*BinSize*/ 0.1F, /*IntrinsicTilt*/ 0.F, - 1, 1, 1, 1, 0, 0, 1); + 1, 1, 1, 1, 0, 0, 1, + 0.0F, 511.F, + 1, 1.F, 1.F); break; case nanoPET: @@ -254,7 +281,9 @@ Scanner::Scanner(Type scanner_type) 81, 39*3, /* We could also model gaps in the future as one detector so 39->39+1, while 1 (point source), 3 (mouse) or 5 (rats) */ 39*3, /* Just put the same with NonArcCor for now*/ 12 * 39, 174.F, 5.0F, 1.17F, 1.17F, /* Actual size is 1.12 and 0.05 is the thickness of the optical reflector */ 0.0F, /* not sure for this */ - 0,0,0,0,0,0, 1); + 0,0,0,0,0,0, 1, + 0.0F, 511.F, + 1, 1.F, 1.F); break; case HYPERimage: @@ -262,7 +291,9 @@ Scanner::Scanner(Type scanner_type) set_params(HYPERimage, string_list("HYPERimage"), /*Modelling the gap with one fake crystal */ 22, 239, 245, 490, 103.97F, 3.0F, 1.4F, 1.4F, /* Actual size is 1.3667 and assume 0.0333 is the thickness of the optical reflector */ 0.F, - 0,0,0,0,0,0,1); + 0,0,0,0,0,0,1, + 0.0F, 511.F, + 1, 1.F, 1.F); break; @@ -274,7 +305,9 @@ Scanner::Scanner(Type scanner_type) set_params(Advance, string_list("GE Advance", "Advance"), 18, 283, 281, 2 * 336, 471.875F - 8.4F, 8.4F, 8.5F, 1.970177F, 0.0F, //TODO view offset shouldn't be zero - 3, 2, 6, 6, 1, 1, 1); + 3, 2, 6, 6, 1, 1, 1, + 0.0F, 511.F, + 1, 1.F, 1.F); break; case DiscoveryLS: @@ -282,7 +315,9 @@ Scanner::Scanner(Type scanner_type) set_params(DiscoveryLS, string_list("GE Discovery LS", "Discovery LS"), 18, 283, 281, 2 * 336, 471.875F - 8.4F, 8.4F, 8.5F, 1.970177F, 0.0F, //TODO view offset shouldn't be zero - 3, 2, 6, 6, 1, 1, 1); + 3, 2, 6, 6, 1, 1, 1, + 0.0F, 511.F, + 1, 1.F, 1.F); break; case DiscoveryST: @@ -293,7 +328,9 @@ Scanner::Scanner(Type scanner_type) 24, 249, 221, 2 * 210, 886.2F/2.F, 8.4F, 6.54F, 3.195F, static_cast(-4.54224*_PI/180),//sign? - 4, 2, 6, 6, 1, 1, 1);// TODO not sure about sign of view_offset + 4, 2, 6, 6, 1, 1, 1, + 0.0F, 511.F, + 1, 1.F, 1.F);// TODO not sure about sign of view_offset break; case DiscoverySTE: @@ -303,6 +340,7 @@ Scanner::Scanner(Type scanner_type) 886.2F/2.F, 8.4F, 6.54F, 2.397F, static_cast(-4.5490*_PI/180),//sign? 4, 2, 6, 8, 1, 1, 1, + 0.0F, 511.F, (short int)(410), (float)(10.0F), (float)(400.0F) );// TODO not sure about sign of view_offset @@ -321,7 +359,9 @@ Scanner::Scanner(Type scanner_type) static_cast(-4.5950*_PI/180),//sign? 4, 2, - 6, 9, 1, 1, 1);// TODO not sure about sign of view_offset + 6, 9, 1, 1, 1, + 0.0F, 511.F, + 1, 1.F, 1.F);// TODO not sure about sign of view_offset break; case Discovery600: @@ -338,7 +378,9 @@ Scanner::Scanner(Type scanner_type) static_cast(-4.5490*_PI/180),//sign? TODO value 4, 2, - 6, 8, 1, 1, 1); + 6, 8, 1, 1, 1, + 0.0F, 511.F, + 1, 1.F, 1.F); break; case PETMR_Signa: @@ -355,7 +397,8 @@ case PETMR_Signa: static_cast(-5.23*_PI/180),//sign? TODO value 5, 4, - 9, 4, 1, 1, 1, + 9, 4, 1, 1, 1, + 0.0F, 511.F, (short int)(351), (float)(89.0F/13.0F), //TODO (float)(390.0F) ); @@ -376,22 +419,22 @@ case PETMR_Signa: static_cast(-5.021*_PI/180),//sign? TODO value 4, 2, - 6, 9, 1, 1, 1 -#ifdef STIR_TOF - , + 6, 9, 1, 1, 1, + 0.0F, 511.F, (short int)(55), (float)(89.0F), (float)(550.0F) -#endif ); break; case HZLR: set_params(HZLR, string_list("Positron HZL/R"), - 32, 256, 2 * 192, + 32, 256, 2 * 192, 2*192, 780.0F, 7.0F, 5.1875F, 2.F, 0.0F, - 0, 0, 0, 0, 0,0, 1); + 0, 0, 0, 0, 0,0, 1, + 0.0F, 511.F, + 1, 1.F, 1.F); // Default 7.0mm average interaction depth. // crystals per singles unit etc unknown break; @@ -399,9 +442,11 @@ case PETMR_Signa: case HRRT: set_params(HRRT, string_list("HRRT"), - 104, 288, 2 * 288, + 104, 288, 2 * 288, 2*288, 234.765F, 7.0F, 2.4375F, 1.21875F, 0.0F, - 0, 0, 0, 0, 0, 0, 2); // added by Dylan Togane + 0, 0, 0, 0, 0, 0, 2, + 0.0F, 511.F, + 1, 1.F, 1.F); // added by Dylan Togane // warning: used 7.0mm average interaction depth. // crystals per singles unit etc unknown break; @@ -430,13 +475,15 @@ case PETMR_Signa: scanner. */ set_params(Allegro,string_list("Allegro", "Philips Allegro"), - 29, 295, 28*23, + 29, 295, 28*23, 28*23, 430.05F, 12.F, 6.3F, 4.3F, 0.0F, 1, 0, 29, 0 /* 23* or 22*/, 29, 0 /* all detectors in a ring? */, - 1); + 1, + 0.0F, 511.F, + 1, 1.F, 1.F); break; case GeminiTF: @@ -448,32 +495,40 @@ case PETMR_Signa: 0, 0, 0, 0, // Not considering any gap, but this is per module 28 flat modules in total, while 420 PMTs 0, 0 /* Not sure about these, but shouldn't be important */, - 1); + 1, + 0.0F, 511.F, + 1, 1.F, 1.F); break; case HiDAC: // all of these don't make any sense for the HiDAC set_params(HiDAC, string_list("HiDAC"), - 0, 0, 0, + 0, 0, 0,0, 0.F, 0.F, 0.F, 0.F, 0.F, - 0, 0, 0, 0, 0, 0, 0); + 0, 0, 0, 0, 0, 0, 0, + 0.0F, 511.F, + 1, 1.F, 1.F); break; case User_defined_scanner: // zlong, 08-04-2004, Userdefined support set_params(User_defined_scanner, string_list("Userdefined"), - 0, 0, 0, + 0, 0, 0,0, 0.F, 0.F, 0.F, 0.F, 0.F, - 0, 0, 0, 0, 0, 0, 0); + 0, 0, 0, 0, 0, 0, 0, + 0.0F, 511.F, + 1, 1.F, 1.F); break; default: // warning("Unknown scanner type used for initialisation of Scanner\n"); set_params(Unknown_scanner, string_list("Unknown"), - 0, 0, 0, + 0, 0, 0,0, 0.F, 0.F, 0.F, 0.F, 0.F, - 0, 0, 0, 0, 0, 0, 0); + 0, 0, 0, 0, 0, 0, 0, + 0.0F, 511.F, + 1, 1.F, 1.F); break; @@ -481,33 +536,6 @@ case PETMR_Signa: } - -Scanner::Scanner(Type type_v, const list& list_of_names_v, - int num_detectors_per_ring_v, int num_rings_v, - int max_num_non_arccorrected_bins_v, - int default_num_arccorrected_bins_v, - float inner_ring_radius_v, float average_depth_of_interaction_v, - float ring_spacing_v, float bin_size_v, float intrinsic_tilt_v, - int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, - int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, - int num_axial_crystals_per_singles_unit_v, - int num_transaxial_crystals_per_singles_unit_v, - int num_detector_layers_v) -{ - set_params(type_v, list_of_names_v, num_rings_v, - max_num_non_arccorrected_bins_v, - default_num_arccorrected_bins_v, - num_detectors_per_ring_v, - inner_ring_radius_v, - average_depth_of_interaction_v, - ring_spacing_v, bin_size_v, intrinsic_tilt_v, - num_axial_blocks_per_bucket_v, num_transaxial_blocks_per_bucket_v, - num_axial_crystals_per_block_v, num_transaxial_crystals_per_block_v, - num_axial_crystals_per_singles_unit_v, - num_transaxial_crystals_per_singles_unit_v, - num_detector_layers_v); -} - Scanner::Scanner(Type type_v, const list& list_of_names_v, int num_detectors_per_ring_v, int num_rings_v, int max_num_non_arccorrected_bins_v, @@ -520,308 +548,51 @@ Scanner::Scanner(Type type_v, const list& list_of_names_v, int num_transaxial_crystals_per_singles_unit_v, int num_detector_layers_v, float energy_resolution_v, - float reference_energy_v) -{ - set_params(type_v, list_of_names_v, num_rings_v, - max_num_non_arccorrected_bins_v, - default_num_arccorrected_bins_v, - num_detectors_per_ring_v, - inner_ring_radius_v, - average_depth_of_interaction_v, - ring_spacing_v, bin_size_v, intrinsic_tilt_v, - energy_resolution_v, - reference_energy_v, - num_axial_blocks_per_bucket_v, num_transaxial_blocks_per_bucket_v, - num_axial_crystals_per_block_v, num_transaxial_crystals_per_block_v, - num_axial_crystals_per_singles_unit_v, - num_transaxial_crystals_per_singles_unit_v, - num_detector_layers_v); -} - -Scanner::Scanner(Type type_v, const list& list_of_names_v, - int num_detectors_per_ring_v, int num_rings_v, - int max_num_non_arccorrected_bins_v, - int default_num_arccorrected_bins_v, - float inner_ring_radius_v, float average_depth_of_interaction_v, - float ring_spacing_v, float bin_size_v, float intrinsic_tilt_v, - int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, - int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, - int num_axial_crystals_per_singles_unit_v, - int num_transaxial_crystals_per_singles_unit_v, - int num_detector_layers_v, + float reference_energy_v, short int max_num_of_timing_poss_v, float size_timing_pos_v, float timing_resolution_v) { - set_params(type_v, list_of_names_v, num_rings_v, - max_num_non_arccorrected_bins_v, - default_num_arccorrected_bins_v, - num_detectors_per_ring_v, - inner_ring_radius_v, - average_depth_of_interaction_v, - ring_spacing_v, bin_size_v, intrinsic_tilt_v, - num_axial_blocks_per_bucket_v, num_transaxial_blocks_per_bucket_v, - num_axial_crystals_per_block_v, num_transaxial_crystals_per_block_v, - num_axial_crystals_per_singles_unit_v, - num_transaxial_crystals_per_singles_unit_v, - num_detector_layers_v, - max_num_of_timing_poss_v, - size_timing_pos_v, - timing_resolution_v); -} - - -Scanner::Scanner(Type type_v, const string& name, - int num_detectors_per_ring_v, int num_rings_v, - int max_num_non_arccorrected_bins_v, - int default_num_arccorrected_bins_v, - float inner_ring_radius_v, float average_depth_of_interaction_v, - float ring_spacing_v, float bin_size_v, float intrinsic_tilt_v, - int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, - int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, - int num_axial_crystals_per_singles_unit_v, - int num_transaxial_crystals_per_singles_unit_v, - int num_detector_layers_v) -{ - set_params(type_v, string_list(name), num_rings_v, - max_num_non_arccorrected_bins_v, - default_num_arccorrected_bins_v, - num_detectors_per_ring_v, - inner_ring_radius_v, - average_depth_of_interaction_v, - ring_spacing_v, bin_size_v, intrinsic_tilt_v, - num_axial_blocks_per_bucket_v, num_transaxial_blocks_per_bucket_v, - num_axial_crystals_per_block_v, num_transaxial_crystals_per_block_v, - num_axial_crystals_per_singles_unit_v, - num_transaxial_crystals_per_singles_unit_v, - num_detector_layers_v); -} - -Scanner::Scanner(Type type_v, const string& name, - int num_detectors_per_ring_v, int num_rings_v, - int max_num_non_arccorrected_bins_v, - int default_num_arccorrected_bins_v, - float inner_ring_radius_v, float average_depth_of_interaction_v, - float ring_spacing_v, float bin_size_v, float intrinsic_tilt_v, - int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, - int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, - int num_axial_crystals_per_singles_unit_v, - int num_transaxial_crystals_per_singles_unit_v, - int num_detector_layers_v, - float energy_resolution_v, - float reference_energy_v) -{ - set_params(type_v, string_list(name), num_rings_v, + set_params(type_v, list_of_names_v, num_rings_v, max_num_non_arccorrected_bins_v, default_num_arccorrected_bins_v, num_detectors_per_ring_v, inner_ring_radius_v, average_depth_of_interaction_v, ring_spacing_v, bin_size_v, intrinsic_tilt_v, - energy_resolution_v, - reference_energy_v, num_axial_blocks_per_bucket_v, num_transaxial_blocks_per_bucket_v, num_axial_crystals_per_block_v, num_transaxial_crystals_per_block_v, num_axial_crystals_per_singles_unit_v, num_transaxial_crystals_per_singles_unit_v, - num_detector_layers_v); -} - -Scanner::Scanner(Type type_v, const string& name, - int num_detectors_per_ring_v, int num_rings_v, - int max_num_non_arccorrected_bins_v, - int default_num_arccorrected_bins_v, - float inner_ring_radius_v, float average_depth_of_interaction_v, - float ring_spacing_v, float bin_size_v, float intrinsic_tilt_v, - int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, - int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, - int num_axial_crystals_per_singles_unit_v, - int num_transaxial_crystals_per_singles_unit_v, - int num_detector_layers_v, - short int max_num_of_timing_poss_v, - float size_timing_pos_v, - float timing_resolution_v - ) -{ - set_params(type_v, string_list(name), num_rings_v, - max_num_non_arccorrected_bins_v, - default_num_arccorrected_bins_v, - num_detectors_per_ring_v, - inner_ring_radius_v, - average_depth_of_interaction_v, - ring_spacing_v, bin_size_v, intrinsic_tilt_v, - num_axial_blocks_per_bucket_v, num_transaxial_blocks_per_bucket_v, - num_axial_crystals_per_block_v, num_transaxial_crystals_per_block_v, - num_axial_crystals_per_singles_unit_v, - num_transaxial_crystals_per_singles_unit_v, - num_detector_layers_v, - max_num_of_timing_poss_v, - size_timing_pos_v, - timing_resolution_v); -} - -void -Scanner:: -set_params(Type type_v,const list& list_of_names_v, - int num_rings_v, - int max_num_non_arccorrected_bins_v, - int num_detectors_per_ring_v, - float inner_ring_radius_v, - float average_depth_of_interaction_v, - float ring_spacing_v, - float bin_size_v, float intrinsic_tilt_v, - int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, - int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, - int num_axial_crystals_per_singles_unit_v, - int num_transaxial_crystals_per_singles_unit_v, - int num_detector_layers_v) -{ - set_params(type_v, list_of_names_v, num_rings_v, - max_num_non_arccorrected_bins_v, - max_num_non_arccorrected_bins_v, - num_detectors_per_ring_v, - inner_ring_radius_v, - average_depth_of_interaction_v, - ring_spacing_v, bin_size_v, intrinsic_tilt_v, - num_axial_blocks_per_bucket_v, num_transaxial_blocks_per_bucket_v, - num_axial_crystals_per_block_v, num_transaxial_crystals_per_block_v, - num_axial_crystals_per_singles_unit_v, - num_transaxial_crystals_per_singles_unit_v, - num_detector_layers_v); -} - -void -Scanner:: -set_params(Type type_v,const list& list_of_names_v, - int num_rings_v, - int max_num_non_arccorrected_bins_v, - int num_detectors_per_ring_v, - float inner_ring_radius_v, - float average_depth_of_interaction_v, - float ring_spacing_v, - float bin_size_v, float intrinsic_tilt_v, - float energy_resolution_v, - float reference_energy_v, - int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, - int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, - int num_axial_crystals_per_singles_unit_v, - int num_transaxial_crystals_per_singles_unit_v, - int num_detector_layers_v) -{ - set_params(type_v, list_of_names_v, num_rings_v, - max_num_non_arccorrected_bins_v, - max_num_non_arccorrected_bins_v, - num_detectors_per_ring_v, - inner_ring_radius_v, - average_depth_of_interaction_v, - ring_spacing_v, bin_size_v, intrinsic_tilt_v, + num_detector_layers_v, energy_resolution_v, reference_energy_v, - num_axial_blocks_per_bucket_v, num_transaxial_blocks_per_bucket_v, - num_axial_crystals_per_block_v, num_transaxial_crystals_per_block_v, - num_axial_crystals_per_singles_unit_v, - num_transaxial_crystals_per_singles_unit_v, - num_detector_layers_v); + max_num_of_timing_poss_v, + size_timing_pos_v, + timing_resolution_v); } void Scanner:: -set_params(Type type_v,const list& list_of_names_v, - int num_rings_v, - int max_num_non_arccorrected_bins_v, - int num_detectors_per_ring_v, - float inner_ring_radius_v, - float average_depth_of_interaction_v, - float ring_spacing_v, - float bin_size_v, float intrinsic_tilt_v, - int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, - int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, - int num_axial_crystals_per_singles_unit_v, - int num_transaxial_crystals_per_singles_unit_v, - int num_detector_layers_v, - short int max_num_of_timing_poss_v, - float size_timing_pos_v, - float timing_resolution_v) -{ - set_params(type_v, list_of_names_v, num_rings_v, - max_num_non_arccorrected_bins_v, - max_num_non_arccorrected_bins_v, - num_detectors_per_ring_v, - inner_ring_radius_v, - average_depth_of_interaction_v, - ring_spacing_v, bin_size_v, intrinsic_tilt_v, - num_axial_blocks_per_bucket_v, num_transaxial_blocks_per_bucket_v, - num_axial_crystals_per_block_v, num_transaxial_crystals_per_block_v, - num_axial_crystals_per_singles_unit_v, - num_transaxial_crystals_per_singles_unit_v, - num_detector_layers_v, - max_num_of_timing_poss_v, - size_timing_pos_v, - timing_resolution_v); -} - -void -Scanner:: -set_params(Type type_v,const list& list_of_names_v, - int num_rings_v, - int max_num_non_arccorrected_bins_v, - int default_num_arccorrected_bins_v, - int num_detectors_per_ring_v, - float inner_ring_radius_v, - float average_depth_of_interaction_v, - float ring_spacing_v, - float bin_size_v, float intrinsic_tilt_v, - int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, - int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, - int num_axial_crystals_per_singles_unit_v, - int num_transaxial_crystals_per_singles_unit_v, - int num_detector_layers_v) -{ - type = type_v; - list_of_names = list_of_names_v; - num_rings = num_rings_v; - max_num_non_arccorrected_bins = max_num_non_arccorrected_bins_v; - default_num_arccorrected_bins = default_num_arccorrected_bins_v; - num_detectors_per_ring = num_detectors_per_ring_v; - inner_ring_radius = inner_ring_radius_v; - average_depth_of_interaction = average_depth_of_interaction_v; - ring_spacing = ring_spacing_v; - bin_size = bin_size_v; - intrinsic_tilt = intrinsic_tilt_v; - num_transaxial_blocks_per_bucket = num_transaxial_blocks_per_bucket_v; - num_axial_blocks_per_bucket = num_axial_blocks_per_bucket_v; - num_axial_crystals_per_block= num_axial_crystals_per_block_v; - num_transaxial_crystals_per_block= num_transaxial_crystals_per_block_v; - num_axial_crystals_per_singles_unit = num_axial_crystals_per_singles_unit_v; - num_transaxial_crystals_per_singles_unit = num_transaxial_crystals_per_singles_unit_v; - num_detector_layers = num_detector_layers_v; - - energy_resolution = -1.f; - reference_energy = -1.f; - max_num_of_timing_poss = -1; - size_timing_pos = -1.f; - timing_resolution = -1.f; - -} - -void -Scanner:: -set_params(Type type_v,const list& list_of_names_v, - int num_rings_v, - int max_num_non_arccorrected_bins_v, - int default_num_arccorrected_bins_v, - int num_detectors_per_ring_v, - float inner_ring_radius_v, - float average_depth_of_interaction_v, - float ring_spacing_v, - float bin_size_v, float intrinsic_tilt_v, - float energy_resolution_v, - float reference_energy_v, - int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, - int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, - int num_axial_crystals_per_singles_unit_v, - int num_transaxial_crystals_per_singles_unit_v, - int num_detector_layers_v) +set_params(Type type_v, const std::list& list_of_names_v, + int num_rings_v, + int max_num_non_arccorrected_bins_v, + int default_num_arccorrected_bins_v, + int num_detectors_per_ring_v, + float inner_ring_radius_v, + float average_depth_of_interaction_v, + float ring_spacing_v, + float bin_size_v, float intrinsic_tilt_v, + int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, + int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, + int num_axial_crystals_per_singles_unit_v, + int num_transaxial_crystals_per_singles_unit_v, + int num_detector_layers_v, + float energy_resolution_v, + float reference_energy_v, + short int max_num_of_timing_poss_v, + float size_timing_pos_v, + float timing_resolution_v) { type = type_v; list_of_names = list_of_names_v; @@ -844,61 +615,12 @@ set_params(Type type_v,const list& list_of_names_v, energy_resolution = energy_resolution_v; reference_energy = reference_energy_v; - max_num_of_timing_poss = -1; - size_timing_pos = -1.f; - timing_resolution = -1.f; - -} - + max_num_of_timing_poss = max_num_of_timing_poss_v; + size_timing_pos = size_timing_pos_v; + timing_resolution = timing_resolution_v; -void -Scanner:: -set_params(Type type_v,const list& list_of_names_v, - int num_rings_v, - int max_num_non_arccorrected_bins_v, - int default_num_arccorrected_bins_v, - int num_detectors_per_ring_v, - float inner_ring_radius_v, - float average_depth_of_interaction_v, - float ring_spacing_v, - float bin_size_v, float intrinsic_tilt_v, - int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, - int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, - int num_axial_crystals_per_singles_unit_v, - int num_transaxial_crystals_per_singles_unit_v, - int num_detector_layers_v, - short int max_num_of_timing_poss_v, - float size_timing_pos_v, - float timing_resolution_v) -{ - type = type_v; - list_of_names = list_of_names_v; - num_rings = num_rings_v; - max_num_non_arccorrected_bins = max_num_non_arccorrected_bins_v; - default_num_arccorrected_bins = default_num_arccorrected_bins_v; - num_detectors_per_ring = num_detectors_per_ring_v; - inner_ring_radius = inner_ring_radius_v; - average_depth_of_interaction = average_depth_of_interaction_v; - ring_spacing = ring_spacing_v; - bin_size = bin_size_v; - intrinsic_tilt = intrinsic_tilt_v; - num_transaxial_blocks_per_bucket = num_transaxial_blocks_per_bucket_v; - num_axial_blocks_per_bucket = num_axial_blocks_per_bucket_v; - num_axial_crystals_per_block= num_axial_crystals_per_block_v; - num_transaxial_crystals_per_block= num_transaxial_crystals_per_block_v; - num_axial_crystals_per_singles_unit = num_axial_crystals_per_singles_unit_v; - num_transaxial_crystals_per_singles_unit = num_transaxial_crystals_per_singles_unit_v; - num_detector_layers = num_detector_layers_v; - - max_num_of_timing_poss = max_num_of_timing_poss_v; - size_timing_pos = size_timing_pos_v; - timing_resolution = timing_resolution_v; - energy_resolution = -1.f; - reference_energy = -1.f; } - - Succeeded Scanner:: check_consistency() const @@ -1246,8 +968,8 @@ Scanner* Scanner::ask_parameters() int TransaxialCrystalsPerSinglesUnit = ask_num("Enter number of transaxial crystals per singles unit: ", 0, num_detectors_per_ring, 1); - int Num_TOF_bins = - ask_num("Number of TOF time bins :", 0.0f, 800.0f, 0.0f); + short int Num_TOF_bins = + ask_num("Number of TOF time bins :", 0, 800, 0); float Size_TOF_bin = ask_num("Size of timing bin (ps) :", 0.0f, 100.0f, 0.0f); float TOF_resolution = @@ -1263,23 +985,7 @@ Scanner* Scanner::ask_parameters() ask_num("Enter number of detector layers per block: ",1,100,1); Type type = User_defined_scanner; - bool make_tof_scanner = false; - if (Num_TOF_bins > 0 && Size_TOF_bin > 0 && TOF_resolution > 0) - make_tof_scanner = true; - - Scanner* scanner_ptr = - new Scanner(type, string_list(name), - num_detectors_per_ring, NoRings, - NoBins, NoBins, - InnerRingRadius, AverageDepthOfInteraction, - RingSpacing, BinSize,intrTilt*float(_PI)/180, - AxialBlocksPerBucket,TransBlocksPerBucket, - AxialCrystalsPerBlock,TransaxialCrystalsPerBlock, - AxialCrstalsPerSinglesUnit, TransaxialCrystalsPerSinglesUnit, - num_detector_layers); - - if (EnergyResolution > -1 && ReferenceEnergy > -1) - scanner_ptr = + scanner_ptr = new Scanner(type, string_list(name), num_detectors_per_ring, NoRings, NoBins, NoBins, @@ -1290,29 +996,10 @@ Scanner* Scanner::ask_parameters() AxialCrstalsPerSinglesUnit, TransaxialCrystalsPerSinglesUnit, num_detector_layers, EnergyResolution, - ReferenceEnergy ); - else - scanner_ptr = make_tof_scanner ? new Scanner(type, string_list(name), - num_detectors_per_ring, NoRings, - NoBins, NoBins, - InnerRingRadius, AverageDepthOfInteraction, - RingSpacing, BinSize,intrTilt*float(_PI)/180, - AxialBlocksPerBucket,TransBlocksPerBucket, - AxialCrystalsPerBlock,TransaxialCrystalsPerBlock, - AxialCrstalsPerSinglesUnit, TransaxialCrystalsPerSinglesUnit, - num_detector_layers, - Num_TOF_bins, - Size_TOF_bin, - TOF_resolution) : - new Scanner(type, string_list(name), - num_detectors_per_ring, NoRings, - NoBins, NoBins, - InnerRingRadius, AverageDepthOfInteraction, - RingSpacing, BinSize,intrTilt*float(_PI)/180, - AxialBlocksPerBucket,TransBlocksPerBucket, - AxialCrystalsPerBlock,TransaxialCrystalsPerBlock, - AxialCrstalsPerSinglesUnit, TransaxialCrystalsPerSinglesUnit, - num_detector_layers); + ReferenceEnergy, + Num_TOF_bins, + Size_TOF_bin, + TOF_resolution ); if (scanner_ptr->check_consistency()==Succeeded::yes || !ask("Ask questions again?",true)) diff --git a/src/include/stir/Scanner.h b/src/include/stir/Scanner.h index cba4a8e647..0bb651c3ea 100644 --- a/src/include/stir/Scanner.h +++ b/src/include/stir/Scanner.h @@ -126,19 +126,6 @@ class Scanner \param intrinsic_tilt_v value in radians, \see get_default_intrinsic_tilt() \warning calls error() when block/bucket info are inconsistent */ - Scanner(Type type_v, const std::list& list_of_names_v, - int num_detectors_per_ring_v, int num_rings_v, - int max_num_non_arccorrected_bins_v, - int default_num_arccorrected_bins_v, - float inner_ring_radius_v, float average_depth_of_interaction_v, - float ring_spacing_v, float bin_size_v, float intrinsic_tilt_v, - int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, - int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, - int num_axial_crystals_per_singles_unit_v, - int num_transaxial_crystals_per_singles_unit_v, - int num_detector_layers_v); - - //! Overloaded for energy resolution Scanner(Type type_v, const std::list& list_of_names_v, int num_detectors_per_ring_v, int num_rings_v, int max_num_non_arccorrected_bins_v, @@ -151,42 +138,17 @@ class Scanner int num_transaxial_crystals_per_singles_unit_v, int num_detector_layers_v, float energy_resolution_v, - float reference_energy_v); + float reference_energy_v, + short int max_num_of_timing_poss, + float size_timing_pos, + float timing_resolution); - //! Overloaded constructed with TOF information - Scanner(Type type_v, const std::list& list_of_names_v, - int num_detectors_per_ring_v, int num_rings_v, - int max_num_non_arccorrected_bins_v, - int default_num_arccorrected_bins_v, - float inner_ring_radius_v, float average_depth_of_interaction_v, - float ring_spacing_v, float bin_size_v, float intrinsic_tilt_v, - int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, - int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, - int num_axial_crystals_per_singles_unit_v, - int num_transaxial_crystals_per_singles_unit_v, - int num_detector_layers_v, - short int max_num_of_timing_poss, - float size_timing_pos, - float timing_resolution); //! constructor ( a single name) /*! size info is in mm \param intrinsic_tilt value in radians, \see get_default_intrinsic_tilt() \warning calls error() when block/bucket info are inconsistent */ - Scanner(Type type_v, const std::string& name, - int num_detectors_per_ring_v, int num_rings_v, - int max_num_non_arccorrected_bins_v, - int default_num_arccorrected_bins_v, - float inner_ring_radius_v, float average_depth_of_interaction_v, - float ring_spacing_v, float bin_size_v, float intrinsic_tilt_v, - int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, - int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, - int num_axial_crystals_per_singles_unit_v, - int num_transaxial_crystals_per_singles_unit_v, - int num_detector_layers_v); - - //! Overloaded for energy resolution Scanner(Type type_v, const std::string& name, int num_detectors_per_ring_v, int num_rings_v, int max_num_non_arccorrected_bins_v, @@ -199,24 +161,10 @@ class Scanner int num_transaxial_crystals_per_singles_unit_v, int num_detector_layers_v, float energy_resolution_v, - float reference_energy_v); - - //! Overloaded constructor with TOF information ( a single name) - Scanner(Type type_v, const std::string& name, - int num_detectors_per_ring_v, int num_rings_v, - int max_num_non_arccorrected_bins_v, - int default_num_arccorrected_bins_v, - float inner_ring_radius_v, float average_depth_of_interaction_v, - float ring_spacing_v, float bin_size_v, float intrinsic_tilt_v, - int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, - int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, - int num_axial_crystals_per_singles_unit_v, - int num_transaxial_crystals_per_singles_unit_v, - int num_detector_layers_v, - short int max_num_of_timing_poss, - float size_timing_pos, - float timing_resolution); - + float reference_energy_v, + short int max_num_of_timing_poss, + float size_timing_pos, + float timing_resolution); //! get scanner parameters as a std::string @@ -457,57 +405,6 @@ class Scanner //! This number corresponds the the least significant clock digit. float size_timing_pos; - - //! set all parameters, case where default_num_arccorrected_bins==max_num_non_arccorrected_bins - void set_params(Type type_v, const std::list& list_of_names_v, - int num_rings_v, - int max_num_non_arccorrected_bins_v, - int num_detectors_per_ring_v, - float inner_ring_radius_v, - float average_depth_of_interaction_v, - float ring_spacing_v, - float bin_size_v, float intrinsic_tilt_v, - int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, - int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, - int num_axial_crystals_per_singles_unit_v, - int num_transaxial_crystals_per_singles_unit_v, - int num_detector_layers_v); - - void set_params(Type type_v, const std::list& list_of_names_v, - int num_rings_v, - int max_num_non_arccorrected_bins_v, - int num_detectors_per_ring_v, - float inner_ring_radius_v, - float average_depth_of_interaction_v, - float ring_spacing_v, - float bin_size_v, float intrinsic_tilt_v, - float energy_resolution_v, - float reference_energy, - int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, - int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, - int num_axial_crystals_per_singles_unit_v, - int num_transaxial_crystals_per_singles_unit_v, - int num_detector_layers_v); - - //! Overloaded with TOF stuff. - void set_params(Type type_v, const std::list& list_of_names_v, - int num_rings_v, - int max_num_non_arccorrected_bins_v, - int num_detectors_per_ring_v, - float inner_ring_radius_v, - float average_depth_of_interaction_v, - float ring_spacing_v, - float bin_size_v, float intrinsic_tilt_v, - int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, - int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, - int num_axial_crystals_per_singles_unit_v, - int num_transaxial_crystals_per_singles_unit_v, - int num_detector_layers_v, - short int max_num_of_timing_poss_v, - float size_timing_pos_v, - float timing_resolution_v); - - // ! set all parameters void set_params(Type type_v, const std::list& list_of_names_v, int num_rings_v, int max_num_non_arccorrected_bins_v, @@ -521,45 +418,13 @@ class Scanner int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, int num_axial_crystals_per_singles_unit_v, int num_transaxial_crystals_per_singles_unit_v, - int num_detector_layers_v); - - void set_params(Type type_v, const std::list& list_of_names_v, - int num_rings_v, - int max_num_non_arccorrected_bins_v, - int default_num_arccorrected_bins_v, - int num_detectors_per_ring_v, - float inner_ring_radius_v, - float average_depth_of_interaction_v, - float ring_spacing_v, - float bin_size_v, float intrinsic_tilt_v, + int num_detector_layers_v, float energy_resolution_v, float reference_energy, - int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, - int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, - int num_axial_crystals_per_singles_unit_v, - int num_transaxial_crystals_per_singles_unit_v, - int num_detector_layers_v); - - //! Overloaded with TOF stuff. - void set_params(Type type_v, const std::list& list_of_names_v, - int num_rings_v, - int max_num_non_arccorrected_bins_v, - int default_num_arccorrected_bins_v, - int num_detectors_per_ring_v, - float inner_ring_radius_v, - float average_depth_of_interaction_v, - float ring_spacing_v, - float bin_size_v, float intrinsic_tilt_v, - int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, - int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, - int num_axial_crystals_per_singles_unit_v, - int num_transaxial_crystals_per_singles_unit_v, - int num_detector_layers_v, short int max_num_of_timing_poss_v, float size_timing_pos_v, float timing_resolution_v); - }; END_NAMESPACE_STIR From 0d58b914c78a0822a04482fe458515385bec41e1 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Thu, 6 Dec 2018 14:53:04 +0000 Subject: [PATCH 114/170] Fix tests and clean up *. test_ArcCorrection shorter execution time *. Further work on simpler Scanner *. clean up *. Tests for TOF corrected and documented --- scripts/plot_TOF_bins.m | 70 +++ src/buildblock/Scanner.cxx | 121 ++++- src/include/stir/Scanner.h | 7 +- src/include/stir/listmode/CListModeDataROOT.h | 4 + .../stir/recon_buildblock/ProjMatrixByBin.inl | 65 +-- src/listmode_buildblock/CListModeDataROOT.cxx | 5 + src/recon_buildblock/ProjMatrixByBin.cxx | 10 - src/test/test_ArcCorrection.cxx | 2 +- src/test/test_time_of_flight.cxx | 474 +++++++++--------- 9 files changed, 435 insertions(+), 323 deletions(-) create mode 100644 scripts/plot_TOF_bins.m diff --git a/scripts/plot_TOF_bins.m b/scripts/plot_TOF_bins.m new file mode 100644 index 0000000000..b7ed40758e --- /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 from the disk and plots them. +%% +clc; clear all; +%Path to TOF files. +path_name ='/home/nikos/Desktop/conv_LOR/' + +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/buildblock/Scanner.cxx b/src/buildblock/Scanner.cxx index 756ee8e0d3..88169ea932 100644 --- a/src/buildblock/Scanner.cxx +++ b/src/buildblock/Scanner.cxx @@ -124,7 +124,7 @@ Scanner::Scanner(Type scanner_type) 510.0F, 7.0F, 13.5F, 3.129F, 0.0F, 2, 4, 4, 8, 4, 8 * 4, 1, 0.0F, 511.F, - 1, 1.F, 1.F); + 0, 0.F, 0.F); // 16 BUCKETS per ring in TWO rings - i.e. 32 buckets in total break; @@ -136,7 +136,7 @@ Scanner::Scanner(Type scanner_type) 510.0F, 7.0F, 6.75F, 3.12932F, 0.0F, 1, 4, 8, 8, 8, 8 * 4, 1, 0.0F, 511.F, - 1, 1.F, 1.F); + 0, 0.F, 0.F); break; case E953: @@ -146,7 +146,7 @@ Scanner::Scanner(Type scanner_type) 382.5F, 7.0F, 6.75F, 3.12932F, static_cast(15.*_PI/180), 1, 4, 8, 8, 8, 8 * 4, 1, 0.0F, 511.F, - 1, 1.F, 1.F); + 0, 0.F, 0.F); break; case E921: @@ -156,7 +156,7 @@ Scanner::Scanner(Type scanner_type) 412.5F, 7.0F, 6.75F, 3.375F, static_cast(15.*_PI/180), 1, 4, 8, 8, 8, 8 * 4, 1, 0.0F, 511.F, - 1, 1.F, 1.F); + 0, 0.F, 0.F); break; case E925: @@ -166,7 +166,7 @@ Scanner::Scanner(Type scanner_type) 412.5F, 7.0F, 6.75F, 3.375F, static_cast(15.*_PI/180), 3, 4, 8, 8, 8, 8 * 4, 1, 0.0F, 511.F, - 1, 1.F, 1.F); + 0, 0.F, 0.F); break; @@ -177,7 +177,7 @@ Scanner::Scanner(Type scanner_type) 412.0F, 7.0F, 6.25F, 1.650F, static_cast(13.*_PI/180), 1, 8, 8, 7, 8, 7 * 8, 1, 0.0F, 511.F, - 1, 1.F, 1.F); + 0, 0.F, 0.F); break; case E962: @@ -187,7 +187,7 @@ Scanner::Scanner(Type scanner_type) 412.0F, 7.0F, 4.85F, 2.25F, 0.0F, 4, 3, 8, 8, 8, 8 * 3, 1, 0.0F, 511.F, - 1, 1.F, 1.F); + 0, 0.F, 0.F); break; case E966: @@ -197,7 +197,7 @@ Scanner::Scanner(Type scanner_type) 412.0F, 7.0F, 4.850F, 2.250F, 0.0, 6, 2, 8, 8, 2 * 8, 8 * 2, 1, 0.0F, 511.F, - 1, 1.F, 1.F); + 0, 0.F, 0.F); break; case E1080: @@ -207,7 +207,7 @@ Scanner::Scanner(Type scanner_type) 412.0F, 7.0F, 4.0F, 2.000F, 0.0F, 1, 2, 41, 14, 41, 14, 1, 0.0F, 511.F, - 1, 1.F, 1.F); + 0, 0.F, 0.F); // Transaxial blocks have 13 physical crystals and a gap at the // 140th crystal where the counts are zero. // There are 39 rings with 13 axial crystals per block. Two virtual @@ -223,7 +223,7 @@ Scanner::Scanner(Type scanner_type) 328.0F, 7.0F, 4.0625F, 2.08626F, 0.0F, 2, 1, 8, 9, 16, 9, 1, 0.0F, 511.F, - 1, 1.F, 1.F ); // TODO bucket/singles info incorrect? 224 buckets in total, but not sure how distributed + 0, 0.F, 0.F); // TODO bucket/singles info incorrect? 224 buckets in total, but not sure how distributed break; case test_scanner: @@ -245,7 +245,7 @@ Scanner::Scanner(Type scanner_type) 380.0F - 7.0F, 7.0F, 6.75F, 3.1088F, 0.0F, 1, 4, 8, 8, 8, 32, 1, 0.0F, 511.F, - 1, 1.F, 1.F); + 0, 0.F, 0.F); // Default 7.0mm average interaction depth. // This 7mm taken off the inner ring radius so that the effective radius remains 380mm @@ -258,7 +258,7 @@ Scanner::Scanner(Type scanner_type) 115 / 2.F, 7.0F, 6.25F, 1.65F, 0.0F, 1, 16, 8, 7, 8, 0, 1, 0.0F, 511.F, - 1, 1.F, 1.F); // HR block, 4 buckets per ring + 0, 0.F, 0.F); // HR block, 4 buckets per ring // Default 7.0mm average interaction depth. // 8 x 0 crystals per singles unit because not known @@ -272,7 +272,7 @@ Scanner::Scanner(Type scanner_type) /*MeanInnerRadius*/ 75.5/2.F, /*AverageDoI*/ 10.F, /*Ring Spacing*/ 3.F, /*BinSize*/ 0.1F, /*IntrinsicTilt*/ 0.F, 1, 1, 1, 1, 0, 0, 1, 0.0F, 511.F, - 1, 1.F, 1.F); + 0, 0.F, 0.F); break; case nanoPET: @@ -283,7 +283,7 @@ Scanner::Scanner(Type scanner_type) 12 * 39, 174.F, 5.0F, 1.17F, 1.17F, /* Actual size is 1.12 and 0.05 is the thickness of the optical reflector */ 0.0F, /* not sure for this */ 0,0,0,0,0,0, 1, 0.0F, 511.F, - 1, 1.F, 1.F); + 0, 0.F, 0.F); break; case HYPERimage: @@ -293,7 +293,7 @@ Scanner::Scanner(Type scanner_type) 490, 103.97F, 3.0F, 1.4F, 1.4F, /* Actual size is 1.3667 and assume 0.0333 is the thickness of the optical reflector */ 0.F, 0,0,0,0,0,0,1, 0.0F, 511.F, - 1, 1.F, 1.F); + 0, 0.F, 0.F); break; @@ -307,7 +307,7 @@ Scanner::Scanner(Type scanner_type) 471.875F - 8.4F, 8.4F, 8.5F, 1.970177F, 0.0F, //TODO view offset shouldn't be zero 3, 2, 6, 6, 1, 1, 1, 0.0F, 511.F, - 1, 1.F, 1.F); + 0, 0.F, 0.F); break; case DiscoveryLS: @@ -317,7 +317,7 @@ Scanner::Scanner(Type scanner_type) 471.875F - 8.4F, 8.4F, 8.5F, 1.970177F, 0.0F, //TODO view offset shouldn't be zero 3, 2, 6, 6, 1, 1, 1, 0.0F, 511.F, - 1, 1.F, 1.F); + 0, 0.F, 0.F); break; case DiscoveryST: @@ -330,7 +330,7 @@ Scanner::Scanner(Type scanner_type) static_cast(-4.54224*_PI/180),//sign? 4, 2, 6, 6, 1, 1, 1, 0.0F, 511.F, - 1, 1.F, 1.F);// TODO not sure about sign of view_offset + 0, 0.F, 0.F);// TODO not sure about sign of view_offset break; case DiscoverySTE: @@ -346,6 +346,20 @@ Scanner::Scanner(Type scanner_type) (float)(400.0F) );// TODO not sure about sign of view_offset break; + case ntest_TOF_50: // dummy + // 8x8 blocks, 1 virtual "crystal", 56 blocks along the ring, 8 blocks in axial direction + // Transaxial blocks have 8 physical crystals and a gap at the + // 9th crystal where the counts are zero. + set_params(ntest_TOF_50, string_list("ntest_TOF_50"), + 24, 320, 320,666, + 424.5F, 7.0F, 4.16F, 2.0F, 0.0F, + 1, 1, 24, 1, 24, 1, 1, + 0.0f, 511.f, + (short int)(2999), + (float)(1.0F), + (float)(81.2) ); // TODO bucket/singles info incorrect? 224 buckets in total, but not sure how di$ + break; + case DiscoveryRX: set_params(DiscoveryRX, string_list("GE Discovery RX", "Discovery RX"), @@ -404,6 +418,27 @@ case PETMR_Signa: (float)(390.0F) ); break; + case PETMR_Signa_nonTOF: + + set_params(PETMR_Signa_nonTOF, string_list("GE PET/MR Signa nonTOF", "GE PET/MR Signa nonTOF"), + 45, + 357, + 331, // TODO + 2 * 224, + 317.0F, + 9.4F, + 5.55F, + 2.1306F, // TO CHECK + static_cast(-5.23*_PI/180),//sign? TODO value + 5, + 4, + 9, 4, 1, 1, 1, + 0.0F, 511.F, + (short int)(0), + (float)(0), //TODO + (float)(0) ); + break; + case Discovery690: // same as 710 set_params(Discovery690, string_list("GE Discovery 690", "Discovery 690", @@ -434,7 +469,7 @@ case PETMR_Signa: 780.0F, 7.0F, 5.1875F, 2.F, 0.0F, 0, 0, 0, 0, 0,0, 1, 0.0F, 511.F, - 1, 1.F, 1.F); + 0, 0.F, 0.F); // Default 7.0mm average interaction depth. // crystals per singles unit etc unknown break; @@ -446,7 +481,7 @@ case PETMR_Signa: 234.765F, 7.0F, 2.4375F, 1.21875F, 0.0F, 0, 0, 0, 0, 0, 0, 2, 0.0F, 511.F, - 1, 1.F, 1.F); // added by Dylan Togane + 0, 0.F, 0.F); // added by Dylan Togane // warning: used 7.0mm average interaction depth. // crystals per singles unit etc unknown break; @@ -483,7 +518,7 @@ case PETMR_Signa: 29, 0 /* all detectors in a ring? */, 1, 0.0F, 511.F, - 1, 1.F, 1.F); + 0, 0.F, 0.F); break; case GeminiTF: @@ -497,7 +532,7 @@ case PETMR_Signa: 0, 0 /* Not sure about these, but shouldn't be important */, 1, 0.0F, 511.F, - 1, 1.F, 1.F); + 0, 0.F, 0.F); break; case HiDAC: // all of these don't make any sense for the HiDAC @@ -506,7 +541,7 @@ case PETMR_Signa: 0.F, 0.F, 0.F, 0.F, 0.F, 0, 0, 0, 0, 0, 0, 0, 0.0F, 511.F, - 1, 1.F, 1.F); + 0, 0.F, 0.F); break; @@ -517,7 +552,7 @@ case PETMR_Signa: 0.F, 0.F, 0.F, 0.F, 0.F, 0, 0, 0, 0, 0, 0, 0, 0.0F, 511.F, - 1, 1.F, 1.F); + 0, 0.F, 0.F); break; @@ -528,7 +563,7 @@ case PETMR_Signa: 0.F, 0.F, 0.F, 0.F, 0.F, 0, 0, 0, 0, 0, 0, 0, 0.0F, 511.F, - 1, 1.F, 1.F); + 0, 0.F, 0.F); break; @@ -572,6 +607,42 @@ Scanner::Scanner(Type type_v, const list& list_of_names_v, timing_resolution_v); } +Scanner::Scanner(Type type_v, const string& name, + int num_detectors_per_ring_v, int num_rings_v, + int max_num_non_arccorrected_bins_v, + int default_num_arccorrected_bins_v, + float inner_ring_radius_v, float average_depth_of_interaction_v, + float ring_spacing_v, float bin_size_v, float intrinsic_tilt_v, + int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, + int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, + int num_axial_crystals_per_singles_unit_v, + int num_transaxial_crystals_per_singles_unit_v, + int num_detector_layers_v, + float energy_resolution_v, + float reference_energy_v, + short int max_num_of_timing_poss_v, + float size_timing_pos_v, + float timing_resolution_v) +{ + set_params(type_v, string_list(name), num_rings_v, + max_num_non_arccorrected_bins_v, + default_num_arccorrected_bins_v, + num_detectors_per_ring_v, + inner_ring_radius_v, + average_depth_of_interaction_v, + ring_spacing_v, bin_size_v, intrinsic_tilt_v, + num_axial_blocks_per_bucket_v, num_transaxial_blocks_per_bucket_v, + num_axial_crystals_per_block_v, num_transaxial_crystals_per_block_v, + num_axial_crystals_per_singles_unit_v, + num_transaxial_crystals_per_singles_unit_v, + num_detector_layers_v, + energy_resolution_v, + reference_energy_v, + max_num_of_timing_poss_v, + size_timing_pos_v, + timing_resolution_v); +} + void Scanner:: set_params(Type type_v, const std::list& list_of_names_v, diff --git a/src/include/stir/Scanner.h b/src/include/stir/Scanner.h index 0bb651c3ea..4f3f67a6b2 100644 --- a/src/include/stir/Scanner.h +++ b/src/include/stir/Scanner.h @@ -82,6 +82,9 @@ class Succeeded; \warning This information is only sensible for discrete detector-based scanners. \warning Currently, in a TOF compatible scanner template, the last three types have to be explicitly defined to avoid ambiguity. + \warning The energy resolution has to be specified but it is used only for scatter correction. + \warning In order to define a nonTOF scanner the timing resolution has to be set to 0 or 1. + Anything else will trigger a TOF reconstruction. \todo Some scanners do not have all info filled in at present. Values are then set to 0. @@ -113,8 +116,8 @@ class Scanner any given parameters. */ enum Type {E931, E951, E953, E921, E925, E961, E962, E966, E1080, test_scanner, Siemens_mMR, RPT,HiDAC, - Advance, DiscoveryLS, DiscoveryST, DiscoverySTE, DiscoveryRX, Discovery600,Discovery690,PETMR_Signa, - HZLR, RATPET, PANDA, HYPERimage, nanoPET, HRRT, Allegro, GeminiTF, User_defined_scanner, + Advance, DiscoveryLS, DiscoveryST, DiscoverySTE, DiscoveryRX, Discovery600,Discovery690,PETMR_Signa, PETMR_Signa_nonTOF, + HZLR, RATPET, PANDA, HYPERimage, nanoPET, HRRT, Allegro, GeminiTF, User_defined_scanner,ntest_TOF_50, Unknown_scanner}; //! constructor that takes scanner type as an input argument diff --git a/src/include/stir/listmode/CListModeDataROOT.h b/src/include/stir/listmode/CListModeDataROOT.h index f439d8a83c..85884df7d5 100644 --- a/src/include/stir/listmode/CListModeDataROOT.h +++ b/src/include/stir/listmode/CListModeDataROOT.h @@ -178,6 +178,10 @@ class CListModeDataROOT : public CListModeData float size_timing_bin; float timing_resolution; + + float energy_resolution; + + float reference_energy; //@} int tof_mash_factor; diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl index 41c7351036..259e6e4a61 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl +++ b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl @@ -154,20 +154,14 @@ ProjMatrixByBin::apply_tof_kernel_and_symm_transformation(ProjMatrixElemsForOneB float high_dist = 0.f; float d1; - //float step = 100000.f / 8.f; - //int p1, p2; - // THe direction can be from 1 -> 2 depending on the bin sign. + // The direction can be from 1 -> 2 depending on the bin sign. const CartesianCoordinate3D middle = (point1 + point2)*0.5f; - const CartesianCoordinate3D difference = point2 - point1; + const CartesianCoordinate3D diff = point2 - middle; -// const CartesianCoordinate3D diff = point2 - middle; - - const float denom = 1.f / inner_product(difference, difference); - - const float lor_length = 2.f / (std::sqrt(difference.x() * difference.x() + - difference.y() * difference.y() + - difference.z() * difference.z())); + const float lor_length = 1.f / (std::sqrt(diff.x() * diff.x() + + diff.y() * diff.y() + + diff.z() * diff.z())); for (ProjMatrixElemsForOneBin::iterator element_ptr = probabilities.begin(); element_ptr != probabilities.end(); ++element_ptr) @@ -175,57 +169,28 @@ ProjMatrixByBin::apply_tof_kernel_and_symm_transformation(ProjMatrixElemsForOneB Coordinate3D c(element_ptr->get_coords()); symm_ptr->transform_image_coordinates(c); -// voxel_center = -// image_info_sptr->get_physical_coordinates_for_indices (c); - -// project_point_on_a_line(point1, point2, voxel_center); - -// const CartesianCoordinate3D x = voxel_center - middle; + voxel_center = + image_info_sptr->get_physical_coordinates_for_indices (c); -// const float d2 = -inner_product(x, diff) * lor_length; - - - // The following is the optimisation of the previous: - { - voxel_center = - image_info_sptr->get_physical_coordinates_for_indices (c); + project_point_on_a_line(point1, point2, voxel_center); - const CartesianCoordinate3D x = point2 - voxel_center; - - const float u = inner_product(x, difference) * denom; - - voxel_center[3] = point1[3] + u * difference[3]; - voxel_center[2] = point1[2] + u * difference[2]; - voxel_center[1] = point1[1] + u * difference[1]; - - const CartesianCoordinate3D x_dim = voxel_center - middle; - - d1 = -inner_product(x_dim, difference) * lor_length; - - } + const CartesianCoordinate3D x = voxel_center - middle; - low_dist = ((proj_data_info_sptr->tof_bin_boundaries_mm[probabilities.get_bin_ptr()->timing_pos_num()].low_lim - d1) * r_sqrt2_gauss_sigma); - high_dist = ((proj_data_info_sptr->tof_bin_boundaries_mm[probabilities.get_bin_ptr()->timing_pos_num()].high_lim - d1) * r_sqrt2_gauss_sigma); + const float d2 = -inner_product(x, diff) * lor_length; - //p1 = (((proj_data_info_sptr->tof_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].low_lim - d1) * r_sqrt2_gauss_sigma) + 4.f) * step; - //p2 = (((proj_data_info_sptr->tof_bin_boundaries_mm[tof_probabilities.get_bin_ptr()->timing_pos_num()].high_lim - d1) * r_sqrt2_gauss_sigma) + 4.f) * step; + low_dist = ((proj_data_info_sptr->tof_bin_boundaries_mm[probabilities.get_bin_ptr()->timing_pos_num()].low_lim - d2) * r_sqrt2_gauss_sigma); + high_dist = ((proj_data_info_sptr->tof_bin_boundaries_mm[probabilities.get_bin_ptr()->timing_pos_num()].high_lim - d2) * r_sqrt2_gauss_sigma); -// if (p1 < 0 || p2 < 0 || -// p1 >= 100000 || p2 >= 100000) -// { -// *element_ptr = ProjMatrixElemsForOneBin::value_type(c, 0.0f); -// continue; -// } - if (low_dist >= 4.f || high_dist >= 4.f || - low_dist <= -4.f || high_dist <= -4.f) + if ((low_dist >= 4.f && high_dist >= 4.f) || + (low_dist <= -4.f && high_dist <= -4.f)) { *element_ptr = ProjMatrixElemsForOneBin::value_type(c, 0.0f); continue; } get_tof_value(low_dist, high_dist, new_value); - //new_value = element_ptr->get_value() *(cache_erf[p2] - cache_erf[p1]); + new_value *= element_ptr->get_value(); *element_ptr = ProjMatrixElemsForOneBin::value_type(c, new_value); } } diff --git a/src/listmode_buildblock/CListModeDataROOT.cxx b/src/listmode_buildblock/CListModeDataROOT.cxx index 4562660f0c..e4baaee9e4 100644 --- a/src/listmode_buildblock/CListModeDataROOT.cxx +++ b/src/listmode_buildblock/CListModeDataROOT.cxx @@ -59,6 +59,9 @@ CListModeDataROOT(const std::string& hroot_filename) this->parser.add_key("Maximum number of non-arc-corrected bins", &this->max_num_non_arccorrected_bins); // end Scanner and physical dimensions. + this->parser.add_key("energy resolution", &this->energy_resolution); + this->parser.add_key("reference energy", &this->reference_energy); + this->parser.add_key("number of TOF time bins", &this->max_num_timing_bins); this->parser.add_key("Size of timing bin (ps)", &this->size_timing_bin); this->parser.add_key("Timing resolution (ps)", &this->timing_resolution); @@ -143,6 +146,8 @@ CListModeDataROOT(const std::string& hroot_filename) /*num_transaxial_crystals_per_singles_unit_v*/ this->root_file_sptr->get_num_trans_crystals_per_singles_unit(), /*num_detector_layers_v*/ 1, + this->energy_resolution, + this->reference_energy, /* maximum number of timing bins */ max_num_timing_bins, /* size of basic TOF bin */ diff --git a/src/recon_buildblock/ProjMatrixByBin.cxx b/src/recon_buildblock/ProjMatrixByBin.cxx index c62d78de87..f666d48af1 100644 --- a/src/recon_buildblock/ProjMatrixByBin.cxx +++ b/src/recon_buildblock/ProjMatrixByBin.cxx @@ -85,16 +85,6 @@ enable_tof(const shared_ptr& _proj_data_info_sptr, const bool v) proj_data_info_sptr = _proj_data_info_sptr; gauss_sigma_in_mm = ProjDataInfo::tof_delta_time_to_mm(proj_data_info_sptr->get_scanner_ptr()->get_timing_resolution()) / 2.355f; r_sqrt2_gauss_sigma = 1.0f/ (gauss_sigma_in_mm * static_cast(sqrt(2.0))); - -// cache_erf.resize(0, 100000); - -// float step = 8.f / 100000.f; -// // cache erf with reasonable sampling -// for (int i = 0 ;i < 100000; ++i) -// { -// float d = -4.0f + i * step; -// cache_erf[i] = 0.5f * erf(d); -// } } } diff --git a/src/test/test_ArcCorrection.cxx b/src/test/test_ArcCorrection.cxx index ea9885bb0d..71e0354839 100644 --- a/src/test/test_ArcCorrection.cxx +++ b/src/test/test_ArcCorrection.cxx @@ -189,7 +189,7 @@ ArcCorrectionTests::run_tests_tof() shared_ptr proj_data_info_ptr( ProjDataInfo::ProjDataInfoGE(scanner_ptr, - /*max_delta*/ 10,/*views*/ 224, /*tang_pos*/ 357, /*arc_corrected*/ false, /*tof_mashing_factor*/ 39)); + /*max_delta*/ 10,/*views*/ 224, /*tang_pos*/ 357, /*arc_corrected*/ false, /*tof_mashing_factor*/ 116)); cerr << "Using default range and bin-size\n"; { diff --git a/src/test/test_time_of_flight.cxx b/src/test/test_time_of_flight.cxx index 128160a0b9..f20449e6cd 100644 --- a/src/test/test_time_of_flight.cxx +++ b/src/test/test_time_of_flight.cxx @@ -14,11 +14,7 @@ GNU Lesser General Public License for more details. See STIR/LICENSE.txt for details */ -/*! - \ingroup test - \brief Test class for Time-Of-Flight - \author Nikos Efthimiou -*/ + #include "stir/ProjDataInfoCylindricalNoArcCorr.h" #include "stir/recon_buildblock/ProjMatrixByBin.h" #include "stir/recon_buildblock/ProjMatrixByBinUsingRayTracing.h" @@ -48,6 +44,7 @@ START_NAMESPACE_STIR //! A helper class to keep the combination of a view, a segment and //! a key tight. //! \author Nikos Efthimiou +//! class cache_index{ public: cache_index() { @@ -88,6 +85,21 @@ class FloatFloat{ float float2; }; +/*! + \ingroup test + \brief Test class for Time Of Flight + \author Nikos Efthimiou + + + The following 2 tests are performed: + + *. Compare the ProjDataInfo of the GE Signa scanner to known values. + + *. Check that the sum of the TOF LOR is the same as the non TOF. + + \warning If you change the mashing factor the test_tof_proj_data_info() will fail. + \warning The execution time strongly depends on the value of the TOF mashing factor +*/ class TOF_Tests : public RunTests { public: @@ -97,19 +109,21 @@ class TOF_Tests : public RunTests void test_tof_proj_data_info(); - void test_tof_geometry_1(); - - void test_tof_geometry_2(); - //! This checks peaks a specific bin, finds the LOR and applies all the - //! kernels of all available timing positions. - void test_tof_kernel_application(); + //! kernels of all available timing positions. Then check if the sum + //! of the TOF bins is equal to the non-TOF LOR. + void test_tof_kernel_application(bool export_to_file); + //! Exports the nonTOF LOR to a file indicated by the current_id value + //! in the filename. void export_lor(ProjMatrixElemsForOneBin& probabilities, const CartesianCoordinate3D& point1, const CartesianCoordinate3D& point2,int current_id); + //! Exports the TOF LOR. The TOFid is indicated in the fileName. + //! Only the common elements with the nonTOF LOR will be written in the file. + //! Although changing that is straight forward. void export_lor(ProjMatrixElemsForOneBin& probabilities, const CartesianCoordinate3D& point1, @@ -118,8 +132,14 @@ class TOF_Tests : public RunTests shared_ptr test_scanner_sptr; shared_ptr test_proj_data_info_sptr; + + shared_ptr test_nonTOF_scanner_sptr; + shared_ptr test_nonTOF_proj_data_info_sptr; + shared_ptr > test_discretised_density_sptr; shared_ptr test_proj_matrix_sptr; + shared_ptr test_nonTOF_proj_matrix_sptr; + shared_ptr projector_pair_sptr; shared_ptr symmetries_used_sptr; }; @@ -129,6 +149,7 @@ TOF_Tests::run_tests() { // New Scanner test_scanner_sptr.reset(new Scanner(Scanner::PETMR_Signa)); + test_nonTOF_scanner_sptr.reset(new Scanner(Scanner::PETMR_Signa_nonTOF)); // New Proj_Data_Info const int test_tof_mashing_factor = 39; // to have 9 TOF bins (381/39=9) @@ -139,8 +160,14 @@ TOF_Tests::run_tests() /* arc_correction*/false)); test_proj_data_info_sptr->set_tof_mash_factor(test_tof_mashing_factor); + test_nonTOF_proj_data_info_sptr.reset(ProjDataInfo::ProjDataInfoCTI(test_nonTOF_scanner_sptr, + 1,test_scanner_sptr->get_num_rings() -1, + test_scanner_sptr->get_num_detectors_per_ring()/2, + test_scanner_sptr->get_max_num_non_arccorrected_bins(), + /* arc_correction*/false)); + test_tof_proj_data_info(); -// test_tof_geometry_1(); + // test_tof_geometry_1(); // New Discretised Density test_discretised_density_sptr.reset( new VoxelsOnCartesianGrid (*test_proj_data_info_sptr, 1.f, @@ -150,23 +177,13 @@ TOF_Tests::run_tests() test_proj_matrix_sptr.reset(new ProjMatrixByBinUsingRayTracing()); dynamic_cast(test_proj_matrix_sptr.get())->set_num_tangential_LORs(1); dynamic_cast(test_proj_matrix_sptr.get())->set_up(test_proj_data_info_sptr, test_discretised_density_sptr); -// test_proj_matrix_sptr->enable_tof(test_proj_data_info_sptr); - - shared_ptr forward_projector_ptr( - new ForwardProjectorByBinUsingProjMatrixByBin(test_proj_matrix_sptr)); - shared_ptr back_projector_ptr( - new BackProjectorByBinUsingProjMatrixByBin(test_proj_matrix_sptr)); - projector_pair_sptr.reset( - new ProjectorByBinPairUsingSeparateProjectors(forward_projector_ptr, back_projector_ptr)); - projector_pair_sptr->set_up(test_proj_data_info_sptr, test_discretised_density_sptr); + test_nonTOF_proj_matrix_sptr.reset(new ProjMatrixByBinUsingRayTracing()); + dynamic_cast(test_nonTOF_proj_matrix_sptr.get())->set_num_tangential_LORs(1); + dynamic_cast(test_nonTOF_proj_matrix_sptr.get())->set_up(test_nonTOF_proj_data_info_sptr, test_discretised_density_sptr); - symmetries_used_sptr.reset(projector_pair_sptr->get_symmetries_used()->clone()); - - // Deactivated it now because it takes a long time to finish. - // test_cache(); - - test_tof_kernel_application(); + // Switch to true in order to export the LORs at files in the current directory + test_tof_kernel_application(false); } void @@ -176,8 +193,11 @@ TOF_Tests::test_tof_proj_data_info() const int num_timing_positions = 9; float correct_width_of_tof_bin = test_scanner_sptr->get_size_of_timing_pos() * test_proj_data_info_sptr->get_tof_mash_factor() * 0.299792458f/2; - float correct_timing_locations[num_timing_positions] = {-360.201f/2, -280.156f/2, -200.111f/2, -120.067f/2, -40.022f/2, 40.022f/2, - 120.067f/2, 200.111f/2, 280.156f/2}; + float correct_timing_locations[num_timing_positions] = {-360.201f/2 + correct_width_of_tof_bin/2, -280.156f/2 + correct_width_of_tof_bin/2, + -200.111f/2 + correct_width_of_tof_bin/2, -120.067f/2 + correct_width_of_tof_bin/2, + 0.0f, 40.022f/2 + correct_width_of_tof_bin/2, + 120.067f/2 + correct_width_of_tof_bin/2, 200.111f/2 + correct_width_of_tof_bin/2, + 280.156f/2+ correct_width_of_tof_bin/2}; check_if_equal(correct_tof_mashing_factor, test_proj_data_info_sptr->get_tof_mash_factor(), "Different TOF mashing factor."); @@ -208,131 +228,43 @@ TOF_Tests::test_tof_proj_data_info() } void -TOF_Tests::test_tof_geometry_1() -{ - - float correct_scanner_length = test_scanner_sptr->get_ring_spacing() * - test_scanner_sptr->get_num_rings() - test_proj_data_info_sptr->get_sampling_in_m(Bin(0,0,0,0,0)); - - CartesianCoordinate3D ez1_coord0, ez2_coord0, ez3_coord0, ez4_coord0, ez5_coord0; - //ProjDataInfoCylindrical* proj_data_ptr = - // dynamic_cast (test_proj_data_info_sptr.get()); - - int mid_seg = test_proj_data_info_sptr->get_num_segments()/2; - - int mid_axial_0 = (test_proj_data_info_sptr->get_min_axial_pos_num(0) + - test_proj_data_info_sptr->get_max_axial_pos_num(0)) /2; - - int mid_axial_mid_seg = (test_proj_data_info_sptr->get_min_axial_pos_num(mid_seg) + - test_proj_data_info_sptr->get_max_axial_pos_num(mid_seg)) /2; - - // Some easy to validate bins: - Bin ez1_bin(0,0,0,0,0,1.f); - Bin ez2_bin(0,0,mid_axial_0,0,0,1.f); - Bin ez3_bin(mid_seg,0,mid_axial_mid_seg,0,0,1.f); - Bin ez4_bin(0,0,test_proj_data_info_sptr->get_min_axial_pos_num(0),0,0,1.f); - Bin ez5_bin(0,0,test_proj_data_info_sptr->get_max_axial_pos_num(0),0,0,1.f); - - // Get middle points -// proj_data_ptr->get_LOR_middle_point(ez1_coord0, ez1_bin); -// proj_data_ptr->get_LOR_middle_point(ez2_coord0, ez2_bin); -// proj_data_ptr->get_LOR_middle_point(ez3_coord0, ez3_bin); -// proj_data_ptr->get_LOR_middle_point(ez4_coord0, ez4_bin); -// proj_data_ptr->get_LOR_middle_point(ez5_coord0, ez5_bin); - - // axial ez1 && ez4 should be -scanner_length/2.f - check_if_equal(static_cast(ez1_coord0.z()), - static_cast(-correct_scanner_length/2.f), - "Min axial positions of mid-points don't look " - "reasonable."); - - check_if_equal(static_cast(ez4_coord0.z()), - static_cast(-correct_scanner_length/2.f), - "Min axial positions of mid-points don't look " - "reasonable."); - - // axial ez2 should be -ring_spacing/2 - check_if_equal(static_cast(ez2_coord0.z()), - static_cast(-test_scanner_sptr->get_ring_spacing()/2.f), - "[1]Central axial positions of mid-points don't look " - "reasonable."); - - // axial ez3 should be 0 - check_if_equal(static_cast(ez3_coord0.z()), - static_cast(-test_proj_data_info_sptr->get_m(Bin(mid_seg,0,0,0,0))), - "[2]Central axial positions of mid-points don't look " - "reasonable."); - - // axial ez5 should be scanner_length/2.f - check_if_equal(static_cast(ez5_coord0.z()), - static_cast(correct_scanner_length/2.f), - "Max axial positions of mid-points don't look " - "reasonable."); - - //TODO: more tests for X and Y -} - -void -TOF_Tests::test_tof_geometry_2() -{ - //float correct_scanner_length = test_scanner_sptr->get_ring_spacing() * - // test_scanner_sptr->get_num_rings() - test_proj_data_info_sptr->get_sampling_in_m(Bin(0,0,0,0,0)); - - CartesianCoordinate3D ez1_coord1, ez2_coord1, ez3_coord1, ez4_coord1, ez5_coord1; - CartesianCoordinate3D ez1_coord2, ez2_coord2, ez3_coord2, ez4_coord2, ez5_coord2; - //ProjDataInfoCylindrical* proj_data_ptr = - // dynamic_cast (test_proj_data_info_sptr.get()); - - int mid_seg = test_proj_data_info_sptr->get_num_segments()/2; - - int mid_axial_0 = (test_proj_data_info_sptr->get_min_axial_pos_num(0) + - test_proj_data_info_sptr->get_max_axial_pos_num(0)) /2; - - int mid_axial_mid_seg = (test_proj_data_info_sptr->get_min_axial_pos_num(mid_seg) + - test_proj_data_info_sptr->get_max_axial_pos_num(mid_seg)) /2; - - // Some easy to validate bins: - Bin ez1_bin(0,0,0,0,0,1.f); - Bin ez2_bin(0,0,mid_axial_0,0,0,1.f); - Bin ez3_bin(mid_seg,0,mid_axial_mid_seg,0,0,1.f); - Bin ez4_bin(0,0,test_proj_data_info_sptr->get_min_axial_pos_num(0),0,0,1.f); - Bin ez5_bin(0,0,test_proj_data_info_sptr->get_max_axial_pos_num(0),0,0,1.f); - - // Get middle points -// proj_data_ptr->get_LOR_as_two_points(ez1_coord1,ez1_coord2, ez1_bin); -// proj_data_ptr->get_LOR_as_two_points(ez2_coord1,ez2_coord2, ez2_bin); -// proj_data_ptr->get_LOR_as_two_points(ez3_coord1,ez3_coord2, ez3_bin); -// proj_data_ptr->get_LOR_as_two_points(ez4_coord1,ez4_coord2, ez4_bin); -// proj_data_ptr->get_LOR_as_two_points(ez5_coord1,ez5_coord2, ez5_bin); - - // TESTS TO COME. - - // TEST IF THE FLIPING IS OK. -} - -void -TOF_Tests::test_tof_kernel_application() +TOF_Tests::test_tof_kernel_application(bool print_to_file) { int seg_num = 0; int view_num = 0; int axial_num = 0; int tang_num = 0; + float nonTOF_val = 0.0; + float TOF_val = 0.0; + ProjMatrixElemsForOneBin proj_matrix_row; + ProjMatrixElemsForOneBin sum_tof_proj_matrix_row; + HighResWallClockTimer t; std::vector times_of_tofing; ProjDataInfoCylindrical* proj_data_ptr = dynamic_cast (test_proj_data_info_sptr.get()); + ProjDataInfoCylindrical* proj_data_nonTOF_ptr = + dynamic_cast (test_nonTOF_proj_data_info_sptr.get()); + + LORInAxialAndNoArcCorrSinogramCoordinates lor; + Bin this_bin(seg_num, view_num, axial_num, tang_num, 1.f); t.reset(); t.start(); - test_proj_matrix_sptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, this_bin); + test_nonTOF_proj_matrix_sptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, this_bin); t.stop(); + std::cerr<<"Execution time for nonTOF: "<get_LOR(lor, this_bin); + LORAs2Points lor2(lor); + + if (print_to_file) + export_lor(proj_matrix_row, + lor2.p1(), lor2.p2(), 500000000); for (int timing_num = test_proj_data_info_sptr->get_min_tof_pos_num(); timing_num <= test_proj_data_info_sptr->get_max_tof_pos_num(); ++ timing_num) @@ -342,27 +274,101 @@ TOF_Tests::test_tof_kernel_application() t.reset(); t.start(); test_proj_matrix_sptr->get_proj_matrix_elems_for_one_bin(new_proj_matrix_row, - bin); + bin); t.stop(); times_of_tofing.push_back(t.value()); -// export_lor(new_proj_matrix_row, -// lor_point_1, lor_point_2, timing_num, -// proj_matrix_row); + + if (print_to_file) + export_lor(new_proj_matrix_row, + lor2.p1(), lor2.p2(), timing_num, + proj_matrix_row); + + + if (sum_tof_proj_matrix_row.size() > 0) + { + ProjMatrixElemsForOneBin::iterator element_ptr = new_proj_matrix_row.begin(); + while (element_ptr != new_proj_matrix_row.end()) + { + + ProjMatrixElemsForOneBin::iterator sum_element_ptr = sum_tof_proj_matrix_row.begin(); + bool found = false; + while(sum_element_ptr != sum_tof_proj_matrix_row.end()) + { + if(element_ptr->get_coords() == sum_element_ptr->get_coords()) + { + float new_value = element_ptr->get_value() + sum_element_ptr->get_value(); + *sum_element_ptr = ProjMatrixElemsForOneBin::value_type(element_ptr->get_coords(), new_value); + found = true; + break; + } + ++sum_element_ptr; + } + if (!found) + { + sum_tof_proj_matrix_row.push_back(ProjMatrixElemsForOneBin::value_type(element_ptr->get_coords(), + element_ptr->get_value())); + break; + } + ++element_ptr; + } + + } + else + { + ProjMatrixElemsForOneBin::iterator element_ptr = new_proj_matrix_row.begin(); + while (element_ptr != new_proj_matrix_row.end()) + { + sum_tof_proj_matrix_row.push_back(ProjMatrixElemsForOneBin::value_type(element_ptr->get_coords(), + element_ptr->get_value())); + ++element_ptr; + } + } + + } + + // Get value of nonTOF LOR, for central voxels only + + { + ProjMatrixElemsForOneBin::iterator element_ptr = proj_matrix_row.begin(); + while (element_ptr != proj_matrix_row.end()) + { + if (element_ptr->get_value() > nonTOF_val) + nonTOF_val = element_ptr->get_value(); + ++element_ptr; + } + } + + // Get value of TOF LOR, for central voxels only + + { + ProjMatrixElemsForOneBin::iterator element_ptr = sum_tof_proj_matrix_row.begin(); + while (element_ptr != sum_tof_proj_matrix_row.end()) + { + if (element_ptr->get_value() > TOF_val) + TOF_val = element_ptr->get_value(); + ++element_ptr; + } } - double mean = 0.0; - for (unsigned i = 0; i < times_of_tofing.size(); i++) - mean += times_of_tofing.at(i); - mean /= (times_of_tofing.size()); + check_if_equal(static_cast(nonTOF_val), static_cast(TOF_val), + "Sum over nonTOF LOR does not match sum over TOF LOR."); - double s=0.0; - for (unsigned i = 0; i < times_of_tofing.size(); i++) - s += (times_of_tofing.at(i) - mean) * (times_of_tofing.at(i) - mean) / (times_of_tofing.size()-1); + { + double mean = 0.0; + for (unsigned i = 0; i < times_of_tofing.size(); i++) + mean += times_of_tofing.at(i); + + mean /= (times_of_tofing.size()); + + double s=0.0; + for (unsigned i = 0; i < times_of_tofing.size(); i++) + s += (times_of_tofing.at(i) - mean) * (times_of_tofing.at(i) - mean) / (times_of_tofing.size()-1); - s = std::sqrt(s); + s = std::sqrt(s); + std::cerr<<"Execution time for TOF: "<& point1, - const CartesianCoordinate3D& point2, int current_id) + const CartesianCoordinate3D& point2, + int current_id) { - std::ofstream myfile; - std::string file_name = "glor_" + boost::lexical_cast(current_id) + ".txt"; - myfile.open (file_name.c_str()); + std::ofstream myfile; + std::string file_name = "glor_" + boost::lexical_cast(current_id) + ".txt"; + myfile.open (file_name.c_str()); - CartesianCoordinate3D voxel_center; + CartesianCoordinate3D voxel_center; - std::vector lor_to_export; - lor_to_export.reserve(probabilities.size()); + std::vector lor_to_export; + lor_to_export.reserve(probabilities.size()); - ProjMatrixElemsForOneBin::iterator element_ptr = probabilities.begin(); - while (element_ptr != probabilities.end()) - { - voxel_center = - test_discretised_density_sptr->get_physical_coordinates_for_indices (element_ptr->get_coords()); + const CartesianCoordinate3D middle = (point1 + point2)*0.5f; + const CartesianCoordinate3D diff = point2 - middle; - if(voxel_center.z() == 0.f) - { - project_point_on_a_line(point1, point2, voxel_center ); + const float lor_length = 1.f / (std::sqrt(diff.x() * diff.x() + + diff.y() * diff.y() + + diff.z() * diff.z())); - float d1 = std::sqrt((point1.x() - voxel_center.x()) *(point1.x() - voxel_center.x()) + - (point1.y() - voxel_center.y()) *(point1.y() - voxel_center.y()) + - (point1.z() - voxel_center.z()) *(point1.z() - voxel_center.z())); + ProjMatrixElemsForOneBin::iterator element_ptr = probabilities.begin(); + while (element_ptr != probabilities.end()) + { + voxel_center = + test_discretised_density_sptr->get_physical_coordinates_for_indices (element_ptr->get_coords()); - float d2 = std::sqrt( (point2.x() - voxel_center.x()) *(point2.x() - voxel_center.x()) + - (point2.y() - voxel_center.y()) *(point2.y() - voxel_center.y()) + - (point2.z() - voxel_center.z()) *(point2.z() - voxel_center.z())); +// if(voxel_center.z() == 0.f) + { + project_point_on_a_line(point1, point2, voxel_center ); + const CartesianCoordinate3D x = voxel_center - middle; - float d12 = (d2 - d1) * 0.5f; + const float d2 = -inner_product(x, diff) * lor_length; - std::cerr<< voxel_center.x() << " " << voxel_center.y() << " " << voxel_center.z() << " " << - d1 << " " << d2 << " " << d12 <get_value(); - lor_to_export.push_back(tmp); -} - ++element_ptr; - } +// std::cerr<< voxel_center.x() << " " << voxel_center.y() << " " << voxel_center.z() << " " << +// d1 << " " << d2 << " " << d12 << " " << element_ptr->get_value() <get_value(); + lor_to_export.push_back(tmp); + } + ++element_ptr; + } for (unsigned int i = 0; i < lor_to_export.size(); i++) - myfile << lor_to_export.at(i).float1 << " " << lor_to_export.at(i).float2 << std::endl; + myfile << lor_to_export.at(i).float1 << " " << lor_to_export.at(i).float2 << std::endl; - myfile << std::endl; - myfile.close(); + myfile << std::endl; + myfile.close(); } void @@ -427,65 +435,61 @@ export_lor(ProjMatrixElemsForOneBin& probabilities, const CartesianCoordinate3D& point2, int current_id, ProjMatrixElemsForOneBin& template_probabilities) { - std::ofstream myfile; - std::string file_name = "glor_" + boost::lexical_cast(current_id) + ".txt"; - myfile.open (file_name.c_str()); - - CartesianCoordinate3D voxel_center; + std::ofstream myfile; + std::string file_name = "glor_" + boost::lexical_cast(current_id) + ".txt"; + myfile.open (file_name.c_str()); - std::vector lor_to_export; - lor_to_export.reserve(template_probabilities.size()); + const CartesianCoordinate3D middle = (point1 + point2)*0.5f; + const CartesianCoordinate3D diff = point2 - middle; - ProjMatrixElemsForOneBin::iterator tmpl_element_ptr = template_probabilities.begin(); - while (tmpl_element_ptr != template_probabilities.end()) - { - voxel_center = - test_discretised_density_sptr->get_physical_coordinates_for_indices (tmpl_element_ptr->get_coords()); - if(voxel_center.z() == 0.f) - { - project_point_on_a_line(point1, point2, voxel_center ); - - float d1 = std::sqrt((point1.x() - voxel_center.x()) *(point1.x() - voxel_center.x()) + - (point1.y() - voxel_center.y()) *(point1.y() - voxel_center.y()) + - (point1.z() - voxel_center.z()) *(point1.z() - voxel_center.z())); - - float d2 = std::sqrt( (point2.x() - voxel_center.x()) *(point2.x() - voxel_center.x()) + - (point2.y() - voxel_center.y()) *(point2.y() - voxel_center.y()) + - (point2.z() - voxel_center.z()) *(point2.z() - voxel_center.z())); - - float d12 = (d2 - d1) * 0.5f; - - FloatFloat tmp; - tmp.float1 = d12; - - ProjMatrixElemsForOneBin::iterator element_ptr = probabilities.begin(); - bool found = false; + const float lor_length = 1.f / (std::sqrt(diff.x() * diff.x() + + diff.y() * diff.y() + + diff.z() * diff.z())); - while (element_ptr != probabilities.end()) - { - if (element_ptr->get_coords() == tmpl_element_ptr->get_coords()) - { - tmp.float2 = element_ptr->get_value(); - found = true; - break; - } - ++element_ptr; - } + CartesianCoordinate3D voxel_center; - if (!found) - tmp.float2 = 0.f; + std::vector lor_to_export; + lor_to_export.reserve(template_probabilities.size()); - - lor_to_export.push_back(tmp); -} - ++tmpl_element_ptr; - } + ProjMatrixElemsForOneBin::iterator tmpl_element_ptr = template_probabilities.begin(); + while (tmpl_element_ptr != template_probabilities.end()) + { + voxel_center = + test_discretised_density_sptr->get_physical_coordinates_for_indices (tmpl_element_ptr->get_coords()); +// if(voxel_center.z() == 0.f) + { + project_point_on_a_line(point1, point2, voxel_center ); + + const CartesianCoordinate3D x = voxel_center - middle; + + const float d2 = -inner_product(x, diff) * lor_length; + + FloatFloat tmp; + tmp.float1 = d2; + + ProjMatrixElemsForOneBin::iterator element_ptr = probabilities.begin(); + bool found = false; + + while (element_ptr != probabilities.end()) + { + if (element_ptr->get_coords() == tmpl_element_ptr->get_coords()) + { + tmp.float2 = element_ptr->get_value(); + found = true; + lor_to_export.push_back(tmp); + break; + } + ++element_ptr; + } + } + ++tmpl_element_ptr; + } for (unsigned int i = 0; i < lor_to_export.size(); i++) - myfile << lor_to_export.at(i).float1 << " " << lor_to_export.at(i).float2 << std::endl; + myfile << lor_to_export.at(i).float1 << " " << lor_to_export.at(i).float2 << std::endl; - myfile << std::endl; - myfile.close(); + myfile << std::endl; + myfile.close(); } END_NAMESPACE_STIR @@ -493,7 +497,7 @@ END_NAMESPACE_STIR int main() { USING_NAMESPACE_STIR - TOF_Tests tests; + TOF_Tests tests; tests.run_tests(); return tests.main_return_value(); } From f9d87a50e24ed972a2855207e92e7513ad1275b2 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Thu, 6 Dec 2018 16:08:12 +0000 Subject: [PATCH 115/170] fix application of tof mashing TOF mashing can be applied in listmode and projection reconstruction. --- src/buildblock/ProjDataInfo.cxx | 3 +++ src/include/stir/ProjDataInfo.h | 7 ++++++ src/include/stir/ProjDataInfo.inl | 24 ++++++++++++++++++++- src/listmode_buildblock/CListRecordROOT.cxx | 3 ++- 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/buildblock/ProjDataInfo.cxx b/src/buildblock/ProjDataInfo.cxx index 117a671ff8..7f996fddbc 100644 --- a/src/buildblock/ProjDataInfo.cxx +++ b/src/buildblock/ProjDataInfo.cxx @@ -203,6 +203,9 @@ ProjDataInfo::set_tof_mash_factor(const int new_num) min_tof_pos_num = - (scanner_ptr->get_num_max_of_timing_poss() / tof_mash_factor)/2; max_tof_pos_num = min_tof_pos_num + (scanner_ptr->get_num_max_of_timing_poss() / tof_mash_factor) -1; + min_unmashed_tof_pos_num = - (scanner_ptr->get_num_max_of_timing_poss())/2; + max_unmashed_tof_pos_num = min_tof_pos_num + (scanner_ptr->get_num_max_of_timing_poss()) -1; + num_tof_bins = max_tof_pos_num - min_tof_pos_num +1 ; // Ensure that we have a central tof bin. diff --git a/src/include/stir/ProjDataInfo.h b/src/include/stir/ProjDataInfo.h index 94e562d19b..3a94c12677 100644 --- a/src/include/stir/ProjDataInfo.h +++ b/src/include/stir/ProjDataInfo.h @@ -221,6 +221,9 @@ class ProjDataInfo inline int get_num_tangential_poss() const; //! Get number of tof bins inline int get_tof_bin(const double& delta) const; + + inline int get_unmashed_tof_bin(const double& delta) const; + //! Get number of TOF bins inline int get_num_tof_poss() const; //! Get minimum segment number @@ -450,6 +453,10 @@ class ProjDataInfo int min_tof_pos_num; //! Maximum TOF pos int max_tof_pos_num; + //! Minimum TOF pos regardless of the mashing factor + int min_unmashed_tof_pos_num; + //! Maximum TOF pos regardless of the mashing factor + int max_unmashed_tof_pos_num; //! TOF mash factor. int tof_mash_factor; //! Finally (with any mashing factor) TOF bin increament. diff --git a/src/include/stir/ProjDataInfo.inl b/src/include/stir/ProjDataInfo.inl index fa4cf80266..fce8226496 100644 --- a/src/include/stir/ProjDataInfo.inl +++ b/src/include/stir/ProjDataInfo.inl @@ -90,7 +90,7 @@ ProjDataInfo::get_tof_bin(const double& delta) const if (!is_tof_data()) return 0; - for (int i = min_tof_pos_num; i <= max_tof_pos_num; i++) + for (int i = min_tof_pos_num; i <= max_tof_pos_num; ++i) { if (delta >= tof_bin_boundaries_ps[i].low_lim && delta < tof_bin_boundaries_ps[i].high_lim) @@ -101,6 +101,28 @@ ProjDataInfo::get_tof_bin(const double& delta) const return 0; } +int +ProjDataInfo::get_unmashed_tof_bin(const double& delta) const +{ + if (!is_tof_data()) + return 0; + + float d = tof_bin_boundaries_ps[min_tof_pos_num].low_lim; + float dd = tof_bin_boundaries_ps[max_tof_pos_num].high_lim; + + if (delta < tof_bin_boundaries_ps[min_tof_pos_num].low_lim && + delta > tof_bin_boundaries_ps[max_tof_pos_num].high_lim) + { + // TODO handle differently + warning(boost::format("TOF delta time %g out of range") % delta); + return 0; + } + + return delta * get_scanner_ptr()->get_num_max_of_timing_poss() / + get_coincidence_window_in_pico_sec(); + +} + int ProjDataInfo::get_tof_mash_factor() const { return tof_mash_factor; } diff --git a/src/listmode_buildblock/CListRecordROOT.cxx b/src/listmode_buildblock/CListRecordROOT.cxx index 5a526bb5e3..387c32454e 100644 --- a/src/listmode_buildblock/CListRecordROOT.cxx +++ b/src/listmode_buildblock/CListRecordROOT.cxx @@ -51,7 +51,8 @@ void CListEventROOT::get_detection_position(DetectionPositionPair<>& _det_pos) c _det_pos.pos1() = det1; _det_pos.pos2() = det2; - _det_pos.timing_pos() = this->get_uncompressed_proj_data_info_sptr()->get_tof_bin(delta_time); +// _det_pos.timing_pos() = this->get_uncompressed_proj_data_info_sptr()->get_tof_bin(delta_time); + _det_pos.timing_pos() = this->get_uncompressed_proj_data_info_sptr()->get_unmashed_tof_bin(delta_time); } void CListEventROOT::set_detection_position(const DetectionPositionPair<>&) From 4236a05a02f76bdda81625d7a646b50e48f78b2a Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Thu, 6 Dec 2018 16:26:05 +0000 Subject: [PATCH 116/170] remove two control variables. --- src/include/stir/ProjDataInfo.inl | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/include/stir/ProjDataInfo.inl b/src/include/stir/ProjDataInfo.inl index fce8226496..415aee28b0 100644 --- a/src/include/stir/ProjDataInfo.inl +++ b/src/include/stir/ProjDataInfo.inl @@ -107,9 +107,6 @@ ProjDataInfo::get_unmashed_tof_bin(const double& delta) const if (!is_tof_data()) return 0; - float d = tof_bin_boundaries_ps[min_tof_pos_num].low_lim; - float dd = tof_bin_boundaries_ps[max_tof_pos_num].high_lim; - if (delta < tof_bin_boundaries_ps[min_tof_pos_num].low_lim && delta > tof_bin_boundaries_ps[max_tof_pos_num].high_lim) { From 0d0db6f481625dabfd27ce12628620ed37ab9bc1 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Thu, 6 Dec 2018 16:41:12 +0000 Subject: [PATCH 117/170] Reduce the scanner in test_ArcCorrection This test fails due to time out --- src/test/test_ArcCorrection.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/test_ArcCorrection.cxx b/src/test/test_ArcCorrection.cxx index 71e0354839..57d9e1ac57 100644 --- a/src/test/test_ArcCorrection.cxx +++ b/src/test/test_ArcCorrection.cxx @@ -189,7 +189,7 @@ ArcCorrectionTests::run_tests_tof() shared_ptr proj_data_info_ptr( ProjDataInfo::ProjDataInfoGE(scanner_ptr, - /*max_delta*/ 10,/*views*/ 224, /*tang_pos*/ 357, /*arc_corrected*/ false, /*tof_mashing_factor*/ 116)); + /*max_delta*/ 5,/*views*/ 112, /*tang_pos*/ 357, /*arc_corrected*/ false, /*tof_mashing_factor*/ 116)); cerr << "Using default range and bin-size\n"; { From 728699d0abfdcf72148cfb57f8ad5d0bf29279ce Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Thu, 6 Dec 2018 16:47:33 +0000 Subject: [PATCH 118/170] Reduced scanner in DataSymmetriesForBins_PET_CartesianGridTests This test was prone to timeouts --- .../test_DataSymmetriesForBins_PET_CartesianGrid.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/recon_test/test_DataSymmetriesForBins_PET_CartesianGrid.cxx b/src/recon_test/test_DataSymmetriesForBins_PET_CartesianGrid.cxx index e6edd0f879..6127c18b46 100644 --- a/src/recon_test/test_DataSymmetriesForBins_PET_CartesianGrid.cxx +++ b/src/recon_test/test_DataSymmetriesForBins_PET_CartesianGrid.cxx @@ -723,11 +723,11 @@ DataSymmetriesForBins_PET_CartesianGridTests::run_tests() proj_data_info_sptr.reset( ProjDataInfo::ProjDataInfoCTI(scanner_sptr, /*span=*/11, - /*max_delta=*/scanner_sptr->get_num_rings()-1, + /*max_delta=*/5, /*num_views=*/scanner_sptr->get_num_detectors_per_ring()/8, /*num_tang_poss=*/64, /*arc_corrected*/false, - /*tof_mashing*/100)); + /*tof_mashing*/116)); run_tests_for_1_projdata(proj_data_info_sptr); From 2a6d383cbb2b7064fa8b2c4ab114df348a762943 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Tue, 29 Jan 2019 04:12:43 +0000 Subject: [PATCH 119/170] comments1 --- ..._test_time_of_fligh.sh => run_test_time_of_flight.sh} | 0 scripts/plot_TOF_bins.m | 2 +- src/include/stir/ProjDataInfo.h | 9 ++++----- src/include/stir/ProjDataInfo.inl | 4 ++-- 4 files changed, 7 insertions(+), 8 deletions(-) rename recon_test_pack/{run_test_time_of_fligh.sh => run_test_time_of_flight.sh} (100%) diff --git a/recon_test_pack/run_test_time_of_fligh.sh b/recon_test_pack/run_test_time_of_flight.sh similarity index 100% rename from recon_test_pack/run_test_time_of_fligh.sh rename to recon_test_pack/run_test_time_of_flight.sh diff --git a/scripts/plot_TOF_bins.m b/scripts/plot_TOF_bins.m index b7ed40758e..d566be56db 100644 --- a/scripts/plot_TOF_bins.m +++ b/scripts/plot_TOF_bins.m @@ -3,7 +3,7 @@ % Nikos Efthimiou. 2018/11/01 % University of Hull -%This scripts loads LOR files, exported by test_time_of_flight from the disk and plots them. +%This scripts loads LOR files, exported by test_time_of_flight to the disk and plots them. %% clc; clear all; %Path to TOF files. diff --git a/src/include/stir/ProjDataInfo.h b/src/include/stir/ProjDataInfo.h index 3a94c12677..486fdd7edf 100644 --- a/src/include/stir/ProjDataInfo.h +++ b/src/include/stir/ProjDataInfo.h @@ -219,11 +219,10 @@ class ProjDataInfo inline int get_num_views() const; //! Get number of tangential positions inline int get_num_tangential_poss() const; - //! Get number of tof bins - inline int get_tof_bin(const double& delta) const; - - inline int get_unmashed_tof_bin(const double& delta) const; - + //! Get number of tof bin for a given time difference + inline int get_tof_bin(const double delta) const; + //! Get number of tof bin for a given time difference, ignoring the TOF mashing factor + inline int get_unmashed_tof_bin(const double delta) const; //! Get number of TOF bins inline int get_num_tof_poss() const; //! Get minimum segment number diff --git a/src/include/stir/ProjDataInfo.inl b/src/include/stir/ProjDataInfo.inl index 415aee28b0..3e7749762d 100644 --- a/src/include/stir/ProjDataInfo.inl +++ b/src/include/stir/ProjDataInfo.inl @@ -85,7 +85,7 @@ ProjDataInfo::get_num_tof_poss() const { return num_tof_bins; } int -ProjDataInfo::get_tof_bin(const double& delta) const +ProjDataInfo::get_tof_bin(const double delta) const { if (!is_tof_data()) return 0; @@ -102,7 +102,7 @@ ProjDataInfo::get_tof_bin(const double& delta) const } int -ProjDataInfo::get_unmashed_tof_bin(const double& delta) const +ProjDataInfo::get_unmashed_tof_bin(const double delta) const { if (!is_tof_data()) return 0; From 3dae02c84c7059f5abcd195a76a2a050565ea2a1 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Fri, 8 Feb 2019 04:09:15 +0000 Subject: [PATCH 120/170] Application of comments --- src/CMakeLists.txt | 12 ++------- ...putStreamFromROOTFileForCylindricalPET.cxx | 5 ++-- src/IO/InterfileHeader.cxx | 20 +++++++-------- src/buildblock/ML_norm.cxx | 22 +++++++++++----- src/buildblock/ProjDataInfo.cxx | 25 +++++++++++++++++++ src/include/stir/ProjDataInfo.h | 6 ++++- src/include/stir/ProjDataInfo.inl | 20 +++++++-------- src/include/stir/common.h | 5 ++++ 8 files changed, 75 insertions(+), 40 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 90e262948c..381407c7a5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -33,6 +33,8 @@ option(BUILD_EXECUTABLES option(BUILD_SHARED_LIBS "Use shared libraries" OFF) +### Settings for external libraries + if (LLN_FOUND) set(HAVE_ECAT ON) message(STATUS "ECAT support enabled.") @@ -66,16 +68,6 @@ else() message(STATUS "RDF support disabled.") endif() -if (HDF5_FOUND) - set(HAVE_HDF5 ON) - message(STATUS "HDF5 support enabled.") - add_definitions(-D HAVE_HDF5) - include_directories(${HDF5_INCLUDE_DIRS}) - -else() - message(STATUS "HDF5 support disabled.") -endif() - if (ITK_FOUND) message(STATUS "ITK libraries added.") diff --git a/src/IO/InputStreamFromROOTFileForCylindricalPET.cxx b/src/IO/InputStreamFromROOTFileForCylindricalPET.cxx index c8417af6ae..c2fb22df77 100644 --- a/src/IO/InputStreamFromROOTFileForCylindricalPET.cxx +++ b/src/IO/InputStreamFromROOTFileForCylindricalPET.cxx @@ -56,7 +56,7 @@ InputStreamFromROOTFileForCylindricalPET(std::string _filename, up_energy_window = _up_energy_window; offset_dets = _offset_dets; - half_block = static_cast( (module_repeater_y * submodule_repeater_y * crystal_repeater_y) / 2); + half_block = (module_repeater_y * submodule_repeater_y * crystal_repeater_y) / 2; if (half_block < 0 ) half_block = 0; } @@ -207,8 +207,7 @@ set_up(const std::string & header_path) if (nentries == 0) error("InputStreamFromROOTFileForCylindricalPET: The total number of entries in the ROOT file is zero. Abort."); - return Succeeded::yes; - half_block = static_cast( (module_repeater_y * submodule_repeater_y * crystal_repeater_y) / 2); + half_block = (module_repeater_y * submodule_repeater_y * crystal_repeater_y) / 2; if (half_block < 0 ) half_block = 0; diff --git a/src/IO/InterfileHeader.cxx b/src/IO/InterfileHeader.cxx index a6c3ce0896..2202b9cb93 100644 --- a/src/IO/InterfileHeader.cxx +++ b/src/IO/InterfileHeader.cxx @@ -672,18 +672,21 @@ int InterfilePDFSHeader::find_storage_order() // If TOF information is in there if (matrix_labels.size() > 4) { - num_timing_poss = matrix_size[4][0]; - storage_order =ProjDataFromStream::Timing_Segment_View_AxialPos_TangPos; - num_views = matrix_size[2][0]; + 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 { - storage_order =ProjDataFromStream::Segment_View_AxialPos_TangPos; + 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()); @@ -1360,10 +1363,7 @@ bool InterfilePDFSHeader::post_processing() // float azimuthal_angle_sampling =_PI/num_views; - - - //TODO: TOF ProjDataInfo - + if (is_arccorrected) { if (effective_central_bin_size_in_cm <= 0) diff --git a/src/buildblock/ML_norm.cxx b/src/buildblock/ML_norm.cxx index a4290eb521..5e7cf2cc9b 100644 --- a/src/buildblock/ML_norm.cxx +++ b/src/buildblock/ML_norm.cxx @@ -479,6 +479,7 @@ FanProjData(const int num_rings, const int num_detectors_per_ring, const int max assert(num_detectors_per_ring%2 == 0); assert(max_ring_diff fan_indices; fan_indices.grow(0,num_rings-1); @@ -756,6 +757,9 @@ void make_fan_data(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."); + shared_ptr proj_data_info_ptr = get_fan_info(num_rings, num_detectors_per_ring, max_delta, fan_size, *proj_data.get_proj_data_info_ptr()); @@ -768,9 +772,9 @@ void make_fan_data(FanProjData& fan_data, for (bin.segment_num() = proj_data.get_min_segment_num(); bin.segment_num() <= proj_data.get_max_segment_num(); ++ 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()) +// 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()))); + 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()); @@ -800,6 +804,9 @@ void set_fan_data(ProjData& proj_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."); + shared_ptr proj_data_info_ptr = get_fan_info(num_rings, num_detectors_per_ring, max_delta, fan_size, *proj_data.get_proj_data_info_ptr()); @@ -814,9 +821,9 @@ void set_fan_data(ProjData& proj_data, for (bin.segment_num() = proj_data.get_min_segment_num(); bin.segment_num() <= proj_data.get_max_segment_num(); ++ 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()) +// 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_empty_segment_by_sinogram(bin.segment_num(),bin.timing_pos_num()))); + segment_ptr.reset(new SegmentBySinogram(proj_data.get_empty_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()); @@ -935,6 +942,9 @@ void make_fan_sum_data(Array<2,float>& data_fan_sums, 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."); + shared_ptr proj_data_info_ptr = get_fan_info(num_rings, num_detectors_per_ring, max_delta, fan_size, *proj_data.get_proj_data_info_ptr()); @@ -946,9 +956,9 @@ void make_fan_sum_data(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()) { - 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()) +// 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()))); + 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()); diff --git a/src/buildblock/ProjDataInfo.cxx b/src/buildblock/ProjDataInfo.cxx index 7f996fddbc..007770d723 100644 --- a/src/buildblock/ProjDataInfo.cxx +++ b/src/buildblock/ProjDataInfo.cxx @@ -198,6 +198,31 @@ ProjDataInfo::set_tof_mash_factor(const int new_num) "the scanner's number of max timing bins. Abort."); tof_mash_factor = new_num; + tof_increament_in_mm = tof_delta_time_to_mm(scanner_ptr->get_size_of_timing_pos()); + min_unmashed_tof_pos_num = - (scanner_ptr->get_num_max_of_timing_poss())/2; + max_unmashed_tof_pos_num = min_tof_pos_num + (scanner_ptr->get_num_max_of_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; + + 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_boundaries_mm[k].low_lim)); + tof_bin_unmashed_boundaries_ps[k].high_lim = static_cast(mm_to_tof_delta_time(tof_bin_boundaries_mm[k].high_lim)); + + } + + // 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()); min_tof_pos_num = - (scanner_ptr->get_num_max_of_timing_poss() / tof_mash_factor)/2; diff --git a/src/include/stir/ProjDataInfo.h b/src/include/stir/ProjDataInfo.h index 486fdd7edf..592cbb57ac 100644 --- a/src/include/stir/ProjDataInfo.h +++ b/src/include/stir/ProjDataInfo.h @@ -424,7 +424,11 @@ class ProjDataInfo mutable VectorWithOffset tof_bin_boundaries_mm; //! Vector which holds the lower and higher boundary for each TOF position in ps`, for faster access. mutable VectorWithOffset tof_bin_boundaries_ps; - + //! Vector which holds the lower and higher boundary for each TOF position, without the application of TOF mashing, in mm, for faster access. + mutable VectorWithOffset tof_bin_unmashed_boundaries_mm; + //! Vector which holds the lower and higher boundary for each TOF position, without the application of TOF mashing, in ps`, for faster access. + mutable VectorWithOffset tof_bin_unmashed_boundaries_ps; + //! Set horizontal bed position void set_bed_position_horizontal(const float bed_position_horizontal_arg) { bed_position_horizontal = bed_position_horizontal_arg; } diff --git a/src/include/stir/ProjDataInfo.inl b/src/include/stir/ProjDataInfo.inl index 3e7749762d..887bf993d6 100644 --- a/src/include/stir/ProjDataInfo.inl +++ b/src/include/stir/ProjDataInfo.inl @@ -38,12 +38,13 @@ START_NAMESPACE_STIR double ProjDataInfo::mm_to_tof_delta_time(const float dist) { - return dist / (0.299792458 / 2); + return dist / _c_light_div2; } + float ProjDataInfo::tof_delta_time_to_mm(const double delta_time) { - return static_cast(delta_time * (0.299792458 / 2)); + return static_cast(delta_time * _c_light_div2); } shared_ptr @@ -107,16 +108,15 @@ ProjDataInfo::get_unmashed_tof_bin(const double delta) const if (!is_tof_data()) return 0; - if (delta < tof_bin_boundaries_ps[min_tof_pos_num].low_lim && - delta > tof_bin_boundaries_ps[max_tof_pos_num].high_lim) + for (int i = min_unmashed_tof_pos_num; i <= max_unmashed_tof_pos_num; ++i) { - // TODO handle differently - warning(boost::format("TOF delta time %g out of range") % delta); - return 0; + if (delta >= tof_bin_boundaries_ps[i].low_lim && + delta < tof_bin_boundaries_ps[i].high_lim) + return i; } - - return delta * get_scanner_ptr()->get_num_max_of_timing_poss() / - get_coincidence_window_in_pico_sec(); + // TODO handle differently + warning(boost::format("TOF delta time %g out of range") % delta); + return 0; } diff --git a/src/include/stir/common.h b/src/include/stir/common.h index d5b9949a36..2ad5ce2d4c 100644 --- a/src/include/stir/common.h +++ b/src/include/stir/common.h @@ -299,6 +299,11 @@ START_NAMESPACE_STIR #define _PI boost::math::constants::pi() #endif +//! Define the speed of light in mm / ps +const double _c_light = 0.299792458; +//! This ratio is used often. +const double _c_light_div2 = _c_light * 0.5; + //! returns the square of a number, templated. /*! \ingroup buildblock */ template From f29e92a2b37b9595a5437f43057eff184dc5ac06 Mon Sep 17 00:00:00 2001 From: NikEfth Date: Wed, 13 Feb 2019 12:57:09 +0000 Subject: [PATCH 121/170] Somehow the option to disable HDF5 got disappeared. I bring it back. --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index e3e7f1807a..38c7ac0933 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,6 +86,7 @@ option(DISABLE_AVW "disable use of AVW library" OFF) option(DISABLE_RDF "disable use of GE RDF library" OFF) option(DISABLE_STIR_LOCAL "disable use of LOCAL extensions to STIR" OFF) option(DISABLE_CERN_ROOT "disable use of Cern ROOT libraries" OFF) +option(DISABLE_HDF5_SUPPORT "disable use of HDF5 libraries" OFF) option(STIR_ENABLE_EXPERIMENTAL "disable use of STIR experimental code" OFF) # disable by default if(NOT DISABLE_ITK) From 442eaf3ffa8a599591f2c5c05d234c2024790084 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Sun, 3 Mar 2019 04:23:45 +0000 Subject: [PATCH 122/170] Corrections: all recon_test_pack should pass *. Introduction of nonTOF Discovery STE. fixes problems in ./run_test_simulate_and_recon *. In Scanner comparison the TOF information is taken into account only when the two scanners are TOFready. *. Bug fix in ProjDataInfo --- recon_test_pack/Siemens_mMR_seg2.hs | 2 + .../lm_generate_atten_cylinder.par | 8 +-- recon_test_pack/root_header.hroot | 4 +- recon_test_pack/run_test_listmode_recon.sh | 2 +- .../run_test_simulate_and_recon.sh | 2 +- recon_test_pack/template_for_ROOT_scanner.hs | 5 ++ src/buildblock/ProjDataInfo.cxx | 4 +- src/buildblock/Scanner.cxx | 55 +++++++++++++------ src/include/stir/Scanner.h | 2 +- 9 files changed, 55 insertions(+), 29 deletions(-) diff --git a/recon_test_pack/Siemens_mMR_seg2.hs b/recon_test_pack/Siemens_mMR_seg2.hs index f419f744ec..6337e33864 100644 --- a/recon_test_pack/Siemens_mMR_seg2.hs +++ b/recon_test_pack/Siemens_mMR_seg2.hs @@ -44,4 +44,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/lm_generate_atten_cylinder.par b/recon_test_pack/lm_generate_atten_cylinder.par index 0aa3511bfb..c7227c749c 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/root_header.hroot b/recon_test_pack/root_header.hroot index f8af3f7559..51abcc463e 100644 --- a/recon_test_pack/root_header.hroot +++ b/recon_test_pack/root_header.hroot @@ -9,8 +9,8 @@ Distance between rings (cm) := 0.40625 Default bin size (cm) := 0.208626 Maximum number of non-arc-corrected bins := 344 Number of TOF time bins := 410 -Size of timing bin (in picoseconds) := 10.00 -Timing resolution (in picoseconds) := 400.0 +Size of timing bin (ps) := 10.00 +Timing resolution (ps) := 400.0 %TOF mashing factor:= 82 diff --git a/recon_test_pack/run_test_listmode_recon.sh b/recon_test_pack/run_test_listmode_recon.sh index d9c5c460e9..d5bf2f44b1 100755 --- a/recon_test_pack/run_test_listmode_recon.sh +++ b/recon_test_pack/run_test_listmode_recon.sh @@ -76,7 +76,7 @@ rm -f my_*v my_*s my_*S echo "=== Simulate normalisation data" # For normalisation data we are going to use a cylinder in the center, # with water attenuation values -echo "=== Gnerete fake emission image" +echo "=== Generate fake emission 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 diff --git a/recon_test_pack/run_test_simulate_and_recon.sh b/recon_test_pack/run_test_simulate_and_recon.sh index 9f68e837fb..5654d6cd61 100755 --- a/recon_test_pack/run_test_simulate_and_recon.sh +++ b/recon_test_pack/run_test_simulate_and_recon.sh @@ -86,7 +86,7 @@ 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 <(mm_to_tof_delta_time(tof_bin_boundaries_mm[k].low_lim)); - tof_bin_unmashed_boundaries_ps[k].high_lim = static_cast(mm_to_tof_delta_time(tof_bin_boundaries_mm[k].high_lim)); + 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)); } diff --git a/src/buildblock/Scanner.cxx b/src/buildblock/Scanner.cxx index 88169ea932..e564765433 100644 --- a/src/buildblock/Scanner.cxx +++ b/src/buildblock/Scanner.cxx @@ -120,7 +120,7 @@ Scanner::Scanner(Type scanner_type) // KT 25/01/2002 corrected ring_spacing set_params(E931, string_list("ECAT 931"), - 8, 192, 2 * 256, 2*256, + 8, 192, 192, 2*256, 510.0F, 7.0F, 13.5F, 3.129F, 0.0F, 2, 4, 4, 8, 4, 8 * 4, 1, 0.0F, 511.F, @@ -132,7 +132,7 @@ Scanner::Scanner(Type scanner_type) case E951: set_params(E951, string_list("ECAT 951"), - 16, 192, 2 * 256, 2*256, + 16, 192, 192, 2*256, 510.0F, 7.0F, 6.75F, 3.12932F, 0.0F, 1, 4, 8, 8, 8, 8 * 4, 1, 0.0F, 511.F, @@ -142,7 +142,7 @@ Scanner::Scanner(Type scanner_type) case E953: set_params(E953, string_list("ECAT 953"), - 16, 160, 2 * 192, 2*192, + 16, 160, 160, 2*192, 382.5F, 7.0F, 6.75F, 3.12932F, static_cast(15.*_PI/180), 1, 4, 8, 8, 8, 8 * 4, 1, 0.0F, 511.F, @@ -152,7 +152,7 @@ Scanner::Scanner(Type scanner_type) case E921: set_params(E921, string_list("ECAT 921", "ECAT EXACT", "EXACT"), - 24, 192, 2* 192, 2 * 192, + 24, 192, 192, 2 * 192, 412.5F, 7.0F, 6.75F, 3.375F, static_cast(15.*_PI/180), 1, 4, 8, 8, 8, 8 * 4, 1, 0.0F, 511.F, @@ -162,7 +162,7 @@ Scanner::Scanner(Type scanner_type) case E925: set_params(E925, string_list("ECAT 925", "ECAT ART"), - 24, 192, 2* 192, 2* 192, + 24, 192, 192, 2* 192, 412.5F, 7.0F, 6.75F, 3.375F, static_cast(15.*_PI/180), 3, 4, 8, 8, 8, 8 * 4, 1, 0.0F, 511.F, @@ -173,7 +173,7 @@ Scanner::Scanner(Type scanner_type) case E961: set_params(E961,string_list("ECAT 961", "ECAT HR"), - 24, 336, 2* 392, 2*392, + 24, 336, 336, 2*392, 412.0F, 7.0F, 6.25F, 1.650F, static_cast(13.*_PI/180), 1, 8, 8, 7, 8, 7 * 8, 1, 0.0F, 511.F, @@ -183,7 +183,7 @@ Scanner::Scanner(Type scanner_type) case E962: set_params(E962,string_list("ECAT 962","ECAT HR+"), - 32, 288, 2* 288, 2*288, + 32, 288, 288, 2*288, 412.0F, 7.0F, 4.85F, 2.25F, 0.0F, 4, 3, 8, 8, 8, 8 * 3, 1, 0.0F, 511.F, @@ -193,7 +193,7 @@ Scanner::Scanner(Type scanner_type) case E966: set_params(E966, string_list("ECAT EXACT 3D", "EXACT 3D", "ECAT HR++","ECAT 966"), - 48, 288, 2* 288, 2*288, + 48, 288, 288, 2*288, 412.0F, 7.0F, 4.850F, 2.250F, 0.0, 6, 2, 8, 8, 2 * 8, 8 * 2, 1, 0.0F, 511.F, @@ -203,7 +203,7 @@ Scanner::Scanner(Type scanner_type) case E1080: // data added by Robert Barnett, Westmead Hospital, Sydney set_params(E1080, string_list("ECAT 1080", "Biograph 16", "1080"), - 41, 336, 2* 336, 2*336, + 41, 336, 336, 2*336, 412.0F, 7.0F, 4.0F, 2.000F, 0.0F, 1, 2, 41, 14, 41, 14, 1, 0.0F, 511.F, @@ -219,7 +219,7 @@ Scanner::Scanner(Type scanner_type) // Transaxial blocks have 8 physical crystals and a gap at the // 9th crystal where the counts are zero. set_params(Siemens_mMR, string_list("Siemens mMR", "mMR", "2008"), - 64, 344, 2* 252, 2*252, + 64, 344, 344, 2*252, 328.0F, 7.0F, 4.0625F, 2.08626F, 0.0F, 2, 1, 8, 9, 16, 9, 1, 0.0F, 511.F, @@ -229,7 +229,7 @@ Scanner::Scanner(Type scanner_type) case test_scanner: // This is a relatively small scanner for test purposes. set_params(test_scanner, string_list("test_scanner"), - 4, 344, 2*252,2*252, + 4, 344, 344,2*252, 328.0F, 7.0F, 4.0625F, 2.08626F, 0.0F, 1, 1, 4, 1, 4, 1, 1, 0.0F, 511.F, @@ -241,7 +241,7 @@ Scanner::Scanner(Type scanner_type) case RPT: set_params(RPT, string_list("PRT-1", "RPT"), - 16, 128, 2 * 192, 2*192, + 16, 128, 128, 2*192, 380.0F - 7.0F, 7.0F, 6.75F, 3.1088F, 0.0F, 1, 4, 8, 8, 8, 32, 1, 0.0F, 511.F, @@ -254,7 +254,7 @@ Scanner::Scanner(Type scanner_type) case RATPET: set_params(RATPET, string_list("RATPET"), - 8, 56, 2 * 56, 2*56, + 8, 56, 56, 2*56, 115 / 2.F, 7.0F, 6.25F, 1.65F, 0.0F, 1, 16, 8, 7, 8, 0, 1, 0.0F, 511.F, @@ -346,6 +346,19 @@ Scanner::Scanner(Type scanner_type) (float)(400.0F) );// TODO not sure about sign of view_offset break; + case DiscoverySTE_nonTOF: + + set_params(DiscoverySTE_nonTOF, string_list("GE Discovery STE nonTOF", "Discovery STE nonTOF"), + 24, 329, 293, 2 * 280, + 886.2F/2.F, 8.4F, 6.54F, 2.397F, + static_cast(-4.5490*_PI/180),//sign? + 4, 2, 6, 8, 1, 1, 1, + 0.0F, 511.F, + (short int)(0.F), + (float)(0.F), + (float)(0.F) );// TODO not sure about sign of view_offset + break; + case ntest_TOF_50: // dummy // 8x8 blocks, 1 virtual "crystal", 56 blocks along the ring, 8 blocks in axial direction // Transaxial blocks have 8 physical crystals and a gap at the @@ -846,7 +859,7 @@ if (!close_enough(energy_resolution, scanner.energy_resolution) && " %d opposed to %d" "This only affects scatter simulation. \n", energy_resolution, scanner.energy_resolution); - return + bool ok = (num_rings == scanner.num_rings) && (max_num_non_arccorrected_bins == scanner.max_num_non_arccorrected_bins) && (default_num_arccorrected_bins == scanner.default_num_arccorrected_bins) && @@ -862,10 +875,16 @@ if (!close_enough(energy_resolution, scanner.energy_resolution) && (num_transaxial_crystals_per_block == scanner.num_transaxial_crystals_per_block) && (num_detector_layers == scanner.num_detector_layers) && (num_axial_crystals_per_singles_unit == scanner.num_axial_crystals_per_singles_unit) && - (num_transaxial_crystals_per_singles_unit == scanner.num_transaxial_crystals_per_singles_unit) && - (max_num_of_timing_poss == scanner.max_num_of_timing_poss) && - close_enough(size_timing_pos, scanner.size_timing_pos) && - close_enough(timing_resolution, scanner.timing_resolution); + (num_transaxial_crystals_per_singles_unit == scanner.num_transaxial_crystals_per_singles_unit); + + if (this->is_tof_ready() && scanner.is_tof_ready()) + { + ok = (max_num_of_timing_poss == scanner.max_num_of_timing_poss) && + close_enough(size_timing_pos, scanner.size_timing_pos) && + close_enough(timing_resolution, scanner.timing_resolution); + } + + return ok; } diff --git a/src/include/stir/Scanner.h b/src/include/stir/Scanner.h index 4f3f67a6b2..63f64c8837 100644 --- a/src/include/stir/Scanner.h +++ b/src/include/stir/Scanner.h @@ -116,7 +116,7 @@ class Scanner any given parameters. */ enum Type {E931, E951, E953, E921, E925, E961, E962, E966, E1080, test_scanner, Siemens_mMR, RPT,HiDAC, - Advance, DiscoveryLS, DiscoveryST, DiscoverySTE, DiscoveryRX, Discovery600,Discovery690,PETMR_Signa, PETMR_Signa_nonTOF, + Advance, DiscoveryLS, DiscoveryST, DiscoverySTE, DiscoverySTE_nonTOF, DiscoveryRX, Discovery600,Discovery690,PETMR_Signa, PETMR_Signa_nonTOF, HZLR, RATPET, PANDA, HYPERimage, nanoPET, HRRT, Allegro, GeminiTF, User_defined_scanner,ntest_TOF_50, Unknown_scanner}; From 63d3848fe19d2aaf09df3eeb4f72ed6f2e9e169c Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Sun, 3 Mar 2019 04:30:17 +0000 Subject: [PATCH 123/170] Fix run_test_simulate_and_recon_with_motion Use of nonTOF Discovery STE. fixes problems in run_test_simulate_and_recon_with_motion --- recon_test_pack/run_test_simulate_and_recon_with_motion.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 5b9b09c7bb..af66a78b95 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 @@ -124,7 +124,7 @@ fi 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 < Date: Sun, 3 Mar 2019 11:39:59 +0000 Subject: [PATCH 124/170] Fix double free error in SPECT reconstruction --- src/recon_buildblock/ProjMatrixByBinSPECTUB.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/recon_buildblock/ProjMatrixByBinSPECTUB.cxx b/src/recon_buildblock/ProjMatrixByBinSPECTUB.cxx index 4ce6ef8c94..3de387f7f9 100644 --- a/src/recon_buildblock/ProjMatrixByBinSPECTUB.cxx +++ b/src/recon_buildblock/ProjMatrixByBinSPECTUB.cxx @@ -584,7 +584,7 @@ ProjMatrixByBinSPECTUB::clone() const ProjMatrixByBinSPECTUB:: ~ProjMatrixByBinSPECTUB() { - delete_UB_SPECT_arrays(); + // delete_UB_SPECT_arrays(); } void From b4651a6e9e06e587b1666d99fed1d67e55a0b68b Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Sun, 3 Mar 2019 14:28:15 +0000 Subject: [PATCH 125/170] Minor fixes to make Codacy happy(-ier). --- recon_test_pack/run_test_time_of_flight.sh | 4 ++-- src/recon_buildblock/BinNormalisationFromGEHDF5.cxx | 4 ++-- .../ForwardProjectorByBinUsingRayTracing.cxx | 2 +- src/recon_buildblock/distributed_functions.cxx | 2 +- src/recon_test/test_consistency_root.cxx | 4 ++-- src/test/test_time_of_flight.cxx | 6 +++--- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/recon_test_pack/run_test_time_of_flight.sh b/recon_test_pack/run_test_time_of_flight.sh index 9d2d5ecd4d..2bff60c52a 100755 --- a/recon_test_pack/run_test_time_of_flight.sh +++ b/recon_test_pack/run_test_time_of_flight.sh @@ -90,8 +90,8 @@ echo "Data maxs:" $Data_maxs for i in $(seq 5) do - if [ $(( $(($(($i-1)) - $((TOF_bins/2)))) - $((Data_mins[$i])))) -ne 0 ]; then - echo "Wrong values in TOF sinogram. Error. $(( $(($(($i-1)) - $((TOF_bins/2)))) - $((Data_mins[$i]))))" + if [ $(( $((i-1 - TOF_bins/2)) - Data_mins[i])) -ne 0 ]; then + echo "Wrong values in TOF sinogram. Error. $(( $(($((i-1)) -TOF_bins/2)) - Data_mins[i]))" exit 1 fi done diff --git a/src/recon_buildblock/BinNormalisationFromGEHDF5.cxx b/src/recon_buildblock/BinNormalisationFromGEHDF5.cxx index c0185264ef..d140e3b87a 100644 --- a/src/recon_buildblock/BinNormalisationFromGEHDF5.cxx +++ b/src/recon_buildblock/BinNormalisationFromGEHDF5.cxx @@ -290,8 +290,8 @@ read_norm_data(const string& filename) normalisation data. */ - const int min_tang_pos_num = -(scanner_ptr->get_max_num_non_arccorrected_bins())/2; - const int max_tang_pos_num = min_tang_pos_num +scanner_ptr->get_max_num_non_arccorrected_bins()- 1; + //const int min_tang_pos_num = -(scanner_ptr->get_max_num_non_arccorrected_bins())/2; + //const int max_tang_pos_num = min_tang_pos_num +scanner_ptr->get_max_num_non_arccorrected_bins()- 1; //geometric_factors = // Array<2,float>(IndexRange2D(0,127-1, //XXXXnrm_subheader_ptr->num_geo_corr_planes-1, diff --git a/src/recon_buildblock/ForwardProjectorByBinUsingRayTracing.cxx b/src/recon_buildblock/ForwardProjectorByBinUsingRayTracing.cxx index 961cf67c11..24cc08eed5 100644 --- a/src/recon_buildblock/ForwardProjectorByBinUsingRayTracing.cxx +++ b/src/recon_buildblock/ForwardProjectorByBinUsingRayTracing.cxx @@ -1129,7 +1129,7 @@ forward_project_all_symmetries_2D(Viewgram & pos_view, const int nviews = pos_view.get_proj_data_info_ptr()->get_num_views(); const int segment_num = pos_view.get_segment_num(); - const int timing_pos_num = pos_view.get_timing_pos_num(); + //const int timing_pos_num = pos_view.get_timing_pos_num(); const float delta = proj_data_info_ptr->get_average_ring_difference(segment_num); const int view = pos_view.get_view_num(); diff --git a/src/recon_buildblock/distributed_functions.cxx b/src/recon_buildblock/distributed_functions.cxx index ced7944f50..08f9fe1a95 100644 --- a/src/recon_buildblock/distributed_functions.cxx +++ b/src/recon_buildblock/distributed_functions.cxx @@ -353,7 +353,7 @@ namespace distributed void send_viewgram(const stir::Viewgram& viewgram, int destination) { //send dimensions of viewgram (axial and tangential positions and the view and segment numbers) - int viewgram_values[6]; + int viewgram_values[7]; viewgram_values[0] = viewgram.get_min_axial_pos_num(); viewgram_values[1] = viewgram.get_max_axial_pos_num(); viewgram_values[2] = viewgram.get_min_tangential_pos_num(); diff --git a/src/recon_test/test_consistency_root.cxx b/src/recon_test/test_consistency_root.cxx index 5e9970f281..a7f705950d 100644 --- a/src/recon_test/test_consistency_root.cxx +++ b/src/recon_test/test_consistency_root.cxx @@ -60,7 +60,7 @@ START_NAMESPACE_STIR class ROOTconsistency_Tests : public RunTests { public: - ROOTconsistency_Tests(std::string in, std::string image) + ROOTconsistency_Tests(std::string in, const std::string& image) : root_header_filename(in), image_filename(image) {} void run_tests(); @@ -95,7 +95,7 @@ class ROOTconsistency_Tests : public RunTests const BasicCoordinate<3, float>& grid_spacing); //! Modified version of check_if_equal for this test - bool check_if_almost_equal(const double a, const double b, std::string str, const double tolerance); + bool check_if_almost_equal(const double a, const double b, const std::string& str, const double tolerance); std::string root_header_filename; std::string image_filename; diff --git a/src/test/test_time_of_flight.cxx b/src/test/test_time_of_flight.cxx index f20449e6cd..1f04db82ad 100644 --- a/src/test/test_time_of_flight.cxx +++ b/src/test/test_time_of_flight.cxx @@ -287,11 +287,11 @@ TOF_Tests::test_tof_kernel_application(bool print_to_file) if (sum_tof_proj_matrix_row.size() > 0) { ProjMatrixElemsForOneBin::iterator element_ptr = new_proj_matrix_row.begin(); + bool found = false; while (element_ptr != new_proj_matrix_row.end()) { - ProjMatrixElemsForOneBin::iterator sum_element_ptr = sum_tof_proj_matrix_row.begin(); - bool found = false; + found = false; while(sum_element_ptr != sum_tof_proj_matrix_row.end()) { if(element_ptr->get_coords() == sum_element_ptr->get_coords()) @@ -309,7 +309,7 @@ TOF_Tests::test_tof_kernel_application(bool print_to_file) element_ptr->get_value())); break; } - ++element_ptr; + ++element_ptr; } } From 929e01f529531e72ac22648acd15bc9cbca6f185 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Mon, 4 Mar 2019 09:58:10 +0000 Subject: [PATCH 126/170] Modification for Codacy --- src/recon_test/test_consistency_root.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/recon_test/test_consistency_root.cxx b/src/recon_test/test_consistency_root.cxx index a7f705950d..09dbb8b671 100644 --- a/src/recon_test/test_consistency_root.cxx +++ b/src/recon_test/test_consistency_root.cxx @@ -60,7 +60,7 @@ START_NAMESPACE_STIR class ROOTconsistency_Tests : public RunTests { public: - ROOTconsistency_Tests(std::string in, const std::string& image) + ROOTconsistency_Tests(const std::string& in, const std::string& image) : root_header_filename(in), image_filename(image) {} void run_tests(); From ad906823bbc111bc59865436e10a4e2e17059abc Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Tue, 5 Mar 2019 10:01:12 +0000 Subject: [PATCH 127/170] AppVeyor failed test_proj_dat_in_memory, prob. due to memory limitations I reduced the size of the proj data --- src/test/test_proj_data_in_memory.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/test_proj_data_in_memory.cxx b/src/test/test_proj_data_in_memory.cxx index 024885b394..fde6c2983e 100644 --- a/src/test/test_proj_data_in_memory.cxx +++ b/src/test/test_proj_data_in_memory.cxx @@ -168,7 +168,7 @@ run_tests_tof() shared_ptr proj_data_info_sptr (ProjDataInfo::ProjDataInfoCTI(scanner_sptr, - /*span*/1, 10,/*views*/ 96, /*tang_pos*/128, /*arc_corrected*/ true, 11) + /*span*/1, 5,/*views*/ 96, /*tang_pos*/64, /*arc_corrected*/ true, 70) ); shared_ptr exam_info_sptr(new ExamInfo); @@ -176,7 +176,7 @@ run_tests_tof() // construct with filling to 0 ProjDataInMemory proj_data(exam_info_sptr, proj_data_info_sptr); { - check_if_equal( proj_data.get_sinogram(0,0,false,-2).get_timing_pos_num(), + check_if_equal( proj_data.get_sinogram(0,0,false,-2).get_timing_pos_num(), -2, "test get_sinogram timing position index"); Sinogram sinogram = proj_data.get_sinogram(0,0,false,-2); From cb2c46cae88ad3b7339f4e28c4ba0b4d639ee56f Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Thu, 11 Apr 2019 17:15:01 +0100 Subject: [PATCH 128/170] Two more changes asked by Codacy --- src/recon_buildblock/ForwardProjectorByBinUsingRayTracing.cxx | 2 +- src/test/test_time_of_flight.cxx | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/recon_buildblock/ForwardProjectorByBinUsingRayTracing.cxx b/src/recon_buildblock/ForwardProjectorByBinUsingRayTracing.cxx index 24cc08eed5..d9fcba5db2 100644 --- a/src/recon_buildblock/ForwardProjectorByBinUsingRayTracing.cxx +++ b/src/recon_buildblock/ForwardProjectorByBinUsingRayTracing.cxx @@ -425,7 +425,7 @@ forward_project_all_symmetries( const int nviews = pos_view.get_proj_data_info_ptr()->get_num_views(); const int segment_num = pos_view.get_segment_num(); - const int timing_pos_num = pos_view.get_timing_pos_num(); + //const int timing_pos_num = pos_view.get_timing_pos_num(); const float delta = proj_data_info_ptr->get_average_ring_difference(segment_num); const int view = pos_view.get_view_num(); diff --git a/src/test/test_time_of_flight.cxx b/src/test/test_time_of_flight.cxx index 1f04db82ad..67d89100c2 100644 --- a/src/test/test_time_of_flight.cxx +++ b/src/test/test_time_of_flight.cxx @@ -287,11 +287,10 @@ TOF_Tests::test_tof_kernel_application(bool print_to_file) if (sum_tof_proj_matrix_row.size() > 0) { ProjMatrixElemsForOneBin::iterator element_ptr = new_proj_matrix_row.begin(); - bool found = false; while (element_ptr != new_proj_matrix_row.end()) { ProjMatrixElemsForOneBin::iterator sum_element_ptr = sum_tof_proj_matrix_row.begin(); - found = false; + bool found = false; while(sum_element_ptr != sum_tof_proj_matrix_row.end()) { if(element_ptr->get_coords() == sum_element_ptr->get_coords()) From a4b815e9c7ae19e5202cb25ef88376d10d63d797 Mon Sep 17 00:00:00 2001 From: Alexander Whitehead Date: Mon, 22 Apr 2019 21:30:31 +0100 Subject: [PATCH 129/170] Number of time frames and function declaration (#12) Fix a missing function declaration. --- src/include/stir/recon_buildblock/ProjMatrixByBinSPECTUB.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBinSPECTUB.h b/src/include/stir/recon_buildblock/ProjMatrixByBinSPECTUB.h index f8c608c5c4..fe080d6b6a 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBinSPECTUB.h +++ b/src/include/stir/recon_buildblock/ProjMatrixByBinSPECTUB.h @@ -154,6 +154,10 @@ class ProjMatrixByBinSPECTUB : void set_resolution_model(const float collimator_sigma_0_in_mm, const float collimator_slope_in_mm, const bool full_3D = true); + + //Alex + //Fix to compile, missing function definition in header + ProjMatrixByBinSPECTUB * clone() const; private: From 7c5ec7b9f2688e9f4774140586346abe7e6606d8 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Thu, 6 Jun 2019 00:24:57 +0100 Subject: [PATCH 130/170] Fix for TOF-PSF reconstruction --- ...issonLogLikelihoodWithLinearModelForMeanAndProjData.cxx | 7 +++++-- src/recon_buildblock/PostsmoothingBackProjectorByBin.cxx | 4 +++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx index 20334f1731..41328daf89 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx @@ -751,9 +751,12 @@ add_subset_sensitivity(TargetT& sensitivity, const int subset_num) const #if 1 shared_ptr sensitivity_this_subset_sptr(sensitivity.clone()); - + shared_ptr sens_proj_data_sptr; // have to create a ProjData object filled with 1 here because otherwise zero_seg0_endplanes will not be effective - shared_ptr sens_proj_data_sptr(new ProjDataInMemory(this->proj_data_sptr->get_exam_info_sptr(), this->proj_data_sptr->get_proj_data_info_sptr())); + if (!this->use_tofsens) + sens_proj_data_sptr.reset(new ProjDataInMemory(this->proj_data_sptr->get_exam_info_sptr(), this->proj_data_sptr->get_proj_data_info_sptr()->create_non_tof_clone())); + else + sens_proj_data_sptr.reset(new ProjDataInMemory(this->proj_data_sptr->get_exam_info_sptr(), this->proj_data_sptr->get_proj_data_info_sptr())); sens_proj_data_sptr->fill(1.0F); distributable_sensitivity_computation(this->projector_pair_ptr->get_forward_projector_sptr(), diff --git a/src/recon_buildblock/PostsmoothingBackProjectorByBin.cxx b/src/recon_buildblock/PostsmoothingBackProjectorByBin.cxx index 62b9eb9321..a89036aabe 100644 --- a/src/recon_buildblock/PostsmoothingBackProjectorByBin.cxx +++ b/src/recon_buildblock/PostsmoothingBackProjectorByBin.cxx @@ -88,7 +88,9 @@ PostsmoothingBackProjectorByBin::get_original_back_projector_ptr() const PostsmoothingBackProjectorByBin* PostsmoothingBackProjectorByBin::clone() const { - return new PostsmoothingBackProjectorByBin(*this); + PostsmoothingBackProjectorByBin* sptr(new PostsmoothingBackProjectorByBin(*this)); + sptr->original_back_projector_ptr.reset(this->original_back_projector_ptr->clone()); + return sptr; } void PostsmoothingBackProjectorByBin:: From 2c068c27ad6187f3ddffb56dd9b645e06669cf44 Mon Sep 17 00:00:00 2001 From: Kris Thielemans Date: Wed, 29 Jan 2020 16:17:16 +0000 Subject: [PATCH 131/170] fix ProjData::get_num_sinograms() for TOF --- src/include/stir/ProjData.inl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/include/stir/ProjData.inl b/src/include/stir/ProjData.inl index 37f55eb13c..ee4b183582 100644 --- a/src/include/stir/ProjData.inl +++ b/src/include/stir/ProjData.inl @@ -110,7 +110,7 @@ int ProjData::get_num_sinograms() const for (int s=1; s<= this->get_max_segment_num(); ++s) num_sinos += 2* this->get_num_axial_poss(s); - return num_sinos; + return num_sinos*this->get_num_tof_poss(); } std::size_t ProjData::size_all() const From 8fa31698a818a19e9e7c444e1ad92433878f1fd9 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Tue, 4 Feb 2020 14:37:38 +0000 Subject: [PATCH 132/170] fix sensitivity calculation for lm recon --- ...nearModelForMeanAndListModeDataWithProjMatrixByBin.cxx | 4 ++-- src/test/test_time_of_flight.cxx | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx index 4141862623..3ea159a3d5 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx @@ -371,7 +371,7 @@ add_subset_sensitivity(TargetT& sensitivity, const int subset_num) const const int min_segment_num = proj_data_info_sptr->get_min_segment_num(); const int max_segment_num = proj_data_info_sptr->get_max_segment_num(); - this->projector_pair_sptr->get_back_projector_sptr()-> + this->sens_backprojector_sptr-> start_accumulating_in_new_target(); // warning: has to be same as subset scheme used as in distributable_computation @@ -388,7 +388,7 @@ add_subset_sensitivity(TargetT& sensitivity, const int subset_num) const this->add_view_seg_to_sensitivity(view_segment_num); } } - this->projector_pair_sptr->get_back_projector_sptr()-> + this->sens_backprojector_sptr-> get_output(sensitivity); } diff --git a/src/test/test_time_of_flight.cxx b/src/test/test_time_of_flight.cxx index 67d89100c2..439fddcfa2 100644 --- a/src/test/test_time_of_flight.cxx +++ b/src/test/test_time_of_flight.cxx @@ -47,10 +47,10 @@ START_NAMESPACE_STIR //! class cache_index{ public: - cache_index() { + cache_index(): + key(0){ view_num = 0; seg_num = 0; - key = 0; } inline bool operator==(const cache_index& Y) const @@ -247,8 +247,8 @@ TOF_Tests::test_tof_kernel_application(bool print_to_file) ProjDataInfoCylindrical* proj_data_ptr = dynamic_cast (test_proj_data_info_sptr.get()); - ProjDataInfoCylindrical* proj_data_nonTOF_ptr = - dynamic_cast (test_nonTOF_proj_data_info_sptr.get()); +// ProjDataInfoCylindrical* proj_data_nonTOF_ptr = +// dynamic_cast (test_nonTOF_proj_data_info_sptr.get()); LORInAxialAndNoArcCorrSinogramCoordinates lor; From 53085f0814855a1429c57fc65626981d1490a1a3 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Tue, 4 Feb 2020 15:28:11 +0000 Subject: [PATCH 133/170] Fixed test_zoom_images --- recon_test_pack/run_test_zoom_image.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/recon_test_pack/run_test_zoom_image.sh b/recon_test_pack/run_test_zoom_image.sh index e70a570a0c..3f38e52aef 100755 --- a/recon_test_pack/run_test_zoom_image.sh +++ b/recon_test_pack/run_test_zoom_image.sh @@ -156,6 +156,7 @@ template_sino=my_DSTE_3D_rd2_template.hs cat > my_input.txt < Date: Tue, 4 Feb 2020 16:13:29 +0000 Subject: [PATCH 134/170] fix: python cannot test right now. Let's see what travis will say. --- src/swig/stir.i | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/swig/stir.i b/src/swig/stir.i index 5925c6406e..43456f05bd 100644 --- a/src/swig/stir.i +++ b/src/swig/stir.i @@ -640,9 +640,7 @@ namespace std { // { // num_sinos += 2*proj_data.get_num_axial_poss(s); // } - int num_sinos = proj_data.get_num_sinograms(); - - Array<4,float> array(IndexRange4D(proj_data.get_num_tof_poss(),num_sinos, proj_data.get_num_views(), proj_data.get_num_tangential_poss())); + Array<4,float> array(IndexRange4D(proj_data.get_num_tof_poss(),proj_data.get_num_segments(), proj_data.get_num_views(), proj_data.get_num_tangential_poss())); return array; } From 3f4c94d74f69074a398450a03019dca71bfd42fe Mon Sep 17 00:00:00 2001 From: Kris Thielemans Date: Tue, 4 Feb 2020 21:26:04 +0000 Subject: [PATCH 135/170] added ProjData::get_num_non_tof_sinograms and fixed SWIG for tof --- src/include/stir/ProjData.h | 8 ++++++++ src/include/stir/ProjData.inl | 9 +++++++-- src/swig/stir.i | 8 ++------ 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/include/stir/ProjData.h b/src/include/stir/ProjData.h index e73374d73f..0a760891fe 100644 --- a/src/include/stir/ProjData.h +++ b/src/include/stir/ProjData.h @@ -333,7 +333,15 @@ class ProjData : public ExamData //! Get maximum tangential position number inline int get_max_tangential_pos_num() const; //! Get the number of sinograms + /*! Note that this will count TOF sinograms as well. + \see get_num_non_tof_sinograms() + */ inline int get_num_sinograms() const; + //! Get the number of sinograms + /*! Note that this is the sum of the number of axial poss over all segments. + \see get_num_sinograms() + */ + inline int get_num_non_tof_sinograms() const; //! Get the total size of the data inline std::size_t size_all() const; //! writes data to a file in Interfile format diff --git a/src/include/stir/ProjData.inl b/src/include/stir/ProjData.inl index ee4b183582..72c945ac6d 100644 --- a/src/include/stir/ProjData.inl +++ b/src/include/stir/ProjData.inl @@ -104,13 +104,18 @@ int ProjData::get_min_tof_pos_num() const int ProjData::get_max_tof_pos_num() const { return proj_data_info_ptr->get_max_tof_pos_num(); } -int ProjData::get_num_sinograms() const +int ProjData::get_num_non_tof_sinograms() const { int num_sinos = proj_data_info_ptr->get_num_axial_poss(0); for (int s=1; s<= this->get_max_segment_num(); ++s) num_sinos += 2* this->get_num_axial_poss(s); - return num_sinos*this->get_num_tof_poss(); + return num_sinos; +} + +int ProjData::get_num_sinograms() const +{ + return this->get_num_non_tof_sinograms()*this->get_num_tof_poss(); } std::size_t ProjData::size_all() const diff --git a/src/swig/stir.i b/src/swig/stir.i index 43456f05bd..9451268290 100644 --- a/src/swig/stir.i +++ b/src/swig/stir.i @@ -635,12 +635,8 @@ namespace std { #endif static Array<4,float> create_array_for_proj_data(const ProjData& proj_data) { - // int num_sinos=proj_data.get_num_axial_poss(0); - // for (int s=1; s<= proj_data.get_max_segment_num(); ++s) - // { - // num_sinos += 2*proj_data.get_num_axial_poss(s); - // } - Array<4,float> array(IndexRange4D(proj_data.get_num_tof_poss(),proj_data.get_num_segments(), proj_data.get_num_views(), proj_data.get_num_tangential_poss())); + const int num_non_tof_sinos = proj_data.get_num_non_tof_sinograms(); + Array<4,float> array(IndexRange4D(proj_data.get_num_tof_poss(),num_non_tof_sinos, proj_data.get_num_views(), proj_data.get_num_tangential_poss())); return array; } From f22abd6921625022242b9536f4067bf723224973 Mon Sep 17 00:00:00 2001 From: Kris Thielemans Date: Fri, 7 Feb 2020 23:09:58 +0000 Subject: [PATCH 136/170] removed unused ForwardProjectorByBin::forward_project(Bin&,...) The implementation in terms of actual_forward_project got lost in the previous merge, but it would need to be rewritten in terms of the new style (not passing the image) anyway. In any case, most implementations were calling error anyway. As it is currently not used, it seemed easiest to just comment it out... --- src/include/stir/recon_buildblock/ForwardProjectorByBin.h | 8 ++++++-- .../ForwardProjectorByBinUsingProjMatrixByBin.h | 2 ++ .../ForwardProjectorByBinUsingRayTracing.h | 4 ++-- .../recon_buildblock/PresmoothingForwardProjectorByBin.h | 2 ++ .../ForwardProjectorByBinUsingProjMatrixByBin.cxx | 4 +++- .../ForwardProjectorByBinUsingRayTracing.cxx | 4 +++- .../PresmoothingForwardProjectorByBin.cxx | 4 +++- 7 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/include/stir/recon_buildblock/ForwardProjectorByBin.h b/src/include/stir/recon_buildblock/ForwardProjectorByBin.h index 1beec05025..2c28b86a1c 100644 --- a/src/include/stir/recon_buildblock/ForwardProjectorByBin.h +++ b/src/include/stir/recon_buildblock/ForwardProjectorByBin.h @@ -127,10 +127,12 @@ virtual void set_up( const int min_axial_pos_num, const int max_axial_pos_num, const int min_tangential_pos_num, const int max_tangential_pos_num); - //! Overloaded function mainly used in ListMode reconstruction. +#if 0 // disabled as currently not used. needs to be written in the new style anyway + //! function mainly used in ListMode reconstruction. + /*! Calls actual_forward_project */ void forward_project(Bin&, const DiscretisedDensity<3,float>&); - +#endif virtual ~ForwardProjectorByBin(); /// Set input @@ -150,9 +152,11 @@ virtual void set_up( const int min_axial_pos_num, const int max_axial_pos_num, const int min_tangential_pos_num, const int max_tangential_pos_num); +#if 0 // disabled as currently not used. needs to be written in the new style anyway //! This virtual function has to be implemented by the derived class. virtual void actual_forward_project(Bin&, const DiscretisedDensity<3,float>&) = 0; +#endif //! check if the argument is the same as what was used for set_up() /*! calls error() if anything is wrong. diff --git a/src/include/stir/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.h b/src/include/stir/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.h index 28ed510e4d..289d10cc41 100644 --- a/src/include/stir/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.h +++ b/src/include/stir/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.h @@ -88,7 +88,9 @@ class ForwardProjectorByBinUsingProjMatrixByBin: const int min_axial_pos_num, const int max_axial_pos_num, const int min_tangential_pos_num, const int max_tangential_pos_num); +#if 0 // disabled as currently not used. needs to be written in the new style anyway void actual_forward_project(Bin&, const DiscretisedDensity<3,float>&); +#endif virtual void set_defaults(); virtual void initialise_keymap(); diff --git a/src/include/stir/recon_buildblock/ForwardProjectorByBinUsingRayTracing.h b/src/include/stir/recon_buildblock/ForwardProjectorByBinUsingRayTracing.h index 96af693885..e4c6a56421 100644 --- a/src/include/stir/recon_buildblock/ForwardProjectorByBinUsingRayTracing.h +++ b/src/include/stir/recon_buildblock/ForwardProjectorByBinUsingRayTracing.h @@ -112,10 +112,10 @@ class ForwardProjectorByBinUsingRayTracing : const DiscretisedDensity<3,float>&, const int min_axial_pos_num, const int max_axial_pos_num, const int min_tangential_pos_num, const int max_tangential_pos_num); - +#if 0 // disabled as currently not used. needs to be written in the new style anyway void actual_forward_project(Bin&, const DiscretisedDensity<3,float>&); - +#endif // KT 20/06/2001 changed type from 'const DataSymmetriesForViewSegmentNumbers *' shared_ptr symmetries_ptr; diff --git a/src/include/stir/recon_buildblock/PresmoothingForwardProjectorByBin.h b/src/include/stir/recon_buildblock/PresmoothingForwardProjectorByBin.h index e102170185..dc09bdb7b7 100644 --- a/src/include/stir/recon_buildblock/PresmoothingForwardProjectorByBin.h +++ b/src/include/stir/recon_buildblock/PresmoothingForwardProjectorByBin.h @@ -101,8 +101,10 @@ class PresmoothingForwardProjectorByBin : const int min_axial_pos_num, const int max_axial_pos_num, const int min_tangential_pos_num, const int max_tangential_pos_num); +#if 0 // disabled as currently not used. needs to be written in the new style anyway void actual_forward_project(Bin&, const DiscretisedDensity<3,float>&); +#endif virtual void set_defaults(); virtual void initialise_keymap(); diff --git a/src/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.cxx b/src/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.cxx index 35aabc50f8..1fa202b6f9 100644 --- a/src/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.cxx +++ b/src/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.cxx @@ -233,12 +233,13 @@ ForwardProjectorByBinUsingProjMatrixByBin:: } } +#if 0 // disabled as currently not used. needs to be written in the new style anyway void ForwardProjectorByBinUsingProjMatrixByBin:: actual_forward_project(Bin& this_bin, const DiscretisedDensity<3, float> &density) { - + // KT does not understand this `if` statement. The cache has nothing to do with symmetries etc. if (proj_matrix_ptr->is_cache_enabled()) { ProjMatrixElemsForOneBin proj_matrix_row; @@ -249,5 +250,6 @@ ForwardProjectorByBinUsingProjMatrixByBin:: else error("ForwardProjectorByBinUsingProjMatrixByBin: Symmetries should be handled by ProjMatrix. Abort. "); } +#endif END_NAMESPACE_STIR diff --git a/src/recon_buildblock/ForwardProjectorByBinUsingRayTracing.cxx b/src/recon_buildblock/ForwardProjectorByBinUsingRayTracing.cxx index d9fcba5db2..9558bbf590 100644 --- a/src/recon_buildblock/ForwardProjectorByBinUsingRayTracing.cxx +++ b/src/recon_buildblock/ForwardProjectorByBinUsingRayTracing.cxx @@ -1451,12 +1451,14 @@ forward_project_all_symmetries_2D(Viewgram & pos_view, stop_timers(); } +#if 0 // disabled as currently not used. needs to be written in the new style anyway void ForwardProjectorByBinUsingRayTracing:: actual_forward_project(Bin& this_bin, const DiscretisedDensity<3,float>& density) { - error("ForwardProjectorByBinUsingRayTracing is not supported for list-mode data. Abort."); + error("ForwardProjectorByBinUsingRayTracing does not support single-bin forward projection. Abort."); } +#endif END_NAMESPACE_STIR diff --git a/src/recon_buildblock/PresmoothingForwardProjectorByBin.cxx b/src/recon_buildblock/PresmoothingForwardProjectorByBin.cxx index e90a0a20cf..0393bcd5ba 100644 --- a/src/recon_buildblock/PresmoothingForwardProjectorByBin.cxx +++ b/src/recon_buildblock/PresmoothingForwardProjectorByBin.cxx @@ -148,11 +148,13 @@ actual_forward_project(RelatedViewgrams& viewgrams, min_tangential_pos_num, max_tangential_pos_num); } +#if 0 // disabled as currently not used. needs to be written in the new style anyway void PresmoothingForwardProjectorByBin::actual_forward_project(Bin&, const DiscretisedDensity<3,float>&) { - + error("TODO"); } +#endif END_NAMESPACE_STIR From 8683ef71c6909f27aefcf44920a2d3f8edc24697 Mon Sep 17 00:00:00 2001 From: Kris Thielemans Date: Fri, 7 Feb 2020 23:13:52 +0000 Subject: [PATCH 137/170] removed unused variable --- src/include/stir/recon_buildblock/ProjMatrixByBin.inl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl index 259e6e4a61..939c545073 100644 --- a/src/include/stir/recon_buildblock/ProjMatrixByBin.inl +++ b/src/include/stir/recon_buildblock/ProjMatrixByBin.inl @@ -153,8 +153,6 @@ ProjMatrixByBin::apply_tof_kernel_and_symm_transformation(ProjMatrixElemsForOneB float low_dist = 0.f; float high_dist = 0.f; - float d1; - // The direction can be from 1 -> 2 depending on the bin sign. const CartesianCoordinate3D middle = (point1 + point2)*0.5f; const CartesianCoordinate3D diff = point2 - middle; From fafeefdd0b2f4512a885c1f6b94e1203f48c6770 Mon Sep 17 00:00:00 2001 From: Kris Thielemans Date: Sat, 8 Feb 2020 07:47:49 +0000 Subject: [PATCH 138/170] implemented codacy recommendations --- src/recon_test/test_consistency_root.cxx | 3 ++- src/test/test_time_of_flight.cxx | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/recon_test/test_consistency_root.cxx b/src/recon_test/test_consistency_root.cxx index 09dbb8b671..e34201c38c 100644 --- a/src/recon_test/test_consistency_root.cxx +++ b/src/recon_test/test_consistency_root.cxx @@ -69,7 +69,8 @@ class ROOTconsistency_Tests : public RunTests // used to calculate the centre of gravity (see below). class LORMax{ public: - LORMax() { voxel_centre = CartesianCoordinate3D(0.f,0.f,0.f); value = 0.f;} + LORMax() : voxel_centre(CartesianCoordinate3D(0.f,0.f,0.f)), value(0.f) + {} CartesianCoordinate3D voxel_centre; float value; }; diff --git a/src/test/test_time_of_flight.cxx b/src/test/test_time_of_flight.cxx index 439fddcfa2..3ddd24922a 100644 --- a/src/test/test_time_of_flight.cxx +++ b/src/test/test_time_of_flight.cxx @@ -467,14 +467,12 @@ export_lor(ProjMatrixElemsForOneBin& probabilities, tmp.float1 = d2; ProjMatrixElemsForOneBin::iterator element_ptr = probabilities.begin(); - bool found = false; while (element_ptr != probabilities.end()) { if (element_ptr->get_coords() == tmpl_element_ptr->get_coords()) { tmp.float2 = element_ptr->get_value(); - found = true; lor_to_export.push_back(tmp); break; } From 2b0691e25e3e5d4c963093ff25ec8d4be49aa399 Mon Sep 17 00:00:00 2001 From: Kris Thielemans Date: Sat, 8 Feb 2020 21:56:34 +0000 Subject: [PATCH 139/170] enable tests for filling TOF data --- src/test/test_proj_data_in_memory.cxx | 61 ++++++++++++++------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/src/test/test_proj_data_in_memory.cxx b/src/test/test_proj_data_in_memory.cxx index 92d145286a..3c074d825f 100644 --- a/src/test/test_proj_data_in_memory.cxx +++ b/src/test/test_proj_data_in_memory.cxx @@ -64,7 +64,7 @@ void ProjDataInMemoryTests:: run_tests_no_tof() { - std::cerr << "-------- Testing ProjDataInMemory --------\n"; + std::cerr << "-------- Testing ProjDataInMemory without TOF --------\n"; shared_ptr scanner_sptr(new Scanner(Scanner::E953)); shared_ptr proj_data_info_sptr @@ -153,6 +153,7 @@ run_tests_no_tof() // this should call error, so we'll catch it try { + std::cout << "\nthis test should throw an error (which we will catch)\n"; proj_data2.fill(proj_data); check(false, "test fill wtih too small proj_data should have thrown"); } @@ -167,12 +168,12 @@ void ProjDataInMemoryTests:: run_tests_tof() { - std::cerr << "-------- Testing ProjDataInMemory --------\n"; + std::cerr << "-------- Testing ProjDataInMemory with TOF --------\n"; shared_ptr scanner_sptr(new Scanner(Scanner::PETMR_Signa)); shared_ptr proj_data_info_sptr (ProjDataInfo::ProjDataInfoCTI(scanner_sptr, - /*span*/1, 5,/*views*/ 96, /*tang_pos*/64, /*arc_corrected*/ true, 70) + /*span*/1, 10,/*views*/ 96, /*tang_pos*/64, /*arc_corrected*/ true, 70) ); shared_ptr exam_info_sptr(new ExamInfo); @@ -253,41 +254,43 @@ run_tests_tof() { shared_ptr proj_data_info_sptr2 (ProjDataInfo::ProjDataInfoCTI(scanner_sptr, - /*span*/1, 8,/*views*/ 96, /*tang_pos*/128, /*arc_corrected*/ true) + /*span*/1, 8,/*views*/ 96, /*tang_pos*/64, /*arc_corrected*/ true, proj_data.get_tof_mash_factor()) ); // construct without filling ProjDataInMemory proj_data2(exam_info_sptr, proj_data_info_sptr2, false); -// proj_data2.fill(proj_data); -// check_if_equal(proj_data2.get_viewgram(0,0).find_max(), -// proj_data.get_viewgram(0,0).find_max(), -// "test 1 for copy-constructor and get_viewgram"); -// check_if_equal(proj_data2.get_viewgram(1,1).find_max(), -// proj_data.get_viewgram(1,1).find_max(), -// "test 1 for copy-constructor and get_viewgram"); + proj_data2.fill(proj_data); + check_if_equal(proj_data2.get_viewgram(0,0,false,-2).find_max(), + proj_data.get_viewgram(0,0,false,-2).find_max(), + "test 1 for copy-constructor and get_viewgram(0,0,-2)"); + check_if_equal(proj_data2.get_viewgram(1,1,false,2).find_max(), + proj_data.get_viewgram(1,1,false,2).find_max(), + "test 1 for copy-constructor and get_viewgram(1,1,2)"); + } // test fill with smaller input { -// shared_ptr proj_data_info_sptr2 -// (ProjDataInfo::ProjDataInfoCTI(scanner_sptr, -// /*span*/1, 12,/*views*/ 96, /*tang_pos*/128, /*arc_corrected*/ true) -// ); - - -// // construct without filling -// ProjDataInMemory proj_data2(exam_info_sptr, proj_data_info_sptr2, false); -// // this should call error, so we'll catch it -// try -// { -// proj_data2.fill(proj_data); -// check(false, "test fill wtih too small proj_data should have thrown"); -// } -// catch (...) -// { -// // ok -// } + shared_ptr proj_data_info_sptr2 + (ProjDataInfo::ProjDataInfoCTI(scanner_sptr, + /*span*/1, 20,/*views*/ 96, /*tang_pos*/64, /*arc_corrected*/ true, 70) + ); + + // construct without filling + ProjDataInMemory proj_data2(exam_info_sptr, proj_data_info_sptr2, false); + // this should call error, so we'll catch it + try + { + std::cout << "\nthis test should throw an error (which we will catch)\n"; + proj_data2.fill(proj_data); + check(false, "test fill with too small proj_data should have thrown"); + } + catch (...) + { + // ok + } + } } From 4645c1571dd034b9de34ba15f6cd190f2dc7ed11 Mon Sep 17 00:00:00 2001 From: Kris Thielemans Date: Sat, 8 Feb 2020 22:00:00 +0000 Subject: [PATCH 140/170] corrected some diagnostic messages in test_proj_data_in_memory --- src/test/test_proj_data_in_memory.cxx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/test_proj_data_in_memory.cxx b/src/test/test_proj_data_in_memory.cxx index 3c074d825f..ade318c1e5 100644 --- a/src/test/test_proj_data_in_memory.cxx +++ b/src/test/test_proj_data_in_memory.cxx @@ -134,10 +134,10 @@ run_tests_no_tof() proj_data2.fill(proj_data); check_if_equal(proj_data2.get_viewgram(0,0).find_max(), proj_data.get_viewgram(0,0).find_max(), - "test 1 for copy-constructor and get_viewgram"); + "test 1 for constructor, fill and get_viewgram(0,0)"); check_if_equal(proj_data2.get_viewgram(1,1).find_max(), proj_data.get_viewgram(1,1).find_max(), - "test 1 for copy-constructor and get_viewgram"); + "test 1 for constructor, fill and get_viewgram(1,1)"); } // test fill with smaller input @@ -155,7 +155,7 @@ run_tests_no_tof() { std::cout << "\nthis test should throw an error (which we will catch)\n"; proj_data2.fill(proj_data); - check(false, "test fill wtih too small proj_data should have thrown"); + check(false, "test fill with too small proj_data should have thrown"); } catch (...) { @@ -263,10 +263,10 @@ run_tests_tof() proj_data2.fill(proj_data); check_if_equal(proj_data2.get_viewgram(0,0,false,-2).find_max(), proj_data.get_viewgram(0,0,false,-2).find_max(), - "test 1 for copy-constructor and get_viewgram(0,0,-2)"); + "test 1 for constructor, fill and get_viewgram(0,0,-2)"); check_if_equal(proj_data2.get_viewgram(1,1,false,2).find_max(), proj_data.get_viewgram(1,1,false,2).find_max(), - "test 1 for copy-constructor and get_viewgram(1,1,2)"); + "test 1 for constructor, fill and get_viewgram(1,1,2)"); } From 99c8f7ff686a5d1d70a029986e2a55094ea97f82 Mon Sep 17 00:00:00 2001 From: Kris Thielemans Date: Sat, 8 Feb 2020 22:04:25 +0000 Subject: [PATCH 141/170] check TOF range in ProjDataInfo::operator== --- src/buildblock/ProjDataInfo.cxx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/buildblock/ProjDataInfo.cxx b/src/buildblock/ProjDataInfo.cxx index e156ba988e..c08f2d2dbb 100644 --- a/src/buildblock/ProjDataInfo.cxx +++ b/src/buildblock/ProjDataInfo.cxx @@ -724,6 +724,8 @@ blindly_equals(const root_type * const that) const (get_max_view_num()==proj.get_max_view_num()) && (get_min_tangential_pos_num() ==proj.get_min_tangential_pos_num())&& (get_max_tangential_pos_num() ==proj.get_max_tangential_pos_num())&& + (get_min_tof_pos_num() ==proj.get_min_tof_pos_num())&& + (get_max_tof_pos_num() ==proj.get_max_tof_pos_num())&& equal(min_axial_pos_per_seg.begin(), min_axial_pos_per_seg.end(), proj.min_axial_pos_per_seg.begin())&& equal(max_axial_pos_per_seg.begin(), max_axial_pos_per_seg.end(), proj.max_axial_pos_per_seg.begin())&& (*get_scanner_ptr()== *(proj.get_scanner_ptr()))&& @@ -752,7 +754,7 @@ operator !=(const root_type& that) const /*! \return \c true only if the types are the same, they are equal, or the range for the - segments, axial and tangential positions is at least as large. + TOF, segments, axial and tangential positions is at least as large. \warning Currently view ranges have to be identical. */ From e4e58048b0a3b4134adca693b50341171e2d0daf Mon Sep 17 00:00:00 2001 From: Kris Thielemans Date: Sat, 8 Feb 2020 22:52:28 +0000 Subject: [PATCH 142/170] [TOF] fixed bug in max_unmashed_tof_pos_num It was set via min_tof_bin, as opposed to min_unmashed_tof_bin, and was therefore generally too large. This seems harmless though (too small would have been a disaster). Also added some tests on min/max/num functions in ProjDataInfo --- src/buildblock/ProjDataInfo.cxx | 2 +- src/test/test_proj_data_info.cxx | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/buildblock/ProjDataInfo.cxx b/src/buildblock/ProjDataInfo.cxx index c08f2d2dbb..398e9b72f4 100644 --- a/src/buildblock/ProjDataInfo.cxx +++ b/src/buildblock/ProjDataInfo.cxx @@ -200,7 +200,7 @@ ProjDataInfo::set_tof_mash_factor(const int new_num) tof_increament_in_mm = tof_delta_time_to_mm(scanner_ptr->get_size_of_timing_pos()); min_unmashed_tof_pos_num = - (scanner_ptr->get_num_max_of_timing_poss())/2; - max_unmashed_tof_pos_num = min_tof_pos_num + (scanner_ptr->get_num_max_of_timing_poss()) -1; + max_unmashed_tof_pos_num = min_unmashed_tof_pos_num + (scanner_ptr->get_num_max_of_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); diff --git a/src/test/test_proj_data_info.cxx b/src/test/test_proj_data_info.cxx index 73326097c8..9c7e12e103 100644 --- a/src/test/test_proj_data_info.cxx +++ b/src/test/test_proj_data_info.cxx @@ -106,6 +106,30 @@ void ProjDataInfoTests:: test_generic_proj_data_info(ProjDataInfo& proj_data_info) { + cerr << "\tTests on get_min/max_num\n"; + check_if_equal(proj_data_info.get_max_tangential_pos_num() - proj_data_info.get_min_tangential_pos_num() + 1, + proj_data_info.get_num_tangential_poss(), + "basic check on min/max/num_tangential_pos_num"); + check(abs(proj_data_info.get_max_tangential_pos_num() + proj_data_info.get_min_tangential_pos_num()) <=1, + "check on min/max_tangential_pos_num being (almost) centred"); + check_if_equal(proj_data_info.get_max_tof_pos_num() - proj_data_info.get_min_tof_pos_num() + 1, + proj_data_info.get_num_tof_poss(), + "basic check on min/max/num_tof_pos_num"); + check_if_equal(proj_data_info.get_max_tof_pos_num() + proj_data_info.get_min_tof_pos_num(), 0, + "check on min/max_tof_pos_num being (almost) centred"); + check_if_equal(proj_data_info.get_max_view_num() - proj_data_info.get_min_view_num() + 1, + proj_data_info.get_num_views(), + "basic check on min/max/num_view_num"); + check_if_equal(proj_data_info.get_max_segment_num() - proj_data_info.get_min_segment_num() + 1, + proj_data_info.get_num_segments(), + "basic check on min/max/num_segment_num"); + // not strictly necessary in most of the code, but most likely required in some of it + check_if_equal(proj_data_info.get_max_segment_num() + proj_data_info.get_min_segment_num(), 0, + "check on min/max_segment_num being centred"); + check_if_equal(proj_data_info.get_max_axial_pos_num(0) - proj_data_info.get_min_axial_pos_num(0) + 1, + proj_data_info.get_num_axial_poss(0), + "basic check on min/max/num_axial_pos_num"); + cerr << "\tTests on get_LOR/get_bin\n"; int max_diff_segment_num=0; int max_diff_view_num=0; From d0753e303e3518a68c91840c5100ea363a429c40 Mon Sep 17 00:00:00 2001 From: Kris Thielemans Date: Sat, 8 Feb 2020 22:59:48 +0000 Subject: [PATCH 143/170] renamed Scanner::get_num_max_of_timing_poss to get_max_num_timing_poss --- src/IO/InterfileHeader.cxx | 4 ++-- src/buildblock/ProjDataInfo.cxx | 16 ++++++++-------- src/buildblock/Scanner.cxx | 2 +- src/include/stir/ProjDataInfo.inl | 2 +- src/include/stir/Scanner.h | 2 +- src/include/stir/Scanner.inl | 2 +- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/IO/InterfileHeader.cxx b/src/IO/InterfileHeader.cxx index 1a019ff38e..2ba80aa28c 100644 --- a/src/IO/InterfileHeader.cxx +++ b/src/IO/InterfileHeader.cxx @@ -1263,10 +1263,10 @@ bool InterfilePDFSHeader::post_processing() if (guessed_scanner_ptr->is_tof_ready()) { - if (max_num_timing_poss != guessed_scanner_ptr->get_num_max_of_timing_poss()) + 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_num_max_of_timing_poss()); + 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()) diff --git a/src/buildblock/ProjDataInfo.cxx b/src/buildblock/ProjDataInfo.cxx index 398e9b72f4..3aeb954a96 100644 --- a/src/buildblock/ProjDataInfo.cxx +++ b/src/buildblock/ProjDataInfo.cxx @@ -193,14 +193,14 @@ ProjDataInfo::set_tof_mash_factor(const int new_num) { if (scanner_ptr->is_tof_ready() && new_num > 0 ) { - if(tof_mash_factor < 0 || tof_mash_factor > scanner_ptr->get_num_max_of_timing_poss()) + if(tof_mash_factor < 0 || tof_mash_factor > scanner_ptr->get_max_num_timing_poss()) error("ProjDataInfo: TOF mashing factor must be positive and smaller or equal than" "the scanner's number of max timing bins. Abort."); tof_mash_factor = new_num; tof_increament_in_mm = tof_delta_time_to_mm(scanner_ptr->get_size_of_timing_pos()); - min_unmashed_tof_pos_num = - (scanner_ptr->get_num_max_of_timing_poss())/2; - max_unmashed_tof_pos_num = min_unmashed_tof_pos_num + (scanner_ptr->get_num_max_of_timing_poss()) -1; + 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); @@ -225,11 +225,11 @@ ProjDataInfo::set_tof_mash_factor(const int new_num) // 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()); - min_tof_pos_num = - (scanner_ptr->get_num_max_of_timing_poss() / tof_mash_factor)/2; - max_tof_pos_num = min_tof_pos_num + (scanner_ptr->get_num_max_of_timing_poss() / tof_mash_factor) -1; + 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; - min_unmashed_tof_pos_num = - (scanner_ptr->get_num_max_of_timing_poss())/2; - max_unmashed_tof_pos_num = min_tof_pos_num + (scanner_ptr->get_num_max_of_timing_poss()) -1; + min_unmashed_tof_pos_num = - (scanner_ptr->get_max_num_timing_poss())/2; + max_unmashed_tof_pos_num = min_tof_pos_num + (scanner_ptr->get_max_num_timing_poss()) -1; num_tof_bins = max_tof_pos_num - min_tof_pos_num +1 ; @@ -677,7 +677,7 @@ ProjDataInfo* ProjDataInfo::ask_parameters() const int tof_mash_factor = scanner_ptr->is_tof_ready() ? ask_num("Time-of-flight mash factor:", 0, - scanner_ptr->get_num_max_of_timing_poss(), 25) : 0; + scanner_ptr->get_max_num_timing_poss(), 25) : 0; const bool arc_corrected = ask("Is the data arc-corrected?",true); diff --git a/src/buildblock/Scanner.cxx b/src/buildblock/Scanner.cxx index e564765433..567dd39f83 100644 --- a/src/buildblock/Scanner.cxx +++ b/src/buildblock/Scanner.cxx @@ -937,7 +937,7 @@ Scanner::parameter_info() const if (is_tof_ready()) { - s << "Number of TOF time bins :=" << get_num_max_of_timing_poss() << "\n"; + s << "Number of TOF time bins :=" << get_max_num_timing_poss() << "\n"; s << "Size of timing bin (ps) :=" << get_size_of_timing_pos() << "\n"; s << "Timing resolution (ps) :=" << get_timing_resolution() << "\n"; } diff --git a/src/include/stir/ProjDataInfo.inl b/src/include/stir/ProjDataInfo.inl index 887bf993d6..3acf6c15d2 100644 --- a/src/include/stir/ProjDataInfo.inl +++ b/src/include/stir/ProjDataInfo.inl @@ -174,7 +174,7 @@ ProjDataInfo::get_max_tof_pos_num() const float ProjDataInfo::get_coincidence_window_in_pico_sec() const { - return scanner_ptr->is_tof_ready()? (scanner_ptr->get_num_max_of_timing_poss() * + return scanner_ptr->is_tof_ready()? (scanner_ptr->get_max_num_timing_poss() * scanner_ptr->get_size_of_timing_pos()) :(scanner_ptr->get_size_of_timing_pos()); } diff --git a/src/include/stir/Scanner.h b/src/include/stir/Scanner.h index 63f64c8837..7f16997e71 100644 --- a/src/include/stir/Scanner.h +++ b/src/include/stir/Scanner.h @@ -276,7 +276,7 @@ class Scanner /* inline int get_num_layers_singles_units() const; */ inline int get_num_singles_units() const; //! Get the maximum number of TOF bins. - inline int get_num_max_of_timing_poss() const; + inline int get_max_num_timing_poss() const; //! Get the delta t which correspnds to the max number of TOF bins in picosecs. inline float get_size_of_timing_pos() const; //! Get the timing resolution of the scanner. diff --git a/src/include/stir/Scanner.inl b/src/include/stir/Scanner.inl index bc5480ea25..a948c6aac2 100644 --- a/src/include/stir/Scanner.inl +++ b/src/include/stir/Scanner.inl @@ -229,7 +229,7 @@ Scanner::get_reference_energy() const return reference_energy; } -int Scanner::get_num_max_of_timing_poss() const +int Scanner::get_max_num_timing_poss() const { return max_num_of_timing_poss; } From 5a45c96dd6cabb0c309d63277aefc1d95e752814 Mon Sep 17 00:00:00 2001 From: Kris Thielemans Date: Sat, 8 Feb 2020 23:45:27 +0000 Subject: [PATCH 144/170] remove compiler warnings in ProjDataFromStream --- src/buildblock/ProjDataFromStream.cxx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/buildblock/ProjDataFromStream.cxx b/src/buildblock/ProjDataFromStream.cxx index 2b57456d60..f506086f4f 100644 --- a/src/buildblock/ProjDataFromStream.cxx +++ b/src/buildblock/ProjDataFromStream.cxx @@ -386,6 +386,11 @@ ProjDataFromStream::get_offsets(const int view_num, const int segment_num, temp[2] = 0; return temp; } + else + { + error("ProjDataFromStream::get_offsets: unsupported storage_order"); + return vector(); // return something to avoid compiler warning + } } Succeeded @@ -641,7 +646,8 @@ ProjDataFromStream::get_offsets_bin(const Bin this_bin) const } else { - error("ProjDataFromStream::get_offsets_bin: unsupported storage order\n"); + error("ProjDataFromStream::get_offsets_bin: unsupported storage order"); + return vector(); // return something to avoid compiler warning } } @@ -737,7 +743,8 @@ ProjDataFromStream::get_offsets_sino(const int ax_pos_num, const int segment_num } else { - error("ProjDataFromStream::get_offsets_sino: unsupported storage order\n"); + error("ProjDataFromStream::get_offsets_sino: unsupported storage order"); + return vector(); // return something to avoid compiler warning } } From b1402b36b9027382d4834e1b22523e1a3d1dc3d6 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Wed, 8 Apr 2020 17:00:54 -0400 Subject: [PATCH 145/170] fix ProjDataInfo comparison --- src/buildblock/ProjDataInfo.cxx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/buildblock/ProjDataInfo.cxx b/src/buildblock/ProjDataInfo.cxx index 3aeb954a96..6bd9ed613c 100644 --- a/src/buildblock/ProjDataInfo.cxx +++ b/src/buildblock/ProjDataInfo.cxx @@ -724,8 +724,8 @@ blindly_equals(const root_type * const that) const (get_max_view_num()==proj.get_max_view_num()) && (get_min_tangential_pos_num() ==proj.get_min_tangential_pos_num())&& (get_max_tangential_pos_num() ==proj.get_max_tangential_pos_num())&& - (get_min_tof_pos_num() ==proj.get_min_tof_pos_num())&& - (get_max_tof_pos_num() ==proj.get_max_tof_pos_num())&& + (that->is_tof_data() && this->is_tof_data() ? (get_min_tof_pos_num() == proj.get_min_tof_pos_num()) && + (get_max_tof_pos_num() == proj.get_max_tof_pos_num()) : true) && equal(min_axial_pos_per_seg.begin(), min_axial_pos_per_seg.end(), proj.min_axial_pos_per_seg.begin())&& equal(max_axial_pos_per_seg.begin(), max_axial_pos_per_seg.end(), proj.max_axial_pos_per_seg.begin())&& (*get_scanner_ptr()== *(proj.get_scanner_ptr()))&& @@ -774,9 +774,9 @@ operator>=(const ProjDataInfo& proj_data_info) const proj_data_info.get_min_segment_num() < larger_proj_data_info.get_min_segment_num() || proj_data_info.get_max_tangential_pos_num() > larger_proj_data_info.get_max_tangential_pos_num() || proj_data_info.get_min_tangential_pos_num() < larger_proj_data_info.get_min_tangential_pos_num() || - ((proj_data_info.get_min_tof_pos_num() < larger_proj_data_info.get_min_tof_pos_num() || - proj_data_info.get_max_tof_pos_num() > larger_proj_data_info.get_max_tof_pos_num()) && - (proj_data_info.is_tof_data() && larger_proj_data_info.is_tof_data()))) + ((proj_data_info.is_tof_data() && larger_proj_data_info.is_tof_data()) ? + (proj_data_info.get_min_tof_pos_num() < larger_proj_data_info.get_min_tof_pos_num() || + proj_data_info.get_max_tof_pos_num() > larger_proj_data_info.get_max_tof_pos_num()) : true)) return false; for (int segment_num=proj_data_info.get_min_segment_num(); From b70a0c19aca0d3118dc4d228d86fe9c40ebf4065 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Wed, 8 Apr 2020 18:17:15 -0400 Subject: [PATCH 146/170] fix on previous commit --- src/buildblock/ProjDataInfo.cxx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/buildblock/ProjDataInfo.cxx b/src/buildblock/ProjDataInfo.cxx index 6bd9ed613c..27ce1739f0 100644 --- a/src/buildblock/ProjDataInfo.cxx +++ b/src/buildblock/ProjDataInfo.cxx @@ -724,7 +724,7 @@ blindly_equals(const root_type * const that) const (get_max_view_num()==proj.get_max_view_num()) && (get_min_tangential_pos_num() ==proj.get_min_tangential_pos_num())&& (get_max_tangential_pos_num() ==proj.get_max_tangential_pos_num())&& - (that->is_tof_data() && this->is_tof_data() ? (get_min_tof_pos_num() == proj.get_min_tof_pos_num()) && + (proj.is_tof_data() && is_tof_data() ? (get_min_tof_pos_num() == proj.get_min_tof_pos_num()) && (get_max_tof_pos_num() == proj.get_max_tof_pos_num()) : true) && equal(min_axial_pos_per_seg.begin(), min_axial_pos_per_seg.end(), proj.min_axial_pos_per_seg.begin())&& equal(max_axial_pos_per_seg.begin(), max_axial_pos_per_seg.end(), proj.max_axial_pos_per_seg.begin())&& @@ -774,9 +774,9 @@ operator>=(const ProjDataInfo& proj_data_info) const proj_data_info.get_min_segment_num() < larger_proj_data_info.get_min_segment_num() || proj_data_info.get_max_tangential_pos_num() > larger_proj_data_info.get_max_tangential_pos_num() || proj_data_info.get_min_tangential_pos_num() < larger_proj_data_info.get_min_tangential_pos_num() || - ((proj_data_info.is_tof_data() && larger_proj_data_info.is_tof_data()) ? - (proj_data_info.get_min_tof_pos_num() < larger_proj_data_info.get_min_tof_pos_num() || - proj_data_info.get_max_tof_pos_num() > larger_proj_data_info.get_max_tof_pos_num()) : true)) + ((proj_data_info.get_min_tof_pos_num() < larger_proj_data_info.get_min_tof_pos_num() || + proj_data_info.get_max_tof_pos_num() > larger_proj_data_info.get_max_tof_pos_num()) && + (proj_data_info.is_tof_data() && larger_proj_data_info.is_tof_data()))) return false; for (int segment_num=proj_data_info.get_min_segment_num(); From 2bd6cf85188c241affb5ef8b82381c0e7e3c77b5 Mon Sep 17 00:00:00 2001 From: Nikos Efthimiou Date: Sun, 24 May 2020 23:19:52 -0400 Subject: [PATCH 147/170] Delete a small duplicate --- ...ForMeanAndListModeDataWithProjMatrixByBin.cxx | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx index 3ea159a3d5..a63c0b0317 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx @@ -528,18 +528,10 @@ compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, } } - measured_bin.set_bin_value(1.0f); - // If more than 1 subsets, check if the current bin belongs to - // the current. - if (this->num_subsets > 1) - { - Bin basic_bin = measured_bin; - this->PM_sptr->get_symmetries_ptr()->find_basic_bin(basic_bin); - if (subset_num != static_cast(basic_bin.view_num() % this->num_subsets)) - continue; - } - this->PM_sptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, - measured_bin); + measured_bin.set_bin_value(1.0f); + + this->PM_sptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, + measured_bin); //in_the_range++; fwd_bin.set_bin_value(0.0f); From 0f0cccb30c2abe56f6fb2cb2ff6dbf4ade4cb1d4 Mon Sep 17 00:00:00 2001 From: Kris Thielemans Date: Wed, 2 Dec 2020 11:31:52 +0000 Subject: [PATCH 148/170] correct ProjData::get_num_non_tof_sinograms() --- src/include/stir/ProjData.inl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/include/stir/ProjData.inl b/src/include/stir/ProjData.inl index 6b2122589f..5ed5f80f7b 100644 --- a/src/include/stir/ProjData.inl +++ b/src/include/stir/ProjData.inl @@ -93,12 +93,10 @@ int ProjData::get_max_tof_pos_num() const { return proj_data_info_ptr->get_max_tof_pos_num(); } int ProjData::get_num_non_tof_sinograms() const -{ return proj_data_info_ptr->get_num_sinograms(); } +{ return proj_data_info_ptr->get_num_non_tof_sinograms(); } int ProjData::get_num_sinograms() const -{ - return this->get_num_non_tof_sinograms()*this->get_num_tof_poss(); -} +{ return proj_data_info_ptr->get_num_sinograms(); } std::size_t ProjData::size_all() const { return proj_data_info_ptr->size_all(); } From 521be8da8204f87f6e9416ca047d8584b650c3ad Mon Sep 17 00:00:00 2001 From: NikEfth Date: Thu, 17 Dec 2020 20:28:51 -0500 Subject: [PATCH 149/170] If failed to find a TOF bin return min rather than centre --- src/include/stir/ProjDataInfo.inl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/include/stir/ProjDataInfo.inl b/src/include/stir/ProjDataInfo.inl index 3acf6c15d2..0c1507bf75 100644 --- a/src/include/stir/ProjDataInfo.inl +++ b/src/include/stir/ProjDataInfo.inl @@ -99,7 +99,7 @@ ProjDataInfo::get_tof_bin(const double delta) const } // TODO handle differently warning(boost::format("TOF delta time %g out of range") % delta); - return 0; + return min_tof_pos_num; } int @@ -116,7 +116,7 @@ ProjDataInfo::get_unmashed_tof_bin(const double delta) const } // TODO handle differently warning(boost::format("TOF delta time %g out of range") % delta); - return 0; + return min_tof_pos_num; } From 66df04293f7dc29e271e1e3f03c017d62163ca97 Mon Sep 17 00:00:00 2001 From: NikEfth Date: Fri, 18 Dec 2020 01:15:57 -0500 Subject: [PATCH 150/170] Update LmToProjData.cxx and CListEventCylindricalScannerWithDiscreteDetectors.inl Uncommented the two commands I had left out. --- ...ventCylindricalScannerWithDiscreteDetectors.inl | 4 ++-- src/listmode_buildblock/LmToProjData.cxx | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/include/stir/listmode/CListEventCylindricalScannerWithDiscreteDetectors.inl b/src/include/stir/listmode/CListEventCylindricalScannerWithDiscreteDetectors.inl index 91582b0252..303035e2c6 100644 --- a/src/include/stir/listmode/CListEventCylindricalScannerWithDiscreteDetectors.inl +++ b/src/include/stir/listmode/CListEventCylindricalScannerWithDiscreteDetectors.inl @@ -32,8 +32,8 @@ START_NAMESPACE_STIR CListEventCylindricalScannerWithDiscreteDetectors:: CListEventCylindricalScannerWithDiscreteDetectors(const shared_ptr& proj_data_info_sptr) { -// this->uncompressed_proj_data_info_sptr.reset(dynamic_cast( -// *proj_data_info_sptr); + this->uncompressed_proj_data_info_sptr.reset(dynamic_cast( + proj_data_info_sptr.get())); if (is_null_ptr(this->uncompressed_proj_data_info_sptr)) error("CListEventCylindricalScannerWithDiscreteDetectors takes only ProjDataInfoCylindricalNoArcCorr. Abord."); diff --git a/src/listmode_buildblock/LmToProjData.cxx b/src/listmode_buildblock/LmToProjData.cxx index bb4add8641..78c954d539 100644 --- a/src/listmode_buildblock/LmToProjData.cxx +++ b/src/listmode_buildblock/LmToProjData.cxx @@ -321,13 +321,13 @@ post_processing() { shared_ptr scanner_sptr(new Scanner(*scanner_ptr)); // TODO this won't work for the HiDAC or so -// proj_data_info_cyl_uncompressed_ptr.reset(dynamic_cast( -// ProjDataInfo::ProjDataInfoCTI(scanner_sptr, -// 1, scanner_ptr->get_num_rings()-1, -// scanner_ptr->get_num_detectors_per_ring()/2, -// scanner_ptr->get_default_num_arccorrected_bins(), -// false, -// 1))); + // N.E: The following command used to do a dynamic cast which now I removed. + proj_data_info_cyl_uncompressed_ptr.reset(ProjDataInfo::ProjDataInfoCTI(scanner_sptr, + 1, scanner_ptr->get_num_rings()-1, + scanner_ptr->get_num_detectors_per_ring()/2, + scanner_ptr->get_default_num_arccorrected_bins(), + false, + 1)); if ( normalisation_ptr->set_up(proj_data_info_cyl_uncompressed_ptr) != Succeeded::yes) From 7f778d1588e28b9358e528acc9b9e006547da173 Mon Sep 17 00:00:00 2001 From: NikEfth Date: Sat, 19 Dec 2020 01:10:02 -0500 Subject: [PATCH 151/170] Curently Passes: * run_tests.sh * run_root_GATE.sh * run_scatter_tests.sh Update bcktest.cxx, ListModeData.cxx, and 4 more files... --- recon_test_pack/simulate_PET_data_for_tests.sh | 1 + .../CListEventCylindricalScannerWithDiscreteDetectors.inl | 3 +-- src/include/stir/listmode/ListModeData.h | 2 +- src/listmode_buildblock/CListModeDataROOT.cxx | 7 ++++--- src/listmode_buildblock/CListRecordROOT.cxx | 4 ++-- src/listmode_buildblock/ListModeData.cxx | 6 +++--- src/recon_test/bcktest.cxx | 6 +++--- src/test/test_multiple_proj_data.cxx | 2 +- 8 files changed, 16 insertions(+), 15 deletions(-) diff --git a/recon_test_pack/simulate_PET_data_for_tests.sh b/recon_test_pack/simulate_PET_data_for_tests.sh index e1b965bd1a..54df49d607 100755 --- a/recon_test_pack/simulate_PET_data_for_tests.sh +++ b/recon_test_pack/simulate_PET_data_for_tests.sh @@ -48,6 +48,7 @@ template_sino=my_DSTE_3D_rd2_template.hs cat > my_input.txt <& proj_data_info_sptr) { - this->uncompressed_proj_data_info_sptr.reset(dynamic_cast( - proj_data_info_sptr.get())); + this->uncompressed_proj_data_info_sptr = std::dynamic_pointer_cast< const ProjDataInfoCylindricalNoArcCorr >(proj_data_info_sptr->create_shared_clone()); if (is_null_ptr(this->uncompressed_proj_data_info_sptr)) error("CListEventCylindricalScannerWithDiscreteDetectors takes only ProjDataInfoCylindricalNoArcCorr. Abord."); diff --git a/src/include/stir/listmode/ListModeData.h b/src/include/stir/listmode/ListModeData.h index 75e0e20ba7..3bea3f59fd 100644 --- a/src/include/stir/listmode/ListModeData.h +++ b/src/include/stir/listmode/ListModeData.h @@ -221,7 +221,7 @@ class ListModeData : public ExamData list mode data that is being read. \warning This member is obsolete and might be removed soon. */ - virtual const Scanner* get_scanner_ptr() const ; + virtual shared_ptr get_scanner_ptr() const ; //! Return if the file stores delayed events as well (as opposed to prompts) virtual bool has_delayeds() const = 0; diff --git a/src/listmode_buildblock/CListModeDataROOT.cxx b/src/listmode_buildblock/CListModeDataROOT.cxx index 9e620b021f..4d7ab3762f 100644 --- a/src/listmode_buildblock/CListModeDataROOT.cxx +++ b/src/listmode_buildblock/CListModeDataROOT.cxx @@ -185,14 +185,15 @@ CListModeDataROOT(const std::string& hroot_filename) error(error_str.c_str()); } - shared_ptr tmp( ProjDataInfo::construct_proj_data_info(this_scanner_sptr, + proj_data_info_sptr = std::const_pointer_cast( ProjDataInfo::construct_proj_data_info(this_scanner_sptr, 1, this_scanner_sptr->get_num_rings()-1, this_scanner_sptr->get_num_detectors_per_ring()/2, this_scanner_sptr->get_max_num_non_arccorrected_bins(), /* arc_correction*/false, - tof_mash_factor)); - this->set_proj_data_info_sptr(tmp); + tof_mash_factor)->create_shared_clone()); + //this->set_proj_data_info_sptr(tmp); + if (this->open_lm_file() == Succeeded::no) error("CListModeDataROOT: error opening ROOT file for filename '%s'", diff --git a/src/listmode_buildblock/CListRecordROOT.cxx b/src/listmode_buildblock/CListRecordROOT.cxx index fedb7e7f97..c4f033c422 100644 --- a/src/listmode_buildblock/CListRecordROOT.cxx +++ b/src/listmode_buildblock/CListRecordROOT.cxx @@ -51,8 +51,8 @@ void CListEventROOT::get_detection_position(DetectionPositionPair<>& _det_pos) c _det_pos.pos1() = det1; _det_pos.pos2() = det2; -// _det_pos.timing_pos() = this->get_uncompressed_proj_data_info_sptr()->get_tof_bin(delta_time); - _det_pos.timing_pos() = this->get_uncompressed_proj_data_info_sptr()->get_unmashed_tof_bin(delta_time); + _det_pos.timing_pos() = this->get_uncompressed_proj_data_info_sptr()->get_tof_bin(delta_time); +// _det_pos.timing_pos() = this->get_uncompressed_proj_data_info_sptr()->get_unmashed_tof_bin(delta_time); } void CListEventROOT::set_detection_position(const DetectionPositionPair<>&) diff --git a/src/listmode_buildblock/ListModeData.cxx b/src/listmode_buildblock/ListModeData.cxx index e223c85eac..6fcafc0f9a 100644 --- a/src/listmode_buildblock/ListModeData.cxx +++ b/src/listmode_buildblock/ListModeData.cxx @@ -40,20 +40,20 @@ ListModeData:: ~ListModeData() {} -const Scanner* +shared_ptr ListModeData:: get_scanner_ptr() const { if(is_null_ptr(proj_data_info_sptr)) error("ListModeData: ProjDataInfo has not been set."); - return proj_data_info_sptr->get_scanner_ptr(); + return proj_data_info_sptr->get_scanner_sptr(); } void ListModeData:: set_proj_data_info_sptr(shared_ptr new_proj_data_info_sptr) { - proj_data_info_sptr = new_proj_data_info_sptr; + proj_data_info_sptr = new_proj_data_info_sptr->create_shared_clone(); } shared_ptr diff --git a/src/recon_test/bcktest.cxx b/src/recon_test/bcktest.cxx index 84841caab4..15286536bc 100644 --- a/src/recon_test/bcktest.cxx +++ b/src/recon_test/bcktest.cxx @@ -280,10 +280,10 @@ main(int argc, char **argv) do { int min_timing_num = ask_num("Minimum timing position index to backproject", - proj_data_info_ptr->get_min_tof_pos_num(), proj_data_info_ptr->get_max_tof_pos_num(), - proj_data_info_ptr->get_min_tof_pos_num()); + proj_data_info_sptr->get_min_tof_pos_num(), proj_data_info_sptr->get_max_tof_pos_num(), + proj_data_info_sptr->get_min_tof_pos_num()); int max_timing_num = ask_num("Maximum timing position index to backproject", - min_timing_num, proj_data_info_ptr->get_max_tof_pos_num(), + min_timing_num, proj_data_info_sptr->get_max_tof_pos_num(), min_timing_num); int min_segment_num = ask_num("Minimum segment number to backproject", proj_data_info_sptr->get_min_segment_num(), proj_data_info_sptr->get_max_segment_num(), 0); diff --git a/src/test/test_multiple_proj_data.cxx b/src/test/test_multiple_proj_data.cxx index 35445558e1..b03d2af0b8 100644 --- a/src/test/test_multiple_proj_data.cxx +++ b/src/test/test_multiple_proj_data.cxx @@ -41,7 +41,7 @@ #include "stir/MultipleProjData.h" #include "stir/ProjDataInMemory.h" #include - +#include #ifndef STIR_NO_NAMESPACES using std::cerr; using std::setw; From dc0b2933a72b1816e2c3e17efe0d78e92bf66f5c Mon Sep 17 00:00:00 2001 From: NikEfth Date: Sat, 19 Dec 2020 02:33:19 -0500 Subject: [PATCH 152/170] Update and Pass run_test_simulate_and_recon.sh --- .../run_test_simulate_and_recon.sh | 25 +------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/recon_test_pack/run_test_simulate_and_recon.sh b/recon_test_pack/run_test_simulate_and_recon.sh index 67567ea0ec..9405b3937e 100755 --- a/recon_test_pack/run_test_simulate_and_recon.sh +++ b/recon_test_pack/run_test_simulate_and_recon.sh @@ -77,31 +77,8 @@ echo "Using `command -v OSMAPOSL`" LC_ALL=C export LC_ALL -<<<<<<< HEAD -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_create_${template_sino}.log 2>&1 -if [ $? -ne 0 ]; then - echo "ERROR running create_projdata_template. Check my_create_${template_sino}.log"; exit 1; -fi - -# create sinograms -./simulate_data.sh my_uniform_cylinder.hv my_atten_image.hv ${template_sino} -======= ./simulate_PET_data_for_tests.sh ->>>>>>> master + if [ $? -ne 0 ]; then echo "Error running simulation" exit 1 From 11d9b04e42368b4b94bd14621f96370e40adc879 Mon Sep 17 00:00:00 2001 From: NikEfth Date: Sat, 19 Dec 2020 17:40:30 -0500 Subject: [PATCH 153/170] Compiles with GESinga TOF support and stir.i updates. Update stir.i, CListModeDataGEHDF5.cxx, and 3 more files... --- src/buildblock/ProjDataGEHDF5.cxx | 6 +- src/include/stir/ProjDataGEHDF5.h | 6 +- src/include/stir/listmode/CListRecordGEHDF5.h | 204 +++--------------- .../CListModeDataGEHDF5.cxx | 2 +- src/swig/stir.i | 61 +++--- 5 files changed, 75 insertions(+), 204 deletions(-) diff --git a/src/buildblock/ProjDataGEHDF5.cxx b/src/buildblock/ProjDataGEHDF5.cxx index c1cf35dd92..5c046773a5 100644 --- a/src/buildblock/ProjDataGEHDF5.cxx +++ b/src/buildblock/ProjDataGEHDF5.cxx @@ -125,7 +125,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"); @@ -194,7 +195,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/include/stir/ProjDataGEHDF5.h b/src/include/stir/ProjDataGEHDF5.h index 2adcb21bb2..9dca79e642 100644 --- a/src/include/stir/ProjDataGEHDF5.h +++ b/src/include/stir/ProjDataGEHDF5.h @@ -67,9 +67,11 @@ class ProjDataGEHDF5 : public ProjData //! Set Sinogram Succeeded set_sinogram(const Sinogram& s); //! Get Viewgram - Viewgram get_viewgram(const int view_num, const int segment_num,const bool make_num_tangential_poss_odd=false) const; + Viewgram get_viewgram(const int view_num, const int segment_num,const bool make_num_tangential_poss_odd=false, + const int timing_pos = 0) const; //! Get Sinogram - Sinogram get_sinogram(const int ax_pos_num, const int sergment_num,const bool make_num_tangential_poss_odd=false) const; + Sinogram get_sinogram(const int ax_pos_num, const int sergment_num,const bool make_num_tangential_poss_odd=false, + const int timing_pos = 0) const; //! Get the segment sequence std::vector get_segment_sequence_in_hdf5() const; std::vector< unsigned int > seg_ax_offset; diff --git a/src/include/stir/listmode/CListRecordGEHDF5.h b/src/include/stir/listmode/CListRecordGEHDF5.h index b88b5c1d96..06d2d5f234 100644 --- a/src/include/stir/listmode/CListRecordGEHDF5.h +++ b/src/include/stir/listmode/CListRecordGEHDF5.h @@ -32,77 +32,6 @@ START_NAMESPACE_STIR namespace GE { namespace RDF_HDF5 { -<<<<<<< HEAD:src/include/stir/listmode/CListRecordGESigna.h -/*********************************** - * Supported Event Types - ***********************************/ -enum EventType -{ - EXTENDED_EVT = 0x0, - COINC_EVT = 0x1 -}; - -/*********************************** - * Supported Extended Event Types - ***********************************/ -enum ExtendedEvtType -{ - TIME_MARKER_EVT = 0x0, - COINC_COUNT_EVT = 0x1, - EXTERN_TRIG_EVT = 0x2, - TABLE_POS_EVT = 0x3, - /* RESERVED = 0x4 to 0xE */ - /* 0xE is temporary taken here to mark end of it. */ - END_LIST_EVT = 0xE, - SINGLE_EVT = 0xF -}; - - -//! Class for storing and using a coincidence event from a GE Signa PET/MR listmode file -/*! \ingroup listmode - \ingroup GE - This class cannot have virtual functions, as it needs to just store the data 6 bytes for CListRecordGESigna to work. -*/ -class CListEventDataGESigna -{ - public: - inline bool is_prompt() const { return true; } // TODO - inline Succeeded set_prompt(const bool prompt = true) - { - //if (prompt) random=1; else random=0; return Succeeded::yes; - return Succeeded::no; - } - inline void get_detection_position(DetectionPositionPair<>& det_pos) const - { - // TODO 447->get_num_detectors_per_ring()-1 - if (deltaTime<0) - { - det_pos.pos1().tangential_coord() = 447 - hiXtalTransAxID; - det_pos.pos1().axial_coord() = hiXtalAxialID; - det_pos.pos2().tangential_coord() = 447 - loXtalTransAxID; - det_pos.pos2().axial_coord() = loXtalAxialID; - det_pos.timing_pos() = -get_tof_bin(); - } - else - { - det_pos.pos1().tangential_coord() = 447 - loXtalTransAxID; - det_pos.pos1().axial_coord() = loXtalAxialID; - det_pos.pos2().tangential_coord() = 447 - hiXtalTransAxID; - det_pos.pos2().axial_coord() = hiXtalAxialID; - det_pos.timing_pos() = get_tof_bin(); - } - } - inline bool is_event() const - { - return (eventType==COINC_EVT)/* && eventTypeExt==COINC_COUNT_EVT)*/; - } // TODO need to find out how to see if it's a coincidence event - inline int get_tof_bin() const - { - return static_cast(deltaTime); - } - private: - -======= namespace detail { /*********************************** * Supported Event Length Modes @@ -146,7 +75,6 @@ class CListEventDataGESigna class CListAnyRecordDataGEHDF5 { public: ->>>>>>> master:src/include/stir/listmode/CListRecordGEHDF5.h #if STIRIsNativeByteOrderBigEndian // Do byteswapping first before using this bit field. TODO; @@ -172,39 +100,24 @@ class CListEventDataGESigna //if (prompt) random=1; else random=0; return Succeeded::yes; return Succeeded::no; } + inline void get_detection_position(DetectionPositionPair<>& det_pos) const + { + // TODO 447->get_num_detectors_per_ring()-1 + det_pos.pos1().tangential_coord() = 447 - loXtalTransAxID; + det_pos.pos1().axial_coord() = loXtalAxialID; + det_pos.pos2().tangential_coord() = 447 - hiXtalTransAxID; +// std::cout << hiXtalTransAxID << " " << loXtalTransAxID << std::endl; + det_pos.pos2().axial_coord() = hiXtalAxialID; + } inline bool is_event() const { return (eventType==COINC_EVT)/* && eventTypeExt==COINC_COUNT_EVT)*/; } // TODO need to find out how to see if it's a coincidence event -<<<<<<< HEAD:src/include/stir/listmode/CListRecordGESigna.h -//! A class for storing and using a timing 'event' from a GE Signa PET/MR listmode file -/*! \ingroup listmode - \ingroup GE - This class cannot have virtual functions, as it needs to just store the data 6 bytes for CListRecordGESigna to work. - */ -class ListTimeDataGESigna -{ - public: - inline unsigned long get_time_in_millisecs() const - { return (time_hi()<<16) | time_lo(); } - inline Succeeded set_time_in_millisecs(const unsigned long time_in_millisecs) - { - data.timeMarkerLS = ((1UL<<16)-1) & (time_in_millisecs); - data.timeMarkerMS = (time_in_millisecs) >> 16; - // TODO return more useful value - return Succeeded::yes; - } - inline bool is_time() const - { // TODO need to find out how to see if it's a timing event - return (data.eventType==EXTENDED_EVT) && (data.eventTypeExt==TIME_MARKER_EVT); - }// TODO - -private: - typedef union{ - struct { -======= ->>>>>>> master:src/include/stir/listmode/CListRecordGEHDF5.h + inline int get_tof_bin() const + { + return static_cast(deltaTime); + } #if STIRIsNativeByteOrderBigEndian // Do byteswapping first before using this bit field. TODO @@ -221,16 +134,8 @@ class ListTimeDataGESigna boost::uint16_t loXtalAxialID:6; /* Low Crystal Axial Id */ boost::uint16_t loXtalTransAxID:10; /* Low Crystal Trans-Axial Id */ #endif -<<<<<<< HEAD:src/include/stir/listmode/CListRecordGESigna.h - }; - } data_t; - data_t data; + }; /*-coincidence event*/ - unsigned long time_lo() const - { return data.timeMarkerLS; } - unsigned long time_hi() const - { return data.timeMarkerMS; } -}; #if 0 //! A class for storing and using a trigger 'event' from a GE Signa PET/MR listmode file @@ -262,9 +167,22 @@ class CListGatingDataGESigna private: typedef union{ struct { -======= - }; /*-coincidence event*/ +#if STIRIsNativeByteOrderBigEndian + boost::uint32_t signature : 5; + boost::uint32_t reserved : 3; + boost::uint32_t value : 24; // timing info here in the first word, but we're ignoring it +#else + boost::uint32_t value : 24; + boost::uint32_t reserved : 3; + boost::uint32_t signature : 5; +#endif + }; + boost::uint32_t raw; + } oneword_t; + oneword_t words[2]; +}; +#endif //! A class for storing and using a timing 'event' from a GE RDF9 listmode file /*! \ingroup listmode @@ -291,7 +209,6 @@ class CListGatingDataGESigna private: typedef union{ struct { ->>>>>>> master:src/include/stir/listmode/CListRecordGEHDF5.h #if STIRIsNativeByteOrderBigEndian TODO #else @@ -306,17 +223,9 @@ class CListGatingDataGESigna boost::uint16_t timeMarkerLS:16; /* Least Significant 16 bits of 32-bit Time Marker */ boost::uint16_t timeMarkerMS:16; /* Most Significant 16 bits of 32-bitTime Marker */ #endif -<<<<<<< HEAD:src/include/stir/listmode/CListRecordGESigna.h - }; - boost::uint32_t raw; - } oneword_t; - oneword_t words[2]; -}; -======= }; } data_t; data_t data; ->>>>>>> master:src/include/stir/listmode/CListRecordGEHDF5.h unsigned long time_lo() const { return data.timeMarkerLS; } @@ -340,30 +249,20 @@ class CListRecordGEHDF5 : public CListRecord, public ListTime, // public CListGa //typedef CListGatingDataGEHDF5 GatingType; public: -<<<<<<< HEAD:src/include/stir/listmode/CListRecordGESigna.h - CListRecordGESigna(const shared_ptr& proj_data_info_sptr) : - CListEventCylindricalScannerWithDiscreteDetectors(proj_data_info_sptr) - {} - - bool is_time() const - { - return this->time_data.is_time(); -======= //! constructor /*! Takes the scanner and first_time stamp. The former will be used for checking and swapping, the latter for adjusting the time of each event, as GE listmode files do not start with time-stamp 0. get_time_in_millisecs() should therefore be zero at the first time stamp. */ - CListRecordGEHDF5(const shared_ptr& scanner_sptr, const unsigned long first_time_stamp) : - CListEventCylindricalScannerWithDiscreteDetectors(scanner_sptr), + CListRecordGEHDF5(const shared_ptr& proj_data_info_sptr, const unsigned long first_time_stamp) : + CListEventCylindricalScannerWithDiscreteDetectors(proj_data_info_sptr), first_time_stamp(first_time_stamp) {} bool is_time() const { return this->time_data.is_time(); ->>>>>>> master:src/include/stir/listmode/CListRecordGEHDF5.h } #if 0 bool is_gating_input() const @@ -399,15 +298,10 @@ dynamic_cast(&e2) != 0 && #endif } -<<<<<<< HEAD:src/include/stir/listmode/CListRecordGESigna.h - // time - inline unsigned long get_time_in_millisecs() const - { return time_data.get_time_in_millisecs(); } -======= // time inline unsigned long get_time_in_millisecs() const { return time_data.get_time_in_millisecs() - first_time_stamp; } ->>>>>>> master:src/include/stir/listmode/CListRecordGEHDF5.h + inline Succeeded set_time_in_millisecs(const unsigned long time_in_millisecs) { return time_data.set_time_in_millisecs(time_in_millisecs); } #if 0 @@ -423,9 +317,9 @@ dynamic_cast(&e2) != 0 && virtual void get_detection_position(DetectionPositionPair<>& det_pos) const { - det_pos.pos1().tangential_coord() = scanner_sptr->get_num_detectors_per_ring() - 1 - event_data.loXtalTransAxID; + det_pos.pos1().tangential_coord() = this->uncompressed_proj_data_info_sptr->get_scanner_sptr()->get_num_detectors_per_ring() - 1 - event_data.loXtalTransAxID; det_pos.pos1().axial_coord() = event_data.loXtalAxialID; - det_pos.pos2().tangential_coord() = scanner_sptr->get_num_detectors_per_ring() - 1 - event_data.hiXtalTransAxID; + det_pos.pos2().tangential_coord() = this->uncompressed_proj_data_info_sptr->get_scanner_sptr()->get_num_detectors_per_ring() - 1 - event_data.hiXtalTransAxID; det_pos.pos2().axial_coord() = event_data.hiXtalAxialID; } @@ -435,22 +329,6 @@ dynamic_cast(&e2) != 0 && error("TODO"); } -<<<<<<< HEAD:src/include/stir/listmode/CListRecordGESigna.h - virtual std::size_t size_of_record_at_ptr(const char * const data_ptr, const std::size_t /*size*/, - const bool do_byte_swap) const - { - // TODO: get size of record from the file, whereas here I have hard-coded as being 6bytes (I know it's the case for the Orsay data) OtB 15/09 - - return std::size_t(6); // std::size_t(data_ptr[0]&0x80); - } - - virtual Succeeded init_from_data_ptr(const char * const data_ptr, - const std::size_t -#ifndef NDEBUG - size // only used within assert, so don't define otherwise to avoid compiler warning -#endif - , const bool do_byte_swap) -======= virtual std::size_t size_of_record_at_ptr(const char * const data_ptr, const std::size_t, const bool do_byte_swap) const { @@ -478,7 +356,6 @@ dynamic_cast(&e2) != 0 && virtual Succeeded init_from_data_ptr(const char * const data_ptr, const std::size_t size, const bool do_byte_swap) ->>>>>>> master:src/include/stir/listmode/CListRecordGEHDF5.h { assert(size >= 6); assert(size <= 16); @@ -486,7 +363,6 @@ dynamic_cast(&e2) != 0 && if (do_byte_swap) { -<<<<<<< HEAD:src/include/stir/listmode/CListRecordGESigna.h ByteOrder::swap_order(this->raw[0]); } if (this->is_event() || this->is_time()) @@ -501,10 +377,6 @@ dynamic_cast(&e2) != 0 && { error("don't know how to byteswap"); ByteOrder::swap_order(this->raw[1]); -======= - error("ClistRecordGEHDF5: byte-swapping not supported yet. sorry"); - //ByteOrder::swap_order(this->raw[0]); ->>>>>>> master:src/include/stir/listmode/CListRecordGEHDF5.h } if (this->is_event()) @@ -512,9 +384,6 @@ dynamic_cast(&e2) != 0 && // set TOF info in ps this->delta_time = this->event_data.get_tof_bin() *this-> get_scanner_ptr()->get_size_of_timing_pos(); } - - - return Succeeded::yes; } @@ -528,13 +397,8 @@ dynamic_cast(&e2) != 0 && }; BOOST_STATIC_ASSERT(sizeof(boost::int32_t)==4); BOOST_STATIC_ASSERT(sizeof(DataType)==6); -<<<<<<< HEAD:src/include/stir/listmode/CListRecordGESigna.h - BOOST_STATIC_ASSERT(sizeof(TimeType)==6); - //BOOST_STATIC_ASSERT(sizeof(GatingType)==8); -======= BOOST_STATIC_ASSERT(sizeof(TimeType)==6); //BOOST_STATIC_ASSERT(sizeof(GatingType)==8); ->>>>>>> master:src/include/stir/listmode/CListRecordGEHDF5.h }; diff --git a/src/listmode_buildblock/CListModeDataGEHDF5.cxx b/src/listmode_buildblock/CListModeDataGEHDF5.cxx index 64bfcb4c59..d50cc8ce87 100644 --- a/src/listmode_buildblock/CListModeDataGEHDF5.cxx +++ b/src/listmode_buildblock/CListModeDataGEHDF5.cxx @@ -65,7 +65,7 @@ get_empty_record_sptr() const if (is_null_ptr(this->get_proj_data_info_sptr())) error("listmode file needs to be opened before calling get_empty_record_sptr()"); - shared_ptr sptr(new CListRecordT(this->get_proj_data_info_sptr()->get_scanner_sptr(), + shared_ptr sptr(new CListRecordT(this->get_proj_data_info_sptr(), this->first_time_stamp)); return sptr; } diff --git a/src/swig/stir.i b/src/swig/stir.i index 4909212482..e8ad993ab7 100644 --- a/src/swig/stir.i +++ b/src/swig/stir.i @@ -707,26 +707,26 @@ namespace std { proj_data.fill_from(array_iter); } - static Array<3,float> create_array_for_proj_data(const ProjData& proj_data) - { - // int num_sinos=proj_data.get_num_axial_poss(0); - // for (int s=1; s<= proj_data.get_max_segment_num(); ++s) - // { - // num_sinos += 2*proj_data.get_num_axial_poss(s); - // } - int num_sinos = proj_data.get_num_sinograms(); - - Array<3,float> array(IndexRange3D(num_sinos, proj_data.get_num_views(), proj_data.get_num_tangential_poss())); - return array; - } - - static Array<3,float> projdata_to_3D(const ProjData& proj_data) - { - Array<3,float> array = create_array_for_proj_data(proj_data); - Array<3,float>::full_iterator array_iter = array.begin_all(); - copy_to(proj_data, array_iter); - return array; - } +// static Array<3,float> create_array_for_proj_data(const ProjData& proj_data) +// { +// // int num_sinos=proj_data.get_num_axial_poss(0); +// // for (int s=1; s<= proj_data.get_max_segment_num(); ++s) +// // { +// // num_sinos += 2*proj_data.get_num_axial_poss(s); +// // } +// int num_sinos = proj_data.get_num_sinograms(); + +// Array<3,float> array(IndexRange3D(num_sinos, proj_data.get_num_views(), proj_data.get_num_tangential_poss())); +// return array; +// } + +// static Array<3,float> projdata_to_3D(const ProjData& proj_data) +// { +// Array<3,float> array = create_array_for_proj_data(proj_data); +// Array<3,float>::full_iterator array_iter = array.begin_all(); +// copy_to(proj_data, array_iter); +// return array; +// } } // end of namespace @@ -1448,14 +1448,14 @@ namespace stir { if (PyIter_Check(arg)) { // From TOF branch - //Array<4,float> array = swigstir::create_array_for_proj_data(*$self); - //swigstir::fill_Array_from_Python_iterator(&array, arg); - //swigstir::fill_proj_data_from_4D(*$self, array); + Array<4,float> array = swigstir::create_array_for_proj_data(*$self); + swigstir::fill_Array_from_Python_iterator(&array, arg); + swigstir::fill_proj_data_from_4D(*$self, array); // TODO avoid need for copy to Array - Array<3,float> array = swigstir::create_array_for_proj_data(*$self); - swigstir::fill_Array_from_Python_iterator(&array, arg); - fill_from(*$self, array.begin_all(), array.end_all()); +// Array<3,float> array = swigstir::create_array_for_proj_data(*$self); +// swigstir::fill_Array_from_Python_iterator(&array, arg); +// fill_from(*$self, array.begin_all(), array.end_all()); } else { @@ -1495,9 +1495,12 @@ namespace stir { { if (PyIter_Check(arg)) { - Array<3,float> array = swigstir::create_array_for_proj_data(*$self); - swigstir::fill_Array_from_Python_iterator(&array, arg); - fill_from(*$self, array.begin_all(), array.end_all()); + Array<4,float> array = swigstir::create_array_for_proj_data(*$self); + swigstir::fill_Array_from_Python_iterator(&array, arg); + swigstir::fill_proj_data_from_4D(*$self, array); +// Array<3,float> array = swigstir::create_array_for_proj_data(*$self); +// swigstir::fill_Array_from_Python_iterator(&array, arg); +// fill_from(*$self, array.begin_all(), array.end_all()); } else { From b3f91c0fb1124ca8bbffa7d5db1aef3714bfa2b4 Mon Sep 17 00:00:00 2001 From: NikEfth Date: Sat, 19 Dec 2020 19:38:35 -0500 Subject: [PATCH 154/170] T.B.C. this value allows the test_ArcCorrection to pass --- src/buildblock/Scanner.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/buildblock/Scanner.cxx b/src/buildblock/Scanner.cxx index 14989590e2..e8c90fed27 100644 --- a/src/buildblock/Scanner.cxx +++ b/src/buildblock/Scanner.cxx @@ -436,7 +436,7 @@ case PETMR_Signa: 311.8F, 8.5F, 5.56F, - 2.01565F, // TO CHECK + 2.1306F, // TO CHECK static_cast(-5.23*_PI/180),//sign? TODO value 5, 4, From 76994bef4482a9e83dfd3d7cbad9877ac35fd43d Mon Sep 17 00:00:00 2001 From: NikEfth Date: Sat, 19 Dec 2020 20:17:07 -0500 Subject: [PATCH 155/170] Yet another scanner generation fix. --- examples/PET_simulation/generate_input_data.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/PET_simulation/generate_input_data.sh b/examples/PET_simulation/generate_input_data.sh index aaf4790307..ab83aa01c8 100755 --- a/examples/PET_simulation/generate_input_data.sh +++ b/examples/PET_simulation/generate_input_data.sh @@ -49,6 +49,7 @@ template_sino=my_DSTE_3D_rd1_template.hs cat > my_input.txt < Date: Mon, 21 Dec 2020 11:59:30 -0500 Subject: [PATCH 156/170] Revert "T.B.C. this value allows the test_ArcCorrection to pass" This reverts commit b3f91c0fb1124ca8bbffa7d5db1aef3714bfa2b4. --- src/buildblock/Scanner.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/buildblock/Scanner.cxx b/src/buildblock/Scanner.cxx index e8c90fed27..14989590e2 100644 --- a/src/buildblock/Scanner.cxx +++ b/src/buildblock/Scanner.cxx @@ -436,7 +436,7 @@ case PETMR_Signa: 311.8F, 8.5F, 5.56F, - 2.1306F, // TO CHECK + 2.01565F, // TO CHECK static_cast(-5.23*_PI/180),//sign? TODO value 5, 4, From 4a2be3211ea8f021570c5fd52aaf8aefc9abfe1b Mon Sep 17 00:00:00 2001 From: NikEfth Date: Mon, 21 Dec 2020 12:00:47 -0500 Subject: [PATCH 157/170] Reduce the threshold on overlap_interpolate.inl to fix a numerical instability with Signa. --- src/include/stir/numerics/overlap_interpolate.inl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/include/stir/numerics/overlap_interpolate.inl b/src/include/stir/numerics/overlap_interpolate.inl index 36d7fc9ccf..1f05dc8732 100644 --- a/src/include/stir/numerics/overlap_interpolate.inl +++ b/src/include/stir/numerics/overlap_interpolate.inl @@ -86,9 +86,9 @@ void // we'll take it 1000 times smaller than the minimum of the average out_box size or in_box size const coord_t epsilon = std::min(((*(out_coord_end-1)) - (*out_coord_begin)) / - ((out_coord_end-1 - out_coord_begin)*1000), + ((out_coord_end-1 - out_coord_begin)*10000), ((*(in_coord_end-1)) - (*in_coord_begin)) / - ((in_coord_end-1 - in_coord_begin)*1000)); + ((in_coord_end-1 - in_coord_begin)*10000)); // do actual interpolation // we walk through the boxes, checking the overlap. From 970264ba915ef4c9d9bff5b442e59d4ae7320264 Mon Sep 17 00:00:00 2001 From: Kris Thielemans Date: Sat, 30 Jan 2021 11:07:45 +0000 Subject: [PATCH 158/170] hopefully more clear printing of TOF info --- src/buildblock/ProjDataInfo.cxx | 1 + src/utilities/list_projdata_info.cxx | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/buildblock/ProjDataInfo.cxx b/src/buildblock/ProjDataInfo.cxx index 9bd4008bd6..658c1678b7 100644 --- a/src/buildblock/ProjDataInfo.cxx +++ b/src/buildblock/ProjDataInfo.cxx @@ -327,6 +327,7 @@ ProjDataInfo::parameter_info() const << get_bed_position_vertical() << endl; s << "start horizontal bed position (mm) := " << get_bed_position_horizontal() << endl; + s << "\nNumber of TOF positions in data: " << get_num_tof_poss() << '\n'; s << "\nSegment_num range: (" << get_min_segment_num() << ", " << get_max_segment_num() << ")\n"; diff --git a/src/utilities/list_projdata_info.cxx b/src/utilities/list_projdata_info.cxx index a5dfd661f9..c64f50bc31 100644 --- a/src/utilities/list_projdata_info.cxx +++ b/src/utilities/list_projdata_info.cxx @@ -137,11 +137,10 @@ int main(int argc, char *argv[]) const int max_segment_num = proj_data_sptr->get_max_segment_num(); const int min_timing_num = proj_data_sptr->get_min_tof_pos_num(); const int max_timing_num = proj_data_sptr->get_max_tof_pos_num(); - std::cout << "\nTotal number of TOF positions: " << proj_data_sptr->get_num_tof_poss(); for (int timing_num = min_timing_num; timing_num <= max_timing_num; ++timing_num) { - std::cout << "\nTiming location: " << timing_num; + std::cout << "\nTOF bin: " << timing_num; bool accumulators_initialized = false; float accum_min=std::numeric_limits::max(); // initialize to very large in case projdata is empty (although that's unlikely) float accum_max=std::numeric_limits::min(); From 31c399089876c44538c58b0b691f05edd210d654 Mon Sep 17 00:00:00 2001 From: Kris Thielemans Date: Sat, 30 Jan 2021 11:08:20 +0000 Subject: [PATCH 159/170] allow SSRB tof_mashing for proj_data_info actual proj-data not done yet (will call error()) --- src/buildblock/SSRB.cxx | 8 ++++++++ src/include/stir/SSRB.h | 4 +++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/buildblock/SSRB.cxx b/src/buildblock/SSRB.cxx index 906bfd8ce8..7110443478 100644 --- a/src/buildblock/SSRB.cxx +++ b/src/buildblock/SSRB.cxx @@ -164,6 +164,14 @@ SSRB(const ProjDataInfo& in_proj_data_info, out_segment_num); } } + + if (num_tof_bins_to_combine!=1) + { + if (num_tof_bins_to_combine<1) + error("SSRB: num_tof_bins_to_combine needs to be at least 1"); + const int new_tof_mash_factor = in_proj_data_info_sptr->get_tof_mash_factor() * num_tof_bins_to_combine; + out_proj_data_info_sptr->set_tof_mash_factor(new_tof_mash_factor); + } return out_proj_data_info_sptr; } diff --git a/src/include/stir/SSRB.h b/src/include/stir/SSRB.h index d2f137d110..72a10da0e9 100644 --- a/src/include/stir/SSRB.h +++ b/src/include/stir/SSRB.h @@ -49,7 +49,7 @@ class ProjDataInfo; away some bins. Half of the bins will be thrown away at each 'side' of a sinogram (see below). \param max_in_segment_num_to_process rebinned in_proj_data only upto this segment. Default value -1 means 'do all segments'. - \param num_tof_bins_to_combine currently has to be 1. + \param num_tof_bins_to_combine can be used to increase TOF mashing. The original SSRB algorithm was developed in M.E. Daube-Witherspoon and G. Muehllehner, (1987) Treatment of axial data in three-dimensional PET, @@ -101,6 +101,7 @@ SSRB(const ProjDataInfo& in_proj_data_info, Default value -1 means 'do all segments'. \param do_normalisation (default true) wether to normalise the output sinograms corresponding to how many input sinograms contribute to them. + \param num_tof_bins_to_combine currently has to be 1. If it doesn't, error() will be called. \see SSRB(const ProjDataInfo& in_proj_data_info, const int num_segments_to_combine, @@ -132,6 +133,7 @@ SSRB(const std::string& output_filename, corresponding to how many input sinograms contribute to them. \warning in_proj_data_info has to be (at least) of type ProjDataInfoCylindrical + \warning TOF info has to match currently. If it doesn't, error() will be called. */ void SSRB(ProjData& out_projdata, From ea5d0b9f56ff2df7a7696283d1679741001995fc Mon Sep 17 00:00:00 2001 From: Kris Thielemans Date: Thu, 22 Oct 2020 08:24:46 +0000 Subject: [PATCH 160/170] add .clang-format, precommit hooks and minimal doc --- .clang-format | 115 +++++++++++++++++++++++++ .gitattributes | 11 +++ .pre-commit-config.yaml | 6 ++ documentation/devel/README.md | 8 ++ documentation/devel/editor-settings.md | 5 ++ documentation/devel/git-hooks.md | 27 ++++++ 6 files changed, 172 insertions(+) create mode 100644 .clang-format create mode 100644 .pre-commit-config.yaml create mode 100644 documentation/devel/README.md create mode 100644 documentation/devel/editor-settings.md create mode 100644 documentation/devel/git-hooks.md diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000000..2f00d92f9c --- /dev/null +++ b/.clang-format @@ -0,0 +1,115 @@ +--- +Language: Cpp +# BasedOnStyle: GNU +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Right +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: All +AlwaysBreakAfterReturnType: TopLevelDefinitions +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: true +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: true + AfterControlStatement: true + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: true + AfterExternBlock: true + BeforeCatch: true + BeforeElse: true + IndentBraces: true + SplitEmptyFunction: false + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: All +BreakBeforeBraces: GNU +BreakBeforeInheritanceComma: false +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 130 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: false +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: false +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + - Regex: '.*' + Priority: 1 +IncludeIsMainRegex: '(Test)?$' +IndentCaseLabels: false +IndentPPDirectives: AfterHash +IndentWidth: 2 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Left +RawStringFormats: + - Delimiter: pb + Language: TextProto + BasedOnStyle: google +ReflowComments: true +SortIncludes: false +SortUsingDeclarations: false +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: C++11 +TabWidth: 8 +UseTab: Never +... + diff --git a/.gitattributes b/.gitattributes index 70faca108b..7d845e399e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,14 @@ +# currently empty attributes (using pre-commit) +[attr]cppfiles +[attr]cfiles +[attr]pyfiles +*.cpp cppfiles +*.cxx cppfiles +*.h cppfiles +*.inl cppfiles +*.txx cppfiles +*.c cfiles +*.py pyfiles *.v -text -diff *.s -text -diff *.scn -text -diff diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000000..c5655a3205 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,6 @@ +repos: +- repo: git://github.com/doublify/pre-commit-clang-format + rev: master + hooks: + - id: clang-format + files: \.(c|cc|cxx|cpp|h|hpp|hxx|inl|txx) diff --git a/documentation/devel/README.md b/documentation/devel/README.md new file mode 100644 index 0000000000..f3226412f6 --- /dev/null +++ b/documentation/devel/README.md @@ -0,0 +1,8 @@ +# Information specific for developers + +Please check files here for information/code practices for developers. + +- Do read our [contribution guidelines](../../CONTRIBUTING.md) +- Set your editor settings appropriately: [instructions](editor-settings.md) +- Install git hooks for serious development: [instructions](git-hooks.md) +- Read the documentation, including the STIR developers guide \ No newline at end of file diff --git a/documentation/devel/editor-settings.md b/documentation/devel/editor-settings.md new file mode 100644 index 0000000000..999f00a84d --- /dev/null +++ b/documentation/devel/editor-settings.md @@ -0,0 +1,5 @@ +# Developer documentation: editor settings + +White-spaces and indentation with multiple developers are a pain. Please adhere to +our white-space policy, which we try to enforce via [clang-format](https://clang.llvm.org/docs/ClangFormat.html). +Check that site for integration with your editor/IDE. diff --git a/documentation/devel/git-hooks.md b/documentation/devel/git-hooks.md new file mode 100644 index 0000000000..ccf70b82f6 --- /dev/null +++ b/documentation/devel/git-hooks.md @@ -0,0 +1,27 @@ +# Developer documentation: how to install (software for) git hooks + +You first need to have Python and pip + +## Install [pre-commit](https://pre-commit.com) +See https://pre-commit.com/#install but the following might work. + + sudo -H pip install pre-commit + +If that fails with a message about `PyYAML` and `distutils, try + + sudo -H pip install --ignore-installed PyYAML + +## Install clang-format +### debian/Ubuntu + sudo apt install clang-format +### MacOS + brew install clang-format +### Others +search the internet and tell us + +## Enable pre-commit hooks +```sh +cd /whereever/STIR +pre-commit install +``` + From 4bda591297ccfbc1565a4b7a9fb36c598e399b09 Mon Sep 17 00:00:00 2001 From: Kris Thielemans Date: Thu, 22 Oct 2020 13:36:07 +0100 Subject: [PATCH 161/170] remove use of sudo for pip instructions Co-authored-by: Casper da Costa-Luis --- documentation/devel/git-hooks.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/documentation/devel/git-hooks.md b/documentation/devel/git-hooks.md index ccf70b82f6..28ec3a5d86 100644 --- a/documentation/devel/git-hooks.md +++ b/documentation/devel/git-hooks.md @@ -5,11 +5,13 @@ You first need to have Python and pip ## Install [pre-commit](https://pre-commit.com) See https://pre-commit.com/#install but the following might work. - sudo -H pip install pre-commit + pip install pre-commit -If that fails with a message about `PyYAML` and `distutils, try +If this fails with a permission error, try adding `--user` to the command. - sudo -H pip install --ignore-installed PyYAML +If that fails with a message about `PyYAML` and `distutils`, try + + pip install --ignore-installed PyYAML ## Install clang-format ### debian/Ubuntu @@ -24,4 +26,3 @@ search the internet and tell us cd /whereever/STIR pre-commit install ``` - From 713759fb58342a6c9ac62788ca6245ba722cd4c1 Mon Sep 17 00:00:00 2001 From: Kris Thielemans Date: Thu, 22 Oct 2020 22:29:23 +0000 Subject: [PATCH 162/170] reduced keys .clang-format [ci skip] Only use the non-default ones for the style, as otherwise it causes problems with different versions of clang-format --- .clang-format | 130 ++++++-------------------------------------------- 1 file changed, 15 insertions(+), 115 deletions(-) diff --git a/.clang-format b/.clang-format index 2f00d92f9c..ba26f5568a 100644 --- a/.clang-format +++ b/.clang-format @@ -1,115 +1,15 @@ ---- -Language: Cpp -# BasedOnStyle: GNU -AccessModifierOffset: -2 -AlignAfterOpenBracket: Align -AlignConsecutiveAssignments: false -AlignConsecutiveDeclarations: false -AlignEscapedNewlines: Right -AlignOperands: true -AlignTrailingComments: true -AllowAllParametersOfDeclarationOnNextLine: true -AllowShortBlocksOnASingleLine: false -AllowShortCaseLabelsOnASingleLine: false -AllowShortFunctionsOnASingleLine: All -AllowShortIfStatementsOnASingleLine: false -AllowShortLoopsOnASingleLine: false -AlwaysBreakAfterDefinitionReturnType: All -AlwaysBreakAfterReturnType: TopLevelDefinitions -AlwaysBreakBeforeMultilineStrings: false -AlwaysBreakTemplateDeclarations: true -BinPackArguments: true -BinPackParameters: true -BraceWrapping: - AfterClass: true - AfterControlStatement: true - AfterEnum: true - AfterFunction: true - AfterNamespace: true - AfterObjCDeclaration: true - AfterStruct: true - AfterUnion: true - AfterExternBlock: true - BeforeCatch: true - BeforeElse: true - IndentBraces: true - SplitEmptyFunction: false - SplitEmptyRecord: true - SplitEmptyNamespace: true -BreakBeforeBinaryOperators: All -BreakBeforeBraces: GNU -BreakBeforeInheritanceComma: false -BreakBeforeTernaryOperators: true -BreakConstructorInitializersBeforeComma: false -BreakConstructorInitializers: BeforeColon -BreakAfterJavaFieldAnnotations: false -BreakStringLiterals: true -ColumnLimit: 130 -CommentPragmas: '^ IWYU pragma:' -CompactNamespaces: false -ConstructorInitializerAllOnOneLineOrOnePerLine: false -ConstructorInitializerIndentWidth: 4 -ContinuationIndentWidth: 4 -Cpp11BracedListStyle: false -DerivePointerAlignment: false -DisableFormat: false -ExperimentalAutoDetectBinPacking: false -FixNamespaceComments: false -ForEachMacros: - - foreach - - Q_FOREACH - - BOOST_FOREACH -IncludeBlocks: Preserve -IncludeCategories: - - Regex: '^"(llvm|llvm-c|clang|clang-c)/' - Priority: 2 - - Regex: '^(<|"(gtest|gmock|isl|json)/)' - Priority: 3 - - Regex: '.*' - Priority: 1 -IncludeIsMainRegex: '(Test)?$' -IndentCaseLabels: false -IndentPPDirectives: AfterHash -IndentWidth: 2 -IndentWrappedFunctionNames: false -JavaScriptQuotes: Leave -JavaScriptWrapImports: true -KeepEmptyLinesAtTheStartOfBlocks: true -MacroBlockBegin: '' -MacroBlockEnd: '' -MaxEmptyLinesToKeep: 1 -NamespaceIndentation: None -ObjCBlockIndentWidth: 2 -ObjCSpaceAfterProperty: false -ObjCSpaceBeforeProtocolList: true -PenaltyBreakAssignment: 2 -PenaltyBreakBeforeFirstCallParameter: 19 -PenaltyBreakComment: 300 -PenaltyBreakFirstLessLess: 120 -PenaltyBreakString: 1000 -PenaltyExcessCharacter: 1000000 -PenaltyReturnTypeOnItsOwnLine: 60 -PointerAlignment: Left -RawStringFormats: - - Delimiter: pb - Language: TextProto - BasedOnStyle: google -ReflowComments: true -SortIncludes: false -SortUsingDeclarations: false -SpaceAfterCStyleCast: false -SpaceAfterTemplateKeyword: true -SpaceBeforeAssignmentOperators: true -SpaceBeforeParens: ControlStatements -SpaceInEmptyParentheses: false -SpacesBeforeTrailingComments: 1 -SpacesInAngles: false -SpacesInContainerLiterals: true -SpacesInCStyleCastParentheses: false -SpacesInParentheses: false -SpacesInSquareBrackets: false -Standard: C++11 -TabWidth: 8 -UseTab: Never -... - +--- +Language : Cpp +# BasedOnStyle : GNU +AlwaysBreakAfterReturnType: TopLevelDefinitions +AlwaysBreakTemplateDeclarations: true +BraceWrapping: + SplitEmptyFunction: false +ColumnLimit: 130 +IndentPPDirectives: AfterHash +PointerAlignment: Left +SortIncludes: false +SortUsingDeclarations: false +SpaceBeforeParens: ControlStatements +Standard: Cpp11 +... From 661a14d0747007da34ca89d418195539f404eead Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Thu, 22 Oct 2020 14:01:57 +0100 Subject: [PATCH 163/170] fix pre-commit --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c5655a3205..32fee3e553 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,4 +3,4 @@ repos: rev: master hooks: - id: clang-format - files: \.(c|cc|cxx|cpp|h|hpp|hxx|inl|txx) + files: \.(c|cc|cxx|cpp|h|hpp|hxx|inl|txx)$ From f07b9c324252fb26c2032d6c7557a9600fc2a94b Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Thu, 22 Oct 2020 14:09:28 +0100 Subject: [PATCH 164/170] add CI test --- .github/workflows/test.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000000..d924e78790 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,15 @@ +name: Test +on: +- push +jobs: + check: + runs-on: ubuntu-latest + name: Lint + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: 3.x + - run: sudo apt-get install -yqq clang-format + - run: pip install pre-commit + - run: pre-commit run --all-files From 3d41bcd2c3ecda6f53fc71741c08e06317788a55 Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Thu, 22 Oct 2020 14:18:41 +0100 Subject: [PATCH 165/170] use pre-commit action --- .github/workflows/test.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d924e78790..588a5120a3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,8 +8,5 @@ jobs: steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 - with: - python-version: 3.x - run: sudo apt-get install -yqq clang-format - - run: pip install pre-commit - - run: pre-commit run --all-files + - uses: pre-commit/action@v2.0.0 From 3a83d348ad405eef8d5f2e5fabd20129f6e34274 Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Thu, 22 Oct 2020 14:20:34 +0100 Subject: [PATCH 166/170] add pre-commit on PR --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 588a5120a3..5b426f5cc3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,6 +1,7 @@ name: Test on: - push +- pull_request jobs: check: runs-on: ubuntu-latest From 4ec1ce5d46a550514aa6a54a874b4ea35b859cf3 Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Thu, 22 Oct 2020 23:56:55 +0100 Subject: [PATCH 167/170] CI: rename Test/Lint => Check/pre-commit --- .github/workflows/{test.yml => check.yml} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename .github/workflows/{test.yml => check.yml} (87%) diff --git a/.github/workflows/test.yml b/.github/workflows/check.yml similarity index 87% rename from .github/workflows/test.yml rename to .github/workflows/check.yml index 5b426f5cc3..1ffa1ebeb8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/check.yml @@ -1,11 +1,11 @@ -name: Test +name: Check on: - push - pull_request jobs: check: runs-on: ubuntu-latest - name: Lint + name: pre-commit steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 From d406f45d7c7ccf2c2509fa35144f09990783a8da Mon Sep 17 00:00:00 2001 From: Ashley Gillman Date: Wed, 26 May 2021 12:03:41 +1000 Subject: [PATCH 168/170] use explicit commit for clang-format --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 32fee3e553..f1589c12c3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: git://github.com/doublify/pre-commit-clang-format - rev: master + rev: 62302476d0da01515660132d76902359bed0f782 hooks: - id: clang-format files: \.(c|cc|cxx|cpp|h|hpp|hxx|inl|txx)$ From 42416cf95a3843fc55d936f3948f42acb00d02bc Mon Sep 17 00:00:00 2001 From: clang-format Date: Wed, 26 May 2021 12:03:59 +1000 Subject: [PATCH 169/170] run clang-format --- .../General_Reconstruction.cxx | 59 +- .../General_Reconstruction.h | 27 +- examples/C++/src/demo1.cxx | 35 +- examples/C++/src/demo2.cxx | 40 +- examples/C++/src/demo3.cxx | 57 +- examples/src/demo4_obj_fun.cxx | 70 +- src/IO/ECAT6OutputFileFormat.cxx | 99 +- ...namicDiscretisedDensityInputFileFormat.cxx | 107 +- ...amicDiscretisedDensityOutputFileFormat.cxx | 83 +- src/IO/ECAT7OutputFileFormat.cxx | 92 +- ...ECAT7ParametricDensityOutputFileFormat.cxx | 122 +- src/IO/GEHDF5Wrapper.cxx | 1327 +++--- src/IO/GIPL_ImageFormat.cxx | 1056 +++-- src/IO/IO_registries.cxx | 50 +- src/IO/ITKImageInputFileFormat.cxx | 533 +-- src/IO/ITKOutputFileFormat.cxx | 210 +- src/IO/InputFileFormatRegistry.cxx | 10 +- src/IO/InputStreamFromROOTFile.cxx | 196 +- ...putStreamFromROOTFileForCylindricalPET.cxx | 385 +- src/IO/InputStreamFromROOTFileForECATPET.cxx | 244 +- ...amicDiscretisedDensityOutputFileFormat.cxx | 77 +- src/IO/InterfileHeader.cxx | 1343 +++--- src/IO/InterfileHeaderSiemens.cxx | 357 +- src/IO/InterfileOutputFileFormat.cxx | 50 +- src/IO/InterfilePDFSHeaderSPECT.cxx | 218 +- ...tricDiscretisedDensityOutputFileFormat.cxx | 56 +- ...amicDiscretisedDensityOutputFileFormat.cxx | 107 +- ...tricDiscretisedDensityOutputFileFormat.cxx | 103 +- src/IO/OutputFileFormat.cxx | 20 +- src/IO/OutputFileFormat_default.cxx | 62 +- src/IO/RegisteredObject.cxx | 13 +- src/IO/ecat6_utils.cxx | 721 +-- src/IO/interfile.cxx | 1666 +++---- src/IO/stir_AVW.cxx | 171 +- src/IO/stir_ecat6.cxx | 1244 +++--- src/IO/stir_ecat7.cxx | 2605 +++++------ src/IO/stir_ecat_common.cxx | 135 +- src/Shape_buildblock/Box3D.cxx | 134 +- src/Shape_buildblock/DiscretisedShape3D.cxx | 123 +- src/Shape_buildblock/Ellipsoid.cxx | 131 +- src/Shape_buildblock/EllipsoidalCylinder.cxx | 350 +- src/Shape_buildblock/RegisteredObject.cxx | 15 +- src/Shape_buildblock/Shape3D.cxx | 183 +- .../Shape3DWithOrientation.cxx | 90 +- .../Shape_buildblock_registries.cxx | 2 +- src/SimSET/conv_SimSET_projdata_to_STIR.cxx | 207 +- src/SimSET/conv_to_SimSET_att_image.cxx | 124 +- src/SimSET/write_phg_image_info.c | 112 +- src/analytic/FBP2D/FBP2D.cxx | 25 +- src/analytic/FBP2D/FBP2DReconstruction.cxx | 339 +- src/analytic/FBP2D/RampFilter.cxx | 150 +- src/analytic/FBP3DRP/ColsherFilter.cxx | 561 ++- src/analytic/FBP3DRP/FBP3DRP.cxx | 27 +- .../FBP3DRP/FBP3DRPReconstruction.cxx | 959 ++-- src/buildblock/ArcCorrection.cxx | 339 +- src/buildblock/Array.cxx | 35 +- .../ArrayFilter1DUsingConvolution.cxx | 225 +- ...ilter1DUsingConvolutionSymmetricKernel.cxx | 59 +- .../ArrayFilter2DUsingConvolution.cxx | 119 +- .../ArrayFilter3DUsingConvolution.cxx | 199 +- .../ArrayFilterUsingRealDFTWithPadding.cxx | 145 +- src/buildblock/ByteOrder.cxx | 14 +- src/buildblock/ChainedDataProcessor.cxx | 104 +- .../DataSymmetriesForViewSegmentNumbers.cxx | 32 +- src/buildblock/DiscretisedDensity.cxx | 220 +- src/buildblock/DynamicDiscretisedDensity.cxx | 250 +- src/buildblock/DynamicProjData.cxx | 356 +- src/buildblock/ExamData.cxx | 29 +- src/buildblock/ExamInfo.cxx | 39 +- src/buildblock/FilePath.cxx | 626 ++- src/buildblock/GatedDiscretisedDensity.cxx | 217 +- src/buildblock/GatedProjData.cxx | 211 +- .../GeneralisedPoissonNoiseGenerator.cxx | 108 +- src/buildblock/HUToMuImageProcessor.cxx | 133 +- src/buildblock/IndexRange.cxx | 244 +- src/buildblock/KeyParser.cxx | 1626 +++---- src/buildblock/ML_norm.cxx | 2144 ++++----- src/buildblock/MaximalArrayFilter3D.cxx | 87 +- src/buildblock/MaximalImageFilter3D.cxx | 58 +- src/buildblock/MedianArrayFilter3D.cxx | 109 +- src/buildblock/MedianImageFilter3D.cxx | 59 +- src/buildblock/MinimalArrayFilter3D.cxx | 82 +- src/buildblock/MinimalImageFilter3D.cxx | 56 +- src/buildblock/MultipleDataSetHeader.cxx | 65 +- src/buildblock/MultipleProjData.cxx | 92 +- ...ableConvolutionUsingRealDFTImageFilter.cxx | 126 +- src/buildblock/NumericType.cxx | 265 +- .../ParseDiscretisedDensityParameters.cxx | 159 +- src/buildblock/ParsingObject.cxx | 108 +- src/buildblock/PatientPosition.cxx | 119 +- src/buildblock/ProjData.cxx | 484 +- src/buildblock/ProjDataFromStream.cxx | 1416 +++--- src/buildblock/ProjDataGEAdvance.cxx | 382 +- src/buildblock/ProjDataGEHDF5.cxx | 201 +- src/buildblock/ProjDataInMemory.cxx | 232 +- src/buildblock/ProjDataInfo.cxx | 803 ++-- src/buildblock/ProjDataInfoCylindrical.cxx | 579 +-- .../ProjDataInfoCylindricalArcCorr.cxx | 178 +- .../ProjDataInfoCylindricalNoArcCorr.cxx | 693 ++- src/buildblock/ProjDataInterfile.cxx | 68 +- src/buildblock/RegisteredObject.cxx | 13 +- src/buildblock/RelatedViewgrams.cxx | 294 +- src/buildblock/SSRB.cxx | 420 +- src/buildblock/Scanner.cxx | 1249 ++---- src/buildblock/Segment.cxx | 79 +- src/buildblock/SegmentBySinogram.cxx | 126 +- src/buildblock/SegmentByView.cxx | 124 +- .../SeparableArrayFunctionObject.cxx | 70 +- .../SeparableCartesianMetzImageFilter.cxx | 108 +- .../SeparableConvolutionImageFilter.cxx | 207 +- .../SeparableGaussianArrayFilter.cxx | 139 +- .../SeparableGaussianImageFilter.cxx | 136 +- src/buildblock/SeparableMetzArrayFilter.cxx | 302 +- src/buildblock/Sinogram.cxx | 118 +- src/buildblock/TextWriter.cxx | 25 +- ...ldMinToSmallPositiveValueDataProcessor.cxx | 70 +- src/buildblock/TimeFrameDefinitions.cxx | 231 +- src/buildblock/TimeGateDefinitions.cxx | 137 +- ...TruncateToCylindricalFOVImageProcessor.cxx | 60 +- src/buildblock/Verbosity.cxx | 18 +- src/buildblock/Viewgram.cxx | 116 +- src/buildblock/VoxelsOnCartesianGrid.cxx | 344 +- src/buildblock/buildblock_registries.cxx | 10 +- src/buildblock/centre_of_gravity.cxx | 119 +- src/buildblock/date_time_functions.cxx | 231 +- src/buildblock/error.cxx | 28 +- src/buildblock/extend_projdata.cxx | 217 +- src/buildblock/find_fwhm_in_image.cxx | 421 +- src/buildblock/getopt.c | 1551 +++---- .../interfile_keyword_functions.cxx | 43 +- src/buildblock/interpolate_projdata.cxx | 358 +- src/buildblock/inverse_SSRB.cxx | 147 +- src/buildblock/line.cxx | 368 +- src/buildblock/linear_regression.cxx | 114 +- src/buildblock/num_threads.cxx | 58 +- src/buildblock/overlap_interpolate.cxx | 381 +- src/buildblock/recon_array_functions.cxx | 548 +-- src/buildblock/scale_sinograms.cxx | 164 +- src/buildblock/utilities.cxx | 410 +- src/buildblock/warning.cxx | 26 +- src/buildblock/zoom.cxx | 502 +-- src/data_buildblock/RegisteredObject.cxx | 17 +- src/data_buildblock/SinglesRates.cxx | 61 +- .../SinglesRatesForTimeFrames.cxx | 44 +- src/data_buildblock/SinglesRatesFromECAT7.cxx | 97 +- .../SinglesRatesFromGEHDF5.cxx | 413 +- .../SinglesRatesFromSglFile.cxx | 484 +- .../data_buildblock_registries.cxx | 9 +- src/display/display_array.cxx | 552 +-- src/display/gen.c | 117 +- src/display/gen.h | 138 +- src/display/mathlinkhelp.c | 167 +- src/display/screen.c | 810 ++-- src/display/screen.h | 241 +- src/display/screengen.c | 130 +- src/eval_buildblock/ROIValues.cxx | 33 +- src/eval_buildblock/compute_ROI_values.cxx | 163 +- ...amicDiscretisedDensityOutputFileFormat.cxx | 83 +- .../IO/OutputFileFormat_default.cxx | 4 +- .../IO/local_InputFileFormatRegistry.cxx | 1 - .../IO/local_OutputFileFormat_default.cxx | 4 +- .../AbsTimeIntervalFromDynamicData.cxx | 121 +- .../AbsTimeIntervalFromECAT7ACF.cxx | 80 +- .../buildblock/AbsTimeIntervalWithParsing.cxx | 52 +- .../buildblock/DAVArrayFilter3D.cxx | 159 +- .../buildblock/DAVImageFilter3D.cxx | 62 +- ...ModifiedInverseAveragingImageFilterAll.cxx | 3892 ++++++++--------- .../ModifiedInverseAverigingArrayFilter.cxx | 461 +- .../ModifiedInverseAverigingImageFilter.cxx | 1880 ++++---- .../NonseparableSpatiallyVaryingFilters.cxx | 1791 ++++---- .../NonseparableSpatiallyVaryingFilters3D.cxx | 1629 ++++--- src/experimental/buildblock/Quaternion.cxx | 18 +- .../buildblock/RegisteredObject.cxx | 10 +- .../SeparableLowPassArrayFilter.cxx | 64 +- .../SeparableLowPassImageFilter.cxx | 117 +- .../fwd_and_bck_manipulation_for_SAF.cxx | 174 +- .../local_buildblock_registries.cxx | 33 +- .../buildblock/local_helping_functions.cxx | 428 +- ...iply_plane_scale_factorsImageProcessor.cxx | 96 +- .../iterative/OSSPS/line_search.cxx | 235 +- .../listmode/CListModeDataLMF.cxx | 78 +- .../listmode/LmToProjDataWithMC.cxx | 169 +- .../change_lm_time_tags.cxx | 110 +- .../listmode_utilities/get_singles_info.cxx | 39 +- .../lm_to_projdata_with_MC.cxx | 28 +- .../motion/MatchTrackerAndScanner.cxx | 315 +- ...RigidObjectTransformationUsingBSplines.cxx | 509 +-- ...ModelForMeanAndGatedProjDataWithMotion.cxx | 551 +-- src/experimental/motion/Polaris_MT_File.cxx | 279 +- src/experimental/motion/RegisteredObject.cxx | 18 +- .../motion/RigidObject3DMotion.cxx | 67 +- .../motion/RigidObject3DMotionFromPolaris.cxx | 1097 ++--- .../motion/RigidObject3DTransformation.cxx | 647 ++- src/experimental/motion/TimeFrameMotion.cxx | 158 +- .../Transform3DObjectImageProcessor.cxx | 304 +- .../motion/local_motion_registries.cxx | 9 +- .../motion/transform_3d_object.cxx | 374 +- .../test_BSpline_transformations.cxx | 180 +- .../motion_utilities/add_planes_to_image.cxx | 36 +- .../find_motion_corrected_norm_factors.cxx | 628 +-- .../list_deformation_vectors.cxx | 154 +- .../motion_utilities/list_polaris_info.cxx | 67 +- .../match_tracker_and_scanner.cxx | 18 +- .../motion_utilities/move_image.cxx | 207 +- .../motion_utilities/move_projdata.cxx | 217 +- .../motion_utilities/non_rigid_transform.cxx | 160 +- .../remove_corrupted_sinograms.cxx | 367 +- .../motion_utilities/report_movement.cxx | 262 +- .../rigid_object_transform_image.cxx | 178 +- .../rigid_object_transform_projdata.cxx | 129 +- .../motion_utilities/sync_polaris.cxx | 69 +- .../BinNormalisationFromML2D.cxx | 218 +- .../BinNormalisationSinogramRescaling.cxx | 165 +- .../BinNormalisationUsingProfile.cxx | 104 +- ...SymmetriesForDensels_PET_CartesianGrid.cxx | 234 +- .../ParametricQuadraticPrior.cxx | 245 +- ...thLinearModelForMeanAndDynamicProjData.cxx | 162 +- .../PostsmoothingForwardProjectorByBin.cxx | 220 +- .../ProjMatrixByBinSinglePhoton.cxx | 89 +- .../ProjMatrixByBinUsingSolidAngle.cxx | 400 +- .../ProjMatrixByBinWithPositronRange.cxx | 153 +- .../recon_buildblock/ProjMatrixByDensel.cxx | 74 +- ...rixByDenselOnCartesianGridUsingElement.cxx | 239 +- .../ProjMatrixByDenselUsingRayTracing.cxx | 327 +- .../local_recon_buildblock_registries.cxx | 29 +- src/experimental/recon_test/fwdtestDensel.cxx | 449 +- ...test_ProjMatrixByBinUsingInterpolation.cxx | 757 ++-- src/experimental/test/test_Fourier.cxx | 371 +- .../test/test_LmToProjdataWithMC.cxx | 89 +- src/experimental/test/test_Quaternion.cxx | 240 +- .../test/test_RigidObject3DTransformation.cxx | 631 ++- .../test/test_proj_data_info_LOR.cxx | 236 +- src/experimental/utilities/AVWROI.cxx | 136 +- .../utilities/Bland_Altman_plot.cxx | 232 +- src/experimental/utilities/CoG.cxx | 77 +- .../utilities/add_ecat7_header_to_sgl.cxx | 173 +- .../utilities/add_side_shields.cxx | 134 +- .../utilities/add_time_frame_info.cxx | 40 +- .../utilities/apply_plane_rescale_factors.cxx | 49 +- .../utilities/change_mhead_file_type.cxx | 47 +- .../utilities/compute_gradient.cxx | 81 +- .../compute_plane_rescale_factors.cxx | 55 +- .../utilities/create_normfactors.cxx | 163 +- .../utilities/create_normfactors3D.cxx | 114 +- src/experimental/utilities/fillwith1.cxx | 40 +- .../utilities/fillwithotherprojdata.cxx | 59 +- .../find_sinogram_rescaling_factors.cxx | 139 +- src/experimental/utilities/fit_cylinder.cxx | 128 +- src/experimental/utilities/image_flip_x.cxx | 80 +- .../utilities/interpolate_blocks.cxx | 232 +- .../utilities/interpolate_projdata.cxx | 110 +- src/experimental/utilities/inverse_SSRB.cxx | 63 +- .../utilities/inverse_proj_data.cxx | 174 +- .../line_profiles_through_projdata.cxx | 135 +- .../utilities/list_TAC_ROI_values.cxx | 247 +- src/experimental/utilities/make_cylinder.cxx | 119 +- .../utilities/make_grid_image.cxx | 87 +- src/experimental/utilities/mode.cxx | 204 +- .../utilities/normalizedbckproj.cxx | 225 +- .../utilities/precompute_denominator_SPS.cxx | 309 +- .../utilities/prepare_projdata.cxx | 366 +- .../utilities/print_rdf_singles.cxx | 38 +- .../utilities/remove_sinograms.cxx | 107 +- .../utilities/set_blocks_to_value.cxx | 187 +- .../utilities/shift_projdata_along_axis.cxx | 65 +- .../utilities/show_ecat7_header.cxx | 219 +- .../utilities/threshold_norm_data.cxx | 75 +- .../utilities/zero_projdata_from_norm.cxx | 73 +- src/include/stir/ArcCorrection.h | 68 +- src/include/stir/Array.h | 314 +- src/include/stir/Array.inl | 599 +-- src/include/stir/Array1d.h | 362 +- .../stir/ArrayFilter1DUsingConvolution.h | 48 +- ...yFilter1DUsingConvolutionSymmetricKernel.h | 37 +- .../stir/ArrayFilter2DUsingConvolution.h | 37 +- .../stir/ArrayFilter3DUsingConvolution.h | 39 +- .../stir/ArrayFilterUsingRealDFTWithPadding.h | 50 +- src/include/stir/ArrayFunction.h | 181 +- src/include/stir/ArrayFunction.inl | 392 +- src/include/stir/ArrayFunctionObject.h | 48 +- ...ayFunctionObject_1ArgumentImplementation.h | 29 +- ...ayFunctionObject_2ArgumentImplementation.h | 30 +- src/include/stir/Array_complex_numbers.h | 55 +- src/include/stir/BasicCoordinate.h | 162 +- src/include/stir/BasicCoordinate.inl | 523 +-- src/include/stir/Bin.h | 86 +- src/include/stir/Bin.inl | 152 +- src/include/stir/BoundaryConditions.h | 6 +- src/include/stir/ByteOrder.h | 72 +- src/include/stir/ByteOrder.inl | 36 +- src/include/stir/ByteOrderDefine.h | 33 +- src/include/stir/CPUTimer.h | 44 +- src/include/stir/CPUTimer.inl | 80 +- src/include/stir/CartesianCoordinate2D.h | 23 +- src/include/stir/CartesianCoordinate2D.inl | 46 +- src/include/stir/CartesianCoordinate3D.h | 20 +- src/include/stir/CartesianCoordinate3D.inl | 65 +- src/include/stir/ChainedDataProcessor.h | 77 +- src/include/stir/Coordinate2D.h | 21 +- src/include/stir/Coordinate2D.inl | 27 +- src/include/stir/Coordinate3D.h | 17 +- src/include/stir/Coordinate3D.inl | 28 +- src/include/stir/Coordinate4D.h | 20 +- src/include/stir/Coordinate4D.inl | 29 +- src/include/stir/DataProcessor.h | 65 +- src/include/stir/DataProcessor.inl | 64 +- .../DataSymmetriesForViewSegmentNumbers.h | 38 +- src/include/stir/Densel.h | 4 +- src/include/stir/DetectionPosition.h | 51 +- src/include/stir/DetectionPosition.inl | 64 +- src/include/stir/DetectionPositionPair.h | 28 +- src/include/stir/DetectionPositionPair.inl | 68 +- src/include/stir/DiscretisedDensity.h | 164 +- src/include/stir/DiscretisedDensity.inl | 330 +- .../stir/DiscretisedDensityOnCartesianGrid.h | 78 +- .../DiscretisedDensityOnCartesianGrid.inl | 145 +- src/include/stir/DynamicDiscretisedDensity.h | 181 +- .../stir/DynamicDiscretisedDensity.inl | 27 +- src/include/stir/DynamicProjData.h | 143 +- src/include/stir/ExamData.h | 44 +- src/include/stir/ExamInfo.h | 81 +- src/include/stir/ExamInfo.inl | 50 +- src/include/stir/FactoryRegistry.h | 43 +- src/include/stir/FactoryRegistry.inl | 108 +- src/include/stir/FilePath.h | 197 +- src/include/stir/FilePath.inl | 55 +- src/include/stir/FullArrayIterator.h | 72 +- src/include/stir/FullArrayIterator.inl | 112 +- src/include/stir/GatedDiscretisedDensity.h | 122 +- src/include/stir/GatedProjData.h | 26 +- .../stir/GeneralisedPoissonNoiseGenerator.h | 35 +- src/include/stir/HUToMuImageProcessor.h | 46 +- src/include/stir/HighResWallClockTimer.h | 597 ++- .../stir/IO/ECAT6ImageInputFileFormat.h | 93 +- src/include/stir/IO/ECAT6OutputFileFormat.h | 40 +- ...DynamicDiscretisedDensityInputFileFormat.h | 26 +- ...ynamicDiscretisedDensityOutputFileFormat.h | 38 +- .../stir/IO/ECAT7ImageInputFileFormat.h | 61 +- src/include/stir/IO/ECAT7OutputFileFormat.h | 41 +- .../ECAT7ParametricDensityOutputFileFormat.h | 44 +- .../IO/ECAT8_32bitListmodeInputFileFormat.h | 37 +- .../stir/IO/ECAT962ListmodeInputFileFormat.h | 60 +- .../stir/IO/ECAT966ListmodeInputFileFormat.h | 71 +- src/include/stir/IO/FileSignature.h | 52 +- .../stir/IO/GEHDF5ListmodeInputFileFormat.h | 100 +- src/include/stir/IO/GEHDF5Wrapper.h | 211 +- src/include/stir/IO/GEHDF5Wrapper.inl | 41 +- src/include/stir/IO/GIPL_ImageFormat.h | 212 +- src/include/stir/IO/ITKImageInputFileFormat.h | 41 +- src/include/stir/IO/ITKOutputFileFormat.h | 47 +- src/include/stir/IO/InputFileFormat.h | 38 +- src/include/stir/IO/InputFileFormatRegistry.h | 100 +- .../stir/IO/InputFileFormatRegistry.txx | 100 +- src/include/stir/IO/InputStreamFromROOTFile.h | 323 +- .../stir/IO/InputStreamFromROOTFile.inl | 136 +- ...InputStreamFromROOTFileForCylindricalPET.h | 129 +- ...putStreamFromROOTFileForCylindricalPET.inl | 124 +- .../IO/InputStreamFromROOTFileForECATPET.h | 92 +- .../IO/InputStreamFromROOTFileForECATPET.inl | 47 +- src/include/stir/IO/InputStreamWithRecords.h | 73 +- .../stir/IO/InputStreamWithRecords.inl | 131 +- .../stir/IO/InputStreamWithRecordsFromHDF5.h | 73 +- .../IO/InputStreamWithRecordsFromHDF5.inl | 93 +- ...DynamicDiscretisedDensityInputFileFormat.h | 42 +- ...ynamicDiscretisedDensityOutputFileFormat.h | 44 +- src/include/stir/IO/InterfileHeader.h | 154 +- src/include/stir/IO/InterfileHeaderSiemens.h | 165 +- .../stir/IO/InterfileImageInputFileFormat.h | 40 +- .../stir/IO/InterfileOutputFileFormat.h | 46 +- .../stir/IO/InterfilePDFSHeaderSPECT.h | 22 +- ...ametricDiscretisedDensityInputFileFormat.h | 42 +- ...metricDiscretisedDensityOutputFileFormat.h | 51 +- ...DynamicDiscretisedDensityInputFileFormat.h | 91 +- ...ynamicDiscretisedDensityOutputFileFormat.h | 47 +- ...ametricDiscretisedDensityInputFileFormat.h | 61 +- ...metricDiscretisedDensityOutputFileFormat.h | 54 +- src/include/stir/IO/OutputFileFormat.h | 87 +- src/include/stir/IO/OutputFileFormat.txx | 190 +- .../stir/IO/ROOTListmodeInputFileFormat.h | 65 +- .../stir/IO/SAFIRCListmodeInputFileFormat.h | 253 +- src/include/stir/IO/ecat6_types.h | 55 +- src/include/stir/IO/ecat6_utils.h | 130 +- src/include/stir/IO/interfile.h | 221 +- src/include/stir/IO/read_data.h | 56 +- src/include/stir/IO/read_data.inl | 174 +- src/include/stir/IO/read_data_1d.h | 16 +- src/include/stir/IO/read_data_1d.inl | 69 +- src/include/stir/IO/read_from_file.h | 29 +- src/include/stir/IO/stir_AVW.h | 23 +- src/include/stir/IO/stir_ecat6.h | 109 +- src/include/stir/IO/stir_ecat7.h | 179 +- src/include/stir/IO/stir_ecat_common.h | 63 +- src/include/stir/IO/test/test_IO.h | 373 +- src/include/stir/IO/write_data.h | 123 +- src/include/stir/IO/write_data.inl | 259 +- src/include/stir/IO/write_data_1d.h | 27 +- src/include/stir/IO/write_data_1d.inl | 111 +- src/include/stir/IO/write_to_file.h | 18 +- src/include/stir/ImagingModality.h | 122 +- src/include/stir/IndexRange.h | 61 +- src/include/stir/IndexRange.inl | 183 +- src/include/stir/IndexRange2D.h | 6 +- src/include/stir/IndexRange2D.inl | 26 +- src/include/stir/IndexRange3D.h | 14 +- src/include/stir/IndexRange3D.inl | 25 +- src/include/stir/IndexRange4D.h | 18 +- src/include/stir/IndexRange4D.inl | 29 +- .../stir/KOSMAPOSL/KOSMAPOSLReconstruction.h | 196 +- src/include/stir/KeyParser.h | 254 +- src/include/stir/LORCoordinates.h | 736 ++-- src/include/stir/LORCoordinates.inl | 469 +- src/include/stir/ML_norm.h | 386 +- src/include/stir/MaximalArrayFilter3D.h | 41 +- src/include/stir/MaximalImageFilter3D.h | 59 +- src/include/stir/MedianArrayFilter3D.h | 31 +- src/include/stir/MedianImageFilter3D.h | 55 +- src/include/stir/MinimalArrayFilter3D.h | 31 +- src/include/stir/MinimalImageFilter3D.h | 53 +- src/include/stir/MultipleDataSetHeader.h | 56 +- src/include/stir/MultipleDataSetHeader.inl | 16 +- src/include/stir/MultipleProjData.h | 191 +- src/include/stir/NestedIterator.h | 61 +- src/include/stir/NestedIterator.inl | 125 +- src/include/stir/NestedIteratorHelpers.h | 88 +- ...arableConvolutionUsingRealDFTImageFilter.h | 73 +- src/include/stir/NumericInfo.h | 296 +- src/include/stir/NumericType.h | 79 +- src/include/stir/NumericType.inl | 22 +- src/include/stir/NumericVectorWithOffset.h | 84 +- src/include/stir/NumericVectorWithOffset.inl | 244 +- .../stir/OSMAPOSL/OSMAPOSLReconstruction.h | 109 +- src/include/stir/OSSPS/OSSPSReconstruction.h | 122 +- src/include/stir/ParseAndCreateFrom.h | 79 +- src/include/stir/ParseAndCreateFrom.inl | 49 +- .../stir/ParseDiscretisedDensityParameters.h | 67 +- src/include/stir/ParsingObject.h | 52 +- src/include/stir/PatientPosition.h | 105 +- src/include/stir/PixelsOnCartesianGrid.h | 96 +- src/include/stir/PixelsOnCartesianGrid.inl | 125 +- src/include/stir/PostFiltering.h | 27 +- src/include/stir/PostFiltering.inl | 40 +- src/include/stir/ProjData.h | 243 +- src/include/stir/ProjData.inl | 117 +- src/include/stir/ProjDataFromStream.h | 136 +- src/include/stir/ProjDataFromStream.inl | 60 +- src/include/stir/ProjDataGEAdvance.h | 70 +- src/include/stir/ProjDataGEHDF5.h | 74 +- src/include/stir/ProjDataInMemory.h | 85 +- src/include/stir/ProjDataInfo.h | 225 +- src/include/stir/ProjDataInfo.inl | 230 +- src/include/stir/ProjDataInfoCylindrical.h | 152 +- src/include/stir/ProjDataInfoCylindrical.inl | 220 +- .../stir/ProjDataInfoCylindricalArcCorr.h | 30 +- .../stir/ProjDataInfoCylindricalArcCorr.inl | 12 +- .../stir/ProjDataInfoCylindricalNoArcCorr.h | 179 +- .../stir/ProjDataInfoCylindricalNoArcCorr.inl | 186 +- src/include/stir/ProjDataInterfile.h | 40 +- src/include/stir/RegisteredObject.h | 37 +- src/include/stir/RegisteredObject.inl | 28 +- src/include/stir/RegisteredObjectBase.h | 12 +- src/include/stir/RegisteredParsingObject.h | 41 +- src/include/stir/RegisteredParsingObject.inl | 26 +- src/include/stir/RelatedViewgrams.h | 140 +- src/include/stir/RelatedViewgrams.inl | 135 +- src/include/stir/RunTests.h | 354 +- src/include/stir/SSRB.h | 75 +- src/include/stir/Scanner.h | 268 +- src/include/stir/Scanner.inl | 324 +- src/include/stir/Segment.h | 54 +- src/include/stir/Segment.inl | 22 +- src/include/stir/SegmentBySinogram.h | 37 +- src/include/stir/SegmentBySinogram.inl | 83 +- src/include/stir/SegmentByView.h | 46 +- src/include/stir/SegmentByView.inl | 82 +- .../stir/SeparableArrayFunctionObject.h | 28 +- .../stir/SeparableCartesianMetzImageFilter.h | 81 +- .../stir/SeparableConvolutionImageFilter.h | 83 +- .../stir/SeparableGaussianArrayFilter.h | 39 +- .../stir/SeparableGaussianImageFilter.h | 72 +- src/include/stir/SeparableMetzArrayFilter.h | 43 +- src/include/stir/Shape/Box3D.h | 43 +- src/include/stir/Shape/CombinedShape3D.h | 34 +- src/include/stir/Shape/CombinedShape3D.inl | 43 +- src/include/stir/Shape/DiscretisedShape3D.h | 85 +- src/include/stir/Shape/DiscretisedShape3D.inl | 9 +- src/include/stir/Shape/Ellipsoid.h | 43 +- src/include/stir/Shape/EllipsoidalCylinder.h | 60 +- .../stir/Shape/EllipsoidalCylinder.inl | 21 +- src/include/stir/Shape/Shape3D.h | 72 +- src/include/stir/Shape/Shape3D.inl | 34 +- .../stir/Shape/Shape3DWithOrientation.h | 30 +- src/include/stir/Sinogram.h | 59 +- src/include/stir/Sinogram.inl | 100 +- src/include/stir/StirException.h | 48 +- src/include/stir/Succeeded.h | 14 +- src/include/stir/TextWriter.h | 153 +- ...holdMinToSmallPositiveValueDataProcessor.h | 56 +- src/include/stir/TimeFrameDefinitions.h | 38 +- src/include/stir/TimeGateDefinitions.h | 36 +- src/include/stir/TimedBlock.h | 112 +- src/include/stir/TimedObject.h | 18 +- src/include/stir/TimedObject.inl | 18 +- src/include/stir/Timer.h | 12 +- src/include/stir/Timer.inl | 36 +- ...ivialDataSymmetriesForViewSegmentNumbers.h | 24 +- ...ialDataSymmetriesForViewSegmentNumbers.inl | 25 +- .../TruncateToCylindricalFOVImageProcessor.h | 66 +- src/include/stir/VectorWithOffset.h | 166 +- src/include/stir/VectorWithOffset.inl | 631 ++- src/include/stir/Verbosity.h | 7 +- src/include/stir/ViewSegmentNumbers.h | 23 +- src/include/stir/ViewSegmentNumbers.inl | 73 +- src/include/stir/Viewgram.h | 55 +- src/include/stir/Viewgram.inl | 96 +- src/include/stir/VoxelsOnCartesianGrid.h | 233 +- src/include/stir/VoxelsOnCartesianGrid.inl | 97 +- src/include/stir/ZoomOptions.h | 29 +- .../stir/analytic/FBP2D/FBP2DReconstruction.h | 83 +- src/include/stir/analytic/FBP2D/RampFilter.h | 34 +- .../stir/analytic/FBP3DRP/ColsherFilter.h | 104 +- .../analytic/FBP3DRP/FBP3DRPReconstruction.h | 206 +- src/include/stir/array_index_functions.h | 32 +- src/include/stir/array_index_functions.inl | 239 +- src/include/stir/assign.h | 81 +- src/include/stir/assign_to_subregion.h | 17 +- src/include/stir/assign_to_subregion.inl | 47 +- src/include/stir/centre_of_gravity.h | 46 +- src/include/stir/common.h | 269 +- src/include/stir/config/gcc.h | 16 +- src/include/stir/config/visualc.h | 28 +- src/include/stir/convert_array.h | 72 +- src/include/stir/convert_array.inl | 28 +- src/include/stir/convert_range.h | 28 +- src/include/stir/convert_range.inl | 220 +- src/include/stir/copy_fill.h | 79 +- src/include/stir/cross_product.h | 23 +- src/include/stir/data/SinglesRates.h | 154 +- src/include/stir/data/SinglesRates.inl | 15 +- .../stir/data/SinglesRatesForTimeFrames.h | 91 +- src/include/stir/data/SinglesRatesFromECAT7.h | 30 +- .../stir/data/SinglesRatesFromGEHDF5.h | 280 +- .../stir/data/SinglesRatesFromSglFile.h | 282 +- src/include/stir/date_time_functions.h | 33 +- src/include/stir/decay_correction_factor.h | 27 +- src/include/stir/deprecated.h | 13 +- src/include/stir/detail/test_if_1d.h | 102 +- src/include/stir/display.h | 119 +- src/include/stir/display.inl | 133 +- src/include/stir/doxygen_doc_for_boost.h | 47 +- src/include/stir/doxygengroups.h | 52 +- src/include/stir/doxygenmain.h | 16 +- src/include/stir/error.h | 13 +- src/include/stir/evaluation/ROIValues.h | 78 +- .../stir/evaluation/compute_ROI_values.h | 82 +- src/include/stir/extend_projdata.h | 17 +- src/include/stir/extract_line.h | 10 +- src/include/stir/extract_line.inl | 27 +- src/include/stir/find_fwhm_in_image.h | 86 +- src/include/stir/find_fwhm_in_image.inl | 70 +- src/include/stir/geometry/line_distances.h | 176 +- src/include/stir/getopt.h | 125 +- src/include/stir/index_at_maximum.h | 77 +- src/include/stir/info.h | 15 +- .../stir/interfile_keyword_functions.h | 24 +- src/include/stir/interpolate.h | 5 +- src/include/stir/interpolate_projdata.h | 54 +- src/include/stir/inverse_SSRB.h | 24 +- src/include/stir/is_null_ptr.h | 34 +- src/include/stir/line.h | 43 +- src/include/stir/linear_regression.h | 64 +- src/include/stir/linear_regression.inl | 221 +- ...tCylindricalScannerWithDiscreteDetectors.h | 33 +- ...ylindricalScannerWithDiscreteDetectors.inl | 54 +- ...ricalScannerWithViewTangRingRingEncoding.h | 39 +- ...calScannerWithViewTangRingRingEncoding.inl | 121 +- src/include/stir/listmode/CListModeData.h | 34 +- src/include/stir/listmode/CListModeDataECAT.h | 44 +- .../stir/listmode/CListModeDataECAT8_32bit.h | 29 +- .../stir/listmode/CListModeDataGEHDF5.h | 40 +- src/include/stir/listmode/CListModeDataROOT.h | 155 +- .../stir/listmode/CListModeDataSAFIR.h | 99 +- src/include/stir/listmode/CListRecord.h | 51 +- .../stir/listmode/CListRecordECAT8_32bit.h | 241 +- .../stir/listmode/CListRecordECAT962.h | 241 +- .../stir/listmode/CListRecordECAT966.h | 255 +- src/include/stir/listmode/CListRecordGEHDF5.h | 429 +- src/include/stir/listmode/CListRecordROOT.h | 193 +- src/include/stir/listmode/CListRecordROOT.inl | 14 +- src/include/stir/listmode/CListRecordSAFIR.h | 287 +- .../stir/listmode/CListRecordSAFIR.inl | 58 +- .../listmode/DetectorCoordinateMapFromFile.h | 100 +- src/include/stir/listmode/ListEvent.h | 16 +- src/include/stir/listmode/ListGatingInput.h | 3 +- src/include/stir/listmode/ListModeData.h | 64 +- src/include/stir/listmode/ListRecord.h | 14 +- .../stir/listmode/ListRecordWithGatingInput.h | 9 +- src/include/stir/listmode/ListTime.h | 20 +- src/include/stir/listmode/LmToProjData.h | 52 +- .../stir/listmode/LmToProjDataAbstract.h | 24 +- .../stir/listmode/LmToProjDataBootstrap.h | 24 +- .../LmToProjDataWithRandomRejection.h | 26 +- .../NiftyPET_listmode/LmToProjDataNiftyPET.h | 112 +- src/include/stir/listmode/SPECTListEvent.h | 7 +- src/include/stir/listmode/SPECTListModeData.h | 32 +- src/include/stir/listmode/SPECTListRecord.h | 15 +- .../listmode/SPECTListRecordWithGatingInput.h | 9 +- src/include/stir/make_array.h | 172 +- src/include/stir/make_array.inl | 316 +- src/include/stir/min_positive_element.h | 20 +- src/include/stir/modelling/KineticModel.h | 12 +- .../stir/modelling/KineticParameters.h | 24 +- .../stir/modelling/KineticParameters.inl | 39 +- src/include/stir/modelling/ModelMatrix.h | 137 +- src/include/stir/modelling/ModelMatrix.inl | 494 +-- .../modelling/ParametricDiscretisedDensity.h | 107 +- ...ndCreateParametricDiscretisedDensityFrom.h | 38 +- ...CreateParametricDiscretisedDensityFrom.inl | 56 +- src/include/stir/modelling/PatlakPlot.h | 161 +- src/include/stir/modelling/PlasmaData.h | 102 +- src/include/stir/modelling/PlasmaData.inl | 356 +- src/include/stir/modelling/PlasmaSample.h | 35 +- src/include/stir/modelling/PlasmaSample.inl | 72 +- src/include/stir/modulo.h | 104 +- src/include/stir/more_algorithms.h | 35 +- src/include/stir/more_algorithms.inl | 76 +- src/include/stir/num_threads.h | 10 +- src/include/stir/numerics/BSplines.h | 82 +- .../stir/numerics/BSplines1DRegularGrid.h | 183 +- .../stir/numerics/BSplines1DRegularGrid.inl | 241 +- src/include/stir/numerics/BSplinesDetail.inl | 478 +- .../stir/numerics/BSplinesRegularGrid.h | 242 +- .../stir/numerics/BSplinesRegularGrid.inl | 98 +- src/include/stir/numerics/BSplines_coef.inl | 182 +- .../stir/numerics/BSplines_weights.inl | 720 ++- src/include/stir/numerics/IR_filters.h | 38 +- src/include/stir/numerics/IR_filters.inl | 118 +- src/include/stir/numerics/MatrixFunction.h | 61 +- src/include/stir/numerics/MatrixFunction.inl | 192 +- src/include/stir/numerics/determinant.h | 12 +- src/include/stir/numerics/divide.h | 20 +- src/include/stir/numerics/divide.inl | 39 +- src/include/stir/numerics/erf.h | 13 +- src/include/stir/numerics/erf.inl | 706 ++- src/include/stir/numerics/fourier.h | 87 +- src/include/stir/numerics/ieeefp.h | 54 +- .../numerics/integrate_discrete_function.h | 10 +- .../numerics/integrate_discrete_function.inl | 58 +- src/include/stir/numerics/max_eigenvector.h | 152 +- src/include/stir/numerics/norm.h | 76 +- src/include/stir/numerics/norm.inl | 68 +- .../stir/numerics/overlap_interpolate.h | 62 +- .../stir/numerics/overlap_interpolate.inl | 167 +- .../stir/numerics/sampling_functions.h | 13 +- .../stir/numerics/sampling_functions.inl | 61 +- .../stir/numerics/stir_NumericalRecipes.h | 121 +- src/include/stir/recon_array_functions.h | 54 +- .../recon_buildblock/AnalyticReconstruction.h | 77 +- .../recon_buildblock/BackProjectorByBin.h | 142 +- .../BackProjectorByBinUsingInterpolation.h | 220 +- .../BackProjectorByBinUsingProjMatrixByBin.h | 59 +- ...ProjectorByBinUsingSquareProjMatrixByBin.h | 61 +- .../stir/recon_buildblock/BinNormalisation.h | 71 +- .../BinNormalisationFromAttenuationImage.h | 49 +- .../BinNormalisationFromECAT7.h | 36 +- .../BinNormalisationFromECAT8.h | 37 +- .../BinNormalisationFromGEHDF5.h | 44 +- .../BinNormalisationFromProjData.h | 41 +- .../recon_buildblock/BinNormalisationSPECT.h | 63 +- .../BinNormalisationWithCalibration.h | 32 +- .../ChainedBinNormalisation.h | 62 +- .../recon_buildblock/DataSymmetriesForBins.h | 87 +- .../DataSymmetriesForBins.inl | 28 +- .../DataSymmetriesForBins_PET_CartesianGrid.h | 129 +- ...ataSymmetriesForBins_PET_CartesianGrid.inl | 544 +-- .../DataSymmetriesForDensels.h | 53 +- .../DataSymmetriesForDensels.inl | 16 +- .../DistributedCachingInformation.h | 72 +- .../stir/recon_buildblock/DistributedWorker.h | 89 +- .../stir/recon_buildblock/FilterRootPrior.h | 60 +- .../recon_buildblock/ForwardProjectorByBin.h | 126 +- ...orwardProjectorByBinUsingProjMatrixByBin.h | 54 +- .../ForwardProjectorByBinUsingRayTracing.h | 212 +- .../stir/recon_buildblock/FourierRebinning.h | 296 +- .../GeneralisedObjectiveFunction.h | 247 +- .../stir/recon_buildblock/GeneralisedPrior.h | 40 +- .../recon_buildblock/GeneralisedPrior.inl | 20 +- .../IterativeReconstruction.h | 136 +- .../stir/recon_buildblock/LogcoshPrior.h | 242 +- .../BackProjectorByBinNiftyPET.h | 38 +- .../ForwardProjectorByBinNiftyPET.h | 81 +- .../NiftyPET_projector/NiftyPETHelper.h | 176 +- .../ProjectorByBinPairUsingNiftyPET.h | 22 +- src/include/stir/recon_buildblock/PLSPrior.h | 104 +- ...nearKineticModelAndDynamicProjectionData.h | 83 +- ...arKineticModelAndDynamicProjectionData.txx | 746 ++-- ...issonLogLikelihoodWithLinearModelForMean.h | 91 +- ...arModelForMeanAndGatedProjDataWithMotion.h | 92 +- ...ModelForMeanAndGatedProjDataWithMotion.txx | 643 ++- ...oodWithLinearModelForMeanAndListModeData.h | 70 +- ...orMeanAndListModeDataWithProjMatrixByBin.h | 82 +- ...elihoodWithLinearModelForMeanAndProjData.h | 120 +- .../PostsmoothingBackProjectorByBin.h | 55 +- .../PresmoothingForwardProjectorByBin.h | 52 +- .../PriorWithParabolicSurrogate.h | 24 +- .../stir/recon_buildblock/ProjDataRebinning.h | 47 +- .../stir/recon_buildblock/ProjMatrixByBin.h | 139 +- .../stir/recon_buildblock/ProjMatrixByBin.inl | 182 +- .../ProjMatrixByBinFromFile.h | 71 +- .../recon_buildblock/ProjMatrixByBinSPECTUB.h | 97 +- .../ProjMatrixByBinUsingInterpolation.h | 168 +- .../ProjMatrixByBinUsingRayTracing.h | 70 +- .../ProjMatrixElemsForOneBin.h | 86 +- .../ProjMatrixElemsForOneBin.inl | 75 +- .../ProjMatrixElemsForOneBinValue.h | 37 +- .../ProjMatrixElemsForOneBinValue.inl | 152 +- .../ProjMatrixElemsForOneDensel.h | 72 +- .../ProjMatrixElemsForOneDensel.inl | 64 +- .../ProjMatrixElemsForOneDenselValue.h | 30 +- .../ProjMatrixElemsForOneDenselValue.inl | 110 +- .../recon_buildblock/ProjectorByBinPair.h | 55 +- .../ProjectorByBinPairUsingProjMatrixByBin.h | 32 +- ...rojectorByBinPairUsingSeparateProjectors.h | 26 +- .../stir/recon_buildblock/QuadraticPrior.h | 104 +- .../RayTraceVoxelsOnCartesianGrid.h | 18 +- .../stir/recon_buildblock/Reconstruction.h | 77 +- .../stir/recon_buildblock/RelatedBins.h | 53 +- .../stir/recon_buildblock/RelatedBins.inl | 52 +- .../stir/recon_buildblock/RelatedDensels.h | 53 +- .../stir/recon_buildblock/RelatedDensels.inl | 53 +- .../RelativeDifferencePrior.h | 86 +- .../stir/recon_buildblock/SPECTUB_Tools.h | 548 ++- .../stir/recon_buildblock/SPECTUB_Weight3d.h | 110 +- .../stir/recon_buildblock/SqrtHessianRowSum.h | 157 +- .../SumOfGeneralisedObjectiveFunctions.h | 56 +- .../SumOfGeneralisedObjectiveFunctions.inl | 111 +- .../stir/recon_buildblock/SymmetryOperation.h | 59 +- .../SymmetryOperations_PET_CartesianGrid.h | 445 +- .../SymmetryOperations_PET_CartesianGrid.inl | 592 +-- .../TrivialBinNormalisation.h | 17 +- .../TrivialDataSymmetriesForBins.h | 56 +- .../stir/recon_buildblock/distributable.h | 125 +- .../distributableMPICacheEnabled.h | 35 +- .../recon_buildblock/distributable_main.h | 9 +- .../recon_buildblock/distributed_functions.h | 699 ++- .../distributed_test_functions.h | 97 +- .../find_basic_vs_nums_in_subsets.h | 29 +- .../test/PoissonLLReconstructionTests.h | 24 +- .../test/ReconstructionTests.h | 169 +- src/include/stir/round.h | 29 +- src/include/stir/round.inl | 79 +- src/include/stir/scale_sinograms.h | 13 +- .../stir/scatter/CreateTailMaskFromACFs.h | 92 +- src/include/stir/scatter/ScatterEstimation.h | 568 ++- .../stir/scatter/ScatterEstimation.inl | 52 +- src/include/stir/scatter/ScatterSimulation.h | 526 +-- .../stir/scatter/ScatterSimulation.inl | 58 +- .../stir/scatter/SingleScatterSimulation.h | 86 +- src/include/stir/shared_ptr.h | 42 +- .../GatedSpatialTransformation.h | 56 +- .../stir/spatial_transformation/InvertAxis.h | 30 +- .../SpatialTransformation.h | 19 +- .../stir/spatial_transformation/warp_image.h | 21 +- src/include/stir/stir_math.h | 26 +- src/include/stir/stream.h | 75 +- src/include/stir/stream.inl | 129 +- src/include/stir/thresholding.h | 74 +- src/include/stir/unique_ptr.h | 48 +- src/include/stir/utilities.h | 164 +- src/include/stir/utilities.inl | 80 +- src/include/stir/warning.h | 13 +- src/include/stir/zoom.h | 148 +- .../stir_experimental/AbsTimeInterval.h | 35 +- .../AbsTimeIntervalFromDynamicData.h | 24 +- .../AbsTimeIntervalFromECAT7ACF.h | 23 +- .../AbsTimeIntervalWithParsing.h | 16 +- .../stir_experimental/DAVArrayFilter3D.h | 31 +- .../stir_experimental/DAVImageFilter3D.h | 32 +- src/include/stir_experimental/Filter.h | 147 +- .../ModifiedInverseAveragingImageFilterAll.h | 123 +- .../ModifiedInverseAverigingArrayFilter.h | 65 +- .../ModifiedInverseAverigingImageFilter.h | 94 +- .../NonseparableSpatiallyVaryingFilters.h | 120 +- .../NonseparableSpatiallyVaryingFilters3D.h | 122 +- src/include/stir_experimental/Quaternion.h | 56 +- src/include/stir_experimental/Quaternion.inl | 149 +- .../SeparableGaussianArrayFilter.h | 43 +- .../SeparableGaussianImageFilter.h | 55 +- .../SeparableLowPassArrayFilter.h | 35 +- .../SeparableLowPassArrayFilter2.h | 131 +- .../SeparableLowPassImageFilter.h | 59 +- src/include/stir_experimental/fft.h | 42 +- .../fwd_and_bck_manipulation_for_SAF.h | 60 +- .../listmode/CListModeDataLMF.h | 42 +- .../listmode/CListRecordLMF.h | 116 +- .../listmode/LmToProjDataWithMC.h | 19 +- .../local_helping_functions.h | 50 +- .../stir_experimental/modelling/BloodFrame.h | 55 +- .../modelling/BloodFrame.inl | 99 +- .../modelling/BloodFrameData.h | 89 +- .../modelling/BloodFrameData.inl | 151 +- .../modelling/OneParamModel.h | 10 +- .../modelling/OneParamModel.inl | 58 +- .../motion/MatchTrackerAndScanner.h | 54 +- ...onRigidObjectTransformationUsingBSplines.h | 38 +- .../motion/ObjectTransformation.h | 15 +- ...arModelForMeanAndGatedProjDataWithMotion.h | 73 +- .../motion/Polaris_MT_File.h | 88 +- .../motion/RigidObject3DMotion.h | 62 +- .../motion/RigidObject3DMotionFromPolaris.h | 64 +- .../motion/RigidObject3DTransformation.h | 78 +- .../motion/TimeFrameMotion.h | 44 +- .../motion/Transform3DObjectImageProcessor.h | 57 +- .../motion/bin_interpolate.h | 291 +- .../motion/transform_3d_object.h | 93 +- .../motion/transform_3d_object.inl | 87 +- ...ltiply_plane_scale_factorsImageProcessor.h | 54 +- .../numerics/linear_extrapolation.h | 17 +- .../numerics/more_interpolators.h | 122 +- .../numerics/more_interpolators.inl | 175 +- .../phantoms/CylindersWithLineSource.h | 223 +- src/include/stir_experimental/phantoms/Utah.h | 130 +- .../BinNormalisationFromML2D.h | 27 +- .../BinNormalisationSinogramRescaling.h | 28 +- .../BinNormalisationUsingProfile.h | 17 +- ...taSymmetriesForDensels_PET_CartesianGrid.h | 59 +- ...SymmetriesForDensels_PET_CartesianGrid.inl | 186 +- .../ParametricQuadraticPrior.h | 91 +- ...WithLinearModelForMeanAndDynamicProjData.h | 43 +- .../PostsmoothingForwardProjectorByBin.h | 51 +- .../ProjMatrixByBinSinglePhoton.h | 49 +- .../ProjMatrixByBinUsingSolidAngle.h | 51 +- .../ProjMatrixByBinWithPositronRange.h | 54 +- .../recon_buildblock/ProjMatrixByDensel.h | 120 +- .../recon_buildblock/ProjMatrixByDensel.inl | 75 +- ...atrixByDenselOnCartesianGridUsingElement.h | 51 +- .../ProjMatrixByDenselUsingRayTracing.h | 62 +- src/iterative/KOSMAPOSL/KOSMAPOSL.cxx | 36 +- .../KOSMAPOSL/KOSMAPOSLReconstruction.cxx | 1302 +++--- src/iterative/OSMAPOSL/OSMAPOSL.cxx | 36 +- .../OSMAPOSL/OSMAPOSLReconstruction.cxx | 478 +- src/iterative/OSSPS/OSSPS.cxx | 17 +- src/iterative/OSSPS/OSSPSReconstruction.cxx | 424 +- src/iterative/POSMAPOSL/POSMAPOSL.cxx | 17 +- src/iterative/POSSPS/POSSPS.cxx | 13 +- src/listmode_buildblock/CListEvent.cxx | 13 +- src/listmode_buildblock/CListModeDataECAT.cxx | 297 +- .../CListModeDataECAT8_32bit.cxx | 96 +- .../CListModeDataGEHDF5.cxx | 82 +- src/listmode_buildblock/CListModeDataROOT.cxx | 543 ++- .../CListModeDataSAFIR.cxx | 124 +- .../CListRecordECAT8_32bit.cxx | 64 +- .../CListRecordECAT962.cxx | 49 +- .../CListRecordECAT966.cxx | 37 +- src/listmode_buildblock/CListRecordROOT.cxx | 76 +- .../DetectorCoordinateMapFromFile.cxx | 92 +- src/listmode_buildblock/ListEvent.cxx | 10 +- src/listmode_buildblock/ListModeData.cxx | 32 +- src/listmode_buildblock/LmToProjData.cxx | 1408 +++--- .../LmToProjDataAbstract.cxx | 5 +- .../LmToProjDataBootstrap.cxx | 243 +- .../LmToProjDataWithRandomRejection.cxx | 114 +- .../LmToProjDataNiftyPET.cxx | 57 +- .../add_ecat7_header_to_sgl.cxx | 173 +- src/listmode_utilities/conv_NiftyPET_stir.cxx | 334 +- src/listmode_utilities/list_lm_countrates.cxx | 94 +- src/listmode_utilities/list_lm_events.cxx | 236 +- src/listmode_utilities/list_lm_info.cxx | 68 +- src/listmode_utilities/lm_fansums.cxx | 262 +- src/listmode_utilities/lm_to_projdata.cxx | 64 +- .../lm_to_projdata_NiftyPET.cxx | 226 +- .../lm_to_projdata_bootstrap.cxx | 35 +- .../lm_to_projdata_with_random_rejection.cxx | 22 +- src/listmode_utilities/print_sgl_values.cxx | 52 +- src/listmode_utilities/rebin_sgl_file.cxx | 83 +- src/listmode_utilities/scan_sgl_file.cxx | 275 +- src/modelling_buildblock/KineticModel.cxx | 15 +- .../ParametricDiscretisedDensity.cxx | 145 +- src/modelling_buildblock/PatlakPlot.cxx | 486 +- .../modelling_registries.cxx | 3 +- .../apply_patlak_to_images.cxx | 94 +- ...ct_single_images_from_parametric_image.cxx | 140 +- ..._dynamic_images_from_parametric_images.cxx | 164 +- .../make_parametric_image_from_components.cxx | 113 +- .../mult_image_parameters.cxx | 95 +- .../mult_model_with_dyn_images.cxx | 87 +- .../write_patlak_matrix.cxx | 53 +- src/numerics_buildblock/determinant.cxx | 77 +- src/numerics_buildblock/fourier.cxx | 480 +- .../AnalyticReconstruction.cxx | 201 +- src/recon_buildblock/BackProjectorByBin.cxx | 334 +- .../BackProjectorByBinUsingInterpolation.cxx | 1074 ++--- ...ProjectorByBinUsingInterpolation_3DCho.cxx | 3800 ++++++++-------- ...rojectorByBinUsingInterpolation_linear.cxx | 4 +- ...BinUsingInterpolation_piecewise_linear.cxx | 4 +- ...BackProjectorByBinUsingProjMatrixByBin.cxx | 314 +- ...ojectorByBinUsingSquareProjMatrixByBin.cxx | 112 +- src/recon_buildblock/BinNormalisation.cxx | 222 +- .../BinNormalisationFromAttenuationImage.cxx | 133 +- .../BinNormalisationFromECAT7.cxx | 505 +-- .../BinNormalisationFromECAT8.cxx | 611 +-- .../BinNormalisationFromGEHDF5.cxx | 535 +-- .../BinNormalisationFromProjData.cxx | 159 +- .../BinNormalisationSPECT.cxx | 461 +- .../BinNormalisationWithCalibration.cxx | 68 +- .../ChainedBinNormalisation.cxx | 188 +- .../DataSymmetriesForBins.cxx | 87 +- ...ataSymmetriesForBins_PET_CartesianGrid.cxx | 251 +- .../DataSymmetriesForDensels.cxx | 31 +- .../DistributedCachingInformation.cxx | 178 +- src/recon_buildblock/DistributedWorker.cxx | 677 ++- src/recon_buildblock/FilterRootPrior.cxx | 138 +- .../ForwardProjectorByBin.cxx | 298 +- ...wardProjectorByBinUsingProjMatrixByBin.cxx | 179 +- .../ForwardProjectorByBinUsingRayTracing.cxx | 1452 +++--- ...rdProjectorByBinUsingRayTracing_Siddon.cxx | 622 ++- src/recon_buildblock/FourierRebinning.cxx | 1000 ++--- .../GeneralisedObjectiveFunction.cxx | 434 +- src/recon_buildblock/GeneralisedPrior.cxx | 60 +- .../IterativeReconstruction.cxx | 583 ++- src/recon_buildblock/LogcoshPrior.cxx | 406 +- .../BackProjectorByBinNiftyPET.cxx | 173 +- .../ForwardProjectorByBinNiftyPET.cxx | 161 +- .../NiftyPET_projector/NiftyPETHelper.cxx | 2150 +++++---- .../ProjectorByBinPairUsingNiftyPET.cxx | 89 +- src/recon_buildblock/PLSPrior.cxx | 766 ++-- ...arKineticModelAndDynamicProjectionData.cxx | 11 +- ...sonLogLikelihoodWithLinearModelForMean.cxx | 571 +-- ...ModelForMeanAndGatedProjDataWithMotion.cxx | 19 +- ...dWithLinearModelForMeanAndListModeData.cxx | 175 +- ...MeanAndListModeDataWithProjMatrixByBin.cxx | 754 ++-- ...ihoodWithLinearModelForMeanAndProjData.cxx | 1225 +++--- .../PostsmoothingBackProjectorByBin.cxx | 143 +- .../PresmoothingForwardProjectorByBin.cxx | 110 +- src/recon_buildblock/ProjDataRebinning.cxx | 132 +- src/recon_buildblock/ProjMatrixByBin.cxx | 241 +- .../ProjMatrixByBinFromFile.cxx | 575 ++- .../ProjMatrixByBinSPECTUB.cxx | 1162 +++-- .../ProjMatrixByBinUsingInterpolation.cxx | 373 +- .../ProjMatrixByBinUsingRayTracing.cxx | 741 ++-- .../ProjMatrixElemsForOneBin.cxx | 412 +- .../ProjMatrixElemsForOneDensel.cxx | 163 +- src/recon_buildblock/ProjectorByBinPair.cxx | 42 +- ...ProjectorByBinPairUsingProjMatrixByBin.cxx | 65 +- ...jectorByBinPairUsingSeparateProjectors.cxx | 55 +- src/recon_buildblock/QuadraticPrior.cxx | 832 ++-- .../RayTraceVoxelsOnCartesianGrid.cxx | 251 +- src/recon_buildblock/Reconstruction.cxx | 162 +- src/recon_buildblock/RegisteredObject.cxx | 38 +- .../RelativeDifferencePrior.cxx | 495 +-- src/recon_buildblock/SPECTUB_Tools.cxx | 1368 +++--- src/recon_buildblock/SPECTUB_Weight3d.cxx | 1734 ++++---- src/recon_buildblock/SqrtHessianRowSum.cxx | 125 +- src/recon_buildblock/SymmetryOperation.cxx | 28 +- .../SymmetryOperations_PET_CartesianGrid.cxx | 403 +- .../TrivialBinNormalisation.cxx | 4 +- .../TrivialDataSymmetriesForBins.cxx | 123 +- src/recon_buildblock/distributable.cxx | 676 ++- .../distributableMPICacheEnabled.cxx | 502 +-- .../distributed_functions.cxx | 1411 +++--- .../distributed_test_functions.cxx | 470 +- .../find_basic_vs_nums_in_subset.cxx | 62 +- .../recon_buildblock_registries.cxx | 27 +- src/recon_test/bcktest.cxx | 441 +- src/recon_test/fwdtest.cxx | 562 +-- src/recon_test/recontest.cxx | 111 +- ...ataSymmetriesForBins_PET_CartesianGrid.cxx | 871 ++-- src/recon_test/test_FBP2D.cxx | 81 +- src/recon_test/test_FBP3DRP.cxx | 46 +- src/recon_test/test_OSMAPOSL.cxx | 90 +- ...ihoodWithLinearModelForMeanAndProjData.cxx | 316 +- src/recon_test/test_consistency_root.cxx | 300 +- .../test_data_processor_projectors.cxx | 509 +-- src/recon_test/test_priors.cxx | 176 +- .../CreateTailMaskFromACFs.cxx | 225 +- src/scatter_buildblock/ScatterEstimation.cxx | 1987 ++++----- src/scatter_buildblock/ScatterSimulation.cxx | 1236 +++--- .../SingleScatterSimulation.cxx | 103 +- .../cached_single_scatter_integrals.cxx | 171 +- src/scatter_buildblock/extradebug.cxx | 102 +- .../sample_scatter_points.cxx | 80 +- .../scatter_detection_modelling.cxx | 157 +- ...scatter_estimate_for_one_scatter_point.cxx | 150 +- .../single_scatter_estimate.cxx | 42 +- .../single_scatter_integrals.cxx | 140 +- .../upsample_and_fit_scatter_estimate.cxx | 156 +- .../create_tail_mask_from_ACFs.cxx | 175 +- src/scatter_utilities/estimate_scatter.cxx | 51 +- src/scatter_utilities/simulate_scatter.cxx | 86 +- .../upsample_and_fit_single_scatter.cxx | 208 +- .../GatedSpatialTransformation.cxx | 251 +- .../InvertAxis.cxx | 120 +- .../SpatialTransformation.cxx | 25 +- .../spatial_transformation_registries.cxx | 35 +- .../warp_image.cxx | 94 +- src/test/IO/test_IO_DiscretisedDensity.cxx | 69 +- .../IO/test_IO_DynamicDiscretisedDensity.cxx | 133 +- src/test/IO/test_IO_ITKMulticomponent.cxx | 69 +- .../test_IO_ParametricDiscretisedDensity.cxx | 153 +- .../test_ProjectorNiftyPET_adjoint.cxx | 629 ++- .../test_ParametricDiscretisedDensity.cxx | 200 +- src/test/modelling/test_modelling.cxx | 272 +- src/test/numerics/BSplines_timing.cxx | 255 +- src/test/numerics/test_BSplines.cxx | 719 ++- .../numerics/test_BSplinesRegularGrid.cxx | 878 ++-- .../numerics/test_BSplinesRegularGrid1D.cxx | 418 +- src/test/numerics/test_Fourier.cxx | 109 +- src/test/numerics/test_IR_filters.cxx | 193 +- src/test/numerics/test_erf.cxx | 93 +- .../test_integrate_discrete_function.cxx | 99 +- src/test/numerics/test_matrices.cxx | 425 +- .../numerics/test_overlap_interpolate.cxx | 272 +- src/test/test_ArcCorrection.cxx | 211 +- src/test/test_Array.cxx | 759 ++-- src/test/test_ArrayFilter.cxx | 540 ++- src/test/test_ByteOrder.cxx | 21 +- src/test/test_DateTime.cxx | 136 +- src/test/test_DynamicDiscretisedDensity.cxx | 462 +- .../test_GeneralisedPoissonNoiseGenerator.cxx | 45 +- src/test/test_IndexRange.cxx | 106 +- src/test/test_KeyParser.cxx | 68 +- src/test/test_NestedIterator.cxx | 429 +- src/test/test_OutputFileFormat.cxx | 166 +- src/test/test_ROIs.cxx | 454 +- src/test/test_Scanner.cxx | 96 +- src/test/test_ScatterSimulation.cxx | 694 ++- .../test_SeparableGaussianArrayFilter.cxx | 79 +- src/test/test_SeparableMetzArrayFilter.cxx | 67 +- src/test/test_VectorWithOffset.cxx | 929 ++-- src/test/test_VoxelsOnCartesianGrid.cxx | 348 +- src/test/test_convert_array.cxx | 186 +- src/test/test_coordinates.cxx | 308 +- src/test/test_display.cxx | 73 +- src/test/test_export_array.cxx | 472 +- src/test/test_filename_functions.cxx | 636 ++- src/test/test_find_fwhm_in_image.cxx | 359 +- src/test/test_interpolate.cxx | 173 +- src/test/test_linear_regression.cxx | 155 +- src/test/test_multiple_proj_data.cxx | 107 +- src/test/test_proj_data.cxx | 240 +- src/test/test_proj_data_in_memory.cxx | 242 +- src/test/test_proj_data_info.cxx | 1316 +++--- src/test/test_proj_data_maths.cxx | 152 +- src/test/test_stir_math.cxx | 276 +- src/test/test_time_of_flight.cxx | 663 ++- src/test/test_warp_image.cxx | 185 +- src/test/test_zoom_image.cxx | 149 +- src/utilities/SSRB.cxx | 67 +- src/utilities/abs_image.cxx | 180 +- src/utilities/apply_normfactors.cxx | 173 +- src/utilities/apply_normfactors3D.cxx | 201 +- ...ttenuation_coefficients_to_projections.cxx | 103 +- src/utilities/back_project.cxx | 72 +- .../calculate_attenuation_coefficients.cxx | 127 +- src/utilities/compare_image.cxx | 148 +- src/utilities/compare_projdata.cxx | 159 +- .../compute_sqrt_Hessian_row_sum.cxx | 18 +- .../construct_randoms_from_GEsingles.cxx | 202 +- .../construct_randoms_from_singles.cxx | 216 +- src/utilities/conv_AVW.cxx | 150 +- ...nv_GATE_raw_ECAT_projdata_to_interfile.cxx | 140 +- src/utilities/conv_gipl_to_interfile.cxx | 231 +- src/utilities/conv_interfile_to_gipl.cxx | 193 +- src/utilities/convert_to_binary_image.cxx | 47 +- src/utilities/correct_projdata.cxx | 440 +- src/utilities/create_multi_header.cxx | 44 +- src/utilities/create_projdata_template.cxx | 28 +- src/utilities/ctac_to_mu_values.cxx | 173 +- src/utilities/display_projdata.cxx | 68 +- src/utilities/do_linear_regression.cxx | 108 +- src/utilities/ecat/conv_to_ecat6.cxx | 328 +- src/utilities/ecat/conv_to_ecat7.cxx | 231 +- src/utilities/ecat/convecat6_if.cxx | 236 +- src/utilities/ecat/copy_ecat7_header.cxx | 640 ++- src/utilities/ecat/ecat_swap_corners.cxx | 233 +- src/utilities/ecat/ifheaders_for_ecat7.cxx | 72 +- src/utilities/ecat/is_ecat7_file.cxx | 82 +- .../ecat/print_ecat_singles_values.cxx | 61 +- ..._triple_energy_window_scatter_sinogram.cxx | 236 +- src/utilities/extract_segments.cxx | 86 +- ...tract_single_images_from_dynamic_image.cxx | 118 +- src/utilities/find_ML_normfactors.cxx | 384 +- src/utilities/find_ML_normfactors3D.cxx | 486 +- .../find_ML_singles_from_delayed.cxx | 237 +- src/utilities/find_fwhm_in_image.cxx | 170 +- src/utilities/find_maxima_in_image.cxx | 149 +- src/utilities/forward_project.cxx | 99 +- src/utilities/generate_image.cxx | 327 +- src/utilities/get_time_frame_info.cxx | 181 +- src/utilities/invert_axis.cxx | 37 +- src/utilities/list_ROI_values.cxx | 289 +- src/utilities/list_detector_and_bin_info.cxx | 75 +- src/utilities/list_image_info.cxx | 187 +- src/utilities/list_image_values.cxx | 124 +- src/utilities/list_projdata_info.cxx | 170 +- src/utilities/manip_image.cxx | 932 ++-- src/utilities/manip_projdata.cxx | 746 ++-- src/utilities/poisson_noise.cxx | 70 +- src/utilities/postfilter.cxx | 283 +- src/utilities/rebin_projdata.cxx | 80 +- src/utilities/shift_image.cxx | 126 +- src/utilities/shift_image_origin.cxx | 57 +- src/utilities/stir_math.cxx | 754 ++-- src/utilities/stir_write_pgm.cxx | 222 +- .../warp_and_accumulate_gated_images.cxx | 37 +- src/utilities/warp_image.cxx | 71 +- src/utilities/write_proj_matrix_by_bin.cxx | 128 +- src/utilities/zeropad_planes.cxx | 85 +- src/utilities/zoom_image.cxx | 181 +- 1108 files changed, 91590 insertions(+), 121055 deletions(-) mode change 100755 => 100644 src/data_buildblock/SinglesRatesFromGEHDF5.cxx mode change 100755 => 100644 src/include/stir/data/SinglesRatesFromGEHDF5.h mode change 100755 => 100644 src/listmode_utilities/conv_NiftyPET_stir.cxx mode change 100755 => 100644 src/utilities/construct_randoms_from_GEsingles.cxx mode change 100755 => 100644 src/utilities/invert_axis.cxx diff --git a/examples/C++/General_Reconstruction/General_Reconstruction.cxx b/examples/C++/General_Reconstruction/General_Reconstruction.cxx index 277dc792ff..d24605ce04 100644 --- a/examples/C++/General_Reconstruction/General_Reconstruction.cxx +++ b/examples/C++/General_Reconstruction/General_Reconstruction.cxx @@ -5,53 +5,40 @@ #include START_NAMESPACE_STIR -General_Reconstruction:: -General_Reconstruction() -{ - this->set_defaults(); -} +General_Reconstruction::General_Reconstruction() { this->set_defaults(); } void -General_Reconstruction::set_defaults() -{ - -} +General_Reconstruction::set_defaults() {} void -General_Reconstruction::initialise_keymap() -{ - this->parser.add_start_key("General reconstruction"); - this->parser.add_stop_key("End General reconstruction"); +General_Reconstruction::initialise_keymap() { + this->parser.add_start_key("General reconstruction"); + this->parser.add_stop_key("End General reconstruction"); - this->parser.add_parsing_key("reconstruction method", &this->reconstruction_method_sptr); + this->parser.add_parsing_key("reconstruction method", &this->reconstruction_method_sptr); } bool -General_Reconstruction::post_processing() -{ - return false; +General_Reconstruction::post_processing() { + return false; } Succeeded -General_Reconstruction::process_data() -{ - HighResWallClockTimer t; - t.reset(); - t.start(); - - //return reconstruction_object.reconstruct() == Succeeded::yes ? - // EXIT_SUCCESS : EXIT_FAILURE; - if (reconstruction_method_sptr->reconstruct() == Succeeded::yes) - { - t.stop(); - std::cout << "Total Wall clock time: " << t.value() << " seconds" << std::endl; - return Succeeded::yes; - } - else - { - t.stop(); - return Succeeded::no; - } +General_Reconstruction::process_data() { + HighResWallClockTimer t; + t.reset(); + t.start(); + + // return reconstruction_object.reconstruct() == Succeeded::yes ? + // EXIT_SUCCESS : EXIT_FAILURE; + if (reconstruction_method_sptr->reconstruct() == Succeeded::yes) { + t.stop(); + std::cout << "Total Wall clock time: " << t.value() << " seconds" << std::endl; + return Succeeded::yes; + } else { + t.stop(); + return Succeeded::no; + } } END_NAMESPACE_STIR diff --git a/examples/C++/General_Reconstruction/General_Reconstruction.h b/examples/C++/General_Reconstruction/General_Reconstruction.h index a97321c60a..f2da886158 100644 --- a/examples/C++/General_Reconstruction/General_Reconstruction.h +++ b/examples/C++/General_Reconstruction/General_Reconstruction.h @@ -16,31 +16,26 @@ #include "stir/CartesianCoordinate3D.h" #include "Reconstruction.h" - START_NAMESPACE_STIR class Succeeded; -class General_Reconstruction : public ParsingObject -{ +class General_Reconstruction : public ParsingObject { public: - //! - //! \brief General_Reconstuction - //! \details Default constructor - General_Reconstruction(); + //! + //! \brief General_Reconstuction + //! \details Default constructor + General_Reconstruction(); - virtual Succeeded process_data(); -protected: + virtual Succeeded process_data(); - void set_defaults(); - void initialise_keymap(); - bool post_processing(); +protected: + void set_defaults(); + void initialise_keymap(); + bool post_processing(); private: - - shared_ptr < Reconstruction < DiscretisedDensity < 3, float > > > - reconstruction_method_sptr; - + shared_ptr>> reconstruction_method_sptr; }; END_NAMESPACE_STIR diff --git a/examples/C++/src/demo1.cxx b/examples/C++/src/demo1.cxx index 8b9a10168d..ed91da07d9 100644 --- a/examples/C++/src/demo1.cxx +++ b/examples/C++/src/demo1.cxx @@ -6,20 +6,20 @@ \brief A simple program that backprojects some projection data. It illustrates - - basic interaction with the user, - - reading of images and projection data - - construction of a specified type of back-projector, - - how to use back-project all projection data - - output of images + - basic interaction with the user, + - reading of images and projection data + - construction of a specified type of back-projector, + - how to use back-project all projection data + - output of images See README.txt in the directory where this file is located. - \author Kris Thielemans + \author Kris Thielemans */ /* Copyright (C) 2004- 2011, Hammersmith Imanet Ltd - This software is distributed under the terms + This software is distributed under the terms of the GNU General Public Licence (GPL) See STIR/LICENSE.txt for details */ @@ -33,25 +33,20 @@ #include "stir/utilities.h" #include "stir/Succeeded.h" -int main() -{ +int +main() { using namespace stir; - + /////////////// input sinogram - const std::string input_filename = - ask_filename_with_extension("Input file",".hs"); + const std::string input_filename = ask_filename_with_extension("Input file", ".hs"); - shared_ptr - proj_data_sptr(ProjData::read_from_file(input_filename)); - shared_ptr - proj_data_info_sptr(proj_data_sptr->get_proj_data_info_sptr()->clone()); + shared_ptr proj_data_sptr(ProjData::read_from_file(input_filename)); + shared_ptr proj_data_info_sptr(proj_data_sptr->get_proj_data_info_sptr()->clone()); /////////////// template image (for sizes etc) - const std::string template_filename = - ask_filename_with_extension("Template image file",".hv"); + const std::string template_filename = ask_filename_with_extension("Template image file", ".hv"); - shared_ptr > - density_sptr(read_from_file >(template_filename)); + shared_ptr> density_sptr(read_from_file>(template_filename)); density_sptr->fill(0); diff --git a/examples/C++/src/demo2.cxx b/examples/C++/src/demo2.cxx index 028122ef07..4c9f367dd6 100644 --- a/examples/C++/src/demo2.cxx +++ b/examples/C++/src/demo2.cxx @@ -4,23 +4,23 @@ \file \ingroup examples \brief A small modification of demo1.cxx to ask the user for the - back projector she wants to use. + back projector she wants to use. It illustrates - - how to ask the user for objects for which different types - exist (e.g. back-projector, forward-projectors, image processors - etc), anything based on the RegisteredObject hierarchy. - - that STIR is able to select basic processing units at run-time - - how to use the (very) basic display facilities in STIR + - how to ask the user for objects for which different types + exist (e.g. back-projector, forward-projectors, image processors + etc), anything based on the RegisteredObject hierarchy. + - that STIR is able to select basic processing units at run-time + - how to use the (very) basic display facilities in STIR See README.txt in the directory where this file is located. - \author Kris Thielemans + \author Kris Thielemans */ /* Copyright (C) 2004- 2012, Hammersmith Imanet Ltd - This software is distributed under the terms + This software is distributed under the terms of the GNU General Public Licence (GPL) See STIR/LICENSE.txt for details */ @@ -34,31 +34,25 @@ #include "stir/Succeeded.h" #include "stir/display.h" -int main() -{ +int +main() { using namespace stir; - + /////////////// input sinogram - const std::string input_filename = - ask_filename_with_extension("Input file",".hs"); + const std::string input_filename = ask_filename_with_extension("Input file", ".hs"); - shared_ptr - proj_data_sptr(ProjData::read_from_file(input_filename)); - shared_ptr - proj_data_info_sptr(proj_data_sptr->get_proj_data_info_sptr()->clone()); + shared_ptr proj_data_sptr(ProjData::read_from_file(input_filename)); + shared_ptr proj_data_info_sptr(proj_data_sptr->get_proj_data_info_sptr()->clone()); /////////////// template image (for sizes etc) - const std::string template_filename = - ask_filename_with_extension("Template image file",".hv"); + const std::string template_filename = ask_filename_with_extension("Template image file", ".hv"); - shared_ptr > - density_sptr(read_from_file >(template_filename)); + shared_ptr> density_sptr(read_from_file>(template_filename)); density_sptr->fill(0); /////////////// back project - shared_ptr back_projector_sptr - (BackProjectorByBin::ask_type_and_parameters()); + shared_ptr back_projector_sptr(BackProjectorByBin::ask_type_and_parameters()); back_projector_sptr->set_up(proj_data_info_sptr, density_sptr); diff --git a/examples/C++/src/demo3.cxx b/examples/C++/src/demo3.cxx index 3ae745d11f..989d9b3bd4 100644 --- a/examples/C++/src/demo3.cxx +++ b/examples/C++/src/demo3.cxx @@ -6,10 +6,10 @@ \brief A modification of demo2.cxx that parses all parameters from a parameter file. It illustrates - - basic class derivation principles - - how to use ParsingObject to have automatic capabilities of parsing - parameters files (and interactive questions to the user) - - how most STIR programs parse the parameter files. + - basic class derivation principles + - how to use ParsingObject to have automatic capabilities of parsing + parameters files (and interactive questions to the user) + - how most STIR programs parse the parameter files. Note that the same functionality could be provided without deriving a new class from ParsingObject. One could have a KeyParser object @@ -17,12 +17,12 @@ See README.txt in the directory where this file is located. - \author Kris Thielemans + \author Kris Thielemans */ /* Copyright (C) 2004- 2012, Hammersmith Imanet Ltd - This software is distributed under the terms + This software is distributed under the terms of the GNU General Public Licence (GPL) See STIR/LICENSE.txt for details */ @@ -38,29 +38,27 @@ namespace stir { -class MyStuff: public ParsingObject -{ +class MyStuff : public ParsingObject { public: void set_defaults(); void initialise_keymap(); void run(); + private: std::string input_filename; std::string template_filename; shared_ptr back_projector_sptr; - shared_ptr > > output_file_format_sptr; + shared_ptr>> output_file_format_sptr; }; void -MyStuff::set_defaults() -{ +MyStuff::set_defaults() { back_projector_sptr.reset(new BackProjectorByBinUsingInterpolation); - output_file_format_sptr = OutputFileFormat >::default_sptr(); + output_file_format_sptr = OutputFileFormat>::default_sptr(); } -void -MyStuff::initialise_keymap() -{ +void +MyStuff::initialise_keymap() { parser.add_start_key("MyStuff parameters"); parser.add_key("input file", &input_filename); parser.add_key("template image file", &template_filename); @@ -70,16 +68,12 @@ MyStuff::initialise_keymap() } void -MyStuff::run() -{ +MyStuff::run() { - shared_ptr - proj_data_sptr(ProjData::read_from_file(input_filename)); - shared_ptr - proj_data_info_sptr(proj_data_sptr->get_proj_data_info_sptr()->clone()); + shared_ptr proj_data_sptr(ProjData::read_from_file(input_filename)); + shared_ptr proj_data_info_sptr(proj_data_sptr->get_proj_data_info_sptr()->clone()); - shared_ptr > - density_sptr(read_from_file >(template_filename)); + shared_ptr> density_sptr(read_from_file>(template_filename)); density_sptr->fill(0); @@ -94,20 +88,19 @@ MyStuff::run() display(*density_sptr, density_sptr->find_max(), "Output"); } -}// end of namespace stir +} // end of namespace stir -int main(int argc, char **argv) -{ +int +main(int argc, char** argv) { using namespace stir; - if (argc!=2) - { - std::cerr << "Normal usage: " << argv[0] << " parameter-file\n"; - std::cerr << "I will now ask you the questions interactively\n"; - } + if (argc != 2) { + std::cerr << "Normal usage: " << argv[0] << " parameter-file\n"; + std::cerr << "I will now ask you the questions interactively\n"; + } MyStuff my_stuff; my_stuff.set_defaults(); - if (argc!=2) + if (argc != 2) my_stuff.ask_parameters(); else my_stuff.parse(argv[1]); diff --git a/examples/src/demo4_obj_fun.cxx b/examples/src/demo4_obj_fun.cxx index 7a6e248d37..51b9970e84 100644 --- a/examples/src/demo4_obj_fun.cxx +++ b/examples/src/demo4_obj_fun.cxx @@ -21,12 +21,12 @@ See README.txt in the directory where this file is located. - \author Kris Thielemans and Robert Twyman + \author Kris Thielemans and Robert Twyman */ /* Copyright (C) 2020 University College London - This software is distributed under the terms + This software is distributed under the terms of the GNU General Public Licence (GPL) See STIR/LICENSE.txt for details */ @@ -38,44 +38,38 @@ namespace stir { -class MyStuff: public ParsingObject -{ +class MyStuff : public ParsingObject { public: MyStuff(); void set_defaults(); void run(); - typedef DiscretisedDensity<3,float> target_type; + typedef DiscretisedDensity<3, float> target_type; protected: - shared_ptr > objective_function_sptr; + shared_ptr> objective_function_sptr; private: std::string input_filename; std::string image_filename; int num_iterations; float step_size; - shared_ptr > > output_file_format_sptr; + shared_ptr>> output_file_format_sptr; void initialise_keymap(); bool post_processing(); }; -MyStuff::MyStuff() -{ - set_defaults(); -} +MyStuff::MyStuff() { set_defaults(); } void -MyStuff::set_defaults() -{ +MyStuff::set_defaults() { objective_function_sptr.reset(new PoissonLogLikelihoodWithLinearModelForMeanAndProjData); - output_file_format_sptr = OutputFileFormat >::default_sptr(); + output_file_format_sptr = OutputFileFormat>::default_sptr(); num_iterations = 10; step_size = 0.001; } -void -MyStuff::initialise_keymap() -{ +void +MyStuff::initialise_keymap() { parser.add_start_key("MyStuff parameters"); parser.add_key("input file", &input_filename); parser.add_key("image filename", &image_filename); @@ -85,27 +79,22 @@ MyStuff::initialise_keymap() parser.add_stop_key("End"); } -bool MyStuff:: -post_processing() -{ - if (is_null_ptr(this->objective_function_sptr)) - { - error("objective_function_sptr is null"); - return true; +bool +MyStuff::post_processing() { + if (is_null_ptr(this->objective_function_sptr)) { + error("objective_function_sptr is null"); + return true; } return false; } void -MyStuff::run() -{ +MyStuff::run() { /////// load initial density from file - shared_ptr > - density_sptr(read_from_file >(image_filename)); + shared_ptr> density_sptr(read_from_file>(image_filename)); //////// gradient it copied Density filled with 0's - shared_ptr > - gradient_sptr(density_sptr->get_empty_copy()); + shared_ptr> gradient_sptr(density_sptr->get_empty_copy()); /////// setup objective function object objective_function_sptr->set_up(density_sptr); @@ -132,25 +121,24 @@ MyStuff::run() /////// Return the objective function values and improvement std::cout << "The initial Objective Function Value = " << my_objective_function_value1 << "\n"; - std::cout << "The Objective Function Value of after " << num_iterations << " iteration(s) =" - << my_objective_function_value2 << "\n"; + std::cout << "The Objective Function Value of after " << num_iterations << " iteration(s) =" << my_objective_function_value2 + << "\n"; std::cout << "A change of " << my_objective_function_value2 - my_objective_function_value1 << "\n"; } -}// end of namespace stir +} // end of namespace stir -int main(int argc, char **argv) -{ +int +main(int argc, char** argv) { using namespace stir; - if (argc!=2) - { - std::cerr << "Normal usage: " << argv[0] << " parameter-file\n"; - std::cerr << "I will now ask you the questions interactively\n"; - } + if (argc != 2) { + std::cerr << "Normal usage: " << argv[0] << " parameter-file\n"; + std::cerr << "I will now ask you the questions interactively\n"; + } MyStuff my_stuff; my_stuff.set_defaults(); - if (argc!=2) + if (argc != 2) my_stuff.ask_parameters(); else my_stuff.parse(argv[1]); diff --git a/src/IO/ECAT6OutputFileFormat.cxx b/src/IO/ECAT6OutputFileFormat.cxx index d067e99013..2c968f257c 100644 --- a/src/IO/ECAT6OutputFileFormat.cxx +++ b/src/IO/ECAT6OutputFileFormat.cxx @@ -37,126 +37,89 @@ START_NAMESPACE_STIR START_NAMESPACE_ECAT START_NAMESPACE_ECAT6 +const char* const ECAT6OutputFileFormat::registered_name = "ECAT6"; -const char * const -ECAT6OutputFileFormat::registered_name = "ECAT6"; - -ECAT6OutputFileFormat:: -ECAT6OutputFileFormat(const NumericType& type, - const ByteOrder& byte_order) -{ +ECAT6OutputFileFormat::ECAT6OutputFileFormat(const NumericType& type, const ByteOrder& byte_order) { base_type::set_defaults(); set_type_of_numbers(type); set_byte_order(byte_order); } -void -ECAT6OutputFileFormat:: -initialise_keymap() -{ +void +ECAT6OutputFileFormat::initialise_keymap() { parser.add_start_key("ECAT6 Output File Format Parameters"); parser.add_stop_key("End ECAT6 Output File Format Parameters"); parser.add_key("default scanner name", &default_scanner_name); base_type::initialise_keymap(); } -void -ECAT6OutputFileFormat:: -set_defaults() -{ +void +ECAT6OutputFileFormat::set_defaults() { default_scanner_name = "ECAT 953"; base_type::set_defaults(); file_byte_order = ByteOrder::little_endian; type_of_numbers = NumericType::SHORT; - } bool -ECAT6OutputFileFormat:: -post_processing() -{ +ECAT6OutputFileFormat::post_processing() { if (base_type::post_processing()) return true; - shared_ptr scanner_ptr ( - Scanner::get_scanner_from_name(default_scanner_name)); + shared_ptr scanner_ptr(Scanner::get_scanner_from_name(default_scanner_name)); - if (find_ECAT_system_type(*scanner_ptr)==0) - { - warning("ECAT6OutputFileFormat: default_scanner_name %s is not supported\n", - default_scanner_name.c_str()); - return true; - } + if (find_ECAT_system_type(*scanner_ptr) == 0) { + warning("ECAT6OutputFileFormat: default_scanner_name %s is not supported\n", default_scanner_name.c_str()); + return true; + } return false; } -NumericType -ECAT6OutputFileFormat:: -set_type_of_numbers(const NumericType& new_type, const bool warn) -{ - const NumericType supported_type_of_numbers = - NumericType("signed integer", 2); - if (new_type != supported_type_of_numbers) - { +NumericType +ECAT6OutputFileFormat::set_type_of_numbers(const NumericType& new_type, const bool warn) { + const NumericType supported_type_of_numbers = NumericType("signed integer", 2); + if (new_type != supported_type_of_numbers) { if (warn) warning("ECAT6OutputFileFormat: output type of numbers is currently fixed to short (2 byte signed integers)\n"); type_of_numbers = supported_type_of_numbers; - } - else + } else type_of_numbers = new_type; return type_of_numbers; - } -ByteOrder -ECAT6OutputFileFormat:: -set_byte_order(const ByteOrder& new_byte_order, const bool warn) -{ - if (new_byte_order != ByteOrder::little_endian) - { +ByteOrder +ECAT6OutputFileFormat::set_byte_order(const ByteOrder& new_byte_order, const bool warn) { + if (new_byte_order != ByteOrder::little_endian) { if (warn) warning("ECAT6OutputFileFormat: byte_order is currently fixed to little-endian\n"); file_byte_order = ByteOrder::little_endian; - } - else + } else file_byte_order = new_byte_order; return file_byte_order; } -Succeeded -ECAT6OutputFileFormat:: -actual_write_to_file(std::string& filename, - const DiscretisedDensity<3,float>& density) const -{ - shared_ptr scanner_ptr( - Scanner::get_scanner_from_name(default_scanner_name)); - +Succeeded +ECAT6OutputFileFormat::actual_write_to_file(std::string& filename, const DiscretisedDensity<3, float>& density) const { + shared_ptr scanner_ptr(Scanner::get_scanner_from_name(default_scanner_name)); + add_extension(filename, ".img"); ECAT6_Main_header mhead; make_ECAT6_Main_header(mhead, *scanner_ptr, "", density); mhead.num_frames = 1; - - FILE *fptr= cti_create (filename.c_str(), &mhead); - if (fptr == NULL) - { - warning("ECAT6OutputFileFormat::write_to_file: error opening output file %s\n", - filename.c_str()); + + FILE* fptr = cti_create(filename.c_str(), &mhead); + if (fptr == NULL) { + warning("ECAT6OutputFileFormat::write_to_file: error opening output file %s\n", filename.c_str()); return Succeeded::no; } - Succeeded success = - DiscretisedDensity_to_ECAT6(fptr, - density, - mhead, - 1 /*frame_num*/); + Succeeded success = DiscretisedDensity_to_ECAT6(fptr, density, mhead, 1 /*frame_num*/); fclose(fptr); - + return success; } END_NAMESPACE_ECAT6 END_NAMESPACE_ECAT END_NAMESPACE_STIR - - diff --git a/src/IO/ECAT7DynamicDiscretisedDensityInputFileFormat.cxx b/src/IO/ECAT7DynamicDiscretisedDensityInputFileFormat.cxx index 8f8449b5a0..b2f8ff1cf4 100644 --- a/src/IO/ECAT7DynamicDiscretisedDensityInputFileFormat.cxx +++ b/src/IO/ECAT7DynamicDiscretisedDensityInputFileFormat.cxx @@ -34,9 +34,8 @@ #include #include - #ifndef HAVE_LLN_MATRIX -#error HAVE_LLN_MATRIX not defined: you need the lln ecat library. +# error HAVE_LLN_MATRIX not defined: you need the lln ecat library. #endif #include "stir/IO/stir_ecat7.h" @@ -49,11 +48,8 @@ START_NAMESPACE_ECAT7 \preliminary */ -bool -ECAT7DynamicDiscretisedDensityInputFileFormat:: -actual_can_read(const FileSignature& signature, - std::istream& input) const -{ +bool +ECAT7DynamicDiscretisedDensityInputFileFormat::actual_can_read(const FileSignature& signature, std::istream& input) const { if (strncmp(signature.get_signature(), "MATRIX", 6) != 0) return false; @@ -63,70 +59,53 @@ actual_can_read(const FileSignature& signature, } unique_ptr -ECAT7DynamicDiscretisedDensityInputFileFormat:: -read_from_file(std::istream& input) const -{ - //TODO - error("read_from_file for ECAT7 with istream not implemented %s:%s. Sorry", - __FILE__, __LINE__); - return - unique_ptr(); +ECAT7DynamicDiscretisedDensityInputFileFormat::read_from_file(std::istream& input) const { + // TODO + error("read_from_file for ECAT7 with istream not implemented %s:%s. Sorry", __FILE__, __LINE__); + return unique_ptr(); } unique_ptr -ECAT7DynamicDiscretisedDensityInputFileFormat:: -read_from_file(const std::string& filename) const -{ - if (is_ECAT7_image_file(filename)) - { - Main_header mhead; - if (read_ECAT7_main_header(mhead, filename) == Succeeded::no) - { - error("ECAT7DynamicDiscretisedDensityInputFileFormat::read_from_file cannot read %s as ECAT7 (failed to read main header)", filename.c_str()); - return unique_ptr(); - } - - TimeFrameDefinitions time_frame_definitions(filename); - shared_ptr scanner_sptr(find_scanner_from_ECAT_system_type(mhead.system_type)); - - unique_ptr - dynamic_image_ptr - (new DynamicDiscretisedDensity(time_frame_definitions, - static_cast(mhead.scan_start_time), - scanner_sptr) - ); - - dynamic_image_ptr->set_calibration_factor(mhead.calibration_factor); - - dynamic_image_ptr->set_isotope_halflife(mhead.isotope_halflife); - - // TODO get this from the subheader fields or so - // dynamic_image_ptr->_is_decay_corrected = - // shead.processing_code & DecayPrc - dynamic_image_ptr->set_if_decay_corrected(false); - - for (unsigned int frame_num=1; frame_num <= dynamic_image_ptr->get_num_time_frames(); ++ frame_num) - { - shared_ptr dens_sptr - (ECAT7_to_VoxelsOnCartesianGrid(filename, - frame_num, - /* gate_num, data_num, bed_num */ 1,0,0) - ); - if (is_null_ptr(dens_sptr)) - error("read_from_file for DynamicDiscretisedDensity: No frame %d available", frame_num); - dynamic_image_ptr->set_density(*dens_sptr, frame_num ); - } - return dynamic_image_ptr; - } - else - { - error("read_from_file for DynamicDiscretisedDensity: ECAT7 file %s is not an image file", filename.c_str()); - // return something to satisfy compilers +ECAT7DynamicDiscretisedDensityInputFileFormat::read_from_file(const std::string& filename) const { + if (is_ECAT7_image_file(filename)) { + Main_header mhead; + if (read_ECAT7_main_header(mhead, filename) == Succeeded::no) { + error("ECAT7DynamicDiscretisedDensityInputFileFormat::read_from_file cannot read %s as ECAT7 (failed to read main header)", + filename.c_str()); return unique_ptr(); } + + TimeFrameDefinitions time_frame_definitions(filename); + shared_ptr scanner_sptr(find_scanner_from_ECAT_system_type(mhead.system_type)); + + unique_ptr dynamic_image_ptr( + new DynamicDiscretisedDensity(time_frame_definitions, static_cast(mhead.scan_start_time), scanner_sptr)); + + dynamic_image_ptr->set_calibration_factor(mhead.calibration_factor); + + dynamic_image_ptr->set_isotope_halflife(mhead.isotope_halflife); + + // TODO get this from the subheader fields or so + // dynamic_image_ptr->_is_decay_corrected = + // shead.processing_code & DecayPrc + dynamic_image_ptr->set_if_decay_corrected(false); + + for (unsigned int frame_num = 1; frame_num <= dynamic_image_ptr->get_num_time_frames(); ++frame_num) { + shared_ptr dens_sptr( + ECAT7_to_VoxelsOnCartesianGrid(filename, frame_num, + /* gate_num, data_num, bed_num */ 1, 0, 0)); + if (is_null_ptr(dens_sptr)) + error("read_from_file for DynamicDiscretisedDensity: No frame %d available", frame_num); + dynamic_image_ptr->set_density(*dens_sptr, frame_num); + } + return dynamic_image_ptr; + } else { + error("read_from_file for DynamicDiscretisedDensity: ECAT7 file %s is not an image file", filename.c_str()); + // return something to satisfy compilers + return unique_ptr(); + } } END_NAMESPACE_ECAT END_NAMESPACE_ECAT7 END_NAMESPACE_STIR - diff --git a/src/IO/ECAT7DynamicDiscretisedDensityOutputFileFormat.cxx b/src/IO/ECAT7DynamicDiscretisedDensityOutputFileFormat.cxx index 674c8f5289..1d29a6c02b 100644 --- a/src/IO/ECAT7DynamicDiscretisedDensityOutputFileFormat.cxx +++ b/src/IO/ECAT7DynamicDiscretisedDensityOutputFileFormat.cxx @@ -36,32 +36,25 @@ START_NAMESPACE_STIR START_NAMESPACE_ECAT START_NAMESPACE_ECAT7 -const char * const -ECAT7DynamicDiscretisedDensityOutputFileFormat::registered_name = "ECAT7"; +const char* const ECAT7DynamicDiscretisedDensityOutputFileFormat::registered_name = "ECAT7"; -ECAT7DynamicDiscretisedDensityOutputFileFormat:: -ECAT7DynamicDiscretisedDensityOutputFileFormat(const NumericType& type, - const ByteOrder& byte_order) -{ +ECAT7DynamicDiscretisedDensityOutputFileFormat::ECAT7DynamicDiscretisedDensityOutputFileFormat(const NumericType& type, + const ByteOrder& byte_order) { this->set_defaults(); this->set_type_of_numbers(type); this->set_byte_order(byte_order); } -void -ECAT7DynamicDiscretisedDensityOutputFileFormat:: -initialise_keymap() -{ +void +ECAT7DynamicDiscretisedDensityOutputFileFormat::initialise_keymap() { this->parser.add_start_key("ECAT7 Output File Format Parameters"); this->parser.add_stop_key("End ECAT7 Output File Format Parameters"); this->parser.add_key("default scanner name", &default_scanner_name); base_type::initialise_keymap(); } -void -ECAT7DynamicDiscretisedDensityOutputFileFormat:: -set_defaults() -{ +void +ECAT7DynamicDiscretisedDensityOutputFileFormat::set_defaults() { this->default_scanner_name = "ECAT 962"; base_type::set_defaults(); this->file_byte_order = ByteOrder::big_endian; @@ -71,69 +64,53 @@ set_defaults() } bool -ECAT7DynamicDiscretisedDensityOutputFileFormat:: -post_processing() -{ +ECAT7DynamicDiscretisedDensityOutputFileFormat::post_processing() { if (base_type::post_processing()) return true; - shared_ptr scanner_ptr( - Scanner::get_scanner_from_name(this->default_scanner_name)); + shared_ptr scanner_ptr(Scanner::get_scanner_from_name(this->default_scanner_name)); - if (find_ECAT_system_type(*scanner_ptr)==0) - { - warning("ECAT7DynamicDiscretisedDensityOutputFileFormat: default_scanner_name %s is not supported\n", - this->default_scanner_name.c_str()); - return true; - } + if (find_ECAT_system_type(*scanner_ptr) == 0) { + warning("ECAT7DynamicDiscretisedDensityOutputFileFormat: default_scanner_name %s is not supported\n", + this->default_scanner_name.c_str()); + return true; + } return false; } -NumericType -ECAT7DynamicDiscretisedDensityOutputFileFormat:: -set_type_of_numbers(const NumericType& new_type, const bool warn) -{ - const NumericType supported_type_of_numbers = - NumericType("signed integer", 2); - if (new_type != supported_type_of_numbers) - { +NumericType +ECAT7DynamicDiscretisedDensityOutputFileFormat::set_type_of_numbers(const NumericType& new_type, const bool warn) { + const NumericType supported_type_of_numbers = NumericType("signed integer", 2); + if (new_type != supported_type_of_numbers) { if (warn) - warning("ECAT7DynamicDiscretisedDensityOutputFileFormat: output type of numbers is currently fixed to short (2 byte signed integers)\n"); + warning("ECAT7DynamicDiscretisedDensityOutputFileFormat: output type of numbers is currently fixed to short (2 byte signed " + "integers)\n"); this->type_of_numbers = supported_type_of_numbers; - } - else + } else this->type_of_numbers = new_type; return this->type_of_numbers; } -ByteOrder -ECAT7DynamicDiscretisedDensityOutputFileFormat:: -set_byte_order(const ByteOrder& new_byte_order, const bool warn) -{ - if (new_byte_order != ByteOrder::big_endian) - { +ByteOrder +ECAT7DynamicDiscretisedDensityOutputFileFormat::set_byte_order(const ByteOrder& new_byte_order, const bool warn) { + if (new_byte_order != ByteOrder::big_endian) { if (warn) warning("ECAT7DynamicDiscretisedDensityOutputFileFormat: byte_order is currently fixed to big-endian\n"); this->file_byte_order = ByteOrder::big_endian; - } - else + } else this->file_byte_order = new_byte_order; return this->file_byte_order; } -Succeeded -ECAT7DynamicDiscretisedDensityOutputFileFormat:: -actual_write_to_file(std::string& filename, - const DynamicDiscretisedDensity& dynamic_density) const -{ +Succeeded +ECAT7DynamicDiscretisedDensityOutputFileFormat::actual_write_to_file(std::string& filename, + const DynamicDiscretisedDensity& dynamic_density) const { add_extension(filename, ".img"); - return - dynamic_density.write_to_ecat7(filename); + return dynamic_density.write_to_ecat7(filename); } - -//template class ECAT7DynamicDiscretisedDensityOutputFileFormat; +// template class ECAT7DynamicDiscretisedDensityOutputFileFormat; END_NAMESPACE_ECAT7 END_NAMESPACE_ECAT diff --git a/src/IO/ECAT7OutputFileFormat.cxx b/src/IO/ECAT7OutputFileFormat.cxx index f90a8a01df..65a1f0195e 100644 --- a/src/IO/ECAT7OutputFileFormat.cxx +++ b/src/IO/ECAT7OutputFileFormat.cxx @@ -35,120 +35,90 @@ START_NAMESPACE_STIR START_NAMESPACE_ECAT START_NAMESPACE_ECAT7 -const char * const -ECAT7OutputFileFormat::registered_name = "ECAT7"; +const char* const ECAT7OutputFileFormat::registered_name = "ECAT7"; -ECAT7OutputFileFormat:: -ECAT7OutputFileFormat(const NumericType& type, - const ByteOrder& byte_order) -{ +ECAT7OutputFileFormat::ECAT7OutputFileFormat(const NumericType& type, const ByteOrder& byte_order) { base_type::set_defaults(); set_type_of_numbers(type); set_byte_order(byte_order); } -void -ECAT7OutputFileFormat:: -initialise_keymap() -{ +void +ECAT7OutputFileFormat::initialise_keymap() { parser.add_start_key("ECAT7 Output File Format Parameters"); parser.add_stop_key("End ECAT7 Output File Format Parameters"); parser.add_key("default scanner name", &default_scanner_name); base_type::initialise_keymap(); } -void -ECAT7OutputFileFormat:: -set_defaults() -{ +void +ECAT7OutputFileFormat::set_defaults() { default_scanner_name = "ECAT 962"; base_type::set_defaults(); file_byte_order = ByteOrder::big_endian; type_of_numbers = NumericType::SHORT; set_key_values(); - } bool -ECAT7OutputFileFormat:: -post_processing() -{ +ECAT7OutputFileFormat::post_processing() { if (base_type::post_processing()) return true; shared_ptr scanner_ptr(Scanner::get_scanner_from_name(default_scanner_name)); - if (find_ECAT_system_type(*scanner_ptr)==0) - { - warning("ECAT7OutputFileFormat: default_scanner_name %s is not supported\n", - default_scanner_name.c_str()); - return true; - } + if (find_ECAT_system_type(*scanner_ptr) == 0) { + warning("ECAT7OutputFileFormat: default_scanner_name %s is not supported\n", default_scanner_name.c_str()); + return true; + } return false; } -NumericType -ECAT7OutputFileFormat:: -set_type_of_numbers(const NumericType& new_type, const bool warn) -{ - const NumericType supported_type_of_numbers = - NumericType("signed integer", 2); - if (new_type != supported_type_of_numbers) - { +NumericType +ECAT7OutputFileFormat::set_type_of_numbers(const NumericType& new_type, const bool warn) { + const NumericType supported_type_of_numbers = NumericType("signed integer", 2); + if (new_type != supported_type_of_numbers) { if (warn) warning("ECAT7OutputFileFormat: output type of numbers is currently fixed to short (2 byte signed integers)\n"); type_of_numbers = supported_type_of_numbers; - } - else + } else type_of_numbers = new_type; return type_of_numbers; - } -ByteOrder -ECAT7OutputFileFormat:: -set_byte_order(const ByteOrder& new_byte_order, const bool warn) -{ - if (new_byte_order != ByteOrder::big_endian) - { +ByteOrder +ECAT7OutputFileFormat::set_byte_order(const ByteOrder& new_byte_order, const bool warn) { + if (new_byte_order != ByteOrder::big_endian) { if (warn) warning("ECAT7OutputFileFormat: byte_order is currently fixed to big-endian\n"); file_byte_order = ByteOrder::big_endian; - } - else + } else file_byte_order = new_byte_order; return file_byte_order; } -Succeeded -ECAT7OutputFileFormat:: -actual_write_to_file(std::string& filename, - const DiscretisedDensity<3,float>& density) const -{ +Succeeded +ECAT7OutputFileFormat::actual_write_to_file(std::string& filename, const DiscretisedDensity<3, float>& density) const { shared_ptr scanner_ptr(Scanner::get_scanner_from_name(default_scanner_name)); - + add_extension(filename, ".img"); Main_header mhead; make_ECAT7_main_header(mhead, *scanner_ptr, "", density); mhead.num_frames = 1; - mhead.acquisition_type = - mhead.num_frames>1 ? DynamicEmission : StaticEmission; - - MatrixFile* mptr= matrix_create (filename.c_str(), MAT_CREATE, &mhead); - if (mptr == 0) - { - warning("ECAT7OutputFileFormat::write_to_file: error opening output file %s\n", - filename.c_str()); + mhead.acquisition_type = mhead.num_frames > 1 ? DynamicEmission : StaticEmission; + + MatrixFile* mptr = matrix_create(filename.c_str(), MAT_CREATE, &mhead); + if (mptr == 0) { + warning("ECAT7OutputFileFormat::write_to_file: error opening output file %s\n", filename.c_str()); return Succeeded::no; } - - Succeeded success = - DiscretisedDensity_to_ECAT7(mptr, density, 1 /*frame_num*/); + + Succeeded success = DiscretisedDensity_to_ECAT7(mptr, density, 1 /*frame_num*/); matrix_close(mptr); - + return success; } diff --git a/src/IO/ECAT7ParametricDensityOutputFileFormat.cxx b/src/IO/ECAT7ParametricDensityOutputFileFormat.cxx index 0778b22ddb..788048a419 100644 --- a/src/IO/ECAT7ParametricDensityOutputFileFormat.cxx +++ b/src/IO/ECAT7ParametricDensityOutputFileFormat.cxx @@ -36,24 +36,19 @@ START_NAMESPACE_ECAT START_NAMESPACE_ECAT7 template -const char * const -ECAT7ParametricDensityOutputFileFormat::registered_name = "ECAT7"; +const char* const ECAT7ParametricDensityOutputFileFormat::registered_name = "ECAT7"; template -ECAT7ParametricDensityOutputFileFormat:: -ECAT7ParametricDensityOutputFileFormat(const NumericType& type, - const ByteOrder& byte_order) -{ +ECAT7ParametricDensityOutputFileFormat::ECAT7ParametricDensityOutputFileFormat(const NumericType& type, + const ByteOrder& byte_order) { this->set_defaults(); this->set_type_of_numbers(type); this->set_byte_order(byte_order); } template -void -ECAT7ParametricDensityOutputFileFormat:: -initialise_keymap() -{ +void +ECAT7ParametricDensityOutputFileFormat::initialise_keymap() { this->parser.add_start_key("ECAT7 Output File Format Parameters"); this->parser.add_stop_key("End ECAT7 Output File Format Parameters"); this->parser.add_key("default scanner name", &default_scanner_name); @@ -61,89 +56,68 @@ initialise_keymap() } template -void -ECAT7ParametricDensityOutputFileFormat:: -set_defaults() -{ +void +ECAT7ParametricDensityOutputFileFormat::set_defaults() { this->default_scanner_name = "ECAT 962"; base_type::set_defaults(); this->file_byte_order = ByteOrder::big_endian; this->type_of_numbers = NumericType::SHORT; this->set_key_values(); - } template bool -ECAT7ParametricDensityOutputFileFormat:: -post_processing() -{ +ECAT7ParametricDensityOutputFileFormat::post_processing() { if (base_type::post_processing()) return true; - shared_ptr scanner_ptr( - Scanner::get_scanner_from_name(this->default_scanner_name)); + shared_ptr scanner_ptr(Scanner::get_scanner_from_name(this->default_scanner_name)); - if (find_ECAT_system_type(*scanner_ptr)==0) - { - warning("ECAT7ParametricDensityOutputFileFormat: default_scanner_name %s is not supported\n", - this->default_scanner_name.c_str()); - return true; - } + if (find_ECAT_system_type(*scanner_ptr) == 0) { + warning("ECAT7ParametricDensityOutputFileFormat: default_scanner_name %s is not supported\n", + this->default_scanner_name.c_str()); + return true; + } return false; } template -NumericType -ECAT7ParametricDensityOutputFileFormat:: -set_type_of_numbers(const NumericType& new_type, const bool warn) -{ - const NumericType supported_type_of_numbers = - NumericType("signed integer", 2); - if (new_type != supported_type_of_numbers) - { +NumericType +ECAT7ParametricDensityOutputFileFormat::set_type_of_numbers(const NumericType& new_type, const bool warn) { + const NumericType supported_type_of_numbers = NumericType("signed integer", 2); + if (new_type != supported_type_of_numbers) { if (warn) - warning("ECAT7ParametricDensityOutputFileFormat: output type of numbers is currently fixed to short (2 byte signed integers)\n"); + warning("ECAT7ParametricDensityOutputFileFormat: output type of numbers is currently fixed to short (2 byte signed " + "integers)\n"); this->type_of_numbers = supported_type_of_numbers; - } - else + } else this->type_of_numbers = new_type; return this->type_of_numbers; - } template -ByteOrder -ECAT7ParametricDensityOutputFileFormat:: -set_byte_order(const ByteOrder& new_byte_order, const bool warn) -{ - if (new_byte_order != ByteOrder::big_endian) - { +ByteOrder +ECAT7ParametricDensityOutputFileFormat::set_byte_order(const ByteOrder& new_byte_order, const bool warn) { + if (new_byte_order != ByteOrder::big_endian) { if (warn) warning("ECAT7ParametricDensityOutputFileFormat: byte_order is currently fixed to big-endian\n"); this->file_byte_order = ByteOrder::big_endian; - } - else + } else this->file_byte_order = new_byte_order; return this->file_byte_order; } template -Succeeded -ECAT7ParametricDensityOutputFileFormat:: -actual_write_to_file(std::string& filename, - const ParametricDiscretisedDensity& parametric_density) const -{ - shared_ptr scanner_ptr( - Scanner::get_scanner_from_name(this->default_scanner_name)); - +Succeeded +ECAT7ParametricDensityOutputFileFormat::actual_write_to_file( + std::string& filename, const ParametricDiscretisedDensity& parametric_density) const { + shared_ptr scanner_ptr(Scanner::get_scanner_from_name(this->default_scanner_name)); + add_extension(filename, ".img"); - typedef - typename ParametricDiscretisedDensity::SingleDiscretisedDensityType - SingleDensityType; + typedef typename ParametricDiscretisedDensity::SingleDiscretisedDensityType SingleDensityType; // somewhat naughty trick to get elemT of DiscretisedDensityT typedef typename DiscretisedDensityT::full_value_type KinParsT; @@ -154,34 +128,28 @@ actual_write_to_file(std::string& filename, mhead.num_frames = KinParsT::size(); mhead.acquisition_type = DynamicEmission; - - MatrixFile* mptr= matrix_create (filename.c_str(), MAT_CREATE, &mhead); - if (mptr == 0) - { - warning("ECAT7ParametricDensityOutputFileFormat::write_to_file: error opening output file %s\n", - filename.c_str()); + + MatrixFile* mptr = matrix_create(filename.c_str(), MAT_CREATE, &mhead); + if (mptr == 0) { + warning("ECAT7ParametricDensityOutputFileFormat::write_to_file: error opening output file %s\n", filename.c_str()); return Succeeded::no; } - - for (unsigned int f=1; f<=KinParsT::size(); ++f) - { - parametric_density.construct_single_density(single_density,f); - Succeeded success = - DiscretisedDensity_to_ECAT7(mptr, single_density, f); - if (success == Succeeded::no) - { - matrix_close(mptr); - return success; - } + + for (unsigned int f = 1; f <= KinParsT::size(); ++f) { + parametric_density.construct_single_density(single_density, f); + Succeeded success = DiscretisedDensity_to_ECAT7(mptr, single_density, f); + if (success == Succeeded::no) { + matrix_close(mptr); + return success; } + } matrix_close(mptr); - + return Succeeded::yes; } -template class ECAT7ParametricDensityOutputFileFormat; +template class ECAT7ParametricDensityOutputFileFormat; END_NAMESPACE_ECAT7 END_NAMESPACE_ECAT END_NAMESPACE_STIR - diff --git a/src/IO/GEHDF5Wrapper.cxx b/src/IO/GEHDF5Wrapper.cxx index 2c9d2608ef..b875b39fba 100644 --- a/src/IO/GEHDF5Wrapper.cxx +++ b/src/IO/GEHDF5Wrapper.cxx @@ -37,66 +37,61 @@ #include "stir/info.h" #include - START_NAMESPACE_STIR namespace GE { namespace RDF_HDF5 { std::uint32_t -GEHDF5Wrapper::read_dataset_uint32(const std::string& dataset_name) -{ - std::uint32_t tmp=0U; +GEHDF5Wrapper::read_dataset_uint32(const std::string& dataset_name) { + std::uint32_t tmp = 0U; H5::DataSet dataset = file.openDataSet(dataset_name); dataset.read(&tmp, H5::PredType::NATIVE_UINT32); return tmp; } std::int32_t -GEHDF5Wrapper::read_dataset_int32(const std::string& dataset_name) -{ - std::int32_t tmp=0; +GEHDF5Wrapper::read_dataset_int32(const std::string& dataset_name) { + std::int32_t tmp = 0; H5::DataSet dataset = file.openDataSet(dataset_name); dataset.read(&tmp, H5::PredType::NATIVE_INT32); return tmp; } -bool GEHDF5Wrapper::check_GE_signature(const std::string& filename) -{ - try - { - if(!H5::H5File::isHdf5(filename)) - return false; +bool +GEHDF5Wrapper::check_GE_signature(const std::string& filename) { + try { + if (!H5::H5File::isHdf5(filename)) + return false; - H5::H5File file; - file.openFile( filename, H5F_ACC_RDONLY ); + H5::H5File file; + file.openFile(filename, H5F_ACC_RDONLY); - return (check_GE_signature(file)); - } - catch(...) - { - return false; - } + return (check_GE_signature(file)); + } catch (...) { + return false; + } } -bool GEHDF5Wrapper::check_GE_signature(H5::H5File& file) -{ - if(file.getId() == -1) - error("File is not open. Aborting"); - - H5::StrType vlst(0,37); // 37 here is the length of the string (PW got it from the text file generated by list2txt with the LIST000_decomp.BLF - std::string read_str_manufacturer; - - H5::DataSet dataset2= file.openDataSet("/HeaderData/ExamData/manufacturer"); - dataset2.read(read_str_manufacturer,vlst); - - if(read_str_manufacturer == "GE MEDICAL SYSTEMS") - { - return true; - } - return false; +bool +GEHDF5Wrapper::check_GE_signature(H5::H5File& file) { + if (file.getId() == -1) + error("File is not open. Aborting"); + + H5::StrType vlst( + 0, + 37); // 37 here is the length of the string (PW got it from the text file generated by list2txt with the LIST000_decomp.BLF + std::string read_str_manufacturer; + + H5::DataSet dataset2 = file.openDataSet("/HeaderData/ExamData/manufacturer"); + dataset2.read(read_str_manufacturer, vlst); + + if (read_str_manufacturer == "GE MEDICAL SYSTEMS") { + return true; + } + return false; } -// // Checks if input file is listfile. +// // Checks if input file is listfile. // AB: todo do we want this func? helps test from filename /* bool @@ -118,772 +113,750 @@ GEHDF5Wrapper::is_list_file(const std::string& filename) } */ - // AB todo: only valid for RDF9 (until they tell us otherwise) bool -GEHDF5Wrapper::is_list_file() const -{ - // have we already checked this? - if(is_list) - return true; - - if(file.getId() == -1) - error("File is not open. Aborting"); - - // All RDF files shoudl have this DataSet - H5::DataSet dataset = file.openDataSet("/HeaderData/RDFConfiguration/isListFile"); - std::uint32_t is_list_file; - dataset.read(&is_list_file, H5::PredType::NATIVE_UINT32); - return is_list_file; - +GEHDF5Wrapper::is_list_file() const { + // have we already checked this? + if (is_list) + return true; + + if (file.getId() == -1) + error("File is not open. Aborting"); + + // All RDF files shoudl have this DataSet + H5::DataSet dataset = file.openDataSet("/HeaderData/RDFConfiguration/isListFile"); + std::uint32_t is_list_file; + dataset.read(&is_list_file, H5::PredType::NATIVE_UINT32); + return is_list_file; } -// Checks if file is a sino file. +// Checks if file is a sino file. // AB todo: only valid for RDF9 (until they tell us otherwise) bool -GEHDF5Wrapper::is_sino_file() const -{ - if(is_sino) - return true; - if(file.getId() == -1) - error("File is not open. Aborting"); - - // If this Group exists, its a sino file. - // huh, no C++ way to do this without try catch. https://stackoverflow.com/questions/35668056/test-group-existence-in-hdf5-c - return H5Lexists( file.getId(), "/SegmentData/Segment2", H5P_DEFAULT ) > 0; +GEHDF5Wrapper::is_sino_file() const { + if (is_sino) + return true; + if (file.getId() == -1) + error("File is not open. Aborting"); + + // If this Group exists, its a sino file. + // huh, no C++ way to do this without try catch. https://stackoverflow.com/questions/35668056/test-group-existence-in-hdf5-c + return H5Lexists(file.getId(), "/SegmentData/Segment2", H5P_DEFAULT) > 0; } bool -GEHDF5Wrapper::is_geo_file() const -{ - // Apparently the norm file has the geo file inside. This means that the geo file is superfluous. - // For now, lets just make this fucntion is_geo_or_norm_file(). Perhaps later versions will no be like this and this we need to change this function. - - if(is_geo || is_norm) - return true; - if(file.getId() == -1) - error("File is not open. Aborting"); - return H5Lexists( file.getId(), "/SegmentData/Segment4/3D_Norm_Correction/slice1", H5P_DEFAULT ) > 0; - // AB if you want to make sure geo is definetly geo, and not geo or norm, add to the avobe line: && !H5Lexists( file.getId(), "/3DCrystalEfficiency/crystalEfficiency", H5P_DEFAULT ); +GEHDF5Wrapper::is_geo_file() const { + // Apparently the norm file has the geo file inside. This means that the geo file is superfluous. + // For now, lets just make this fucntion is_geo_or_norm_file(). Perhaps later versions will no be like this and this we need to + // change this function. + + if (is_geo || is_norm) + return true; + if (file.getId() == -1) + error("File is not open. Aborting"); + return H5Lexists(file.getId(), "/SegmentData/Segment4/3D_Norm_Correction/slice1", H5P_DEFAULT) > 0; + // AB if you want to make sure geo is definetly geo, and not geo or norm, add to the avobe line: && !H5Lexists( file.getId(), + // "/3DCrystalEfficiency/crystalEfficiency", H5P_DEFAULT ); } bool -GEHDF5Wrapper::is_norm_file() const -{ - if(is_norm) - return true; - if(file.getId() == -1) - error("File is not open. Aborting"); +GEHDF5Wrapper::is_norm_file() const { + if (is_norm) + return true; + if (file.getId() == -1) + error("File is not open. Aborting"); - return H5Lexists( file.getId(), "/3DCrystalEfficiency/crystalEfficiency", H5P_DEFAULT ) > 0; + return H5Lexists(file.getId(), "/3DCrystalEfficiency/crystalEfficiency", H5P_DEFAULT) > 0; } -GEHDF5Wrapper::GEHDF5Wrapper() -{ - // Not much. +GEHDF5Wrapper::GEHDF5Wrapper() { + // Not much. } -GEHDF5Wrapper::GEHDF5Wrapper(const std::string& filename) -{ - if(open(filename) == Succeeded::no) - error("GEHDF5Wrapper: Error opening HDF5 file. Abort."); +GEHDF5Wrapper::GEHDF5Wrapper(const std::string& filename) { + if (open(filename) == Succeeded::no) + error("GEHDF5Wrapper: Error opening HDF5 file. Abort."); } unsigned int -GEHDF5Wrapper::check_geo_type() -{ - if(!is_geo ) - error("Not a geo file. Aborting"); - if(file.getId() == -1) - error("File is not open. Aborting"); - - unsigned int geo_type=0; - H5::DataSet str_geo_size = file.openDataSet("/HeaderData/Sorter/Segment4/dimension3Size"); - str_geo_size.read(&geo_type, H5::PredType::NATIVE_UINT32); - if (geo_type>1) - geo_type=3; - else - geo_type=2; - return geo_type; +GEHDF5Wrapper::check_geo_type() { + if (!is_geo) + error("Not a geo file. Aborting"); + if (file.getId() == -1) + error("File is not open. Aborting"); + + unsigned int geo_type = 0; + H5::DataSet str_geo_size = file.openDataSet("/HeaderData/Sorter/Segment4/dimension3Size"); + str_geo_size.read(&geo_type, H5::PredType::NATIVE_UINT32); + if (geo_type > 1) + geo_type = 3; + else + geo_type = 2; + return geo_type; } // Function that error checks the input file and sets flags for the correct formats. GEHDF5Wrapper.file must be already open. -// AB todo: this file is only valid for RDF 9. -Succeeded -GEHDF5Wrapper::check_file() -{ - if(file.getId()==-1) - error("File is not open. Aborting"); - if(!check_GE_signature(file)) - error("File is HDF5 but not GE data. Aborting"); - - //AB: We are openign a new file. The same class should not be used twice, but lets make sure that if it happens, we reseted the file identifiers. - is_list=false; is_norm=false;is_geo=false;is_sino=false; - - //AB Find out the RDF version of the file. - H5::DataSet str_file_version = file.openDataSet("/HeaderData/RDFConfiguration/fileVersion/majorVersion"); - str_file_version.read(&rdf_ver, H5::PredType::NATIVE_UINT32); - - //AB Lets just error for now. - if (rdf_ver!=9) - error("Only RDF version 9 supported. Aborting"); - - if(is_list_file()) - { - is_list = true; - // AB Now lets check all things that are required - if (rdf_ver == 9) //AB todo: is this valid for 10? - { - // Check 1: Is the file compressed? - unsigned int is_compressed; - H5::DataSet str_file_version = file.openDataSet("/HeaderData/ListHeader/isListCompressed"); - str_file_version.read(&is_compressed, H5::PredType::NATIVE_UINT32); - if (is_compressed) - error("The RDF9 Listmode file is compressed, we won't be able to read it. Please uncompress it and retry. Aborting"); - } - - return Succeeded::yes; - } - if(is_sino_file()) - { - is_sino = true; - if (rdf_ver == 9) //AB todo: is this valid for 10? - { - // Check 1: Is the file compressed? - unsigned int is_compressed; - H5::DataSet str_file_version = file.openDataSet("/HeaderData/Sorter/Segment2/compDataSegSize"); - str_file_version.read(&is_compressed, H5::PredType::NATIVE_UINT32); - if (is_compressed) - error("The RDF9 file sinogram is compressed, we won't be able to read it. Please uncompress it and retry. Aborting"); - } - return Succeeded::yes; - } - if(is_norm_file()) +// AB todo: this file is only valid for RDF 9. +Succeeded +GEHDF5Wrapper::check_file() { + if (file.getId() == -1) + error("File is not open. Aborting"); + if (!check_GE_signature(file)) + error("File is HDF5 but not GE data. Aborting"); + + // AB: We are openign a new file. The same class should not be used twice, but lets make sure that if it happens, we reseted the + // file identifiers. + is_list = false; + is_norm = false; + is_geo = false; + is_sino = false; + + // AB Find out the RDF version of the file. + H5::DataSet str_file_version = file.openDataSet("/HeaderData/RDFConfiguration/fileVersion/majorVersion"); + str_file_version.read(&rdf_ver, H5::PredType::NATIVE_UINT32); + + // AB Lets just error for now. + if (rdf_ver != 9) + error("Only RDF version 9 supported. Aborting"); + + if (is_list_file()) { + is_list = true; + // AB Now lets check all things that are required + if (rdf_ver == 9) // AB todo: is this valid for 10? { - is_norm=true; - is_geo =true; // in RFD9, if its norm, it is also geo (it contains it) - // Check the type of geo file it contains. - geo_dims = check_geo_type(); - - return Succeeded::yes; + // Check 1: Is the file compressed? + unsigned int is_compressed; + H5::DataSet str_file_version = file.openDataSet("/HeaderData/ListHeader/isListCompressed"); + str_file_version.read(&is_compressed, H5::PredType::NATIVE_UINT32); + if (is_compressed) + error("The RDF9 Listmode file is compressed, we won't be able to read it. Please uncompress it and retry. Aborting"); } - if(is_geo_file()) + + return Succeeded::yes; + } + if (is_sino_file()) { + is_sino = true; + if (rdf_ver == 9) // AB todo: is this valid for 10? { - is_geo=true; - geo_dims = check_geo_type(); - - return Succeeded::yes; + // Check 1: Is the file compressed? + unsigned int is_compressed; + H5::DataSet str_file_version = file.openDataSet("/HeaderData/Sorter/Segment2/compDataSegSize"); + str_file_version.read(&is_compressed, H5::PredType::NATIVE_UINT32); + if (is_compressed) + error("The RDF9 file sinogram is compressed, we won't be able to read it. Please uncompress it and retry. Aborting"); } - // should not get here. - return Succeeded::no; + return Succeeded::yes; + } + if (is_norm_file()) { + is_norm = true; + is_geo = true; // in RFD9, if its norm, it is also geo (it contains it) + // Check the type of geo file it contains. + geo_dims = check_geo_type(); + + return Succeeded::yes; + } + if (is_geo_file()) { + is_geo = true; + geo_dims = check_geo_type(); + + return Succeeded::yes; + } + // should not get here. + return Succeeded::no; } Succeeded -GEHDF5Wrapper::open(const std::string& filename) -{ - if(!file.isHdf5(filename)) - error("GEHDF5Wrapper: The input file is not HDF5! Abort."); +GEHDF5Wrapper::open(const std::string& filename) { + if (!file.isHdf5(filename)) + error("GEHDF5Wrapper: The input file is not HDF5! Abort."); - file.openFile(filename, H5F_ACC_RDONLY); + file.openFile(filename, H5F_ACC_RDONLY); - //AB: check if the input file is valid, not only as a HDF5, also a valid PET file. - check_file(); + // AB: check if the input file is valid, not only as a HDF5, also a valid PET file. + check_file(); - initialise_exam_info(); - initialise_proj_data_info_from_HDF5(); + initialise_exam_info(); + initialise_proj_data_info_from_HDF5(); - // functions above will throw actually, so if we get here, it worked. - return Succeeded::yes; + // functions above will throw actually, so if we get here, it worked. + return Succeeded::yes; } -shared_ptr GEHDF5Wrapper::get_scanner_from_HDF5() -{ - std::string read_str_scanner; - H5::StrType vlst(0,37); // 37 here is the length of the string (PW got it from the text file generated by list2txt with the LIST000_decomp.BLF - - H5::DataSet dataset = file.openDataSet("/HeaderData/ExamData/scannerDesc"); - dataset.read(read_str_scanner,vlst); - - float effective_ring_diameter; - int num_transaxial_blocks_per_bucket = 0; - int num_axial_blocks_per_bucket = 0; - int axial_blocks_per_unit = 0; - int radial_blocks_per_unit = 0; - int axial_units_per_module = 0; - int radial_units_per_module = 0; - int axial_modules_per_system = 0; - int radial_modules_per_system = 0; - int max_num_non_arccorrected_bins = 0; - int num_transaxial_crystals_per_block = 0; - int num_axial_crystals_per_block = 0 ; - float detector_axial_size = 0.0; - float intrinsic_tilt = 0.0; - int num_detector_layers = 1; - - H5::DataSet str_effective_ring_diameter = file.openDataSet("/HeaderData/SystemGeometry/effectiveRingDiameter"); - H5::DataSet str_axial_blocks_per_module = file.openDataSet("/HeaderData/SystemGeometry/axialBlocksPerModule"); - H5::DataSet str_radial_blocks_per_module = file.openDataSet("/HeaderData/SystemGeometry/radialBlocksPerModule"); - H5::DataSet str_axial_blocks_per_unit = file.openDataSet("/HeaderData/SystemGeometry/axialBlocksPerUnit"); - H5::DataSet str_radial_blocks_per_unit = file.openDataSet("/HeaderData/SystemGeometry/radialBlocksPerUnit"); - H5::DataSet str_axial_units_per_module = file.openDataSet("/HeaderData/SystemGeometry/axialUnitsPerModule"); - H5::DataSet str_radial_units_per_module = file.openDataSet("/HeaderData/SystemGeometry/radialUnitsPerModule"); - H5::DataSet str_axial_modules_per_system = file.openDataSet("/HeaderData/SystemGeometry/axialModulesPerSystem"); - H5::DataSet str_radial_modules_per_system = file.openDataSet("/HeaderData/SystemGeometry/radialModulesPerSystem"); - //! \todo P.W: Find the crystal gaps and other info missing. - H5::DataSet str_detector_axial_size = file.openDataSet("/HeaderData/SystemGeometry/detectorAxialSize"); - H5::DataSet str_intrinsic_tilt = file.openDataSet("/HeaderData/SystemGeometry/transaxial_crystal_0_offset"); - - H5::DataSet str_max_number_of_non_arc_corrected_bins; - // TODO RDF10, what happens here? - if (rdf_ver == 9) - { // Bug in RDF9 makes this dimension2Size instead of the expected dimension1Size - if(is_sino_file()) - str_max_number_of_non_arc_corrected_bins = file.openDataSet("/HeaderData/Sorter/dimension2Size"); - else - str_max_number_of_non_arc_corrected_bins = file.openDataSet("/HeaderData/Sorter/dimension1Size"); - } - H5::DataSet str_axial_crystals_per_block = file.openDataSet("/HeaderData/SystemGeometry/axialCrystalsPerBlock"); - H5::DataSet str_radial_crystals_per_block = file.openDataSet("/HeaderData/SystemGeometry/radialCrystalsPerBlock"); - // Convert to numbers. - - str_radial_blocks_per_module.read(&num_transaxial_blocks_per_bucket, H5::PredType::NATIVE_UINT32); - str_axial_blocks_per_module.read(&num_axial_blocks_per_bucket, H5::PredType::NATIVE_UINT32); - str_axial_blocks_per_unit.read(&axial_blocks_per_unit, H5::PredType::NATIVE_UINT32); - str_radial_blocks_per_unit.read(&radial_blocks_per_unit, H5::PredType::NATIVE_UINT32); - str_axial_units_per_module.read(&axial_units_per_module, H5::PredType::NATIVE_UINT32); - str_radial_units_per_module.read(&radial_units_per_module, H5::PredType::NATIVE_UINT32); - str_axial_modules_per_system.read(&axial_modules_per_system, H5::PredType::NATIVE_UINT32); - str_radial_modules_per_system.read(&radial_modules_per_system, H5::PredType::NATIVE_UINT32); - str_detector_axial_size.read(&detector_axial_size, H5::PredType::NATIVE_FLOAT); - str_intrinsic_tilt.read(&intrinsic_tilt, H5::PredType::NATIVE_FLOAT); - str_effective_ring_diameter.read(&effective_ring_diameter, H5::PredType::NATIVE_FLOAT); - str_max_number_of_non_arc_corrected_bins.read(&max_num_non_arccorrected_bins, H5::PredType::NATIVE_UINT32); - str_radial_crystals_per_block.read(&num_transaxial_crystals_per_block, H5::PredType::NATIVE_UINT32); - str_axial_crystals_per_block.read(&num_axial_crystals_per_block, H5::PredType::NATIVE_UINT32); - - 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)); - if (is_null_ptr(scanner_sptr)) - error("Scanner read from RDF file is " + read_str_scanner + ", but this is not supported yet"); - - scanner_sptr->set_num_detectors_per_ring(num_detectors_per_ring); - scanner_sptr->set_num_rings(num_rings); - if (!is_list_file()) - scanner_sptr->set_max_num_non_arccorrected_bins(max_num_non_arccorrected_bins); - scanner_sptr->set_ring_spacing(ring_spacing); - scanner_sptr->set_default_intrinsic_tilt(intrinsic_tilt*_PI/180); - scanner_sptr->set_num_axial_blocks_per_bucket(num_axial_blocks_per_bucket); - scanner_sptr->set_num_transaxial_blocks_per_bucket(num_transaxial_blocks_per_bucket); - scanner_sptr->set_num_axial_crystals_per_block(num_axial_crystals_per_block); - scanner_sptr->set_num_transaxial_crystals_per_block(num_transaxial_crystals_per_block); - scanner_sptr->set_num_detector_layers(num_detector_layers); - scanner_sptr->set_reference_energy(511.F); - - if (fabs(scanner_sptr->get_effective_ring_radius() - effective_ring_diameter/2) > .1F) - { - const float def_DOI = .0F; - warning("GEHDF5Wrapper: default STIR effective ring radius is " - + std::to_string(scanner_sptr->get_effective_ring_radius()) - + ", while RDF says " + std::to_string(effective_ring_diameter/2) - + "\nWill adjust scanner info to fit with the RDF file using default average DOI of " - + std::to_string(def_DOI) + "mm"); - scanner_sptr->set_inner_ring_radius((effective_ring_diameter/2) - def_DOI); - scanner_sptr->set_average_depth_of_interaction(def_DOI); - } - if (scanner_sptr->get_default_bin_size() <= 0.F) - { - warning("GEHDF5Wrapper: default bin-size is not set. This will create trouble for FBP etc"); - } - if (scanner_sptr->get_default_num_arccorrected_bins() <= 0) - { - warning("GEHDF5Wrapper: default num_arccorrected bins is not set. This will create trouble for FBP etc"); - } - if (scanner_sptr->get_energy_resolution() <= 0) - { - warning("GEHDF5Wrapper: energy resolution is not set. This will create trouble for scatter estimation"); - } - - return scanner_sptr; +shared_ptr +GEHDF5Wrapper::get_scanner_from_HDF5() { + std::string read_str_scanner; + H5::StrType vlst( + 0, + 37); // 37 here is the length of the string (PW got it from the text file generated by list2txt with the LIST000_decomp.BLF + + H5::DataSet dataset = file.openDataSet("/HeaderData/ExamData/scannerDesc"); + dataset.read(read_str_scanner, vlst); + + float effective_ring_diameter; + int num_transaxial_blocks_per_bucket = 0; + int num_axial_blocks_per_bucket = 0; + int axial_blocks_per_unit = 0; + int radial_blocks_per_unit = 0; + int axial_units_per_module = 0; + int radial_units_per_module = 0; + int axial_modules_per_system = 0; + int radial_modules_per_system = 0; + int max_num_non_arccorrected_bins = 0; + int num_transaxial_crystals_per_block = 0; + int num_axial_crystals_per_block = 0; + float detector_axial_size = 0.0; + float intrinsic_tilt = 0.0; + int num_detector_layers = 1; + + H5::DataSet str_effective_ring_diameter = file.openDataSet("/HeaderData/SystemGeometry/effectiveRingDiameter"); + H5::DataSet str_axial_blocks_per_module = file.openDataSet("/HeaderData/SystemGeometry/axialBlocksPerModule"); + H5::DataSet str_radial_blocks_per_module = file.openDataSet("/HeaderData/SystemGeometry/radialBlocksPerModule"); + H5::DataSet str_axial_blocks_per_unit = file.openDataSet("/HeaderData/SystemGeometry/axialBlocksPerUnit"); + H5::DataSet str_radial_blocks_per_unit = file.openDataSet("/HeaderData/SystemGeometry/radialBlocksPerUnit"); + H5::DataSet str_axial_units_per_module = file.openDataSet("/HeaderData/SystemGeometry/axialUnitsPerModule"); + H5::DataSet str_radial_units_per_module = file.openDataSet("/HeaderData/SystemGeometry/radialUnitsPerModule"); + H5::DataSet str_axial_modules_per_system = file.openDataSet("/HeaderData/SystemGeometry/axialModulesPerSystem"); + H5::DataSet str_radial_modules_per_system = file.openDataSet("/HeaderData/SystemGeometry/radialModulesPerSystem"); + //! \todo P.W: Find the crystal gaps and other info missing. + H5::DataSet str_detector_axial_size = file.openDataSet("/HeaderData/SystemGeometry/detectorAxialSize"); + H5::DataSet str_intrinsic_tilt = file.openDataSet("/HeaderData/SystemGeometry/transaxial_crystal_0_offset"); + + H5::DataSet str_max_number_of_non_arc_corrected_bins; + // TODO RDF10, what happens here? + if (rdf_ver == 9) { // Bug in RDF9 makes this dimension2Size instead of the expected dimension1Size + if (is_sino_file()) + str_max_number_of_non_arc_corrected_bins = file.openDataSet("/HeaderData/Sorter/dimension2Size"); + else + str_max_number_of_non_arc_corrected_bins = file.openDataSet("/HeaderData/Sorter/dimension1Size"); + } + H5::DataSet str_axial_crystals_per_block = file.openDataSet("/HeaderData/SystemGeometry/axialCrystalsPerBlock"); + H5::DataSet str_radial_crystals_per_block = file.openDataSet("/HeaderData/SystemGeometry/radialCrystalsPerBlock"); + // Convert to numbers. + + str_radial_blocks_per_module.read(&num_transaxial_blocks_per_bucket, H5::PredType::NATIVE_UINT32); + str_axial_blocks_per_module.read(&num_axial_blocks_per_bucket, H5::PredType::NATIVE_UINT32); + str_axial_blocks_per_unit.read(&axial_blocks_per_unit, H5::PredType::NATIVE_UINT32); + str_radial_blocks_per_unit.read(&radial_blocks_per_unit, H5::PredType::NATIVE_UINT32); + str_axial_units_per_module.read(&axial_units_per_module, H5::PredType::NATIVE_UINT32); + str_radial_units_per_module.read(&radial_units_per_module, H5::PredType::NATIVE_UINT32); + str_axial_modules_per_system.read(&axial_modules_per_system, H5::PredType::NATIVE_UINT32); + str_radial_modules_per_system.read(&radial_modules_per_system, H5::PredType::NATIVE_UINT32); + str_detector_axial_size.read(&detector_axial_size, H5::PredType::NATIVE_FLOAT); + str_intrinsic_tilt.read(&intrinsic_tilt, H5::PredType::NATIVE_FLOAT); + str_effective_ring_diameter.read(&effective_ring_diameter, H5::PredType::NATIVE_FLOAT); + str_max_number_of_non_arc_corrected_bins.read(&max_num_non_arccorrected_bins, H5::PredType::NATIVE_UINT32); + str_radial_crystals_per_block.read(&num_transaxial_crystals_per_block, H5::PredType::NATIVE_UINT32); + str_axial_crystals_per_block.read(&num_axial_crystals_per_block, H5::PredType::NATIVE_UINT32); + + 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)); + if (is_null_ptr(scanner_sptr)) + error("Scanner read from RDF file is " + read_str_scanner + ", but this is not supported yet"); + + scanner_sptr->set_num_detectors_per_ring(num_detectors_per_ring); + scanner_sptr->set_num_rings(num_rings); + if (!is_list_file()) + scanner_sptr->set_max_num_non_arccorrected_bins(max_num_non_arccorrected_bins); + scanner_sptr->set_ring_spacing(ring_spacing); + scanner_sptr->set_default_intrinsic_tilt(intrinsic_tilt * _PI / 180); + scanner_sptr->set_num_axial_blocks_per_bucket(num_axial_blocks_per_bucket); + scanner_sptr->set_num_transaxial_blocks_per_bucket(num_transaxial_blocks_per_bucket); + scanner_sptr->set_num_axial_crystals_per_block(num_axial_crystals_per_block); + scanner_sptr->set_num_transaxial_crystals_per_block(num_transaxial_crystals_per_block); + scanner_sptr->set_num_detector_layers(num_detector_layers); + scanner_sptr->set_reference_energy(511.F); + + if (fabs(scanner_sptr->get_effective_ring_radius() - effective_ring_diameter / 2) > .1F) { + const float def_DOI = .0F; + warning("GEHDF5Wrapper: default STIR effective ring radius is " + std::to_string(scanner_sptr->get_effective_ring_radius()) + + ", while RDF says " + std::to_string(effective_ring_diameter / 2) + + "\nWill adjust scanner info to fit with the RDF file using default average DOI of " + std::to_string(def_DOI) + "mm"); + scanner_sptr->set_inner_ring_radius((effective_ring_diameter / 2) - def_DOI); + scanner_sptr->set_average_depth_of_interaction(def_DOI); + } + if (scanner_sptr->get_default_bin_size() <= 0.F) { + warning("GEHDF5Wrapper: default bin-size is not set. This will create trouble for FBP etc"); + } + if (scanner_sptr->get_default_num_arccorrected_bins() <= 0) { + warning("GEHDF5Wrapper: default num_arccorrected bins is not set. This will create trouble for FBP etc"); + } + if (scanner_sptr->get_energy_resolution() <= 0) { + warning("GEHDF5Wrapper: energy resolution is not set. This will create trouble for scatter estimation"); + } + + return scanner_sptr; } -void GEHDF5Wrapper::initialise_proj_data_info_from_HDF5() -{ +void +GEHDF5Wrapper::initialise_proj_data_info_from_HDF5() { shared_ptr scanner_sptr = get_scanner_from_HDF5(); this->proj_data_info_sptr = - ProjDataInfo::construct_proj_data_info(scanner_sptr, - /*span*/ 2, - /* max_delta*/ scanner_sptr->get_num_rings()-1, - /* num_views */ scanner_sptr->get_num_detectors_per_ring()/2, - /* num_tangential_poss */ scanner_sptr->get_max_num_non_arccorrected_bins(), - /* arc_corrected */ false - ); - this->proj_data_info_sptr-> - set_bed_position_horizontal(this->read_dataset_int32("/HeaderData/AcqParameters/LandmarkParameters/absTableLongitude")/10.F); /* units in RDF are 0.1 mm */ - //this->proj_data_info_sptr->set_gantry_tilt(this->read_dataset_uint32("/HeaderData/AcqParameters/LandmarkParameters/gantryTilt")); /* units in RDF are 0.25 degrees, patient relative */ - this->proj_data_info_sptr-> - set_bed_position_vertical(this->read_dataset_int32("/HeaderData/AcqParameters/LandmarkParameters/tableElevation")/10.F); /* units in RDF are 0.1 mm */ + ProjDataInfo::construct_proj_data_info(scanner_sptr, + /*span*/ 2, + /* max_delta*/ scanner_sptr->get_num_rings() - 1, + /* num_views */ scanner_sptr->get_num_detectors_per_ring() / 2, + /* num_tangential_poss */ scanner_sptr->get_max_num_non_arccorrected_bins(), + /* arc_corrected */ false); + this->proj_data_info_sptr->set_bed_position_horizontal( + this->read_dataset_int32("/HeaderData/AcqParameters/LandmarkParameters/absTableLongitude") / + 10.F); /* units in RDF are 0.1 mm */ + // this->proj_data_info_sptr->set_gantry_tilt(this->read_dataset_uint32("/HeaderData/AcqParameters/LandmarkParameters/gantryTilt")); + // /* units in RDF are 0.25 degrees, patient relative */ + this->proj_data_info_sptr->set_bed_position_vertical( + this->read_dataset_int32("/HeaderData/AcqParameters/LandmarkParameters/tableElevation") / + 10.F); /* units in RDF are 0.1 mm */ } -unsigned int GEHDF5Wrapper::get_num_singles_samples() -{ - return m_num_singles_samples; +unsigned int +GEHDF5Wrapper::get_num_singles_samples() { + return m_num_singles_samples; } - -void GEHDF5Wrapper::initialise_exam_info() -{ - this->exam_info_sptr.reset(new ExamInfo()); - this->exam_info_sptr->imaging_modality = ImagingModality(ImagingModality::PT); - { - const std::uint32_t patientEntry = read_dataset_uint32("/HeaderData/AcqParameters/LandmarkParameters/patientEntry"); - const std::uint32_t patientPosition = read_dataset_uint32("/HeaderData/AcqParameters/LandmarkParameters/patientPosition"); - PatientPosition::OrientationValue orientation; - PatientPosition::RotationValue rotation; - switch (patientEntry) - { - case AcqPatientEntries::ACQ_HEAD_FIRST: orientation = PatientPosition::OrientationValue::head_in; break; - case AcqPatientEntries::ACQ_FEET_FIRST: orientation = PatientPosition::OrientationValue::feet_in; break; - default: orientation = PatientPosition::OrientationValue::unknown_orientation; - } - switch (patientPosition) - { - case AcqPatientPositions::ACQ_SUPINE: rotation = PatientPosition::RotationValue::supine; break; - case AcqPatientPositions::ACQ_PRONE: rotation = PatientPosition::RotationValue::prone; break; - case AcqPatientPositions::ACQ_LEFT_DECUB: rotation = PatientPosition::RotationValue::left; break; - case AcqPatientPositions::ACQ_RIGHT_DECUB: rotation = PatientPosition::RotationValue::right; break; - default: rotation = PatientPosition::RotationValue::unknown_rotation; break; - } - exam_info_sptr->patient_position = PatientPosition(orientation, rotation); +void +GEHDF5Wrapper::initialise_exam_info() { + this->exam_info_sptr.reset(new ExamInfo()); + this->exam_info_sptr->imaging_modality = ImagingModality(ImagingModality::PT); + { + const std::uint32_t patientEntry = read_dataset_uint32("/HeaderData/AcqParameters/LandmarkParameters/patientEntry"); + const std::uint32_t patientPosition = read_dataset_uint32("/HeaderData/AcqParameters/LandmarkParameters/patientPosition"); + PatientPosition::OrientationValue orientation; + PatientPosition::RotationValue rotation; + switch (patientEntry) { + case AcqPatientEntries::ACQ_HEAD_FIRST: + orientation = PatientPosition::OrientationValue::head_in; + break; + case AcqPatientEntries::ACQ_FEET_FIRST: + orientation = PatientPosition::OrientationValue::feet_in; + break; + default: + orientation = PatientPosition::OrientationValue::unknown_orientation; } - // PW Get the high and low energy threshold values from HDF5 header. - unsigned int low_energy_thres = 0; - unsigned int high_energy_thres = 0; + switch (patientPosition) { + case AcqPatientPositions::ACQ_SUPINE: + rotation = PatientPosition::RotationValue::supine; + break; + case AcqPatientPositions::ACQ_PRONE: + rotation = PatientPosition::RotationValue::prone; + break; + case AcqPatientPositions::ACQ_LEFT_DECUB: + rotation = PatientPosition::RotationValue::left; + break; + case AcqPatientPositions::ACQ_RIGHT_DECUB: + rotation = PatientPosition::RotationValue::right; + break; + default: + rotation = PatientPosition::RotationValue::unknown_rotation; + break; + } + exam_info_sptr->patient_position = PatientPosition(orientation, rotation); + } + // PW Get the high and low energy threshold values from HDF5 header. + unsigned int low_energy_thres = 0; + unsigned int high_energy_thres = 0; + + H5::DataSet str_low_energy_thres = file.openDataSet("/HeaderData/AcqParameters/EDCATParameters/lower_energy_limit"); + H5::DataSet str_high_energy_thres = file.openDataSet("/HeaderData/AcqParameters/EDCATParameters/upper_energy_limit"); + + str_low_energy_thres.read(&low_energy_thres, H5::PredType::NATIVE_UINT32); + str_high_energy_thres.read(&high_energy_thres, H5::PredType::NATIVE_UINT32); + + // PW Set these values in exam_info_sptr. + exam_info_sptr->set_high_energy_thres(static_cast(high_energy_thres)); + exam_info_sptr->set_low_energy_thres(static_cast(low_energy_thres)); + + // read time since 1970 + std::uint32_t scanStartTime = 0; + H5::DataSet scanStartTime_dataset = file.openDataSet("/HeaderData/AcqStats/scanStartTime"); + scanStartTime_dataset.read(&scanStartTime, H5::PredType::NATIVE_UINT32); + exam_info_sptr->start_time_in_secs_since_1970 = double(scanStartTime); + + // get time frame + std::uint32_t frameStartTime = 0; + std::uint32_t frameDuration = 0; + H5::DataSet frameStartTime_dataset = file.openDataSet("/HeaderData/AcqStats/frameStartTime"); + H5::DataSet frameDuration_dataset = file.openDataSet("/HeaderData/AcqStats/frameDuration"); + + frameStartTime_dataset.read(&frameStartTime, H5::PredType::NATIVE_UINT32); + frameDuration_dataset.read(&frameDuration, H5::PredType::NATIVE_UINT32); + const double frame_start_time = double(frameStartTime - scanStartTime); + + std::vector> tf{{frame_start_time, frame_start_time + frameDuration / 1000}}; + + TimeFrameDefinitions tm(tf); + exam_info_sptr->set_time_frame_definitions(tm); +} - H5::DataSet str_low_energy_thres = file.openDataSet("/HeaderData/AcqParameters/EDCATParameters/lower_energy_limit"); - H5::DataSet str_high_energy_thres = file.openDataSet("/HeaderData/AcqParameters/EDCATParameters/upper_energy_limit"); +Succeeded +GEHDF5Wrapper::initialise_listmode_data() { + if (!is_list_file()) + error("The file provided is not listmode. Aborting"); + + if (rdf_ver == 9) { + m_address = "/ListData/listData"; + // These values are not in the file are come from info shared by GE. + m_size_of_record_signature = 6; + m_max_size_of_record = 16; + + unsigned int num_time_slices = 0; + H5::DataSet timeframe_dataspace = file.openDataSet("/HeaderData/SinglesHeader/numValidSamples"); + timeframe_dataspace.read(&num_time_slices, H5::PredType::NATIVE_UINT32); + if (num_time_slices == 0) { + error("Zero number of valid samples singles samples in data. Aborting"); + } - str_low_energy_thres.read(&low_energy_thres, H5::PredType::NATIVE_UINT32); - str_high_energy_thres.read(&high_energy_thres, H5::PredType::NATIVE_UINT32); + m_num_singles_samples = num_time_slices; - // PW Set these values in exam_info_sptr. - exam_info_sptr->set_high_energy_thres(static_cast(high_energy_thres)); - exam_info_sptr->set_low_energy_thres(static_cast(low_energy_thres)); + } else + return Succeeded::no; - // read time since 1970 - std::uint32_t scanStartTime = 0; - H5::DataSet scanStartTime_dataset = file.openDataSet("/HeaderData/AcqStats/scanStartTime"); - scanStartTime_dataset.read(&scanStartTime, H5::PredType::NATIVE_UINT32); - exam_info_sptr->start_time_in_secs_since_1970=double(scanStartTime); + m_dataset_sptr.reset(new H5::DataSet(file.openDataSet(m_address))); - // get time frame - std::uint32_t frameStartTime = 0; - std::uint32_t frameDuration = 0; - H5::DataSet frameStartTime_dataset = file.openDataSet("/HeaderData/AcqStats/frameStartTime"); - H5::DataSet frameDuration_dataset = file.openDataSet("/HeaderData/AcqStats/frameDuration"); + m_dataspace = m_dataset_sptr->getSpace(); + int dataset_list_Ndims = m_dataspace.getSimpleExtentNdims(); - frameStartTime_dataset.read(&frameStartTime, H5::PredType::NATIVE_UINT32); - frameDuration_dataset.read(&frameDuration, H5::PredType::NATIVE_UINT32); - const double frame_start_time = double(frameStartTime - scanStartTime); + // We allocate dims_out in the stack for efficiecy and safety but we need an error check just in case then + if (dataset_list_Ndims > m_max_dataset_dims) + error("Dataset dimensions (" + std::to_string(dataset_list_Ndims) + ") bigger than maximum of" + + std::to_string(m_max_dataset_dims) + ". This is unexpected, Aborting."); + hsize_t dims_out[m_max_dataset_dims]; - std::vector >tf{{frame_start_time,frame_start_time+frameDuration/1000}}; + m_dataspace.getSimpleExtentDims(dims_out, NULL); + m_list_size = dims_out[0]; + const hsize_t tmp_size_of_record_signature = m_size_of_record_signature; + m_memspace_ptr = new H5::DataSpace(dataset_list_Ndims, &tmp_size_of_record_signature); - TimeFrameDefinitions tm(tf); - exam_info_sptr->set_time_frame_definitions(tm); + return Succeeded::yes; } -Succeeded GEHDF5Wrapper::initialise_listmode_data() -{ - if(!is_list_file()) - error("The file provided is not listmode. Aborting"); - - if(rdf_ver==9) - { - m_address = "/ListData/listData"; - // These values are not in the file are come from info shared by GE. - m_size_of_record_signature = 6; - m_max_size_of_record = 16; - - unsigned int num_time_slices = 0; - H5::DataSet timeframe_dataspace = file.openDataSet("/HeaderData/SinglesHeader/numValidSamples"); - timeframe_dataspace.read(&num_time_slices, H5::PredType::NATIVE_UINT32); - if(num_time_slices==0) - { - error("Zero number of valid samples singles samples in data. Aborting"); - } - - m_num_singles_samples=num_time_slices; - - } - else - return Succeeded::no; +Succeeded +GEHDF5Wrapper::initialise_singles_data() { - m_dataset_sptr.reset(new H5::DataSet(file.openDataSet(m_address))); + if (!is_list_file() && !is_sino_file()) + error("The file provided is not listmode or sinogram data. Aborting"); + if (rdf_ver == 9) { + m_address = "/Singles/CrystalSingles/sample"; + // Get the DataSpace (metadata) corresponding to the DataSet that we want to read + m_dataset_sptr.reset(new H5::DataSet(file.openDataSet(m_address + "1"))); m_dataspace = m_dataset_sptr->getSpace(); - int dataset_list_Ndims = m_dataspace.getSimpleExtentNdims(); - - // We allocate dims_out in the stack for efficiecy and safety but we need an error check just in case then - if (dataset_list_Ndims>m_max_dataset_dims) - error("Dataset dimensions ("+ std::to_string(dataset_list_Ndims) + ") bigger than maximum of" + std::to_string(m_max_dataset_dims) + ". This is unexpected, Aborting."); - hsize_t dims_out[m_max_dataset_dims]; - - m_dataspace.getSimpleExtentDims( dims_out, NULL); - m_list_size=dims_out[0]; - const hsize_t tmp_size_of_record_signature = m_size_of_record_signature; - m_memspace_ptr = new H5::DataSpace( dataset_list_Ndims, - &tmp_size_of_record_signature); - - return Succeeded::yes; -} - -Succeeded GEHDF5Wrapper::initialise_singles_data() -{ - - if(!is_list_file() && !is_sino_file()) - error("The file provided is not listmode or sinogram data. Aborting"); - - if(rdf_ver==9) - { - m_address = "/Singles/CrystalSingles/sample"; - // Get the DataSpace (metadata) corresponding to the DataSet that we want to read - m_dataset_sptr.reset(new H5::DataSet(file.openDataSet(m_address + "1"))); - m_dataspace = m_dataset_sptr->getSpace(); - // Create an array to host the size of the dimensions - const int rank = m_dataspace.getSimpleExtentNdims(); - // We allocate dims in the stack for efficiecy and safety but we need an error check just in case then - if (rank>m_max_dataset_dims) - error("Dataset dimensions ("+ std::to_string(rank) + ") bigger than maximum of" + std::to_string(m_max_dataset_dims) + ". This is unexpected, Aborting."); - hsize_t dims[m_max_dataset_dims]; - // Read size of dimensions - m_dataspace.getSimpleExtentDims( dims, NULL); - - m_NX_SUB = dims[0]; // hyperslab dimensions - m_NY_SUB = dims[1]; - m_NZ_SUB = (rank==2)? 1 : dims[2]; // Signa has rank==2, but this stay shere just in case... - - - unsigned int num_time_slices = 0; - H5::DataSet timeframe_dataspace = file.openDataSet("/HeaderData/SinglesHeader/numValidSamples"); - timeframe_dataspace.read(&num_time_slices, H5::PredType::NATIVE_UINT32); - if(num_time_slices==0) - { - error("Zero number of valid samples singles samples in data. Aborting"); - } + // Create an array to host the size of the dimensions + const int rank = m_dataspace.getSimpleExtentNdims(); + // We allocate dims in the stack for efficiecy and safety but we need an error check just in case then + if (rank > m_max_dataset_dims) + error("Dataset dimensions (" + std::to_string(rank) + ") bigger than maximum of" + std::to_string(m_max_dataset_dims) + + ". This is unexpected, Aborting."); + hsize_t dims[m_max_dataset_dims]; + // Read size of dimensions + m_dataspace.getSimpleExtentDims(dims, NULL); + + m_NX_SUB = dims[0]; // hyperslab dimensions + m_NY_SUB = dims[1]; + m_NZ_SUB = (rank == 2) ? 1 : dims[2]; // Signa has rank==2, but this stay shere just in case... + + unsigned int num_time_slices = 0; + H5::DataSet timeframe_dataspace = file.openDataSet("/HeaderData/SinglesHeader/numValidSamples"); + timeframe_dataspace.read(&num_time_slices, H5::PredType::NATIVE_UINT32); + if (num_time_slices == 0) { + error("Zero number of valid samples singles samples in data. Aborting"); + } - m_num_singles_samples=num_time_slices; + m_num_singles_samples = num_time_slices; #if 0 m_NX = dims[0]; // output buffer dimensions m_NY = dims[1]; m_NZ = (rank==2)? 1 : dims[2]; #endif - } - else - return Succeeded::no; + } else + return Succeeded::no; - return Succeeded::yes; + return Succeeded::yes; } -Succeeded GEHDF5Wrapper::initialise_proj_data(const unsigned int view_num) -{ - if(!is_sino_file()) - error("The file provided is not sinogram data. Aborting"); +Succeeded +GEHDF5Wrapper::initialise_proj_data(const unsigned int view_num) { + if (!is_sino_file()) + error("The file provided is not sinogram data. Aborting"); - if(view_num == 0 || view_num > static_cast(this->get_scanner_sptr()->get_num_detectors_per_ring()/2)) - error("internal error in GE HDF5 code: view number "+ std::to_string(view_num) +" is incorrect"); + if (view_num == 0 || view_num > static_cast(this->get_scanner_sptr()->get_num_detectors_per_ring() / 2)) + error("internal error in GE HDF5 code: view number " + std::to_string(view_num) + " is incorrect"); - if(rdf_ver==9) - { - m_address = "/SegmentData/Segment2/3D_TOF_Sinogram/view" + std::to_string(view_num); - - m_dataset_sptr.reset(new H5::DataSet(file.openDataSet(m_address))); - m_dataspace = m_dataset_sptr->getSpace(); - // Create an array to host the size of the dimensions - const int rank = m_dataspace.getSimpleExtentNdims(); - // We allocate dims in the stack for efficiecy and safety but we need an error check just in case then - if (rank>m_max_dataset_dims) - error("Dataset dimensions ("+ std::to_string(rank) + ") bigger than maximum of" + std::to_string(m_max_dataset_dims) + ". This is unexpected, Aborting."); - hsize_t dims[m_max_dataset_dims]; - // Read size of dimensions - m_dataspace.getSimpleExtentDims( dims, NULL); - - // AB for signa, these where [1981,27,357] and [45,448,357] - m_NX_SUB = dims[0] ; // hyperslab dimensions - m_NY_SUB = dims[1]; - m_NZ_SUB = dims[2]; + if (rdf_ver == 9) { + m_address = "/SegmentData/Segment2/3D_TOF_Sinogram/view" + std::to_string(view_num); + + m_dataset_sptr.reset(new H5::DataSet(file.openDataSet(m_address))); + m_dataspace = m_dataset_sptr->getSpace(); + // Create an array to host the size of the dimensions + const int rank = m_dataspace.getSimpleExtentNdims(); + // We allocate dims in the stack for efficiecy and safety but we need an error check just in case then + if (rank > m_max_dataset_dims) + error("Dataset dimensions (" + std::to_string(rank) + ") bigger than maximum of" + std::to_string(m_max_dataset_dims) + + ". This is unexpected, Aborting."); + hsize_t dims[m_max_dataset_dims]; + // Read size of dimensions + m_dataspace.getSimpleExtentDims(dims, NULL); + + // AB for signa, these where [1981,27,357] and [45,448,357] + m_NX_SUB = dims[0]; // hyperslab dimensions + m_NY_SUB = dims[1]; + m_NZ_SUB = dims[2]; #if 0 // AB todo: ??? why are these different? m_NX = 45; // output buffer dimensions m_NY = 448; m_NZ = 357; #endif - } - else - return Succeeded::no; + } else + return Succeeded::no; - return Succeeded::yes; + return Succeeded::yes; } // PW The geo factors are stored in geo3d file under the file path called /SegmentData/Segment4/3D_Norm_correction/slice%d where // slice numbers go from 1 to 16. Here this path is initialised, along with the output buffer and hyperslab. // -Succeeded GEHDF5Wrapper::initialise_geo_factors_data(const unsigned int slice_num) -{ - if(!is_geo_file()) - error("The file provided is not geometry data. Aborting"); +Succeeded +GEHDF5Wrapper::initialise_geo_factors_data(const unsigned int slice_num) { + if (!is_geo_file()) + error("The file provided is not geometry data. Aborting"); - if(slice_num == 0) - error("internal error in GE HDF5 geo code: slice number "+ std::to_string(slice_num) +" is incorrect"); + if (slice_num == 0) + error("internal error in GE HDF5 geo code: slice number " + std::to_string(slice_num) + " is incorrect"); - if(rdf_ver==9) + if (rdf_ver == 9) { + m_address = "/SegmentData/Segment4/3D_Norm_Correction/slice"; { - m_address = "/SegmentData/Segment4/3D_Norm_Correction/slice"; - { - // Open Dataset and get Dataspace(metadata) - m_dataset_sptr.reset(new H5::DataSet(file.openDataSet(m_address + std::to_string(slice_num)))); - m_dataspace = m_dataset_sptr->getSpace(); - - // Read dimensions - const int rank = m_dataspace.getSimpleExtentNdims(); - // We allocate dims in the stack for efficiecy and safety but we need an error check just in case then - if (rank>m_max_dataset_dims) - error("Dataset dimensions ("+ std::to_string(rank) + ") bigger than maximum of" + std::to_string(m_max_dataset_dims) + ". This is unexpected, Aborting."); - hsize_t dims[m_max_dataset_dims]; - - m_dataspace.getSimpleExtentDims( dims, NULL); - - m_NX_SUB = dims[0]; // hyperslab dimensions - m_NY_SUB = dims[1]; - m_NZ_SUB = (rank==2)? 1 : dims[2]; // Signa has rank==2, but this stay shere just in case... + // Open Dataset and get Dataspace(metadata) + m_dataset_sptr.reset(new H5::DataSet(file.openDataSet(m_address + std::to_string(slice_num)))); + m_dataspace = m_dataset_sptr->getSpace(); + + // Read dimensions + const int rank = m_dataspace.getSimpleExtentNdims(); + // We allocate dims in the stack for efficiecy and safety but we need an error check just in case then + if (rank > m_max_dataset_dims) + error("Dataset dimensions (" + std::to_string(rank) + ") bigger than maximum of" + std::to_string(m_max_dataset_dims) + + ". This is unexpected, Aborting."); + hsize_t dims[m_max_dataset_dims]; + + m_dataspace.getSimpleExtentDims(dims, NULL); + + m_NX_SUB = dims[0]; // hyperslab dimensions + m_NY_SUB = dims[1]; + m_NZ_SUB = (rank == 2) ? 1 : dims[2]; // Signa has rank==2, but this stay shere just in case... #if 0 m_NX = dims[0]; // output buffer dimensions m_NY = dims[1]; m_NZ = (rank==2)? 1 : dims[2]; // Signa has rank==2, but this stay shere just in case... #endif - } } - else - return Succeeded::no; + } else + return Succeeded::no; - return Succeeded::yes; + return Succeeded::yes; } // This info is in norm3d file -Succeeded GEHDF5Wrapper::initialise_efficiency_factors() -{ - if(!is_norm_file()) - error("The file provided is not norm data. Aborting"); +Succeeded +GEHDF5Wrapper::initialise_efficiency_factors() { + if (!is_norm_file()) + error("The file provided is not norm data. Aborting"); - if(rdf_ver==9) - { - - m_address = "/3DCrystalEfficiency/crystalEfficiency"; - // Get the DataSpace (metadata) corresponding to the DataSet that we want to read - m_dataset_sptr.reset(new H5::DataSet(file.openDataSet(m_address))); - m_dataspace = m_dataset_sptr->getSpace(); - // Create an array to host the size of the dimensions - const int rank = m_dataspace.getSimpleExtentNdims(); - // We allocate dims in the stack for efficiecy and safety but we need an error check just in case then - if (rank>m_max_dataset_dims) - error("Dataset dimensions ("+ std::to_string(rank) + ") bigger than maximum of" + std::to_string(m_max_dataset_dims) + ". This is unexpected, Aborting."); - hsize_t dims[m_max_dataset_dims]; - // Read size of dimensions - m_dataspace.getSimpleExtentDims( dims, NULL); - - - m_NX_SUB = dims[0]; // hyperslab dimensions - // TODO: Why is this divided by 2?? - m_NY_SUB = dims[1]/2; // should be equal to scanner_sptr->get_num_detectors_per_ring(); - m_NZ_SUB = (rank==2)? 1 : dims[2]; + if (rdf_ver == 9) { + + m_address = "/3DCrystalEfficiency/crystalEfficiency"; + // Get the DataSpace (metadata) corresponding to the DataSet that we want to read + m_dataset_sptr.reset(new H5::DataSet(file.openDataSet(m_address))); + m_dataspace = m_dataset_sptr->getSpace(); + // Create an array to host the size of the dimensions + const int rank = m_dataspace.getSimpleExtentNdims(); + // We allocate dims in the stack for efficiecy and safety but we need an error check just in case then + if (rank > m_max_dataset_dims) + error("Dataset dimensions (" + std::to_string(rank) + ") bigger than maximum of" + std::to_string(m_max_dataset_dims) + + ". This is unexpected, Aborting."); + hsize_t dims[m_max_dataset_dims]; + // Read size of dimensions + m_dataspace.getSimpleExtentDims(dims, NULL); + + m_NX_SUB = dims[0]; // hyperslab dimensions + // TODO: Why is this divided by 2?? + m_NY_SUB = dims[1] / 2; // should be equal to scanner_sptr->get_num_detectors_per_ring(); + m_NZ_SUB = (rank == 2) ? 1 : dims[2]; #if 0 m_NX = dims[0]; // output buffer dimensions m_NY = dims[1]/2; // should be equal to scanner_sptr->get_num_detectors_per_ring(); m_NZ = (rank==2)? 1 : dims[2]; #endif - } - else - return Succeeded::no; - + } else + return Succeeded::no; - return Succeeded::yes; + return Succeeded::yes; } -float GEHDF5Wrapper::get_coincidence_time_window() const -{ - if(!is_list_file() && !is_sino_file()) - error("The file provided is not list or sino data. Aborting"); +float +GEHDF5Wrapper::get_coincidence_time_window() const { + if (!is_list_file() && !is_sino_file()) + error("The file provided is not list or sino data. Aborting"); - H5::DataSet ds_coincTimingPrecision = file.openDataSet("/HeaderData/AcqParameters/EDCATParameters/coincTimingPrecision"); - H5::DataSet ds_posCoincidenceWindow = file.openDataSet("/HeaderData/AcqParameters/EDCATParameters/posCoincidenceWindow"); - float coincTimingPrecision = 0; - int posCoincidenceWindow = 0; - ds_coincTimingPrecision.read(&coincTimingPrecision,H5::PredType::NATIVE_FLOAT); - ds_posCoincidenceWindow.read(&posCoincidenceWindow,H5::PredType::NATIVE_INT32); + H5::DataSet ds_coincTimingPrecision = file.openDataSet("/HeaderData/AcqParameters/EDCATParameters/coincTimingPrecision"); + H5::DataSet ds_posCoincidenceWindow = file.openDataSet("/HeaderData/AcqParameters/EDCATParameters/posCoincidenceWindow"); + float coincTimingPrecision = 0; + int posCoincidenceWindow = 0; + ds_coincTimingPrecision.read(&coincTimingPrecision, H5::PredType::NATIVE_FLOAT); + ds_posCoincidenceWindow.read(&posCoincidenceWindow, H5::PredType::NATIVE_INT32); - return (2*posCoincidenceWindow+1) *coincTimingPrecision*1e-9; + return (2 * posCoincidenceWindow + 1) * coincTimingPrecision * 1e-9; } - // Developed for listmode access -Succeeded GEHDF5Wrapper::read_list_data( char* output,std::streampos& current_offset, const hsize_t size) const -{ - if(!is_list_file()) - error("The file provided is not list data. Aborting"); - hsize_t pos = static_cast(current_offset); - m_dataspace.selectHyperslab( H5S_SELECT_SET, &size, &pos ); - m_dataset_sptr->read( output, H5::PredType::STD_U8LE, *m_memspace_ptr, m_dataspace ); - current_offset += static_cast(size); - - return Succeeded::yes; +Succeeded +GEHDF5Wrapper::read_list_data(char* output, std::streampos& current_offset, const hsize_t size) const { + if (!is_list_file()) + error("The file provided is not list data. Aborting"); + hsize_t pos = static_cast(current_offset); + m_dataspace.selectHyperslab(H5S_SELECT_SET, &size, &pos); + m_dataset_sptr->read(output, H5::PredType::STD_U8LE, *m_memspace_ptr, m_dataspace); + current_offset += static_cast(size); + + return Succeeded::yes; } // Developed for ProjData -Succeeded GEHDF5Wrapper::read_sinogram(Array<3, unsigned char> &output, - const std::array& offset, - const std::array& stride) -{ - // AB: this is only used for proj data, so for now lets ensure the file is sino. If its reused, we can change this. - if(!is_sino_file()) - error("File is not sinogram. Aborting"); - - if(offset[0] != 0 || offset[1] != 0 || offset[2] != 0) //AB there are other C++ ways of doing this, but this is the most readable really. - error("Only {0,0,0} offset supported. Aborting"); - if(stride[0] != 1 || stride[1] != 1 || stride[2] != 1) - error("Only {1,1,1} stride supported. Aborting"); - - // We know the size of the DataSpace - hsize_t str_dimsf[3] {m_NX_SUB, m_NY_SUB, m_NZ_SUB} ; - - - // get a temporary buffer here, - std::vector aux_buffer(m_NX_SUB*m_NY_SUB*m_NZ_SUB); - - m_dataspace.selectHyperslab(H5S_SELECT_SET, str_dimsf, offset.data()); - m_memspace_ptr= new H5::DataSpace(3, str_dimsf); - m_dataset_sptr->read(static_cast(aux_buffer.data()), H5::PredType::STD_U8LE, *m_memspace_ptr, m_dataspace); - - // the data is not in the correct size if its RDF9, so we will need to transpose the output of the data read. - if(rdf_ver==9) - { - output.resize(IndexRange3D(m_NZ_SUB,m_NY_SUB,m_NX_SUB)); - // now transpose the output. - // AB: todo - // todo 1: test - for(unsigned int i=0;i& output, const std::array& offset, + const std::array& stride) { + // AB: this is only used for proj data, so for now lets ensure the file is sino. If its reused, we can change this. + if (!is_sino_file()) + error("File is not sinogram. Aborting"); + + if (offset[0] != 0 || offset[1] != 0 || + offset[2] != 0) // AB there are other C++ ways of doing this, but this is the most readable really. + error("Only {0,0,0} offset supported. Aborting"); + if (stride[0] != 1 || stride[1] != 1 || stride[2] != 1) + error("Only {1,1,1} stride supported. Aborting"); + + // We know the size of the DataSpace + hsize_t str_dimsf[3]{m_NX_SUB, m_NY_SUB, m_NZ_SUB}; + + // get a temporary buffer here, + std::vector aux_buffer(m_NX_SUB * m_NY_SUB * m_NZ_SUB); + + m_dataspace.selectHyperslab(H5S_SELECT_SET, str_dimsf, offset.data()); + m_memspace_ptr = new H5::DataSpace(3, str_dimsf); + m_dataset_sptr->read(static_cast(aux_buffer.data()), H5::PredType::STD_U8LE, *m_memspace_ptr, m_dataspace); + + // the data is not in the correct size if its RDF9, so we will need to transpose the output of the data read. + if (rdf_ver == 9) { + output.resize(IndexRange3D(m_NZ_SUB, m_NY_SUB, m_NX_SUB)); + // now transpose the output. + // AB: todo + // todo 1: test + for (unsigned int i = 0; i < m_NZ_SUB; ++i) { + unsigned int ioffset = i * m_NX_SUB * m_NY_SUB; + for (unsigned int j = 0; j < m_NY_SUB; ++j) { + unsigned int joffset = j * m_NX_SUB; + for (unsigned int k = 0; k < m_NX_SUB; ++k) { + output[i][j][k] = aux_buffer[ioffset + joffset + k]; } - output.release_data_ptr(); + } } - + output.release_data_ptr(); + } - return Succeeded::yes; + return Succeeded::yes; } +// PW Developed for Geometric Correction Factors +Succeeded +GEHDF5Wrapper::read_geometric_factors(Array<1, unsigned int>& output, const std::array& offset, + const std::array& count, const std::array& stride) -//PW Developed for Geometric Correction Factors -Succeeded GEHDF5Wrapper::read_geometric_factors(Array<1, unsigned int> &output, - const std::array& offset, - const std::array& count, - const std::array& stride) - { - // AB: this is only used for geo data, so for now lets ensure the file is sino. If its reused, we can change this. - if(!is_geo_file()) - error("File is Geometry. Aborting"); - - if(count[0] == 0 || count[1] == 0) //AB there are other C++ ways of doing this, but this is the most readable really. - error("Requested zero data to read. Aborting"); - if(stride[0] != 1 || stride[1] != 1) - error("Only {1,1} stride supported. Aborting"); - - Array<1, unsigned int> aux_reader; - output.resize(count[0]*count[1]); - aux_reader.resize(count[0]*count[1]); - - m_dataspace.selectHyperslab(H5S_SELECT_SET, count.data(), offset.data()); - m_memspace_ptr= new H5::DataSpace(2, count.data()); - m_dataset_sptr->read(aux_reader.get_data_ptr(), H5::PredType::NATIVE_UINT32, *m_memspace_ptr, m_dataspace); - aux_reader.release_data_ptr(); - - // GE/RDF9 stores the tangetial axis reversed to STIR. Flip. - for(unsigned int ax=0;ax aux_reader; + output.resize(count[0] * count[1]); + aux_reader.resize(count[0] * count[1]); + + m_dataspace.selectHyperslab(H5S_SELECT_SET, count.data(), offset.data()); + m_memspace_ptr = new H5::DataSpace(2, count.data()); + m_dataset_sptr->read(aux_reader.get_data_ptr(), H5::PredType::NATIVE_UINT32, *m_memspace_ptr, m_dataspace); + aux_reader.release_data_ptr(); + + // GE/RDF9 stores the tangetial axis reversed to STIR. Flip. + for (unsigned int ax = 0; ax < count[0]; ++ax) + for (unsigned int tan = 0; tan < count[1]; ++tan) + output[ax * count[1] + ((count[1] - 1) - tan)] = aux_reader[ax * count[1] + tan]; + return Succeeded::yes; } -//PW Developed for Efficiency Factors -Succeeded GEHDF5Wrapper::read_efficiency_factors(Array<1, float> &output, - const std::array& offset, - const std::array& stride) - -{ - if(!is_norm_file()) - error("The file provided is not norm data. Aborting"); - - if(offset[0] != 0 || offset[1] != 0) //AB there are other C++ ways of doing this, but this is the most readable really. - error("Only {0,0} offset supported. Aborting"); - if(stride[0] != 1 || stride[1] != 1) - error("Only {1,1} stride supported. Aborting"); - - // We know the size of the DataSpace - hsize_t str_dimsf[2] {m_NX_SUB, m_NY_SUB} ; - Array<1, float> aux_reader; - output.resize(m_NX_SUB*m_NY_SUB); - aux_reader.resize(m_NX_SUB*m_NY_SUB); - - m_dataspace.selectHyperslab(H5S_SELECT_SET, str_dimsf, offset.data()); - m_memspace_ptr= new H5::DataSpace(2, str_dimsf); - m_dataset_sptr->read(aux_reader.get_data_ptr(), H5::PredType::NATIVE_FLOAT, *m_memspace_ptr, m_dataspace); - aux_reader.release_data_ptr(); - - // GE/RDF9 stores the tangetial axis reversed to STIR. Flip. - for(unsigned int ax=0;ax& output, const std::array& offset, + const std::array& stride) - return Succeeded::yes; +{ + if (!is_norm_file()) + error("The file provided is not norm data. Aborting"); + + if (offset[0] != 0 || offset[1] != 0) // AB there are other C++ ways of doing this, but this is the most readable really. + error("Only {0,0} offset supported. Aborting"); + if (stride[0] != 1 || stride[1] != 1) + error("Only {1,1} stride supported. Aborting"); + + // We know the size of the DataSpace + hsize_t str_dimsf[2]{m_NX_SUB, m_NY_SUB}; + Array<1, float> aux_reader; + output.resize(m_NX_SUB * m_NY_SUB); + aux_reader.resize(m_NX_SUB * m_NY_SUB); + + m_dataspace.selectHyperslab(H5S_SELECT_SET, str_dimsf, offset.data()); + m_memspace_ptr = new H5::DataSpace(2, str_dimsf); + m_dataset_sptr->read(aux_reader.get_data_ptr(), H5::PredType::NATIVE_FLOAT, *m_memspace_ptr, m_dataspace); + aux_reader.release_data_ptr(); + + // GE/RDF9 stores the tangetial axis reversed to STIR. Flip. + for (unsigned int ax = 0; ax < m_NX_SUB; ++ax) + for (unsigned int tan = 0; tan < m_NY_SUB; ++tan) + output[ax * m_NY_SUB + ((m_NY_SUB - 1) - tan)] = aux_reader[ax * m_NY_SUB + tan]; + + return Succeeded::yes; } // Developed for Singles -Succeeded GEHDF5Wrapper::read_singles(Array<1, unsigned int>& output, const unsigned int current_id) -{ - BOOST_STATIC_ASSERT(sizeof(unsigned int)==4); // Compilation time assert. - if(!is_list_file()&& !is_sino_file()) - error("The file provided is not listmode or sinogram data. Aborting"); +Succeeded +GEHDF5Wrapper::read_singles(Array<1, unsigned int>& output, const unsigned int current_id) { + BOOST_STATIC_ASSERT(sizeof(unsigned int) == 4); // Compilation time assert. + if (!is_list_file() && !is_sino_file()) + error("The file provided is not listmode or sinogram data. Aborting"); - if(current_id == 0 || current_id > this->m_num_singles_samples) - error("internal error in GE HDF5 code: singles slice_id "+ std::to_string(current_id) +" is incorrect"); + if (current_id == 0 || current_id > this->m_num_singles_samples) + error("internal error in GE HDF5 code: singles slice_id " + std::to_string(current_id) + " is incorrect"); - Array<1, unsigned int> aux_reader; - output.resize(m_NX_SUB*m_NY_SUB); - aux_reader.resize(m_NX_SUB*m_NY_SUB); + Array<1, unsigned int> aux_reader; + output.resize(m_NX_SUB * m_NY_SUB); + aux_reader.resize(m_NX_SUB * m_NY_SUB); - // AB: todo check if output allocated data size is correct. - m_dataset_sptr.reset(new H5::DataSet(file.openDataSet(m_address + std::to_string(current_id)))); - m_dataset_sptr->read(aux_reader.get_data_ptr(), H5::PredType::NATIVE_UINT32); - aux_reader.release_data_ptr(); + // AB: todo check if output allocated data size is correct. + m_dataset_sptr.reset(new H5::DataSet(file.openDataSet(m_address + std::to_string(current_id)))); + m_dataset_sptr->read(aux_reader.get_data_ptr(), H5::PredType::NATIVE_UINT32); + aux_reader.release_data_ptr(); - // GE/RDF9 stores the tangetial axis reversed to STIR. Flip. - for(unsigned int ax=0;axm_image_type=data_type_case; - this->MaxLength=num_voxels; - if(this->m_image_type == 64) - { - vData_f = new float[this->MaxLength]; - for (int i = 0; i < this->MaxLength; i++) - this->vData_f[i] = 0.F; - vData = NULL; - } - else if(this->m_image_type == 15) - { - vData = new short[this->MaxLength]; - for (int i = 0; i < this->MaxLength; i++) - this->vData[i] = 0; - vData_f = NULL; - } - else - { - vData = NULL; - vData_f = NULL; - } - vDownsample[0] = 1; - vDownsample[1] = 1; - vDownsample[2] = 1; - - vCenter[0] = 0; - vCenter[1] = 0; - vCenter[2] = 0; - vCenter[3] = 0; +Image::Image(const int num_voxels, const short data_type_case) { + this->m_image_type = data_type_case; + this->MaxLength = num_voxels; + if (this->m_image_type == 64) { + vData_f = new float[this->MaxLength]; + for (int i = 0; i < this->MaxLength; i++) + this->vData_f[i] = 0.F; + vData = NULL; + } else if (this->m_image_type == 15) { + vData = new short[this->MaxLength]; + for (int i = 0; i < this->MaxLength; i++) + this->vData[i] = 0; + vData_f = NULL; + } else { + vData = NULL; + vData_f = NULL; + } + vDownsample[0] = 1; + vDownsample[1] = 1; + vDownsample[2] = 1; + + vCenter[0] = 0; + vCenter[1] = 0; + vCenter[2] = 0; + vCenter[3] = 0; } // ------------------------------------------------------------------------- // Destructor // ------------------------------------------------------------------------- -Image::~Image() -{ - if (vData) - delete[] vData; - if (vData_f) - delete[] vData_f; +Image::~Image() { + if (vData) + delete[] vData; + if (vData_f) + delete[] vData_f; } // ------------------------------------------------------------------------- @@ -129,547 +121,532 @@ Image::~Image() // ------------------------------------------------------------------------- /** -* \brief Read data from GIPL filename. -* -* \param filename Input filename -*/ + * \brief Read data from GIPL filename. + * + * \param filename Input filename + */ // ------------------------------------------------------------------------- -void Image::GiplRead(char* filename) -{ - printf("Read %s\n",filename); - - // Open input binary file - std::fstream myFile(filename, std::ios_base::in | std::ios_base::binary); - - // Initialize image dimensions by zero - m_dim[0] = 0; - m_dim[1] = 0; - m_dim[2] = 0; - - // open file for reading - /*char *buf; - try { - buf = new char[512]; - if( buf == 0 ) - throw "Memory allocation failure!"; - } - catch( char * str ) { - cout << "Exception raised: " << str << '\n'; - }*/ - - // Read gipl header - ReadGiplHeader(&myFile); - - // Require big endian format - ByteSwapHeader(); - - // Length of the data to be read in - MaxLength = m_dim[0]*m_dim[1]*m_dim[2]; - - // File cannot be read, wrong path or filename - if (MaxLength == 0) - { - printf("Error: File %s cannot be found\n",filename); - exit(1); - } - - // Update image dimension - ImageDimension = 2; - if(m_dim[2]>1) ImageDimension = 3; - - // Update parameters dimension - ParametersDimension = ImageDimension*ImageDimension+ImageDimension; - - // Set offset vector - ImageOffset[0] = m_dim[0]; // XLen - ImageOffset[1] = m_dim[0]*m_dim[1]; // XLen*YLen - - // Set center of rotation of the image - for (int i = 0; i < ImageDimension; i++) - vCenter[i] = 0;//m_dim[i]/2.0*m_pixdim[i] - m_origin[i]; - - // Delete possible old data vectors - if (vData) - delete[] vData; - if (vData_f) - delete[] vData_f; - - - - // Check data type - switch(m_image_type) - { - case 15: // shorts (default) - - // Initialize image vector - vData = new short[MaxLength]; - - // Read image data - myFile.read((char*)vData, sizeof(short)*MaxLength); - - // Swap hi lo bytes - for( int i = 0; i < MaxLength; i++) - ByteSwap(&vData[i]); - - // Get min and max gray value - GetMinMaxValue(); - - break; - - case 64: // floats - - // Initialize image vector - vData_f = new float[MaxLength]; - - // Read image data - myFile.read((char*)vData_f, sizeof(float)*MaxLength); - - // Swap hi lo bytes - for( int i = 0; i < MaxLength; i++) - ByteSwap(&vData_f[i]); - - break; - - default: - break; - } - - //int SourceIndex = ((int)(5 + 10*ImageOffset[0] + 20*ImageOffset[1])); - //float test = vData_f[SourceIndex]; - - // Initially no downscaling - vDownsample[0] = 1; - vDownsample[1] = 1; - vDownsample[2] = 1; - - // Close file - myFile.close(); +void +Image::GiplRead(char* filename) { + printf("Read %s\n", filename); + + // Open input binary file + std::fstream myFile(filename, std::ios_base::in | std::ios_base::binary); + + // Initialize image dimensions by zero + m_dim[0] = 0; + m_dim[1] = 0; + m_dim[2] = 0; + + // open file for reading + /*char *buf; +try { +buf = new char[512]; +if( buf == 0 ) + throw "Memory allocation failure!"; +} +catch( char * str ) { +cout << "Exception raised: " << str << '\n'; +}*/ + + // Read gipl header + ReadGiplHeader(&myFile); + + // Require big endian format + ByteSwapHeader(); + + // Length of the data to be read in + MaxLength = m_dim[0] * m_dim[1] * m_dim[2]; + + // File cannot be read, wrong path or filename + if (MaxLength == 0) { + printf("Error: File %s cannot be found\n", filename); + exit(1); + } + + // Update image dimension + ImageDimension = 2; + if (m_dim[2] > 1) + ImageDimension = 3; + + // Update parameters dimension + ParametersDimension = ImageDimension * ImageDimension + ImageDimension; + + // Set offset vector + ImageOffset[0] = m_dim[0]; // XLen + ImageOffset[1] = m_dim[0] * m_dim[1]; // XLen*YLen + + // Set center of rotation of the image + for (int i = 0; i < ImageDimension; i++) + vCenter[i] = 0; // m_dim[i]/2.0*m_pixdim[i] - m_origin[i]; + + // Delete possible old data vectors + if (vData) + delete[] vData; + if (vData_f) + delete[] vData_f; + + // Check data type + switch (m_image_type) { + case 15: // shorts (default) + + // Initialize image vector + vData = new short[MaxLength]; + + // Read image data + myFile.read((char*)vData, sizeof(short) * MaxLength); + + // Swap hi lo bytes + for (int i = 0; i < MaxLength; i++) + ByteSwap(&vData[i]); + + // Get min and max gray value + GetMinMaxValue(); + + break; + + case 64: // floats + + // Initialize image vector + vData_f = new float[MaxLength]; + + // Read image data + myFile.read((char*)vData_f, sizeof(float) * MaxLength); + + // Swap hi lo bytes + for (int i = 0; i < MaxLength; i++) + ByteSwap(&vData_f[i]); + + break; + + default: + break; + } + + // int SourceIndex = ((int)(5 + 10*ImageOffset[0] + 20*ImageOffset[1])); + // float test = vData_f[SourceIndex]; + + // Initially no downscaling + vDownsample[0] = 1; + vDownsample[1] = 1; + vDownsample[2] = 1; + + // Close file + myFile.close(); } // ------------------------------------------------------------------------- /** -* \brief Get minimum and maximum gray values in image. -*/ + * \brief Get minimum and maximum gray values in image. + */ // ------------------------------------------------------------------------- -void Image::GetMinMaxValue() -{ - iMax = -16959; - iMin = +16959; - - // Check data type - if(m_image_type == 15) - { - for( int i = 0; i < MaxLength; i++) - { - if (vData[i] > iMax) - iMax = vData[i]; - if (vData[i] < iMin) - iMin = vData[i]; - } - } - if(m_image_type == 64) - { - for( int i = 0; i < MaxLength; i++) - { - if (vData_f[i] > iMax) - iMax = vData_f[i]; - if (vData_f[i] < iMin) - iMin = vData_f[i]; - } - } +void +Image::GetMinMaxValue() { + iMax = -16959; + iMin = +16959; + + // Check data type + if (m_image_type == 15) { + for (int i = 0; i < MaxLength; i++) { + if (vData[i] > iMax) + iMax = vData[i]; + if (vData[i] < iMin) + iMin = vData[i]; + } + } + if (m_image_type == 64) { + for (int i = 0; i < MaxLength; i++) { + if (vData_f[i] > iMax) + iMax = vData_f[i]; + if (vData_f[i] < iMin) + iMin = vData_f[i]; + } + } } // ------------------------------------------------------------------------- /** -* \brief Read GIPL header. -* -* \param myfile Input file -*/ + * \brief Read GIPL header. + * + * \param myfile Input file + */ // ------------------------------------------------------------------------- -void Image::ReadGiplHeader(std::fstream* myFile) -{ - int i; - for(i=0;i<4;i++) - myFile->read(reinterpret_cast < char * > (&m_dim[i]), sizeof(m_dim[i])); - myFile->read(reinterpret_cast < char * > (&m_image_type), sizeof(m_image_type)); - for(i=0;i<4;i++) - myFile->read(reinterpret_cast < char * > (&m_pixdim[i]), sizeof(m_pixdim[i])); - for(i=0;i<80;i++) - myFile->read(reinterpret_cast < char * > (&m_patDesc[i]), sizeof(m_patDesc[i])); - for(i=0;i<12;i++) - myFile->read(reinterpret_cast < char * > (&m_matrixElements[i]), sizeof(m_matrixElements[i])); - myFile->read(reinterpret_cast < char * > (&m_identifier), sizeof(m_identifier)); - for(i=0;i<28;i++) - myFile->read(reinterpret_cast < char * > (&m_spare[i]), sizeof(m_spare[i])); - myFile->read(reinterpret_cast < char * > (&m_orientationFlag), sizeof(m_orientationFlag)); - myFile->read(reinterpret_cast < char * > (&m_flag1), sizeof(m_flag1)); - myFile->read(reinterpret_cast < char * > (&m_min), sizeof(m_min)); - myFile->read(reinterpret_cast < char * > (&m_max), sizeof(m_max)); - for(i=0;i<4;i++) - myFile->read(reinterpret_cast < char * > (&m_origin[i]), sizeof(m_origin[i])); - myFile->read(reinterpret_cast < char * > (&m_pixval_offset), sizeof(m_pixval_offset)); - myFile->read(reinterpret_cast < char * > (&m_pixval_cal), sizeof(m_pixval_cal)); - myFile->read(reinterpret_cast < char * > (&m_user_def1), sizeof(m_user_def1)); - myFile->read(reinterpret_cast < char * > (&m_user_def2), sizeof(m_user_def2)); - myFile->read(reinterpret_cast < char * > (&m_magic_number), sizeof(m_magic_number)); - +void +Image::ReadGiplHeader(std::fstream* myFile) { + int i; + for (i = 0; i < 4; i++) + myFile->read(reinterpret_cast(&m_dim[i]), sizeof(m_dim[i])); + myFile->read(reinterpret_cast(&m_image_type), sizeof(m_image_type)); + for (i = 0; i < 4; i++) + myFile->read(reinterpret_cast(&m_pixdim[i]), sizeof(m_pixdim[i])); + for (i = 0; i < 80; i++) + myFile->read(reinterpret_cast(&m_patDesc[i]), sizeof(m_patDesc[i])); + for (i = 0; i < 12; i++) + myFile->read(reinterpret_cast(&m_matrixElements[i]), sizeof(m_matrixElements[i])); + myFile->read(reinterpret_cast(&m_identifier), sizeof(m_identifier)); + for (i = 0; i < 28; i++) + myFile->read(reinterpret_cast(&m_spare[i]), sizeof(m_spare[i])); + myFile->read(reinterpret_cast(&m_orientationFlag), sizeof(m_orientationFlag)); + myFile->read(reinterpret_cast(&m_flag1), sizeof(m_flag1)); + myFile->read(reinterpret_cast(&m_min), sizeof(m_min)); + myFile->read(reinterpret_cast(&m_max), sizeof(m_max)); + for (i = 0; i < 4; i++) + myFile->read(reinterpret_cast(&m_origin[i]), sizeof(m_origin[i])); + myFile->read(reinterpret_cast(&m_pixval_offset), sizeof(m_pixval_offset)); + myFile->read(reinterpret_cast(&m_pixval_cal), sizeof(m_pixval_cal)); + myFile->read(reinterpret_cast(&m_user_def1), sizeof(m_user_def1)); + myFile->read(reinterpret_cast(&m_user_def2), sizeof(m_user_def2)); + myFile->read(reinterpret_cast(&m_magic_number), sizeof(m_magic_number)); } // ------------------------------------------------------------------------- /** -* \brief Write image data to GIPL output file. -* -* \param filename Output filename -*/ + * \brief Write image data to GIPL output file. + * + * \param filename Output filename + */ // ------------------------------------------------------------------------- -void Image::GiplWrite(const char* filename) -{ - // Open file for writing - std::fstream myFile(filename, std::ios_base::out | std::ios_base::binary); - - // Require big endian format - ByteSwapHeader(); - - // Write gipl header - WriteGiplHeader(&myFile); - - // Go back to correct format - ByteSwapHeader(); - - // Check data type - switch(m_image_type) - { - case 15: // shorts (default) - - // Swap bytes of data vector before writing to file - for( int i = 0; i < MaxLength; i++) - ByteSwap(&vData[i]); - - // Read image data - myFile.write((char*)vData, sizeof(short)*MaxLength); - - // Swap hi lo bytes - for( int i = 0; i < MaxLength; i++) - ByteSwap(&vData[i]); - break; - - // Swap bytes back - for(int i = 0; i < MaxLength; i++) - ByteSwap(&vData[i]); - - case 64: // floats - - // Swap bytes of data vector before writing to file - for( int i = 0; i < MaxLength; i++) - ByteSwap(&vData_f[i]); - - // Read image data - myFile.write((char*)vData_f, sizeof(float)*MaxLength); - - // Swap hi lo bytes - for( int i = 0; i < MaxLength; i++) - ByteSwap(&vData_f[i]); - break; - - // Swap bytes back - for(int i = 0; i < MaxLength; i++) - ByteSwap(&vData_f[i]); - - default: - break; - } - - // Close file - myFile.close(); +void +Image::GiplWrite(const char* filename) { + // Open file for writing + std::fstream myFile(filename, std::ios_base::out | std::ios_base::binary); + + // Require big endian format + ByteSwapHeader(); + + // Write gipl header + WriteGiplHeader(&myFile); + + // Go back to correct format + ByteSwapHeader(); + + // Check data type + switch (m_image_type) { + case 15: // shorts (default) + + // Swap bytes of data vector before writing to file + for (int i = 0; i < MaxLength; i++) + ByteSwap(&vData[i]); + + // Read image data + myFile.write((char*)vData, sizeof(short) * MaxLength); + + // Swap hi lo bytes + for (int i = 0; i < MaxLength; i++) + ByteSwap(&vData[i]); + break; + + // Swap bytes back + for (int i = 0; i < MaxLength; i++) + ByteSwap(&vData[i]); + + case 64: // floats + + // Swap bytes of data vector before writing to file + for (int i = 0; i < MaxLength; i++) + ByteSwap(&vData_f[i]); + + // Read image data + myFile.write((char*)vData_f, sizeof(float) * MaxLength); + + // Swap hi lo bytes + for (int i = 0; i < MaxLength; i++) + ByteSwap(&vData_f[i]); + break; + + // Swap bytes back + for (int i = 0; i < MaxLength; i++) + ByteSwap(&vData_f[i]); + + default: + break; + } + + // Close file + myFile.close(); } // ------------------------------------------------------------------------- /** -* \brief Write GIPL header to output file. -* -* \param myfile Output file -*/ + * \brief Write GIPL header to output file. + * + * \param myfile Output file + */ // ------------------------------------------------------------------------- -void Image::WriteGiplHeader(fstream* myFile) -{ - int i; - for(i=0;i<4;i++) - myFile->write(reinterpret_cast < char * > (&m_dim[i]), sizeof(m_dim[i])); - myFile->write(reinterpret_cast < char * > (&m_image_type), sizeof(m_image_type)); - for(i=0;i<4;i++) - myFile->write(reinterpret_cast < char * > (&m_pixdim[i]), sizeof(m_pixdim[i])); - for(i=0;i<80;i++) - myFile->write(reinterpret_cast < char * > (&m_patDesc[i]), sizeof(m_patDesc[i])); - for(i=0;i<12;i++) - myFile->write(reinterpret_cast < char * > (&m_matrixElements[i]), sizeof(m_matrixElements[i])); - myFile->write(reinterpret_cast < char * > (&m_identifier), sizeof(m_identifier)); - for(i=0;i<28;i++) - myFile->write(reinterpret_cast < char * > (&m_spare[i]), sizeof(m_spare[i])); - myFile->write(reinterpret_cast < char * > (&m_orientationFlag), sizeof(m_orientationFlag)); - myFile->write(reinterpret_cast < char * > (&m_flag1), sizeof(m_flag1)); - myFile->write(reinterpret_cast < char * > (&m_min), sizeof(m_min)); - myFile->write(reinterpret_cast < char * > (&m_max), sizeof(m_max)); - for(i=0;i<4;i++) - myFile->write(reinterpret_cast < char * > (&m_origin[i]), sizeof(m_origin[i])); - myFile->write(reinterpret_cast < char * > (&m_pixval_offset), sizeof(m_pixval_offset)); - myFile->write(reinterpret_cast < char * > (&m_pixval_cal), sizeof(m_pixval_cal)); - myFile->write(reinterpret_cast < char * > (&m_user_def1), sizeof(m_user_def1)); - myFile->write(reinterpret_cast < char * > (&m_user_def2), sizeof(m_user_def2)); - myFile->write(reinterpret_cast < char * > (&m_magic_number), sizeof(m_magic_number)); +void +Image::WriteGiplHeader(fstream* myFile) { + int i; + for (i = 0; i < 4; i++) + myFile->write(reinterpret_cast(&m_dim[i]), sizeof(m_dim[i])); + myFile->write(reinterpret_cast(&m_image_type), sizeof(m_image_type)); + for (i = 0; i < 4; i++) + myFile->write(reinterpret_cast(&m_pixdim[i]), sizeof(m_pixdim[i])); + for (i = 0; i < 80; i++) + myFile->write(reinterpret_cast(&m_patDesc[i]), sizeof(m_patDesc[i])); + for (i = 0; i < 12; i++) + myFile->write(reinterpret_cast(&m_matrixElements[i]), sizeof(m_matrixElements[i])); + myFile->write(reinterpret_cast(&m_identifier), sizeof(m_identifier)); + for (i = 0; i < 28; i++) + myFile->write(reinterpret_cast(&m_spare[i]), sizeof(m_spare[i])); + myFile->write(reinterpret_cast(&m_orientationFlag), sizeof(m_orientationFlag)); + myFile->write(reinterpret_cast(&m_flag1), sizeof(m_flag1)); + myFile->write(reinterpret_cast(&m_min), sizeof(m_min)); + myFile->write(reinterpret_cast(&m_max), sizeof(m_max)); + for (i = 0; i < 4; i++) + myFile->write(reinterpret_cast(&m_origin[i]), sizeof(m_origin[i])); + myFile->write(reinterpret_cast(&m_pixval_offset), sizeof(m_pixval_offset)); + myFile->write(reinterpret_cast(&m_pixval_cal), sizeof(m_pixval_cal)); + myFile->write(reinterpret_cast(&m_user_def1), sizeof(m_user_def1)); + myFile->write(reinterpret_cast(&m_user_def2), sizeof(m_user_def2)); + myFile->write(reinterpret_cast(&m_magic_number), sizeof(m_magic_number)); } // ------------------------------------------------------------------------- /** -* \brief Swap bytes (little/big endian conversion). -*/ + * \brief Swap bytes (little/big endian conversion). + */ // ------------------------------------------------------------------------- -void Image::ByteSwapHeader() // for PC little endian -{ - int i; - for(i=0;i<4;i++) ByteSwap(&(m_dim[i])); +void +Image::ByteSwapHeader() // for PC little endian +{ + int i; + for (i = 0; i < 4; i++) + ByteSwap(&(m_dim[i])); - ByteSwap(&(m_image_type)); + ByteSwap(&(m_image_type)); - for(i=0;i<4;i++) ByteSwap(&(m_pixdim[i])); + for (i = 0; i < 4; i++) + ByteSwap(&(m_pixdim[i])); - for(i=0;i<12;i++) ByteSwap(&(m_matrixElements[i])); + for (i = 0; i < 12; i++) + ByteSwap(&(m_matrixElements[i])); - ByteSwap(&(m_min)); - ByteSwap(&(m_max)); + ByteSwap(&(m_min)); + ByteSwap(&(m_max)); - for(i=0;i<4;i++) ByteSwap(&(m_origin[i])); - - ByteSwap(&(m_pixval_offset)); - ByteSwap(&(m_pixval_cal)); - ByteSwap(&(m_user_def1)); - ByteSwap(&(m_user_def2)); - ByteSwap((int*)&(m_magic_number)); + for (i = 0; i < 4; i++) + ByteSwap(&(m_origin[i])); - return; + ByteSwap(&(m_pixval_offset)); + ByteSwap(&(m_pixval_cal)); + ByteSwap(&(m_user_def1)); + ByteSwap(&(m_user_def2)); + ByteSwap((int*)&(m_magic_number)); + + return; } // ------------------------------------------------------------------------- /** -* \brief Initialize zero image -* -* \param Input Input image -*/ + * \brief Initialize zero image + * + * \param Input Input image + */ // ------------------------------------------------------------------------- -void Image::Zeros(Image* Input, short iType) -{ - // Initialize image with dimensions - this->Initialize(Input, iType); - - if (iType == _SHORT) - { - // Initialize with zeros - for (int i = 0; i < this->MaxLength; i++) - this->vData[i] = 0; - - // Update min max values - //this->GetMinMaxValue(); - } - - if (iType == _FLOAT) - { - // Initialize with zeros - for (int i = 0; i < this->MaxLength; i++) - this->vData_f[i] = 0; - } - - iMin = 0; - iMax = 0; +void +Image::Zeros(Image* Input, short iType) { + // Initialize image with dimensions + this->Initialize(Input, iType); + + if (iType == _SHORT) { + // Initialize with zeros + for (int i = 0; i < this->MaxLength; i++) + this->vData[i] = 0; + + // Update min max values + // this->GetMinMaxValue(); + } + + if (iType == _FLOAT) { + // Initialize with zeros + for (int i = 0; i < this->MaxLength; i++) + this->vData_f[i] = 0; + } + + iMin = 0; + iMax = 0; } // ------------------------------------------------------------------------- /** -* \brief Initialize zero image -* -* \param Input Input image -*/ + * \brief Initialize zero image + * + * \param Input Input image + */ // ------------------------------------------------------------------------- -void Image::Zeros() -{ - // Set image to 0 - if (this->m_image_type == 15) - { - // Initialize with zeros - for (int i = 0; i < this->MaxLength; i++) - this->vData[i] = 0; - - // Update min max values - //this->GetMinMaxValue(); - } - - if (this->m_image_type == 64) - { - // Initialize with zeros - for (int i = 0; i < this->MaxLength; i++) - this->vData_f[i] = 0; - } - - iMin = 0; - iMax = 0; +void +Image::Zeros() { + // Set image to 0 + if (this->m_image_type == 15) { + // Initialize with zeros + for (int i = 0; i < this->MaxLength; i++) + this->vData[i] = 0; + + // Update min max values + // this->GetMinMaxValue(); + } + + if (this->m_image_type == 64) { + // Initialize with zeros + for (int i = 0; i < this->MaxLength; i++) + this->vData_f[i] = 0; + } + + iMin = 0; + iMax = 0; } // ------------------------------------------------------------------------- /** -* \brief Initialize one image -* -* \param Input Input image -*/ + * \brief Initialize one image + * + * \param Input Input image + */ // ------------------------------------------------------------------------- -void Image::Ones(Image* Input, short iType) -{ - // Initialize image with dimensions - this->Initialize(Input, iType); - - if (iType == _SHORT) - { - // Initialize with zeros - for (int i = 0; i < this->MaxLength; i++) - this->vData[i] = 1; - } - - if (iType == _FLOAT) - { - // Initialize with zeros - for (int i = 0; i < this->MaxLength; i++) - this->vData_f[i] = 1; - } - - iMax = 1; - iMin = 1; +void +Image::Ones(Image* Input, short iType) { + // Initialize image with dimensions + this->Initialize(Input, iType); + + if (iType == _SHORT) { + // Initialize with zeros + for (int i = 0; i < this->MaxLength; i++) + this->vData[i] = 1; + } + + if (iType == _FLOAT) { + // Initialize with zeros + for (int i = 0; i < this->MaxLength; i++) + this->vData_f[i] = 1; + } + + iMax = 1; + iMin = 1; } // ------------------------------------------------------------------------- /** -* \brief Initialize image data by with the dimension of the input image -* -* \param Input Input image -*/ + * \brief Initialize image data by with the dimension of the input image + * + * \param Input Input image + */ // ------------------------------------------------------------------------- -void Image::Initialize(Image* Input, short iType) -{ - // Set new data properties - for (int i = 0; i < 4; i++) - { - this->m_dim[i] = Input->m_dim[i]; - this->m_pixdim[i] = Input->m_pixdim[i]; - this->vCenter[i] = Input->vCenter[i]; - this->m_origin[i] = Input->m_origin[i]; - } - - this->m_orientationFlag = Input->m_orientationFlag; - this->m_flag1 = Input->m_flag1; - this->m_min = Input->m_min; - this->m_max = Input->m_max; - this->m_pixval_offset = Input->m_pixval_offset; - this->m_pixval_cal = Input->m_pixval_cal; - this->m_user_def1 = Input->m_user_def1; - this->m_user_def2 = Input->m_user_def2; - this->m_magic_number = Input->m_magic_number; - for (int i = 0; i < 2; i++) - this->ImageOffset[i] = Input->ImageOffset[i]; // XLen and XLen*YLen - this->MaxLength = Input->MaxLength; - - // Initialize data vactor - if (iType == _SHORT) - { - // Delete old vector and initialize data vector - if (this->vData) - delete[] this->vData; - - this->m_image_type = 15; - this->vData = new short[Input->MaxLength]; - } - - if (iType == _FLOAT) - { - // Delete old vector and initialize data vector - if (this->vData_f) - delete[] this->vData_f; - - this->m_image_type = 64; - this->vData_f = new float[Input->MaxLength]; - } +void +Image::Initialize(Image* Input, short iType) { + // Set new data properties + for (int i = 0; i < 4; i++) { + this->m_dim[i] = Input->m_dim[i]; + this->m_pixdim[i] = Input->m_pixdim[i]; + this->vCenter[i] = Input->vCenter[i]; + this->m_origin[i] = Input->m_origin[i]; + } + + this->m_orientationFlag = Input->m_orientationFlag; + this->m_flag1 = Input->m_flag1; + this->m_min = Input->m_min; + this->m_max = Input->m_max; + this->m_pixval_offset = Input->m_pixval_offset; + this->m_pixval_cal = Input->m_pixval_cal; + this->m_user_def1 = Input->m_user_def1; + this->m_user_def2 = Input->m_user_def2; + this->m_magic_number = Input->m_magic_number; + for (int i = 0; i < 2; i++) + this->ImageOffset[i] = Input->ImageOffset[i]; // XLen and XLen*YLen + this->MaxLength = Input->MaxLength; + + // Initialize data vactor + if (iType == _SHORT) { + // Delete old vector and initialize data vector + if (this->vData) + delete[] this->vData; + + this->m_image_type = 15; + this->vData = new short[Input->MaxLength]; + } + + if (iType == _FLOAT) { + // Delete old vector and initialize data vector + if (this->vData_f) + delete[] this->vData_f; + + this->m_image_type = 64; + this->vData_f = new float[Input->MaxLength]; + } } // ------------------------------------------------------------------------- /** -* \brief Initialize image data by with the dimension of the input image -* -* \param Input Input image -*/ + * \brief Initialize image data by with the dimension of the input image + * + * \param Input Input image + */ // ------------------------------------------------------------------------- -void Image::Initialize(Image* Input) -{ - // Set new data properties - for (int i = 0; i < 4; i++) - { - this->m_dim[i] = Input->m_dim[i]; - this->m_pixdim[i] = Input->m_pixdim[i]; - this->vCenter[i] = Input->vCenter[i]; - this->m_origin[i] = Input->m_origin[i]; - } - - this->m_orientationFlag = Input->m_orientationFlag; - this->m_flag1 = Input->m_flag1; - this->m_min = Input->m_min; - this->m_max = Input->m_max; - this->m_pixval_offset = Input->m_pixval_offset; - this->m_pixval_cal = Input->m_pixval_cal; - this->m_user_def1 = Input->m_user_def1; - this->m_user_def2 = Input->m_user_def2; - this->m_magic_number = Input->m_magic_number; - for (int i = 0; i < 2; i++) - this->ImageOffset[i] = Input->ImageOffset[i]; // XLen and XLen*YLen - this->MaxLength = Input->MaxLength; - - // Initialize data vactor - if (this->vData) - delete[] this->vData; - - if (this->vData_f) - delete[] this->vData_f; +void +Image::Initialize(Image* Input) { + // Set new data properties + for (int i = 0; i < 4; i++) { + this->m_dim[i] = Input->m_dim[i]; + this->m_pixdim[i] = Input->m_pixdim[i]; + this->vCenter[i] = Input->vCenter[i]; + this->m_origin[i] = Input->m_origin[i]; + } + + this->m_orientationFlag = Input->m_orientationFlag; + this->m_flag1 = Input->m_flag1; + this->m_min = Input->m_min; + this->m_max = Input->m_max; + this->m_pixval_offset = Input->m_pixval_offset; + this->m_pixval_cal = Input->m_pixval_cal; + this->m_user_def1 = Input->m_user_def1; + this->m_user_def2 = Input->m_user_def2; + this->m_magic_number = Input->m_magic_number; + for (int i = 0; i < 2; i++) + this->ImageOffset[i] = Input->ImageOffset[i]; // XLen and XLen*YLen + this->MaxLength = Input->MaxLength; + + // Initialize data vactor + if (this->vData) + delete[] this->vData; + + if (this->vData_f) + delete[] this->vData_f; } // ------------------------------------------------------------------------- /** -* \brief Copy image -* -* \param Input Input image -*/ + * \brief Copy image + * + * \param Input Input image + */ // ------------------------------------------------------------------------- -void Image::Copy(Image* Input, short iType) -{ - // Initialize image with dimensions - this->Initialize(Input, iType); - - if (iType == _SHORT) - { - // Initialize with zeros - for (int i = 0; i < Input->MaxLength; i++) - this->vData[i] = Input->vData[i]; - - // Update min max values - this->GetMinMaxValue(); - } - - if (iType == _FLOAT) - { - // Initialize with zeros - for (int i = 0; i < Input->MaxLength; i++) - this->vData_f[i] = Input->vData_f[i]; - } +void +Image::Copy(Image* Input, short iType) { + // Initialize image with dimensions + this->Initialize(Input, iType); + + if (iType == _SHORT) { + // Initialize with zeros + for (int i = 0; i < Input->MaxLength; i++) + this->vData[i] = Input->vData[i]; + + // Update min max values + this->GetMinMaxValue(); + } + + if (iType == _FLOAT) { + // Initialize with zeros + for (int i = 0; i < Input->MaxLength; i++) + this->vData_f[i] = Input->vData_f[i]; + } } // ------------------------------------------------------------------------- // Byte swap functions // ------------------------------------------------------------------------- -void Image::ByteSwap(int *i) // for PC little endian -{ +void +Image::ByteSwap(int* i) // for PC little endian +{ typedef struct { unsigned char byte1; unsigned char byte2; @@ -689,13 +666,14 @@ void Image::ByteSwap(int *i) // for PC little endian intUS.bytes.byte2 = intU.bytes.byte3; intUS.bytes.byte3 = intU.bytes.byte2; intUS.bytes.byte4 = intU.bytes.byte1; - + *i = intUS.integer; return; } -void Image::ByteSwap(short *s) // for PC little endian -{ +void +Image::ByteSwap(short* s) // for PC little endian +{ typedef struct { unsigned char byte1; unsigned char byte2; @@ -716,8 +694,9 @@ void Image::ByteSwap(short *s) // for PC little endian return; } -void Image::ByteSwap(float *f) // for PC little endian -{ +void +Image::ByteSwap(float* f) // for PC little endian +{ typedef struct { unsigned char byte1; unsigned char byte2; @@ -742,8 +721,9 @@ void Image::ByteSwap(float *f) // for PC little endian return; } -void Image::ByteSwap(double *d) // for PC little endian -{ +void +Image::ByteSwap(double* d) // for PC little endian +{ typedef struct { unsigned char byte1; unsigned char byte2; diff --git a/src/IO/IO_registries.cxx b/src/IO/IO_registries.cxx index 91bb64bb84..4111e8afb9 100644 --- a/src/IO/IO_registries.cxx +++ b/src/IO/IO_registries.cxx @@ -3,7 +3,7 @@ Copyright (C) 2012, Kris Thielemans Copyright (C) 2013, Institute for Bioengineering of Catalonia Copyright (C) 2013, University College London - + This file is part of STIR. This file is free software; you can redistribute it and/or modify @@ -41,42 +41,41 @@ #include "stir/IO/MultiParametricDiscretisedDensityInputFileFormat.h" #include "stir/IO/MultiParametricDiscretisedDensityOutputFileFormat.h" #ifdef HAVE_LLN_MATRIX -#include "stir/IO/ECAT6OutputFileFormat.h" -#include "stir/IO/ECAT7OutputFileFormat.h" -#include "stir/IO/ECAT7DynamicDiscretisedDensityOutputFileFormat.h" -#include "stir/IO/ECAT7ParametricDensityOutputFileFormat.h" -#include "stir/IO/ECAT7DynamicDiscretisedDensityInputFileFormat.h" +# include "stir/IO/ECAT6OutputFileFormat.h" +# include "stir/IO/ECAT7OutputFileFormat.h" +# include "stir/IO/ECAT7DynamicDiscretisedDensityOutputFileFormat.h" +# include "stir/IO/ECAT7ParametricDensityOutputFileFormat.h" +# include "stir/IO/ECAT7DynamicDiscretisedDensityInputFileFormat.h" #endif - #if 1 -#include "stir/IO/InputFileFormatRegistry.h" -#include "stir/IO/InterfileImageInputFileFormat.h" -#ifdef HAVE_LLN_MATRIX -#include "stir/IO/ECAT6ImageInputFileFormat.h" -#include "stir/IO/ECAT7ImageInputFileFormat.h" -#include "stir/IO/ECAT966ListmodeInputFileFormat.h" -#include "stir/IO/ECAT962ListmodeInputFileFormat.h" -#endif +# include "stir/IO/InputFileFormatRegistry.h" +# include "stir/IO/InterfileImageInputFileFormat.h" +# ifdef HAVE_LLN_MATRIX +# include "stir/IO/ECAT6ImageInputFileFormat.h" +# include "stir/IO/ECAT7ImageInputFileFormat.h" +# include "stir/IO/ECAT966ListmodeInputFileFormat.h" +# include "stir/IO/ECAT962ListmodeInputFileFormat.h" +# endif #endif #include "stir/IO/ECAT8_32bitListmodeInputFileFormat.h" #ifdef HAVE_HDF5 -#include "stir/IO/GEHDF5ListmodeInputFileFormat.h" +# include "stir/IO/GEHDF5ListmodeInputFileFormat.h" #endif //! Addition for SAFIR listmode input file format #include "stir/IO/SAFIRCListmodeInputFileFormat.h" //! Addition for ROOT support - Nikos Efthimiou #ifdef HAVE_CERN_ROOT -#include "stir/IO/ROOTListmodeInputFileFormat.h" -#include "stir/IO/InputStreamFromROOTFileForCylindricalPET.h" -#include "stir/IO/InputStreamFromROOTFileForECATPET.h" +# include "stir/IO/ROOTListmodeInputFileFormat.h" +# include "stir/IO/InputStreamFromROOTFileForCylindricalPET.h" +# include "stir/IO/InputStreamFromROOTFileForECATPET.h" #endif #ifdef HAVE_ITK -#include "stir/IO/ITKOutputFileFormat.h" -#include "stir/IO/ITKImageInputFileFormat.h" +# include "stir/IO/ITKOutputFileFormat.h" +# include "stir/IO/ITKImageInputFileFormat.h" #endif START_NAMESPACE_STIR @@ -93,7 +92,6 @@ static MultiParametricDiscretisedDensityOutputFileFormat LMdummySAFIR(4); - //! //! \brief LMdummyROOT //! \author Nikos Efthimiou @@ -117,12 +115,11 @@ END_NAMESPACE_ECAT7 END_NAMESPACE_ECAT #endif - static RegisterInputFileFormat idummy0(0); #ifdef HAVE_LLN_MATRIX static RegisterInputFileFormat idummy2(4); -// ECAT6 very low priority it doesn't have a signature +// ECAT6 very low priority it doesn't have a signature static RegisterInputFileFormat idummy4(100000); static RegisterInputFileFormat dynidummy(0); @@ -130,15 +127,14 @@ static RegisterInputFileFormat > > idummy6(10000); -static RegisterInputFileFormat > > > idummy7(10000); +static RegisterInputFileFormat>> idummy6(10000); +static RegisterInputFileFormat>>> idummy7(10000); #endif static RegisterInputFileFormat dyndummy_intf(1); static RegisterInputFileFormat paradummy_intf(1); static RegisterInputFileFormat dynim_dummy_multi(1); static RegisterInputFileFormat parim_dummy_multi(1); - /*************************** listmode data **********************/ #ifdef HAVE_LLN_MATRIX static RegisterInputFileFormat LMdummyECAT966(4); diff --git a/src/IO/ITKImageInputFileFormat.cxx b/src/IO/ITKImageInputFileFormat.cxx index 6cf84ffb52..ba7bf5c29f 100644 --- a/src/IO/ITKImageInputFileFormat.cxx +++ b/src/IO/ITKImageInputFileFormat.cxx @@ -53,73 +53,55 @@ START_NAMESPACE_STIR */ -typedef itk::Image ITKImageSingle; -typedef itk::VectorImage ITKImageMulti; -typedef DiscretisedDensity<3, float> STIRImageSingle; -typedef VoxelsOnCartesianGrid STIRImageSingleConcrete; -typedef DiscretisedDensity<3, CartesianCoordinate3D > STIRImageMulti; -typedef VoxelsOnCartesianGrid > STIRImageMultiConcrete; -typedef itk::MetaDataObject< std::string > MetaDataStringType; - -template -static -STIRImageType * -read_file_itk(const std::string &filename); +typedef itk::Image ITKImageSingle; +typedef itk::VectorImage ITKImageMulti; +typedef DiscretisedDensity<3, float> STIRImageSingle; +typedef VoxelsOnCartesianGrid STIRImageSingleConcrete; +typedef DiscretisedDensity<3, CartesianCoordinate3D> STIRImageMulti; +typedef VoxelsOnCartesianGrid> STIRImageMultiConcrete; +typedef itk::MetaDataObject MetaDataStringType; template -bool -ITKImageInputFileFormat::actual_can_read(const FileSignature& signature, - std::istream& input) const -{ +static STIRImageType* read_file_itk(const std::string& filename); + +template +bool +ITKImageInputFileFormat::actual_can_read(const FileSignature& signature, std::istream& input) const { return false; } template bool -ITKImageInputFileFormat::can_read(const FileSignature& signature, - std::istream& input) const -{ +ITKImageInputFileFormat::can_read(const FileSignature& signature, std::istream& input) const { return this->actual_can_read(signature, input); } template -bool -ITKImageInputFileFormat::can_read(const FileSignature& /*signature*/, - const std::string& filename) const -{ +bool +ITKImageInputFileFormat::can_read(const FileSignature& /*signature*/, const std::string& filename) const { typedef itk::ImageFileReader ReaderType; ReaderType::Pointer reader = ReaderType::New(); reader->SetFileName(filename); - try - { - reader->Update(); - return true; - } - catch( itk::ExceptionObject & /*err*/ ) - { - - return false; - } + try { + reader->Update(); + return true; + } catch (itk::ExceptionObject& /*err*/) { + + return false; + } } template unique_ptr -ITKImageInputFileFormat::read_from_file(std::istream& input) const -{ - error("read_from_file for ITK with istream not implemented %s:%d. Sorry", - __FILE__, __LINE__); - return - unique_ptr(); +ITKImageInputFileFormat::read_from_file(std::istream& input) const { + error("read_from_file for ITK with istream not implemented %s:%d. Sorry", __FILE__, __LINE__); + return unique_ptr(); } -template +template unique_ptr -ITKImageInputFileFormat:: -read_from_file(const std::string& filename) const -{ - return - unique_ptr - (read_file_itk< STIRImageType >(filename)); +ITKImageInputFileFormat::read_from_file(const std::string& filename) const { + return unique_ptr(read_file_itk(filename)); } /* Convert ITK (LPS) coordinates into STIR physical coordinates and @@ -129,32 +111,25 @@ read_from_file(const std::string& filename) const interpreted as being displacement vectors and hence the change of origin is ignored. */ -template -static inline -CartesianCoordinate3D -ITK_coordinates_to_STIR_physical_coordinates -(const ITKPointType &itk_coord, - const STIRImageType &stir_image, bool is_relative_coordinate=false) -{ +template +static inline CartesianCoordinate3D +ITK_coordinates_to_STIR_physical_coordinates(const ITKPointType& itk_coord, const STIRImageType& stir_image, + bool is_relative_coordinate = false) { // find STIR origin // Note: need to use - for z-coordinate because of different axis conventions - CartesianCoordinate3D stir_coord - = stir_image.get_physical_coordinates_for_LPS_coordinates - (CartesianCoordinate3D(static_cast(itk_coord[2]), - static_cast(itk_coord[1]), - static_cast(itk_coord[0]))); + CartesianCoordinate3D stir_coord = stir_image.get_physical_coordinates_for_LPS_coordinates(CartesianCoordinate3D( + static_cast(itk_coord[2]), static_cast(itk_coord[1]), static_cast(itk_coord[0]))); // The following is not required for displacement vectors, such as a displacement field, as // the coordinates are relative. - if (!is_relative_coordinate) - { + if (!is_relative_coordinate) { CartesianCoordinate3D stir_origin_index(0, 0, 0); // assuming we previously oriented the ITK image, min_indices is the // index where ITK origin points to in physical space - const CartesianCoordinate3D stir_origin_wrt_itk_origin - = stir_image.get_physical_coordinates_for_indices(stir_origin_index) - - stir_image.get_physical_coordinates_for_indices(stir_image.get_min_indices()); + const CartesianCoordinate3D stir_origin_wrt_itk_origin = + stir_image.get_physical_coordinates_for_indices(stir_origin_index) - + stir_image.get_physical_coordinates_for_indices(stir_image.get_min_indices()); stir_coord += stir_origin_wrt_itk_origin; } @@ -163,70 +138,52 @@ ITK_coordinates_to_STIR_physical_coordinates } /* Convert an ITK Pixel (i.e., float) to a STIR Pixel. */ -template -static inline -STIRPixelType -ITK_pixel_to_STIR_pixel(ITKPixelType itk_pixel, - const STIRImageType &stir_image, - bool) -{ +template +static inline STIRPixelType +ITK_pixel_to_STIR_pixel(ITKPixelType itk_pixel, const STIRImageType& stir_image, bool) { return static_cast(itk_pixel); } /* Specialisation if the pixel is a vector and we want a multi-image */ -template<> -inline -typename STIRImageMultiConcrete::full_value_type -ITK_pixel_to_STIR_pixel(typename ITKImageMulti::PixelType itk_pixel, - const STIRImageMultiConcrete &stir_image, - bool is_displacement_field) -{ +template <> +inline typename STIRImageMultiConcrete::full_value_type +ITK_pixel_to_STIR_pixel(typename ITKImageMulti::PixelType itk_pixel, const STIRImageMultiConcrete& stir_image, + bool is_displacement_field) { // ITK VariableLengthVector to ITK FixedArray // We know it is length 3 assert(itk_pixel.GetSize() == 3); // TODO: currently this is only for deformation/displacement images // However, dynamic images may be other lengths. typename ITKImageMulti::PointType itk_coord; - for (unsigned int i=0; i<3; ++i) + for (unsigned int i = 0; i < 3; ++i) itk_coord[i] = itk_pixel[i]; - return ITK_coordinates_to_STIR_physical_coordinates - (itk_coord, stir_image, is_displacement_field); + return ITK_coordinates_to_STIR_physical_coordinates(itk_coord, stir_image, is_displacement_field); } /* Calculate the STIR index range from an ITK image. */ -template -static inline -IndexRange<3> -calc_stir_index_range(const ITKImagePtrType itk_image) -{ +template +static inline IndexRange<3> +calc_stir_index_range(const ITKImagePtrType itk_image) { // find index range in usual STIR convention const int z_size = itk_image->GetLargestPossibleRegion().GetSize()[2]; const int y_size = itk_image->GetLargestPossibleRegion().GetSize()[1]; const int x_size = itk_image->GetLargestPossibleRegion().GetSize()[0]; - const BasicCoordinate<3, int> min_indices - = BasicCoordinate<3,int>(make_coordinate(0, -y_size/2, -x_size/2)); - const BasicCoordinate<3, int> max_indices - = min_indices + make_coordinate(z_size, y_size, x_size) - 1; + const BasicCoordinate<3, int> min_indices = BasicCoordinate<3, int>(make_coordinate(0, -y_size / 2, -x_size / 2)); + const BasicCoordinate<3, int> max_indices = min_indices + make_coordinate(z_size, y_size, x_size) - 1; return IndexRange<3>(min_indices, max_indices); } /* Calculate the STIR origin for a given voxel_size and index_range from an ITK image. */ -template -static inline -const CartesianCoordinate3D -calc_stir_origin(CartesianCoordinate3D voxel_size, - IndexRange<3> index_range, - const ITKImagePtrType itk_image) -{ +template +static inline const CartesianCoordinate3D +calc_stir_origin(CartesianCoordinate3D voxel_size, IndexRange<3> index_range, const ITKImagePtrType itk_image) { const CartesianCoordinate3D stir_origin_index(0, 0, 0); // dummy image that has minumum to be able to find ITK -> STIR origin vector - const VoxelsOnCartesianGrid dummy_image - (index_range, stir_origin_index, voxel_size); + const VoxelsOnCartesianGrid dummy_image(index_range, stir_origin_index, voxel_size); - return ITK_coordinates_to_STIR_physical_coordinates - (itk_image->GetOrigin(), dummy_image); + return ITK_coordinates_to_STIR_physical_coordinates(itk_image->GetOrigin(), dummy_image); } /* Constructs an exam info object from an ITK meta data dictionary. @@ -241,11 +198,9 @@ calc_stir_origin(CartesianCoordinate3D voxel_size, \todo This will only work for DICOM meta-data. Other fileformats store meta-data with different names. */ -static -shared_ptr -construct_exam_info_from_metadata_dictionary(itk::MetaDataDictionary dictionary) -{ - shared_ptr exam_info_sptr (new ExamInfo()); +static shared_ptr +construct_exam_info_from_metadata_dictionary(itk::MetaDataDictionary dictionary) { + shared_ptr exam_info_sptr(new ExamInfo()); #if 0 //example data to read @@ -278,26 +233,24 @@ construct_exam_info_from_metadata_dictionary(itk::MetaDataDictionary dictionary) series_datetime = DICOM_date_time_to_DT(series_date, series_time, TimezoneOffsetFromUTC); } itk::ExposeMetaData(dictionary, "0008|002a", acq_datetime); - if (acq_datetime.empty()) - { - std::string acq_date, acq_time; - itk::ExposeMetaData(dictionary, "0008|0022", acq_date); - itk::ExposeMetaData(dictionary, "0008|0032", acq_time); - if (!acq_date.empty() && !acq_time.empty()) - acq_datetime = DICOM_date_time_to_DT(acq_date, acq_time, TimezoneOffsetFromUTC); - } + if (acq_datetime.empty()) { + std::string acq_date, acq_time; + itk::ExposeMetaData(dictionary, "0008|0022", acq_date); + itk::ExposeMetaData(dictionary, "0008|0032", acq_time); + if (!acq_date.empty() && !acq_time.empty()) + acq_datetime = DICOM_date_time_to_DT(acq_date, acq_time, TimezoneOffsetFromUTC); + } itk::ExposeMetaData(dictionary, "0018|1242", actual_frame_duration); - if (!series_datetime.empty() && !acq_datetime.empty() && !actual_frame_duration.empty()) - { - std::vector start_times(1), durations(1); - start_times[0] = DICOM_datetime_to_secs_since_Unix_epoch(series_datetime, false) - DICOM_datetime_to_secs_since_Unix_epoch(acq_datetime, false); - durations[0] = boost::lexical_cast(actual_frame_duration)/1000.; - exam_info_sptr->set_time_frame_definitions(TimeFrameDefinitions(start_times, durations)); - } - if (!series_datetime.empty()) - { - exam_info_sptr->start_time_in_secs_since_1970 = DICOM_datetime_to_secs_since_Unix_epoch(series_datetime); - } + if (!series_datetime.empty() && !acq_datetime.empty() && !actual_frame_duration.empty()) { + std::vector start_times(1), durations(1); + start_times[0] = DICOM_datetime_to_secs_since_Unix_epoch(series_datetime, false) - + DICOM_datetime_to_secs_since_Unix_epoch(acq_datetime, false); + durations[0] = boost::lexical_cast(actual_frame_duration) / 1000.; + exam_info_sptr->set_time_frame_definitions(TimeFrameDefinitions(start_times, durations)); + } + if (!series_datetime.empty()) { + exam_info_sptr->start_time_in_secs_since_1970 = DICOM_datetime_to_secs_since_Unix_epoch(series_datetime); + } } #if 0 @@ -313,14 +266,11 @@ construct_exam_info_from_metadata_dictionary(itk::MetaDataDictionary dictionary) itk::ExposeMetaData(dictionary, "0018|5100", patient_position_str); // Now patient_positon_str is empty or the value, but is it a valid value? // If so, update patient_position - for (unsigned int position_idx = 0; - (position_idx < PatientPosition::unknown_position) - && (patient_position.get_position() == PatientPosition::unknown_position); + for (unsigned int position_idx = 0; (position_idx < PatientPosition::unknown_position) && + (patient_position.get_position() == PatientPosition::unknown_position); ++position_idx) { - PatientPosition possible_position - (static_cast(position_idx)); - if (patient_position_str.find(possible_position.get_position_as_string()) - != std::string::npos) { + PatientPosition possible_position(static_cast(position_idx)); + if (patient_position_str.find(possible_position.get_position_as_string()) != std::string::npos) { patient_position = possible_position; } } @@ -338,26 +288,20 @@ construct_exam_info_from_metadata_dictionary(itk::MetaDataDictionary dictionary) This method expects that itk_image is already oriented to be consistent with STIR x, y, z axes. */ -template -static inline -STIRImageType* -construct_empty_stir_image(const ITKImagePtrType itk_image, - shared_ptr exam_info_sptr) -{ +template +static inline STIRImageType* +construct_empty_stir_image(const ITKImagePtrType itk_image, shared_ptr exam_info_sptr) { // find voxel size - const CartesianCoordinate3D voxel_size - (static_cast(itk_image->GetSpacing()[2]), - static_cast(itk_image->GetSpacing()[1]), - static_cast(itk_image->GetSpacing()[0])); + const CartesianCoordinate3D voxel_size(static_cast(itk_image->GetSpacing()[2]), + static_cast(itk_image->GetSpacing()[1]), + static_cast(itk_image->GetSpacing()[0])); // find info STIR image geometrical metadata const IndexRange<3> index_range = calc_stir_index_range(itk_image); - const CartesianCoordinate3D stir_origin = calc_stir_origin - (voxel_size, index_range, itk_image); + const CartesianCoordinate3D stir_origin = calc_stir_origin(voxel_size, index_range, itk_image); // create STIR image - STIRImageType* image_ptr = new STIRImageType - (exam_info_sptr, index_range, stir_origin, voxel_size); + STIRImageType* image_ptr = new STIRImageType(exam_info_sptr, index_range, stir_origin, voxel_size); return image_ptr; } @@ -365,29 +309,24 @@ construct_empty_stir_image(const ITKImagePtrType itk_image, This method expects that itk_image is already oriented to be consistent with STIR x, y, z axes. */ -template -static inline -void copy_ITK_data_to_STIR_image(const typename ITKImageType::Pointer itk_image, - STIRImageType& stir_image, - bool is_displacement_field) -{ +template +static inline void +copy_ITK_data_to_STIR_image(const typename ITKImageType::Pointer itk_image, STIRImageType& stir_image, + bool is_displacement_field) { typename STIRImageType::full_iterator stir_iter = stir_image.begin_all(); typedef itk::ImageRegionConstIterator IteratorType; - IteratorType it (itk_image, itk_image->GetLargestPossibleRegion()); - for (it.GoToBegin(); !it.IsAtEnd(); ++it, ++stir_iter) - { - *stir_iter = ITK_pixel_to_STIR_pixel - - (it.Get(), stir_image, is_displacement_field); + IteratorType it(itk_image, itk_image->GetLargestPossibleRegion()); + for (it.GoToBegin(); !it.IsAtEnd(); ++it, ++stir_iter) { + *stir_iter = + ITK_pixel_to_STIR_pixel( + it.Get(), stir_image, is_displacement_field); } } -template +template typename ITKImageType::Pointer -orient_ITK_image(const typename ITKImageType::Pointer itk_image_orig, - const shared_ptr exam_info_sptr) -{ - typedef itk::OrientImageFilter OrienterType; +orient_ITK_image(const typename ITKImageType::Pointer itk_image_orig, const shared_ptr exam_info_sptr) { + typedef itk::OrientImageFilter OrienterType; typename OrienterType::Pointer orienter = OrienterType::New(); orienter->UseImageDirectionOn(); orienter->SetInput(itk_image_orig); @@ -400,29 +339,25 @@ orient_ITK_image(const typename ITKImageType::Pointer itk_image_orig, case PatientPosition::HFS: // HFS means currently in LPI // So origin is in RAS direction - orienter - ->SetDesiredCoordinateOrientation(itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RAS); + orienter->SetDesiredCoordinateOrientation(itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RAS); break; case PatientPosition::HFP: // HFP means currently in RAI // So origin is in LPS direction - orienter - ->SetDesiredCoordinateOrientation(itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_LPS); + orienter->SetDesiredCoordinateOrientation(itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_LPS); break; case PatientPosition::FFS: // FFS means currently in RPS // So origin is in LAI direction - orienter - ->SetDesiredCoordinateOrientation(itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_LAI); + orienter->SetDesiredCoordinateOrientation(itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_LAI); break; case PatientPosition::FFP: // FFP means currently in LAS // So origin is in RPI direction - orienter - ->SetDesiredCoordinateOrientation(itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RPI); + orienter->SetDesiredCoordinateOrientation(itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RPI); break; default: @@ -434,168 +369,142 @@ orient_ITK_image(const typename ITKImageType::Pointer itk_image_orig, } /* Convert an ITK image into an internal STIR one. */ -template -static inline -STIRImageType* -convert_ITK_to_STIR(const typename ITKImageType::Pointer itk_image, - bool is_displacement_field=false) -{ +template +static inline STIRImageType* +convert_ITK_to_STIR(const typename ITKImageType::Pointer itk_image, bool is_displacement_field = false) { // Construct extra metadata - const shared_ptr exam_info_sptr - = construct_exam_info_from_metadata_dictionary(itk_image->GetMetaDataDictionary()); + const shared_ptr exam_info_sptr = construct_exam_info_from_metadata_dictionary(itk_image->GetMetaDataDictionary()); // Reorient the ITK image to align with STIR axes - typename ITKImageType::Pointer reor_itk_image - = orient_ITK_image(itk_image, exam_info_sptr); + typename ITKImageType::Pointer reor_itk_image = orient_ITK_image(itk_image, exam_info_sptr); // Make the STIR Image - STIRImageType* stir_image_ptr = construct_empty_stir_image - (reor_itk_image, exam_info_sptr); + STIRImageType* stir_image_ptr = + construct_empty_stir_image(reor_itk_image, exam_info_sptr); // Copy the ITK image data into the STIR Image - copy_ITK_data_to_STIR_image - (reor_itk_image, *stir_image_ptr, is_displacement_field); + copy_ITK_data_to_STIR_image(reor_itk_image, *stir_image_ptr, is_displacement_field); return stir_image_ptr; } -//To read any file format via ITK -template<> -inline -STIRImageSingle* -read_file_itk(const std::string &filename) -{ - typedef itk::GDCMImageIO ImageIOType; +// To read any file format via ITK +template <> +inline STIRImageSingle* +read_file_itk(const std::string& filename) { + typedef itk::GDCMImageIO ImageIOType; ImageIOType::Pointer dicomIO = ImageIOType::New(); - try - { - if (!dicomIO->CanReadFile(filename.c_str())) - { - info("Reading " + filename + " via ITK non-DICOM IO",2); - // Not a DICOM file, so we just read a single image - typedef itk::ImageFileReader ReaderType; - ReaderType::Pointer reader = ReaderType::New(); - - reader->SetFileName(filename); - reader->Update(); - ITKImageSingle::Pointer itk_image = reader->GetOutput(); - - return convert_ITK_to_STIR - - (itk_image); - } - else - { - // It's a DICOM file (I hope). - info("Reading " + filename + " via ITK DICOM IO",2); - - // For this, we need to read all slices in a series. - // We use code from ITK's Examples/IO/DicomSeriesReadImageWrite2.cxx - // to do this. - // However, we change it to read the series which contains the filename that was passed. - - // find all series in the directory - // This is by default based on unique - // \item[0020 0011] Series Number - // \item[0018 0024] Sequence Name - // \item[0018 0050] Slice Thickness - // \item[0028 0010] Rows - // \item[0028 0011] Columns - typedef itk::GDCMSeriesFileNames NamesGeneratorType; - typename NamesGeneratorType::Pointer nameGenerator = NamesGeneratorType::New(); - nameGenerator->SetUseSeriesDetails( true ); - // Reads complete series. - nameGenerator->AddSeriesRestriction("0008|0022" ); // AcquisitionDate - //nameGenerator->AddSeriesRestriction("0008|0032" ); // AcquisitionTime - //nameGenerator->AddSeriesRestriction("0018|1060" ); // TriggerTime - //nameGenerator->AddSeriesRestriction("0018|1063" ); // FrameTime - - const std::string dir_name = get_directory_name(filename); - nameGenerator->SetDirectory( dir_name.c_str() ); - typedef std::vector< std::string > SeriesIdContainer; - const SeriesIdContainer & seriesUIDs = nameGenerator->GetSeriesUIDs(); - - // We've found all "series" (i.e. different data-sets according to above restrictions). Now see which one we should read. - // We do this by checking which one contains the original filename. - typedef std::vector< std::string > FileNamesContainer; - FileNamesContainer fileNames; - // Loop through all "series" - for (SeriesIdContainer::const_iterator iter=seriesUIDs.begin(); iter!= seriesUIDs.end(); ++iter) - { - fileNames = nameGenerator->GetFileNames( iter->c_str() ); - // check if filename is present - if (std::find(fileNames.begin(), fileNames.end(), filename) != fileNames.end()) - break; // yes, get out of series-loop - } - - // ok. we know which filenames are in the same "series", so let's read them - typedef itk::ImageSeriesReader< ITKImageSingle > ReaderType; - ReaderType::Pointer reader = ReaderType::New(); - - reader->SetImageIO( dicomIO ); - reader->SetFileNames( fileNames ); - reader->Update(); - ITKImageSingle::Pointer itk_image = reader->GetOutput(); - - // Update custom patient position tag in metadata - itk_image->SetMetaDataDictionary(dicomIO->GetMetaDataDictionary()); - - // Finally, convert to STIR! - return convert_ITK_to_STIR - - (itk_image); - - } - } - catch (std::exception &ex) - { - error(ex.what()); - return 0; - } -} - -//To read any file format via ITK -template<> -inline -STIRImageMulti* -read_file_itk(const std::string &filename) -{ - try - { + try { + if (!dicomIO->CanReadFile(filename.c_str())) { + info("Reading " + filename + " via ITK non-DICOM IO", 2); // Not a DICOM file, so we just read a single image - typedef itk::ImageFileReader ReaderType; + typedef itk::ImageFileReader ReaderType; ReaderType::Pointer reader = ReaderType::New(); + reader->SetFileName(filename); reader->Update(); + ITKImageSingle::Pointer itk_image = reader->GetOutput(); + + return convert_ITK_to_STIR(itk_image); + } else { + // It's a DICOM file (I hope). + info("Reading " + filename + " via ITK DICOM IO", 2); + + // For this, we need to read all slices in a series. + // We use code from ITK's Examples/IO/DicomSeriesReadImageWrite2.cxx + // to do this. + // However, we change it to read the series which contains the filename that was passed. + + // find all series in the directory + // This is by default based on unique + // \item[0020 0011] Series Number + // \item[0018 0024] Sequence Name + // \item[0018 0050] Slice Thickness + // \item[0028 0010] Rows + // \item[0028 0011] Columns + typedef itk::GDCMSeriesFileNames NamesGeneratorType; + typename NamesGeneratorType::Pointer nameGenerator = NamesGeneratorType::New(); + nameGenerator->SetUseSeriesDetails(true); + // Reads complete series. + nameGenerator->AddSeriesRestriction("0008|0022"); // AcquisitionDate + // nameGenerator->AddSeriesRestriction("0008|0032" ); // AcquisitionTime + // nameGenerator->AddSeriesRestriction("0018|1060" ); // TriggerTime + // nameGenerator->AddSeriesRestriction("0018|1063" ); // FrameTime + + const std::string dir_name = get_directory_name(filename); + nameGenerator->SetDirectory(dir_name.c_str()); + typedef std::vector SeriesIdContainer; + const SeriesIdContainer& seriesUIDs = nameGenerator->GetSeriesUIDs(); + + // We've found all "series" (i.e. different data-sets according to above restrictions). Now see which one we should read. + // We do this by checking which one contains the original filename. + typedef std::vector FileNamesContainer; + FileNamesContainer fileNames; + // Loop through all "series" + for (SeriesIdContainer::const_iterator iter = seriesUIDs.begin(); iter != seriesUIDs.end(); ++iter) { + fileNames = nameGenerator->GetFileNames(iter->c_str()); + // check if filename is present + if (std::find(fileNames.begin(), fileNames.end(), filename) != fileNames.end()) + break; // yes, get out of series-loop + } + + // ok. we know which filenames are in the same "series", so let's read them + typedef itk::ImageSeriesReader ReaderType; + ReaderType::Pointer reader = ReaderType::New(); + + reader->SetImageIO(dicomIO); + reader->SetFileNames(fileNames); + reader->Update(); + ITKImageSingle::Pointer itk_image = reader->GetOutput(); - // Only support Nifti for now - if (strcmp(reader->GetImageIO()->GetNameOfClass(), "NiftiImageIO") != 0) { - error("read_file_itk: Only Nifti images are currently support for multicomponent images %s:%d.", - __FILE__, __LINE__); - return NULL; } + // Update custom patient position tag in metadata + itk_image->SetMetaDataDictionary(dicomIO->GetMetaDataDictionary()); - if (reader->GetImageIO()->GetPixelType() != -#if ITK_VERSION_MAJOR<5 || (ITK_VERSION_MAJOR==5 && ITK_VERSION_MINOR==0) - itk::ImageIOBase::VECTOR + // Finally, convert to STIR! + return convert_ITK_to_STIR(itk_image); + } + } catch (std::exception& ex) { + error(ex.what()); + return 0; + } +} + +// To read any file format via ITK +template <> +inline STIRImageMulti* +read_file_itk(const std::string& filename) { + try { + // Not a DICOM file, so we just read a single image + typedef itk::ImageFileReader ReaderType; + ReaderType::Pointer reader = ReaderType::New(); + reader->SetFileName(filename); + reader->Update(); + + // Only support Nifti for now + if (strcmp(reader->GetImageIO()->GetNameOfClass(), "NiftiImageIO") != 0) { + error("read_file_itk: Only Nifti images are currently support for multicomponent images %s:%d.", __FILE__, __LINE__); + return NULL; + } + + if (reader->GetImageIO()->GetPixelType() != +#if ITK_VERSION_MAJOR < 5 || (ITK_VERSION_MAJOR == 5 && ITK_VERSION_MINOR == 0) + itk::ImageIOBase::VECTOR #else - itk::IOPixelEnum::VECTOR + itk::IOPixelEnum::VECTOR #endif - ) { - error("read_file_itk: Image type should be vector %s:%d.", - __FILE__, __LINE__); - return NULL; } + ) { + error("read_file_itk: Image type should be vector %s:%d.", __FILE__, __LINE__); + return NULL; + } - warning("Only displacement fields are currently supported in STIR (not deformations). " - "There is no way of verifying this from the nifti_image metadata, so you need to " - "make sure that the image you are supplying is a displacement field image."); + warning("Only displacement fields are currently supported in STIR (not deformations). " + "There is no way of verifying this from the nifti_image metadata, so you need to " + "make sure that the image you are supplying is a displacement field image."); - ITKImageMulti::Pointer itk_image = reader->GetOutput(); + ITKImageMulti::Pointer itk_image = reader->GetOutput(); - return convert_ITK_to_STIR - (itk_image, true); + return convert_ITK_to_STIR(itk_image, true); - } - catch (std::exception &ex) - { - error(ex.what()); - return 0; - } + } catch (std::exception& ex) { + error(ex.what()); + return 0; + } } // explicit instantiations diff --git a/src/IO/ITKOutputFileFormat.cxx b/src/IO/ITKOutputFileFormat.cxx index 0c2986aa15..996d773fbb 100644 --- a/src/IO/ITKOutputFileFormat.cxx +++ b/src/IO/ITKOutputFileFormat.cxx @@ -37,161 +37,133 @@ START_NAMESPACE_STIR +const char* const ITKOutputFileFormat::registered_name = "ITK"; -const char * const -ITKOutputFileFormat::registered_name = "ITK"; - -ITKOutputFileFormat:: -ITKOutputFileFormat(const NumericType& type, - const ByteOrder& byte_order) -{ +ITKOutputFileFormat::ITKOutputFileFormat(const NumericType& type, const ByteOrder& byte_order) { this->set_defaults(); this->set_type_of_numbers(type); this->set_byte_order(byte_order); } -void -ITKOutputFileFormat:: -set_defaults() -{ +void +ITKOutputFileFormat::set_defaults() { base_type::set_defaults(); this->default_extension = ".nhdr"; } -void -ITKOutputFileFormat:: -initialise_keymap() -{ +void +ITKOutputFileFormat::initialise_keymap() { parser.add_start_key("ITK Output File Format Parameters"); parser.add_key("default extension", &this->default_extension); parser.add_stop_key("End ITK Output File Format Parameters"); base_type::initialise_keymap(); } -bool -ITKOutputFileFormat:: -post_processing() -{ +bool +ITKOutputFileFormat::post_processing() { if (base_type::post_processing()) return true; return false; } // note 'warn' commented below to avoid compiler warning message about unused variables -ByteOrder -ITKOutputFileFormat:: -set_byte_order(const ByteOrder& new_byte_order, const bool /* warn */) -{ +ByteOrder +ITKOutputFileFormat::set_byte_order(const ByteOrder& new_byte_order, const bool /* warn */) { this->file_byte_order = new_byte_order; return this->file_byte_order; } - - -Succeeded -ITKOutputFileFormat:: -actual_write_to_file(std::string& filename, - const DiscretisedDensity<3,float>& density) const -{ +Succeeded +ITKOutputFileFormat::actual_write_to_file(std::string& filename, const DiscretisedDensity<3, float>& density) const { #if 0 TODO use: this->type_of_numbers, this->scale_to_write_data, this->file_byte_order); #endif - try - { - add_extension(filename, this->default_extension); - - const VoxelsOnCartesianGrid& image = - dynamic_cast& >(density); - CartesianCoordinate3D min_indices; - CartesianCoordinate3D max_indices; - if (!density.get_regular_range(min_indices, max_indices)) - { - warning("ITK writer: can handle only regular index ranges."); - return Succeeded::no; - } - - typedef itk::Image< float, 3> ImageType; - typedef itk::ImageFileWriter WriterType; - WriterType::Pointer writer = WriterType::New(); - - // use 0 start indices in ITK - ImageType::IndexType start; - start[0] = 0; // first index on X - start[1] = 0; // first index on Y - start[2] = 0; // first index on Z - - // find ITK origin (i.e. coordinates of first voxel) - ImageType::PointType origin; - CartesianCoordinate3D stir_offset - = density.get_LPS_coordinates_for_indices(min_indices); - origin[0] = stir_offset.x(); - origin[1] = stir_offset.y(); - origin[2] = stir_offset.z(); - - // find ITK size - ImageType::SizeType size; - size[0] = image.get_x_size(); // size along X - size[1] = image.get_y_size(); // size along Y - size[2] = image.get_z_size(); // size along Z - - // find ITK voxel size - ImageType::SpacingType spacing; - spacing[0] = image.get_voxel_size().x(); // size along X - spacing[1] = image.get_voxel_size().y(); // size along Y - spacing[2] = image.get_voxel_size().z(); // size along Z - - // ITK Direction Matrix columns are unit vectors in axes LPS direction. - // NB: ITK Matrix is in row, column order - ImageType::DirectionType matrix; - for (unsigned int axis = 0; axis < 3; ++axis) { - CartesianCoordinate3D next_idx_along_this_axis(min_indices); - next_idx_along_this_axis[3 - axis] += 1; - const CartesianCoordinate3D next_coord_along_this_dim - = density.get_LPS_coordinates_for_indices(next_idx_along_this_axis); - const CartesianCoordinate3D axis_direction - = next_coord_along_this_dim - stir_offset; - for (unsigned int dim = 0; dim < 3; ++dim) { - matrix(dim, axis) = axis_direction[3 - dim] / norm(axis_direction); - } - } + try { + add_extension(filename, this->default_extension); + + const VoxelsOnCartesianGrid& image = dynamic_cast&>(density); + CartesianCoordinate3D min_indices; + CartesianCoordinate3D max_indices; + if (!density.get_regular_range(min_indices, max_indices)) { + warning("ITK writer: can handle only regular index ranges."); + return Succeeded::no; + } - ImageType::RegionType region; - region.SetSize( size ); - region.SetIndex( start ); - - //Creating the image - ImageType::Pointer itk_image = ImageType::New(); - - itk_image->SetSpacing(spacing); - itk_image->SetRegions( region ); - itk_image->SetOrigin(origin); - itk_image->SetDirection( matrix ); - itk_image->Allocate(); - - // copy data - typedef itk::ImageRegionIterator< ImageType > IteratorType; - IteratorType it (itk_image, itk_image->GetLargestPossibleRegion() ); - DiscretisedDensity<3,float>::const_full_iterator stir_iter = density.begin_all_const(); - for ( it.GoToBegin(); !it.IsAtEnd(); ++it, ++stir_iter ){ - it.Set(*stir_iter); + typedef itk::Image ImageType; + typedef itk::ImageFileWriter WriterType; + WriterType::Pointer writer = WriterType::New(); + + // use 0 start indices in ITK + ImageType::IndexType start; + start[0] = 0; // first index on X + start[1] = 0; // first index on Y + start[2] = 0; // first index on Z + + // find ITK origin (i.e. coordinates of first voxel) + ImageType::PointType origin; + CartesianCoordinate3D stir_offset = density.get_LPS_coordinates_for_indices(min_indices); + origin[0] = stir_offset.x(); + origin[1] = stir_offset.y(); + origin[2] = stir_offset.z(); + + // find ITK size + ImageType::SizeType size; + size[0] = image.get_x_size(); // size along X + size[1] = image.get_y_size(); // size along Y + size[2] = image.get_z_size(); // size along Z + + // find ITK voxel size + ImageType::SpacingType spacing; + spacing[0] = image.get_voxel_size().x(); // size along X + spacing[1] = image.get_voxel_size().y(); // size along Y + spacing[2] = image.get_voxel_size().z(); // size along Z + + // ITK Direction Matrix columns are unit vectors in axes LPS direction. + // NB: ITK Matrix is in row, column order + ImageType::DirectionType matrix; + for (unsigned int axis = 0; axis < 3; ++axis) { + CartesianCoordinate3D next_idx_along_this_axis(min_indices); + next_idx_along_this_axis[3 - axis] += 1; + const CartesianCoordinate3D next_coord_along_this_dim = + density.get_LPS_coordinates_for_indices(next_idx_along_this_axis); + const CartesianCoordinate3D axis_direction = next_coord_along_this_dim - stir_offset; + for (unsigned int dim = 0; dim < 3; ++dim) { + matrix(dim, axis) = axis_direction[3 - dim] / norm(axis_direction); } - - // write it! - writer->SetInput(itk_image); - writer->SetFileName(filename); - writer->Update(); - - return Succeeded::yes; } - catch (...) - { - return Succeeded::no; + + ImageType::RegionType region; + region.SetSize(size); + region.SetIndex(start); + + // Creating the image + ImageType::Pointer itk_image = ImageType::New(); + + itk_image->SetSpacing(spacing); + itk_image->SetRegions(region); + itk_image->SetOrigin(origin); + itk_image->SetDirection(matrix); + itk_image->Allocate(); + + // copy data + typedef itk::ImageRegionIterator IteratorType; + IteratorType it(itk_image, itk_image->GetLargestPossibleRegion()); + DiscretisedDensity<3, float>::const_full_iterator stir_iter = density.begin_all_const(); + for (it.GoToBegin(); !it.IsAtEnd(); ++it, ++stir_iter) { + it.Set(*stir_iter); } + // write it! + writer->SetInput(itk_image); + writer->SetFileName(filename); + writer->Update(); + + return Succeeded::yes; + } catch (...) { + return Succeeded::no; + } } END_NAMESPACE_STIR - - diff --git a/src/IO/InputFileFormatRegistry.cxx b/src/IO/InputFileFormatRegistry.cxx index 815305805b..08214c98ee 100644 --- a/src/IO/InputFileFormatRegistry.cxx +++ b/src/IO/InputFileFormatRegistry.cxx @@ -27,17 +27,17 @@ #include "stir/IO/InputFileFormatRegistry.txx" #include "stir/DiscretisedDensity.h" -#include "stir/modelling/ParametricDiscretisedDensity.h" -#include "stir/DynamicDiscretisedDensity.h" +#include "stir/modelling/ParametricDiscretisedDensity.h" +#include "stir/DynamicDiscretisedDensity.h" #include "stir/listmode/ListModeData.h" START_NAMESPACE_STIR // instantiations -template class InputFileFormatRegistry >; -template class InputFileFormatRegistry; +template class InputFileFormatRegistry>; +template class InputFileFormatRegistry; template class InputFileFormatRegistry; template class InputFileFormatRegistry; -template class InputFileFormatRegistry > >; +template class InputFileFormatRegistry>>; END_NAMESPACE_STIR diff --git a/src/IO/InputStreamFromROOTFile.cxx b/src/IO/InputStreamFromROOTFile.cxx index 592d1cef33..4808f33be1 100644 --- a/src/IO/InputStreamFromROOTFile.cxx +++ b/src/IO/InputStreamFromROOTFile.cxx @@ -38,12 +38,10 @@ START_NAMESPACE_STIR -InputStreamFromROOTFile:: -InputStreamFromROOTFile() -{ - set_defaults(); - reset(); - least_significant_clock_bit = 1.0e+12; // TODO remove cst or rename +InputStreamFromROOTFile::InputStreamFromROOTFile() { + set_defaults(); + reset(); + least_significant_clock_bit = 1.0e+12; // TODO remove cst or rename } #if 0 // disabled as unused and incorrect @@ -64,131 +62,117 @@ InputStreamFromROOTFile(std::string filename, #endif void -InputStreamFromROOTFile::set_defaults() -{ - starting_stream_position = 0; - singles_readout_depth = -1; - exclude_scattered = false; - exclude_randoms = false; - low_energy_window = 0.f; - up_energy_window = 1000.f; - read_optional_root_fields=false; - crystal_repeater_x = -1; - crystal_repeater_y = -1; - crystal_repeater_z = -1; - num_virtual_axial_crystals_per_block = 0; - num_virtual_transaxial_crystals_per_block = 0; +InputStreamFromROOTFile::set_defaults() { + starting_stream_position = 0; + singles_readout_depth = -1; + exclude_scattered = false; + exclude_randoms = false; + low_energy_window = 0.f; + up_energy_window = 1000.f; + read_optional_root_fields = false; + crystal_repeater_x = -1; + crystal_repeater_y = -1; + crystal_repeater_z = -1; + num_virtual_axial_crystals_per_block = 0; + num_virtual_transaxial_crystals_per_block = 0; } void -InputStreamFromROOTFile::initialise_keymap() -{ - this->parser.add_key("name of data file", &this->filename); - this->parser.add_key("Singles readout depth", &this->singles_readout_depth); - this->parser.add_key("name of input TChain", &this->chain_name); - this->parser.add_key("exclude scattered events", &this->exclude_scattered); - this->parser.add_key("exclude random events", &this->exclude_randoms); - this->parser.add_key("offset (num of detectors)", &this->offset_dets); - this->parser.add_key("low energy window (keV)", &this->low_energy_window); - this->parser.add_key("upper energy window (keV)", &this->up_energy_window); - this->parser.add_key("read optional ROOT fields", &this->read_optional_root_fields); - - this->parser.add_key("number of crystals X", &this->crystal_repeater_x); - this->parser.add_key("number of crystals Y", &this->crystal_repeater_y); - this->parser.add_key("number of crystals Z", &this->crystal_repeater_z); +InputStreamFromROOTFile::initialise_keymap() { + this->parser.add_key("name of data file", &this->filename); + this->parser.add_key("Singles readout depth", &this->singles_readout_depth); + this->parser.add_key("name of input TChain", &this->chain_name); + this->parser.add_key("exclude scattered events", &this->exclude_scattered); + this->parser.add_key("exclude random events", &this->exclude_randoms); + this->parser.add_key("offset (num of detectors)", &this->offset_dets); + this->parser.add_key("low energy window (keV)", &this->low_energy_window); + this->parser.add_key("upper energy window (keV)", &this->up_energy_window); + this->parser.add_key("read optional ROOT fields", &this->read_optional_root_fields); + + this->parser.add_key("number of crystals X", &this->crystal_repeater_x); + this->parser.add_key("number of crystals Y", &this->crystal_repeater_y); + this->parser.add_key("number of crystals Z", &this->crystal_repeater_z); } bool -InputStreamFromROOTFile::post_processing() -{ - return false; +InputStreamFromROOTFile::post_processing() { + return false; } Succeeded -InputStreamFromROOTFile::set_up(const std::string & header_path) -{ - // check types, really should be compile time assert - if (!std::is_same::value || - !std::is_same::value || - !std::is_same::value) - error("Internal error: ROOT types are not what we think they are."); - - FilePath f(filename,false); - f.prepend_directory_name(header_path); - - const std::string fullfilename = f.get_as_string(); - // Read the 4 bytes to check whether this is a ROOT file. - FileSignature signature(fullfilename.c_str()); - if ( strncmp(signature.get_signature(), "root", 4) != 0) - { - error("InputStreamFromROOTFile: File '%s' is not a ROOT file! (first 4 bytes should say 'root')", - filename.c_str()); - } - - stream_ptr = new TChain(this->chain_name.c_str()); - stream_ptr->Add(fullfilename.c_str()); - stream_ptr->SetBranchAddress("time1", &time1); - stream_ptr->SetBranchAddress("time2", &time2); - stream_ptr->SetBranchAddress("eventID1",&eventID1); - stream_ptr->SetBranchAddress("eventID2",&eventID2); - stream_ptr->SetBranchAddress("energy1", &energy1); - stream_ptr->SetBranchAddress("energy2", &energy2); - stream_ptr->SetBranchAddress("comptonPhantom1", &comptonphantom1); - stream_ptr->SetBranchAddress("comptonPhantom2", &comptonphantom2); - - if (read_optional_root_fields) - { - stream_ptr->SetBranchAddress("axialPos",&axialPos); - stream_ptr->SetBranchAddress("globalPosX1",&globalPosX1); - stream_ptr->SetBranchAddress("globalPosX2",&globalPosX2); - stream_ptr->SetBranchAddress("globalPosY1",&globalPosY1); - stream_ptr->SetBranchAddress("globalPosY2",&globalPosY2); - stream_ptr->SetBranchAddress("globalPosZ1",&globalPosZ1); - stream_ptr->SetBranchAddress("globalPosZ2",&globalPosZ2); - stream_ptr->SetBranchAddress("rotationAngle",&rotation_angle); - stream_ptr->SetBranchAddress("runID",&runID); - stream_ptr->SetBranchAddress("sinogramS",&sinogramS); - stream_ptr->SetBranchAddress("sinogramTheta",&sinogramTheta); - stream_ptr->SetBranchAddress("sourceID1",&sourceID1); - stream_ptr->SetBranchAddress("sourceID2",&sourceID2); - stream_ptr->SetBranchAddress("sourcePosX1",&sourcePosX1); - stream_ptr->SetBranchAddress("sourcePosX2",&sourcePosX2); - stream_ptr->SetBranchAddress("sourcePosY1",&sourcePosY1); - stream_ptr->SetBranchAddress("sourcePosY2",&sourcePosY2); - stream_ptr->SetBranchAddress("sourcePosZ1",&sourcePosZ1); - stream_ptr->SetBranchAddress("sourcePosZ2",&sourcePosZ2); - } - - return Succeeded::yes; +InputStreamFromROOTFile::set_up(const std::string& header_path) { + // check types, really should be compile time assert + if (!std::is_same::value || !std::is_same::value || !std::is_same::value) + error("Internal error: ROOT types are not what we think they are."); + + FilePath f(filename, false); + f.prepend_directory_name(header_path); + + const std::string fullfilename = f.get_as_string(); + // Read the 4 bytes to check whether this is a ROOT file. + FileSignature signature(fullfilename.c_str()); + if (strncmp(signature.get_signature(), "root", 4) != 0) { + error("InputStreamFromROOTFile: File '%s' is not a ROOT file! (first 4 bytes should say 'root')", filename.c_str()); + } + + stream_ptr = new TChain(this->chain_name.c_str()); + stream_ptr->Add(fullfilename.c_str()); + stream_ptr->SetBranchAddress("time1", &time1); + stream_ptr->SetBranchAddress("time2", &time2); + stream_ptr->SetBranchAddress("eventID1", &eventID1); + stream_ptr->SetBranchAddress("eventID2", &eventID2); + stream_ptr->SetBranchAddress("energy1", &energy1); + stream_ptr->SetBranchAddress("energy2", &energy2); + stream_ptr->SetBranchAddress("comptonPhantom1", &comptonphantom1); + stream_ptr->SetBranchAddress("comptonPhantom2", &comptonphantom2); + + if (read_optional_root_fields) { + stream_ptr->SetBranchAddress("axialPos", &axialPos); + stream_ptr->SetBranchAddress("globalPosX1", &globalPosX1); + stream_ptr->SetBranchAddress("globalPosX2", &globalPosX2); + stream_ptr->SetBranchAddress("globalPosY1", &globalPosY1); + stream_ptr->SetBranchAddress("globalPosY2", &globalPosY2); + stream_ptr->SetBranchAddress("globalPosZ1", &globalPosZ1); + stream_ptr->SetBranchAddress("globalPosZ2", &globalPosZ2); + stream_ptr->SetBranchAddress("rotationAngle", &rotation_angle); + stream_ptr->SetBranchAddress("runID", &runID); + stream_ptr->SetBranchAddress("sinogramS", &sinogramS); + stream_ptr->SetBranchAddress("sinogramTheta", &sinogramTheta); + stream_ptr->SetBranchAddress("sourceID1", &sourceID1); + stream_ptr->SetBranchAddress("sourceID2", &sourceID2); + stream_ptr->SetBranchAddress("sourcePosX1", &sourcePosX1); + stream_ptr->SetBranchAddress("sourcePosX2", &sourcePosX2); + stream_ptr->SetBranchAddress("sourcePosY1", &sourcePosY1); + stream_ptr->SetBranchAddress("sourcePosY2", &sourcePosY2); + stream_ptr->SetBranchAddress("sourcePosZ1", &sourcePosZ1); + stream_ptr->SetBranchAddress("sourcePosZ2", &sourcePosZ2); + } + + return Succeeded::yes; } void -InputStreamFromROOTFile::set_crystal_repeater_x(int val) -{ - crystal_repeater_x = val; +InputStreamFromROOTFile::set_crystal_repeater_x(int val) { + crystal_repeater_x = val; } void -InputStreamFromROOTFile::set_crystal_repeater_y(int val) -{ - crystal_repeater_y = val; +InputStreamFromROOTFile::set_crystal_repeater_y(int val) { + crystal_repeater_y = val; } void -InputStreamFromROOTFile::set_crystal_repeater_z(int val) -{ - crystal_repeater_z = val; +InputStreamFromROOTFile::set_crystal_repeater_z(int val) { + crystal_repeater_z = val; } void -InputStreamFromROOTFile::set_num_virtual_axial_crystals_per_block(int val) -{ +InputStreamFromROOTFile::set_num_virtual_axial_crystals_per_block(int val) { num_virtual_axial_crystals_per_block = val; } void -InputStreamFromROOTFile::set_num_virtual_transaxial_crystals_per_block(int val) -{ +InputStreamFromROOTFile::set_num_virtual_transaxial_crystals_per_block(int val) { num_virtual_transaxial_crystals_per_block = val; } diff --git a/src/IO/InputStreamFromROOTFileForCylindricalPET.cxx b/src/IO/InputStreamFromROOTFileForCylindricalPET.cxx index 55e8be23bd..e2f42d68a1 100644 --- a/src/IO/InputStreamFromROOTFileForCylindricalPET.cxx +++ b/src/IO/InputStreamFromROOTFileForCylindricalPET.cxx @@ -20,16 +20,9 @@ START_NAMESPACE_STIR -const char * const -InputStreamFromROOTFileForCylindricalPET::registered_name = - "GATE_Cylindrical_PET"; +const char* const InputStreamFromROOTFileForCylindricalPET::registered_name = "GATE_Cylindrical_PET"; -InputStreamFromROOTFileForCylindricalPET:: -InputStreamFromROOTFileForCylindricalPET(): - base_type() -{ - set_defaults(); -} +InputStreamFromROOTFileForCylindricalPET::InputStreamFromROOTFileForCylindricalPET() : base_type() { set_defaults(); } #if 0 // not used, so commented out (would need adapting since moving crystal_repeated_*) InputStreamFromROOTFileForCylindricalPET:: InputStreamFromROOTFileForCylindricalPET(std::string _filename, @@ -65,225 +58,195 @@ InputStreamFromROOTFileForCylindricalPET(std::string _filename, #endif Succeeded -InputStreamFromROOTFileForCylindricalPET:: -get_next_record(CListRecordROOT& record) -{ +InputStreamFromROOTFileForCylindricalPET::get_next_record(CListRecordROOT& record) { + + while (true) { + if (current_position == nentries) + return Succeeded::no; + + if (stream_ptr->GetEntry(static_cast(current_position)) == 0) + return Succeeded::no; + + current_position++; + + if ((this->comptonphantom1 > 0 || this->comptonphantom2 > 0) && this->exclude_scattered) + continue; + if ((this->eventID1 != this->eventID2) && this->exclude_randoms) + continue; + // multiply here by 1000 to convert the list mode energy from MeV to keV + if (this->get_energy1_in_keV() < this->low_energy_window || this->get_energy1_in_keV() > this->up_energy_window || + this->get_energy2_in_keV() < this->low_energy_window || this->get_energy2_in_keV() > this->up_energy_window) + continue; + + break; + } + + int ring1 = static_cast(crystalID1 / crystal_repeater_y) + + static_cast(submoduleID1 / submodule_repeater_y) * get_num_axial_crystals_per_block_v() + + static_cast(moduleID1 / module_repeater_y) * submodule_repeater_z * get_num_axial_crystals_per_block_v(); + + int ring2 = static_cast(crystalID2 / crystal_repeater_y) + + static_cast(submoduleID2 / submodule_repeater_y) * get_num_axial_crystals_per_block_v() + + static_cast(moduleID2 / module_repeater_y) * submodule_repeater_z * get_num_axial_crystals_per_block_v(); + + int crystal1 = rsectorID1 * module_repeater_y * submodule_repeater_y * get_num_transaxial_crystals_per_block_v() + + (moduleID1 % module_repeater_y) * submodule_repeater_y * get_num_transaxial_crystals_per_block_v() + + (submoduleID1 % submodule_repeater_y) * get_num_transaxial_crystals_per_block_v() + + (crystalID1 % crystal_repeater_y); + + int crystal2 = rsectorID2 * module_repeater_y * submodule_repeater_y * get_num_transaxial_crystals_per_block_v() + + (moduleID2 % module_repeater_y) * submodule_repeater_y * get_num_transaxial_crystals_per_block_v() + + (submoduleID2 % submodule_repeater_y) * get_num_transaxial_crystals_per_block_v() + + (crystalID2 % crystal_repeater_y); + + // GATE counts crystal ID =0 the most negative. Therefore + // ID = 0 should be negative, in Rsector 0 and the mid crystal ID be 0 . + // Moved to post_processings(). + // crystal1 -= half_block; + // crystal2 -= half_block; + + // Add offset + crystal1 += offset_dets; + crystal2 += offset_dets; - while(true) - { - if (current_position == nentries) - return Succeeded::no; - - - if (stream_ptr->GetEntry(static_cast(current_position)) == 0 ) - return Succeeded::no; - - current_position ++ ; - - if ( (this->comptonphantom1 > 0 || this->comptonphantom2 > 0) && this->exclude_scattered ) - continue; - if ( (this->eventID1 != this->eventID2) && this->exclude_randoms) - continue; - //multiply here by 1000 to convert the list mode energy from MeV to keV - if (this->get_energy1_in_keV() < this->low_energy_window || - this->get_energy1_in_keV() > this->up_energy_window || - this->get_energy2_in_keV() < this->low_energy_window || - this->get_energy2_in_keV() > this->up_energy_window) - continue; - - break; - } - - int ring1 = static_cast(crystalID1/crystal_repeater_y) - + static_cast(submoduleID1/submodule_repeater_y)*get_num_axial_crystals_per_block_v() - + static_cast(moduleID1/module_repeater_y)*submodule_repeater_z*get_num_axial_crystals_per_block_v(); - - int ring2 = static_cast(crystalID2/crystal_repeater_y) - + static_cast(submoduleID2/submodule_repeater_y)*get_num_axial_crystals_per_block_v() - + static_cast(moduleID2/module_repeater_y)*submodule_repeater_z*get_num_axial_crystals_per_block_v(); - - int crystal1 = rsectorID1 * module_repeater_y * submodule_repeater_y * get_num_transaxial_crystals_per_block_v() - + (moduleID1%module_repeater_y) * submodule_repeater_y * get_num_transaxial_crystals_per_block_v() - + (submoduleID1%submodule_repeater_y) * get_num_transaxial_crystals_per_block_v() - + (crystalID1%crystal_repeater_y); - - int crystal2 = rsectorID2 * module_repeater_y * submodule_repeater_y * get_num_transaxial_crystals_per_block_v() - + (moduleID2%module_repeater_y) * submodule_repeater_y * get_num_transaxial_crystals_per_block_v() - + (submoduleID2% submodule_repeater_y) * get_num_transaxial_crystals_per_block_v() - + (crystalID2%crystal_repeater_y); - - // GATE counts crystal ID =0 the most negative. Therefore - // ID = 0 should be negative, in Rsector 0 and the mid crystal ID be 0 . - // Moved to post_processings(). - //crystal1 -= half_block; - //crystal2 -= half_block; - - // Add offset - crystal1 += offset_dets; - crystal2 += offset_dets; - - double delta_timing_bin = (time2 - time1) * least_significant_clock_bit; - - return - record.init_from_data(ring1, ring2, - crystal1, crystal2, - time1, delta_timing_bin, - eventID1, eventID2); + double delta_timing_bin = (time2 - time1) * least_significant_clock_bit; + + return record.init_from_data(ring1, ring2, crystal1, crystal2, time1, delta_timing_bin, eventID1, eventID2); } std::string -InputStreamFromROOTFileForCylindricalPET:: -method_info() const -{ - std::ostringstream s; - s << this->registered_name; - return s.str(); +InputStreamFromROOTFileForCylindricalPET::method_info() const { + std::ostringstream s; + s << this->registered_name; + return s.str(); } void -InputStreamFromROOTFileForCylindricalPET::set_defaults() -{ - base_type::set_defaults(); - submodule_repeater_x = -1; - submodule_repeater_y = -1; - submodule_repeater_z = -1; - module_repeater_x = -1; - module_repeater_y = -1; - module_repeater_z = -1; - rsector_repeater = -1; +InputStreamFromROOTFileForCylindricalPET::set_defaults() { + base_type::set_defaults(); + submodule_repeater_x = -1; + submodule_repeater_y = -1; + submodule_repeater_z = -1; + module_repeater_x = -1; + module_repeater_y = -1; + module_repeater_z = -1; + rsector_repeater = -1; } void -InputStreamFromROOTFileForCylindricalPET::initialise_keymap() -{ - base_type::initialise_keymap(); - this->parser.add_start_key("GATE_Cylindrical_PET Parameters"); - this->parser.add_stop_key("End GATE_Cylindrical_PET Parameters"); - this->parser.add_key("number of Rsectors", &this->rsector_repeater); - this->parser.add_key("number of modules X", &this->module_repeater_x); - this->parser.add_key("number of modules Y", &this->module_repeater_y); - this->parser.add_key("number of modules Z", &this->module_repeater_z); - - this->parser.add_key("number of submodules X", &this->submodule_repeater_x); - this->parser.add_key("number of submodules Y", &this->submodule_repeater_y); - this->parser.add_key("number of submodules Z", &this->submodule_repeater_z); +InputStreamFromROOTFileForCylindricalPET::initialise_keymap() { + base_type::initialise_keymap(); + this->parser.add_start_key("GATE_Cylindrical_PET Parameters"); + this->parser.add_stop_key("End GATE_Cylindrical_PET Parameters"); + this->parser.add_key("number of Rsectors", &this->rsector_repeater); + this->parser.add_key("number of modules X", &this->module_repeater_x); + this->parser.add_key("number of modules Y", &this->module_repeater_y); + this->parser.add_key("number of modules Z", &this->module_repeater_z); + + this->parser.add_key("number of submodules X", &this->submodule_repeater_x); + this->parser.add_key("number of submodules Y", &this->submodule_repeater_y); + this->parser.add_key("number of submodules Z", &this->submodule_repeater_z); } -bool InputStreamFromROOTFileForCylindricalPET:: -post_processing() -{ - if (base_type::post_processing()) - return true; - return false; +bool +InputStreamFromROOTFileForCylindricalPET::post_processing() { + if (base_type::post_processing()) + return true; + return false; } Succeeded -InputStreamFromROOTFileForCylindricalPET:: -set_up(const std::string & header_path) -{ - if (base_type::set_up(header_path) == Succeeded::no) - return Succeeded::no; - - std::string missing_keywords; - if(!check_all_required_keywords_are_set(missing_keywords)) - { - warning(missing_keywords.c_str()); - return Succeeded::no; - } - - stream_ptr->SetBranchAddress("crystalID1",&crystalID1); - stream_ptr->SetBranchAddress("crystalID2",&crystalID2); - stream_ptr->SetBranchAddress("submoduleID1",&submoduleID1); - stream_ptr->SetBranchAddress("submoduleID2",&submoduleID2); - stream_ptr->SetBranchAddress("moduleID1",&moduleID1); - stream_ptr->SetBranchAddress("moduleID2",&moduleID2); - stream_ptr->SetBranchAddress("rsectorID1",&rsectorID1); - stream_ptr->SetBranchAddress("rsectorID2",&rsectorID2); - - nentries = static_cast(stream_ptr->GetEntries()); - if (nentries == 0) - error("InputStreamFromROOTFileForCylindricalPET: The total number of entries in the ROOT file is zero. Abort."); - - half_block = (module_repeater_y * submodule_repeater_y * crystal_repeater_y) / 2; - if (half_block < 0 ) - half_block = 0; - - offset_dets -= half_block; - - return Succeeded::yes; +InputStreamFromROOTFileForCylindricalPET::set_up(const std::string& header_path) { + if (base_type::set_up(header_path) == Succeeded::no) + return Succeeded::no; + + std::string missing_keywords; + if (!check_all_required_keywords_are_set(missing_keywords)) { + warning(missing_keywords.c_str()); + return Succeeded::no; + } + + stream_ptr->SetBranchAddress("crystalID1", &crystalID1); + stream_ptr->SetBranchAddress("crystalID2", &crystalID2); + stream_ptr->SetBranchAddress("submoduleID1", &submoduleID1); + stream_ptr->SetBranchAddress("submoduleID2", &submoduleID2); + stream_ptr->SetBranchAddress("moduleID1", &moduleID1); + stream_ptr->SetBranchAddress("moduleID2", &moduleID2); + stream_ptr->SetBranchAddress("rsectorID1", &rsectorID1); + stream_ptr->SetBranchAddress("rsectorID2", &rsectorID2); + + nentries = static_cast(stream_ptr->GetEntries()); + if (nentries == 0) + error("InputStreamFromROOTFileForCylindricalPET: The total number of entries in the ROOT file is zero. Abort."); + + half_block = (module_repeater_y * submodule_repeater_y * crystal_repeater_y) / 2; + if (half_block < 0) + half_block = 0; + + offset_dets -= half_block; + + return Succeeded::yes; } -bool InputStreamFromROOTFileForCylindricalPET:: -check_all_required_keywords_are_set(std::string& ret) const -{ - std::ostringstream stream("InputStreamFromROOTFileForCylindricalPET: Required keywords are missing! Check: "); - bool ok = true; - - if (crystal_repeater_x == -1) - { - stream << "crystal_repeater_x, "; - ok = false; - } - - if (crystal_repeater_y == -1) - { - stream << "crystal_repeater_x, "; - ok = false; - } - - if (crystal_repeater_z == -1) - { - stream << "crystal_repeater_x, "; - ok = false; - } - - if (submodule_repeater_x == -1) - { - stream << "crystal_repeater_x, "; - ok = false; - } - - if (submodule_repeater_y == -1) - { - stream << "crystal_repeater_x, "; - ok = false; - } - - if (submodule_repeater_z == -1) - { - stream << "crystal_repeater_x, "; - ok = false; - } - - if (module_repeater_x == -1) - { - stream << "crystal_repeater_x, "; - ok = false; - } - - if (module_repeater_y == -1) - { - stream << "crystal_repeater_x, "; - ok = false; - } - - if (module_repeater_z == -1) - { - stream << "crystal_repeater_x, "; - ok = false; - } - - if (rsector_repeater == -1) - { - stream << "crystal_repeater_x, "; - ok = false; - } - - if (!ok) - ret = stream.str(); - - return ok; +bool +InputStreamFromROOTFileForCylindricalPET::check_all_required_keywords_are_set(std::string& ret) const { + std::ostringstream stream("InputStreamFromROOTFileForCylindricalPET: Required keywords are missing! Check: "); + bool ok = true; + + if (crystal_repeater_x == -1) { + stream << "crystal_repeater_x, "; + ok = false; + } + + if (crystal_repeater_y == -1) { + stream << "crystal_repeater_x, "; + ok = false; + } + + if (crystal_repeater_z == -1) { + stream << "crystal_repeater_x, "; + ok = false; + } + + if (submodule_repeater_x == -1) { + stream << "crystal_repeater_x, "; + ok = false; + } + + if (submodule_repeater_y == -1) { + stream << "crystal_repeater_x, "; + ok = false; + } + + if (submodule_repeater_z == -1) { + stream << "crystal_repeater_x, "; + ok = false; + } + + if (module_repeater_x == -1) { + stream << "crystal_repeater_x, "; + ok = false; + } + + if (module_repeater_y == -1) { + stream << "crystal_repeater_x, "; + ok = false; + } + + if (module_repeater_z == -1) { + stream << "crystal_repeater_x, "; + ok = false; + } + + if (rsector_repeater == -1) { + stream << "crystal_repeater_x, "; + ok = false; + } + + if (!ok) + ret = stream.str(); + + return ok; } - END_NAMESPACE_STIR diff --git a/src/IO/InputStreamFromROOTFileForECATPET.cxx b/src/IO/InputStreamFromROOTFileForECATPET.cxx index 36639c2c7f..0e325c30f5 100644 --- a/src/IO/InputStreamFromROOTFileForECATPET.cxx +++ b/src/IO/InputStreamFromROOTFileForECATPET.cxx @@ -21,16 +21,9 @@ START_NAMESPACE_STIR -const char * const -InputStreamFromROOTFileForECATPET::registered_name = - "GATE_ECAT_PET"; +const char* const InputStreamFromROOTFileForECATPET::registered_name = "GATE_ECAT_PET"; -InputStreamFromROOTFileForECATPET:: -InputStreamFromROOTFileForECATPET(): - base_type() -{ - set_defaults(); -} +InputStreamFromROOTFileForECATPET::InputStreamFromROOTFileForECATPET() : base_type() { set_defaults(); } #if 0 // not used, so commented out (would need adapting since moving crystal_repeated_*) InputStreamFromROOTFileForECATPET:: @@ -63,162 +56,137 @@ InputStreamFromROOTFileForECATPET(std::string _filename, #endif Succeeded -InputStreamFromROOTFileForECATPET:: -get_next_record(CListRecordROOT& record) -{ - while(true) - { - if (current_position == nentries) - return Succeeded::no; +InputStreamFromROOTFileForECATPET::get_next_record(CListRecordROOT& record) { + while (true) { + if (current_position == nentries) + return Succeeded::no; + if (stream_ptr->GetEntry(current_position) == 0) + return Succeeded::no; - if (stream_ptr->GetEntry(current_position) == 0 ) - return Succeeded::no; + current_position++; - current_position ++ ; + if ((comptonphantom1 > 0 || comptonphantom2 > 0) && exclude_scattered) + continue; + if (eventID1 != eventID2 && exclude_randoms) + continue; + // multiply here by 1000 to convert the list mode energy from MeV to keV + if (this->get_energy1_in_keV() < low_energy_window || this->get_energy1_in_keV() > up_energy_window || + this->get_energy2_in_keV() < low_energy_window || this->get_energy2_in_keV() > up_energy_window) + continue; - if ( (comptonphantom1 > 0 || comptonphantom2 > 0) && exclude_scattered ) - continue; - if ( eventID1 != eventID2 && exclude_randoms ) - continue; - //multiply here by 1000 to convert the list mode energy from MeV to keV - if (this->get_energy1_in_keV() < low_energy_window || - this->get_energy1_in_keV() > up_energy_window || - this->get_energy2_in_keV() < low_energy_window || - this->get_energy2_in_keV() > up_energy_window) - continue; + break; + } - break; - } + int ring1 = + static_cast(crystalID1 / crystal_repeater_y) + static_cast(blockID1 / block_repeater_y) * crystal_repeater_z; - int ring1 = static_cast(crystalID1/crystal_repeater_y) - + static_cast(blockID1/ block_repeater_y)*crystal_repeater_z; + int ring2 = + static_cast(crystalID2 / crystal_repeater_y) + static_cast(blockID2 / block_repeater_y) * crystal_repeater_z; - int ring2 = static_cast(crystalID2/crystal_repeater_y) - + static_cast(blockID2/block_repeater_y)*crystal_repeater_z; + int crystal1 = (blockID1 % block_repeater_y) * get_num_transaxial_crystals_per_block_v() + (crystalID1 % crystal_repeater_y); - int crystal1 = (blockID1%block_repeater_y) * get_num_transaxial_crystals_per_block_v() - + (crystalID1%crystal_repeater_y); + int crystal2 = (blockID2 % block_repeater_y) * get_num_transaxial_crystals_per_block_v() + (crystalID2 % crystal_repeater_y); - int crystal2 = (blockID2%block_repeater_y) * get_num_transaxial_crystals_per_block_v() - + (crystalID2%crystal_repeater_y); + // GATE counts crystal ID =0 the most negative. Therefore + // ID = 0 should be negative, in Rsector 0 and the mid crystal ID be 0 . + crystal1 -= half_block; + crystal2 -= half_block; - // GATE counts crystal ID =0 the most negative. Therefore - // ID = 0 should be negative, in Rsector 0 and the mid crystal ID be 0 . - crystal1 -= half_block; - crystal2 -= half_block; + // Add offset + crystal1 += offset_dets; + crystal2 += offset_dets; - // Add offset - crystal1 += offset_dets; - crystal2 += offset_dets; + double delta_timing_bin = (time2 - time1) * least_significant_clock_bit; - double delta_timing_bin = (time2 - time1) * least_significant_clock_bit; - - return - record.init_from_data(ring1, ring2, - crystal1, crystal2, - time1, delta_timing_bin, - eventID1, eventID2); + return record.init_from_data(ring1, ring2, crystal1, crystal2, time1, delta_timing_bin, eventID1, eventID2); } std::string -InputStreamFromROOTFileForECATPET:: -method_info() const -{ - std::ostringstream s; - s << this->registered_name; - return s.str(); +InputStreamFromROOTFileForECATPET::method_info() const { + std::ostringstream s; + s << this->registered_name; + return s.str(); } void -InputStreamFromROOTFileForECATPET::set_defaults() -{ - base_type::set_defaults(); - block_repeater_y = -1; - block_repeater_z = -1; +InputStreamFromROOTFileForECATPET::set_defaults() { + base_type::set_defaults(); + block_repeater_y = -1; + block_repeater_z = -1; } void -InputStreamFromROOTFileForECATPET::initialise_keymap() -{ - base_type::initialise_keymap(); - this->parser.add_start_key("GATE_ECAT_PET Parameters"); - this->parser.add_stop_key("End GATE_ECAT_PET Parameters"); - this->parser.add_key("number of blocks Y", &this->block_repeater_y); - this->parser.add_key("number of blocks Z", &this->block_repeater_z); +InputStreamFromROOTFileForECATPET::initialise_keymap() { + base_type::initialise_keymap(); + this->parser.add_start_key("GATE_ECAT_PET Parameters"); + this->parser.add_stop_key("End GATE_ECAT_PET Parameters"); + this->parser.add_key("number of blocks Y", &this->block_repeater_y); + this->parser.add_key("number of blocks Z", &this->block_repeater_z); } -bool InputStreamFromROOTFileForECATPET:: -post_processing() -{ - return false; +bool +InputStreamFromROOTFileForECATPET::post_processing() { + return false; } -Succeeded InputStreamFromROOTFileForECATPET:: -set_up(const std::string & header_path ) -{ - if (base_type::set_up(header_path) == Succeeded::no) - return Succeeded::no; - - std::string missing_keywords; - if(!check_all_required_keywords_are_set(missing_keywords)) - { - warning(missing_keywords.c_str()); - return Succeeded::no; - } - - stream_ptr->SetBranchAddress("crystalID1",&crystalID1); - stream_ptr->SetBranchAddress("crystalID2",&crystalID2); - stream_ptr->SetBranchAddress("blockID1",&blockID1); - stream_ptr->SetBranchAddress("blockID2",&blockID2); - - nentries = static_cast(stream_ptr->GetEntries()); - if (nentries == 0) - error("The total number of entries in the ROOT file is zero. Abort."); - - return Succeeded::yes; +Succeeded +InputStreamFromROOTFileForECATPET::set_up(const std::string& header_path) { + if (base_type::set_up(header_path) == Succeeded::no) + return Succeeded::no; + + std::string missing_keywords; + if (!check_all_required_keywords_are_set(missing_keywords)) { + warning(missing_keywords.c_str()); + return Succeeded::no; + } + + stream_ptr->SetBranchAddress("crystalID1", &crystalID1); + stream_ptr->SetBranchAddress("crystalID2", &crystalID2); + stream_ptr->SetBranchAddress("blockID1", &blockID1); + stream_ptr->SetBranchAddress("blockID2", &blockID2); + + nentries = static_cast(stream_ptr->GetEntries()); + if (nentries == 0) + error("The total number of entries in the ROOT file is zero. Abort."); + + return Succeeded::yes; } -bool InputStreamFromROOTFileForECATPET:: -check_all_required_keywords_are_set(std::string& ret) const -{ - std::ostringstream stream("InputStreamFromROOTFileForCylindricalPET: Required keywords are missing! Check: "); - bool ok = true; - - if (crystal_repeater_x == -1) - { - stream << "crystal_repeater_x, "; - ok = false; - } - - if (crystal_repeater_y == -1) - { - stream << "crystal_repeater_y, "; - ok = false; - } - - if (crystal_repeater_z == -1) - { - stream << "crystal_repeater_z, "; - ok = false; - } - - if (block_repeater_y == -1) - { - stream << "block_repeater_y, "; - ok = false; - } - - if (block_repeater_z == -1) - { - stream << "block_repeater_z, "; - ok = false; - } - - if (!ok) - ret = stream.str(); - - return ok; +bool +InputStreamFromROOTFileForECATPET::check_all_required_keywords_are_set(std::string& ret) const { + std::ostringstream stream("InputStreamFromROOTFileForCylindricalPET: Required keywords are missing! Check: "); + bool ok = true; + + if (crystal_repeater_x == -1) { + stream << "crystal_repeater_x, "; + ok = false; + } + + if (crystal_repeater_y == -1) { + stream << "crystal_repeater_y, "; + ok = false; + } + + if (crystal_repeater_z == -1) { + stream << "crystal_repeater_z, "; + ok = false; + } + + if (block_repeater_y == -1) { + stream << "block_repeater_y, "; + ok = false; + } + + if (block_repeater_z == -1) { + stream << "block_repeater_z, "; + ok = false; + } + + if (!ok) + ret = stream.str(); + + return ok; } END_NAMESPACE_STIR diff --git a/src/IO/InterfileDynamicDiscretisedDensityOutputFileFormat.cxx b/src/IO/InterfileDynamicDiscretisedDensityOutputFileFormat.cxx index 7bb7d1495f..1f28b41fa6 100644 --- a/src/IO/InterfileDynamicDiscretisedDensityOutputFileFormat.cxx +++ b/src/IO/InterfileDynamicDiscretisedDensityOutputFileFormat.cxx @@ -29,84 +29,63 @@ */ #include "stir/IO/InterfileDynamicDiscretisedDensityOutputFileFormat.h" -#include "stir/DynamicDiscretisedDensity.h" +#include "stir/DynamicDiscretisedDensity.h" #include "stir/NumericType.h" #include "stir/Succeeded.h" #include "stir/IO/interfile.h" START_NAMESPACE_STIR -const char * const -InterfileDynamicDiscretisedDensityOutputFileFormat::registered_name = "Interfile"; +const char* const InterfileDynamicDiscretisedDensityOutputFileFormat::registered_name = "Interfile"; -InterfileDynamicDiscretisedDensityOutputFileFormat:: -InterfileDynamicDiscretisedDensityOutputFileFormat(const NumericType& type, - const ByteOrder& byte_order) -{ +InterfileDynamicDiscretisedDensityOutputFileFormat::InterfileDynamicDiscretisedDensityOutputFileFormat( + const NumericType& type, const ByteOrder& byte_order) { base_type::set_defaults(); this->set_type_of_numbers(type); this->set_byte_order(byte_order); } -void -InterfileDynamicDiscretisedDensityOutputFileFormat:: -set_defaults() -{ +void +InterfileDynamicDiscretisedDensityOutputFileFormat::set_defaults() { base_type::set_defaults(); } -void -InterfileDynamicDiscretisedDensityOutputFileFormat:: -initialise_keymap() -{ +void +InterfileDynamicDiscretisedDensityOutputFileFormat::initialise_keymap() { this->parser.add_start_key("Interfile Output File Format Parameters"); this->parser.add_stop_key("End Interfile Output File Format Parameters"); base_type::initialise_keymap(); } -bool -InterfileDynamicDiscretisedDensityOutputFileFormat:: -post_processing() -{ +bool +InterfileDynamicDiscretisedDensityOutputFileFormat::post_processing() { if (base_type::post_processing()) return true; return false; } - -ByteOrder -InterfileDynamicDiscretisedDensityOutputFileFormat:: -set_byte_order(const ByteOrder& new_byte_order, const bool warn) -{ - if (!new_byte_order.is_native_order()) - { - if (warn) - warning("InterfileDynamicDiscretisedDensityOutputFileFormat: byte_order is currently fixed to the native format\n"); - this->file_byte_order = ByteOrder::native; - } - else +ByteOrder +InterfileDynamicDiscretisedDensityOutputFileFormat::set_byte_order(const ByteOrder& new_byte_order, const bool warn) { + if (!new_byte_order.is_native_order()) { + if (warn) + warning("InterfileDynamicDiscretisedDensityOutputFileFormat: byte_order is currently fixed to the native format\n"); + this->file_byte_order = ByteOrder::native; + } else this->file_byte_order = new_byte_order; - return this->file_byte_order; + return this->file_byte_order; } -Succeeded -InterfileDynamicDiscretisedDensityOutputFileFormat:: -actual_write_to_file(std::string& filename, - const DynamicDiscretisedDensity & density) const -{ - // TODO modify write_basic_interfile to return filename - Succeeded success = - write_basic_interfile(filename, density, - this->type_of_numbers, this->scale_to_write_data, - this->file_byte_order); - if (success == Succeeded::yes) - replace_extension(filename, ".hv"); - return success; +Succeeded +InterfileDynamicDiscretisedDensityOutputFileFormat::actual_write_to_file(std::string& filename, + const DynamicDiscretisedDensity& density) const { + // TODO modify write_basic_interfile to return filename + Succeeded success = + write_basic_interfile(filename, density, this->type_of_numbers, this->scale_to_write_data, this->file_byte_order); + if (success == Succeeded::yes) + replace_extension(filename, ".hv"); + return success; } - - -// class InterfileDynamicDiscretisedDensityOutputFileFormat; - +// class InterfileDynamicDiscretisedDensityOutputFileFormat; END_NAMESPACE_STIR diff --git a/src/IO/InterfileHeader.cxx b/src/IO/InterfileHeader.cxx index 7aa6708f7f..bf2f2d40cb 100644 --- a/src/IO/InterfileHeader.cxx +++ b/src/IO/InterfileHeader.cxx @@ -18,8 +18,8 @@ See STIR/LICENSE.txt for details */ /*! - \file - \ingroup InterfileIO + \file + \ingroup InterfileIO \brief implementations for the stir::InterfileHeader class \author Kris Thielemans @@ -49,36 +49,28 @@ using std::vector; #endif START_NAMESPACE_STIR -const double -MinimalInterfileHeader:: -double_value_not_set = -12345.60789; +const double MinimalInterfileHeader::double_value_not_set = -12345.60789; shared_ptr -MinimalInterfileHeader::get_exam_info_sptr() const -{ +MinimalInterfileHeader::get_exam_info_sptr() const { return exam_info_sptr; } const ExamInfo& -MinimalInterfileHeader::get_exam_info() const -{ +MinimalInterfileHeader::get_exam_info() const { return *exam_info_sptr; } -MinimalInterfileHeader::MinimalInterfileHeader() - : KeyParser() -{ +MinimalInterfileHeader::MinimalInterfileHeader() : KeyParser() { exam_info_sptr.reset(new ExamInfo); // need to default to PET for backwards compatibility - //this->exam_info_sptr->imaging_modality = ImagingModality::PT; + // this->exam_info_sptr->imaging_modality = ImagingModality::PT; add_start_key("INTERFILE"); - add_key("imaging modality", - KeyArgument::ASCII, (KeywordProcessor)&MinimalInterfileHeader::set_imaging_modality, - &imaging_modality_as_string); - - add_key("version of keys", - KeyArgument::ASCII, (KeywordProcessor)&MinimalInterfileHeader::set_version_specific_keys, + add_key("imaging modality", KeyArgument::ASCII, (KeywordProcessor)&MinimalInterfileHeader::set_imaging_modality, + &imaging_modality_as_string); + + add_key("version of keys", KeyArgument::ASCII, (KeywordProcessor)&MinimalInterfileHeader::set_version_specific_keys, &version_of_keys); // support for siemens interfile @@ -86,38 +78,34 @@ MinimalInterfileHeader::MinimalInterfileHeader() add_stop_key("END OF INTERFILE"); } - - -void MinimalInterfileHeader::set_imaging_modality() -{ +void +MinimalInterfileHeader::set_imaging_modality() { set_variable(); this->exam_info_sptr->imaging_modality = ImagingModality(imaging_modality_as_string); } -void MinimalInterfileHeader::set_version_specific_keys() -{ +void +MinimalInterfileHeader::set_version_specific_keys() { set_variable(); } -InterfileHeader::InterfileHeader() - : MinimalInterfileHeader() -{ +InterfileHeader::InterfileHeader() : MinimalInterfileHeader() { number_format_values.push_back("bit"); number_format_values.push_back("ascii"); number_format_values.push_back("signed integer"); number_format_values.push_back("unsigned integer"); number_format_values.push_back("float"); - + byte_order_values.push_back("LITTLEENDIAN"); byte_order_values.push_back("BIGENDIAN"); - + PET_data_type_values.push_back("Emission"); PET_data_type_values.push_back("Transmission"); PET_data_type_values.push_back("Blank"); PET_data_type_values.push_back("AttenuationCorrection"); PET_data_type_values.push_back("Normalisation"); PET_data_type_values.push_back("Image"); - + type_of_data_values.push_back("Static"); type_of_data_values.push_back("Dynamic"); type_of_data_values.push_back("Tomographic"); @@ -125,42 +113,42 @@ InterfileHeader::InterfileHeader() type_of_data_values.push_back("ROI"); type_of_data_values.push_back("PET"); type_of_data_values.push_back("Other"); - + patient_orientation_values.push_back("head_in"); patient_orientation_values.push_back("feet_in"); patient_orientation_values.push_back("other"); - patient_orientation_values.push_back("unknown"); //default + patient_orientation_values.push_back("unknown"); // default patient_rotation_values.push_back("supine"); patient_rotation_values.push_back("prone"); patient_rotation_values.push_back("right"); patient_rotation_values.push_back("left"); patient_rotation_values.push_back("other"); - patient_rotation_values.push_back("unknown"); //default + patient_rotation_values.push_back("unknown"); // default // default values // KT 07/10/2002 added 2 new ones number_format_index = 3; // unsigned integer - bytes_per_pixel = -1; // standard does not provide a default + bytes_per_pixel = -1; // standard does not provide a default // KT 02/11/98 set default for correct variable - byte_order_index = 1;// file_byte_order = ByteOrder::big_endian; + byte_order_index = 1; // file_byte_order = ByteOrder::big_endian; - type_of_data_index = 6; // PET - PET_data_type_index = 5; // Image - patient_orientation_index = 3; //unknown - patient_rotation_index = 5; //unknown - num_dimensions = 2; // set to 2 to be compatible with Interfile version 3.3 (which doesn't have this keyword) + type_of_data_index = 6; // PET + PET_data_type_index = 5; // Image + patient_orientation_index = 3; // unknown + patient_rotation_index = 5; // unknown + num_dimensions = 2; // set to 2 to be compatible with Interfile version 3.3 (which doesn't have this keyword) matrix_labels.resize(num_dimensions); matrix_size.resize(num_dimensions); pixel_sizes.resize(num_dimensions, 1.); num_energy_windows = 1; lower_en_window_thresholds.resize(num_energy_windows); upper_en_window_thresholds.resize(num_energy_windows); - lower_en_window_thresholds[0]=-1.F; - upper_en_window_thresholds[0]=-1.F; + lower_en_window_thresholds[0] = -1.F; + upper_en_window_thresholds[0] = -1.F; num_time_frames = 1; image_scaling_factors.resize(num_time_frames); - for (int i=0; ioriginating_system); ignore_key("GENERAL DATA"); ignore_key("GENERAL IMAGE DATA"); - add_key("isotope name", &isotope_name); + add_key("isotope name", &isotope_name); add_key("study date", &study_date_time.date); add_key("study_time", &study_date_time.time); - add_key("type of data", - KeyArgument::ASCIIlist, - (KeywordProcessor)&InterfileHeader::set_type_of_data, - &type_of_data_index, + add_key("type of data", KeyArgument::ASCIIlist, (KeywordProcessor)&InterfileHeader::set_type_of_data, &type_of_data_index, &type_of_data_values); - add_key("patient orientation", - &patient_orientation_index, - &patient_orientation_values); - add_key("patient rotation", - &patient_rotation_index, - &patient_rotation_values); + add_key("patient orientation", &patient_orientation_index, &patient_orientation_values); + add_key("patient rotation", &patient_rotation_index, &patient_rotation_values); + add_key("imagedata byte order", &byte_order_index, &byte_order_values); - add_key("imagedata byte order", - &byte_order_index, - &byte_order_values); - ignore_key("data format"); - add_key("number format", - &number_format_index, - &number_format_values); + add_key("number format", &number_format_index, &number_format_values); add_key("number of bytes per pixel", &bytes_per_pixel); - add_key("number of dimensions", - KeyArgument::INT, (KeywordProcessor)&InterfileHeader::read_matrix_info,&num_dimensions); + add_key("number of dimensions", KeyArgument::INT, (KeywordProcessor)&InterfileHeader::read_matrix_info, &num_dimensions); add_vectorised_key("matrix size", &matrix_size); add_vectorised_key("matrix axis label", &matrix_labels); add_vectorised_key("scaling factor (mm/pixel)", &pixel_sizes); - add_key("number of time frames", - KeyArgument::INT, (KeywordProcessor)&InterfileHeader::read_frames_info,&num_time_frames); + add_key("number of time frames", KeyArgument::INT, (KeywordProcessor)&InterfileHeader::read_frames_info, &num_time_frames); add_vectorised_key("image relative start time (sec)", &image_relative_start_times); add_vectorised_key("image duration (sec)", &image_durations); - //image start time[] := + // image start time[] := // ignore these as we'll never use them ignore_key("maximum pixel count"); @@ -222,8 +193,8 @@ InterfileHeader::InterfileHeader() // support for Louvain la Neuve's extension of 3.3 add_key("quantification units", &lln_quantification_units); - add_key("number of energy windows", - KeyArgument::INT, (KeywordProcessor)&InterfileHeader::read_num_energy_windows,&num_energy_windows); + add_key("number of energy windows", KeyArgument::INT, (KeywordProcessor)&InterfileHeader::read_num_energy_windows, + &num_energy_windows); add_vectorised_key("energy window lower level", &lower_en_window_thresholds); add_vectorised_key("energy window upper level", &upper_en_window_thresholds); @@ -233,226 +204,190 @@ InterfileHeader::InterfileHeader() add_key("start vertical bed position (mm)", &bed_position_vertical); } -void InterfileHeader::set_version_specific_keys() -{ +void +InterfileHeader::set_version_specific_keys() { MinimalInterfileHeader::set_version_specific_keys(); - if (this->version_of_keys == "STIR3.0") - { - info("Setting energy window keys as in STIR3.0"); - // only a single energy window, and non-vectorised - remove_key("energy window lower level"); - remove_key("energy window upper level"); - add_key("energy window lower level", &lower_en_window_thresholds[0]); - add_key("energy window upper level", &upper_en_window_thresholds[0]); - } + if (this->version_of_keys == "STIR3.0") { + info("Setting energy window keys as in STIR3.0"); + // only a single energy window, and non-vectorised + remove_key("energy window lower level"); + remove_key("energy window upper level"); + add_key("energy window lower level", &lower_en_window_thresholds[0]); + add_key("energy window upper level", &upper_en_window_thresholds[0]); + } } // MJ 17/05/2000 made bool -bool InterfileHeader::post_processing() -{ - if(type_of_data_index<0) - { - warning("Interfile Warning: 'type_of_data' keyword required"); - return true; - } +bool +InterfileHeader::post_processing() { + if (type_of_data_index < 0) { + warning("Interfile Warning: 'type_of_data' keyword required"); + return true; + } - if (!study_date_time.date.empty() && !study_date_time.time.empty()) - { - try - { - exam_info_sptr->start_time_in_secs_since_1970 = - Interfile_datetime_to_secs_since_Unix_epoch(study_date_time); - } - catch(...) - {} + if (!study_date_time.date.empty() && !study_date_time.time.empty()) { + try { + exam_info_sptr->start_time_in_secs_since_1970 = Interfile_datetime_to_secs_since_Unix_epoch(study_date_time); + } catch (...) { } - - if (!isotope_name.empty()){ - this->exam_info_sptr->set_radionuclide(isotope_name); } - - if (patient_orientation_index<0 || patient_rotation_index<0) + + if (!isotope_name.empty()) { + this->exam_info_sptr->set_radionuclide(isotope_name); + } + + if (patient_orientation_index < 0 || patient_rotation_index < 0) return true; // warning: relies on index taking same values as enums in PatientPosition exam_info_sptr->patient_position.set_rotation(static_cast(patient_rotation_index)); exam_info_sptr->patient_position.set_orientation(static_cast(patient_orientation_index)); - if (number_format_index<0 || - static_cast(number_format_index)>=number_format_values.size()) - { + if (number_format_index < 0 || static_cast(number_format_index) >= number_format_values.size()) { warning("Interfile internal error: 'number_format_index' out of range\n"); return true; } // KT 07/10/2002 new // check if bytes_per_pixel is set if the data type is not 'bit' - if (number_format_index!=0 && bytes_per_pixel<=0) - { + if (number_format_index != 0 && bytes_per_pixel <= 0) { warning("Interfile error: 'number of bytes per pixel' keyword should be set\n to a number > 0"); return true; } type_of_numbers = NumericType(number_format_values[number_format_index], bytes_per_pixel); - - file_byte_order = byte_order_index==0 ? - ByteOrder::little_endian : ByteOrder::big_endian; - + + file_byte_order = byte_order_index == 0 ? ByteOrder::little_endian : ByteOrder::big_endian; + // KT 07/10/2002 more extensive error checking for matrix_size keyword - if (matrix_size.size()==0) - { + if (matrix_size.size() == 0) { warning("Interfile error: no matrix size keywords present\n"); return true; } - if (matrix_size[matrix_size.size()-1].size()!=1) - { + if (matrix_size[matrix_size.size() - 1].size() != 1) { warning("Interfile error: last dimension (%d) of 'matrix size' cannot be a list of numbers\n", - matrix_size[matrix_size.size()-1].size()); + matrix_size[matrix_size.size() - 1].size()); return true; } - for (unsigned int dim=0; dim != matrix_size.size(); ++dim) - { - if (matrix_size[dim].size()==0) - { + for (unsigned int dim = 0; dim != matrix_size.size(); ++dim) { + if (matrix_size[dim].size() == 0) { warning("Interfile error: dimension (%d) of 'matrix size' not present\n", dim); return true; } - for (unsigned int i=0; i != matrix_size[dim].size(); ++i) - { - if (matrix_size[dim][i]<=0) - { + for (unsigned int i = 0; i != matrix_size[dim].size(); ++i) { + if (matrix_size[dim][i] <= 0) { warning("Interfile error: dimension (%d) of 'matrix size' has a number <= 0 at position\n", dim, i); return true; } } } - for (int frame=0; frameget_num_datasets(); frame++) - { - if (image_scaling_factors[frame].size() == 1) - { + for (int frame = 0; frame < this->get_num_datasets(); frame++) { + if (image_scaling_factors[frame].size() == 1) { // use the only value for every scaling factor - image_scaling_factors[frame].resize(matrix_size[matrix_size.size()-1][0]); - for (unsigned int i=1; i(image_scaling_factors[frame].size()) != matrix_size[matrix_size.size()-1][0]) - { + image_scaling_factors[frame].resize(matrix_size[matrix_size.size() - 1][0]); + for (unsigned int i = 1; i < image_scaling_factors[frame].size(); i++) + image_scaling_factors[frame][i] = image_scaling_factors[frame][0]; + } else if (static_cast(image_scaling_factors[frame].size()) != matrix_size[matrix_size.size() - 1][0]) { warning("Interfile error: wrong number of image scaling factors\n"); return true; } } - + // KT 07/10/2002 new // support for non-standard key // TODO as there's currently no way to find out if a key was used in the header, we just rely on the // fact that the default didn't change. This isn't good enough, but it has to do for now. - if (lln_quantification_units!=1.) - { - const bool all_one = image_scaling_factors[0][0] == 1.; - for (int frame=0; frameget_num_datasets(); frame++) - for (unsigned int i=0; iget_num_datasets(); frame++) + for (unsigned int i = 0; i < image_scaling_factors[frame].size(); i++) { + // check if all image_scaling_factors are equal to 1 (i.e. the image_scaling_factors keyword // probably never occured) or lln_quantification_units if ((all_one && image_scaling_factors[frame][i] != 1.) || - (!all_one && image_scaling_factors[frame][i] != lln_quantification_units)) - { - warning("Interfile error: key 'quantification units' can only be used when either " - "image_scaling_factors[] keywords are not present, or have identical values.\n"); - return true; - } + (!all_one && image_scaling_factors[frame][i] != lln_quantification_units)) { + warning("Interfile error: key 'quantification units' can only be used when either " + "image_scaling_factors[] keywords are not present, or have identical values.\n"); + return true; + } // if they're all 1, we set the value to lln_quantification_units if (all_one) image_scaling_factors[frame][i] = lln_quantification_units; } - if (all_one) - { - warning("Interfile warning: non-standard key 'quantification_units' used to set 'image_scaling_factors' to %g\n", - lln_quantification_units); - } + if (all_one) { + warning("Interfile warning: non-standard key 'quantification_units' used to set 'image_scaling_factors' to %g\n", + lln_quantification_units); + } } // lln_quantification_units - if (num_energy_windows>0) - { - if (num_energy_windows>1) - warning("Currently only reading the first energy window."); - if (upper_en_window_thresholds[0] > 0 && lower_en_window_thresholds[0] > 0 ) - { - exam_info_sptr->set_high_energy_thres(upper_en_window_thresholds[0]); - exam_info_sptr->set_low_energy_thres(lower_en_window_thresholds[0]); - } + if (num_energy_windows > 0) { + if (num_energy_windows > 1) + warning("Currently only reading the first energy window."); + if (upper_en_window_thresholds[0] > 0 && lower_en_window_thresholds[0] > 0) { + exam_info_sptr->set_high_energy_thres(upper_en_window_thresholds[0]); + exam_info_sptr->set_low_energy_thres(lower_en_window_thresholds[0]); } + } - exam_info_sptr->time_frame_definitions = - TimeFrameDefinitions(image_relative_start_times, image_durations); + exam_info_sptr->time_frame_definitions = TimeFrameDefinitions(image_relative_start_times, image_durations); return false; - } -void InterfileHeader::read_matrix_info() -{ +void +InterfileHeader::read_matrix_info() { set_variable(); matrix_labels.resize(num_dimensions); matrix_size.resize(num_dimensions); pixel_sizes.resize(num_dimensions, 1.); - } -void InterfileHeader::read_num_energy_windows() -{ +void +InterfileHeader::read_num_energy_windows() { set_variable(); - upper_en_window_thresholds.resize(num_energy_windows,-1.); - lower_en_window_thresholds.resize(num_energy_windows,-1.); + upper_en_window_thresholds.resize(num_energy_windows, -1.); + lower_en_window_thresholds.resize(num_energy_windows, -1.); } -void InterfileHeader::set_type_of_data() -{ +void +InterfileHeader::set_type_of_data() { set_variable(); - + if (this->type_of_data_index == -1) error("Interfile parsing: type_of_data needs to be set to supported value"); const string type_of_data = this->type_of_data_values[this->type_of_data_index]; - if (type_of_data == "PET") - { - ignore_key("PET STUDY (Emission data)"); - ignore_key("PET STUDY (Image data)"); - ignore_key("PET STUDY (General)"); - add_key("PET data type", - &PET_data_type_index, - &PET_data_type_values); - ignore_key("process status"); - ignore_key("IMAGE DATA DESCRIPTION"); - // TODO rename keyword - add_vectorised_key("data offset in bytes", &data_offset_each_dataset); + if (type_of_data == "PET") { + ignore_key("PET STUDY (Emission data)"); + ignore_key("PET STUDY (Image data)"); + ignore_key("PET STUDY (General)"); + add_key("PET data type", &PET_data_type_index, &PET_data_type_values); + ignore_key("process status"); + ignore_key("IMAGE DATA DESCRIPTION"); + // TODO rename keyword + add_vectorised_key("data offset in bytes", &data_offset_each_dataset); - } - else if (type_of_data == "Tomographic") - { - ignore_key("SPECT STUDY (General)" ); - ignore_key("SPECT STUDY (acquired data)"); + } else if (type_of_data == "Tomographic") { + ignore_key("SPECT STUDY (General)"); + ignore_key("SPECT STUDY (acquired data)"); - process_status_values.push_back("Reconstructed"); - process_status_values.push_back("Acquired"); - add_key("process status", - &process_status_index, - &process_status_values); + process_status_values.push_back("Reconstructed"); + process_status_values.push_back("Acquired"); + add_key("process status", &process_status_index, &process_status_values); #if 0 // overwrite vectored-value, as v3.3 had a scalar add_key("data offset in bytes", &data_offset); #endif - } + } } -void InterfileHeader::read_frames_info() -{ +void +InterfileHeader::read_frames_info() { set_variable(); const int num_datasets = this->get_num_datasets(); image_scaling_factors.resize(num_datasets); - for (int i=0; iget_num_datasets(); image_scaling_factors.resize(num_datasets); - for (int i=0; ifirst_pixel_offsets.resize(num_dimensions); - std::fill(this->first_pixel_offsets.begin(), this->first_pixel_offsets.end(), - base_type::double_value_not_set); + std::fill(this->first_pixel_offsets.begin(), this->first_pixel_offsets.end(), base_type::double_value_not_set); } // MJ 17/05/2000 made bool -bool InterfileImageHeader::post_processing() -{ +bool +InterfileImageHeader::post_processing() { if (InterfileHeader::post_processing() == true) return true; - + this->exam_info_sptr->set_calibration_factor(calibration_factor); - - if (PET_data_type_values[PET_data_type_index] != "Image") - { warning("Interfile error: expecting an image\n"); return true; } - - if (num_dimensions != 3) - { warning("Interfile error: expecting 3D image\n"); return true; } - if ( (matrix_size[0].size() != 1) || - (matrix_size[1].size() != 1) || - (matrix_size[2].size() != 1) ) - { warning("Interfile error: only handling image with homogeneous dimensions\n"); return true; } + if (PET_data_type_values[PET_data_type_index] != "Image") { + warning("Interfile error: expecting an image\n"); + return true; + } + + if (num_dimensions != 3) { + warning("Interfile error: expecting 3D image\n"); + return true; + } + + if ((matrix_size[0].size() != 1) || (matrix_size[1].size() != 1) || (matrix_size[2].size() != 1)) { + warning("Interfile error: only handling image with homogeneous dimensions\n"); + return true; + } // KT 09/10/98 changed order z,y,x->x,y,z // KT 09/10/98 allow no labels at all - if (matrix_labels[0].length()>0 - && (matrix_labels[0]!="x" || matrix_labels[1]!="y" || - matrix_labels[2]!="z")) - { - warning("Interfile: only supporting x,y,z order of coordinates now.\n"); - return true; - } - std::vector first_pixel_offsets; + if (matrix_labels[0].length() > 0 && (matrix_labels[0] != "x" || matrix_labels[1] != "y" || matrix_labels[2] != "z")) { + warning("Interfile: only supporting x,y,z order of coordinates now.\n"); + return true; + } + std::vector first_pixel_offsets; return false; } /**********************************************************************/ -//KT 26/10/98 +// KT 26/10/98 // KT 13/11/98 moved stream arg from constructor to parse() -InterfilePDFSHeader::InterfilePDFSHeader() - : InterfileHeader() -{ +InterfilePDFSHeader::InterfilePDFSHeader() : InterfileHeader() { num_segments = -1; - add_key("minimum ring difference per segment", - KeyArgument::LIST_OF_INTS, - (KeywordProcessor)&InterfilePDFSHeader::resize_segments_and_set, - &min_ring_difference); - add_key("maximum ring difference per segment", - KeyArgument::LIST_OF_INTS, - (KeywordProcessor)&InterfilePDFSHeader::resize_segments_and_set, - &max_ring_difference); - - + add_key("minimum ring difference per segment", KeyArgument::LIST_OF_INTS, + (KeywordProcessor)&InterfilePDFSHeader::resize_segments_and_set, &min_ring_difference); + add_key("maximum ring difference per segment", KeyArgument::LIST_OF_INTS, + (KeywordProcessor)&InterfilePDFSHeader::resize_segments_and_set, &max_ring_difference); + // warning these keys should match what is in Scanner::parameter_info() // TODO get Scanner to parse these ignore_key("Scanner parameters"); @@ -560,179 +483,141 @@ InterfilePDFSHeader::InterfilePDFSHeader() // first set to some crazy values num_rings = -1; - add_key("number of rings", - &num_rings); + add_key("number of rings", &num_rings); num_detectors_per_ring = -1; - add_key("number of detectors per ring", - &num_detectors_per_ring); + add_key("number of detectors per ring", &num_detectors_per_ring); transaxial_FOV_diameter_in_cm = -1; - add_key("transaxial FOV diameter (cm)", - &transaxial_FOV_diameter_in_cm); + add_key("transaxial FOV diameter (cm)", &transaxial_FOV_diameter_in_cm); inner_ring_diameter_in_cm = -1; - add_key("inner ring diameter (cm)", - &inner_ring_diameter_in_cm); + add_key("inner ring diameter (cm)", &inner_ring_diameter_in_cm); average_depth_of_interaction_in_cm = -1; - add_key("average depth of interaction (cm)", - &average_depth_of_interaction_in_cm); + add_key("average depth of interaction (cm)", &average_depth_of_interaction_in_cm); distance_between_rings_in_cm = -1; - add_key("distance between rings (cm)", - &distance_between_rings_in_cm); + add_key("distance between rings (cm)", &distance_between_rings_in_cm); default_bin_size_in_cm = -1; - add_key("default bin size (cm)", - &default_bin_size_in_cm); + add_key("default bin size (cm)", &default_bin_size_in_cm); // this is a good default value view_offset_in_degrees = 0; - add_key("view offset (degrees)", - &view_offset_in_degrees); - max_num_non_arccorrected_bins=0; - default_num_arccorrected_bins=0; - add_key("Maximum number of non-arc-corrected bins", - &max_num_non_arccorrected_bins); - add_key("Default number of arc-corrected bins", - &default_num_arccorrected_bins); + add_key("view offset (degrees)", &view_offset_in_degrees); + max_num_non_arccorrected_bins = 0; + default_num_arccorrected_bins = 0; + add_key("Maximum number of non-arc-corrected bins", &max_num_non_arccorrected_bins); + add_key("Default number of arc-corrected bins", &default_num_arccorrected_bins); num_axial_blocks_per_bucket = 0; - add_key("number of blocks_per_bucket in axial direction", - &num_axial_blocks_per_bucket); + add_key("number of blocks_per_bucket in axial direction", &num_axial_blocks_per_bucket); num_transaxial_blocks_per_bucket = 0; - add_key("number of blocks_per_bucket in transaxial direction", - &num_transaxial_blocks_per_bucket); + add_key("number of blocks_per_bucket in transaxial direction", &num_transaxial_blocks_per_bucket); num_axial_crystals_per_block = 0; - add_key("number of crystals_per_block in axial direction", - &num_axial_crystals_per_block); + add_key("number of crystals_per_block in axial direction", &num_axial_crystals_per_block); num_transaxial_crystals_per_block = 0; - add_key("number of crystals_per_block in transaxial direction", - &num_transaxial_crystals_per_block); + add_key("number of crystals_per_block in transaxial direction", &num_transaxial_crystals_per_block); num_axial_crystals_per_singles_unit = -1; - add_key("number of crystals_per_singles_unit in axial direction", - &num_axial_crystals_per_singles_unit); + add_key("number of crystals_per_singles_unit in axial direction", &num_axial_crystals_per_singles_unit); num_transaxial_crystals_per_singles_unit = -1; - add_key("number of crystals_per_singles_unit in transaxial direction", - &num_transaxial_crystals_per_singles_unit); + add_key("number of crystals_per_singles_unit in transaxial direction", &num_transaxial_crystals_per_singles_unit); // sensible default num_detector_layers = 1; - add_key("number of detector layers", - &num_detector_layers); + add_key("number of detector layers", &num_detector_layers); energy_resolution = -1.f; - add_key("Energy resolution", - &energy_resolution); + add_key("Energy resolution", &energy_resolution); reference_energy = -1.f; - add_key("Reference energy (in keV)", - &reference_energy); + add_key("Reference energy (in keV)", &reference_energy); - tof_mash_factor=-1; - add_key("%TOF mashing factor", - &tof_mash_factor); + tof_mash_factor = -1; + add_key("%TOF mashing factor", &tof_mash_factor); max_num_timing_poss = -1; - add_key("Number of TOF time bins", - &max_num_timing_poss); + add_key("Number of TOF time bins", &max_num_timing_poss); size_of_timing_pos = -1.f; - add_key("Size of timing bin (ps)", - &size_of_timing_pos); + add_key("Size of timing bin (ps)", &size_of_timing_pos); timing_resolution = -1.f; - add_key("Timing resolution (ps)", - &timing_resolution); + add_key("Timing resolution (ps)", &timing_resolution); ignore_key("end scanner parameters"); - + effective_central_bin_size_in_cm = -1; - add_key("effective central bin size (cm)", - &effective_central_bin_size_in_cm); + add_key("effective central bin size (cm)", &effective_central_bin_size_in_cm); add_key("applied corrections", &applied_corrections); } -void InterfilePDFSHeader::resize_segments_and_set() -{ +void +InterfilePDFSHeader::resize_segments_and_set() { // find_storage_order returns true if already found (or error) - if (num_segments < 0 && !find_storage_order()) - { + if (num_segments < 0 && !find_storage_order()) { min_ring_difference.resize(num_segments); max_ring_difference.resize(num_segments); - } - + if (num_segments >= 0) set_variable(); - } -int InterfilePDFSHeader::find_storage_order() -{ +int +InterfilePDFSHeader::find_storage_order() { /* if(type_of_data_values[type_of_data_index] != "PET") - { - - warning("Interfile error: expecting PET study "); - stop_parsing(); - return true; + { + + warning("Interfile error: expecting PET study "); + stop_parsing(); + return true; - } + } */ - if (num_dimensions != 4 && - num_dimensions != 5) - { + if (num_dimensions != 4 && num_dimensions != 5) { warning("Interfile error: expecting 4D structure or 5D in case of TOF information "); stop_parsing(); - return true; + return true; } - if (matrix_labels[0] != "tangential coordinate") - { + if (matrix_labels[0] != "tangential coordinate") { // use error message with index [1] as that is what the user sees. - warning("Interfile error: expecting 'matrix axis label[1] := tangential coordinate'\n"); + warning("Interfile error: expecting 'matrix axis label[1] := tangential coordinate'\n"); stop_parsing(); - return true; + return true; } num_bins = matrix_size[0][0]; - - if (matrix_labels[3] == "segment") - { + + if (matrix_labels[3] == "segment") { num_segments = matrix_size[3][0]; - if (matrix_labels[1] == "axial coordinate" && matrix_labels[2] == "view") - { - // 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]; + if (matrix_labels[1] == "axial coordinate" && matrix_labels[2] == "view") { + // 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()); -#else - num_rings_per_segment = matrix_size[1]; + num_rings_per_segment.assign(matrix_size[1].begin(), matrix_size[1].end()); +#else + num_rings_per_segment = matrix_size[1]; #endif - } } - else - { - storage_order = ProjDataFromStream::Segment_View_AxialPos_TangPos; - num_views = matrix_size[2][0]; + } 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()); + 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 if (matrix_labels[1] == "view" && matrix_labels[2] == "axial coordinate") - { + } + } else if (matrix_labels[1] == "view" && matrix_labels[2] == "axial coordinate") { storage_order = ProjDataFromStream::Segment_AxialPos_View_TangPos; num_views = matrix_size[1][0]; #ifdef _MSC_VER - + num_rings_per_segment.assign(matrix_size[2].begin(), matrix_size[2].end()); - + #else num_rings_per_segment = matrix_size[2]; #endif } - + } /* - else if (matrix_labels[3] == "view" && + else if (matrix_labels[3] == "view" && matrix_labels[2] == "segment" && matrix_labels[1] == "axial coordinate") { storage_order = ProjDataFromStream::View_Segment_AxialPos_TangPos; @@ -743,54 +628,40 @@ int InterfilePDFSHeader::find_storage_order() #else num_rings_per_segment = matrix_size[1]; #endif - + } */ - else - { - warning("Interfile error: matrix labels not in expected (or supported) format\n"); + else { + warning("Interfile error: matrix labels not in expected (or supported) format\n"); stop_parsing(); - return true; + return true; } - + return false; - } // definition for using sort() below. -// This is a function object that allows comparing the first elements of 2 +// This is a function object that allows comparing the first elements of 2 // pairs. template -class compare_first : -public binary_function -{ +class compare_first : public binary_function { public: - bool operator()(const pair& p1, const pair& p2) const - { - return p1.first < p2.first; - } + bool operator()(const pair& p1, const pair& p2) const { return p1.first < p2.first; } }; - -// This function assigns segment numbers by sorting the average -// ring differences. It returns a list of the segment numbers +// This function assigns segment numbers by sorting the average +// ring differences. It returns a list of the segment numbers // in the same order as the min/max_ring_difference vectors void -find_segment_sequence(vector& segment_sequence, - VectorWithOffset& sorted_num_rings_per_segment, - VectorWithOffset& sorted_min_ring_diff, - VectorWithOffset& sorted_max_ring_diff, - vector& num_rings_per_segment, - const vector& min_ring_difference, - const vector& max_ring_difference) -{ +find_segment_sequence(vector& segment_sequence, VectorWithOffset& sorted_num_rings_per_segment, + VectorWithOffset& sorted_min_ring_diff, VectorWithOffset& sorted_max_ring_diff, + vector& num_rings_per_segment, const vector& min_ring_difference, + const vector& max_ring_difference) { const int num_segments = static_cast(min_ring_difference.size()); - assert(num_segments%2 == 1); - - - vector< pair > sum_and_location(num_segments); - for (int i=0; i> sum_and_location(num_segments); + for (int i = 0; i < num_segments; i++) { sum_and_location[i].first = static_cast(min_ring_difference[i] + max_ring_difference[i]); sum_and_location[i].second = i; } @@ -810,11 +681,10 @@ find_segment_sequence(vector& segment_sequence, cerr<< sum_and_location[i].second<<" "; } cerr<()); + std::sort(sum_and_location.begin(), sum_and_location.end(), compare_first()); #if 0 cerr<<"display sum_sorted"<& segment_sequence, cerr<< sum_and_location[i].first<<" "; } cerr< 1E-3) - { - error("This data does not seem to contain segment 0. \n" - "We can't handle this at the moment. Sorry."); + + if (segment_zero_num == num_segments || sum_and_location[segment_zero_num].first > 1E-3) { + error("This data does not seem to contain segment 0. \n" + "We can't handle this at the moment. Sorry."); } - - vector< pair > location_and_segment_num(num_segments); - for (int i=0; i> location_and_segment_num(num_segments); + for (int i = 0; i < num_segments; i++) { location_and_segment_num[i].first = sum_and_location[i].second; location_and_segment_num[i].second = i - segment_zero_num; } @@ -860,30 +725,24 @@ find_segment_sequence(vector& segment_sequence, } cerr<(ceil(num_segments/2 )); - - sorted_min_ring_diff = VectorWithOffset(min_segment_num,max_segment_num); - sorted_max_ring_diff = VectorWithOffset(min_segment_num,max_segment_num); - sorted_num_rings_per_segment= VectorWithOffset(min_segment_num,max_segment_num); - - - for (int i=0; i(ceil(num_segments/2 )); + + sorted_min_ring_diff = VectorWithOffset(min_segment_num, max_segment_num); + sorted_max_ring_diff = VectorWithOffset(min_segment_num, max_segment_num); + sorted_num_rings_per_segment = VectorWithOffset(min_segment_num, max_segment_num); + + for (int i = 0; i < num_segments; i++) { + sorted_min_ring_diff[(location_and_segment_num[i].second)] = min_ring_difference[(location_and_segment_num[i].first)]; + + sorted_max_ring_diff[(location_and_segment_num[i].second)] = max_ring_difference[(location_and_segment_num[i].first)]; + + sorted_num_rings_per_segment[(location_and_segment_num[i].second)] = + num_rings_per_segment[(location_and_segment_num[i].first)]; } #if 0 @@ -911,16 +770,13 @@ find_segment_sequence(vector& segment_sequence, cerr<()); - + sort(location_and_segment_num.begin(), location_and_segment_num.end(), compare_first()); + + segment_sequence.resize(num_segments); + for (int i = 0; i < num_segments; i++) + segment_sequence[i] = location_and_segment_num[i].second; - segment_sequence.resize(num_segments); - for (int i=0; i& segment_sequence, cerr<(num_segments)) - { - warning("Interfile error: per-segment information is inconsistent: min_ring_difference\n"); + + if (PET_data_type_values[PET_data_type_index] != "Emission") { + warning("Interfile error: expecting emission data\n"); return true; } - if (max_ring_difference.size() != static_cast(num_segments)) - { - warning("Interfile error: per-segment information is inconsistent: max_ring_difference\n"); + + if (min_ring_difference.size() != static_cast(num_segments)) { + warning("Interfile error: per-segment information is inconsistent: min_ring_difference\n"); return true; } - if (num_rings_per_segment.size()!= static_cast(num_segments)) - { - warning("Interfile error: per-segment information is inconsistent: num_rings_per_segment\n"); + if (max_ring_difference.size() != static_cast(num_segments)) { + warning("Interfile error: per-segment information is inconsistent: max_ring_difference\n"); return true; } - + if (num_rings_per_segment.size() != static_cast(num_segments)) { + warning("Interfile error: per-segment information is inconsistent: num_rings_per_segment\n"); + return true; + } + // check for arc-correction - if (applied_corrections.size() == 0) - { + if (applied_corrections.size() == 0) { warning("\nParsing Interfile header for projection data: \n" "\t'applied corrections' keyword not found. Assuming arc-corrected data\n"); is_arccorrected = true; - } - else - { + } else { is_arccorrected = false; for ( #ifndef STIR_NO_NAMESPACES - std:: + std:: #endif - vector::const_iterator iter = applied_corrections.begin(); - iter != applied_corrections.end(); - ++iter) - { + vector::const_iterator iter = applied_corrections.begin(); + iter != applied_corrections.end(); ++iter) { const string correction = standardise_keyword(*iter); - if(correction == "arc correction" || correction == "arc corrected") - { + if (correction == "arc correction" || correction == "arc corrected") { is_arccorrected = true; break; - } - else if (correction != "none") - warning("\nParsing Interfile header for projection data: \n" - "\t value '%s' for keyword 'applied corrections' ignored\n", - correction.c_str()); - + } else if (correction != "none") + warning("\nParsing Interfile header for projection data: \n" + "\t value '%s' for keyword 'applied corrections' ignored\n", + correction.c_str()); } - } - + VectorWithOffset sorted_min_ring_diff; VectorWithOffset sorted_max_ring_diff; VectorWithOffset sorted_num_rings_per_segment; - - find_segment_sequence( segment_sequence,sorted_num_rings_per_segment, - sorted_min_ring_diff,sorted_max_ring_diff, - num_rings_per_segment, - min_ring_difference, max_ring_difference); + + find_segment_sequence(segment_sequence, sorted_num_rings_per_segment, sorted_min_ring_diff, sorted_max_ring_diff, + num_rings_per_segment, min_ring_difference, max_ring_difference); #if 0 cerr << "PDFS data read inferred header :\n"; cerr << "Segment sequence :"; @@ -1021,302 +862,250 @@ bool InterfilePDFSHeader::post_processing() cerr << sorted_num_rings_per_segment[i] << " "; cerr << endl; cerr << "Total number of planes :" - << -#ifndef STIR_NO_NAMESPACES // stupid work-around for VC + << +# ifndef STIR_NO_NAMESPACES // stupid work-around for VC std::accumulate -#else +# else accumulate -#endif +# endif (num_rings_per_segment.begin(), num_rings_per_segment.end(), 0) << endl; #endif - + // handle scanner shared_ptr guessed_scanner_ptr(Scanner::get_scanner_from_name(get_exam_info().originating_system)); - bool originating_system_was_recognised = - guessed_scanner_ptr->get_type() != Scanner::Unknown_scanner; - if (!originating_system_was_recognised) - { + bool originating_system_was_recognised = guessed_scanner_ptr->get_type() != Scanner::Unknown_scanner; + if (!originating_system_was_recognised) { // feable attempt to guess the system by checking the num_views etc - char const * warning_msg = 0; - if (num_detectors_per_ring < 1) - { - num_detectors_per_ring = num_views*2; + char const* warning_msg = 0; + if (num_detectors_per_ring < 1) { + num_detectors_per_ring = num_views * 2; warning_msg = "\nInterfile warning: I don't recognise 'originating system' value.\n" - "\tI guessed %s from 'num_views' (note: this guess is wrong for mashed data)\n" - " and 'number of rings'\n"; - } - else - { + "\tI guessed %s from 'num_views' (note: this guess is wrong for mashed data)\n" + " and 'number of rings'\n"; + } else { warning_msg = "\nInterfile warning: I don't recognise 'originating system' value.\n" - "I guessed %s from 'number of detectors per ring' and 'number of rings'\n"; + "I guessed %s from 'number of detectors per ring' and 'number of rings'\n"; } - - - switch (num_detectors_per_ring) - { - case 192*2: - guessed_scanner_ptr.reset(new Scanner( Scanner::E953)); + + switch (num_detectors_per_ring) { + case 192 * 2: + guessed_scanner_ptr.reset(new Scanner(Scanner::E953)); warning(warning_msg, "ECAT 953"); break; - case 336*2: - guessed_scanner_ptr.reset(new Scanner( Scanner::Advance)); + case 336 * 2: + guessed_scanner_ptr.reset(new Scanner(Scanner::Advance)); warning(warning_msg, "Advance"); break; - case 288*2: - if(num_rings == 104) - { //added by Dylan Togane - guessed_scanner_ptr.reset(new Scanner( Scanner::HRRT)); - warning(warning_msg, "HRRT"); - } - else if (num_rings == 48) - { - guessed_scanner_ptr.reset(new Scanner( Scanner::E966)); - warning(warning_msg, "ECAT 966"); - } - else if (num_rings == 32) - { - guessed_scanner_ptr.reset(new Scanner( Scanner::E962)); - warning(warning_msg, "ECAT 962"); + case 288 * 2: + if (num_rings == 104) { // added by Dylan Togane + guessed_scanner_ptr.reset(new Scanner(Scanner::HRRT)); + warning(warning_msg, "HRRT"); + } else if (num_rings == 48) { + guessed_scanner_ptr.reset(new Scanner(Scanner::E966)); + warning(warning_msg, "ECAT 966"); + } else if (num_rings == 32) { + guessed_scanner_ptr.reset(new Scanner(Scanner::E962)); + warning(warning_msg, "ECAT 962"); } break; // Dylan Togane [dtogane@camhpet.on.ca] 30/07/2002 bug fix: added break - case 256*2: - guessed_scanner_ptr.reset(new Scanner( Scanner::E951)); + case 256 * 2: + guessed_scanner_ptr.reset(new Scanner(Scanner::E951)); warning(warning_msg, "ECAT 951"); break; } if (guessed_scanner_ptr->get_type() == Scanner::Unknown_scanner) warning("\nInterfile warning: I did not recognise the scanner neither from \n" - "'originating_system' or 'number of detectors per ring' and 'number of rings'.\n"); + "'originating_system' or 'number of detectors per ring' and 'number of rings'.\n"); } bool mismatch_between_header_and_guess = false; - + if (guessed_scanner_ptr->get_type() != Scanner::Unknown_scanner && - guessed_scanner_ptr->get_type() != Scanner::User_defined_scanner) - { - // fill in values which are not in the Interfile header - + guessed_scanner_ptr->get_type() != Scanner::User_defined_scanner) { + // fill in values which are not in the Interfile header + if (num_rings < 1) num_rings = guessed_scanner_ptr->get_num_rings(); if (num_detectors_per_ring < 1) - num_detectors_per_ring = guessed_scanner_ptr->get_max_num_views()*2; + num_detectors_per_ring = guessed_scanner_ptr->get_max_num_views() * 2; #if 0 if (transaxial_FOV_diameter_in_cm < 0) transaxial_FOV_diameter_in_cm = guessed_scanner_ptr->FOV_radius*2/10.; #endif if (inner_ring_diameter_in_cm < 0) - inner_ring_diameter_in_cm = guessed_scanner_ptr->get_inner_ring_radius()*2/10.; + inner_ring_diameter_in_cm = guessed_scanner_ptr->get_inner_ring_radius() * 2 / 10.; if (average_depth_of_interaction_in_cm < 0) - average_depth_of_interaction_in_cm = guessed_scanner_ptr->get_average_depth_of_interaction()/10; + average_depth_of_interaction_in_cm = guessed_scanner_ptr->get_average_depth_of_interaction() / 10; if (distance_between_rings_in_cm < 0) - distance_between_rings_in_cm = guessed_scanner_ptr->get_ring_spacing()/10; + distance_between_rings_in_cm = guessed_scanner_ptr->get_ring_spacing() / 10; if (default_bin_size_in_cm < 0) - default_bin_size_in_cm = - guessed_scanner_ptr->get_default_bin_size()/10; + default_bin_size_in_cm = guessed_scanner_ptr->get_default_bin_size() / 10; if (max_num_non_arccorrected_bins <= 0) max_num_non_arccorrected_bins = guessed_scanner_ptr->get_max_num_non_arccorrected_bins(); if (default_num_arccorrected_bins <= 0) default_num_arccorrected_bins = guessed_scanner_ptr->get_default_num_arccorrected_bins(); - - if (num_axial_blocks_per_bucket<=0) + if (num_axial_blocks_per_bucket <= 0) num_axial_blocks_per_bucket = guessed_scanner_ptr->get_num_axial_blocks_per_bucket(); - if (num_transaxial_blocks_per_bucket<=0) + if (num_transaxial_blocks_per_bucket <= 0) num_transaxial_blocks_per_bucket = guessed_scanner_ptr->get_num_transaxial_blocks_per_bucket(); - if (num_axial_crystals_per_block<=0) + if (num_axial_crystals_per_block <= 0) num_axial_crystals_per_block = guessed_scanner_ptr->get_num_axial_crystals_per_block(); - if (num_transaxial_crystals_per_block<=0) + if (num_transaxial_crystals_per_block <= 0) num_transaxial_crystals_per_block = guessed_scanner_ptr->get_num_transaxial_crystals_per_block(); - if (num_axial_crystals_per_singles_unit < 0) - num_axial_crystals_per_singles_unit = - guessed_scanner_ptr->get_num_axial_crystals_per_singles_unit(); - if (num_transaxial_crystals_per_singles_unit < 0) - num_transaxial_crystals_per_singles_unit = - guessed_scanner_ptr->get_num_transaxial_crystals_per_singles_unit(); - if (num_detector_layers<=0) + if (num_axial_crystals_per_singles_unit < 0) + num_axial_crystals_per_singles_unit = guessed_scanner_ptr->get_num_axial_crystals_per_singles_unit(); + if (num_transaxial_crystals_per_singles_unit < 0) + num_transaxial_crystals_per_singles_unit = guessed_scanner_ptr->get_num_transaxial_crystals_per_singles_unit(); + if (num_detector_layers <= 0) num_detector_layers = guessed_scanner_ptr->get_num_detector_layers(); if (energy_resolution < 0) - energy_resolution = guessed_scanner_ptr->get_energy_resolution(); + energy_resolution = guessed_scanner_ptr->get_energy_resolution(); if (reference_energy < 0) - reference_energy = guessed_scanner_ptr->get_reference_energy(); - + reference_energy = guessed_scanner_ptr->get_reference_energy(); + // consistency check with values of the guessed_scanner_ptr we guessed above - if (num_rings != guessed_scanner_ptr->get_num_rings()) - { - warning("Interfile warning: 'number of rings' (%d) is expected to be %d.\n", - num_rings, guessed_scanner_ptr->get_num_rings()); - mismatch_between_header_and_guess = true; - } - if (num_detectors_per_ring != guessed_scanner_ptr->get_num_detectors_per_ring()) - { - warning("Interfile warning: 'number of detectors per ring' (%d) is expected to be %d.\n", - num_detectors_per_ring, guessed_scanner_ptr->get_num_detectors_per_ring()); - mismatch_between_header_and_guess = true; - } - if (fabs(inner_ring_diameter_in_cm - guessed_scanner_ptr->get_inner_ring_radius()*2/10.) > .001) - { - warning("Interfile warning: 'inner ring diameter (cm)' (%f) is expected to be %f.\n", - inner_ring_diameter_in_cm, guessed_scanner_ptr->get_inner_ring_radius()*2/10.); - mismatch_between_header_and_guess = true; - } - if (fabs(average_depth_of_interaction_in_cm - - guessed_scanner_ptr->get_average_depth_of_interaction()/10) > .001) - { - warning("Interfile warning: 'average depth of interaction (cm)' (%f) is expected to be %f.\n", - average_depth_of_interaction_in_cm, - guessed_scanner_ptr->get_average_depth_of_interaction()/10); - mismatch_between_header_and_guess = true; - } - if (fabs(distance_between_rings_in_cm-guessed_scanner_ptr->get_ring_spacing()/10) > .001) - { - warning("Interfile warning: 'distance between rings (cm)' (%f) is expected to be %f.\n", - distance_between_rings_in_cm, guessed_scanner_ptr->get_ring_spacing()/10); - mismatch_between_header_and_guess = true; - } - if (fabs(default_bin_size_in_cm-guessed_scanner_ptr->get_default_bin_size()/10) > .001) - { - warning("Interfile warning: 'default bin size (cm)' (%f) is expected to be %f.\n", - default_bin_size_in_cm, guessed_scanner_ptr->get_default_bin_size()/10); - mismatch_between_header_and_guess = true; - } - if (max_num_non_arccorrected_bins - guessed_scanner_ptr->get_max_num_non_arccorrected_bins()) - { - warning("Interfile warning: 'max_num_non_arccorrected_bins' (%d) is expected to be %d", - max_num_non_arccorrected_bins, guessed_scanner_ptr->get_max_num_non_arccorrected_bins()); - mismatch_between_header_and_guess = true; - } - if (default_num_arccorrected_bins - guessed_scanner_ptr->get_default_num_arccorrected_bins()) - { - warning("Interfile warning: 'default_num_arccorrected_bins' (%d) is expected to be %d", - default_num_arccorrected_bins, guessed_scanner_ptr->get_default_num_arccorrected_bins()); - mismatch_between_header_and_guess = true; - } - if ( - guessed_scanner_ptr->get_num_transaxial_blocks_per_bucket()>0 && - num_transaxial_blocks_per_bucket != guessed_scanner_ptr->get_num_transaxial_blocks_per_bucket()) - { - warning("Interfile warning: num_transaxial_blocks_per_bucket (%d) is expected to be %d.\n", - num_transaxial_blocks_per_bucket, guessed_scanner_ptr->get_num_transaxial_blocks_per_bucket()); - mismatch_between_header_and_guess = true; - } - if ( - guessed_scanner_ptr->get_num_axial_blocks_per_bucket()>0 && - num_axial_blocks_per_bucket != guessed_scanner_ptr->get_num_axial_blocks_per_bucket()) - { - warning("Interfile warning: num_axial_blocks_per_bucket (%d) is expected to be %d.\n", - num_axial_blocks_per_bucket, guessed_scanner_ptr->get_num_axial_blocks_per_bucket()); - mismatch_between_header_and_guess = true; - } - if ( - guessed_scanner_ptr->get_num_axial_crystals_per_block()>0 && - num_axial_crystals_per_block!= guessed_scanner_ptr->get_num_axial_crystals_per_block()) - { - warning("Interfile warning: num_axial_crystals_per_block (%d) is expected to be %d.\n", - num_axial_crystals_per_block, guessed_scanner_ptr->get_num_axial_crystals_per_block()); - mismatch_between_header_and_guess = true; - } - if ( - guessed_scanner_ptr->get_num_transaxial_crystals_per_block()>0 && - num_transaxial_crystals_per_block!= guessed_scanner_ptr->get_num_transaxial_crystals_per_block()) - { - warning("Interfile warning: num_transaxial_crystals_per_block (%d) is expected to be %d.\n", - num_transaxial_crystals_per_block, guessed_scanner_ptr->get_num_transaxial_crystals_per_block()); - mismatch_between_header_and_guess = true; - } - if ( guessed_scanner_ptr->get_num_axial_crystals_per_singles_unit() > 0 && - num_axial_crystals_per_singles_unit != - guessed_scanner_ptr->get_num_axial_crystals_per_singles_unit() ) - { - warning("Interfile warning: axial crystals per singles unit (%d) is expected to be %d.\n", - num_axial_crystals_per_singles_unit, - guessed_scanner_ptr->get_num_axial_crystals_per_singles_unit()); - mismatch_between_header_and_guess = true; - } - if ( guessed_scanner_ptr->get_num_transaxial_crystals_per_singles_unit() > 0 && - num_transaxial_crystals_per_singles_unit != - guessed_scanner_ptr->get_num_transaxial_crystals_per_singles_unit() ) - { - warning("Interfile warning: transaxial crystals per singles unit (%d) is expected to be %d.\n", - num_transaxial_crystals_per_singles_unit, - guessed_scanner_ptr->get_num_transaxial_crystals_per_singles_unit()); - mismatch_between_header_and_guess = true; - } - if ( - guessed_scanner_ptr->get_num_detector_layers()>0 && - num_detector_layers != guessed_scanner_ptr->get_num_detector_layers()) - { - warning("Interfile warning: num_detector_layers (%d) is expected to be %d.\n", - num_detector_layers, guessed_scanner_ptr->get_num_detector_layers()); - mismatch_between_header_and_guess = true; - } + if (num_rings != guessed_scanner_ptr->get_num_rings()) { + warning("Interfile warning: 'number of rings' (%d) is expected to be %d.\n", num_rings, + guessed_scanner_ptr->get_num_rings()); + mismatch_between_header_and_guess = true; + } + if (num_detectors_per_ring != guessed_scanner_ptr->get_num_detectors_per_ring()) { + warning("Interfile warning: 'number of detectors per ring' (%d) is expected to be %d.\n", num_detectors_per_ring, + guessed_scanner_ptr->get_num_detectors_per_ring()); + mismatch_between_header_and_guess = true; + } + if (fabs(inner_ring_diameter_in_cm - guessed_scanner_ptr->get_inner_ring_radius() * 2 / 10.) > .001) { + warning("Interfile warning: 'inner ring diameter (cm)' (%f) is expected to be %f.\n", inner_ring_diameter_in_cm, + guessed_scanner_ptr->get_inner_ring_radius() * 2 / 10.); + mismatch_between_header_and_guess = true; + } + if (fabs(average_depth_of_interaction_in_cm - guessed_scanner_ptr->get_average_depth_of_interaction() / 10) > .001) { + warning("Interfile warning: 'average depth of interaction (cm)' (%f) is expected to be %f.\n", + average_depth_of_interaction_in_cm, guessed_scanner_ptr->get_average_depth_of_interaction() / 10); + mismatch_between_header_and_guess = true; + } + if (fabs(distance_between_rings_in_cm - guessed_scanner_ptr->get_ring_spacing() / 10) > .001) { + warning("Interfile warning: 'distance between rings (cm)' (%f) is expected to be %f.\n", distance_between_rings_in_cm, + guessed_scanner_ptr->get_ring_spacing() / 10); + mismatch_between_header_and_guess = true; + } + if (fabs(default_bin_size_in_cm - guessed_scanner_ptr->get_default_bin_size() / 10) > .001) { + warning("Interfile warning: 'default bin size (cm)' (%f) is expected to be %f.\n", default_bin_size_in_cm, + guessed_scanner_ptr->get_default_bin_size() / 10); + mismatch_between_header_and_guess = true; + } + if (max_num_non_arccorrected_bins - guessed_scanner_ptr->get_max_num_non_arccorrected_bins()) { + warning("Interfile warning: 'max_num_non_arccorrected_bins' (%d) is expected to be %d", max_num_non_arccorrected_bins, + guessed_scanner_ptr->get_max_num_non_arccorrected_bins()); + mismatch_between_header_and_guess = true; + } + if (default_num_arccorrected_bins - guessed_scanner_ptr->get_default_num_arccorrected_bins()) { + warning("Interfile warning: 'default_num_arccorrected_bins' (%d) is expected to be %d", default_num_arccorrected_bins, + guessed_scanner_ptr->get_default_num_arccorrected_bins()); + mismatch_between_header_and_guess = true; + } + if (guessed_scanner_ptr->get_num_transaxial_blocks_per_bucket() > 0 && + num_transaxial_blocks_per_bucket != guessed_scanner_ptr->get_num_transaxial_blocks_per_bucket()) { + warning("Interfile warning: num_transaxial_blocks_per_bucket (%d) is expected to be %d.\n", + num_transaxial_blocks_per_bucket, guessed_scanner_ptr->get_num_transaxial_blocks_per_bucket()); + mismatch_between_header_and_guess = true; + } + if (guessed_scanner_ptr->get_num_axial_blocks_per_bucket() > 0 && + num_axial_blocks_per_bucket != guessed_scanner_ptr->get_num_axial_blocks_per_bucket()) { + warning("Interfile warning: num_axial_blocks_per_bucket (%d) is expected to be %d.\n", num_axial_blocks_per_bucket, + guessed_scanner_ptr->get_num_axial_blocks_per_bucket()); + mismatch_between_header_and_guess = true; + } + if (guessed_scanner_ptr->get_num_axial_crystals_per_block() > 0 && + num_axial_crystals_per_block != guessed_scanner_ptr->get_num_axial_crystals_per_block()) { + warning("Interfile warning: num_axial_crystals_per_block (%d) is expected to be %d.\n", num_axial_crystals_per_block, + guessed_scanner_ptr->get_num_axial_crystals_per_block()); + mismatch_between_header_and_guess = true; + } + if (guessed_scanner_ptr->get_num_transaxial_crystals_per_block() > 0 && + num_transaxial_crystals_per_block != guessed_scanner_ptr->get_num_transaxial_crystals_per_block()) { + warning("Interfile warning: num_transaxial_crystals_per_block (%d) is expected to be %d.\n", + num_transaxial_crystals_per_block, guessed_scanner_ptr->get_num_transaxial_crystals_per_block()); + mismatch_between_header_and_guess = true; + } + if (guessed_scanner_ptr->get_num_axial_crystals_per_singles_unit() > 0 && + num_axial_crystals_per_singles_unit != guessed_scanner_ptr->get_num_axial_crystals_per_singles_unit()) { + warning("Interfile warning: axial crystals per singles unit (%d) is expected to be %d.\n", + num_axial_crystals_per_singles_unit, guessed_scanner_ptr->get_num_axial_crystals_per_singles_unit()); + mismatch_between_header_and_guess = true; + } + if (guessed_scanner_ptr->get_num_transaxial_crystals_per_singles_unit() > 0 && + num_transaxial_crystals_per_singles_unit != guessed_scanner_ptr->get_num_transaxial_crystals_per_singles_unit()) { + warning("Interfile warning: transaxial crystals per singles unit (%d) is expected to be %d.\n", + num_transaxial_crystals_per_singles_unit, guessed_scanner_ptr->get_num_transaxial_crystals_per_singles_unit()); + mismatch_between_header_and_guess = true; + } + if (guessed_scanner_ptr->get_num_detector_layers() > 0 && + num_detector_layers != guessed_scanner_ptr->get_num_detector_layers()) { + warning("Interfile warning: num_detector_layers (%d) is expected to be %d.\n", num_detector_layers, + guessed_scanner_ptr->get_num_detector_layers()); + mismatch_between_header_and_guess = true; + } // // 06/16: N.E: Currently, the energy resolution and the reference energy, are used only in the // scatter correction. Therefore a waring is displayed but they don't trigger // a mismatch. I assume that the user will handle this. This is in accordance with the // scanner '==' operator, which displays a warning message for these two parameters // but continues as usual. - if (energy_resolution > 0) - { - if (energy_resolution != guessed_scanner_ptr->get_energy_resolution()) - { - warning("Interfile warning: 'energy resolution' (%4.3f) is expected to be %4.3f. " - "Currently, the energy resolution and the reference energy, are used only in" - " scatter correction.", - energy_resolution, guessed_scanner_ptr->get_energy_resolution()); -// mismatch_between_header_and_guess = true; + if (energy_resolution > 0) { + if (energy_resolution != guessed_scanner_ptr->get_energy_resolution()) { + warning("Interfile warning: 'energy resolution' (%4.3f) is expected to be %4.3f. " + "Currently, the energy resolution and the reference energy, are used only in" + " scatter correction.", + energy_resolution, guessed_scanner_ptr->get_energy_resolution()); + // mismatch_between_header_and_guess = true; } - if (reference_energy != guessed_scanner_ptr->get_reference_energy()) - { - warning("Interfile warning: 'reference energy' (%4.3f) is expected to be %4.3f." - "Currently, the energy resolution and the reference energy, are used only in" - " scatter correction.", - reference_energy, guessed_scanner_ptr->get_reference_energy()); -// mismatch_between_header_and_guess = true; + if (reference_energy != guessed_scanner_ptr->get_reference_energy()) { + warning("Interfile warning: 'reference energy' (%4.3f) is expected to be %4.3f." + "Currently, the energy resolution and the reference energy, are used only in" + " scatter correction.", + reference_energy, guessed_scanner_ptr->get_reference_energy()); + // mismatch_between_header_and_guess = true; } } - 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()); - 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()); - 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()); - mismatch_between_header_and_guess = true; - } + 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()); + 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()); + 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()); + mismatch_between_header_and_guess = true; + } } // end of checks. If they failed, we ignore the guess - if (mismatch_between_header_and_guess) - { - warning("Interfile warning: I have used all explicit settings for the scanner\n" - "\tfrom the Interfile header, and remaining fields set from the\n" - "\t%s model.\n", - guessed_scanner_ptr->get_name().c_str()); - if (!originating_system_was_recognised) - guessed_scanner_ptr.reset(new Scanner( Scanner::Unknown_scanner)); - } + if (mismatch_between_header_and_guess) { + warning("Interfile warning: I have used all explicit settings for the scanner\n" + "\tfrom the Interfile header, and remaining fields set from the\n" + "\t%s model.\n", + guessed_scanner_ptr->get_name().c_str()); + if (!originating_system_was_recognised) + guessed_scanner_ptr.reset(new Scanner(Scanner::Unknown_scanner)); } + } if (guessed_scanner_ptr->get_type() == Scanner::Unknown_scanner || - guessed_scanner_ptr->get_type() == Scanner::User_defined_scanner) - { + guessed_scanner_ptr->get_type() == Scanner::User_defined_scanner) { // warn if the Interfile header does not provide enough info if (num_rings < 1) @@ -1339,7 +1128,6 @@ bool InterfilePDFSHeader::post_processing() warning("Interfile warning: 'axial crystals per singles unit' invalid.\n"); if (num_transaxial_crystals_per_singles_unit <= 0) warning("Interfile warning: 'transaxial crystals per singles unit' invalid.\n"); - } // finally, we construct a new scanner object with @@ -1347,89 +1135,50 @@ bool InterfilePDFSHeader::post_processing() shared_ptr scanner_sptr_from_file; - scanner_sptr_from_file.reset( - 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(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, - num_transaxial_crystals_per_singles_unit, - num_detector_layers, - energy_resolution, - reference_energy, - max_num_timing_poss, - size_of_timing_pos, - timing_resolution)); - - bool is_consistent = - scanner_sptr_from_file->check_consistency() == Succeeded::yes; + scanner_sptr_from_file.reset(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(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, num_transaxial_crystals_per_singles_unit, + num_detector_layers, energy_resolution, reference_energy, max_num_timing_poss, size_of_timing_pos, timing_resolution)); + + bool is_consistent = 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("Interfile parsing ended up with the following scanner:\n%s\n", - scanner_sptr_from_file->parameter_info().c_str()); - } - - + scanner_sptr_from_file->get_type() == Scanner::User_defined_scanner || mismatch_between_header_and_guess || + !is_consistent) { + warning("Interfile parsing ended up with the following scanner:\n%s\n", scanner_sptr_from_file->parameter_info().c_str()); + } + // float azimuthal_angle_sampling =_PI/num_views; - if (is_arccorrected) - { - if (effective_central_bin_size_in_cm <= 0) - 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("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_sptr_from_file->get_default_bin_size()/10); - - data_info_sptr.reset( - new ProjDataInfoCylindricalArcCorr ( - 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, tof_mash_factor)); - } - else - { - data_info_sptr.reset( - new ProjDataInfoCylindricalNoArcCorr ( - scanner_sptr_from_file, - sorted_num_rings_per_segment, - sorted_min_ring_diff, - sorted_max_ring_diff, - 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) - { - warning("Interfile warning: inconsistent effective_central_bin_size_in_cm\n" - "Value in header is %g while I expect %g from the inner ring radius etc\n" - "Ignoring value in header", - effective_central_bin_size_in_cm, - data_info_sptr->get_sampling_in_s(Bin(0,0,0,0))/10.); - } + if (is_arccorrected) { + if (effective_central_bin_size_in_cm <= 0) + 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("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_sptr_from_file->get_default_bin_size() / 10); + + data_info_sptr.reset(new ProjDataInfoCylindricalArcCorr(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, tof_mash_factor)); + } else { + data_info_sptr.reset(new ProjDataInfoCylindricalNoArcCorr(scanner_sptr_from_file, sorted_num_rings_per_segment, + sorted_min_ring_diff, sorted_max_ring_diff, 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) { + warning("Interfile warning: inconsistent effective_central_bin_size_in_cm\n" + "Value in header is %g while I expect %g from the inner ring radius etc\n" + "Ignoring value in header", + effective_central_bin_size_in_cm, data_info_sptr->get_sampling_in_s(Bin(0, 0, 0, 0)) / 10.); } - //cerr << data_info_ptr->parameter_info() << endl; - + } + // cerr << data_info_ptr->parameter_info() << endl; + // Set the bed position data_info_sptr->set_bed_position_horizontal(bed_position_horizontal); data_info_sptr->set_bed_position_vertical(bed_position_vertical); diff --git a/src/IO/InterfileHeaderSiemens.cxx b/src/IO/InterfileHeaderSiemens.cxx index 840576cea8..3712d29d98 100644 --- a/src/IO/InterfileHeaderSiemens.cxx +++ b/src/IO/InterfileHeaderSiemens.cxx @@ -19,8 +19,8 @@ See STIR/LICENSE.txt for details */ /*! - \file - \ingroup InterfileIO + \file + \ingroup InterfileIO \brief implementations for the stir::InterfileHeaderSiemens class \author Kris Thielemans @@ -50,33 +50,30 @@ using std::vector; START_NAMESPACE_STIR -InterfileHeaderSiemens::InterfileHeaderSiemens() - : InterfileHeader() -{ +InterfileHeaderSiemens::InterfileHeaderSiemens() : InterfileHeader() { // always PET exam_info_sptr->imaging_modality = ImagingModality::PT; - + byte_order_values.push_back("LITTLEENDIAN"); byte_order_values.push_back("BIGENDIAN"); - + PET_data_type_values.push_back("Emission"); PET_data_type_values.push_back("Transmission"); PET_data_type_values.push_back("Blank"); PET_data_type_values.push_back("AttenuationCorrection"); PET_data_type_values.push_back("Normalisation"); PET_data_type_values.push_back("Image"); - - for (int patient_position_idx = 0; patient_position_idx <= PatientPosition::unknown_position; ++patient_position_idx) - { - PatientPosition pos((PatientPosition::PositionValue)patient_position_idx); - patient_position_values.push_back(pos.get_position_as_string()); - } - patient_position_index = static_cast(patient_position_values.size()); //unknown - byte_order_index = 1;// file_byte_order = ByteOrder::big_endian; + + for (int patient_position_idx = 0; patient_position_idx <= PatientPosition::unknown_position; ++patient_position_idx) { + PatientPosition pos((PatientPosition::PositionValue)patient_position_idx); + patient_position_values.push_back(pos.get_position_as_string()); + } + patient_position_index = static_cast(patient_position_values.size()); // unknown + byte_order_index = 1; // file_byte_order = ByteOrder::big_endian; // need to default to PET for backwards compatibility this->exam_info_sptr->imaging_modality = ImagingModality::PT; - //type_of_data_index = 6; // PET + // type_of_data_index = 6; // PET PET_data_type_index = 5; // Image num_dimensions = 2; // set to 2 to be compatible with Interfile version 3.3 (which doesn't have this keyword) @@ -88,23 +85,15 @@ InterfileHeaderSiemens::InterfileHeaderSiemens() data_offset = 0UL; - // use this as opposed to InterfileHeader::set_type_of_data() to cope with specifics for Siemens remove_key("type of data"); - add_key("type of data", - KeyArgument::ASCIIlist, - (KeywordProcessor)&InterfileHeaderSiemens::set_type_of_data, - &type_of_data_index, - &type_of_data_values); - - add_key("%patient orientation", - &patient_position_index, - &patient_position_values); - - add_key("image data byte order", - &byte_order_index, - &byte_order_values); - + add_key("type of data", KeyArgument::ASCIIlist, (KeywordProcessor)&InterfileHeaderSiemens::set_type_of_data, + &type_of_data_index, &type_of_data_values); + + add_key("%patient orientation", &patient_position_index, &patient_position_values); + + add_key("image data byte order", &byte_order_index, &byte_order_values); + add_vectorised_key("scale factor (mm/pixel)", &pixel_sizes); // only a single time frame supported by Siemens currently @@ -119,8 +108,8 @@ InterfileHeaderSiemens::InterfileHeaderSiemens() add_key("image duration (sec)", &image_durations[0]); } -bool InterfileHeaderSiemens::post_processing() -{ +bool +InterfileHeaderSiemens::post_processing() { if (InterfileHeader::post_processing() == true) return true; @@ -131,20 +120,19 @@ bool InterfileHeaderSiemens::post_processing() return true; }*/ - if (patient_position_index<0 ) + if (patient_position_index < 0) return true; // note: has to be done after InterfileHeader::post_processing as that sets it as well exam_info_sptr->patient_position = PatientPosition((PatientPosition::PositionValue)patient_position_index); - file_byte_order = byte_order_index==0 ? - ByteOrder::little_endian : ByteOrder::big_endian; + file_byte_order = byte_order_index == 0 ? ByteOrder::little_endian : ByteOrder::big_endian; return false; } -void InterfileHeaderSiemens::set_type_of_data() -{ +void +InterfileHeaderSiemens::set_type_of_data() { set_variable(); if (this->type_of_data_index == -1) @@ -152,9 +140,8 @@ void InterfileHeaderSiemens::set_type_of_data() const string type_of_data = this->type_of_data_values[this->type_of_data_index]; - if (type_of_data == "PET") - { - // already done in constructor + if (type_of_data == "PET") { + // already done in constructor #if 0 add_key("PET data type", &PET_data_type_index, @@ -162,19 +149,14 @@ void InterfileHeaderSiemens::set_type_of_data() ignore_key("process status"); ignore_key("IMAGE DATA DESCRIPTION"); #endif - } - else - { - warning("Interfile parsing of Siemens listmode: unexpected 'type of data:=" + type_of_data + "' (expected PET). Continuing"); - } + } else { + warning("Interfile parsing of Siemens listmode: unexpected 'type of data:=" + type_of_data + "' (expected PET). Continuing"); + } } - /**********************************************************************/ -InterfileRawDataHeaderSiemens::InterfileRawDataHeaderSiemens() - : InterfileHeaderSiemens() -{ +InterfileRawDataHeaderSiemens::InterfileRawDataHeaderSiemens() : InterfileHeaderSiemens() { // first set to some crazy values num_segments = -1; num_rings = -1; @@ -183,19 +165,17 @@ InterfileRawDataHeaderSiemens::InterfileRawDataHeaderSiemens() add_key("number of rings", &num_rings); add_key("%axial compression", &axial_compression); - add_key("%maximum ring difference", &maximum_ring_difference); + add_key("%maximum ring difference", &maximum_ring_difference); add_key("%number of segments", &num_segments); add_key("%segment table", &segment_table); add_key("%number of tof time bins", &num_tof_bins); - + add_vectorised_key("%energy window lower level (keV)", &lower_en_window_thresholds); add_vectorised_key("%energy window upper level (keV)", &upper_en_window_thresholds); remove_key("PET data type"); - add_key("PET data type", - &PET_data_type_index, - &PET_data_type_values); + add_key("PET data type", &PET_data_type_index, &PET_data_type_values); // TODO should add data format:=CoincidenceList|sinogram and then check its value remove_key("process status"); @@ -245,47 +225,39 @@ InterfileRawDataHeaderSiemens::InterfileRawDataHeaderSiemens() ignore_key("%pdr loss fraction"); ignore_key("%detector block singles"); ignore_key("%total uncorrected singles rate"); - } -bool InterfileRawDataHeaderSiemens::post_processing() -{ +bool +InterfileRawDataHeaderSiemens::post_processing() { if (InterfileHeaderSiemens::post_processing() == true) return true; - const std::string PET_data_type = - standardise_interfile_keyword(PET_data_type_values[PET_data_type_index]); - if (PET_data_type != "emission" && PET_data_type != "transmission") - { error("Interfile error: expecting emission or transmission for 'PET data type'"); } + const std::string PET_data_type = standardise_interfile_keyword(PET_data_type_values[PET_data_type_index]); + if (PET_data_type != "emission" && PET_data_type != "transmission") { + error("Interfile error: expecting emission or transmission for 'PET data type'"); + } // handle scanner shared_ptr scanner_sptr(Scanner::get_scanner_from_name(get_exam_info().originating_system)); - if (scanner_sptr->get_type() == Scanner::Unknown_scanner) - { - error("scanner not recognised from originating system"); - } + if (scanner_sptr->get_type() == Scanner::Unknown_scanner) { + error("scanner not recognised from originating system"); + } // consistency check with values of the scanner - if ((num_rings >= 0) && (num_rings != scanner_sptr->get_num_rings())) - { - error("Interfile warning: 'number of rings' (%d) is expected to be %d.\n", - num_rings, scanner_sptr->get_num_rings()); - } + if ((num_rings >= 0) && (num_rings != scanner_sptr->get_num_rings())) { + error("Interfile warning: 'number of rings' (%d) is expected to be %d.\n", num_rings, scanner_sptr->get_num_rings()); + } - data_info_ptr = - ProjDataInfo::construct_proj_data_info(scanner_sptr, - axial_compression, maximum_ring_difference, - num_views, num_bins, - is_arccorrected); + data_info_ptr = ProjDataInfo::construct_proj_data_info(scanner_sptr, axial_compression, maximum_ring_difference, num_views, + num_bins, is_arccorrected); // handle segments { - if (static_cast(num_segments) != segment_table.size()) - { - error("Interfile warning: 'number of segments' and length of 'segment table' are not consistent"); - } + if (static_cast(num_segments) != segment_table.size()) { + error("Interfile warning: 'number of segments' and length of 'segment table' are not consistent"); + } segment_sequence = ecat::find_segment_sequence(*data_info_ptr); - //XXX check if order here and segment_table are consistent + // XXX check if order here and segment_table are consistent } // Set the bed position @@ -295,16 +267,12 @@ bool InterfileRawDataHeaderSiemens::post_processing() return false; } - - /**********************************************************************/ -InterfilePDFSHeaderSiemens::InterfilePDFSHeaderSiemens() - : InterfileRawDataHeaderSiemens() -{ +InterfilePDFSHeaderSiemens::InterfilePDFSHeaderSiemens() : InterfileRawDataHeaderSiemens() { remove_key("scan data type description"); - add_key("number of scan data types", - KeyArgument::INT, (KeywordProcessor)&InterfilePDFSHeaderSiemens::read_scan_data_types, &num_scan_data_types); + add_key("number of scan data types", KeyArgument::INT, (KeywordProcessor)&InterfilePDFSHeaderSiemens::read_scan_data_types, + &num_scan_data_types); // scan data type size depends on the previous field // scan data type description[1]: = prompts // scan data type description[2] : = randoms @@ -324,104 +292,81 @@ InterfilePDFSHeaderSiemens::InterfilePDFSHeaderSiemens() // add_key(%tof mashing factor", &tof_mashing_factor); ignore_key("total number of data sets"); - add_key("%number of buckets", - KeyArgument::INT, (KeywordProcessor)&InterfilePDFSHeaderSiemens::read_bucket_singles_rates, &num_buckets); + add_key("%number of buckets", KeyArgument::INT, (KeywordProcessor)&InterfilePDFSHeaderSiemens::read_bucket_singles_rates, + &num_buckets); add_vectorised_key("%bucket singles rate", &bucket_singles_rates); - - } - -void InterfilePDFSHeaderSiemens::read_scan_data_types() -{ +void +InterfilePDFSHeaderSiemens::read_scan_data_types() { set_variable(); scan_data_types.resize(num_scan_data_types); data_offset_each_dataset.resize(num_scan_data_types); - } -void InterfilePDFSHeaderSiemens::read_bucket_singles_rates() -{ +void +InterfilePDFSHeaderSiemens::read_bucket_singles_rates() { set_variable(); bucket_singles_rates.resize(num_buckets); } -int InterfilePDFSHeaderSiemens::find_storage_order() -{ +int +InterfilePDFSHeaderSiemens::find_storage_order() { - if (num_dimensions != 3) - { + if (num_dimensions != 3) { warning("Interfile error: expecting 3D data "); stop_parsing(); return true; - } + } - if ((matrix_size[0].size() != 1) || - (matrix_size[1].size() != 1) || - (matrix_size[2].size() != 1)) - { + if ((matrix_size[0].size() != 1) || (matrix_size[1].size() != 1) || (matrix_size[2].size() != 1)) { error("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) - { + { // use error message with index [1] as that is what the user sees. error("Interfile error: expecting 'matrix axis label[1] := bin' or 'x'"); - } + } num_bins = matrix_size[0][0]; - if ((matrix_labels[1] == "projection" && matrix_labels[2] == "plane") || // used for emission + 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 - ) - { + ) { storage_order = ProjDataFromStream::Segment_AxialPos_View_TangPos; num_views = matrix_size[1][0]; - } - else - { + } else { error("Interfile error: matrix labels not in expected (or supported) format"); - } + } return false; - } - -bool InterfilePDFSHeaderSiemens::post_processing() -{ +bool +InterfilePDFSHeaderSiemens::post_processing() { // check for arc-correction - if (applied_corrections.size() == 0) - { + if (applied_corrections.size() == 0) { warning("Parsing Interfile header for projection data: \n" - "\t'applied corrections' keyword not found or empty. Assuming non-arc-corrected data"); + "\t'applied corrections' keyword not found or empty. Assuming non-arc-corrected data"); is_arccorrected = false; - } - else - { + } else { is_arccorrected = false; - for ( - std::vector::const_iterator iter = applied_corrections.begin(); - iter != applied_corrections.end(); - ++iter) - { - const string correction = standardise_keyword(*iter); - if (correction == "radial arc-correction" || correction == "arc correction" || correction == "arc corrected") - { - is_arccorrected = true; - break; - } - else if (correction != "none") - warning("\nParsing Interfile header for projection data: \n" - "\t value '%s' for keyword 'applied corrections' ignored\n", - correction.c_str()); - } + for (std::vector::const_iterator iter = applied_corrections.begin(); iter != applied_corrections.end(); ++iter) { + const string correction = standardise_keyword(*iter); + if (correction == "radial arc-correction" || correction == "arc correction" || correction == "arc corrected") { + is_arccorrected = true; + break; + } else if (correction != "none") + warning("\nParsing Interfile header for projection data: \n" + "\t value '%s' for keyword 'applied corrections' ignored\n", + correction.c_str()); } + } - if (find_storage_order()) - { - error("Interfile error determining storage order"); - } + if (find_storage_order()) { + error("Interfile error determining storage order"); + } // can only do this now after the previous things were set if (InterfileRawDataHeaderSiemens::post_processing() == true) @@ -434,49 +379,46 @@ bool InterfilePDFSHeaderSiemens::post_processing() /**********************************************************************/ -InterfileListmodeHeaderSiemens::InterfileListmodeHeaderSiemens() - : InterfileRawDataHeaderSiemens() -{ +InterfileListmodeHeaderSiemens::InterfileListmodeHeaderSiemens() : InterfileRawDataHeaderSiemens() { // need to set this to construct the correct proj_data_info is_arccorrected = false; // need to set this for InterfileHeader::post_processing() // but will otherwise be ignored bytes_per_pixel = 4; - for (unsigned int dim = 0; dim != matrix_size.size(); ++dim) - { - matrix_size[dim].resize(1, 1); - } -/* - keywords different from a sinogram header (in alphabetical order) -< %LM event and tag words format (bits):=32 -< %SMS-MI header name space:=PETLINK bin address - -< %number of projections:=344 -< %number of views:=252 -< %preset type:=time -< %preset unit:=seconds -< %preset value:=900 -< %singles polling interval (sec):=2 -< %singles polling method:=instantaneous -< %singles scale factor:=8 -< %time_sync:=25934299 -< %timing tagwords interval (msec):=1 -< %total listmode word counts:=331257106 -< %total number of singles blocks:=224 -< PET scanner type:=cylindrical -< bin size (cm):=0.20445 - -< data format:=CoincidenceList - -< distance between rings (cm):=0.40625 -< end horizontal bed position (mm):=0 -< gantry crystal radius (cm):=32.8 -< gantry tilt angle (degrees):=0 - -> gantry tilt angle (degrees):=0.0 -< septa state:=none -< transaxial FOV diameter (cm):=59.6 -*/ + for (unsigned int dim = 0; dim != matrix_size.size(); ++dim) { + matrix_size[dim].resize(1, 1); + } + /* + keywords different from a sinogram header (in alphabetical order) + < %LM event and tag words format (bits):=32 + < %SMS-MI header name space:=PETLINK bin address + + < %number of projections:=344 + < %number of views:=252 + < %preset type:=time + < %preset unit:=seconds + < %preset value:=900 + < %singles polling interval (sec):=2 + < %singles polling method:=instantaneous + < %singles scale factor:=8 + < %time_sync:=25934299 + < %timing tagwords interval (msec):=1 + < %total listmode word counts:=331257106 + < %total number of singles blocks:=224 + < PET scanner type:=cylindrical + < bin size (cm):=0.20445 + + < data format:=CoincidenceList + + < distance between rings (cm):=0.40625 + < end horizontal bed position (mm):=0 + < gantry crystal radius (cm):=32.8 + < gantry tilt angle (degrees):=0 + + > gantry tilt angle (degrees):=0.0 + < septa state:=none + < transaxial FOV diameter (cm):=59.6 + */ add_key("%number of projections", &num_bins); add_key("%number of views", &num_views); @@ -505,32 +447,38 @@ InterfileListmodeHeaderSiemens::InterfileListmodeHeaderSiemens() ignore_key("%total number of singles blocks"); ignore_key("%time sync"); ignore_key("%comment"); - } +} -int InterfileListmodeHeaderSiemens::find_storage_order() -{ +int +InterfileListmodeHeaderSiemens::find_storage_order() { // always... storage_order = ProjDataFromStream::Segment_AxialPos_View_TangPos; - + return false; } -int InterfileListmodeHeaderSiemens::get_axial_compression() const -{return axial_compression;} -int InterfileListmodeHeaderSiemens::get_maximum_ring_difference() const -{return maximum_ring_difference;} -int InterfileListmodeHeaderSiemens::get_num_views() const -{return num_views;} -int InterfileListmodeHeaderSiemens::get_num_projections() const -{return num_bins;} - +int +InterfileListmodeHeaderSiemens::get_axial_compression() const { + return axial_compression; +} +int +InterfileListmodeHeaderSiemens::get_maximum_ring_difference() const { + return maximum_ring_difference; +} +int +InterfileListmodeHeaderSiemens::get_num_views() const { + return num_views; +} +int +InterfileListmodeHeaderSiemens::get_num_projections() const { + return num_bins; +} -bool InterfileListmodeHeaderSiemens::post_processing() -{ - if (find_storage_order()) - { - error("Interfile error determining storage order"); - } +bool +InterfileListmodeHeaderSiemens::post_processing() { + if (find_storage_order()) { + error("Interfile error determining storage order"); + } // can only do this now after the previous things were set if (InterfileRawDataHeaderSiemens::post_processing() == true) @@ -539,5 +487,4 @@ bool InterfileListmodeHeaderSiemens::post_processing() return false; } - END_NAMESPACE_STIR diff --git a/src/IO/InterfileOutputFileFormat.cxx b/src/IO/InterfileOutputFileFormat.cxx index a9ae13f3dc..fc45577bcf 100644 --- a/src/IO/InterfileOutputFileFormat.cxx +++ b/src/IO/InterfileOutputFileFormat.cxx @@ -31,71 +31,49 @@ START_NAMESPACE_STIR +const char* const InterfileOutputFileFormat::registered_name = "Interfile"; -const char * const -InterfileOutputFileFormat::registered_name = "Interfile"; - -InterfileOutputFileFormat:: -InterfileOutputFileFormat(const NumericType& type, - const ByteOrder& byte_order) -{ +InterfileOutputFileFormat::InterfileOutputFileFormat(const NumericType& type, const ByteOrder& byte_order) { base_type::set_defaults(); set_type_of_numbers(type); set_byte_order(byte_order); } -void -InterfileOutputFileFormat:: -set_defaults() -{ +void +InterfileOutputFileFormat::set_defaults() { base_type::set_defaults(); } -void -InterfileOutputFileFormat:: -initialise_keymap() -{ +void +InterfileOutputFileFormat::initialise_keymap() { parser.add_start_key("Interfile Output File Format Parameters"); parser.add_stop_key("End Interfile Output File Format Parameters"); base_type::initialise_keymap(); } -bool -InterfileOutputFileFormat:: -post_processing() -{ +bool +InterfileOutputFileFormat::post_processing() { if (base_type::post_processing()) return true; return false; } // note 'warn' commented below to avoid compiler warning message about unused variables -ByteOrder -InterfileOutputFileFormat:: -set_byte_order(const ByteOrder& new_byte_order, const bool /* warn */) -{ +ByteOrder +InterfileOutputFileFormat::set_byte_order(const ByteOrder& new_byte_order, const bool /* warn */) { this->file_byte_order = new_byte_order; return this->file_byte_order; } - - -Succeeded -InterfileOutputFileFormat:: -actual_write_to_file(std::string& filename, - const DiscretisedDensity<3,float>& density) const -{ +Succeeded +InterfileOutputFileFormat::actual_write_to_file(std::string& filename, const DiscretisedDensity<3, float>& density) const { // TODO modify write_basic_interfile to return filename - + Succeeded success = - write_basic_interfile(filename, density, - this->type_of_numbers, this->scale_to_write_data, - this->file_byte_order); + write_basic_interfile(filename, density, this->type_of_numbers, this->scale_to_write_data, this->file_byte_order); if (success == Succeeded::yes) replace_extension(filename, ".hv"); return success; }; END_NAMESPACE_STIR - - diff --git a/src/IO/InterfilePDFSHeaderSPECT.cxx b/src/IO/InterfilePDFSHeaderSPECT.cxx index 3501ef451d..4f8807a6c8 100644 --- a/src/IO/InterfilePDFSHeaderSPECT.cxx +++ b/src/IO/InterfilePDFSHeaderSPECT.cxx @@ -15,7 +15,7 @@ See STIR/LICENSE.txt for details */ -/*! +/*! \file \ingroup InterfileIO \brief This file implements the classes stir::InterfilePDFSHeaderSPECT @@ -40,38 +40,30 @@ using std::endl; START_NAMESPACE_STIR -//KT 26/10/98 +// KT 26/10/98 // KT 13/11/98 moved stream arg from constructor to parse() -InterfilePDFSHeaderSPECT::InterfilePDFSHeaderSPECT() -: InterfileHeader() -{ +InterfilePDFSHeaderSPECT::InterfilePDFSHeaderSPECT() : InterfileHeader() { num_segments = 1; num_views = -1; - add_key("number of projections", - &num_views); - start_angle=0; - add_key("start angle", - &start_angle); - direction_of_rotation="cw"; - add_key("direction of rotation", - &direction_of_rotation); - extent_of_rotation=double_value_not_set; - add_key("extent of rotation", - &extent_of_rotation); + add_key("number of projections", &num_views); + start_angle = 0; + add_key("start angle", &start_angle); + direction_of_rotation = "cw"; + add_key("direction of rotation", &direction_of_rotation); + extent_of_rotation = double_value_not_set; + add_key("extent of rotation", &extent_of_rotation); // TODO convert to ASCIIlist orbit = "circular"; - add_key("orbit", - &orbit); + add_key("orbit", &orbit); radius_of_rotation = double_value_not_set; - add_key("radius",&radius_of_rotation); - add_key("radii",&radii_of_rotation); // for non-circular orbits + add_key("radius", &radius_of_rotation); + add_key("radii", &radii_of_rotation); // for non-circular orbits // overwrite vectored-value, as v3.3 had a scalar add_key("data offset in bytes", &data_offset); - } -bool InterfilePDFSHeaderSPECT::post_processing() -{ +bool +InterfilePDFSHeaderSPECT::post_processing() { if (InterfileHeader::post_processing() == true) return true; @@ -80,79 +72,70 @@ bool InterfilePDFSHeaderSPECT::post_processing() data_offset_each_dataset[0] = data_offset; // SPECT v3.3 doesn't really define matrix_labels. We just check that if they're present, they are as in PET - if (matrix_labels[0].size()>0 && matrix_labels[0] != "bin coordinate") - { - // use error message with index [1] as that is what the user sees. - warning("Interfile error: expecting 'matrix axis label[1] := bin coordinate'"); - return true; - } - if (matrix_labels[1].size()>0 && matrix_labels[1] != "axial coordinate" ) - { - // use error message with index [2] as that is what the user sees. - warning("Interfile error: expecting 'matrix axis label[2] := axial coordinate'"); - return true; - } + if (matrix_labels[0].size() > 0 && matrix_labels[0] != "bin coordinate") { + // use error message with index [1] as that is what the user sees. + warning("Interfile error: expecting 'matrix axis label[1] := bin coordinate'"); + return true; + } + if (matrix_labels[1].size() > 0 && matrix_labels[1] != "axial coordinate") { + // use error message with index [2] as that is what the user sees. + warning("Interfile error: expecting 'matrix axis label[2] := axial coordinate'"); + return true; + } - if (extent_of_rotation == double_value_not_set) - { - warning("Interfile error: extent of rotation needs to be set"); - return true; - } + if (extent_of_rotation == double_value_not_set) { + warning("Interfile error: extent of rotation needs to be set"); + return true; + } num_bins = matrix_size[0][0]; - bin_size_in_cm = pixel_sizes[0]/10.; + bin_size_in_cm = pixel_sizes[0] / 10.; - storage_order =ProjDataFromStream::Segment_View_AxialPos_TangPos; - num_axial_poss= matrix_size[1][0]; - const double z_spacing_in_cm = pixel_sizes[1]/10.; + storage_order = ProjDataFromStream::Segment_View_AxialPos_TangPos; + num_axial_poss = matrix_size[1][0]; + const double z_spacing_in_cm = pixel_sizes[1] / 10.; - //Fill the radius depending on the type of orbit (just two orbits are supported) + // Fill the radius depending on the type of orbit (just two orbits are supported) // will be in mm (SPECT Interfile uses mm) - VectorWithOffset radii(0, num_views-1); - orbit=standardise_keyword(orbit); - if (orbit == "circular") - { - if (radius_of_rotation == double_value_not_set) - { - warning("Interfile error: radius not set"); - return true; - } - for ( int i = 0 ; i < num_views ; i++ ) radii[ i ] = static_cast(radius_of_rotation); - } - else if (orbit == "non-circular") - { - - if ( radii_of_rotation.size() != static_cast(num_views)) - { - warning("Interfile error: number of projections must be consistent with radius vector length"); - return true; - } - for ( int i = 0 ; i < num_views ; i++ ) radii[ i ] = static_cast(radii_of_rotation[i]); + VectorWithOffset radii(0, num_views - 1); + orbit = standardise_keyword(orbit); + if (orbit == "circular") { + if (radius_of_rotation == double_value_not_set) { + warning("Interfile error: radius not set"); + return true; } - else - { - warning("Interfile error: only circular or non-circular orbits are supported"); - return true; + for (int i = 0; i < num_views; i++) + radii[i] = static_cast(radius_of_rotation); + } else if (orbit == "non-circular") { + + if (radii_of_rotation.size() != static_cast(num_views)) { + warning("Interfile error: number of projections must be consistent with radius vector length"); + return true; } - + for (int i = 0; i < num_views; i++) + radii[i] = static_cast(radii_of_rotation[i]); + } else { + warning("Interfile error: only circular or non-circular orbits are supported"); + return true; + } // somewhat strange values to be compatible with PET - VectorWithOffset sorted_min_ring_diff(0,0); - VectorWithOffset sorted_max_ring_diff(0,0); - VectorWithOffset sorted_num_axial_poss_per_segment(0,0); - sorted_min_ring_diff[0]=0; - sorted_max_ring_diff[0]=0; - sorted_num_axial_poss_per_segment[0]=num_axial_poss; + VectorWithOffset sorted_min_ring_diff(0, 0); + VectorWithOffset sorted_max_ring_diff(0, 0); + VectorWithOffset sorted_num_axial_poss_per_segment(0, 0); + sorted_min_ring_diff[0] = 0; + sorted_max_ring_diff[0] = 0; + sorted_num_axial_poss_per_segment[0] = num_axial_poss; // we construct a new scanner object with // data from the Interfile header (or the guessed scanner). // Initialize the scanner values (most are not used in SPECT reconstruction) const int num_rings = sorted_num_axial_poss_per_segment[0]; - const int num_detectors_per_ring = -1;//num_views*2; + const int num_detectors_per_ring = -1; // num_views*2; const double average_depth_of_interaction_in_cm = 0; const double distance_between_rings_in_cm = z_spacing_in_cm; - double default_bin_size_in_cm = bin_size_in_cm ; + double default_bin_size_in_cm = bin_size_in_cm; const double view_offset_in_degrees = start_angle; const int max_num_non_arccorrected_bins = num_bins; const int default_num_arccorrected_bins = num_bins; @@ -168,32 +151,16 @@ bool InterfilePDFSHeaderSPECT::post_processing() 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_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)); + shared_ptr scanner_ptr_from_file(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 = @@ -204,38 +171,27 @@ bool InterfilePDFSHeaderSPECT::post_processing() bin_size_in_cm, scanner_ptr_from_file->get_default_bin_size()/10); #endif - ProjDataInfoCylindricalArcCorr* my_data_info_ptr = - new ProjDataInfoCylindricalArcCorr ( - scanner_ptr_from_file, - float(bin_size_in_cm*10.), - sorted_num_axial_poss_per_segment, - sorted_min_ring_diff, - sorted_max_ring_diff, - num_views,num_bins); - - my_data_info_ptr->set_ring_radii_for_all_views ( radii); - - direction_of_rotation = standardise_keyword(direction_of_rotation); - const float angle_sampling = float (extent_of_rotation)/num_views * float(_PI/180); - if(direction_of_rotation=="cw") - { - my_data_info_ptr->set_azimuthal_angle_sampling(-angle_sampling); - } - else if(direction_of_rotation=="ccw") - { - my_data_info_ptr->set_azimuthal_angle_sampling(angle_sampling); - } - else - { - warning("direction of rotation has to be CW or CCW"); - return true; - } + ProjDataInfoCylindricalArcCorr* my_data_info_ptr = + new ProjDataInfoCylindricalArcCorr(scanner_ptr_from_file, float(bin_size_in_cm * 10.), sorted_num_axial_poss_per_segment, + sorted_min_ring_diff, sorted_max_ring_diff, num_views, num_bins); + + my_data_info_ptr->set_ring_radii_for_all_views(radii); + + direction_of_rotation = standardise_keyword(direction_of_rotation); + const float angle_sampling = float(extent_of_rotation) / num_views * float(_PI / 180); + if (direction_of_rotation == "cw") { + my_data_info_ptr->set_azimuthal_angle_sampling(-angle_sampling); + } else if (direction_of_rotation == "ccw") { + my_data_info_ptr->set_azimuthal_angle_sampling(angle_sampling); + } else { + warning("direction of rotation has to be CW or CCW"); + return true; + } this->data_info_sptr.reset(my_data_info_ptr); - //cerr << data_info_ptr->parameter_info() << endl; + // cerr << data_info_ptr->parameter_info() << endl; return false; } - END_NAMESPACE_STIR diff --git a/src/IO/InterfileParametricDiscretisedDensityOutputFileFormat.cxx b/src/IO/InterfileParametricDiscretisedDensityOutputFileFormat.cxx index 133cbf3414..f2f367f29e 100644 --- a/src/IO/InterfileParametricDiscretisedDensityOutputFileFormat.cxx +++ b/src/IO/InterfileParametricDiscretisedDensityOutputFileFormat.cxx @@ -42,16 +42,12 @@ START_NAMESPACE_STIR //#define InterfileParamDiscDensity InterfileParametricDiscretisedDensityOutputFileFormat #define InterfileParamDiscDensity InterfileParametricDiscretisedDensityOutputFileFormat - TEMPLATE -const char * const -InterfileParamDiscDensity::registered_name = "Interfile"; +const char* const InterfileParamDiscDensity::registered_name = "Interfile"; TEMPLATE -InterfileParamDiscDensity:: -InterfileParametricDiscretisedDensityOutputFileFormat(const NumericType& type, - const ByteOrder& byte_order) -{ +InterfileParamDiscDensity::InterfileParametricDiscretisedDensityOutputFileFormat(const NumericType& type, + const ByteOrder& byte_order) { base_type::set_defaults(); this->set_type_of_numbers(type); this->set_byte_order(byte_order); @@ -59,64 +55,47 @@ InterfileParametricDiscretisedDensityOutputFileFormat(const NumericType& type, TEMPLATE void -InterfileParamDiscDensity:: -set_defaults() -{ +InterfileParamDiscDensity::set_defaults() { base_type::set_defaults(); } TEMPLATE void -InterfileParamDiscDensity:: -initialise_keymap() -{ +InterfileParamDiscDensity::initialise_keymap() { this->parser.add_start_key("Interfile Output File Format Parameters"); - this->parser.add_stop_key("End Interfile Output File Format Parameters"); + this->parser.add_stop_key("End Interfile Output File Format Parameters"); base_type::initialise_keymap(); } TEMPLATE bool -InterfileParamDiscDensity:: -post_processing() -{ +InterfileParamDiscDensity::post_processing() { if (base_type::post_processing()) return true; return false; } - TEMPLATE ByteOrder -InterfileParamDiscDensity:: -set_byte_order(const ByteOrder& new_byte_order, const bool warn) -{ - if (!new_byte_order.is_native_order()) - { +InterfileParamDiscDensity::set_byte_order(const ByteOrder& new_byte_order, const bool warn) { + if (!new_byte_order.is_native_order()) { if (warn) warning("InterfileParametricDiscretisedDensityOutputFileFormat: byte_order is currently fixed to the native format\n"); - this->file_byte_order = ByteOrder::native; - } - else - this->file_byte_order = new_byte_order; - return this->file_byte_order; + this->file_byte_order = ByteOrder::native; + } else + this->file_byte_order = new_byte_order; + return this->file_byte_order; } - - TEMPLATE Succeeded -InterfileParamDiscDensity:: -actual_write_to_file(std::string& filename, - const ParametricDiscretisedDensity& density) const -{ +InterfileParamDiscDensity::actual_write_to_file(std::string& filename, + const ParametricDiscretisedDensity& density) const { // TODO modify write_basic_interfile to return filename Succeeded success = - write_basic_interfile(filename, density, - this->type_of_numbers, this->scale_to_write_data, - this->file_byte_order); + write_basic_interfile(filename, density, this->type_of_numbers, this->scale_to_write_data, this->file_byte_order); if (success == Succeeded::yes) - replace_extension(filename, ".hv"); + replace_extension(filename, ".hv"); return success; } @@ -125,5 +104,4 @@ actual_write_to_file(std::string& filename, template class InterfileParametricDiscretisedDensityOutputFileFormat; - END_NAMESPACE_STIR diff --git a/src/IO/MultiDynamicDiscretisedDensityOutputFileFormat.cxx b/src/IO/MultiDynamicDiscretisedDensityOutputFileFormat.cxx index 07d89553c2..66002aa2b4 100644 --- a/src/IO/MultiDynamicDiscretisedDensityOutputFileFormat.cxx +++ b/src/IO/MultiDynamicDiscretisedDensityOutputFileFormat.cxx @@ -29,7 +29,7 @@ */ #include "stir/IO/MultiDynamicDiscretisedDensityOutputFileFormat.h" -#include "stir/DynamicDiscretisedDensity.h" +#include "stir/DynamicDiscretisedDensity.h" #include "stir/NumericType.h" #include "stir/Succeeded.h" #include "stir/FilePath.h" @@ -37,92 +37,75 @@ START_NAMESPACE_STIR -const char * const -MultiDynamicDiscretisedDensityOutputFileFormat::registered_name = "Multi"; +const char* const MultiDynamicDiscretisedDensityOutputFileFormat::registered_name = "Multi"; -MultiDynamicDiscretisedDensityOutputFileFormat:: -MultiDynamicDiscretisedDensityOutputFileFormat(const NumericType& type, - const ByteOrder& byte_order) -{ +MultiDynamicDiscretisedDensityOutputFileFormat::MultiDynamicDiscretisedDensityOutputFileFormat(const NumericType& type, + const ByteOrder& byte_order) { this->set_defaults(); this->set_type_of_numbers(type); this->set_byte_order(byte_order); } -void -MultiDynamicDiscretisedDensityOutputFileFormat:: -set_defaults() -{ +void +MultiDynamicDiscretisedDensityOutputFileFormat::set_defaults() { base_type::set_defaults(); this->set_type_of_numbers(NumericType::FLOAT); this->set_byte_order(ByteOrder::native); - this->individual_output_type_sptr = OutputFileFormat >::default_sptr(); + this->individual_output_type_sptr = OutputFileFormat>::default_sptr(); } -void -MultiDynamicDiscretisedDensityOutputFileFormat:: -initialise_keymap() -{ +void +MultiDynamicDiscretisedDensityOutputFileFormat::initialise_keymap() { this->parser.add_start_key("Multi Output File Format Parameters"); this->parser.add_stop_key("End Multi Output File Format Parameters"); - this->parser.add_parsing_key("individual output file format type",&individual_output_type_sptr); + this->parser.add_parsing_key("individual output file format type", &individual_output_type_sptr); base_type::initialise_keymap(); } -bool -MultiDynamicDiscretisedDensityOutputFileFormat:: -post_processing() -{ +bool +MultiDynamicDiscretisedDensityOutputFileFormat::post_processing() { if (base_type::post_processing()) return true; return false; } - -ByteOrder -MultiDynamicDiscretisedDensityOutputFileFormat:: -set_byte_order(const ByteOrder& new_byte_order, const bool warn) -{ - if (!new_byte_order.is_native_order()) - { - if (warn) - warning("MultiDynamicDiscretisedDensityOutputFileFormat: byte_order is currently fixed to the native format\n"); - this->file_byte_order = ByteOrder::native; - } - else +ByteOrder +MultiDynamicDiscretisedDensityOutputFileFormat::set_byte_order(const ByteOrder& new_byte_order, const bool warn) { + if (!new_byte_order.is_native_order()) { + if (warn) + warning("MultiDynamicDiscretisedDensityOutputFileFormat: byte_order is currently fixed to the native format\n"); + this->file_byte_order = ByteOrder::native; + } else this->file_byte_order = new_byte_order; - return this->file_byte_order; + return this->file_byte_order; } -Succeeded -MultiDynamicDiscretisedDensityOutputFileFormat:: -actual_write_to_file(std::string& filename, - const DynamicDiscretisedDensity & density) const -{ - { - FilePath file_path(filename, false); // create object without checking if a fo;e exists already - if (!file_path.get_extension().empty()) - error("MultiDynamicDiscretisedDensityOutputFileFormat: currently needs an output filename without extension. sorry"); - } - // Create all the filenames - VectorWithOffset individual_filenames(1,int(density.get_num_time_frames())); - for (int i=1; i<=int(density.get_num_time_frames()); i++) - individual_filenames[i] = filename + "_" + boost::lexical_cast(i); - - // Write each individual image - for (int i=1; i<=int(density.get_num_time_frames()); i++) { - Succeeded success = individual_output_type_sptr->write_to_file(individual_filenames[i],density.get_density(unsigned(i))); - if (success != Succeeded::yes) - warning("MultiDynamicDiscretisedDensity error: Failed to write \"" + individual_filenames[i] + "\".\n"); - } - - // Write some multi header info - filename = filename + ".txt"; - MultipleDataSetHeader::write_header(filename, individual_filenames); - return Succeeded::yes; +Succeeded +MultiDynamicDiscretisedDensityOutputFileFormat::actual_write_to_file(std::string& filename, + const DynamicDiscretisedDensity& density) const { + { + FilePath file_path(filename, false); // create object without checking if a fo;e exists already + if (!file_path.get_extension().empty()) + error("MultiDynamicDiscretisedDensityOutputFileFormat: currently needs an output filename without extension. sorry"); + } + // Create all the filenames + VectorWithOffset individual_filenames(1, int(density.get_num_time_frames())); + for (int i = 1; i <= int(density.get_num_time_frames()); i++) + individual_filenames[i] = filename + "_" + boost::lexical_cast(i); + + // Write each individual image + for (int i = 1; i <= int(density.get_num_time_frames()); i++) { + Succeeded success = individual_output_type_sptr->write_to_file(individual_filenames[i], density.get_density(unsigned(i))); + if (success != Succeeded::yes) + warning("MultiDynamicDiscretisedDensity error: Failed to write \"" + individual_filenames[i] + "\".\n"); + } + + // Write some multi header info + filename = filename + ".txt"; + MultipleDataSetHeader::write_header(filename, individual_filenames); + return Succeeded::yes; } -// class MultiDynamicDiscretisedDensityOutputFileFormat; - +// class MultiDynamicDiscretisedDensityOutputFileFormat; END_NAMESPACE_STIR diff --git a/src/IO/MultiParametricDiscretisedDensityOutputFileFormat.cxx b/src/IO/MultiParametricDiscretisedDensityOutputFileFormat.cxx index 24d34ed991..16a2c74ad6 100644 --- a/src/IO/MultiParametricDiscretisedDensityOutputFileFormat.cxx +++ b/src/IO/MultiParametricDiscretisedDensityOutputFileFormat.cxx @@ -31,7 +31,7 @@ /* largely a copy of the dynamic case. sorry */ #include "stir/IO/MultiParametricDiscretisedDensityOutputFileFormat.h" -#include "stir/modelling/ParametricDiscretisedDensity.h" +#include "stir/modelling/ParametricDiscretisedDensity.h" #include "stir/NumericType.h" #include "stir/Succeeded.h" #include "stir/FilePath.h" @@ -44,24 +44,19 @@ START_NAMESPACE_STIR #define ParamDiscDensityOutputFileFormat MultiParametricDiscretisedDensityOutputFileFormat TEMPLATE -const char * const -ParamDiscDensityOutputFileFormat::registered_name = "Multi"; +const char* const ParamDiscDensityOutputFileFormat::registered_name = "Multi"; TEMPLATE -ParamDiscDensityOutputFileFormat:: -MultiParametricDiscretisedDensityOutputFileFormat(const NumericType& type, - const ByteOrder& byte_order) -{ +ParamDiscDensityOutputFileFormat::MultiParametricDiscretisedDensityOutputFileFormat(const NumericType& type, + const ByteOrder& byte_order) { this->set_defaults(); this->set_type_of_numbers(type); this->set_byte_order(byte_order); } TEMPLATE -void -ParamDiscDensityOutputFileFormat:: -set_defaults() -{ +void +ParamDiscDensityOutputFileFormat::set_defaults() { base_type::set_defaults(); this->set_type_of_numbers(NumericType::FLOAT); this->set_byte_order(ByteOrder::native); @@ -69,69 +64,60 @@ set_defaults() } TEMPLATE -void -ParamDiscDensityOutputFileFormat:: -initialise_keymap() -{ +void +ParamDiscDensityOutputFileFormat::initialise_keymap() { this->parser.add_start_key("Multi Output File Format Parameters"); this->parser.add_stop_key("End Multi Output File Format Parameters"); - this->parser.add_parsing_key("individual output file format type",&this->individual_output_type_sptr); + this->parser.add_parsing_key("individual output file format type", &this->individual_output_type_sptr); base_type::initialise_keymap(); } TEMPLATE -bool -ParamDiscDensityOutputFileFormat:: -post_processing() -{ +bool +ParamDiscDensityOutputFileFormat::post_processing() { if (base_type::post_processing()) return true; return false; } TEMPLATE -ByteOrder -ParamDiscDensityOutputFileFormat:: -set_byte_order(const ByteOrder& new_byte_order, const bool warn) -{ - if (!new_byte_order.is_native_order()) - { - if (warn) - warning("MultiParametricDiscretisedDensityOutputFileFormat: byte_order is currently fixed to the native format\n"); - this->file_byte_order = ByteOrder::native; - } - else +ByteOrder +ParamDiscDensityOutputFileFormat::set_byte_order(const ByteOrder& new_byte_order, const bool warn) { + if (!new_byte_order.is_native_order()) { + if (warn) + warning("MultiParametricDiscretisedDensityOutputFileFormat: byte_order is currently fixed to the native format\n"); + this->file_byte_order = ByteOrder::native; + } else this->file_byte_order = new_byte_order; - return this->file_byte_order; + return this->file_byte_order; } TEMPLATE -Succeeded -ParamDiscDensityOutputFileFormat:: -actual_write_to_file(std::string& filename, - const ParametricDiscretisedDensity & density) const -{ - { - FilePath file_path(filename, false); // create object without checking if a fo;e exists already - if (!file_path.get_extension().empty()) - error("MultiParametricDiscretisedDensityOutputFileFormat: currently needs an output filename without extension. sorry"); - } - // Create all the filenames - VectorWithOffset individual_filenames(1,int(density.get_num_params())); - for (int i=1; i<=int(density.get_num_params()); i++) - individual_filenames[i] = filename + "_" + boost::lexical_cast(i); - - // Write each individual image - for (int i=1; i<=int(density.get_num_params()); i++) { - Succeeded success = this->individual_output_type_sptr->write_to_file(individual_filenames[i],density.construct_single_density(unsigned(i))); - if (success != Succeeded::yes) - warning("MultiParametricDiscretisedDensity error: Failed to write \"" + individual_filenames[i] + "\".\n"); - } - - // Write some multi header info - filename = filename + ".txt"; - MultipleDataSetHeader::write_header(filename, individual_filenames); - return Succeeded::yes; +Succeeded +ParamDiscDensityOutputFileFormat::actual_write_to_file(std::string& filename, + const ParametricDiscretisedDensity& density) const { + { + FilePath file_path(filename, false); // create object without checking if a fo;e exists already + if (!file_path.get_extension().empty()) + error("MultiParametricDiscretisedDensityOutputFileFormat: currently needs an output filename without extension. sorry"); + } + // Create all the filenames + VectorWithOffset individual_filenames(1, int(density.get_num_params())); + for (int i = 1; i <= int(density.get_num_params()); i++) + individual_filenames[i] = filename + "_" + boost::lexical_cast(i); + + // Write each individual image + for (int i = 1; i <= int(density.get_num_params()); i++) { + Succeeded success = + this->individual_output_type_sptr->write_to_file(individual_filenames[i], density.construct_single_density(unsigned(i))); + if (success != Succeeded::yes) + warning("MultiParametricDiscretisedDensity error: Failed to write \"" + individual_filenames[i] + "\".\n"); + } + + // Write some multi header info + filename = filename + ".txt"; + MultipleDataSetHeader::write_header(filename, individual_filenames); + return Succeeded::yes; } #undef ParamDiscDensity @@ -139,5 +125,4 @@ actual_write_to_file(std::string& filename, template class MultiParametricDiscretisedDensityOutputFileFormat; - END_NAMESPACE_STIR diff --git a/src/IO/OutputFileFormat.cxx b/src/IO/OutputFileFormat.cxx index 1b946ab4fa..6c7e642c7f 100644 --- a/src/IO/OutputFileFormat.cxx +++ b/src/IO/OutputFileFormat.cxx @@ -18,27 +18,25 @@ /*! \file \ingroup IO - - \brief Instantiations of the stir::OutputFileFormat class - \author Kris Thielemans + + \brief Instantiations of the stir::OutputFileFormat class + \author Kris Thielemans */ #include "stir/IO/OutputFileFormat.txx" #include "stir/DiscretisedDensity.h" #include "stir/DynamicDiscretisedDensity.h" -#include "stir/modelling/ParametricDiscretisedDensity.h" -#include "stir/modelling/KineticParameters.h" +#include "stir/modelling/ParametricDiscretisedDensity.h" +#include "stir/modelling/KineticParameters.h" #ifdef _MSC_VER -#pragma warning (disable : 4661) +# pragma warning(disable : 4661) #endif START_NAMESPACE_STIR - -template class OutputFileFormat >; -template class OutputFileFormat; -template class OutputFileFormat; +template class OutputFileFormat>; +template class OutputFileFormat; +template class OutputFileFormat; END_NAMESPACE_STIR - diff --git a/src/IO/OutputFileFormat_default.cxx b/src/IO/OutputFileFormat_default.cxx index 526e5159a4..df97bb8f3c 100644 --- a/src/IO/OutputFileFormat_default.cxx +++ b/src/IO/OutputFileFormat_default.cxx @@ -21,30 +21,29 @@ \ingroup IO \brief initialisation of the stir::OutputFileFormat::_default_sptr member \author Kris Thielemans - + */ #include "stir/IO/InterfileOutputFileFormat.h" #include "stir/DiscretisedDensity.h" -#include "stir/modelling/ParametricDiscretisedDensity.h" -#include "stir/DynamicDiscretisedDensity.h" +#include "stir/modelling/ParametricDiscretisedDensity.h" +#include "stir/DynamicDiscretisedDensity.h" #ifdef HAVE_LLN_MATRIX -#include "stir/IO/ECAT7ParametricDensityOutputFileFormat.h" -#include "stir/IO/ECAT7DynamicDiscretisedDensityOutputFileFormat.h" +# include "stir/IO/ECAT7ParametricDensityOutputFileFormat.h" +# include "stir/IO/ECAT7DynamicDiscretisedDensityOutputFileFormat.h" #else -#include "stir/modelling/KineticParameters.h" -#include "stir/IO/InterfileParametricDiscretisedDensityOutputFileFormat.h" -#include "stir/IO/InterfileDynamicDiscretisedDensityOutputFileFormat.h" -#include "stir/IO/MultiDynamicDiscretisedDensityOutputFileFormat.h" +# include "stir/modelling/KineticParameters.h" +# include "stir/IO/InterfileParametricDiscretisedDensityOutputFileFormat.h" +# include "stir/IO/InterfileDynamicDiscretisedDensityOutputFileFormat.h" +# include "stir/IO/MultiDynamicDiscretisedDensityOutputFileFormat.h" #endif START_NAMESPACE_STIR template <> -shared_ptr > > -OutputFileFormat >::_default_sptr(new InterfileOutputFileFormat); - +shared_ptr>> + OutputFileFormat>::_default_sptr(new InterfileOutputFileFormat); #if 0 template <> @@ -52,31 +51,24 @@ OutputFileFormat >::_default_sptr(new InterfileOutpu OutputFileFormat > >::_default_sptr = new InterfileParametricDiscretisedDensityOutputFileFormat<3,KineticParameters<2,float> >; #else - template <> - shared_ptr > - OutputFileFormat::_default_sptr( -#ifdef HAVE_LLN_MATRIX - new ecat::ecat7::ECAT7ParametricDensityOutputFileFormat -#else - new InterfileParametricDiscretisedDensityOutputFileFormat -#endif - ); +template <> +shared_ptr> OutputFileFormat::_default_sptr( +# ifdef HAVE_LLN_MATRIX + new ecat::ecat7::ECAT7ParametricDensityOutputFileFormat +# else + new InterfileParametricDiscretisedDensityOutputFileFormat +# endif +); #endif #if 1 - template <> - shared_ptr > - OutputFileFormat:: - _default_sptr( -#ifdef HAVE_LLN_MATRIX - new ecat::ecat7::ECAT7DynamicDiscretisedDensityOutputFileFormat -#else - new InterfileDynamicDiscretisedDensityOutputFileFormat -#endif - ); +template <> +shared_ptr> OutputFileFormat::_default_sptr( +# ifdef HAVE_LLN_MATRIX + new ecat::ecat7::ECAT7DynamicDiscretisedDensityOutputFileFormat +# else + new InterfileDynamicDiscretisedDensityOutputFileFormat +# endif +); #endif END_NAMESPACE_STIR - - - - diff --git a/src/IO/RegisteredObject.cxx b/src/IO/RegisteredObject.cxx index 9ee5d8b564..33fc714045 100644 --- a/src/IO/RegisteredObject.cxx +++ b/src/IO/RegisteredObject.cxx @@ -33,16 +33,15 @@ #ifdef __STIR_REGISTRY_NOT_INLINE -#pragma message("instantiating RegisteredObject") -#include "stir/IO/OutputFileFormat.h" +# pragma message("instantiating RegisteredObject") +# include "stir/IO/OutputFileFormat.h" // add here all roots of hierarchies based on RegisteredObject START_NAMESPACE_STIR template -RegisteredObject::RegistryType& -RegisteredObject::registry () -{ +RegisteredObject::RegistryType& +RegisteredObject::registry() { static RegistryType the_registry("None", 0); return the_registry; } @@ -50,10 +49,10 @@ RegisteredObject::registry () # ifdef _MSC_VER // prevent warning message on reinstantiation, // note that we get a linking error if we don't have the explicit instantiation below -# pragma warning(disable:4660) +# pragma warning(disable : 4660) # endif -template RegisteredObject; +template RegisteredObject; // add here all roots of hierarchies based on RegisteredObject END_NAMESPACE_STIR diff --git a/src/IO/ecat6_utils.cxx b/src/IO/ecat6_utils.cxx index 6142426a16..64565623f2 100644 --- a/src/IO/ecat6_utils.cxx +++ b/src/IO/ecat6_utils.cxx @@ -1,8 +1,7 @@ // // - -/*! +/*! \file \ingroup ECAT @@ -10,8 +9,8 @@ \author Kris Thielemans (conversions from/to VAX floats, longs) \author PARAPET project - \warning This file relies on ByteOrderDefine.h to find out if it - has to byteswap. This ideally would be changed to use the class stir::ByteOrder. + \warning This file relies on ByteOrderDefine.h to find out if it + has to byteswap. This ideally would be changed to use the class stir::ByteOrder. Make sure you run test/test_ByteOrder. */ @@ -43,14 +42,14 @@ - added support for data types different from 16 bit ints. - added a bit more diagonistics for file IO errors - KT 11/01/2001 - - added cti_read_norm_subheader,get_normheaders and removed get_attndata + KT 11/01/2001 + - added cti_read_norm_subheader,get_normheaders and removed get_attndata as it was identical to get_scandata KT 10/09/2004 - removed aliasing bugs in get_vax_float etc - KT 13/01/2008 + KT 13/01/2008 replace original CTI code with calls to LLN matrix library: - introduced mhead_ptr in various functions - have #define STIR_ORIGINAL_ECAT6 to be able to switch between old and new version @@ -59,15 +58,15 @@ removed CTI-derived code */ #include "stir/IO/stir_ecat_common.h" -#include "stir/IO/ecat6_utils.h" +#include "stir/IO/ecat6_utils.h" #ifndef STIR_ORIGINAL_ECAT6 // we will need file_data_to_host which is declared in machine_indep.h // However, that file has a problem with the definition of swab on some systems // so we declare it here //#include "machine_indep.h" -extern "C" int file_data_to_host(char *dptr, int nblks, int dtype); -extern "C" FILE *mat_create(char *fname, Main_header *mhead); +extern "C" int file_data_to_host(char* dptr, int nblks, int dtype); +extern "C" FILE* mat_create(char* fname, Main_header* mhead); #endif #include "stir/ByteOrder.h" @@ -84,510 +83,514 @@ extern "C" FILE *mat_create(char *fname, Main_header *mhead); // replace bcopy with memcpy #define bcopy(src, dest, length) memcpy(dest, src, length) -#define toblocks(x) ((x + (MatBLKSIZE - 1))/MatBLKSIZE) - -BOOST_STATIC_ASSERT(sizeof(unsigned short)==2); - +#define toblocks(x) ((x + (MatBLKSIZE - 1)) / MatBLKSIZE) +BOOST_STATIC_ASSERT(sizeof(unsigned short) == 2); START_NAMESPACE_STIR START_NAMESPACE_ECAT START_NAMESPACE_ECAT6 +int +get_scanheaders(FILE* fptr, long matnum, ECAT6_Main_header* mhead, Scan_subheader* shead, ScanInfoRec* scanParams) { + int status; + MatDir entry; -int get_scanheaders (FILE *fptr, long matnum, ECAT6_Main_header *mhead, - Scan_subheader *shead, ScanInfoRec *scanParams) -{ - int status; - MatDir entry; - - // check the header - status = cti_read_ECAT6_Main_header (fptr, mhead); - if (status != EXIT_SUCCESS) return EXIT_FAILURE; - - if (mhead->file_type != matScanFile) { - printf ("\n- file is not a scan file, type = %d\n", mhead->file_type); + // check the header + status = cti_read_ECAT6_Main_header(fptr, mhead); + if (status != EXIT_SUCCESS) + return EXIT_FAILURE; + + if (mhead->file_type != matScanFile) { + printf("\n- file is not a scan file, type = %d\n", mhead->file_type); #ifdef STIR_ORIGINAL_ECAT6 - dump_ECAT6_Main_header (0, mhead); + dump_ECAT6_Main_header(0, mhead); #endif - return EXIT_FAILURE; - } + return EXIT_FAILURE; + } - // look up matnum in scan file - if (!cti_lookup (fptr, mhead, matnum, &entry)) { - printf ("\n- specified matrix not in scan file\n"); + // look up matnum in scan file + if (!cti_lookup(fptr, mhead, matnum, &entry)) { + printf("\n- specified matrix not in scan file\n"); #ifdef STIR_ORIGINAL_ECAT6 - dump_ECAT6_Main_header (0, mhead); + dump_ECAT6_Main_header(0, mhead); #endif - return EXIT_FAILURE; - } - - // read scan subheader - status = cti_read_scan_subheader (fptr, mhead, entry.strtblk, shead); - if (status != EXIT_SUCCESS) { - printf ("\n- error reading scan subheader\n"); - return EXIT_FAILURE; - } - - scanParams->strtblk = entry.strtblk + 1; - scanParams->nblks = entry.endblk - entry.strtblk; + return EXIT_FAILURE; + } + + // read scan subheader + status = cti_read_scan_subheader(fptr, mhead, entry.strtblk, shead); + if (status != EXIT_SUCCESS) { + printf("\n- error reading scan subheader\n"); + return EXIT_FAILURE; + } + + scanParams->strtblk = entry.strtblk + 1; + scanParams->nblks = entry.endblk - entry.strtblk; #ifndef STIR_ORIGINAL_ECAT6 - scanParams->nprojs = shead->num_r_elements; - scanParams->nviews = shead->num_angles; + scanParams->nprojs = shead->num_r_elements; + scanParams->nviews = shead->num_angles; #else - scanParams->nprojs = shead->dimension_1; - scanParams->nviews = shead->dimension_2; + scanParams->nprojs = shead->dimension_1; + scanParams->nviews = shead->dimension_2; #endif - scanParams->data_type = shead->data_type; + scanParams->data_type = shead->data_type; #ifdef STIR_ORIGINAL_ECAT6 - if (shead->data_type != mhead->data_type) - printf("\nget_scanheader warning: \n" -"data types differ between main header (%d) and subheader (%d)\n" -"Using value from subheader\n", mhead->data_type, shead->data_type); + if (shead->data_type != mhead->data_type) + printf("\nget_scanheader warning: \n" + "data types differ between main header (%d) and subheader (%d)\n" + "Using value from subheader\n", + mhead->data_type, shead->data_type); #endif - return EXIT_SUCCESS; + return EXIT_SUCCESS; } -int get_scandata (FILE *fptr, char *scan, ScanInfoRec *scanParams) -{ - int status; +int +get_scandata(FILE* fptr, char* scan, ScanInfoRec* scanParams) { + int status; - // read data from scan file - if (!scan) return EXIT_FAILURE; + // read data from scan file + if (!scan) + return EXIT_FAILURE; - status= cti_rblk(fptr, scanParams->strtblk, (char *) scan, scanParams->nblks); - if (status != EXIT_SUCCESS) - return EXIT_FAILURE; - return - file_data_to_host(scan, scanParams->nblks,scanParams->data_type); + status = cti_rblk(fptr, scanParams->strtblk, (char*)scan, scanParams->nblks); + if (status != EXIT_SUCCESS) + return EXIT_FAILURE; + return file_data_to_host(scan, scanParams->nblks, scanParams->data_type); } +int +get_attnheaders(FILE* fptr, long matnum, ECAT6_Main_header* mhead, Attn_subheader* shead, ScanInfoRec* attnParams) { + int status; + MatDir entry; -int get_attnheaders (FILE *fptr, long matnum, ECAT6_Main_header *mhead, - Attn_subheader *shead, ScanInfoRec *attnParams) -{ - int status; - MatDir entry; - - // check the header - status = cti_read_ECAT6_Main_header (fptr, mhead); - if (status != EXIT_SUCCESS) return EXIT_FAILURE; - - if (mhead->file_type != matAttenFile) { - printf ("\n- file is not a attn file, type = %d\n", mhead->file_type); + // check the header + status = cti_read_ECAT6_Main_header(fptr, mhead); + if (status != EXIT_SUCCESS) + return EXIT_FAILURE; + + if (mhead->file_type != matAttenFile) { + printf("\n- file is not a attn file, type = %d\n", mhead->file_type); #ifdef STIR_ORIGINAL_ECAT6 - dump_ECAT6_Main_header (0, mhead); + dump_ECAT6_Main_header(0, mhead); #endif - return EXIT_FAILURE; - } + return EXIT_FAILURE; + } - // look up matnum in attn file - if (!cti_lookup (fptr, mhead, matnum, &entry)) { - printf ("\n- specified matrix not in attn file\n"); + // look up matnum in attn file + if (!cti_lookup(fptr, mhead, matnum, &entry)) { + printf("\n- specified matrix not in attn file\n"); #ifdef STIR_ORIGINAL_ECAT6 - dump_ECAT6_Main_header (0, mhead); + dump_ECAT6_Main_header(0, mhead); #endif - return EXIT_FAILURE; - } - - // read attn subheader - status = cti_read_attn_subheader (fptr, mhead, entry.strtblk, shead); - if (status != EXIT_SUCCESS) { - printf ("\n- error reading attn subheader\n"); - return EXIT_FAILURE; - } - - attnParams->strtblk = entry.strtblk + 1; - attnParams->nblks = entry.endblk - entry.strtblk; + return EXIT_FAILURE; + } + + // read attn subheader + status = cti_read_attn_subheader(fptr, mhead, entry.strtblk, shead); + if (status != EXIT_SUCCESS) { + printf("\n- error reading attn subheader\n"); + return EXIT_FAILURE; + } + + attnParams->strtblk = entry.strtblk + 1; + attnParams->nblks = entry.endblk - entry.strtblk; #ifndef STIR_ORIGINAL_ECAT6 - attnParams->nprojs = shead->num_r_elements; - attnParams->nviews = shead->num_angles; + attnParams->nprojs = shead->num_r_elements; + attnParams->nviews = shead->num_angles; #else - attnParams->nprojs = shead->dimension_1; - attnParams->nviews = shead->dimension_2; + attnParams->nprojs = shead->dimension_1; + attnParams->nviews = shead->dimension_2; #endif - attnParams->data_type = shead->data_type; + attnParams->data_type = shead->data_type; #ifdef STIR_ORIGINAL_ECAT6 - if (shead->data_type != mhead->data_type) - printf("\nget_attnheader warning: \n" -"data types differ between main header (%d) and subheader (%d)\n" -"Using value from subheader\n", mhead->data_type, shead->data_type); + if (shead->data_type != mhead->data_type) + printf("\nget_attnheader warning: \n" + "data types differ between main header (%d) and subheader (%d)\n" + "Using value from subheader\n", + mhead->data_type, shead->data_type); #endif - return EXIT_SUCCESS; + return EXIT_SUCCESS; } +int +get_normheaders(FILE* fptr, long matnum, ECAT6_Main_header* mhead, Norm_subheader* shead, ScanInfoRec* normParams) { + int status; + MatDir entry; + // check the header + status = cti_read_ECAT6_Main_header(fptr, mhead); + if (status != EXIT_SUCCESS) + return EXIT_FAILURE; -int get_normheaders (FILE *fptr, long matnum, ECAT6_Main_header *mhead, - Norm_subheader *shead, ScanInfoRec *normParams) -{ - int status; - MatDir entry; - - // check the header - status = cti_read_ECAT6_Main_header (fptr, mhead); - if (status != EXIT_SUCCESS) return EXIT_FAILURE; - - if (mhead->file_type != matNormFile) { - printf ("\n- file is not a norm file, type = %d\n", mhead->file_type); + if (mhead->file_type != matNormFile) { + printf("\n- file is not a norm file, type = %d\n", mhead->file_type); #ifdef STIR_ORIGINAL_ECAT6 - dump_ECAT6_Main_header (0, mhead); + dump_ECAT6_Main_header(0, mhead); #endif - return EXIT_FAILURE; - } + return EXIT_FAILURE; + } - // look up matnum in norm file - if (!cti_lookup (fptr, mhead, matnum, &entry)) { - printf ("\n- specified matrix not in norm file\n"); + // look up matnum in norm file + if (!cti_lookup(fptr, mhead, matnum, &entry)) { + printf("\n- specified matrix not in norm file\n"); #ifdef STIR_ORIGINAL_ECAT6 - dump_ECAT6_Main_header (0, mhead); + dump_ECAT6_Main_header(0, mhead); #endif - return EXIT_FAILURE; - } - - // read norm subheader - status = cti_read_norm_subheader (fptr, mhead, entry.strtblk, shead); - if (status != EXIT_SUCCESS) { - printf ("\n- error reading norm subheader\n"); - return EXIT_FAILURE; - } - - normParams->strtblk = entry.strtblk + 1; - normParams->nblks = entry.endblk - entry.strtblk; + return EXIT_FAILURE; + } + + // read norm subheader + status = cti_read_norm_subheader(fptr, mhead, entry.strtblk, shead); + if (status != EXIT_SUCCESS) { + printf("\n- error reading norm subheader\n"); + return EXIT_FAILURE; + } + + normParams->strtblk = entry.strtblk + 1; + normParams->nblks = entry.endblk - entry.strtblk; #ifndef STIR_ORIGINAL_ECAT6 - normParams->nprojs = shead->num_r_elements; - normParams->nviews = shead->num_angles; + normParams->nprojs = shead->num_r_elements; + normParams->nviews = shead->num_angles; #else - normParams->nprojs = shead->dimension_1; - normParams->nviews = shead->dimension_2; + normParams->nprojs = shead->dimension_1; + normParams->nviews = shead->dimension_2; #endif - normParams->data_type = shead->data_type; + normParams->data_type = shead->data_type; #ifdef STIR_ORIGINAL_ECAT6 - if (shead->data_type != mhead->data_type) - printf("\nget_normheader warning: \n" -"data types differ between main header (%d) and subheader (%d)\n" -"Using value from subheader\n", mhead->data_type, shead->data_type); + if (shead->data_type != mhead->data_type) + printf("\nget_normheader warning: \n" + "data types differ between main header (%d) and subheader (%d)\n" + "Using value from subheader\n", + mhead->data_type, shead->data_type); #endif - return EXIT_SUCCESS; + return EXIT_SUCCESS; } #ifndef STIR_ORIGINAL_ECAT6 FILE* -cti_create(const char * const fname, const Main_header *mhead) -{ - return mat_create(const_cast(fname), const_cast(mhead)); +cti_create(const char* const fname, const Main_header* mhead) { + return mat_create(const_cast(fname), const_cast(mhead)); } -int cti_read_ECAT6_Main_header (FILE *fptr, ECAT6_Main_header *h) -{ +int +cti_read_ECAT6_Main_header(FILE* fptr, ECAT6_Main_header* h) { const int cti_status = mat_read_main_header(fptr, h); - return cti_status==0 ? EXIT_SUCCESS : EXIT_FAILURE; + return cti_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } -long cti_numcod (int frame, int plane, int gate, int data, int bed) -{ +long +cti_numcod(int frame, int plane, int gate, int data, int bed) { return mat_numcod(frame, plane, gate, data, bed); } -void cti_numdoc (long matnum, Matval *matval) -{ +void +cti_numdoc(long matnum, Matval* matval) { mat_numdoc(matnum, matval); } -int cti_rblk (FILE *fptr, int blkno, void *bufr, int nblks) -{ - const int cti_status = mat_rblk(fptr, blkno, reinterpret_cast(bufr), nblks); - return cti_status==0 ? EXIT_SUCCESS : EXIT_FAILURE; +int +cti_rblk(FILE* fptr, int blkno, void* bufr, int nblks) { + const int cti_status = mat_rblk(fptr, blkno, reinterpret_cast(bufr), nblks); + return cti_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } -int cti_wblk (FILE *fptr, int blkno, void *bufr, int nblks) -{ - const int cti_status = mat_wblk (fptr, blkno, reinterpret_cast(bufr), nblks); - return cti_status==0 ? EXIT_SUCCESS : EXIT_FAILURE; +int +cti_wblk(FILE* fptr, int blkno, void* bufr, int nblks) { + const int cti_status = mat_wblk(fptr, blkno, reinterpret_cast(bufr), nblks); + return cti_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } -int cti_enter (FILE *fptr, const ECAT6_Main_header* mhead_ptr, long matnum, int nblks) -{ +int +cti_enter(FILE* fptr, const ECAT6_Main_header* mhead_ptr, long matnum, int nblks) { return mat_enter(fptr, const_cast(mhead_ptr), matnum, nblks); } -int cti_lookup (FILE *fptr, const ECAT6_Main_header* mhead_ptr, long matnum, MatDir *entry) -{ +int +cti_lookup(FILE* fptr, const ECAT6_Main_header* mhead_ptr, long matnum, MatDir* entry) { return mat_lookup(fptr, const_cast(mhead_ptr), matnum, entry); } -int cti_read_image_subheader (FILE *fptr, const ECAT6_Main_header *h, int blknum, Image_subheader *header_ptr) -{ - const int cti_status = mat_read_image_subheader (fptr, const_cast(h), blknum, header_ptr); - return cti_status==0 ? EXIT_SUCCESS : EXIT_FAILURE; +int +cti_read_image_subheader(FILE* fptr, const ECAT6_Main_header* h, int blknum, Image_subheader* header_ptr) { + const int cti_status = mat_read_image_subheader(fptr, const_cast(h), blknum, header_ptr); + return cti_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } -int cti_read_scan_subheader (FILE *fptr, const ECAT6_Main_header *h, int blknum, Scan_subheader *header_ptr) -{ - const int cti_status = mat_read_scan_subheader (fptr, const_cast(h), blknum, header_ptr); - return cti_status==0 ? EXIT_SUCCESS : EXIT_FAILURE; +int +cti_read_scan_subheader(FILE* fptr, const ECAT6_Main_header* h, int blknum, Scan_subheader* header_ptr) { + const int cti_status = mat_read_scan_subheader(fptr, const_cast(h), blknum, header_ptr); + return cti_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } -int cti_read_attn_subheader (FILE *fptr, const ECAT6_Main_header *h, int blknum, Attn_subheader *header_ptr) -{ - const int cti_status = mat_read_attn_subheader (fptr, const_cast(h), blknum, header_ptr); - return cti_status==0 ? EXIT_SUCCESS : EXIT_FAILURE; +int +cti_read_attn_subheader(FILE* fptr, const ECAT6_Main_header* h, int blknum, Attn_subheader* header_ptr) { + const int cti_status = mat_read_attn_subheader(fptr, const_cast(h), blknum, header_ptr); + return cti_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } -int cti_read_norm_subheader (FILE *fptr, const ECAT6_Main_header *h, int blknum, Norm_subheader *header_ptr) -{ - const int cti_status = mat_read_norm_subheader (fptr, const_cast(h), blknum, header_ptr); - return cti_status==0 ? EXIT_SUCCESS : EXIT_FAILURE; +int +cti_read_norm_subheader(FILE* fptr, const ECAT6_Main_header* h, int blknum, Norm_subheader* header_ptr) { + const int cti_status = mat_read_norm_subheader(fptr, const_cast(h), blknum, header_ptr); + return cti_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } -int cti_write_image_subheader (FILE *fptr, const ECAT6_Main_header *h, int blknum, const Image_subheader *header_ptr) -{ - const int cti_status = mat_write_image_subheader (fptr, const_cast(h), blknum, const_cast(header_ptr)); - return cti_status==0 ? EXIT_SUCCESS : EXIT_FAILURE; +int +cti_write_image_subheader(FILE* fptr, const ECAT6_Main_header* h, int blknum, const Image_subheader* header_ptr) { + const int cti_status = + mat_write_image_subheader(fptr, const_cast(h), blknum, const_cast(header_ptr)); + return cti_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } -int cti_write_scan_subheader (FILE *fptr, const ECAT6_Main_header *h, int blknum, const Scan_subheader *header_ptr) -{ - const int cti_status = mat_write_scan_subheader (fptr, const_cast(h), blknum, const_cast(header_ptr)); - return cti_status==0 ? EXIT_SUCCESS : EXIT_FAILURE; +int +cti_write_scan_subheader(FILE* fptr, const ECAT6_Main_header* h, int blknum, const Scan_subheader* header_ptr) { + const int cti_status = + mat_write_scan_subheader(fptr, const_cast(h), blknum, const_cast(header_ptr)); + return cti_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } -int file_data_to_host(char *dptr, int nblks, int dtype) -{ +int +file_data_to_host(char* dptr, int nblks, int dtype) { const int cti_status = ::file_data_to_host(dptr, nblks, dtype); - return cti_status==0 ? EXIT_SUCCESS : EXIT_FAILURE; + return cti_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } -#else// STIR_ORIGINAL_ECAT6 +#else // STIR_ORIGINAL_ECAT6 -#error Original ECAT6 code removed +# error Original ECAT6 code removed #endif // STIR_ORIGINAL_ECAT6 -int cti_rings2plane (short nrings, short ring0, short ring1) +int +cti_rings2plane(short nrings, short ring0, short ring1) { - int d = (int) (ring0 / (nrings/2)); + int d = (int)(ring0 / (nrings / 2)); - return (ring1 * nrings/2 + ring0 % (nrings/2) + - nrings/2 * nrings * d + 1); + return (ring1 * nrings / 2 + ring0 % (nrings / 2) + nrings / 2 * nrings * d + 1); } #ifdef STIR_ORIGINAL_ECAT6 #endif // STIR_ORIGINAL_ECAT6 -int cti_write_idata (FILE *fptr, int blk, const short *data, int ibytes) -{ - unsigned int nblks; - char *dataptr; - int status; - - if (ibytes%MatBLKSIZE != 0) - { - warning("Error writing ECAT6 data: data_size should be a multiple of %d.\nNo Data written to file.", - MatBLKSIZE); - return (EXIT_FAILURE); - } +int +cti_write_idata(FILE* fptr, int blk, const short* data, int ibytes) { + unsigned int nblks; + char* dataptr; + int status; + + if (ibytes % MatBLKSIZE != 0) { + warning("Error writing ECAT6 data: data_size should be a multiple of %d.\nNo Data written to file.", MatBLKSIZE); + return (EXIT_FAILURE); + } #if STIRIsNativeByteOrderBigEndian - char bufr[MatBLKSIZE]; - - dataptr = (char *) data; // point into data buffer - - // we'll use cti_wblk to write the data via another buffer. - // this way, if we need to transform the data as we went, we can do it. - nblks = toblocks (ibytes); - for (unsigned int i=0; i * +static VoxelsOnCartesianGrid* create_image_and_header_from(InterfileImageHeader& hdr, - char * full_data_file_name, // preallocated - istream& input, - const string& directory_for_data) -{ - if (!hdr.parse(input)) - { - return 0; //KT 10/12/2001 do not call ask_parameters anymore - } - + char* full_data_file_name, // preallocated + istream& input, const string& directory_for_data) { + if (!hdr.parse(input)) { + return 0; // KT 10/12/2001 do not call ask_parameters anymore + } + // prepend directory_for_data to the data_file_name from the header strcpy(full_data_file_name, hdr.data_file_name.c_str()); - prepend_directory_name(full_data_file_name, directory_for_data.c_str()); - - - CartesianCoordinate3D voxel_size(static_cast(hdr.pixel_sizes[2]), - static_cast(hdr.pixel_sizes[1]), - static_cast(hdr.pixel_sizes[0])); - - const int z_size = hdr.matrix_size[2][0]; - const int y_size = hdr.matrix_size[1][0]; - const int x_size = hdr.matrix_size[0][0]; - const BasicCoordinate<3,int> min_indices = - make_coordinate(0, -y_size/2, -x_size/2); - const BasicCoordinate<3,int> max_indices = - min_indices + make_coordinate(z_size, y_size, x_size) - 1; - - CartesianCoordinate3D origin(0,0,0); - if (hdr.first_pixel_offsets[2] != InterfileHeader::double_value_not_set) - { - // make sure that origin is such that - // first_pixel_offsets = min_indices*voxel_size + origin - origin = - make_coordinate(float(hdr.first_pixel_offsets[2]), - float(hdr.first_pixel_offsets[1]), - float(hdr.first_pixel_offsets[0])) - - voxel_size * BasicCoordinate<3,float>(min_indices); - } + prepend_directory_name(full_data_file_name, directory_for_data.c_str()); + + CartesianCoordinate3D voxel_size(static_cast(hdr.pixel_sizes[2]), static_cast(hdr.pixel_sizes[1]), + static_cast(hdr.pixel_sizes[0])); + + const int z_size = hdr.matrix_size[2][0]; + const int y_size = hdr.matrix_size[1][0]; + const int x_size = hdr.matrix_size[0][0]; + const BasicCoordinate<3, int> min_indices = make_coordinate(0, -y_size / 2, -x_size / 2); + const BasicCoordinate<3, int> max_indices = min_indices + make_coordinate(z_size, y_size, x_size) - 1; + + CartesianCoordinate3D origin(0, 0, 0); + if (hdr.first_pixel_offsets[2] != InterfileHeader::double_value_not_set) { + // make sure that origin is such that + // first_pixel_offsets = min_indices*voxel_size + origin + origin = + make_coordinate(float(hdr.first_pixel_offsets[2]), float(hdr.first_pixel_offsets[1]), float(hdr.first_pixel_offsets[0])) - + voxel_size * BasicCoordinate<3, float>(min_indices); + } - return - new VoxelsOnCartesianGrid - (hdr.get_exam_info_sptr(), - IndexRange<3>(min_indices, max_indices), - origin, - voxel_size); + return new VoxelsOnCartesianGrid(hdr.get_exam_info_sptr(), IndexRange<3>(min_indices, max_indices), origin, voxel_size); } -VoxelsOnCartesianGrid * -read_interfile_image(istream& input, - const string& directory_for_data) -{ +VoxelsOnCartesianGrid* +read_interfile_image(istream& input, const string& directory_for_data) { InterfileImageHeader hdr; char full_data_file_name[max_filename_length]; - VoxelsOnCartesianGrid * image_ptr = - create_image_and_header_from(hdr, - full_data_file_name, - input, - directory_for_data); - + VoxelsOnCartesianGrid* image_ptr = create_image_and_header_from(hdr, full_data_file_name, input, directory_for_data); + ifstream data_in; open_read_binary(data_in, full_data_file_name); data_in.seekg(hdr.data_offset_each_dataset[0]); - if (hdr.data_offset_each_dataset[0]>0) + if (hdr.data_offset_each_dataset[0] > 0) data_in.seekg(hdr.data_offset_each_dataset[0]); // read into image_sptr first float scale = float(1); - if (read_data(data_in, *image_ptr, hdr.type_of_numbers, scale, hdr.file_byte_order) - == Succeeded::no - || scale != 1) - { - warning("read_interfile_image: error reading data or scale factor returned by read_data not equal to 1\n"); - return 0; - } - - for (int i=0; i< hdr.matrix_size[2][0]; i++) - if (hdr.image_scaling_factors[0][i]!= 1) + if (read_data(data_in, *image_ptr, hdr.type_of_numbers, scale, hdr.file_byte_order) == Succeeded::no || scale != 1) { + warning("read_interfile_image: error reading data or scale factor returned by read_data not equal to 1\n"); + return 0; + } + + for (int i = 0; i < hdr.matrix_size[2][0]; i++) + if (hdr.image_scaling_factors[0][i] != 1) (*image_ptr)[i] *= static_cast(hdr.image_scaling_factors[0][i]); // Check number of time frames if (image_ptr->get_exam_info().get_time_frame_definitions().get_num_frames() > 1) { - warning(str(boost::format("Discretised density should contain 1 time frame, but this image contains %1%. " - "Only the first will be kept, and the rest discarded.") - % image_ptr->get_exam_info().get_time_frame_definitions().get_num_frames())); - ExamInfo exam_info = image_ptr->get_exam_info(); - exam_info.time_frame_definitions.set_num_time_frames(1); - image_ptr->set_exam_info(exam_info); - } - else if (image_ptr->get_exam_info().get_time_frame_definitions().get_num_frames() == 0) - warning("DiscretisedDensity does not contain any time frames. This might cause an error."); - + warning(str(boost::format("Discretised density should contain 1 time frame, but this image contains %1%. " + "Only the first will be kept, and the rest discarded.") % + image_ptr->get_exam_info().get_time_frame_definitions().get_num_frames())); + ExamInfo exam_info = image_ptr->get_exam_info(); + exam_info.time_frame_definitions.set_num_time_frames(1); + image_ptr->set_exam_info(exam_info); + } else if (image_ptr->get_exam_info().get_time_frame_definitions().get_num_frames() == 0) + warning("DiscretisedDensity does not contain any time frames. This might cause an error."); return image_ptr; } DynamicDiscretisedDensity* -read_interfile_dynamic_image(istream& input, - const string& directory_for_data) -{ +read_interfile_dynamic_image(istream& input, const string& directory_for_data) { InterfileImageHeader hdr; char full_data_file_name[max_filename_length]; - shared_ptr > - image_sptr(create_image_and_header_from(hdr, - full_data_file_name, - input, - directory_for_data)); + shared_ptr> image_sptr( + create_image_and_header_from(hdr, full_data_file_name, input, directory_for_data)); if (is_null_ptr(image_sptr)) error("Error parsing dynamic image"); shared_ptr scanner_sptr(Scanner::get_scanner_from_name(hdr.get_exam_info().originating_system)); - DynamicDiscretisedDensity * dynamic_dens_ptr = - new DynamicDiscretisedDensity(hdr.get_exam_info().time_frame_definitions, - hdr.get_exam_info().start_time_in_secs_since_1970, - scanner_sptr, - image_sptr); + DynamicDiscretisedDensity* dynamic_dens_ptr = new DynamicDiscretisedDensity( + hdr.get_exam_info().time_frame_definitions, hdr.get_exam_info().start_time_in_secs_since_1970, scanner_sptr, image_sptr); ifstream data_in; open_read_binary(data_in, full_data_file_name); @@ -218,64 +178,50 @@ read_interfile_dynamic_image(istream& input, data_in.seekg(hdr.data_offset_each_dataset[0]); ExamInfo _exam_info(hdr.get_exam_info()); - for (unsigned int frame_num=1; frame_num <= dynamic_dens_ptr->get_num_time_frames(); ++frame_num) - { - data_in.seekg(hdr.data_offset_each_dataset[frame_num-1]); - - // read into image_sptr first - float scale = float(1); - if (read_data(data_in, *image_sptr, hdr.type_of_numbers, scale, hdr.file_byte_order) - == Succeeded::no - || fabs(scale-float(1))>float(1e-10)) - { - warning("read_interfile_dynamic_image: error reading data or scale factor returned by read_data not equal to 1"); - return 0; - } - - for (int i=0; i< hdr.matrix_size[2][0]; i++) - if (fabs(hdr.image_scaling_factors[frame_num-1][i]-double(1))>double(1e-10)) - (*image_sptr)[i] *= static_cast(hdr.image_scaling_factors[frame_num-1][i]); + for (unsigned int frame_num = 1; frame_num <= dynamic_dens_ptr->get_num_time_frames(); ++frame_num) { + data_in.seekg(hdr.data_offset_each_dataset[frame_num - 1]); + + // read into image_sptr first + float scale = float(1); + if (read_data(data_in, *image_sptr, hdr.type_of_numbers, scale, hdr.file_byte_order) == Succeeded::no || + fabs(scale - float(1)) > float(1e-10)) { + warning("read_interfile_dynamic_image: error reading data or scale factor returned by read_data not equal to 1"); + return 0; + } - // Set the time frame of the individual frame - _exam_info.time_frame_definitions = - TimeFrameDefinitions(hdr.get_exam_info().time_frame_definitions,frame_num); - image_sptr->set_exam_info(_exam_info); + for (int i = 0; i < hdr.matrix_size[2][0]; i++) + if (fabs(hdr.image_scaling_factors[frame_num - 1][i] - double(1)) > double(1e-10)) + (*image_sptr)[i] *= static_cast(hdr.image_scaling_factors[frame_num - 1][i]); - // now stick into the dynamic image - dynamic_dens_ptr->set_density(*image_sptr,frame_num); + // Set the time frame of the individual frame + _exam_info.time_frame_definitions = TimeFrameDefinitions(hdr.get_exam_info().time_frame_definitions, frame_num); + image_sptr->set_exam_info(_exam_info); - } + // now stick into the dynamic image + dynamic_dens_ptr->set_density(*image_sptr, frame_num); + } return dynamic_dens_ptr; } ParametricVoxelsOnCartesianGrid* -read_interfile_parametric_image(istream& input, - const string& directory_for_data) -{ +read_interfile_parametric_image(istream& input, const string& directory_for_data) { InterfileImageHeader hdr; char full_data_file_name[max_filename_length]; - shared_ptr > - image_sptr(create_image_and_header_from(hdr, - full_data_file_name, - input, - directory_for_data)); + shared_ptr> image_sptr( + create_image_and_header_from(hdr, full_data_file_name, input, directory_for_data)); if (is_null_ptr(image_sptr)) error("Error parsing parametric image"); shared_ptr scanner_sptr(Scanner::get_scanner_from_name(hdr.get_exam_info().originating_system)); - BasicCoordinate<3,float> voxel_size; + BasicCoordinate<3, float> voxel_size; voxel_size[1] = hdr.pixel_sizes[2]; voxel_size[2] = hdr.pixel_sizes[1]; voxel_size[3] = hdr.pixel_sizes[0]; ParametricVoxelsOnCartesianGrid* parametric_dens_ptr = - new ParametricVoxelsOnCartesianGrid( - ParametricVoxelsOnCartesianGridBaseType( - hdr.get_exam_info_sptr(), - image_sptr->get_index_range(), - image_sptr->get_origin(), - voxel_size)); + new ParametricVoxelsOnCartesianGrid(ParametricVoxelsOnCartesianGridBaseType( + hdr.get_exam_info_sptr(), image_sptr->get_index_range(), image_sptr->get_origin(), voxel_size)); ifstream data_in; open_read_binary(data_in, full_data_file_name); @@ -283,67 +229,61 @@ read_interfile_parametric_image(istream& input, data_in.seekg(hdr.data_offset_each_dataset[0]); // loop over each of the parametric image types (e.g., slope, intercept) - for (int kin_param=1; kin_param<=hdr.num_image_data_types; kin_param++) { - - data_in.seekg(hdr.data_offset_each_dataset[kin_param-1]); - - // read into image_sptr first - float scale = float(1); - if (read_data(data_in, *image_sptr, hdr.type_of_numbers, scale, hdr.file_byte_order) - == Succeeded::no - || scale != 1) - { - warning("read_interfile_parametric_image: error reading data or scale factor returned by read_data not equal to 1"); - return 0; - } + for (int kin_param = 1; kin_param <= hdr.num_image_data_types; kin_param++) { + + data_in.seekg(hdr.data_offset_each_dataset[kin_param - 1]); + + // read into image_sptr first + float scale = float(1); + if (read_data(data_in, *image_sptr, hdr.type_of_numbers, scale, hdr.file_byte_order) == Succeeded::no || scale != 1) { + warning("read_interfile_parametric_image: error reading data or scale factor returned by read_data not equal to 1"); + return 0; + } - for (int i=0; i< hdr.matrix_size[2][0]; i++) - if (hdr.image_scaling_factors[kin_param-1][i]!= 1) - (*image_sptr)[i] *= static_cast(hdr.image_scaling_factors[kin_param-1][i]); + for (int i = 0; i < hdr.matrix_size[2][0]; i++) + if (hdr.image_scaling_factors[kin_param - 1][i] != 1) + (*image_sptr)[i] *= static_cast(hdr.image_scaling_factors[kin_param - 1][i]); - // Check that we're dealing with VoxelsOnCartesianGrid - if (dynamic_cast * >(image_sptr.get())==0) - error("ParametricDiscretisedDensity::read_from_file only supports VoxelsOnCartesianGrid"); + // Check that we're dealing with VoxelsOnCartesianGrid + if (dynamic_cast*>(image_sptr.get()) == 0) + error("ParametricDiscretisedDensity::read_from_file only supports VoxelsOnCartesianGrid"); - // Set the image for the given kinetic parameter - ParametricVoxelsOnCartesianGrid::SingleDiscretisedDensityType::const_full_iterator single_density_iter = + // Set the image for the given kinetic parameter + ParametricVoxelsOnCartesianGrid::SingleDiscretisedDensityType::const_full_iterator single_density_iter = image_sptr->begin_all(); - ParametricVoxelsOnCartesianGrid::SingleDiscretisedDensityType::const_full_iterator end_single_density_iter = + ParametricVoxelsOnCartesianGrid::SingleDiscretisedDensityType::const_full_iterator end_single_density_iter = image_sptr->end_all(); - ParametricVoxelsOnCartesianGrid::full_densel_iterator parametric_density_iter = - parametric_dens_ptr->begin_all_densel(); + ParametricVoxelsOnCartesianGrid::full_densel_iterator parametric_density_iter = parametric_dens_ptr->begin_all_densel(); - while (single_density_iter!=end_single_density_iter) - { - (*parametric_density_iter)[kin_param] = *single_density_iter; - ++single_density_iter; ++parametric_density_iter; - } + while (single_density_iter != end_single_density_iter) { + (*parametric_density_iter)[kin_param] = *single_density_iter; + ++single_density_iter; + ++parametric_density_iter; + } } return parametric_dens_ptr; } -VoxelsOnCartesianGrid* read_interfile_image(const string& filename) -{ +VoxelsOnCartesianGrid* +read_interfile_image(const string& filename) { ifstream image_stream(filename.c_str()); - if (!image_stream) - { - error("read_interfile_image: couldn't open file %s\n", filename.c_str()); - } + if (!image_stream) { + error("read_interfile_image: couldn't open file %s\n", filename.c_str()); + } char directory_name[max_filename_length]; get_directory_name(directory_name, filename.c_str()); - + return read_interfile_image(image_stream, directory_name); } -DynamicDiscretisedDensity* read_interfile_dynamic_image(const string& filename) -{ +DynamicDiscretisedDensity* +read_interfile_dynamic_image(const string& filename) { ifstream image_stream(filename.c_str()); - if (!image_stream) - { - error("read_interfile_dynamic_image: couldn't open file %s\n", filename.c_str()); - } + if (!image_stream) { + error("read_interfile_dynamic_image: couldn't open file %s\n", filename.c_str()); + } char directory_name[max_filename_length]; get_directory_name(directory_name, filename.c_str()); @@ -352,13 +292,11 @@ DynamicDiscretisedDensity* read_interfile_dynamic_image(const string& filename) } ParametricVoxelsOnCartesianGrid* -read_interfile_parametric_image(const string& filename) -{ +read_interfile_parametric_image(const string& filename) { ifstream image_stream(filename.c_str()); - if (!image_stream) - { - error("read_interfile_parametric_image: couldn't open file %s\n", filename.c_str()); - } + if (!image_stream) { + error("read_interfile_parametric_image: couldn't open file %s\n", filename.c_str()); + } char directory_name[max_filename_length]; get_directory_name(directory_name, filename.c_str()); @@ -386,159 +324,144 @@ compute_file_offsets(int number_of_time_frames, to write in the header. It tries to cut the directory part of data_file_name if it's the same as the directory part of the header. */ -static -string -interfile_get_data_file_name_in_header(const string& header_file_name, - const string& data_file_name) -{ - const string dir_name_of_binary_data = - get_directory_name(data_file_name); - if (dir_name_of_binary_data.size() == 0) - { - // data_dirname is empty - return data_file_name; - } - const string dir_name_of_header = - get_directory_name(header_file_name); - if (dir_name_of_header == dir_name_of_binary_data) - { - // dirnames are the same, so strip from data_file_name - return - string(data_file_name, - find_pos_of_filename(data_file_name), - string::npos); - } - else - { - // just copy, what else to do? - return data_file_name; - } +static string +interfile_get_data_file_name_in_header(const string& header_file_name, const string& data_file_name) { + const string dir_name_of_binary_data = get_directory_name(data_file_name); + if (dir_name_of_binary_data.size() == 0) { + // data_dirname is empty + return data_file_name; + } + const string dir_name_of_header = get_directory_name(header_file_name); + if (dir_name_of_header == dir_name_of_binary_data) { + // dirnames are the same, so strip from data_file_name + return string(data_file_name, find_pos_of_filename(data_file_name), string::npos); + } else { + // just copy, what else to do? + return data_file_name; + } } //// some static helper functions for writing // probably should be moved to InterfileHeader -static void write_interfile_patient_position(std::ostream& output_header, const ExamInfo& exam_info) -{ +static void +write_interfile_patient_position(std::ostream& output_header, const ExamInfo& exam_info) { string orientation; - switch (exam_info.patient_position.get_orientation()) - { - case PatientPosition::head_in: orientation="head_in";break; - case PatientPosition::feet_in: orientation="feet_in";break; - case PatientPosition::other_orientation: orientation="other";break; - default: orientation="unknown"; break; - } + switch (exam_info.patient_position.get_orientation()) { + case PatientPosition::head_in: + orientation = "head_in"; + break; + case PatientPosition::feet_in: + orientation = "feet_in"; + break; + case PatientPosition::other_orientation: + orientation = "other"; + break; + default: + orientation = "unknown"; + break; + } string rotation; - switch (exam_info.patient_position.get_rotation()) - { - case PatientPosition::prone: rotation="prone";break; - case PatientPosition::supine: rotation="supine";break; - case PatientPosition::other_rotation: - case PatientPosition::left: - case PatientPosition::right: - rotation="other";break; - default: rotation="unknown"; break; - } - if (orientation!="unknown") + switch (exam_info.patient_position.get_rotation()) { + case PatientPosition::prone: + rotation = "prone"; + break; + case PatientPosition::supine: + rotation = "supine"; + break; + case PatientPosition::other_rotation: + case PatientPosition::left: + case PatientPosition::right: + rotation = "other"; + break; + default: + rotation = "unknown"; + break; + } + if (orientation != "unknown") output_header << "patient orientation := " << orientation << '\n'; - if (rotation!="unknown") + if (rotation != "unknown") output_header << "patient rotation := " << rotation << '\n'; } -static void write_interfile_time_frame_definitions(std::ostream& output_header, const ExamInfo& exam_info) - // TODO this is according to the proposed interfile standard for PET. Interfile 3.3 is different +static void +write_interfile_time_frame_definitions(std::ostream& output_header, const ExamInfo& exam_info) +// TODO this is according to the proposed interfile standard for PET. Interfile 3.3 is different { const TimeFrameDefinitions& frame_defs(exam_info.time_frame_definitions); - if (frame_defs.get_num_time_frames()>0) - { - output_header << "number of time frames := " << frame_defs.get_num_time_frames() << '\n'; - for (unsigned int frame_num=1; frame_num<=frame_defs.get_num_time_frames(); ++frame_num) - { - if (frame_defs.get_duration(frame_num)>0) - { - output_header << "image duration (sec)[" << frame_num << "] := " - << frame_defs.get_duration(frame_num) << '\n'; - output_header << "image relative start time (sec)[" << frame_num << "] := " - << frame_defs.get_start_time(frame_num) << '\n'; - } - } - } - else - { - // need to write this anyway to allow vectored keys below - output_header << "number of time frames := 1\n"; + if (frame_defs.get_num_time_frames() > 0) { + output_header << "number of time frames := " << frame_defs.get_num_time_frames() << '\n'; + for (unsigned int frame_num = 1; frame_num <= frame_defs.get_num_time_frames(); ++frame_num) { + if (frame_defs.get_duration(frame_num) > 0) { + output_header << "image duration (sec)[" << frame_num << "] := " << frame_defs.get_duration(frame_num) << '\n'; + output_header << "image relative start time (sec)[" << frame_num << "] := " << frame_defs.get_start_time(frame_num) + << '\n'; + } } + } else { + // need to write this anyway to allow vectored keys below + output_header << "number of time frames := 1\n"; + } } // Write energy window lower and upper thresholds, if they are not -1 -static void write_interfile_energy_windows(std::ostream& output_header, const ExamInfo& exam_info) -{ - if (exam_info.get_high_energy_thres() > 0 && - exam_info.get_low_energy_thres() >= 0) - { - output_header << "number of energy windows := 1\n"; - output_header << "energy window lower level[1] := " << - exam_info.get_low_energy_thres() << '\n'; - output_header << "energy window upper level[1] := " << - exam_info.get_high_energy_thres() << '\n'; - } +static void +write_interfile_energy_windows(std::ostream& output_header, const ExamInfo& exam_info) { + if (exam_info.get_high_energy_thres() > 0 && exam_info.get_low_energy_thres() >= 0) { + output_header << "number of energy windows := 1\n"; + output_header << "energy window lower level[1] := " << exam_info.get_low_energy_thres() << '\n'; + output_header << "energy window upper level[1] := " << exam_info.get_high_energy_thres() << '\n'; + } } // Write data type descriptions (if there are any) -static void write_interfile_image_data_descriptions(std::ostream& output_header, const std::vector& data_type_descriptions) -{ - if (data_type_descriptions.size() == 0) return; - - output_header << "number of image data types := " << data_type_descriptions.size() << '\n'; - output_header << "index nesting level := {data type}\n"; - for (int i=0; i& data_type_descriptions) { + if (data_type_descriptions.size() == 0) + return; + + output_header << "number of image data types := " << data_type_descriptions.size() << '\n'; + output_header << "index nesting level := {data type}\n"; + for (int i = 0; i < data_type_descriptions.size(); i++) + output_header << "image data type description[" << i + 1 << "] := " << data_type_descriptions[i] << "\n"; } -static void write_interfile_modality(std::ostream& output_header, const ExamInfo& exam_info) -{ +static void +write_interfile_modality(std::ostream& output_header, const ExamInfo& exam_info) { /* // default modality is PET ImagingModality imaging_modality(ImagingModality::PT); if (!is_null_ptr(exam_info_ptr)) imaging_modality=exam_info_ptr->imaging_modality; */ - if (exam_info. - imaging_modality.get_modality() != ImagingModality::Unknown) + if (exam_info.imaging_modality.get_modality() != ImagingModality::Unknown) output_header << "!imaging modality := " << exam_info.imaging_modality.get_name() << '\n'; } -static void interfile_create_filenames(const std::string& filename, std::string& data_name, std::string& header_name) -{ - data_name=filename; - string::size_type pos=find_pos_of_extension(filename); - if (pos!=string::npos && filename.substr(pos)==".hv") +static void +interfile_create_filenames(const std::string& filename, std::string& data_name, std::string& header_name) { + data_name = filename; + string::size_type pos = find_pos_of_extension(filename); + if (pos != string::npos && filename.substr(pos) == ".hv") replace_extension(data_name, ".v"); else add_extension(data_name, ".v"); - header_name=filename; + header_name = filename; replace_extension(header_name, ".hv"); } ////// end static functions -Succeeded -write_basic_interfile_image_header(const string& header_file_name, - const string& image_file_name, - const ExamInfo& exam_info, - const IndexRange<3>& index_range, - const CartesianCoordinate3D& voxel_size, - const CartesianCoordinate3D& origin, - const NumericType output_type, - const ByteOrder byte_order, - const VectorWithOffset& scaling_factors, - const VectorWithOffset& file_offsets, - const std::vector& data_type_descriptions) -{ +Succeeded +write_basic_interfile_image_header(const string& header_file_name, const string& image_file_name, const ExamInfo& exam_info, + const IndexRange<3>& index_range, const CartesianCoordinate3D& voxel_size, + const CartesianCoordinate3D& origin, const NumericType output_type, + const ByteOrder byte_order, const VectorWithOffset& scaling_factors, + const VectorWithOffset& file_offsets, + const std::vector& data_type_descriptions) { CartesianCoordinate3D min_indices; CartesianCoordinate3D max_indices; - if (!index_range.get_regular_range(min_indices, max_indices)) - { + if (!index_range.get_regular_range(min_indices, max_indices)) { warning("write_basic_interfile: can handle only regular index ranges\n. No output\n"); return Succeeded::no; } @@ -546,26 +469,23 @@ write_basic_interfile_image_header(const string& header_file_name, string header_name = header_file_name; add_extension(header_name, ".hv"); ofstream output_header(header_name.c_str(), ios::out); - if (!output_header.good()) - { - warning("Error opening Interfile header '%s' for writing\n", - header_name.c_str()); - return Succeeded::no; - } - + if (!output_header.good()) { + warning("Error opening Interfile header '%s' for writing\n", header_name.c_str()); + return Succeeded::no; + } + // handle directory names - const string data_file_name_in_header = - interfile_get_data_file_name_in_header(header_file_name, image_file_name); - + const string data_file_name_in_header = interfile_get_data_file_name_in_header(header_file_name, image_file_name); + output_header << "!INTERFILE :=\n"; const bool is_spect = exam_info.imaging_modality.get_modality() == ImagingModality::NM; if (!is_spect && exam_info.imaging_modality.get_modality() != ImagingModality::PT) - warning("Writing interfile header for a modality that is neither PET nor SPECT. This isn't really defined. There will be some PET keywords anyway."); + warning("Writing interfile header for a modality that is neither PET nor SPECT. This isn't really defined. There will be " + "some PET keywords anyway."); write_interfile_modality(output_header, exam_info); if (!exam_info.originating_system.empty()) - output_header << "originating system := " - << exam_info.originating_system << endl; + output_header << "originating system := " << exam_info.originating_system << endl; #if 0 //we don't have a conformant implementation of Interfile 3.3, even for SPECT @@ -581,113 +501,84 @@ write_basic_interfile_image_header(const string& header_file_name, output_header << "!GENERAL DATA :=\n"; write_interfile_patient_position(output_header, exam_info); output_header << "!GENERAL IMAGE DATA :=\n"; - if (exam_info.start_time_in_secs_since_1970>0) - { - const DateTimeStrings dt = - secs_since_Unix_epoch_to_Interfile_datetime(exam_info.start_time_in_secs_since_1970); - output_header << "study date := " << dt.date << '\n'; - output_header << "study time := " << dt.time << '\n'; - } + if (exam_info.start_time_in_secs_since_1970 > 0) { + const DateTimeStrings dt = secs_since_Unix_epoch_to_Interfile_datetime(exam_info.start_time_in_secs_since_1970); + output_header << "study date := " << dt.date << '\n'; + output_header << "study time := " << dt.time << '\n'; + } output_header << "!type of data := " << (is_spect ? "Tomographic" : "PET") << '\n'; - output_header << "imagedata byte order := " << - (byte_order == ByteOrder::little_endian - ? "LITTLEENDIAN" - : "BIGENDIAN") - << endl; - - if (exam_info.get_calibration_factor()>0.F) - output_header << "calibration factor := " - < 0.F) + output_header << "calibration factor := " << exam_info.get_calibration_factor() << endl; + if (!exam_info.get_radionuclide().empty()) - output_header << "isotope name := " - < 1300) template #else -#define elemT float +# define elemT float #endif -Succeeded -write_basic_interfile(const string& filename, - const Array<3,elemT>& image, - const NumericType output_type, - const float scale, - const ByteOrder byte_order) -{ +Succeeded +write_basic_interfile(const string& filename, const Array<3, elemT>& image, const NumericType output_type, const float scale, + const ByteOrder byte_order) { CartesianCoordinate3D origin; origin.fill(static_cast(InterfileHeader::double_value_not_set)); - return - write_basic_interfile(filename, - image, - CartesianCoordinate3D(1,1,1), - origin, - output_type, - scale, - byte_order); + return write_basic_interfile(filename, image, CartesianCoordinate3D(1, 1, 1), origin, output_type, scale, byte_order); } #if defined(_MSC_VER) && (_MSC_VER <= 1300) -#undef elemT +# undef elemT #endif template -Succeeded write_basic_interfile(const string& filename, - const ExamInfo& exam_info, - const Array<3,NUMBER>& image, - const CartesianCoordinate3D& voxel_size, - const CartesianCoordinate3D& origin, - const NumericType output_type, - const float scale, - const ByteOrder byte_order) -{ - std::string data_name, header_name; - interfile_create_filenames(filename, data_name, header_name); - - ofstream output_data; - open_write_binary(output_data, data_name.c_str()); - - float scale_to_use = scale; - write_data(output_data, image, output_type, scale_to_use, - byte_order); - VectorWithOffset scaling_factors(1); - scaling_factors.fill(scale_to_use); - VectorWithOffset file_offsets(1); - file_offsets.fill(0); - - const Succeeded success = - write_basic_interfile_image_header(header_name, - data_name, - exam_info, - image.get_index_range(), - voxel_size, - origin, - output_type, - byte_order, - scaling_factors, - file_offsets); - #if 0 +Succeeded +write_basic_interfile(const string& filename, const ExamInfo& exam_info, const Array<3, NUMBER>& image, + const CartesianCoordinate3D& voxel_size, const CartesianCoordinate3D& origin, + const NumericType output_type, const float scale, const ByteOrder byte_order) { + std::string data_name, header_name; + interfile_create_filenames(filename, data_name, header_name); + + ofstream output_data; + open_write_binary(output_data, data_name.c_str()); + + float scale_to_use = scale; + write_data(output_data, image, output_type, scale_to_use, byte_order); + VectorWithOffset scaling_factors(1); + scaling_factors.fill(scale_to_use); + VectorWithOffset file_offsets(1); + file_offsets.fill(0); + + const Succeeded success = + write_basic_interfile_image_header(header_name, data_name, exam_info, image.get_index_range(), voxel_size, origin, + output_type, byte_order, scaling_factors, file_offsets); +#if 0 delete[] header_name; delete[] data_name; - #endif - return success; +#endif + return success; } template -Succeeded write_basic_interfile(const string& filename, - const Array<3,NUMBER>& image, - const CartesianCoordinate3D& voxel_size, - const CartesianCoordinate3D& origin, - const NumericType output_type, - const float scale, - const ByteOrder byte_order) -{ +Succeeded +write_basic_interfile(const string& filename, const Array<3, NUMBER>& image, const CartesianCoordinate3D& voxel_size, + const CartesianCoordinate3D& origin, const NumericType output_type, const float scale, + const ByteOrder byte_order) { - return write_basic_interfile(filename, - ExamInfo(), - image, - voxel_size, - origin, - output_type, - scale, - byte_order); + return write_basic_interfile(filename, ExamInfo(), image, voxel_size, origin, output_type, scale, byte_order); } Succeeded -write_basic_interfile(const string& filename, - const VoxelsOnCartesianGrid& image, - const NumericType output_type, - const float scale, - const ByteOrder byte_order) -{ - return - write_basic_interfile(filename, - image.get_exam_info(), - image, // use automatic reference to base class - image.get_grid_spacing(), - image.get_origin(), - output_type, - scale, - byte_order); +write_basic_interfile(const string& filename, const VoxelsOnCartesianGrid& image, const NumericType output_type, + const float scale, const ByteOrder byte_order) { + return write_basic_interfile(filename, image.get_exam_info(), + image, // use automatic reference to base class + image.get_grid_spacing(), image.get_origin(), output_type, scale, byte_order); } -Succeeded -write_basic_interfile(const string& filename, - const DiscretisedDensity<3,float>& image, - const NumericType output_type, - const float scale, - const ByteOrder byte_order) -{ +Succeeded +write_basic_interfile(const string& filename, const DiscretisedDensity<3, float>& image, const NumericType output_type, + const float scale, const ByteOrder byte_order) { // dynamic_cast will throw an exception when it's not valid - return - write_basic_interfile(filename, - dynamic_cast& >(image), - output_type, - scale, byte_order); + return write_basic_interfile(filename, dynamic_cast&>(image), output_type, scale, + byte_order); } Succeeded -write_basic_interfile(const string& filename, - const ParametricVoxelsOnCartesianGrid &image, - const NumericType output_type, - const float scale, - const ByteOrder byte_order) -{ +write_basic_interfile(const string& filename, const ParametricVoxelsOnCartesianGrid& image, const NumericType output_type, + const float scale, const ByteOrder byte_order) { - std::string data_name, header_name; - interfile_create_filenames(filename, data_name, header_name); + std::string data_name, header_name; + interfile_create_filenames(filename, data_name, header_name); - ofstream output_data; - open_write_binary(output_data, data_name.c_str()); + ofstream output_data; + open_write_binary(output_data, data_name.c_str()); - VectorWithOffset file_offsets(image.get_num_params()); - VectorWithOffset scaling_factors(image.get_num_params()); - for (int i=1; i<=image.get_num_params(); i++) { - float scale_to_use = scale; - file_offsets[i-1] = output_data.tellp(); - write_data(output_data, image.construct_single_density(i), output_type, scale_to_use, - byte_order); - scaling_factors[i-1]=(scale_to_use); - } + VectorWithOffset file_offsets(image.get_num_params()); + VectorWithOffset scaling_factors(image.get_num_params()); + for (int i = 1; i <= image.get_num_params(); i++) { + float scale_to_use = scale; + file_offsets[i - 1] = output_data.tellp(); + write_data(output_data, image.construct_single_density(i), output_type, scale_to_use, byte_order); + scaling_factors[i - 1] = (scale_to_use); + } + + // Tell it what the different kinetic parameters mean + std::vector data_type_descriptions; + data_type_descriptions.push_back("slope"); + data_type_descriptions.push_back("intercept"); - // Tell it what the different kinetic parameters mean - std::vector data_type_descriptions; - data_type_descriptions.push_back("slope"); - data_type_descriptions.push_back("intercept"); - - const Succeeded success = - write_basic_interfile_image_header(header_name, - data_name, - image.get_exam_info(), - image.get_index_range(), - image.get_voxel_size(), - image.get_origin(), - output_type, - byte_order, - scaling_factors, - file_offsets, - data_type_descriptions); - #if 0 + const Succeeded success = write_basic_interfile_image_header( + header_name, data_name, image.get_exam_info(), image.get_index_range(), image.get_voxel_size(), image.get_origin(), + output_type, byte_order, scaling_factors, file_offsets, data_type_descriptions); +#if 0 delete[] header_name; delete[] data_name; - #endif - return success; +#endif + return success; } - Succeeded -write_basic_interfile(const string& filename, - const DynamicDiscretisedDensity &image, - const NumericType output_type, - const float scale, - const ByteOrder byte_order) -{ +write_basic_interfile(const string& filename, const DynamicDiscretisedDensity& image, const NumericType output_type, + const float scale, const ByteOrder byte_order) { - std::string data_name, header_name; - interfile_create_filenames(filename, data_name, header_name); + std::string data_name, header_name; + interfile_create_filenames(filename, data_name, header_name); - ofstream output_data; - open_write_binary(output_data, data_name.c_str()); + ofstream output_data; + open_write_binary(output_data, data_name.c_str()); - VectorWithOffset file_offsets(image.get_num_time_frames()); - VectorWithOffset scaling_factors(image.get_num_time_frames()); - for (int i=1; i<=image.get_num_time_frames(); i++) -{ - float scale_to_use = scale; - file_offsets[i-1] = output_data.tellp(); - write_data(output_data, image.get_density(i), output_type, scale_to_use, - byte_order); - scaling_factors[i-1]=(scale_to_use); -} + VectorWithOffset file_offsets(image.get_num_time_frames()); + VectorWithOffset scaling_factors(image.get_num_time_frames()); + for (int i = 1; i <= image.get_num_time_frames(); i++) { + float scale_to_use = scale; + file_offsets[i - 1] = output_data.tellp(); + write_data(output_data, image.get_density(i), output_type, scale_to_use, byte_order); + scaling_factors[i - 1] = (scale_to_use); + } - const Succeeded success = - write_basic_interfile_image_header(header_name, - data_name, - image.get_exam_info(), - image.get_density(1).get_index_range(), - dynamic_cast& >(image.get_density(1)).get_grid_spacing(), - image.get_density(1).get_origin(), - output_type, - byte_order, - scaling_factors, - file_offsets); - #if 0 + const Succeeded success = write_basic_interfile_image_header( + header_name, data_name, image.get_exam_info(), image.get_density(1).get_index_range(), + dynamic_cast&>(image.get_density(1)).get_grid_spacing(), + image.get_density(1).get_origin(), output_type, byte_order, scaling_factors, file_offsets); +#if 0 delete[] header_name; delete[] data_name; - #endif - return success; +#endif + return success; } -static ProjDataFromStream* -read_interfile_PDFS_SPECT(istream& input, - const string& directory_for_data, - const ios::openmode open_mode) -{ - - InterfilePDFSHeaderSPECT hdr; - if (!hdr.parse(input)) - { - return 0; // KT 10122001 do not call ask_parameters anymore - } +static ProjDataFromStream* +read_interfile_PDFS_SPECT(istream& input, const string& directory_for_data, const ios::openmode open_mode) { + + InterfilePDFSHeaderSPECT hdr; + if (!hdr.parse(input)) { + return 0; // KT 10122001 do not call ask_parameters anymore + } char full_data_file_name[max_filename_length]; strcpy(full_data_file_name, hdr.data_file_name.c_str()); - prepend_directory_name(full_data_file_name, directory_for_data.c_str()); - - vector segment_sequence(1); - segment_sequence[0]=0; - - for (unsigned int i=1; i data_in(new fstream (full_data_file_name, open_mode | ios::binary)); - if (!data_in->good()) - { - warning("interfile parsing: error opening file %s",full_data_file_name); - return 0; - } + vector segment_sequence(1); + segment_sequence[0] = 0; - return new ProjDataFromStream(hdr.get_exam_info_sptr(), - hdr.data_info_sptr, - data_in, - hdr.data_offset_each_dataset[0], - segment_sequence, - hdr.storage_order, - hdr.type_of_numbers, - hdr.file_byte_order, - static_cast(hdr.image_scaling_factors[0][0])); + for (unsigned int i = 1; i < hdr.image_scaling_factors[0].size(); i++) + if (hdr.image_scaling_factors[0][0] != hdr.image_scaling_factors[0][i]) { + error("Interfile error: all image scaling factors should be equal at the moment."); + } + assert(!is_null_ptr(hdr.data_info_sptr)); -} + shared_ptr data_in(new fstream(full_data_file_name, open_mode | ios::binary)); + if (!data_in->good()) { + warning("interfile parsing: error opening file %s", full_data_file_name); + return 0; + } + return new ProjDataFromStream(hdr.get_exam_info_sptr(), hdr.data_info_sptr, data_in, hdr.data_offset_each_dataset[0], + segment_sequence, hdr.storage_order, hdr.type_of_numbers, hdr.file_byte_order, + static_cast(hdr.image_scaling_factors[0][0])); +} ProjDataFromStream* -read_interfile_PDFS_Siemens(istream& input, - const string& directory_for_data, - const ios::openmode open_mode) -{ +read_interfile_PDFS_Siemens(istream& input, const string& directory_for_data, const ios::openmode open_mode) { InterfilePDFSHeaderSiemens hdr; - if (!hdr.parse(input)) - { - warning("Interfile parsing of Siemens Interfile projection data failed"); - return 0; - } + if (!hdr.parse(input)) { + warning("Interfile parsing of Siemens Interfile projection data failed"); + return 0; + } // KT 14/01/2000 added directory capability // prepend directory_for_data to the data_file_name from the header @@ -1054,137 +826,102 @@ read_interfile_PDFS_Siemens(istream& input, prepend_directory_name(full_data_file_name, directory_for_data.c_str()); shared_ptr data_in(new fstream(full_data_file_name, open_mode | ios::binary)); - if (!data_in->good()) - { + if (!data_in->good()) { warning("interfile parsing: error opening file %s", full_data_file_name); return 0; - } + } if (hdr.compression) warning("Siemens projection data is compressed. Reading of raw data will fail."); - return new ProjDataFromStream(hdr.get_exam_info_sptr(), - hdr.data_info_ptr->create_shared_clone(), - data_in, - hdr.data_offset_each_dataset[0], - hdr.segment_sequence, - hdr.storage_order, - hdr.type_of_numbers, - hdr.file_byte_order, - 1.); - + return new ProjDataFromStream(hdr.get_exam_info_sptr(), hdr.data_info_ptr->create_shared_clone(), data_in, + hdr.data_offset_each_dataset[0], hdr.segment_sequence, hdr.storage_order, hdr.type_of_numbers, + hdr.file_byte_order, 1.); } -ProjDataFromStream* -read_interfile_PDFS(istream& input, - const string& directory_for_data, - const ios::openmode open_mode) -{ - +ProjDataFromStream* +read_interfile_PDFS(istream& input, const string& directory_for_data, const ios::openmode open_mode) { + { - MinimalInterfileHeader hdr; + MinimalInterfileHeader hdr; std::ios::off_type offset = input.tellg(); if (!hdr.parse(input, false)) // parse without warnings - { - warning("Interfile parsing failed"); - return 0; - } + { + warning("Interfile parsing failed"); + return 0; + } input.clear(); // clear EOF or other flags before we proceed input.seekg(offset); - if (hdr.get_exam_info().imaging_modality.get_modality() == - ImagingModality::NM) - { - // spect data - return read_interfile_PDFS_SPECT(input, directory_for_data, open_mode); - } - if (!hdr.siemens_mi_version.empty()) - { - return read_interfile_PDFS_Siemens(input, directory_for_data, open_mode); - } + if (hdr.get_exam_info().imaging_modality.get_modality() == ImagingModality::NM) { + // spect data + return read_interfile_PDFS_SPECT(input, directory_for_data, open_mode); + } + if (!hdr.siemens_mi_version.empty()) { + return read_interfile_PDFS_Siemens(input, directory_for_data, open_mode); + } } - + // if we get here, it's PET - InterfilePDFSHeader hdr; - if (!hdr.parse(input)) - { - warning("Interfile parsing of PET projection data failed"); - return 0; - } + InterfilePDFSHeader hdr; + if (!hdr.parse(input)) { + warning("Interfile parsing of PET projection data failed"); + return 0; + } // KT 14/01/2000 added directory capability // prepend directory_for_data to the data_file_name from the header char full_data_file_name[max_filename_length]; strcpy(full_data_file_name, hdr.data_file_name.c_str()); - prepend_directory_name(full_data_file_name, directory_for_data.c_str()); - - for (unsigned int i=1; i data_in(new fstream (full_data_file_name, open_mode | ios::binary)); - if (!data_in->good()) - { - warning("interfile parsing: error opening file %s",full_data_file_name); - return 0; - } + for (unsigned int i = 1; i < hdr.image_scaling_factors[0].size(); i++) + if (hdr.image_scaling_factors[0][0] != hdr.image_scaling_factors[0][i]) { + warning("Interfile warning: all image scaling factors should be equal \n" + "at the moment. Using the first scale factor only.\n"); + break; + } - return new ProjDataFromStream(hdr.get_exam_info_sptr(), - hdr.data_info_sptr->create_shared_clone(), - data_in, - hdr.data_offset_each_dataset[0], - hdr.segment_sequence, - hdr.storage_order, - hdr.type_of_numbers, - hdr.file_byte_order, - static_cast(hdr.image_scaling_factors[0][0])); + assert(!is_null_ptr(hdr.data_info_sptr)); + shared_ptr data_in(new fstream(full_data_file_name, open_mode | ios::binary)); + if (!data_in->good()) { + warning("interfile parsing: error opening file %s", full_data_file_name); + return 0; + } + return new ProjDataFromStream(hdr.get_exam_info_sptr(), hdr.data_info_sptr->create_shared_clone(), data_in, + hdr.data_offset_each_dataset[0], hdr.segment_sequence, hdr.storage_order, hdr.type_of_numbers, + hdr.file_byte_order, static_cast(hdr.image_scaling_factors[0][0])); } - ProjDataFromStream* -read_interfile_PDFS(const string& filename, - const ios::openmode open_mode) -{ +read_interfile_PDFS(const string& filename, const ios::openmode open_mode) { ifstream image_stream(filename.c_str()); - if (!image_stream) - { - error("read_interfile_PDFS: couldn't open file %s\n", filename.c_str()); - } - + if (!image_stream) { + error("read_interfile_PDFS: couldn't open file %s\n", filename.c_str()); + } + char directory_name[max_filename_length]; get_directory_name(directory_name, filename.c_str()); - + return read_interfile_PDFS(image_stream, directory_name, open_mode); } -Succeeded -write_basic_interfile_PDFS_header(const string& header_file_name, - const string& data_file_name, - const ProjDataFromStream& pdfs) -{ +Succeeded +write_basic_interfile_PDFS_header(const string& header_file_name, const string& data_file_name, const ProjDataFromStream& pdfs) { string header_name = header_file_name; add_extension(header_name, ".hs"); ofstream output_header(header_name.c_str(), ios::out); - if (!output_header.good()) - { - warning("Error opening Interfile header '%s' for writing\n", - header_name.c_str()); - return Succeeded::no; - } + if (!output_header.good()) { + warning("Error opening Interfile header '%s' for writing\n", header_name.c_str()); + return Succeeded::no; + } // handle directory names - const string data_file_name_in_header = - interfile_get_data_file_name_in_header(header_file_name, data_file_name); + const string data_file_name_in_header = interfile_get_data_file_name_in_header(header_file_name, data_file_name); const vector segment_sequence = pdfs.get_segment_sequence_in_stream(); @@ -1193,12 +930,12 @@ write_basic_interfile_PDFS_header(const string& header_file_name, const float angle_first_view = pdfs.get_proj_data_info_sptr()->get_phi(Bin(0,0,0,0)) * float(180/_PI); #else - const float angle_first_view = - pdfs.get_proj_data_info_sptr()->get_scanner_ptr()->get_default_intrinsic_tilt() * float(180/_PI); + const float angle_first_view = + pdfs.get_proj_data_info_sptr()->get_scanner_ptr()->get_default_intrinsic_tilt() * float(180 / _PI); #endif - const float angle_increment = - (pdfs.get_proj_data_info_sptr()->get_phi(Bin(0,1,0,0)) - - pdfs.get_proj_data_info_sptr()->get_phi(Bin(0,0,0,0))) * float(180/_PI); + const float angle_increment = + (pdfs.get_proj_data_info_sptr()->get_phi(Bin(0, 1, 0, 0)) - pdfs.get_proj_data_info_sptr()->get_phi(Bin(0, 0, 0, 0))) * + float(180 / _PI); output_header << "!INTERFILE :=\n"; @@ -1209,7 +946,7 @@ write_basic_interfile_PDFS_header(const string& header_file_name, output_header << "name of data file := " << data_file_name_in_header << endl; output_header << "originating system := "; - output_header <get_scanner_ptr()->get_name() << endl; + output_header << pdfs.get_proj_data_info_sptr()->get_scanner_ptr()->get_name() << endl; if (is_spect) output_header << "!version of keys := 3.3\n"; @@ -1221,34 +958,27 @@ write_basic_interfile_PDFS_header(const string& header_file_name, output_header << "!type of data := " << (is_spect ? "Tomographic" : "PET") << '\n'; // output patient position - // note: strictly speaking this should come after "!SPECT STUDY (general)" but + // note: strictly speaking this should come after "!SPECT STUDY (general)" but // that's strange as these keys would be useful for all other cases as well write_interfile_patient_position(output_header, pdfs.get_exam_info()); - output_header << "imagedata byte order := " << - (pdfs.get_byte_order_in_stream() == ByteOrder::little_endian - ? "LITTLEENDIAN" - : "BIGENDIAN") - << endl; + output_header << "imagedata byte order := " + << (pdfs.get_byte_order_in_stream() == ByteOrder::little_endian ? "LITTLEENDIAN" : "BIGENDIAN") << endl; - - if (is_spect) - { - // output_header << "number of energy windows :=1\n"; - output_header << "!SPECT STUDY (General) :=\n"; - } - else + if (is_spect) { + // output_header << "number of energy windows :=1\n"; + output_header << "!SPECT STUDY (General) :=\n"; + } else { + output_header << "!PET STUDY (General) :=\n"; + output_header << "!PET data type := Emission\n"; { - output_header << "!PET STUDY (General) :=\n"; - output_header << "!PET data type := Emission\n"; - { - // KT 10/12/2001 write applied corrections keyword - if(!is_null_ptr(dynamic_pointer_cast (pdfs.get_proj_data_info_sptr()))) - output_header << "applied corrections := {arc correction}\n"; - else - output_header << "applied corrections := {None}\n"; - } + // KT 10/12/2001 write applied corrections keyword + if (!is_null_ptr(dynamic_pointer_cast(pdfs.get_proj_data_info_sptr()))) + output_header << "applied corrections := {arc correction}\n"; + else + output_header << "applied corrections := {None}\n"; } + } { string number_format; size_t size_in_bytes; @@ -1260,56 +990,44 @@ write_basic_interfile_PDFS_header(const string& header_file_name, output_header << "!number of bytes per pixel := " << size_in_bytes << "\n"; } - if (is_spect) - { - output_header << "!number of projections := " << pdfs.get_num_views() << '\n'; - output_header << "!extent of rotation := " << pdfs.get_num_views() * fabs(angle_increment) << '\n'; - output_header << "process status := acquired\n"; - output_header << "!SPECT STUDY (acquired data):=\n"; + if (is_spect) { + output_header << "!number of projections := " << pdfs.get_num_views() << '\n'; + output_header << "!extent of rotation := " << pdfs.get_num_views() * fabs(angle_increment) << '\n'; + output_header << "process status := acquired\n"; + output_header << "!SPECT STUDY (acquired data):=\n"; - output_header << "!direction of rotation := " - << (angle_increment>0 ? "CCW" : "CW") - << '\n'; - output_header << "start angle := " << angle_first_view << '\n'; + output_header << "!direction of rotation := " << (angle_increment > 0 ? "CCW" : "CW") << '\n'; + output_header << "start angle := " << angle_first_view << '\n'; - const shared_ptr proj_data_info_cyl_sptr = + const shared_ptr proj_data_info_cyl_sptr = dynamic_pointer_cast(pdfs.get_proj_data_info_sptr()); - VectorWithOffset ring_radii = proj_data_info_cyl_sptr->get_ring_radii_for_all_views(); - if (*std::min_element(ring_radii.begin(),ring_radii.end()) == - *std::max_element(ring_radii.begin(),ring_radii.end())) - { - output_header << "orbit := Circular\n"; - output_header << "Radius := " << *ring_radii.begin() << '\n'; - } - else - { - output_header << "orbit := Non-circular\n"; - output_header << "Radii := " << ring_radii << '\n'; - } - - output_header << "!matrix size [1] := " - <get_num_tangential_poss() << '\n'; - output_header << "!scaling factor (mm/pixel) [1] := " - <get_tangential_sampling() << '\n'; - output_header << "!matrix size [2] := " - <get_num_axial_poss(0) << '\n'; - output_header << "!scaling factor (mm/pixel) [2] := " - <get_axial_sampling(0) << '\n'; - - if (pdfs.get_offset_in_stream()) - output_header<<"data offset in bytes := " - < ring_radii = proj_data_info_cyl_sptr->get_ring_radii_for_all_views(); + if (*std::min_element(ring_radii.begin(), ring_radii.end()) == *std::max_element(ring_radii.begin(), ring_radii.end())) { + output_header << "orbit := Circular\n"; + output_header << "Radius := " << *ring_radii.begin() << '\n'; + } else { + output_header << "orbit := Non-circular\n"; + output_header << "Radii := " << ring_radii << '\n'; } + output_header << "!matrix size [1] := " << proj_data_info_cyl_sptr->get_num_tangential_poss() << '\n'; + output_header << "!scaling factor (mm/pixel) [1] := " << proj_data_info_cyl_sptr->get_tangential_sampling() << '\n'; + output_header << "!matrix size [2] := " << proj_data_info_cyl_sptr->get_num_axial_poss(0) << '\n'; + output_header << "!scaling factor (mm/pixel) [2] := " << proj_data_info_cyl_sptr->get_axial_sampling(0) << '\n'; + + if (pdfs.get_offset_in_stream()) + output_header << "data offset in bytes := " << pdfs.get_offset_in_stream() << '\n'; + ; + output_header << "!END OF INTERFILE :=\n"; + + return Succeeded::yes; + } + // 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"; + 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"; // TODO support more ? { @@ -1319,171 +1037,149 @@ write_basic_interfile_PDFS_header(const string& header_file_name, 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; - } - */ - { - case ProjDataFromStream::Segment_View_AxialPos_TangPos: + switch (pdfs.get_storage_order()) + /* + { + case ProjDataFromStream::ViewSegmentRingBin: + { + 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; } - case ProjDataFromStream::Segment_AxialPos_View_TangPos: - { + case ProjDataFromStream::Segment_AxialPos_View_TangPos: { 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; + 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: - { + default: { error("write_interfile_PSOV_header: unsupported storage order, " - "defaulting to Segment_View_AxialPos_TangPos.\n Please correct by hand !"); + "defaulting to Segment_View_AxialPos_TangPos.\n Please correct by hand !"); + } } - } - 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"; + 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_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"; - + output_header << "!matrix size [" << order_of_view << "] := " << pdfs.get_proj_data_info_sptr()->get_num_views() << "\n"; + output_header << "matrix axis label [" << order_of_z << "] := axial coordinate\n"; output_header << "!matrix size [" << order_of_z << "] := "; // tedious way to print a list of numbers { std::vector::const_iterator seg = segment_sequence.begin(); - output_header << "{ " <get_num_axial_poss(*seg); + output_header << "{ " << pdfs.get_proj_data_info_sptr()->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"; + 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) - { - // 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"; + pdfs.get_proj_data_info_sptr()->get_num_tof_poss() > 1) { + // 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"; } } const shared_ptr proj_data_info_sptr = - dynamic_pointer_cast(pdfs.get_proj_data_info_sptr()); + dynamic_pointer_cast(pdfs.get_proj_data_info_sptr()); - if (!is_null_ptr(proj_data_info_sptr)) - { - // cylindrical scanners - - 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); - for (seg++; seg != segment_sequence.end(); seg++) - output_header << "," <get_min_ring_difference(*seg); - output_header << "}\n"; - } - - output_header << "maximum ring difference per segment := "; - { - std::vector::const_iterator seg = segment_sequence.begin(); - output_header << "{ " <get_max_ring_difference(*seg); - for (seg++; seg != segment_sequence.end(); seg++) - output_header << "," <get_max_ring_difference(*seg); - output_header << "}\n"; - } - - const Scanner& scanner = *proj_data_info_sptr->get_scanner_ptr(); - if (fabs(proj_data_info_sptr->get_ring_radius()- - scanner.get_effective_ring_radius()) > .1) - warning("write_basic_interfile_PDFS_header: inconsistent effective ring radius:\n" - "\tproj_data_info has %g, scanner has %g.\n" - "\tThis really should not happen and signifies a bug.\n" - "\tYou will have a problem reading this data back in.", - proj_data_info_sptr->get_ring_radius(), - scanner.get_effective_ring_radius()); - if (fabs(proj_data_info_sptr->get_ring_spacing()- - scanner.get_ring_spacing()) > .1) - warning("write_basic_interfile_PDFS_header: inconsistent ring spacing:\n" - "\tproj_data_info has %g, scanner has %g.\n" - "\tThis really should not happen and signifies a bug.\n" - "\tYou will have a problem reading this data back in.", - proj_data_info_sptr->get_ring_spacing(), - scanner.get_ring_spacing()); - - output_header << scanner.parameter_info(); - - output_header << "effective central bin size (cm) := " - << proj_data_info_sptr->get_sampling_in_s(Bin(0,0,0,0))/10. << endl; - - } // end of cylindrical scanner - else + if (!is_null_ptr(proj_data_info_sptr)) { + // cylindrical scanners + + output_header << "minimum ring difference per segment := "; { - // TODO something here + std::vector::const_iterator seg = segment_sequence.begin(); + output_header << "{ " << proj_data_info_sptr->get_min_ring_difference(*seg); + for (seg++; seg != segment_sequence.end(); seg++) + output_header << "," << proj_data_info_sptr->get_min_ring_difference(*seg); + output_header << "}\n"; } + output_header << "maximum ring difference per segment := "; + { + std::vector::const_iterator seg = segment_sequence.begin(); + output_header << "{ " << proj_data_info_sptr->get_max_ring_difference(*seg); + for (seg++; seg != segment_sequence.end(); seg++) + output_header << "," << proj_data_info_sptr->get_max_ring_difference(*seg); + output_header << "}\n"; + } + + const Scanner& scanner = *proj_data_info_sptr->get_scanner_ptr(); + if (fabs(proj_data_info_sptr->get_ring_radius() - scanner.get_effective_ring_radius()) > .1) + warning("write_basic_interfile_PDFS_header: inconsistent effective ring radius:\n" + "\tproj_data_info has %g, scanner has %g.\n" + "\tThis really should not happen and signifies a bug.\n" + "\tYou will have a problem reading this data back in.", + proj_data_info_sptr->get_ring_radius(), scanner.get_effective_ring_radius()); + if (fabs(proj_data_info_sptr->get_ring_spacing() - scanner.get_ring_spacing()) > .1) + warning("write_basic_interfile_PDFS_header: inconsistent ring spacing:\n" + "\tproj_data_info has %g, scanner has %g.\n" + "\tThis really should not happen and signifies a bug.\n" + "\tYou will have a problem reading this data back in.", + proj_data_info_sptr->get_ring_spacing(), scanner.get_ring_spacing()); + + output_header << scanner.parameter_info(); + + output_header << "effective central bin size (cm) := " << proj_data_info_sptr->get_sampling_in_s(Bin(0, 0, 0, 0)) / 10. + << endl; + + } // end of cylindrical scanner + else { + // TODO something here + } - // write time frame info and energy windows - write_interfile_time_frame_definitions(output_header, pdfs.get_exam_info()); - write_interfile_energy_windows(output_header, pdfs.get_exam_info()); + // write time frame info and energy windows + write_interfile_time_frame_definitions(output_header, pdfs.get_exam_info()); + write_interfile_energy_windows(output_header, pdfs.get_exam_info()); - if (pdfs.get_scale_factor()!=1.F) - output_header <<"image scaling factor[1] := " - <(const string& filename, const Array<3, signed short>&, + const CartesianCoordinate3D& voxel_size, + const CartesianCoordinate3D& origin, const NumericType output_type, + const float scale, const ByteOrder byte_order); +template Succeeded write_basic_interfile<>(const string& filename, const Array<3, unsigned short>&, + const CartesianCoordinate3D& voxel_size, + const CartesianCoordinate3D& origin, const NumericType output_type, + const float scale, const ByteOrder byte_order); -template -Succeeded -write_basic_interfile<>(const string& filename, - const Array<3,signed short>&, - const CartesianCoordinate3D& voxel_size, - const CartesianCoordinate3D& origin, - const NumericType output_type, - const float scale, - const ByteOrder byte_order); -template -Succeeded -write_basic_interfile<>(const string& filename, - const Array<3,unsigned short>&, - const CartesianCoordinate3D& voxel_size, - const CartesianCoordinate3D& origin, - const NumericType output_type, - const float scale, - const ByteOrder byte_order); - -template -Succeeded -write_basic_interfile<>(const string& filename, - const Array<3,float>&, - const CartesianCoordinate3D& voxel_size, - const CartesianCoordinate3D& origin, - const NumericType output_type, - const float scale, - const ByteOrder byte_order); +template Succeeded write_basic_interfile<>(const string& filename, const Array<3, float>&, + const CartesianCoordinate3D& voxel_size, + const CartesianCoordinate3D& origin, const NumericType output_type, + const float scale, const ByteOrder byte_order); #if !defined(_MSC_VER) || (_MSC_VER > 1300) -template -Succeeded -write_basic_interfile<>(const string& filename, - const Array<3,signed short>& image, - const NumericType output_type, - const float scale, - const ByteOrder byte_order); - -template -Succeeded -write_basic_interfile<>(const string& filename, - const Array<3,unsigned short>& image, - const NumericType output_type, - const float scale, - const ByteOrder byte_order); - -template -Succeeded -write_basic_interfile<>(const string& filename, - const Array<3,float>& image, - const NumericType output_type, - const float scale, - const ByteOrder byte_order); +template Succeeded write_basic_interfile<>(const string& filename, const Array<3, signed short>& image, + const NumericType output_type, const float scale, const ByteOrder byte_order); + +template Succeeded write_basic_interfile<>(const string& filename, const Array<3, unsigned short>& image, + const NumericType output_type, const float scale, const ByteOrder byte_order); + +template Succeeded write_basic_interfile<>(const string& filename, const Array<3, float>& image, const NumericType output_type, + const float scale, const ByteOrder byte_order); #endif END_NAMESPACE_STIR diff --git a/src/IO/stir_AVW.cxx b/src/IO/stir_AVW.cxx index 685379ac29..3f7249cf6b 100644 --- a/src/IO/stir_AVW.cxx +++ b/src/IO/stir_AVW.cxx @@ -21,143 +21,104 @@ \file \ingroup IO \brief routines to convert AVW data structures to STIR -\author Kris Thielemans +\author Kris Thielemans */ #ifdef HAVE_AVW -#include "stir/IO/stir_AVW.h" -#include "stir/IndexRange3D.h" -#include "stir/VoxelsOnCartesianGrid.h" -#include "stir/CartesianCoordinate3D.h" - +# include "stir/IO/stir_AVW.h" +# include "stir/IndexRange3D.h" +# include "stir/VoxelsOnCartesianGrid.h" +# include "stir/CartesianCoordinate3D.h" START_NAMESPACE_STIR -namespace AVW -{ +namespace AVW { template -static -void -AVW_Volume_to_VoxelsOnCartesianGrid_help(VoxelsOnCartesianGrid& image, - elemT const* avw_data, - const bool flip_z) -{ +static void +AVW_Volume_to_VoxelsOnCartesianGrid_help(VoxelsOnCartesianGrid& image, elemT const* avw_data, const bool flip_z) { // std::copy(avw_data, avw_data+avw_volume->VoxelsPerVolume, image->begin_all()); - + // AVW data seems to be y-flipped - for (int z=image.get_min_z(); z<=image.get_max_z(); ++z) - { - const int out_z = - !flip_z ? z : image.get_max_z() - z + image.get_min_z(); - for (int y=image.get_max_y(); y>=image.get_min_y(); --y) - { - for (int x=image.get_min_x(); x<=image.get_max_x(); ++x) + for (int z = image.get_min_z(); z <= image.get_max_z(); ++z) { + const int out_z = !flip_z ? z : image.get_max_z() - z + image.get_min_z(); + for (int y = image.get_max_y(); y >= image.get_min_y(); --y) { + for (int x = image.get_min_x(); x <= image.get_max_x(); ++x) image[out_z][y][x] = static_cast(*avw_data++); - //std::copy(avw_data, avw_data + image.get_x_size(), image[z][y].begin()); - //avw_data += image.get_x_size(); + // std::copy(avw_data, avw_data + image.get_x_size(), image[z][y].begin()); + // avw_data += image.get_x_size(); } } } - -VoxelsOnCartesianGrid * -AVW_Volume_to_VoxelsOnCartesianGrid(AVW_Volume const* const avw_volume, - const bool flip_z) -{ - // find sizes et al +VoxelsOnCartesianGrid* +AVW_Volume_to_VoxelsOnCartesianGrid(AVW_Volume const* const avw_volume, const bool flip_z) { + // find sizes et al const int size_x = avw_volume->Width; const int size_y = avw_volume->Height; const int size_z = avw_volume->Depth; - IndexRange3D range(0, size_z-1, - -(size_y/2), -(size_y/2)+size_y-1, - -(size_x/2), -(size_x/2)+size_x-1); + IndexRange3D range(0, size_z - 1, -(size_y / 2), -(size_y / 2) + size_y - 1, -(size_x / 2), -(size_x / 2) + size_x - 1); CartesianCoordinate3D voxel_size; - voxel_size.x() = - static_cast(AVW_GetNumericInfo("VoxelWidth", avw_volume->Info)); - if (voxel_size.x()==0) - { + voxel_size.x() = static_cast(AVW_GetNumericInfo("VoxelWidth", avw_volume->Info)); + if (voxel_size.x() == 0) { warning("AVW_Volume_to_VoxelsOnCartesianGrid: VoxelWidth not found or 0"); } - - voxel_size.y() = - static_cast(AVW_GetNumericInfo("VoxelHeight", avw_volume->Info)); - if (voxel_size.y()==0) - { + + voxel_size.y() = static_cast(AVW_GetNumericInfo("VoxelHeight", avw_volume->Info)); + if (voxel_size.y() == 0) { warning("AVW_Volume_to_VoxelsOnCartesianGrid: VoxelHeight not found or 0"); } - - voxel_size.z() = - static_cast(AVW_GetNumericInfo("VoxelDepth", avw_volume->Info)); - if (voxel_size.z()==0) - { + + voxel_size.z() = static_cast(AVW_GetNumericInfo("VoxelDepth", avw_volume->Info)); + if (voxel_size.z() == 0) { warning("AVW_Volume_to_VoxelsOnCartesianGrid: VoxelDepth not found or 0"); } // construct VoxelsOnCartesianGrid - VoxelsOnCartesianGrid * volume_ptr = - new VoxelsOnCartesianGrid(range, - CartesianCoordinate3D(0,0,0), - voxel_size); - - // fill in data - switch(avw_volume->DataType) - { - case AVW_SIGNED_CHAR: - { - AVW_Volume_to_VoxelsOnCartesianGrid_help(*volume_ptr, - reinterpret_cast(avw_volume->Mem), flip_z); - break; - } - case AVW_UNSIGNED_CHAR: - { - AVW_Volume_to_VoxelsOnCartesianGrid_help(*volume_ptr, - reinterpret_cast(avw_volume->Mem), flip_z); - break; - } - case AVW_UNSIGNED_SHORT: - { - AVW_Volume_to_VoxelsOnCartesianGrid_help(*volume_ptr, - reinterpret_cast(avw_volume->Mem), flip_z); - break; - } - case AVW_SIGNED_SHORT: - { - AVW_Volume_to_VoxelsOnCartesianGrid_help(*volume_ptr, - reinterpret_cast(avw_volume->Mem), flip_z); - break; - } - case AVW_UNSIGNED_INT: - { - AVW_Volume_to_VoxelsOnCartesianGrid_help(*volume_ptr, - reinterpret_cast(avw_volume->Mem), flip_z); - break; - } - case AVW_SIGNED_INT: - { - AVW_Volume_to_VoxelsOnCartesianGrid_help(*volume_ptr, - reinterpret_cast(avw_volume->Mem), flip_z); - break; - } - case AVW_FLOAT: - { - AVW_Volume_to_VoxelsOnCartesianGrid_help(*volume_ptr, - reinterpret_cast(avw_volume->Mem), flip_z); - break; - } - default: - { - warning("AVW_Volume_to_VoxelsOnCartesianGrid: unsupported data type %d\n", - avw_volume->DataType); - delete volume_ptr; - return 0; - } + VoxelsOnCartesianGrid* volume_ptr = + new VoxelsOnCartesianGrid(range, CartesianCoordinate3D(0, 0, 0), voxel_size); + + // fill in data + switch (avw_volume->DataType) { + case AVW_SIGNED_CHAR: { + AVW_Volume_to_VoxelsOnCartesianGrid_help(*volume_ptr, reinterpret_cast(avw_volume->Mem), flip_z); + break; + } + case AVW_UNSIGNED_CHAR: { + AVW_Volume_to_VoxelsOnCartesianGrid_help(*volume_ptr, reinterpret_cast(avw_volume->Mem), flip_z); + break; + } + case AVW_UNSIGNED_SHORT: { + AVW_Volume_to_VoxelsOnCartesianGrid_help(*volume_ptr, reinterpret_cast(avw_volume->Mem), flip_z); + break; + } + case AVW_SIGNED_SHORT: { + AVW_Volume_to_VoxelsOnCartesianGrid_help(*volume_ptr, reinterpret_cast(avw_volume->Mem), flip_z); + break; + } + case AVW_UNSIGNED_INT: { + AVW_Volume_to_VoxelsOnCartesianGrid_help(*volume_ptr, reinterpret_cast(avw_volume->Mem), flip_z); + break; } - + case AVW_SIGNED_INT: { + AVW_Volume_to_VoxelsOnCartesianGrid_help(*volume_ptr, reinterpret_cast(avw_volume->Mem), flip_z); + break; + } + case AVW_FLOAT: { + AVW_Volume_to_VoxelsOnCartesianGrid_help(*volume_ptr, reinterpret_cast(avw_volume->Mem), flip_z); + break; + } + default: { + warning("AVW_Volume_to_VoxelsOnCartesianGrid: unsupported data type %d\n", avw_volume->DataType); + delete volume_ptr; + return 0; + } + } + return volume_ptr; } diff --git a/src/IO/stir_ecat6.cxx b/src/IO/stir_ecat6.cxx index 4d0daf489f..47d5c5489d 100644 --- a/src/IO/stir_ecat6.cxx +++ b/src/IO/stir_ecat6.cxx @@ -2,7 +2,7 @@ \file \ingroup ECAT - \brief Implementation of routines which convert CTI things into our + \brief Implementation of routines which convert CTI things into our building blocks and vice versa. \author Kris Thielemans @@ -27,7 +27,6 @@ See STIR/LICENSE.txt for details */ - #include "stir/IO/interfile.h" #include "stir/ExamInfo.h" #include "stir/Sinogram.h" @@ -43,17 +42,17 @@ #include "stir/IndexRange2D.h" #include "stir/utilities.h" -#include "stir/Scanner.h" +#include "stir/Scanner.h" #include "stir/ExamInfo.h" #include "stir/IO/ecat6_utils.h" #include "stir/IO/stir_ecat6.h" #ifndef STIR_ORIGINAL_ECAT6 -#include "stir/IO/stir_ecat7.h" +# include "stir/IO/stir_ecat7.h" #endif -#include "boost/cstdint.hpp" -#include "boost/static_assert.hpp" -#include "boost/scoped_array.hpp" +#include "boost/cstdint.hpp" +#include "boost/static_assert.hpp" +#include "boost/scoped_array.hpp" #include #include #include @@ -70,9 +69,7 @@ START_NAMESPACE_STIR START_NAMESPACE_ECAT START_NAMESPACE_ECAT6 -static void cti_data_to_float_Array(Array<2,float>&out, - char const * const buffer, const float scale_factor, int dtype); - +static void cti_data_to_float_Array(Array<2, float>& out, char const* const buffer, const float scale_factor, int dtype); /* \brief reads data from a CTI file into a Sinogram object @@ -82,205 +79,169 @@ static void cti_data_to_float_Array(Array<2,float>&out, \warning \a buffer has to be allocated with a size at least as large as the multiple of MatBLKSIZE that fits the whole sinogram (because it uses cti_wblk). */ -static void read_sinogram(Sinogram& sino_2D, - char *buffer, - FILE*fptr, - int mat_index, - int frame, int gate, int data, int bed); - -static bool is_ECAT6_file(ECAT6_Main_header& mhead, const std::string& filename) -{ - //check if it's ECAT 6 - FILE * cti_fptr=fopen(filename.c_str(), "rb"); - - if(!cti_fptr) +static void read_sinogram(Sinogram& sino_2D, char* buffer, FILE* fptr, int mat_index, int frame, int gate, int data, + int bed); + +static bool +is_ECAT6_file(ECAT6_Main_header& mhead, const std::string& filename) { + // check if it's ECAT 6 + FILE* cti_fptr = fopen(filename.c_str(), "rb"); + + if (!cti_fptr) return false; // we first need to check if the file size is large enough, due to a bug // in the LLN MATRIX library (it will only read a buffer large enough // for the data, but use 512 bytes of that buffer anyway). // we suppose that any sensible file will be larger than 2048+512 bytes - // Because of some undefined behaviour of fseek and ftell when you try to + // Because of some undefined behaviour of fseek and ftell when you try to // beyond the file size, our tests is 3 staged: // fseek, ftell, fread. All of these should work. { - int ret= fseek(cti_fptr, 2048L, SEEK_SET); - long pos=ftell(cti_fptr); - if (ret || pos != 2048L) - { - // not enough bytes, so not ECAT6 - fclose(cti_fptr); - return false; - } - - char buffer[512]; - ret=fread(buffer,1, 512, cti_fptr); - if (ret != 512) - { - // failure to read - fclose(cti_fptr); - return false; - } - } - // seek back to start - fseek(cti_fptr, 0L, SEEK_SET); - - if(cti_read_ECAT6_Main_header(cti_fptr, &mhead)!=EXIT_SUCCESS) - { - // this is funny as it's just reading a bunch of bytes. anyway. we'll assume it isn't ECAT6 + int ret = fseek(cti_fptr, 2048L, SEEK_SET); + long pos = ftell(cti_fptr); + if (ret || pos != 2048L) { + // not enough bytes, so not ECAT6 fclose(cti_fptr); return false; } - else - { + + char buffer[512]; + ret = fread(buffer, 1, 512, cti_fptr); + if (ret != 512) { + // failure to read fclose(cti_fptr); - // do some checks on the main header - return - mhead.sw_version>=0 && mhead.sw_version<=69 && - ( mhead.file_type == matScanFile || - mhead.file_type == matImageFile || - mhead.file_type == matAttenFile || - mhead.file_type == matNormFile) && - mhead.num_frames>0; + return false; } + } + // seek back to start + fseek(cti_fptr, 0L, SEEK_SET); + + if (cti_read_ECAT6_Main_header(cti_fptr, &mhead) != EXIT_SUCCESS) { + // this is funny as it's just reading a bunch of bytes. anyway. we'll assume it isn't ECAT6 + fclose(cti_fptr); + return false; + } else { + fclose(cti_fptr); + // do some checks on the main header + return mhead.sw_version >= 0 && mhead.sw_version <= 69 && + (mhead.file_type == matScanFile || mhead.file_type == matImageFile || mhead.file_type == matAttenFile || + mhead.file_type == matNormFile) && + mhead.num_frames > 0; + } } - -bool is_ECAT6_file(const std::string& filename) -{ +bool +is_ECAT6_file(const std::string& filename) { ECAT6_Main_header mhead; return is_ECAT6_file(mhead, filename); } -bool is_ECAT6_image_file(const std::string& filename) -{ +bool +is_ECAT6_image_file(const std::string& filename) { ECAT6_Main_header mhead; - return is_ECAT6_file(mhead, filename) && - mhead.file_type ==matImageFile; + return is_ECAT6_file(mhead, filename) && mhead.file_type == matImageFile; } - -bool is_ECAT6_emission_file(const std::string& filename) -{ +bool +is_ECAT6_emission_file(const std::string& filename) { ECAT6_Main_header mhead; - return is_ECAT6_file(mhead, filename) && - mhead.file_type ==matScanFile; + return is_ECAT6_file(mhead, filename) && mhead.file_type == matScanFile; } - -bool is_ECAT6_attenuation_file(const std::string& filename) -{ +bool +is_ECAT6_attenuation_file(const std::string& filename) { ECAT6_Main_header mhead; - return is_ECAT6_file(mhead, filename) && - mhead.file_type ==matAttenFile; + return is_ECAT6_file(mhead, filename) && mhead.file_type == matAttenFile; } - - -Scanner* find_scanner_from_ECAT6_Main_header(const ECAT6_Main_header& mhead) -{ +Scanner* +find_scanner_from_ECAT6_Main_header(const ECAT6_Main_header& mhead) { // we could do more effort here by checking some values of other fields than system_type. // TODO - - Scanner * scanner_ptr = - find_scanner_from_ECAT_system_type(mhead.system_type); + Scanner* scanner_ptr = find_scanner_from_ECAT_system_type(mhead.system_type); return scanner_ptr; } -void make_ECAT6_Main_header(ECAT6_Main_header& mhead, - Scanner const& scanner, - const std::string& orig_name, - ExamInfo const& exam_info - ) -{ +void +make_ECAT6_Main_header(ECAT6_Main_header& mhead, Scanner const& scanner, const std::string& orig_name, + ExamInfo const& exam_info) { #ifndef STIR_ORIGINAL_ECAT6 ecat::ecat7::make_ECAT7_main_header(mhead, scanner, orig_name, exam_info); strcpy(mhead.magic_number, "MATRIX6.4"); - mhead.sw_version= 64; + mhead.sw_version = 64; #else warning("Exam_info currently ignored when creating an ECAT6 file"); - mhead= main_zero_fill(); + mhead = main_zero_fill(); mhead.calibration_factor = 1.F; - + // other header parameters - + strncpy(mhead.original_file_name, orig_name.c_str(), 20); - mhead.num_frames= 1; //hdr.num_time_frames; + mhead.num_frames = 1; // hdr.num_time_frames; // cti_utils routines always write data as VAX short - mhead.data_type= ECAT_I2_little_endian_data_type; - - mhead.system_type= find_ECAT_system_type(scanner); - mhead.axial_fov= scanner.get_num_rings()*scanner.get_ring_spacing()/10; - mhead.transaxial_fov= scanner.get_default_num_arccorrected_bins()*scanner.get_default_bin_size()/10; - - mhead.plane_separation= scanner.get_ring_spacing()/2/10; - //WRONG mhead.gantry_tilt= scanner.get_default_intrinsic_tilt(); + mhead.data_type = ECAT_I2_little_endian_data_type; + + mhead.system_type = find_ECAT_system_type(scanner); + mhead.axial_fov = scanner.get_num_rings() * scanner.get_ring_spacing() / 10; + mhead.transaxial_fov = scanner.get_default_num_arccorrected_bins() * scanner.get_default_bin_size() / 10; + + mhead.plane_separation = scanner.get_ring_spacing() / 2 / 10; + // WRONG mhead.gantry_tilt= scanner.get_default_intrinsic_tilt(); #endif // STIR_ORIGINAL_ECAT6 } -void make_ECAT6_Main_header(ECAT6_Main_header& mhead, - Scanner const& scanner, - const std::string& orig_name, - DiscretisedDensity<3,float> const & density - ) -{ +void +make_ECAT6_Main_header(ECAT6_Main_header& mhead, Scanner const& scanner, const std::string& orig_name, + DiscretisedDensity<3, float> const& density) { make_ECAT6_Main_header(mhead, scanner, orig_name, density.get_exam_info()); - - DiscretisedDensityOnCartesianGrid<3,float> const & image = - dynamic_cast const&>(density); - + DiscretisedDensityOnCartesianGrid<3, float> const& image = + dynamic_cast const&>(density); + // extra main parameters that depend on data type - mhead.file_type= matImageFile; - mhead.num_planes=image.get_length(); - mhead.plane_separation=image.get_grid_spacing()[1]/10; // convert to cm + mhead.file_type = matImageFile; + mhead.num_planes = image.get_length(); + mhead.plane_separation = image.get_grid_spacing()[1] / 10; // convert to cm } -void make_ECAT6_Main_header(ECAT6_Main_header& mhead, - const std::string& orig_name, - ProjDataInfo const & proj_data_info - ) -{ +void +make_ECAT6_Main_header(ECAT6_Main_header& mhead, const std::string& orig_name, ProjDataInfo const& proj_data_info) { warning("Exam_info currently ignored when creating an ECAT6 raw-data file"); ExamInfo dummy_exam_info; make_ECAT6_Main_header(mhead, *proj_data_info.get_scanner_sptr(), orig_name, dummy_exam_info); - + // extra main parameters that depend on data type - mhead.file_type= matScanFile; - + mhead.file_type = matScanFile; + mhead.num_planes = 0; - for(int segment_num=proj_data_info.get_min_segment_num(); - segment_num <= proj_data_info.get_max_segment_num(); - ++segment_num) - mhead.num_planes+= proj_data_info.get_num_axial_poss(segment_num); - - mhead.plane_separation=proj_data_info.get_scanner_sptr()->get_ring_spacing()/10/2; + for (int segment_num = proj_data_info.get_min_segment_num(); segment_num <= proj_data_info.get_max_segment_num(); ++segment_num) + mhead.num_planes += proj_data_info.get_num_axial_poss(segment_num); + + mhead.plane_separation = proj_data_info.get_scanner_sptr()->get_ring_spacing() / 10 / 2; } -VoxelsOnCartesianGrid * -ECAT6_to_VoxelsOnCartesianGrid(const int frame_num, const int gate_num, const int data_num, const int bed_num, - FILE *cti_fptr, const ECAT6_Main_header & mhead) -{ +VoxelsOnCartesianGrid* +ECAT6_to_VoxelsOnCartesianGrid(const int frame_num, const int gate_num, const int data_num, const int bed_num, FILE* cti_fptr, + const ECAT6_Main_header& mhead) { MatDir entry; Image_subheader ihead; + VoxelsOnCartesianGrid* image_ptr = 0; - VoxelsOnCartesianGrid * image_ptr = 0; - // read first subheader to find dimensions { - long matnum = cti_numcod(frame_num, 1,gate_num, data_num, bed_num); - - if(!cti_lookup(cti_fptr, &mhead, matnum, &entry)) { // get entry - error("\nCouldn't find matnum %d in specified file.\n", matnum); + long matnum = cti_numcod(frame_num, 1, gate_num, data_num, bed_num); + + if (!cti_lookup(cti_fptr, &mhead, matnum, &entry)) { // get entry + error("\nCouldn't find matnum %d in specified file.\n", matnum); + } + if (cti_read_image_subheader(cti_fptr, &mhead, entry.strtblk, &ihead) != EXIT_SUCCESS) { + error("\nUnable to look up image subheader\n"); } - if(cti_read_image_subheader(cti_fptr, &mhead, entry.strtblk, &ihead)!=EXIT_SUCCESS) - { - error("\nUnable to look up image subheader\n"); - } } - + #ifndef STIR_ORIGINAL_ECAT6 const int x_size = ihead.x_dimension; const int y_size = ihead.y_dimension; @@ -289,71 +250,63 @@ ECAT6_to_VoxelsOnCartesianGrid(const int frame_num, const int gate_num, const in const int y_size = ihead.dimension_2; #endif const int z_size = mhead.num_planes; - const int min_z = 0; - - IndexRange3D range_3D (0,z_size-1, - -y_size/2,(-y_size/2)+y_size-1, - -x_size/2,(-x_size/2)+x_size-1); - + const int min_z = 0; + + IndexRange3D range_3D(0, z_size - 1, -y_size / 2, (-y_size / 2) + y_size - 1, -x_size / 2, (-x_size / 2) + x_size - 1); + #ifndef STIR_ORIGINAL_ECAT6 - CartesianCoordinate3D - voxel_size(ihead.z_pixel_size*10,ihead.y_pixel_size*10,ihead.x_pixel_size*10); - CartesianCoordinate3D - origin(ihead.z_offset, ihead.y_offset*10, ihead.x_offset*10); + CartesianCoordinate3D voxel_size(ihead.z_pixel_size * 10, ihead.y_pixel_size * 10, ihead.x_pixel_size * 10); + CartesianCoordinate3D origin(ihead.z_offset, ihead.y_offset * 10, ihead.x_offset * 10); #else - CartesianCoordinate3D - voxel_size(ihead.slice_width*10,ihead.pixel_size*10,ihead.pixel_size*10); - CartesianCoordinate3D - origin(0, ihead.y_origin*10, ihead.x_origin*10); + CartesianCoordinate3D voxel_size(ihead.slice_width * 10, ihead.pixel_size * 10, ihead.pixel_size * 10); + CartesianCoordinate3D origin(0, ihead.y_origin * 10, ihead.x_origin * 10); #endif - - - image_ptr = new VoxelsOnCartesianGrid (range_3D, origin, voxel_size); - + + image_ptr = new VoxelsOnCartesianGrid(range_3D, origin, voxel_size); + NumericType type; ByteOrder byte_order; find_type_from_ECAT_data_type(type, byte_order, ihead.data_type); - // allocation for buffer. Provide enough space for a multiple of MatBLKSIZE - const size_t cti_data_size = x_size*y_size*type.size_in_bytes()+ MatBLKSIZE; - char * cti_data= new char[cti_data_size]; - - for(int z=0; z sub_head_origin(ihead.z_offset, ihead.y_offset*10, ihead.x_offset*10); -#else // STIR_ORIGINAL_ECAT6 - const CartesianCoordinate3D sub_head_origin(0, ihead.y_origin*10, ihead.x_origin*10); + const CartesianCoordinate3D sub_head_origin(ihead.z_offset, ihead.y_offset * 10, ihead.x_offset * 10); +#else // STIR_ORIGINAL_ECAT6 + const CartesianCoordinate3D sub_head_origin(0, ihead.y_origin * 10, ihead.x_origin * 10); #endif // STIR_ORIGINAL_ECAT6 - { - if (norm(image_ptr->get_origin() - sub_head_origin) > .01F) - { - warning("ECAT6_to_VoxelsOnCartesianGrid: x,y offset in subheader of plane %d does not agree with plane 1. Ignoring it...\n", - z+1); - } + { + if (norm(image_ptr->get_origin() - sub_head_origin) > .01F) { + warning( + "ECAT6_to_VoxelsOnCartesianGrid: x,y offset in subheader of plane %d does not agree with plane 1. Ignoring it...\n", + z + 1); } + } #ifndef STIR_ORIGINAL_ECAT6 - float scale_factor = ihead.scale_factor; + float scale_factor = ihead.scale_factor; #else // STIR_ORIGINAL_ECAT6 - float scale_factor = ihead.quant_scale; + float scale_factor = ihead.quant_scale; #endif - if(cti_rblk (cti_fptr, entry.strtblk+1, cti_data, entry.endblk-entry.strtblk)!=EXIT_SUCCESS) { // get data - error("\nUnable to read data\n"); - } - if (file_data_to_host(cti_data, entry.endblk-entry.strtblk, ihead.data_type)!=EXIT_SUCCESS) - error("\nerror converting to host data format\n"); - cti_data_to_float_Array((*image_ptr)[z+min_z],cti_data, scale_factor, ihead.data_type); + if (cti_rblk(cti_fptr, entry.strtblk + 1, cti_data, entry.endblk - entry.strtblk) != EXIT_SUCCESS) { // get data + error("\nUnable to read data\n"); + } + if (file_data_to_host(cti_data, entry.endblk - entry.strtblk, ihead.data_type) != EXIT_SUCCESS) + error("\nerror converting to host data format\n"); + cti_data_to_float_Array((*image_ptr)[z + min_z], cti_data, scale_factor, ihead.data_type); #if 0 NumericType type; ByteOrder byte_order; @@ -365,134 +318,112 @@ ECAT6_to_VoxelsOnCartesianGrid(const int frame_num, const int gate_num, const in (*image_ptr)[z+min_z][y+min_y][x+min_x]=scale_factor*cti_data[y*x_size+x]; } #endif - } // end loop on planes - + } // end loop on planes + delete[] cti_data; return image_ptr; } -void ECAT6_to_PDFS(const int frame_num, const int gate_num, const int data_num, const int bed_num, - int max_ring_diff, bool arccorrected, - const std::string& data_name, FILE *cti_fptr, const ECAT6_Main_header &mhead) -{ +void +ECAT6_to_PDFS(const int frame_num, const int gate_num, const int data_num, const int bed_num, int max_ring_diff, + bool arccorrected, const std::string& data_name, FILE* cti_fptr, const ECAT6_Main_header& mhead) { shared_ptr scanner_ptr(find_scanner_from_ECAT6_Main_header(mhead)); cout << "Scanner determined from ECAT6_Main_header: " << scanner_ptr->get_name() << endl; - if (scanner_ptr->get_type() == Scanner::Unknown_scanner || - scanner_ptr->get_type() == Scanner::User_defined_scanner) - { + if (scanner_ptr->get_type() == Scanner::Unknown_scanner || scanner_ptr->get_type() == Scanner::User_defined_scanner) { warning("ECAT6_to_PDFS: Couldn't determine the scanner \n" - "(Main_header.system_type=%d), defaulting to 953.\n" - "This will give dramatic problems when the number of rings of your scanner is NOT 16.\n", - mhead.system_type); + "(Main_header.system_type=%d), defaulting to 953.\n" + "This will give dramatic problems when the number of rings of your scanner is NOT 16.\n", + mhead.system_type); scanner_ptr.reset(new Scanner(Scanner::E953)); } - const int num_rings = scanner_ptr->get_num_rings(); // ECAT 6 does not have a flag for 3D vs. 2D, so we guess it first from num_planes - bool is_3D_file = (mhead.num_planes > 2*num_rings-1); - if (!is_3D_file) - { + bool is_3D_file = (mhead.num_planes > 2 * num_rings - 1); + if (!is_3D_file) { // better make sure by checking if plane (5,5) is not in its '3D' place MatDir entry; - const int mat_index= cti_rings2plane(num_rings, 5,5); - const long matnum= cti_numcod(frame_num, mat_index,gate_num, data_num, bed_num); + const int mat_index = cti_rings2plane(num_rings, 5, 5); + const long matnum = cti_numcod(frame_num, mat_index, gate_num, data_num, bed_num); // KT 18/08/2000 add !=0 to prevent compiler warning on conversion from int to bool - is_3D_file = cti_lookup (cti_fptr, &mhead, matnum, &entry)!=0; + is_3D_file = cti_lookup(cti_fptr, &mhead, matnum, &entry) != 0; } int span = 1; - if (!is_3D_file) - { + if (!is_3D_file) { warning("I'm guessing this is a stack of 2D sinograms\n"); - if(mhead.num_planes == 2*num_rings-1) - { - span=3; + if (mhead.num_planes == 2 * num_rings - 1) { + span = 3; max_ring_diff = 1; - } - else if (mhead.num_planes == num_rings) - { - span=1; + } else if (mhead.num_planes == num_rings) { + span = 1; max_ring_diff = 0; - } - else - { + } else { error("Impossible num_planes: %d\n", mhead.num_planes); } - } - else - { - if (max_ring_diff < 0) - max_ring_diff = num_rings-1; - const int num_sinos = - (2*max_ring_diff+1) * num_rings - (max_ring_diff+1)*max_ring_diff; - + } else { + if (max_ring_diff < 0) + max_ring_diff = num_rings - 1; + const int num_sinos = (2 * max_ring_diff + 1) * num_rings - (max_ring_diff + 1) * max_ring_diff; + if (num_sinos > mhead.num_planes) - warning("\n\aWarning: header says not enough planes in the file: %d (expected %d)." - "Continuing anyway...", mhead.num_planes, num_sinos); + warning("\n\aWarning: header says not enough planes in the file: %d (expected %d)." + "Continuing anyway...", + mhead.num_planes, num_sinos); } - + // construct a ProjDataFromStream object - shared_ptr proj_data; + shared_ptr proj_data; ScanInfoRec scanParams; - { + { // read first subheader for dimensions - { + { // use temporary copy to avoid overwriting mhead argument ECAT6_Main_header mhead_copy; - long matnum= cti_numcod(frame_num, 1,gate_num, data_num, bed_num); - switch(mhead.file_type) - { - case matScanFile: - { - Scan_subheader shead; - if (get_scanheaders(cti_fptr, matnum, &mhead_copy, &shead, &scanParams)!= EXIT_SUCCESS) - error("Error reading matnum %d\n", matnum); - break; - } - case matAttenFile: - { - Attn_subheader shead; - if(get_attnheaders (cti_fptr, matnum, &mhead_copy, - &shead, &scanParams)!= EXIT_SUCCESS) - error("Error reading matnum %d\n", matnum); - break; - } - case matNormFile: - { - Norm_subheader shead; - if(get_normheaders (cti_fptr, matnum, &mhead_copy, - &shead, &scanParams)!= EXIT_SUCCESS) - error("Error reading matnum %d\n", matnum); - break; - } - + long matnum = cti_numcod(frame_num, 1, gate_num, data_num, bed_num); + switch (mhead.file_type) { + case matScanFile: { + Scan_subheader shead; + if (get_scanheaders(cti_fptr, matnum, &mhead_copy, &shead, &scanParams) != EXIT_SUCCESS) + error("Error reading matnum %d\n", matnum); + break; + } + case matAttenFile: { + Attn_subheader shead; + if (get_attnheaders(cti_fptr, matnum, &mhead_copy, &shead, &scanParams) != EXIT_SUCCESS) + error("Error reading matnum %d\n", matnum); + break; + } + case matNormFile: { + Norm_subheader shead; + if (get_normheaders(cti_fptr, matnum, &mhead_copy, &shead, &scanParams) != EXIT_SUCCESS) + error("Error reading matnum %d\n", matnum); + break; + } + default: - error("ECAT6_to_PDFS: unsupported file type %d\n",mhead.file_type); + error("ECAT6_to_PDFS: unsupported file type %d\n", mhead.file_type); } } const int num_views = scanParams.nviews; - const int num_tangential_poss = scanParams.nprojs; - - + const int num_tangential_poss = scanParams.nprojs; + shared_ptr p_data_info( - ProjDataInfo::ProjDataInfoCTI(scanner_ptr,span,max_ring_diff,num_views,num_tangential_poss,arccorrected)); - - - ProjDataFromStream::StorageOrder storage_order= - ProjDataFromStream::Segment_AxialPos_View_TangPos; - + ProjDataInfo::ProjDataInfoCTI(scanner_ptr, span, max_ring_diff, num_views, num_tangential_poss, arccorrected)); + + ProjDataFromStream::StorageOrder storage_order = ProjDataFromStream::Segment_AxialPos_View_TangPos; + #if 1 - std::string actual_data_name=data_name; + std::string actual_data_name = data_name; { - std::string::size_type pos=find_pos_of_extension(data_name); - if (pos!=std::string::npos && data_name.substr(pos)==".hs") - replace_extension(actual_data_name, ".s"); + std::string::size_type pos = find_pos_of_extension(data_name); + if (pos != std::string::npos && data_name.substr(pos) == ".hs") + replace_extension(actual_data_name, ".s"); else - add_extension(actual_data_name, ".s"); + add_extension(actual_data_name, ".s"); } #else // TODO replace these char* things with string based extension stuff @@ -500,582 +431,481 @@ void ECAT6_to_PDFS(const int frame_num, const int gate_num, const int data_num, strcpy(actual_data_name.get(), data_name.c_str()); // KT 30/05/2002 make sure that a filename ending on .hs is treated correctly { - const char * const extension = strchr(find_filename(actual_data_name.get()),'.'); - if (extension!=NULL && strcmp(extension, ".hs")==0) + const char* const extension = strchr(find_filename(actual_data_name.get()), '.'); + if (extension != NULL && strcmp(extension, ".hs") == 0) replace_extension(actual_data_name.get(), ".s"); else add_extension(actual_data_name.get(), ".s"); } #endif - shared_ptr sino_stream( - new fstream (actual_data_name.c_str(), ios::out| ios::binary)); - - if (!sino_stream->good()) - { - error("ECAT6cti_to_PDFS: error opening file %s\n",actual_data_name.c_str()); + shared_ptr sino_stream(new fstream(actual_data_name.c_str(), ios::out | ios::binary)); + + if (!sino_stream->good()) { + error("ECAT6cti_to_PDFS: error opening file %s\n", actual_data_name.c_str()); } - + shared_ptr exam_info_sptr(new ExamInfo); - proj_data.reset( - new ProjDataFromStream(exam_info_sptr,p_data_info,sino_stream, std::streamoff(0), storage_order)); - + proj_data.reset(new ProjDataFromStream(exam_info_sptr, p_data_info, sino_stream, std::streamoff(0), storage_order)); + write_basic_interfile_PDFS_header(actual_data_name, *proj_data); } - // write to proj_data { NumericType type; ByteOrder byte_order; find_type_from_ECAT_data_type(type, byte_order, scanParams.data_type); - // allocation for buffer. Provide enough space for a multiple of MatBLKSIZE - const size_t cti_data_size = - proj_data->get_num_tangential_poss()*proj_data->get_num_views()*type.size_in_bytes()+ MatBLKSIZE; - //use scoped_array to auto-delete the memory + // allocation for buffer. Provide enough space for a multiple of MatBLKSIZE + const size_t cti_data_size = + proj_data->get_num_tangential_poss() * proj_data->get_num_views() * type.size_in_bytes() + MatBLKSIZE; + // use scoped_array to auto-delete the memory boost::scoped_array cti_data_sptr(new char[cti_data_size]); - char * cti_data= cti_data_sptr.get(); - - cout<<"\nProcessing segment number:"; - - if (is_3D_file) - { - for(int w=0; w<=max_ring_diff; w++) - { // loop on segment number - + char* cti_data = cti_data_sptr.get(); + + cout << "\nProcessing segment number:"; + + if (is_3D_file) { + for (int w = 0; w <= max_ring_diff; w++) { // loop on segment number + // positive ring difference - cout<<" "< sino_2D = proj_data->get_empty_sinogram(ring1,w); - //TODO remove as will be set below - proj_data->set_sinogram(sino_2D); - read_sinogram(sino_2D, cti_data, cti_fptr, mat_index, - frame_num, gate_num, data_num, bed_num); - proj_data->set_sinogram(sino_2D); + cout << " " << w; + int num_axial_poss = num_rings - w; + + for (int ring1 = 0; ring1 < num_axial_poss; ring1++) { // ring order: 0-0,1-1,..,15-15 then 0-1,1-2,..,14-15 + int ring2 = ring1 + w; // ring1<=ring2 + int mat_index = cti_rings2plane(num_rings, ring1, ring2); + Sinogram sino_2D = proj_data->get_empty_sinogram(ring1, w); + // TODO remove as will be set below + proj_data->set_sinogram(sino_2D); + read_sinogram(sino_2D, cti_data, cti_fptr, mat_index, frame_num, gate_num, data_num, bed_num); + proj_data->set_sinogram(sino_2D); } - + // negative ring difference - if(w>0) { - cout<<" "<<-w; - for(int ring2=0; ring2ring2 - int mat_index= cti_rings2plane(num_rings, ring1, ring2); - Sinogram sino_2D = proj_data->get_empty_sinogram(ring2,-w,false); - read_sinogram(sino_2D, cti_data, - cti_fptr, mat_index, - frame_num, gate_num, data_num, bed_num); - - proj_data->set_sinogram(sino_2D); + if (w > 0) { + cout << " " << -w; + for (int ring2 = 0; ring2 < num_axial_poss; ring2++) { // ring order: 0-1,2-1,..,15-14 then 2-0,3-1,..,15-13 + int ring1 = ring2 + w; // ring1>ring2 + int mat_index = cti_rings2plane(num_rings, ring1, ring2); + Sinogram sino_2D = proj_data->get_empty_sinogram(ring2, -w, false); + read_sinogram(sino_2D, cti_data, cti_fptr, mat_index, frame_num, gate_num, data_num, bed_num); + + proj_data->set_sinogram(sino_2D); } } } // end of loop on segment number - } // end of 3D case - else - { + } // end of 3D case + else { // 2D case cout << "0\n"; - for(int z=0; zget_num_axial_poss(0); z++) - { - Sinogram sino_2D = proj_data->get_empty_sinogram(z,0,false); - read_sinogram(sino_2D, cti_data, cti_fptr, z+1, - frame_num, gate_num, data_num, bed_num); - proj_data->set_sinogram(sino_2D); + for (int z = 0; z < proj_data->get_num_axial_poss(0); z++) { + Sinogram sino_2D = proj_data->get_empty_sinogram(z, 0, false); + read_sinogram(sino_2D, cti_data, cti_fptr, z + 1, frame_num, gate_num, data_num, bed_num); + proj_data->set_sinogram(sino_2D); } - + } // end of 2D case - - cout<& sino_2D, - char *buffer, - FILE*fptr, - int mat_index, - int frame, int gate, int data, int bed) -{ +void +read_sinogram(Sinogram& sino_2D, char* buffer, FILE* fptr, int mat_index, int frame, int gate, int data, int bed) { ECAT6_Main_header mhead; ScanInfoRec scanParams; - const long matnum= - cti_numcod (frame,mat_index,gate,data,bed); - if (cti_read_ECAT6_Main_header (fptr, &mhead) != EXIT_SUCCESS) + const long matnum = cti_numcod(frame, mat_index, gate, data, bed); + if (cti_read_ECAT6_Main_header(fptr, &mhead) != EXIT_SUCCESS) error("read_sinogram: error reading ECAT6_Main_header"); - + float scale_factor = 0; // intialised to avoid compiler warnings - switch(mhead.file_type) - { - case matScanFile: - { - Scan_subheader shead; - - if(get_scanheaders (fptr, matnum, &mhead, - &shead, &scanParams)!= EXIT_SUCCESS) - error("Error reading matnum %d\n", matnum); - - scale_factor = shead.scale_factor; - if (shead.loss_correction_fctr>0) - scale_factor *= shead.loss_correction_fctr; - else - warning("\nread_sinogram warning: loss_correction_fctr invalid, using 1\n"); - break; - } - case matAttenFile: - { - Attn_subheader shead; - - if(get_attnheaders (fptr, matnum, &mhead, - &shead, &scanParams)!= EXIT_SUCCESS) - error("Error reading matnum %d\n", matnum); - - scale_factor = shead.scale_factor; - break; - } - case matNormFile: - { - Norm_subheader shead; - - if(get_normheaders (fptr, matnum, &mhead, - &shead, &scanParams)!= EXIT_SUCCESS) - error("Error reading matnum %d\n", matnum); - - scale_factor = shead.scale_factor; - break; - } - default: - error("read_sinogram: unsupported format"); + switch (mhead.file_type) { + case matScanFile: { + Scan_subheader shead; + + if (get_scanheaders(fptr, matnum, &mhead, &shead, &scanParams) != EXIT_SUCCESS) + error("Error reading matnum %d\n", matnum); + + scale_factor = shead.scale_factor; + if (shead.loss_correction_fctr > 0) + scale_factor *= shead.loss_correction_fctr; + else + warning("\nread_sinogram warning: loss_correction_fctr invalid, using 1\n"); + break; } - if(get_scandata (fptr, buffer, &scanParams)!= EXIT_SUCCESS) - error("Error reading matnum %d\n", matnum); + case matAttenFile: { + Attn_subheader shead; - cti_data_to_float_Array(sino_2D, buffer, scale_factor, scanParams.data_type); + if (get_attnheaders(fptr, matnum, &mhead, &shead, &scanParams) != EXIT_SUCCESS) + error("Error reading matnum %d\n", matnum); -} + scale_factor = shead.scale_factor; + break; + } + case matNormFile: { + Norm_subheader shead; + + if (get_normheaders(fptr, matnum, &mhead, &shead, &scanParams) != EXIT_SUCCESS) + error("Error reading matnum %d\n", matnum); + scale_factor = shead.scale_factor; + break; + } + default: + error("read_sinogram: unsupported format"); + } + if (get_scandata(fptr, buffer, &scanParams) != EXIT_SUCCESS) + error("Error reading matnum %d\n", matnum); -Succeeded -DiscretisedDensity_to_ECAT6(FILE *fptr, - DiscretisedDensity<3,float> const & density, - const ECAT6_Main_header& mhead, - const int frame_num, const int gate_num, const int data_num, const int bed_num) -{ - + cti_data_to_float_Array(sino_2D, buffer, scale_factor, scanParams.data_type); +} - DiscretisedDensityOnCartesianGrid<3,float> const & image = - dynamic_cast const&>(density); +Succeeded +DiscretisedDensity_to_ECAT6(FILE* fptr, DiscretisedDensity<3, float> const& density, const ECAT6_Main_header& mhead, + const int frame_num, const int gate_num, const int data_num, const int bed_num) { - - if (mhead.file_type!= matImageFile) - { + DiscretisedDensityOnCartesianGrid<3, float> const& image = + dynamic_cast const&>(density); + + if (mhead.file_type != matImageFile) { warning("DiscretisedDensity_to_ECAT6: converting (f%d, g%d, d%d, b%d)\n" "Main header.file_type should be ImageFile\n", frame_num, gate_num, data_num, bed_num); return Succeeded::no; } - if (mhead.num_planes!=image.get_length()) - { + if (mhead.num_planes != image.get_length()) { warning("DiscretisedDensity_to_ECAT6: converting (f%d, g%d, d%d, b%d)\n" "Main header.num_planes should be %d\n", - frame_num, gate_num, data_num, bed_num,image.get_length()); + frame_num, gate_num, data_num, bed_num, image.get_length()); return Succeeded::no; } - const float voxel_size_z = image.get_grid_spacing()[1]/10;// convert to cm - //const float voxel_size_y = image.get_grid_spacing()[2]/10; - const float voxel_size_x = image.get_grid_spacing()[3]/10; - if (fabs(mhead.plane_separation - voxel_size_z) > 1.E-4) - { + const float voxel_size_z = image.get_grid_spacing()[1] / 10; // convert to cm + // const float voxel_size_y = image.get_grid_spacing()[2]/10; + const float voxel_size_x = image.get_grid_spacing()[3] / 10; + if (fabs(mhead.plane_separation - voxel_size_z) > 1.E-4) { warning("DiscretisedDensity_to_ECAT6: converting (f%d, g%d, d%d, b%d)\n" "Main header.plane_separation should be %g\n", - frame_num, gate_num, data_num, bed_num,voxel_size_z); + frame_num, gate_num, data_num, bed_num, voxel_size_z); return Succeeded::no; } - - - Image_subheader ihead= img_zero_fill(); - - const int min_z= image.get_min_index(); - const int min_y= image[min_z].get_min_index(); - const int min_x= image[min_z][min_y].get_min_index(); - - const int z_size= image.get_length(); - const int y_size= image[min_z].get_length(); - const int x_size= image[min_z][min_y].get_length(); - - const int plane_size= y_size * x_size; - + + Image_subheader ihead = img_zero_fill(); + + const int min_z = image.get_min_index(); + const int min_y = image[min_z].get_min_index(); + const int min_x = image[min_z][min_y].get_min_index(); + + const int z_size = image.get_length(); + const int y_size = image[min_z].get_length(); + const int x_size = image[min_z][min_y].get_length(); + + const int plane_size = y_size * x_size; + // Setup subheader params #ifndef STIR_ORIGINAL_ECAT6 - ihead.data_type=ECAT_I2_little_endian_data_type; - ihead.x_dimension= x_size; - ihead.y_dimension= y_size; - ihead.z_dimension= z_size; - ihead.x_pixel_size= voxel_size_x; - ihead.y_pixel_size= voxel_size_x; - ihead.z_pixel_size= voxel_size_z; - - ihead.num_dimensions= 3; + ihead.data_type = ECAT_I2_little_endian_data_type; + ihead.x_dimension = x_size; + ihead.y_dimension = y_size; + ihead.z_dimension = z_size; + ihead.x_pixel_size = voxel_size_x; + ihead.y_pixel_size = voxel_size_x; + ihead.z_pixel_size = voxel_size_z; + + ihead.num_dimensions = 3; // STIR origin depends on the index range, but the index range is lost // after writing to file. // ECAT6 origin is somewhere in the middle of the image // WARNING this has to be consistent with reading - if (image[0][0].get_min_index() != -(x_size/2) || - image[0][0].get_max_index() != -(x_size/2) + x_size - 1 || - image[0].get_min_index() != -(y_size/2) || - image[0].get_max_index() != -(y_size/2) + y_size - 1 || - image.get_min_index() != 0) - { - warning("DiscretisedDensity_to_ECAT6 is currently limited to input images in the standard STIR index range.\n" - "Data not written."); - return Succeeded::no; - } - ihead.x_offset= image.get_origin().x()/10; - ihead.y_offset= image.get_origin().y()/10; - ihead.z_offset= image.get_origin().z()/10; + if (image[0][0].get_min_index() != -(x_size / 2) || image[0][0].get_max_index() != -(x_size / 2) + x_size - 1 || + image[0].get_min_index() != -(y_size / 2) || image[0].get_max_index() != -(y_size / 2) + y_size - 1 || + image.get_min_index() != 0) { + warning("DiscretisedDensity_to_ECAT6 is currently limited to input images in the standard STIR index range.\n" + "Data not written."); + return Succeeded::no; + } + ihead.x_offset = image.get_origin().x() / 10; + ihead.y_offset = image.get_origin().y() / 10; + ihead.z_offset = image.get_origin().z() / 10; shared_ptr scanner_ptr(find_scanner_from_ECAT6_Main_header(mhead)); const float depth_of_interaction_factor = - 1 + - scanner_ptr->get_average_depth_of_interaction() / - scanner_ptr->get_inner_ring_radius(); + 1 + scanner_ptr->get_average_depth_of_interaction() / scanner_ptr->get_inner_ring_radius(); // note: CTI uses shead.x_resolution instead of mhead.bin_size - // but we don't have access to the sinogram here, and these 2 fields + // but we don't have access to the sinogram here, and these 2 fields // should be equal anyway. - ihead.recon_zoom= - mhead.bin_size/voxel_size_x * - scanner_ptr->get_default_num_arccorrected_bins()/ - float(image[0].size()) * - depth_of_interaction_factor; - - ihead.decay_corr_fctr= 1; -#else // STIR_ORIGINAL_ECAT6 - ihead.data_type= mhead.data_type; - ihead.dimension_1= x_size; - ihead.dimension_2= y_size; - ihead.slice_width= mhead.plane_separation; - ihead.pixel_size= voxel_size_x; - - ihead.num_dimensions= 2; - ihead.x_origin= image.get_origin().x()/10; - ihead.y_origin= image.get_origin().y()/10; - ihead.recon_scale= 1; - ihead.decay_corr_fctr= 1; - ihead.loss_corr_fctr= 1; - ihead.ecat_calibration_fctr= 1; - ihead.well_counter_cal_fctr=1; + ihead.recon_zoom = mhead.bin_size / voxel_size_x * scanner_ptr->get_default_num_arccorrected_bins() / float(image[0].size()) * + depth_of_interaction_factor; + + ihead.decay_corr_fctr = 1; +#else // STIR_ORIGINAL_ECAT6 + ihead.data_type = mhead.data_type; + ihead.dimension_1 = x_size; + ihead.dimension_2 = y_size; + ihead.slice_width = mhead.plane_separation; + ihead.pixel_size = voxel_size_x; + + ihead.num_dimensions = 2; + ihead.x_origin = image.get_origin().x() / 10; + ihead.y_origin = image.get_origin().y() / 10; + ihead.recon_scale = 1; + ihead.decay_corr_fctr = 1; + ihead.loss_corr_fctr = 1; + ihead.ecat_calibration_fctr = 1; + ihead.well_counter_cal_fctr = 1; #endif // STIR_ORIGINAL_ECAT6 - + // make sure we have a large enough multiple of MatBLKSIZE - int cti_data_size = plane_size*2; - if (cti_data_size%MatBLKSIZE != 0) - cti_data_size = ((cti_data_size/MatBLKSIZE)+1)*MatBLKSIZE; - short *cti_data= new short[cti_data_size/2]; - Array<2,short> plane(image[min_z].get_index_range()); - - for(int z=0; z plane(image[min_z].get_index_range()); + + for (int z = 0; z < z_size; z++) { // loop on planes float scale_factor = 0; - convert_array(plane, scale_factor, image[z+min_z]); - ihead.image_min= plane.find_min(); - ihead.image_max= plane.find_max(); + convert_array(plane, scale_factor, image[z + min_z]); + ihead.image_min = plane.find_min(); + ihead.image_max = plane.find_max(); #ifndef STIR_ORIGINAL_ECAT6 - ihead.scale_factor= scale_factor==0 ? 1.F : scale_factor; + ihead.scale_factor = scale_factor == 0 ? 1.F : scale_factor; #else - ihead.quant_scale= scale_factor==0 ? 1.F : scale_factor; + ihead.quant_scale = scale_factor == 0 ? 1.F : scale_factor; #endif // STIR_ORIGINAL_ECAT6 - - for(int y=0; y const & density, - std::string const & cti_name, std::string const&orig_name, - const Scanner& scanner, - const int frame_num, const int gate_num, const int data_num, const int bed_num) -{ +Succeeded +DiscretisedDensity_to_ECAT6(DiscretisedDensity<3, float> const& density, std::string const& cti_name, + std::string const& orig_name, const Scanner& scanner, const int frame_num, const int gate_num, + const int data_num, const int bed_num) { ECAT6_Main_header mhead; make_ECAT6_Main_header(mhead, scanner, orig_name, density); - - FILE *fptr= cti_create (cti_name.c_str(), &mhead); - Succeeded result = - DiscretisedDensity_to_ECAT6(fptr, - density, - mhead, - frame_num, gate_num,data_num, bed_num); - - fclose(fptr); + FILE* fptr = cti_create(cti_name.c_str(), &mhead); + Succeeded result = DiscretisedDensity_to_ECAT6(fptr, density, mhead, frame_num, gate_num, data_num, bed_num); + + fclose(fptr); return result; } -Succeeded -ProjData_to_ECAT6(FILE *fptr, ProjData const& proj_data, const ECAT6_Main_header& mhead, - const int frame_num, const int gate_num, const int data_num, const int bed_num, - const bool write_2D_sinograms) -{ - if (mhead.file_type!= matScanFile) - { +Succeeded +ProjData_to_ECAT6(FILE* fptr, ProjData const& proj_data, const ECAT6_Main_header& mhead, const int frame_num, const int gate_num, + const int data_num, const int bed_num, const bool write_2D_sinograms) { + if (mhead.file_type != matScanFile) { warning("ProjData_to_ECAT6: converting (f%d, g%d, d%d, b%d)\n" "Main header.file_type should be ImageFile\n", frame_num, gate_num, data_num, bed_num); return Succeeded::no; } - const int max_segment_num = - write_2D_sinograms - ? 0 - : - min(proj_data.get_max_segment_num(), -proj_data.get_min_segment_num()) -; - const int min_segment_num = - max_segment_num; + const int max_segment_num = write_2D_sinograms ? 0 : min(proj_data.get_max_segment_num(), -proj_data.get_min_segment_num()); + const int min_segment_num = -max_segment_num; { int num_planes = 0; - for(int segment_num=min_segment_num; - segment_num <= max_segment_num; - ++segment_num) - num_planes+= proj_data.get_num_axial_poss(segment_num); - - if (mhead.num_planes!=num_planes) - { - warning("ProjData_to_ECAT6: converting (f%d, g%d, d%d, b%d)\n" - "Main header.num_planes should be %d, but is %d\n", - frame_num, gate_num, data_num, bed_num,num_planes, mhead.num_planes); - if (mhead.num_planes - (proj_data.get_proj_data_info_sptr().get()); - if (proj_data_info_cyl_ptr==NULL) - { + ProjDataInfoCylindricalArcCorr const* const proj_data_info_cyl_ptr = + dynamic_cast(proj_data.get_proj_data_info_sptr().get()); + if (proj_data_info_cyl_ptr == NULL) { warning("This is not arc-corrected data. Filling in default_bin_size from scanner \n"); #ifndef STIR_ORIGINAL_ECAT6 - shead.x_resolution= + shead.x_resolution = #else - shead.sample_distance= + shead.sample_distance = #endif - proj_data.get_proj_data_info_sptr()->get_scanner_sptr()->get_default_bin_size()/10; - } - else - { + proj_data.get_proj_data_info_sptr()->get_scanner_sptr()->get_default_bin_size() / 10; + } else { #ifndef STIR_ORIGINAL_ECAT6 - shead.x_resolution= + shead.x_resolution = #else - shead.sample_distance= + shead.sample_distance = #endif - proj_data_info_cyl_ptr->get_tangential_sampling()/10; + proj_data_info_cyl_ptr->get_tangential_sampling() / 10; } } - + // find num_rings and check span int num_rings = proj_data.get_num_axial_poss(0); { - ProjDataInfoCylindrical const * const - proj_data_info_cyl_ptr = - dynamic_cast - (proj_data.get_proj_data_info_sptr().get()); - if (proj_data_info_cyl_ptr!=NULL) - { - // check if spanned data in segment 0 - if (proj_data_info_cyl_ptr->get_min_ring_difference(0) < - proj_data_info_cyl_ptr->get_max_ring_difference(0)) - { - if (write_2D_sinograms) - num_rings = (proj_data.get_num_axial_poss(0)+1)/2; - else - { - warning("Can only handle span==1 data. Exiting\n"); - return Succeeded::no; - } - } + ProjDataInfoCylindrical const* const proj_data_info_cyl_ptr = + dynamic_cast(proj_data.get_proj_data_info_sptr().get()); + if (proj_data_info_cyl_ptr != NULL) { + // check if spanned data in segment 0 + if (proj_data_info_cyl_ptr->get_min_ring_difference(0) < proj_data_info_cyl_ptr->get_max_ring_difference(0)) { + if (write_2D_sinograms) + num_rings = (proj_data.get_num_axial_poss(0) + 1) / 2; + else { + warning("Can only handle span==1 data. Exiting\n"); + return Succeeded::no; + } } + } } - - if (num_rings != proj_data.get_proj_data_info_sptr()->get_scanner_sptr()->get_num_rings()) -{ + if (num_rings != proj_data.get_proj_data_info_sptr()->get_scanner_sptr()->get_num_rings()) { warning("Expected %d num_rings from scanner while segment 0 implies %d rings\n", proj_data.get_proj_data_info_sptr()->get_scanner_sptr()->get_num_rings(), num_rings); -} - - short *cti_data= new short[plane_size]; - Array<2,short> short_sinogram(IndexRange2D(min_view,proj_data.get_max_view_num(), - min_bin,proj_data.get_max_tangential_pos_num())); - - cout< short_sinogram( + IndexRange2D(min_view, proj_data.get_max_view_num(), min_bin, proj_data.get_max_tangential_pos_num())); + + cout << endl << "Processing segment number:"; + + for (int segment_num = min_segment_num; segment_num <= max_segment_num; ++segment_num) { + cout << " " << segment_num; + + const int num_axial_poss = proj_data.get_num_axial_poss(segment_num); + const int min_axial_poss = proj_data.get_min_axial_pos_num(segment_num); + + if (!write_2D_sinograms && num_axial_poss != num_rings - abs(segment_num)) { warning("Can only handle span==1 data. Number of sinograms in this segment " - "should be %d. Exiting\n", num_rings - abs(segment_num)); + "should be %d. Exiting\n", + num_rings - abs(segment_num)); delete[] cti_data; return Succeeded::no; } - - for(int z=0; z float_sinogram= proj_data.get_sinogram(z+min_axial_poss,segment_num,false); - + + for (int z = 0; z < num_axial_poss; z++) { // loop on planes + Sinogram float_sinogram = proj_data.get_sinogram(z + min_axial_poss, segment_num, false); + float scale_factor = 0; convert_array(short_sinogram, scale_factor, float_sinogram); - - - shead.scan_min= short_sinogram.find_min(); - shead.scan_max= short_sinogram.find_max(); - shead.scale_factor= scale_factor==0 ? 1.F : scale_factor; - - for(int y=0; y=0) - { ring1= z; ring2= z+segment_num; } - else - { ring1= z+abs(segment_num); ring2= z; } - - const int indexcod= - write_2D_sinograms - ? z+1 - : cti_rings2plane( num_rings, ring1, ring2); // change indexation into CTI - const long matnum= cti_numcod(frame_num, indexcod, gate_num, data_num, bed_num); - if(cti_write_scan(fptr, matnum, &mhead, &shead, cti_data, plane_size*sizeof(short))!=EXIT_SUCCESS) - { - warning("Unable to write short_sinogram for rings %d,%d to file, exiting.\n",ring1,ring2); + if (segment_num >= 0) { + ring1 = z; + ring2 = z + segment_num; + } else { + ring1 = z + abs(segment_num); + ring2 = z; + } + + const int indexcod = write_2D_sinograms ? z + 1 : cti_rings2plane(num_rings, ring1, ring2); // change indexation into CTI + const long matnum = cti_numcod(frame_num, indexcod, gate_num, data_num, bed_num); + if (cti_write_scan(fptr, matnum, &mhead, &shead, cti_data, plane_size * sizeof(short)) != EXIT_SUCCESS) { + warning("Unable to write short_sinogram for rings %d,%d to file, exiting.\n", ring1, ring2); delete[] cti_data; return Succeeded::no; } } // end of loop on planes - } // end of loop on segments - cout<&out, - char const * const buffer, const float scale_factor, int dtype) -{ +void +cti_data_to_float_Array(Array<2, float>& out, char const* const buffer, const float scale_factor, int dtype) { - BOOST_STATIC_ASSERT(sizeof(float)==4); + BOOST_STATIC_ASSERT(sizeof(float) == 4); - switch (dtype) - { - case ECAT_Byte_data_type: - { - signed char const * cti_data = - reinterpret_cast(buffer); - for(int y=out.get_min_index(); y<=out.get_max_index(); y++) - for(int x=out[y].get_min_index(); x<=out[y].get_max_index(); x++) - out[y][x]=scale_factor*(*cti_data++); + switch (dtype) { + case ECAT_Byte_data_type: { + signed char const* cti_data = reinterpret_cast(buffer); + for (int y = out.get_min_index(); y <= out.get_max_index(); y++) + for (int x = out[y].get_min_index(); x <= out[y].get_max_index(); x++) + out[y][x] = scale_factor * (*cti_data++); break; } case ECAT_I2_little_endian_data_type: - case ECAT_I2_big_endian_data_type: - { - boost::int16_t const * cti_data = - reinterpret_cast(buffer); - for(int y=out.get_min_index(); y<=out.get_max_index(); y++) - for(int x=out[y].get_min_index(); x<=out[y].get_max_index(); x++) - out[y][x]=scale_factor*(*cti_data++); + case ECAT_I2_big_endian_data_type: { + boost::int16_t const* cti_data = reinterpret_cast(buffer); + for (int y = out.get_min_index(); y <= out.get_max_index(); y++) + for (int x = out[y].get_min_index(); x <= out[y].get_max_index(); x++) + out[y][x] = scale_factor * (*cti_data++); break; } case ECAT_I4_little_endian_data_type: - case ECAT_I4_big_endian_data_type: - { - boost::int32_t const * cti_data = - reinterpret_cast(buffer); - for(int y=out.get_min_index(); y<=out.get_max_index(); y++) - for(int x=out[y].get_min_index(); x<=out[y].get_max_index(); x++) - out[y][x]=scale_factor*(*cti_data++); + case ECAT_I4_big_endian_data_type: { + boost::int32_t const* cti_data = reinterpret_cast(buffer); + for (int y = out.get_min_index(); y <= out.get_max_index(); y++) + for (int x = out[y].get_min_index(); x <= out[y].get_max_index(); x++) + out[y][x] = scale_factor * (*cti_data++); break; } case ECAT_R4_VAX_data_type: - case ECAT_R4_IEEE_big_endian_data_type: - { - float const * cti_data = - reinterpret_cast(buffer); - for(int y=out.get_min_index(); y<=out.get_max_index(); y++) - for(int x=out[y].get_min_index(); x<=out[y].get_max_index(); x++) - out[y][x]=scale_factor*(*cti_data++); + case ECAT_R4_IEEE_big_endian_data_type: { + float const* cti_data = reinterpret_cast(buffer); + for (int y = out.get_min_index(); y <= out.get_max_index(); y++) + for (int x = out[y].get_min_index(); x <= out[y].get_max_index(); x++) + out[y][x] = scale_factor * (*cti_data++); break; } } diff --git a/src/IO/stir_ecat7.cxx b/src/IO/stir_ecat7.cxx index f8ad79f12f..721d1c2bbc 100644 --- a/src/IO/stir_ecat7.cxx +++ b/src/IO/stir_ecat7.cxx @@ -19,20 +19,19 @@ \file \ingroup ECAT - \brief Implementation of routines which convert ECAT7 things into our + \brief Implementation of routines which convert ECAT7 things into our building blocks and vice versa. \author Kris Thielemans \author Cristina de Oliveira (stir::ecat::ecat7::offset_in_ECAT_file function) \warning This only works with some CTI file_types. In particular, it does NOT - work with the ECAT6-like files_types, as then there are subheaders 'in' the + work with the ECAT6-like files_types, as then there are subheaders 'in' the datasets. - + \warning Implementation uses the Louvain la Neuve Ecat library. So, it will only work on systems where this library works properly. */ - #include "stir/ProjDataInfo.h" #include "stir/ProjDataFromStream.h" #include "stir/ExamInfo.h" @@ -52,9 +51,9 @@ #include "stir/ProjDataInfoCylindricalArcCorr.h" #include "stir/SegmentByView.h" #include "stir/IndexRange2D.h" -#include "stir/Scanner.h" -#include "stir/Bin.h" -#include "stir/Succeeded.h" +#include "stir/Scanner.h" +#include "stir/Bin.h" +#include "stir/Succeeded.h" #include "stir/convert_array.h" #include "stir/NumericInfo.h" #include "stir/IO/stir_ecat7.h" @@ -91,384 +90,318 @@ START_NAMESPACE_ECAT7 /* ------------------------------------ * print_debug * ------------------------------------*/ -static int print_debug (char const * const fname, char const * const format, ...) -{ - va_list ap; - char *fmt; - int len; +static int +print_debug(char const* const fname, char const* const format, ...) { + va_list ap; + char* fmt; + int len; + if (0) // flagged (fname) != NULL) + { - if (0)//flagged (fname) != NULL) - { - - len = strlen (fname) + strlen (format) + 5; - if ((fmt = (char *)calloc ((long)len, sizeof (char))) == NULL) - return (1); - sprintf (fmt, "%s%s%s", fname, " :: ", format); - - va_start (ap, format); - vfprintf (stderr, fmt, ap); + len = strlen(fname) + strlen(format) + 5; + if ((fmt = (char*)calloc((long)len, sizeof(char))) == NULL) + return (1); + sprintf(fmt, "%s%s%s", fname, " :: ", format); - free (fmt); - va_end (ap); - - } + va_start(ap, format); + vfprintf(stderr, fmt, ap); - return (0); + free(fmt); + va_end(ap); + } + return (0); } - -Succeeded read_ECAT7_main_header(Main_header& mhead, const string& filename) -{ - FILE * cti_fptr=fopen(filename.c_str(), "rb"); +Succeeded +read_ECAT7_main_header(Main_header& mhead, const string& filename) { + FILE* cti_fptr = fopen(filename.c_str(), "rb"); if (!cti_fptr) return Succeeded::no; - // first check 'magic_number' before going into LLN routines + // first check 'magic_number' before going into LLN routines // to avoid crashes and error messages there // an ECAT7 file should start with MATRIX7 { char magic[7]; - if (fread(magic, 1, 7, cti_fptr) != 7 || - strncmp(magic, "MATRIX7",7)!=0) - { - fclose(cti_fptr); - return Succeeded::no; - } - } - if(mat_read_main_header(cti_fptr, &mhead)!=0) - { + if (fread(magic, 1, 7, cti_fptr) != 7 || strncmp(magic, "MATRIX7", 7) != 0) { fclose(cti_fptr); - // this is funny as it's just reading a bunch of bytes. anyway. we'll assume it isn't ECAT7 return Succeeded::no; } - else - { - // do some checks on the main header - fclose(cti_fptr); - if (mhead.sw_version>=70 && mhead.sw_version<=79 && - ( mhead.file_type >= 1 && mhead.file_type <= Float3dSinogram) && - mhead.num_frames>0) - return Succeeded::yes; - else - return Succeeded::no; - } + } + if (mat_read_main_header(cti_fptr, &mhead) != 0) { + fclose(cti_fptr); + // this is funny as it's just reading a bunch of bytes. anyway. we'll assume it isn't ECAT7 + return Succeeded::no; + } else { + // do some checks on the main header + fclose(cti_fptr); + if (mhead.sw_version >= 70 && mhead.sw_version <= 79 && (mhead.file_type >= 1 && mhead.file_type <= Float3dSinogram) && + mhead.num_frames > 0) + return Succeeded::yes; + else + return Succeeded::no; + } } -static bool is_ECAT7_file(Main_header& mhead, const string& filename) -{ +static bool +is_ECAT7_file(Main_header& mhead, const string& filename) { return read_ECAT7_main_header(mhead, filename) == Succeeded::yes; } -bool is_ECAT7_file(const string& filename) -{ +bool +is_ECAT7_file(const string& filename) { Main_header mhead; return is_ECAT7_file(mhead, filename); } -bool is_ECAT7_image_file(const string& filename) -{ +bool +is_ECAT7_image_file(const string& filename) { Main_header mhead; return is_ECAT7_file(mhead, filename) && - (mhead.file_type == PetImage || - mhead.file_type ==ByteVolume || mhead.file_type == PetVolume); + (mhead.file_type == PetImage || mhead.file_type == ByteVolume || mhead.file_type == PetVolume); } - -bool is_ECAT7_emission_file(const string& filename) -{ +bool +is_ECAT7_emission_file(const string& filename) { Main_header mhead; - return is_ECAT7_file(mhead, filename) && - (mhead.file_type == CTISinogram || mhead.file_type == Byte3dSinogram|| - mhead.file_type == Short3dSinogram || mhead.file_type == Float3dSinogram); + return is_ECAT7_file(mhead, filename) && (mhead.file_type == CTISinogram || mhead.file_type == Byte3dSinogram || + mhead.file_type == Short3dSinogram || mhead.file_type == Float3dSinogram); } - -bool is_ECAT7_attenuation_file(const string& filename) -{ +bool +is_ECAT7_attenuation_file(const string& filename) { Main_header mhead; - return is_ECAT7_file(mhead, filename) && - mhead.file_type ==AttenCor; + return is_ECAT7_file(mhead, filename) && mhead.file_type == AttenCor; } - - -void find_scanner(shared_ptr & scanner_ptr,const Main_header& mhead) -{ +void +find_scanner(shared_ptr& scanner_ptr, const Main_header& mhead) { scanner_ptr.reset(find_scanner_from_ECAT_system_type(mhead.system_type)); } - -short find_ECAT_data_type(const NumericType& type, const ByteOrder& byte_order) -{ +short +find_ECAT_data_type(const NumericType& type, const ByteOrder& byte_order) { if (!type.signed_type()) warning("find_ECAT_data_type: CTI data support only signed types. Using the signed equivalent\n"); - if (type.integer_type()) - { - switch(type.size_in_bytes()) - { + if (type.integer_type()) { + switch (type.size_in_bytes()) { case 1: return ByteData; case 2: - return byte_order==ByteOrder::big_endian ? SunShort : VAX_Ix2; + return byte_order == ByteOrder::big_endian ? SunShort : VAX_Ix2; case 4: - return byte_order==ByteOrder::big_endian ? SunLong : VAX_Ix4; - default: - { - // write error message below - } + return byte_order == ByteOrder::big_endian ? SunLong : VAX_Ix4; + default: { + // write error message below } - } - else - { - switch(type.size_in_bytes()) - { + } + } else { + switch (type.size_in_bytes()) { case 4: - return byte_order==ByteOrder::big_endian ? IeeeFloat : VAX_Rx4; - default: - { - // write error message below - } + return byte_order == ByteOrder::big_endian ? IeeeFloat : VAX_Rx4; + default: { + // write error message below + } } } string number_format; size_t size_in_bytes; type.get_Interfile_info(number_format, size_in_bytes); - warning("find_ECAT_data_type: CTI does not support data type '%s' of %d bytes.\n", - number_format.c_str(), size_in_bytes); + warning("find_ECAT_data_type: CTI does not support data type '%s' of %d bytes.\n", number_format.c_str(), size_in_bytes); return short(0); } /* ------------------------------------------- -* o f f s e t -* ------------------------------------------- -*/ -static long offset_in_ECAT_file (MatrixFile *mptr, int frame, int plane, int gate, int data, - int bed, int segment, int *plane_size_ptr = NULL) -{ - + * o f f s e t + * ------------------------------------------- + */ +static long +offset_in_ECAT_file(MatrixFile* mptr, int frame, int plane, int gate, int data, int bed, int segment, + int* plane_size_ptr = NULL) { + int el_size[15], matnum, strtblk, group = abs(segment), i; - long plane_size = 0, off; + long plane_size = 0, off; struct MatDir matdir; Scan_subheader scansub; Image_subheader imagesub; Norm_subheader normsub; Attn_subheader attnsub; Scan3D_subheader scan3dsub; - const char * const prog = "offset_in_ECAT_file"; + const char* const prog = "offset_in_ECAT_file"; /* set_debug (prog); */ el_size[ByteData] = 1; el_size[VAX_Ix2] = el_size[SunShort] = 2; el_size[VAX_Ix4] = el_size[VAX_Rx4] = el_size[IeeeFloat] = el_size[SunLong] = 4; - + if (mptr->mhptr->sw_version < V7) - matnum = mat_numcod (frame, plane, gate, data, bed); + matnum = mat_numcod(frame, plane, gate, data, bed); else - matnum = mat_numcod (frame, 1, gate, data, bed); - print_debug (prog, "matnum = %d\n", matnum); - - if (matrix_find (mptr, matnum, &matdir) != 0) + matnum = mat_numcod(frame, 1, gate, data, bed); + print_debug(prog, "matnum = %d\n", matnum); + + if (matrix_find(mptr, matnum, &matdir) != 0) return -1; - + strtblk = matdir.strtblk; - print_debug (prog, "strtblk = %d\n", strtblk); - - + print_debug(prog, "strtblk = %d\n", strtblk); + off = (strtblk + 1) * MatBLKSIZE; - - switch (mptr->mhptr->file_type) - { + + switch (mptr->mhptr->file_type) { #ifndef STIR_NO_NAMESPACES case ::Sinogram: #else case CTISinogram: #endif - { - // KT 14/05/2002 added error check. - if (mat_read_scan_subheader (mptr->fptr, mptr->mhptr, strtblk, &scansub)) - { - if (ferror(mptr->fptr)) - perror("offset_in_ECAT_file: error in reading subheader"); - return -1; - } - plane_size = scansub.num_r_elements * - scansub.num_angles * - el_size[scansub.data_type]; - - if (mptr->mhptr->sw_version < V7) - off = strtblk*MatBLKSIZE; - else - off = (strtblk + 1) * MatBLKSIZE + (plane-1) * plane_size; - break; + { + // KT 14/05/2002 added error check. + if (mat_read_scan_subheader(mptr->fptr, mptr->mhptr, strtblk, &scansub)) { + if (ferror(mptr->fptr)) + perror("offset_in_ECAT_file: error in reading subheader"); + return -1; } + plane_size = scansub.num_r_elements * scansub.num_angles * el_size[scansub.data_type]; + + if (mptr->mhptr->sw_version < V7) + off = strtblk * MatBLKSIZE; + else + off = (strtblk + 1) * MatBLKSIZE + (plane - 1) * plane_size; + break; + } case PetImage: case ByteVolume: - case PetVolume: - { - // KT 14/05/2002 added error check. - if (mat_read_image_subheader (mptr->fptr, mptr->mhptr, strtblk, &imagesub)) - { - if (ferror(mptr->fptr)) - perror("offset_in_ECAT_file: error in reading subheader"); - return -1; - } - - off = strtblk*MatBLKSIZE; - plane_size = imagesub.x_dimension * - imagesub.y_dimension * - el_size[imagesub.data_type]; - - if (mptr->mhptr->sw_version >= V7) - off += (plane-1) * plane_size; - break; - + case PetVolume: { + // KT 14/05/2002 added error check. + if (mat_read_image_subheader(mptr->fptr, mptr->mhptr, strtblk, &imagesub)) { + if (ferror(mptr->fptr)) + perror("offset_in_ECAT_file: error in reading subheader"); + return -1; } - - case AttenCor: - { - off = strtblk * MatBLKSIZE; - print_debug (prog, "off = %d\n", off); - - if (mptr->mhptr->sw_version >= V7) - { - print_debug (prog, "AttenCor\n"); - // KT 14/05/2002 added error check. - if (mat_read_attn_subheader (mptr->fptr, mptr->mhptr, strtblk, &attnsub)) - { + + off = strtblk * MatBLKSIZE; + plane_size = imagesub.x_dimension * imagesub.y_dimension * el_size[imagesub.data_type]; + + if (mptr->mhptr->sw_version >= V7) + off += (plane - 1) * plane_size; + break; + } + + case AttenCor: { + off = strtblk * MatBLKSIZE; + print_debug(prog, "off = %d\n", off); + + if (mptr->mhptr->sw_version >= V7) { + print_debug(prog, "AttenCor\n"); + // KT 14/05/2002 added error check. + if (mat_read_attn_subheader(mptr->fptr, mptr->mhptr, strtblk, &attnsub)) { if (ferror(mptr->fptr)) perror("offset_in_ECAT_file: error in reading subheader"); return -1; } - - - switch (attnsub.storage_order) - { - case ElVwAxRd: - plane_size = attnsub.num_r_elements * - attnsub.num_angles * - el_size[attnsub.data_type]; - - if (group) - for (i = 0; i < group; i++) - off += plane_size * attnsub.z_elements[i]; - // KT 25/10/2000 swapped segment order - if (segment > 0) - off += plane_size * attnsub.z_elements[group]/2; - off += (plane - 1) * plane_size; - break; - - case ElAxVwRd: - print_debug (prog, "group %d, plane %d\n", group, plane); - if (group) - for (i = 0; i < group; i++) - { - plane_size = attnsub.num_r_elements * - attnsub.z_elements[i] * - el_size[attnsub.data_type]; - off += plane_size * attnsub.num_angles; - } - plane_size = attnsub.num_r_elements * - attnsub.z_elements[group] * - el_size[attnsub.data_type]; - if (group) - plane_size /=2; - // KT 25/10/2000 swapped segment order - if (segment > 0) - off += plane_size*attnsub.num_angles; - - off += (plane - 1) *plane_size; - - break; - } - + + switch (attnsub.storage_order) { + case ElVwAxRd: + plane_size = attnsub.num_r_elements * attnsub.num_angles * el_size[attnsub.data_type]; + + if (group) + for (i = 0; i < group; i++) + off += plane_size * attnsub.z_elements[i]; + // KT 25/10/2000 swapped segment order + if (segment > 0) + off += plane_size * attnsub.z_elements[group] / 2; + off += (plane - 1) * plane_size; + break; + + case ElAxVwRd: + print_debug(prog, "group %d, plane %d\n", group, plane); + if (group) + for (i = 0; i < group; i++) { + plane_size = attnsub.num_r_elements * attnsub.z_elements[i] * el_size[attnsub.data_type]; + off += plane_size * attnsub.num_angles; + } + plane_size = attnsub.num_r_elements * attnsub.z_elements[group] * el_size[attnsub.data_type]; + if (group) + plane_size /= 2; + // KT 25/10/2000 swapped segment order + if (segment > 0) + off += plane_size * attnsub.num_angles; + + off += (plane - 1) * plane_size; + + break; } - break; } - - - case Normalization: - { - - // KT 14/05/2002 added error check. - if (mat_read_norm_subheader (mptr->fptr, mptr->mhptr, strtblk, &normsub)) - { - if (ferror(mptr->fptr)) - perror("offset_in_ECAT_file: error in reading subheader"); - return -1; - } - off = strtblk*MatBLKSIZE; - plane_size = normsub.num_r_elements * - normsub.num_angles * - el_size[normsub.data_type]; - if (mptr->mhptr->sw_version >= V7) - off += (plane-1) * plane_size; - break; + break; + } + + case Normalization: { + + // KT 14/05/2002 added error check. + if (mat_read_norm_subheader(mptr->fptr, mptr->mhptr, strtblk, &normsub)) { + if (ferror(mptr->fptr)) + perror("offset_in_ECAT_file: error in reading subheader"); + return -1; } + off = strtblk * MatBLKSIZE; + plane_size = normsub.num_r_elements * normsub.num_angles * el_size[normsub.data_type]; + if (mptr->mhptr->sw_version >= V7) + off += (plane - 1) * plane_size; + break; + } case ByteProjection: case PetProjection: - case PolarMap: - { - fprintf (stderr, "Not implemented for this file type\n"); - off = -1; - break; - } + case PolarMap: { + fprintf(stderr, "Not implemented for this file type\n"); + off = -1; + break; + } case Byte3dSinogram: case Short3dSinogram: - case Float3dSinogram : - { - off = (strtblk+1) * MatBLKSIZE; - print_debug (prog, "off = %d\n", off); + case Float3dSinogram: { + off = (strtblk + 1) * MatBLKSIZE; + print_debug(prog, "off = %d\n", off); + + // KT 14/05/2002 added error check. + if (mat_read_Scan3D_subheader(mptr->fptr, mptr->mhptr, strtblk, &scan3dsub)) { + if (ferror(mptr->fptr)) + perror("offset_in_ECAT_file: error in reading subheader"); + return -1; + } - // KT 14/05/2002 added error check. - if (mat_read_Scan3D_subheader (mptr->fptr, mptr->mhptr, strtblk, &scan3dsub)) - { - if (ferror(mptr->fptr)) - perror("offset_in_ECAT_file: error in reading subheader"); - return -1; - } - - switch (scan3dsub.storage_order) - { - case ElVwAxRd: - plane_size = scan3dsub.num_r_elements * - scan3dsub.num_angles * - el_size[scan3dsub.data_type]; - print_debug (prog, "xdim = %d (num_r_elements)\n", scan3dsub.num_r_elements); - print_debug (prog, "ydim = %d (num_angles) \n", scan3dsub.num_angles); - print_debug (prog, "plane_size = %d \n", plane_size); - if (group) - for (i = 0; i < group; i++) - off += (plane_size * scan3dsub.num_z_elements[i]); - // KT 25/10/2000 swapped segment order - if (segment > 0) - off += plane_size * scan3dsub.num_z_elements[group]/2; - - - print_debug (prog, "num_z_elements[group] = %d\n", scan3dsub.num_z_elements[group]); - print_debug (prog, "plane-1 = %d\n", plane-1); - - off += ((plane - 1) * plane_size); - print_debug (prog, "off = %d\n", off); - break; - - case ElAxVwRd: - if (group) - for (i = 0; i < group; i++) - { - plane_size = scan3dsub.num_r_elements * - scan3dsub.num_z_elements[i] * - el_size[scan3dsub.data_type]; - off += plane_size * scan3dsub.num_angles; - } - plane_size = scan3dsub.num_r_elements * - scan3dsub.num_z_elements[group] * - el_size[scan3dsub.data_type]; - // KT 14/05/2002 corrected. It seems that the convention of planes was different - // now it's the same as for AttenCor + switch (scan3dsub.storage_order) { + case ElVwAxRd: + plane_size = scan3dsub.num_r_elements * scan3dsub.num_angles * el_size[scan3dsub.data_type]; + print_debug(prog, "xdim = %d (num_r_elements)\n", scan3dsub.num_r_elements); + print_debug(prog, "ydim = %d (num_angles) \n", scan3dsub.num_angles); + print_debug(prog, "plane_size = %d \n", plane_size); + if (group) + for (i = 0; i < group; i++) + off += (plane_size * scan3dsub.num_z_elements[i]); + // KT 25/10/2000 swapped segment order + if (segment > 0) + off += plane_size * scan3dsub.num_z_elements[group] / 2; + + print_debug(prog, "num_z_elements[group] = %d\n", scan3dsub.num_z_elements[group]); + print_debug(prog, "plane-1 = %d\n", plane - 1); + + off += ((plane - 1) * plane_size); + print_debug(prog, "off = %d\n", off); + break; + + case ElAxVwRd: + if (group) + for (i = 0; i < group; i++) { + plane_size = scan3dsub.num_r_elements * scan3dsub.num_z_elements[i] * el_size[scan3dsub.data_type]; + off += plane_size * scan3dsub.num_angles; + } + plane_size = scan3dsub.num_r_elements * scan3dsub.num_z_elements[group] * el_size[scan3dsub.data_type]; + // KT 14/05/2002 corrected. It seems that the convention of planes was different + // now it's the same as for AttenCor #if 0 if (group) { @@ -481,115 +414,99 @@ static long offset_in_ECAT_file (MatrixFile *mptr, int frame, int plane, int gat else off += (plane - 1) *plane_size; #else - if (group) - plane_size /=2; - // KT 25/10/2000 swapped segment order - if (segment > 0) - off += plane_size*scan3dsub.num_angles; - off += (plane - 1) *plane_size; + if (group) + plane_size /= 2; + // KT 25/10/2000 swapped segment order + if (segment > 0) + off += plane_size * scan3dsub.num_angles; + off += (plane - 1) * plane_size; #endif - break; - } - break; - } - case Norm3d: - { - fprintf (stderr, "Not implemented yet\n"); - off = 1; break; } + break; } - + case Norm3d: { + fprintf(stderr, "Not implemented yet\n"); + off = 1; + break; + } + } + if (plane_size_ptr != NULL) *plane_size_ptr = plane_size; - - return (off); + return (off); } - -static void fill_string (char *str, int len) -{ - for (int i=0; i -read_ECAT7_exam_info(MatrixFile *mptr) -{ - - const int num_frames = std::max(static_cast( mptr->mhptr->num_frames),1); +read_ECAT7_exam_info(MatrixFile* mptr) { + + const int num_frames = std::max(static_cast(mptr->mhptr->num_frames), 1); // funnily enough, num_bed_pos seems to be offset with 1 - // (That's to say, in a singled bed study, num_bed_pos==0) + // (That's to say, in a singled bed study, num_bed_pos==0) // TODO maybe not true for multi-bed studies - const int num_bed_poss = static_cast( mptr->mhptr->num_bed_pos) + 1; + const int num_bed_poss = static_cast(mptr->mhptr->num_bed_pos) + 1; // const int num_gates = std::max(static_cast( mptr->mhptr->num_gates),1); int min_frame_num = 1; int max_frame_num = num_frames; const int min_bed_num = 0; - const int max_bed_num = num_bed_poss-1; + const int max_bed_num = num_bed_poss - 1; const int gate_num = 1; const int data_num = 0; - std::vector > frame_times; + std::vector> frame_times; - for (int frame_num=min_frame_num; frame_num<=max_frame_num;++frame_num) - for (int bed_num=min_bed_num; bed_num<=max_bed_num;++bed_num) - { - const int matnum = mat_numcod (frame_num, 1, gate_num, data_num, bed_num); - MatrixData* matrix = matrix_read( mptr, matnum, MAT_SUB_HEADER); - - if (matrix==NULL) - { - warning("TimeFrameDefinitions: Matrix not found at \"%d,1,%d,%d,%d\" in file \"%s\"\n.", - frame_num, 1, gate_num, data_num, bed_num, mptr->fname); - continue; - } - - switch (mptr->mhptr->file_type) - { - case PetImage: - case ByteVolume: - case PetVolume: - { - Image_subheader *sheader_ptr= - reinterpret_cast(matrix->shptr); - frame_times.push_back(std::make_pair(sheader_ptr->frame_start_time/1000., - sheader_ptr->frame_start_time/1000. - + sheader_ptr->frame_duration/1000.)); - - break; - } - case Byte3dSinogram: - case Short3dSinogram: - case Float3dSinogram : - { - Scan3D_subheader *sheader_ptr= - reinterpret_cast(matrix->shptr); - frame_times.push_back(std::make_pair(sheader_ptr->frame_start_time/1000., - sheader_ptr->frame_start_time/1000. - + sheader_ptr->frame_duration/1000.)); - - break; - } - case CTISinogram : - { - Scan_subheader *sheader_ptr= - reinterpret_cast(matrix->shptr); - frame_times.push_back(std::make_pair(sheader_ptr->frame_start_time/1000., - sheader_ptr->frame_start_time/1000. - + sheader_ptr->frame_duration/1000.)); - - break; - } - default: - { - // can't do anything here - } - } - free_matrix_data(matrix); + for (int frame_num = min_frame_num; frame_num <= max_frame_num; ++frame_num) + for (int bed_num = min_bed_num; bed_num <= max_bed_num; ++bed_num) { + const int matnum = mat_numcod(frame_num, 1, gate_num, data_num, bed_num); + MatrixData* matrix = matrix_read(mptr, matnum, MAT_SUB_HEADER); + + if (matrix == NULL) { + warning("TimeFrameDefinitions: Matrix not found at \"%d,1,%d,%d,%d\" in file \"%s\"\n.", frame_num, 1, gate_num, data_num, + bed_num, mptr->fname); + continue; + } + + switch (mptr->mhptr->file_type) { + case PetImage: + case ByteVolume: + case PetVolume: { + Image_subheader* sheader_ptr = reinterpret_cast(matrix->shptr); + frame_times.push_back(std::make_pair(sheader_ptr->frame_start_time / 1000., + sheader_ptr->frame_start_time / 1000. + sheader_ptr->frame_duration / 1000.)); + + break; + } + case Byte3dSinogram: + case Short3dSinogram: + case Float3dSinogram: { + Scan3D_subheader* sheader_ptr = reinterpret_cast(matrix->shptr); + frame_times.push_back(std::make_pair(sheader_ptr->frame_start_time / 1000., + sheader_ptr->frame_start_time / 1000. + sheader_ptr->frame_duration / 1000.)); + + break; } + case CTISinogram: { + Scan_subheader* sheader_ptr = reinterpret_cast(matrix->shptr); + frame_times.push_back(std::make_pair(sheader_ptr->frame_start_time / 1000., + sheader_ptr->frame_start_time / 1000. + sheader_ptr->frame_duration / 1000.)); + + break; + } + default: { + // can't do anything here + } + } + free_matrix_data(matrix); + } TimeFrameDefinitions time_frame_defs(frame_times); ExamInfo exam_info; @@ -597,75 +514,76 @@ read_ECAT7_exam_info(MatrixFile *mptr) exam_info.set_time_frame_definitions(time_frame_defs); exam_info.start_time_in_secs_since_1970 = double(mptr->mhptr->scan_start_time); - switch(mptr->mhptr->patient_orientation) - { - case FeetFirstProne: - exam_info.patient_position = PatientPosition(PatientPosition::FFP); break; - case HeadFirstProne: - exam_info.patient_position = PatientPosition(PatientPosition::HFP); break; - case FeetFirstSupine: - exam_info.patient_position = PatientPosition(PatientPosition::FFS); break; - case HeadFirstSupine: - exam_info.patient_position = PatientPosition(PatientPosition::HFS); break; - case FeetFirstRight: - exam_info.patient_position = PatientPosition(PatientPosition::FFDR); break; - case HeadFirstRight: - exam_info.patient_position = PatientPosition(PatientPosition::HFDR); break; - case FeetFirstLeft: - exam_info.patient_position = PatientPosition(PatientPosition::FFDL); break; - case HeadFirstLeft: - exam_info.patient_position = PatientPosition(PatientPosition::HFDL); break; - case UnknownOrientation: - default: - exam_info.patient_position = PatientPosition(PatientPosition::unknown_position); break; - } + switch (mptr->mhptr->patient_orientation) { + case FeetFirstProne: + exam_info.patient_position = PatientPosition(PatientPosition::FFP); + break; + case HeadFirstProne: + exam_info.patient_position = PatientPosition(PatientPosition::HFP); + break; + case FeetFirstSupine: + exam_info.patient_position = PatientPosition(PatientPosition::FFS); + break; + case HeadFirstSupine: + exam_info.patient_position = PatientPosition(PatientPosition::HFS); + break; + case FeetFirstRight: + exam_info.patient_position = PatientPosition(PatientPosition::FFDR); + break; + case HeadFirstRight: + exam_info.patient_position = PatientPosition(PatientPosition::HFDR); + break; + case FeetFirstLeft: + exam_info.patient_position = PatientPosition(PatientPosition::FFDL); + break; + case HeadFirstLeft: + exam_info.patient_position = PatientPosition(PatientPosition::HFDL); + break; + case UnknownOrientation: + default: + exam_info.patient_position = PatientPosition(PatientPosition::unknown_position); + break; + } shared_ptr exam_info_sptr(new ExamInfo(exam_info)); return exam_info_sptr; } shared_ptr -read_ECAT7_exam_info(const string& filename) -{ - MatrixFile * const mptr = - matrix_open( filename.c_str(), MAT_READ_ONLY, MAT_UNKNOWN_FTYPE); - if (!mptr) - { - matrix_perror( filename.c_str()); - error("Error reading ECAT7 file"); - } +read_ECAT7_exam_info(const string& filename) { + MatrixFile* const mptr = matrix_open(filename.c_str(), MAT_READ_ONLY, MAT_UNKNOWN_FTYPE); + if (!mptr) { + matrix_perror(filename.c_str()); + error("Error reading ECAT7 file"); + } shared_ptr exam_info_sptr(read_ECAT7_exam_info(mptr)); matrix_close(mptr); return exam_info_sptr; } -void make_ECAT7_main_header(Main_header& mhead, - Scanner const& scanner, - const string& orig_name, - ExamInfo const& exam_info - ) -{ +void +make_ECAT7_main_header(Main_header& mhead, Scanner const& scanner, const string& orig_name, ExamInfo const& exam_info) { // first set to default (sometimes nonsensical) values strcpy(mhead.magic_number, "MATRIX7.0"); // TODO check fill_string(mhead.original_file_name, 32); - mhead.sw_version= V7; - mhead.system_type= -1; - mhead.file_type= -1; - fill_string(mhead.serial_number,10); - mhead.scan_start_time= 0; + mhead.sw_version = V7; + mhead.system_type = -1; + mhead.file_type = -1; + fill_string(mhead.serial_number, 10); + mhead.scan_start_time = 0; fill_string(mhead.isotope_code, 8); - mhead.isotope_halflife= 0.F; + mhead.isotope_halflife = 0.F; fill_string(mhead.radiopharmaceutical, 32); - mhead.gantry_tilt= 0.F; - mhead.gantry_rotation= 0.F; - mhead.bed_elevation= 0.F; + mhead.gantry_tilt = 0.F; + mhead.gantry_rotation = 0.F; + mhead.bed_elevation = 0.F; mhead.intrinsic_tilt = 0; - mhead.wobble_speed= 0; - mhead.transm_source_type= -1; + mhead.wobble_speed = 0; + mhead.transm_source_type = -1; mhead.distance_scanned = -1.F; - mhead.transaxial_fov= -1.F; + mhead.transaxial_fov = -1.F; mhead.angular_compression = -1; - mhead.calibration_factor= 0.F; + mhead.calibration_factor = 0.F; mhead.calibration_units = 0; mhead.calibration_units_label = 0; mhead.compression_code = 0; @@ -677,157 +595,143 @@ void make_ECAT7_main_header(Main_header& mhead, mhead.patient_age = 0.F; mhead.patient_height = 0.F; mhead.patient_weight = 0.F; - mhead.patient_birth_date=1; - fill_string(mhead.physician_name,32); - fill_string(mhead.operator_name,32); - fill_string(mhead.study_description,32); - mhead.acquisition_type = 0; + mhead.patient_birth_date = 1; + fill_string(mhead.physician_name, 32); + fill_string(mhead.operator_name, 32); + fill_string(mhead.study_description, 32); + mhead.acquisition_type = 0; mhead.coin_samp_mode = 0; // default to net_trues - mhead.axial_samp_mode= 0; + mhead.axial_samp_mode = 0; mhead.patient_orientation = HeadFirstSupine; fill_string(mhead.facility_name, 20); - mhead.num_planes= 0; - mhead.num_frames= 1; // used for matnum, so set coherent default values - mhead.num_gates= 1; - mhead.num_bed_pos= 0; - mhead.init_bed_position= -1.F; - for (int i=0; i<15; i++) mhead.bed_offset[i]= 0.F; - mhead.plane_separation= -1.F; - mhead.lwr_sctr_thres= 0; // WARNING: default setup for the 966 - mhead.lwr_true_thres= 350; // WARNING: default setup for the 966 - mhead.upr_true_thres= 650; // WARNING: default setup for the 966 + mhead.num_planes = 0; + mhead.num_frames = 1; // used for matnum, so set coherent default values + mhead.num_gates = 1; + mhead.num_bed_pos = 0; + mhead.init_bed_position = -1.F; + for (int i = 0; i < 15; i++) + mhead.bed_offset[i] = 0.F; + mhead.plane_separation = -1.F; + mhead.lwr_sctr_thres = 0; // WARNING: default setup for the 966 + mhead.lwr_true_thres = 350; // WARNING: default setup for the 966 + mhead.upr_true_thres = 650; // WARNING: default setup for the 966 fill_string(mhead.user_process_code, 10); - mhead.acquisition_mode= 0; // default to NORMAL + mhead.acquisition_mode = 0; // default to NORMAL mhead.bin_size = -1.F; mhead.branching_fraction = -1.F; mhead.dose_start_time = 0; mhead.dosage = 0.F; mhead.well_counter_factor = 1.F; - fill_string(mhead.data_units,32); - mhead.septa_state= -1; - + fill_string(mhead.data_units, 32); + mhead.septa_state = -1; + // now fill in what we can mhead.calibration_factor = 1.F; - mhead.well_counter_factor=1.F; + mhead.well_counter_factor = 1.F; strncpy(mhead.original_file_name, orig_name.c_str(), 31); - mhead.original_file_name[31]='\0'; - mhead.num_frames= 1; - - mhead.system_type= find_ECAT_system_type(scanner); - mhead.transaxial_fov= - scanner.get_inner_ring_radius()*2* - static_cast(sin(_PI/scanner.get_num_detectors_per_ring()* - scanner.get_max_num_non_arccorrected_bins()/2.)/10); + mhead.original_file_name[31] = '\0'; + mhead.num_frames = 1; + + mhead.system_type = find_ECAT_system_type(scanner); + mhead.transaxial_fov = + scanner.get_inner_ring_radius() * 2 * + static_cast(sin(_PI / scanner.get_num_detectors_per_ring() * scanner.get_max_num_non_arccorrected_bins() / 2.) / 10); mhead.intrinsic_tilt = scanner.get_default_intrinsic_tilt(); - mhead.bin_size = scanner.get_default_bin_size()/10; - mhead.plane_separation= scanner.get_ring_spacing()/2/10; + mhead.bin_size = scanner.get_default_bin_size() / 10; + mhead.plane_separation = scanner.get_ring_spacing() / 2 / 10; mhead.intrinsic_tilt = scanner.get_default_intrinsic_tilt(); - - mhead.distance_scanned= - mhead.plane_separation * scanner.get_num_rings()*2; + + mhead.distance_scanned = mhead.plane_separation * scanner.get_num_rings() * 2; mhead.num_frames = exam_info.time_frame_definitions.get_num_frames(); mhead.scan_start_time = static_cast(floor(exam_info.start_time_in_secs_since_1970)); - switch(exam_info.patient_position.get_position()) - { - case PatientPosition::FFP: - mhead.patient_orientation = FeetFirstProne; break; - case PatientPosition::HFP: - mhead.patient_orientation = HeadFirstProne; break; - case PatientPosition::FFS: - mhead.patient_orientation = FeetFirstSupine; break; - case PatientPosition::HFS: - mhead.patient_orientation = HeadFirstSupine; break; - case PatientPosition::FFDR: - mhead.patient_orientation = FeetFirstRight; break; - case PatientPosition::HFDR: - mhead.patient_orientation = HeadFirstRight; break; - case PatientPosition::FFDL: - mhead.patient_orientation = FeetFirstLeft; break; - case PatientPosition::HFDL: - mhead.patient_orientation = HeadFirstLeft; break; - default: - mhead.patient_orientation = UnknownOrientation; break; - } - + switch (exam_info.patient_position.get_position()) { + case PatientPosition::FFP: + mhead.patient_orientation = FeetFirstProne; + break; + case PatientPosition::HFP: + mhead.patient_orientation = HeadFirstProne; + break; + case PatientPosition::FFS: + mhead.patient_orientation = FeetFirstSupine; + break; + case PatientPosition::HFS: + mhead.patient_orientation = HeadFirstSupine; + break; + case PatientPosition::FFDR: + mhead.patient_orientation = FeetFirstRight; + break; + case PatientPosition::HFDR: + mhead.patient_orientation = HeadFirstRight; + break; + case PatientPosition::FFDL: + mhead.patient_orientation = FeetFirstLeft; + break; + case PatientPosition::HFDL: + mhead.patient_orientation = HeadFirstLeft; + break; + default: + mhead.patient_orientation = UnknownOrientation; + break; + } } -void make_ECAT7_main_header(Main_header& mhead, - Scanner const& scanner, - const string& orig_name, - DiscretisedDensity<3,float> const & density - ) -{ +void +make_ECAT7_main_header(Main_header& mhead, Scanner const& scanner, const string& orig_name, + DiscretisedDensity<3, float> const& density) { make_ECAT7_main_header(mhead, scanner, orig_name, density.get_exam_info()); - - DiscretisedDensityOnCartesianGrid<3,float> const & image = - dynamic_cast const&>(density); - - // extra main parameters that depend on data type - mhead.file_type= PetVolume; - mhead.num_planes=image.get_length(); - mhead.plane_separation=image.get_grid_spacing()[1]/10; // convert to cm + DiscretisedDensityOnCartesianGrid<3, float> const& image = + dynamic_cast const&>(density); + // extra main parameters that depend on data type + mhead.file_type = PetVolume; + mhead.num_planes = image.get_length(); + mhead.plane_separation = image.get_grid_spacing()[1] / 10; // convert to cm } -static short find_angular_compression(const ProjDataInfo& proj_data_info) -{ +static short +find_angular_compression(const ProjDataInfo& proj_data_info) { // try to convert to cylindrical ProjDataInfo // use pointer such that we can check if it worked (without catching exceptions) - ProjDataInfoCylindrical const * const proj_data_info_cyl_ptr = - dynamic_cast(&proj_data_info); - if (proj_data_info_cyl_ptr!=0) - { - const int mash_factor = - proj_data_info_cyl_ptr->get_view_mashing_factor(); - if (mash_factor>1 && mash_factor%2==1) - { - warning("ECAT7::find_angular_compression: odd mash factor %d is not supported by CTI header. " - "Using a value of 0\n", mash_factor); - return static_cast(0); - } - else - return static_cast(mash_factor/2); - } - else - { - warning("ECAT7::find_angular_compression: proj data info does not correspond to a cylindrical scanner. " - "Using a value of 0\n"); + ProjDataInfoCylindrical const* const proj_data_info_cyl_ptr = dynamic_cast(&proj_data_info); + if (proj_data_info_cyl_ptr != 0) { + const int mash_factor = proj_data_info_cyl_ptr->get_view_mashing_factor(); + if (mash_factor > 1 && mash_factor % 2 == 1) { + warning("ECAT7::find_angular_compression: odd mash factor %d is not supported by CTI header. " + "Using a value of 0\n", + mash_factor); return static_cast(0); - } - + } else + return static_cast(mash_factor / 2); + } else { + warning("ECAT7::find_angular_compression: proj data info does not correspond to a cylindrical scanner. " + "Using a value of 0\n"); + return static_cast(0); + } } -static short find_axial_compression(const ProjDataInfo& proj_data_info) -{ +static short +find_axial_compression(const ProjDataInfo& proj_data_info) { int axial_compression = 0; // try to convert to cylindrical ProjDataInfo // use pointer such that we can check if it worked (without catching exceptions) - ProjDataInfoCylindrical const * const proj_data_info_cyl_ptr = - dynamic_cast(&proj_data_info); - if (proj_data_info_cyl_ptr!=0) - { + ProjDataInfoCylindrical const* const proj_data_info_cyl_ptr = dynamic_cast(&proj_data_info); + if (proj_data_info_cyl_ptr != 0) { axial_compression = - proj_data_info_cyl_ptr->get_max_ring_difference(0) - - proj_data_info_cyl_ptr->get_min_ring_difference(0) + 1; - for (int segment_num = proj_data_info.get_min_segment_num(); - segment_num <= proj_data_info.get_max_segment_num(); - ++segment_num) - { - const int this_segments_axial_compression = - proj_data_info_cyl_ptr->get_max_ring_difference(segment_num) - - proj_data_info_cyl_ptr->get_min_ring_difference(segment_num) + 1; + proj_data_info_cyl_ptr->get_max_ring_difference(0) - proj_data_info_cyl_ptr->get_min_ring_difference(0) + 1; + for (int segment_num = proj_data_info.get_min_segment_num(); segment_num <= proj_data_info.get_max_segment_num(); + ++segment_num) { + const int this_segments_axial_compression = proj_data_info_cyl_ptr->get_max_ring_difference(segment_num) - + proj_data_info_cyl_ptr->get_min_ring_difference(segment_num) + 1; if (axial_compression != this_segments_axial_compression) error("ECAT 7 file format does not support data with non-uniform angular compression. " "Segment %d has angular compression %d while segment 0 has %d\n", segment_num, this_segments_axial_compression, axial_compression); } - } - else - { + } else { axial_compression = 1; warning("ECAT 7 file format used with non-cylindrical ProjDataInfo type. " "I set axial_compression to 1, but who knows what will happen?"); @@ -835,105 +739,83 @@ static short find_axial_compression(const ProjDataInfo& proj_data_info) return static_cast(axial_compression); } +NumericType +make_ECAT7_main_header(Main_header& mhead, const string& orig_name, ExamInfo const& exam_info, ProjDataInfo const& proj_data_info, + const bool write_as_attenuation, NumericType output_type) { -NumericType -make_ECAT7_main_header(Main_header& mhead, - const string& orig_name, - ExamInfo const & exam_info, - ProjDataInfo const & proj_data_info, - const bool write_as_attenuation, - NumericType output_type - ) -{ - make_ECAT7_main_header(mhead, *proj_data_info.get_scanner_ptr(), orig_name, exam_info); - - mhead.acquisition_type = - mhead.num_frames>1 ? DynamicEmission : StaticEmission; - // extra main parameters that depend on data type - - mhead.num_planes = 0; - for(int segment_num=proj_data_info.get_min_segment_num(); - segment_num <= proj_data_info.get_max_segment_num(); - ++segment_num) - mhead.num_planes+= proj_data_info.get_num_axial_poss(segment_num); - - - const float natural_bin_size = - proj_data_info.get_sampling_in_s(Bin(0,0,0,0)); - const float default_bin_size = - proj_data_info.get_scanner_ptr()->get_default_bin_size(); + mhead.acquisition_type = mhead.num_frames > 1 ? DynamicEmission : StaticEmission; - if (fabs(natural_bin_size - default_bin_size)>.02 && - dynamic_cast(&proj_data_info) == 0) - { - warning("CTI default bin size (%g) differs from STIR sampling in s (%g)\n" - "for this data. Using default bin size for field main header anyway.\n" - "However, you better check this out, especially for arc-corrected data.", - default_bin_size, natural_bin_size); - } - mhead.bin_size = default_bin_size/10; + // extra main parameters that depend on data type + mhead.num_planes = 0; + for (int segment_num = proj_data_info.get_min_segment_num(); segment_num <= proj_data_info.get_max_segment_num(); ++segment_num) + mhead.num_planes += proj_data_info.get_num_axial_poss(segment_num); + + const float natural_bin_size = proj_data_info.get_sampling_in_s(Bin(0, 0, 0, 0)); + const float default_bin_size = proj_data_info.get_scanner_ptr()->get_default_bin_size(); + + if (fabs(natural_bin_size - default_bin_size) > .02 && + dynamic_cast(&proj_data_info) == 0) { + warning("CTI default bin size (%g) differs from STIR sampling in s (%g)\n" + "for this data. Using default bin size for field main header anyway.\n" + "However, you better check this out, especially for arc-corrected data.", + default_bin_size, natural_bin_size); + } + mhead.bin_size = default_bin_size / 10; mhead.angular_compression = find_angular_compression(proj_data_info); // guess septa state // assume that if it has more than 1 segment, it's a 3D scan... // except for some scanners without septa - switch(proj_data_info.get_scanner_ptr()->get_type()) - { - case Scanner::E966: - case Scanner::E925: - case Scanner::RATPET: - mhead.septa_state= NoSeptaInstalled; - break; - default: - mhead.septa_state= - proj_data_info.get_num_segments()==1 - ? SeptaExtended - : SeptaRetracted; - } - + switch (proj_data_info.get_scanner_ptr()->get_type()) { + case Scanner::E966: + case Scanner::E925: + case Scanner::RATPET: + mhead.septa_state = NoSeptaInstalled; + break; + default: + mhead.septa_state = proj_data_info.get_num_segments() == 1 ? SeptaExtended : SeptaRetracted; + } - if (write_as_attenuation) - { - mhead.file_type = AttenCor; - mhead.acquisition_type = TransmissionScan; - if (output_type != NumericType::FLOAT) - { - warning("make_ECAT7_main_header: attenuation file will be written as floats " - "to avoid problems with CTI utilities"); - output_type = NumericType::FLOAT; - } - + if (write_as_attenuation) { + mhead.file_type = AttenCor; + mhead.acquisition_type = TransmissionScan; + if (output_type != NumericType::FLOAT) { + warning("make_ECAT7_main_header: attenuation file will be written as floats " + "to avoid problems with CTI utilities"); + output_type = NumericType::FLOAT; } - else - { - mhead.acquisition_type = StaticEmission; - switch (output_type.id) - { - case NumericType::FLOAT: - mhead.file_type = Float3dSinogram; break; - case NumericType::SHORT: - mhead.file_type = Short3dSinogram; break; - case NumericType::SCHAR: - mhead.file_type = Byte3dSinogram; break; - default: - warning("make_ECAT7_main_header: output type is not supported by ECAT7 format. Will use floats"); - mhead.file_type = Float3dSinogram; - output_type = NumericType::FLOAT; - break; - } + + } else { + mhead.acquisition_type = StaticEmission; + switch (output_type.id) { + case NumericType::FLOAT: + mhead.file_type = Float3dSinogram; + break; + case NumericType::SHORT: + mhead.file_type = Short3dSinogram; + break; + case NumericType::SCHAR: + mhead.file_type = Byte3dSinogram; + break; + default: + warning("make_ECAT7_main_header: output type is not supported by ECAT7 format. Will use floats"); + mhead.file_type = Float3dSinogram; + output_type = NumericType::FLOAT; + break; } + } return output_type; } // A utility function only called by scan_subheader_zero_fill /* - \internal + \internal - Most of the names of the variables we need are the same in the + Most of the names of the variables we need are the same in the Scan3D or Attn subheader, except num_z_elements and span. So, instead of writing essentially the same function twice, we use a templated version. Note that this takes care of the @@ -942,9 +824,9 @@ make_ECAT7_main_header(Main_header& mhead, */ template -static void scan_subheader_zero_fill_aux(Subheader& shead) -{ - shead.data_type= -1; +static void +scan_subheader_zero_fill_aux(Subheader& shead) { + shead.data_type = -1; shead.num_dimensions = -1; shead.num_r_elements = -1; shead.num_angles = -1; @@ -953,42 +835,44 @@ static void scan_subheader_zero_fill_aux(Subheader& shead) shead.x_resolution = -1.F; shead.z_resolution = -1.F; shead.w_resolution = -1.F; - shead.scale_factor= -1.F; + shead.scale_factor = -1.F; } -void scan_subheader_zero_fill(Scan3D_subheader& shead) -{ +void +scan_subheader_zero_fill(Scan3D_subheader& shead) { scan_subheader_zero_fill_aux(shead); shead.v_resolution = -1.F; shead.corrections_applied = 0; - for (int i=0; i<64; ++i) shead.num_z_elements[i] = -1; + for (int i = 0; i < 64; ++i) + shead.num_z_elements[i] = -1; shead.axial_compression = -1; - shead.gate_duration= 0; - shead.r_wave_offset= -1; + shead.gate_duration = 0; + shead.r_wave_offset = -1; shead.num_accepted_beats = -1; - shead.scan_min= -1; - shead.scan_max= -1; - shead.prompts= -1; - shead.delayed= -1; - shead.multiples= -1; - shead.net_trues= -1; - shead.tot_avg_cor= -1.F; - shead.tot_avg_uncor= -1.F; - shead.total_coin_rate= -1; - shead.frame_start_time= 0; - shead.frame_duration= 0; + shead.scan_min = -1; + shead.scan_max = -1; + shead.prompts = -1; + shead.delayed = -1; + shead.multiples = -1; + shead.net_trues = -1; + shead.tot_avg_cor = -1.F; + shead.tot_avg_uncor = -1.F; + shead.total_coin_rate = -1; + shead.frame_start_time = 0; + shead.frame_duration = 0; shead.loss_correction_fctr = -1.F; - for(int i=0;i<128;++i) shead.uncor_singles[i] = -1.F; - + for (int i = 0; i < 128; ++i) + shead.uncor_singles[i] = -1.F; } -void scan_subheader_zero_fill(Attn_subheader& shead) -{ +void +scan_subheader_zero_fill(Attn_subheader& shead) { scan_subheader_zero_fill_aux(shead); shead.y_resolution = -1.F; shead.attenuation_type = 1; // default to measured shead.num_z_elements = -1; - for (int i=0; i<64; ++i) shead.z_elements[i] = -1; + for (int i = 0; i < 64; ++i) + shead.z_elements[i] = -1; shead.span = -1; shead.x_offset = -1.F; shead.y_offset = -1.F; @@ -1000,29 +884,30 @@ void scan_subheader_zero_fill(Attn_subheader& shead) shead.attenuation_max = -1.F; shead.skull_thickness = -1.F; shead.num_additional_atten_coeff = -1; - for (int i=0; i<8; ++i) shead.additional_atten_coeff[i] = -1.F; + for (int i = 0; i < 8; ++i) + shead.additional_atten_coeff[i] = -1.F; shead.edge_finding_threshold = -1.F; } -void img_subheader_zero_fill(Image_subheader & ihead) -{ - ihead.data_type= -1; - ihead.num_dimensions= 3; - ihead.x_dimension= -1; - ihead.y_dimension= -1; - ihead.z_dimension= -1; - ihead.x_offset= 0.F; - ihead.y_offset= 0.F; - ihead.z_offset= 0.F; - ihead.recon_zoom= -1.F; - ihead.scale_factor= -1.F; - ihead.image_min= -1; - ihead.image_max= -1; - ihead.x_pixel_size= -1.F; - ihead.y_pixel_size= -1.F; - ihead.z_pixel_size= -1.F; - ihead.frame_duration= 0; - ihead.frame_start_time= 0; +void +img_subheader_zero_fill(Image_subheader& ihead) { + ihead.data_type = -1; + ihead.num_dimensions = 3; + ihead.x_dimension = -1; + ihead.y_dimension = -1; + ihead.z_dimension = -1; + ihead.x_offset = 0.F; + ihead.y_offset = 0.F; + ihead.z_offset = 0.F; + ihead.recon_zoom = -1.F; + ihead.scale_factor = -1.F; + ihead.image_min = -1; + ihead.image_max = -1; + ihead.x_pixel_size = -1.F; + ihead.y_pixel_size = -1.F; + ihead.z_pixel_size = -1.F; + ihead.frame_duration = 0; + ihead.frame_start_time = 0; ihead.filter_code = -1; ihead.x_resolution = -1.F; ihead.y_resolution = -1.F; @@ -1030,8 +915,8 @@ void img_subheader_zero_fill(Image_subheader & ihead) ihead.num_r_elements = -1; ihead.num_angles = -1; ihead.z_rotation_angle = -1; - ihead.decay_corr_fctr= -1.F; - ihead.processing_code= -1; + ihead.decay_corr_fctr = -1.F; + ihead.processing_code = -1; ihead.gate_duration = 0; ihead.r_wave_offset = -1; ihead.num_accepted_beats = -1; @@ -1064,12 +949,12 @@ void img_subheader_zero_fill(Image_subheader & ihead) ihead.scatter_type = -1; ihead.recon_type = -1; ihead.recon_views = -1; - fill_string(ihead.annotation, 40); + fill_string(ihead.annotation, 40); } //! A utility function to set time frame info in a subheader /*! - \internal + \internal Names of the variables for time frame info in the subheaders are the same. So, instead of writing essentially the same function twice, we @@ -1081,26 +966,20 @@ void img_subheader_zero_fill(Image_subheader & ihead) where the subheader is written). */ template -static void set_time_frame_info(SUBHEADERPTR sub_header_ptr, - const Main_header& mhead, - const ExamInfo& exam_info, - const unsigned frame_num) -{ - const double frame_start_time = - exam_info.get_time_frame_definitions().get_start_time(frame_num) - + exam_info.start_time_in_secs_since_1970 - mhead.scan_start_time; - const double frame_duration = - exam_info.get_time_frame_definitions().get_duration(frame_num); - sub_header_ptr->frame_start_time = static_cast(round(frame_start_time*1000.)); - sub_header_ptr->frame_duration = static_cast(round(frame_duration*1000.)); +static void +set_time_frame_info(SUBHEADERPTR sub_header_ptr, const Main_header& mhead, const ExamInfo& exam_info, const unsigned frame_num) { + const double frame_start_time = exam_info.get_time_frame_definitions().get_start_time(frame_num) + + exam_info.start_time_in_secs_since_1970 - mhead.scan_start_time; + const double frame_duration = exam_info.get_time_frame_definitions().get_duration(frame_num); + sub_header_ptr->frame_start_time = static_cast(round(frame_start_time * 1000.)); + sub_header_ptr->frame_duration = static_cast(round(frame_duration * 1000.)); } - //! A utility function only called by make_subheader_for_ECAT7(..., ProjDataInfo&) /*! - \internal + \internal - Most of the names of the variables we need are the same in the + Most of the names of the variables we need are the same in the Scan3D or Attn subheader, except num_z_elements and span. So, instead of writing essentially the same function twice, we use a templated version. Note that this takes care of the @@ -1111,142 +990,92 @@ static void set_time_frame_info(SUBHEADERPTR sub_header_ptr, */ template static void -make_subheader_for_ECAT7_aux(SUBHEADERPTR sub_header_ptr, - short * num_z_elements, - short& span, - const Main_header& mhead, - const ProjDataInfo& proj_data_info - ) -{ +make_subheader_for_ECAT7_aux(SUBHEADERPTR sub_header_ptr, short* num_z_elements, short& span, const Main_header& mhead, + const ProjDataInfo& proj_data_info) { scan_subheader_zero_fill(*sub_header_ptr); sub_header_ptr->num_dimensions = 4; sub_header_ptr->num_r_elements = proj_data_info.get_num_tangential_poss(); sub_header_ptr->num_angles = proj_data_info.get_num_views(); - - if (proj_data_info.get_max_segment_num() != - -proj_data_info.get_min_segment_num()) + + if (proj_data_info.get_max_segment_num() != -proj_data_info.get_min_segment_num()) error("ECAT 7 file format can only handle data with max_segment_num == -min_segment_num\n"); span = find_axial_compression(proj_data_info); - + if (proj_data_info.get_max_segment_num() > 64) error("ECAT 7 file format supports only a maximum segment number of 64 while this data has %d\n", proj_data_info.get_max_segment_num()); num_z_elements[0] = static_cast(proj_data_info.get_num_axial_poss(0)); - for (int segment_num=1; segment_num<=proj_data_info.get_max_segment_num(); ++segment_num) - { - num_z_elements[segment_num] = - static_cast(2*proj_data_info.get_num_axial_poss(segment_num)); + for (int segment_num = 1; segment_num <= proj_data_info.get_max_segment_num(); ++segment_num) { + num_z_elements[segment_num] = static_cast(2 * proj_data_info.get_num_axial_poss(segment_num)); } - for (int i=proj_data_info.get_max_segment_num()+1; i<64; ++i) + for (int i = proj_data_info.get_max_segment_num() + 1; i < 64; ++i) num_z_elements[i] = 0; - + // try to convert to cylindrical ProjDataInfo // use pointer such that we can check if it worked (without catching exceptions) - const ProjDataInfoCylindrical * const proj_data_info_cyl_ptr = - dynamic_cast(&proj_data_info); - if (proj_data_info_cyl_ptr!=0) - { - sub_header_ptr->ring_difference = - proj_data_info_cyl_ptr->get_max_ring_difference(proj_data_info.get_max_segment_num()); + const ProjDataInfoCylindrical* const proj_data_info_cyl_ptr = + dynamic_cast(&proj_data_info); + if (proj_data_info_cyl_ptr != 0) { + sub_header_ptr->ring_difference = proj_data_info_cyl_ptr->get_max_ring_difference(proj_data_info.get_max_segment_num()); + } else { + sub_header_ptr->ring_difference = -1; } - else - { - sub_header_ptr->ring_difference = -1; - } - float x_resolution; - const Scanner& scanner = - *proj_data_info.get_scanner_ptr(); - if (dynamic_cast(&proj_data_info) != 0) - { - const float depth_of_interaction_factor = - 1 + - scanner.get_average_depth_of_interaction() / - scanner.get_inner_ring_radius(); - x_resolution = - proj_data_info.get_sampling_in_s(Bin(0,0,0,0))/ - depth_of_interaction_factor; - if (fabs(x_resolution - scanner.get_default_bin_size()) > .01) - { - warning("ECAT7 IO: Bin size derived from data (%g) does not agree with expected value %g\n" - "for scanner %s. Using default bin size for header.x_resolution...", - x_resolution, - scanner.get_default_bin_size(), - scanner.get_name().c_str()); - } - // always use default because there's a small discrepancy between the - // default bin size and the value derived from the ring radius etc - x_resolution = scanner.get_default_bin_size(); + const Scanner& scanner = *proj_data_info.get_scanner_ptr(); + if (dynamic_cast(&proj_data_info) != 0) { + const float depth_of_interaction_factor = 1 + scanner.get_average_depth_of_interaction() / scanner.get_inner_ring_radius(); + x_resolution = proj_data_info.get_sampling_in_s(Bin(0, 0, 0, 0)) / depth_of_interaction_factor; + if (fabs(x_resolution - scanner.get_default_bin_size()) > .01) { + warning("ECAT7 IO: Bin size derived from data (%g) does not agree with expected value %g\n" + "for scanner %s. Using default bin size for header.x_resolution...", + x_resolution, scanner.get_default_bin_size(), scanner.get_name().c_str()); } - else - { - x_resolution = - proj_data_info.get_sampling_in_s(Bin(0,0,0,0)); - if (fabs(x_resolution - scanner.get_default_bin_size()) > .01) - { - warning("ECAT7 IO: Bin size derived from data (%g) does not agree with expected value %g\n" - "for scanner %s. Using data-derived value for header.x_resolution...", - x_resolution, - scanner.get_default_bin_size(), - scanner.get_name().c_str()); - } + // always use default because there's a small discrepancy between the + // default bin size and the value derived from the ring radius etc + x_resolution = scanner.get_default_bin_size(); + } else { + x_resolution = proj_data_info.get_sampling_in_s(Bin(0, 0, 0, 0)); + if (fabs(x_resolution - scanner.get_default_bin_size()) > .01) { + warning("ECAT7 IO: Bin size derived from data (%g) does not agree with expected value %g\n" + "for scanner %s. Using data-derived value for header.x_resolution...", + x_resolution, scanner.get_default_bin_size(), scanner.get_name().c_str()); } - sub_header_ptr->x_resolution = x_resolution/10; + } + sub_header_ptr->x_resolution = x_resolution / 10; sub_header_ptr->storage_order = ElAxVwRd; - - } - // WARNING data_type has still to be set void -make_subheader_for_ECAT7(Attn_subheader& shead, - const Main_header& mhead, - const ProjDataInfo& proj_data_info - ) -{ - make_subheader_for_ECAT7_aux(&shead, shead.z_elements, shead.span, - mhead, proj_data_info); - if (dynamic_cast(&proj_data_info)) - { +make_subheader_for_ECAT7(Attn_subheader& shead, const Main_header& mhead, const ProjDataInfo& proj_data_info) { + make_subheader_for_ECAT7_aux(&shead, shead.z_elements, shead.span, mhead, proj_data_info); + if (dynamic_cast(&proj_data_info)) { warning("make_subheader_for_ECAT7: data is not arc-corrected but info is not available in CTI attenuation subheader\n"); } } - + // WARNING data_type has to be set void -make_subheader_for_ECAT7(Scan3D_subheader& shead, - const Main_header& mhead, - const ProjDataInfo& proj_data_info - ) -{ - make_subheader_for_ECAT7_aux(&shead, shead.num_z_elements, shead.axial_compression, - mhead, proj_data_info); +make_subheader_for_ECAT7(Scan3D_subheader& shead, const Main_header& mhead, const ProjDataInfo& proj_data_info) { + make_subheader_for_ECAT7_aux(&shead, shead.num_z_elements, shead.axial_compression, mhead, proj_data_info); // try to convert to cylindrical ProjDataInfo to check if it's arccorrected // use pointer such that we can check if it worked (without catching exceptions) - if (dynamic_cast(&proj_data_info)) - { + if (dynamic_cast(&proj_data_info)) { shead.corrections_applied = static_cast(ArcPrc); - } - else if (dynamic_cast(&proj_data_info)) - { + } else if (dynamic_cast(&proj_data_info)) { shead.corrections_applied = 0; - } - else - { + } else { warning("make_subheader_for_ECAT7: unknown type of proj_data_info. Setting data to arc-corrected anyway\n"); shead.corrections_applied = static_cast(ArcPrc); } - } - //! A utility function only called by make_pdfs_matrix() /*! - \internal + \internal - Most of the names of the variables we need are the same in the + Most of the names of the variables we need are the same in the Scan3D or Attn subheader, except num_z_elements and span. So, instead of writing essentially the same function twice, we use a templated version. Note that this takes care of the @@ -1256,34 +1085,24 @@ make_subheader_for_ECAT7(Scan3D_subheader& shead, Extra parameters are used when the names of the variables do not match. */ template -static -ProjDataFromStream * -make_pdfs_from_matrix_aux(SUBHEADERPTR sub_header_ptr, - short const * num_z_elements, - const int span, - const bool arc_corrected, - unsigned int frame_start_time, - unsigned int frame_duration, - MatrixFile * const mptr, - MatrixData * const matrix, - const ExamInfo& exam_info_whole_file, - const shared_ptr& stream_ptr) -{ +static ProjDataFromStream* +make_pdfs_from_matrix_aux(SUBHEADERPTR sub_header_ptr, short const* num_z_elements, const int span, const bool arc_corrected, + unsigned int frame_start_time, unsigned int frame_duration, MatrixFile* const mptr, + MatrixData* const matrix, const ExamInfo& exam_info_whole_file, + const shared_ptr& stream_ptr) { shared_ptr scanner_ptr; - find_scanner(scanner_ptr, *(mptr->mhptr)); - if (scanner_ptr->get_type() == Scanner::Unknown_scanner) - { + find_scanner(scanner_ptr, *(mptr->mhptr)); + if (scanner_ptr->get_type() == Scanner::Unknown_scanner) { warning("ECAT7 IO: Couldn't determine the scanner \n" - "(Main_header.system_type=%d), defaulting to 962.\n" - "This might give dramatic problems.\n", - mptr->mhptr->system_type); + "(Main_header.system_type=%d), defaulting to 962.\n" + "This might give dramatic problems.\n", + mptr->mhptr->system_type); scanner_ptr.reset(new Scanner(Scanner::E962)); } #ifdef B_JOINT_STIRGATE // zlong, 08-04-2004, add support for Unknown_scanner // we have no idea about the geometry, so, ask user. - if(scanner_ptr->get_type() == Scanner::Unknown_scanner) - { + if (scanner_ptr->get_type() == Scanner::Unknown_scanner) { warning("Joint Gate Stir project warning:\n"); warning("I have no idea about your scanner, please give me the scanner info.\n"); scanner_ptr.reset(Scanner::ask_parameters()); @@ -1291,48 +1110,41 @@ make_pdfs_from_matrix_aux(SUBHEADERPTR sub_header_ptr, #endif shared_ptr exam_info_sptr(new ExamInfo(exam_info_whole_file)); - if (frame_duration>0) - { - std::vector > frame_times; - frame_times.push_back(std::make_pair(frame_start_time/1000., - frame_start_time/1000. - + frame_duration/1000.)); - TimeFrameDefinitions time_frame_defs(frame_times); - exam_info_sptr->set_time_frame_definitions(time_frame_defs); - exam_info_sptr->start_time_in_secs_since_1970 = double(mptr->mhptr->scan_start_time); - } + if (frame_duration > 0) { + std::vector> frame_times; + frame_times.push_back(std::make_pair(frame_start_time / 1000., frame_start_time / 1000. + frame_duration / 1000.)); + TimeFrameDefinitions time_frame_defs(frame_times); + exam_info_sptr->set_time_frame_definitions(time_frame_defs); + exam_info_sptr->start_time_in_secs_since_1970 = double(mptr->mhptr->scan_start_time); + } - if(sub_header_ptr->num_dimensions != 4) + if (sub_header_ptr->num_dimensions != 4) warning("ECAT7 IO: Expected subheader.num_dimensions==4. Continuing..."); const int num_tangential_poss = sub_header_ptr->num_r_elements; const int num_views = sub_header_ptr->num_angles; // find maximum segment int max_segment_num = 0; - while(max_segment_num<64 && num_z_elements[max_segment_num+1] != 0) + while (max_segment_num < 64 && num_z_elements[max_segment_num + 1] != 0) ++max_segment_num; - - VectorWithOffset num_axial_poss_per_seg(-max_segment_num,max_segment_num); - + + VectorWithOffset num_axial_poss_per_seg(-max_segment_num, max_segment_num); + num_axial_poss_per_seg[0] = num_z_elements[0]; - for (int segment_num=1; segment_num<=max_segment_num; ++segment_num) - { - num_axial_poss_per_seg[-segment_num] = - num_axial_poss_per_seg[segment_num] = - num_z_elements[segment_num]/2; + for (int segment_num = 1; segment_num <= max_segment_num; ++segment_num) { + num_axial_poss_per_seg[-segment_num] = num_axial_poss_per_seg[segment_num] = num_z_elements[segment_num] / 2; } - + const int max_delta = sub_header_ptr->ring_difference; const float bin_size = sub_header_ptr->x_resolution * 10; // convert to mm const float scale_factor = sub_header_ptr->scale_factor; - + ProjDataFromStream::StorageOrder storage_order; - switch (sub_header_ptr->storage_order) - { + switch (sub_header_ptr->storage_order) { case ElVwAxRd: storage_order = ProjDataFromStream::Segment_AxialPos_View_TangPos; break; - + case ElAxVwRd: storage_order = ProjDataFromStream::Segment_View_AxialPos_TangPos; break; @@ -1344,177 +1156,121 @@ make_pdfs_from_matrix_aux(SUBHEADERPTR sub_header_ptr, ByteOrder byte_order; find_type_from_ECAT_data_type(data_type, byte_order, sub_header_ptr->data_type); - if (fabs(bin_size - scanner_ptr->get_default_bin_size())>.01) - { + if (fabs(bin_size - scanner_ptr->get_default_bin_size()) > .01) { warning("ECAT7 IO: Bin size from header.x_resolution (%g) does not agree with expected value %g\n" - "for scanner %s. Using bin size from header...", - bin_size, - scanner_ptr->get_default_bin_size(), - scanner_ptr->get_name().c_str()); + "for scanner %s. Using bin size from header...", + bin_size, scanner_ptr->get_default_bin_size(), scanner_ptr->get_name().c_str()); scanner_ptr->set_default_bin_size(bin_size); } // TODO more checks on FOV etc. int span_to_use = span; - if (span == 0) - { - if (num_z_elements[0] == scanner_ptr->get_num_rings()) - { - warning("\nECAT7 subheader says span=0, while span should be odd.\n" - "However, num_z_elements[0]==num_rings, so we'll asssume it's span=1\n"); - span_to_use = 1; - } - else - { - error("\nECAT7 subheader says span=0, while span should be odd.\n" - "Moreover, num_z_elements[0] (%d)!=num_rings (%d), so, I give up.\n", - num_z_elements[0], scanner_ptr->get_num_rings()); - } + if (span == 0) { + if (num_z_elements[0] == scanner_ptr->get_num_rings()) { + warning("\nECAT7 subheader says span=0, while span should be odd.\n" + "However, num_z_elements[0]==num_rings, so we'll asssume it's span=1\n"); + span_to_use = 1; + } else { + error("\nECAT7 subheader says span=0, while span should be odd.\n" + "Moreover, num_z_elements[0] (%d)!=num_rings (%d), so, I give up.\n", + num_z_elements[0], scanner_ptr->get_num_rings()); } - + } + shared_ptr pdi_ptr( - ProjDataInfo::ProjDataInfoCTI(scanner_ptr, span_to_use, max_delta, - num_views, num_tangential_poss, - arc_corrected)); - + ProjDataInfo::ProjDataInfoCTI(scanner_ptr, span_to_use, max_delta, num_views, num_tangential_poss, arc_corrected)); + pdi_ptr->set_num_axial_poss_per_segment(num_axial_poss_per_seg); - - std::vector segment_sequence_in_stream = - find_segment_sequence(*pdi_ptr); - + + std::vector segment_sequence_in_stream = find_segment_sequence(*pdi_ptr); + Matval matval; mat_numdoc(matrix->matnum, &matval); - const long offset_in_file = - offset_in_ECAT_file(mptr, - matval.frame, 1, matval.gate, matval.data, matval.bed, - 0, NULL); + const long offset_in_file = offset_in_ECAT_file(mptr, matval.frame, 1, matval.gate, matval.data, matval.bed, 0, NULL); // KT 14/05/2002 added error check - if (offset_in_ECAT_file<0) + if (offset_in_ECAT_file < 0) return 0; - - return new ProjDataFromStream (exam_info_sptr, pdi_ptr, stream_ptr, offset_in_file, - segment_sequence_in_stream, - storage_order, - data_type, - byte_order, - scale_factor); + return new ProjDataFromStream(exam_info_sptr, pdi_ptr, stream_ptr, offset_in_file, segment_sequence_in_stream, storage_order, + data_type, byte_order, scale_factor); } - -ProjDataFromStream * -make_pdfs_from_matrix(MatrixFile * const mptr, - MatrixData * const matrix, - const shared_ptr& stream_ptr) -{ +ProjDataFromStream* +make_pdfs_from_matrix(MatrixFile* const mptr, MatrixData* const matrix, const shared_ptr& stream_ptr) { shared_ptr exam_info_sptr(read_ECAT7_exam_info(mptr)); - switch (mptr->mhptr->file_type) - { - case AttenCor: - { - Attn_subheader const *sub_header_ptr= - reinterpret_cast(matrix->shptr); - - // CTI does not provide corrections_applied to check if the data - // is arc-corrected. Presumably its attenuation data is always - // arccorrected - const bool arc_corrected = true; - warning("Assuming data is arc-corrected (info not available in CTI attenuation subheader)\n"); - return - make_pdfs_from_matrix_aux(sub_header_ptr, - sub_header_ptr->z_elements, - sub_header_ptr->span, - arc_corrected, - 0U, 0U, // pass invalid frame_duration - mptr, matrix, *exam_info_sptr, stream_ptr); - } - case Byte3dSinogram: - case Short3dSinogram: - case Float3dSinogram : - { - Scan3D_subheader const * sub_header_ptr= - reinterpret_cast(matrix->shptr); - - ProcessingCode cti_processing_code = - static_cast(sub_header_ptr->corrections_applied); - - const bool arc_corrected = - (cti_processing_code & ArcPrc) != 0; - - return - make_pdfs_from_matrix_aux(sub_header_ptr, - sub_header_ptr->num_z_elements, - sub_header_ptr->axial_compression, - arc_corrected, - sub_header_ptr->frame_start_time, - sub_header_ptr->frame_duration, - mptr, matrix, *exam_info_sptr, stream_ptr); - } - default: - { - warning ("make_pdfs_from_matrix: unsupported file_type %d\n", - mptr->mhptr->file_type); - return NULL; - } - } + switch (mptr->mhptr->file_type) { + case AttenCor: { + Attn_subheader const* sub_header_ptr = reinterpret_cast(matrix->shptr); + + // CTI does not provide corrections_applied to check if the data + // is arc-corrected. Presumably its attenuation data is always + // arccorrected + const bool arc_corrected = true; + warning("Assuming data is arc-corrected (info not available in CTI attenuation subheader)\n"); + return make_pdfs_from_matrix_aux(sub_header_ptr, sub_header_ptr->z_elements, sub_header_ptr->span, arc_corrected, 0U, + 0U, // pass invalid frame_duration + mptr, matrix, *exam_info_sptr, stream_ptr); + } + case Byte3dSinogram: + case Short3dSinogram: + case Float3dSinogram: { + Scan3D_subheader const* sub_header_ptr = reinterpret_cast(matrix->shptr); + + ProcessingCode cti_processing_code = static_cast(sub_header_ptr->corrections_applied); + + const bool arc_corrected = (cti_processing_code & ArcPrc) != 0; + + return make_pdfs_from_matrix_aux(sub_header_ptr, sub_header_ptr->num_z_elements, sub_header_ptr->axial_compression, + arc_corrected, sub_header_ptr->frame_start_time, sub_header_ptr->frame_duration, mptr, + matrix, *exam_info_sptr, stream_ptr); + } + default: { + warning("make_pdfs_from_matrix: unsupported file_type %d\n", mptr->mhptr->file_type); + return NULL; + } + } } -static -Succeeded -get_ECAT7_image_info(shared_ptr& exam_info_sptr, - CartesianCoordinate3D& dimensions, - CartesianCoordinate3D& voxel_size, - Coordinate3D& origin, - float& scale_factor, - NumericType& type_of_numbers, - ByteOrder& byte_order, - long& offset_in_file, - - const string& ECAT7_filename, - const int frame_num, const int gate_num, const int data_num, const int bed_num, - const char * const warning_prefix, - const char * const warning_suffix) -{ - MatrixFile * const mptr = - matrix_open( ECAT7_filename.c_str(), MAT_READ_ONLY, MAT_UNKNOWN_FTYPE); +static Succeeded +get_ECAT7_image_info(shared_ptr& exam_info_sptr, CartesianCoordinate3D& dimensions, + CartesianCoordinate3D& voxel_size, Coordinate3D& origin, float& scale_factor, + NumericType& type_of_numbers, ByteOrder& byte_order, long& offset_in_file, + + const string& ECAT7_filename, const int frame_num, const int gate_num, const int data_num, const int bed_num, + const char* const warning_prefix, const char* const warning_suffix) { + MatrixFile* const mptr = matrix_open(ECAT7_filename.c_str(), MAT_READ_ONLY, MAT_UNKNOWN_FTYPE); if (!mptr) { - matrix_perror( ECAT7_filename.c_str()); + matrix_perror(ECAT7_filename.c_str()); + return Succeeded::no; + } + if (mptr->mhptr->sw_version < V7) { + matrix_close(mptr); + warning("%s: %s seems to be an ECAT 6 file. " + "%s", + warning_prefix, ECAT7_filename.c_str(), warning_suffix); return Succeeded::no; } - if (mptr->mhptr->sw_version < V7) - { - matrix_close(mptr); - warning("%s: %s seems to be an ECAT 6 file. " - "%s", - warning_prefix, ECAT7_filename.c_str(), warning_suffix); - return Succeeded::no; - } - //case PetImage: TODO this probably has subheaders? - if (mptr->mhptr->file_type != ByteVolume && - mptr->mhptr->file_type != PetVolume) - { - matrix_close(mptr); - warning("%s: %s has the wrong file type to be read as an image." - "%s", - warning_prefix, ECAT7_filename.c_str(), warning_suffix); - return Succeeded::no; - } - - const int matnum = mat_numcod (frame_num, 1, gate_num, data_num, bed_num); - MatrixData* matrix = matrix_read( mptr, matnum, MAT_SUB_HEADER); - - if (matrix==NULL) - { - matrix_close(mptr); - warning("%s: Matrix not found at \"%d,1,%d,%d,%d\" in file %s\n." - "%s", - warning_prefix, - frame_num, gate_num, data_num, bed_num, ECAT7_filename.c_str(), - warning_suffix); - return Succeeded::no; - } + // case PetImage: TODO this probably has subheaders? + if (mptr->mhptr->file_type != ByteVolume && mptr->mhptr->file_type != PetVolume) { + matrix_close(mptr); + warning("%s: %s has the wrong file type to be read as an image." + "%s", + warning_prefix, ECAT7_filename.c_str(), warning_suffix); + return Succeeded::no; + } + + const int matnum = mat_numcod(frame_num, 1, gate_num, data_num, bed_num); + MatrixData* matrix = matrix_read(mptr, matnum, MAT_SUB_HEADER); + + if (matrix == NULL) { + matrix_close(mptr); + warning("%s: Matrix not found at \"%d,1,%d,%d,%d\" in file %s\n." + "%s", + warning_prefix, frame_num, gate_num, data_num, bed_num, ECAT7_filename.c_str(), warning_suffix); + return Succeeded::no; + } exam_info_sptr = read_ECAT7_exam_info(mptr); { @@ -1522,61 +1278,46 @@ get_ECAT7_image_info(shared_ptr& exam_info_sptr, exam_info_sptr->set_time_frame_definitions(time_frame_defs); } - Image_subheader const * const sub_header_ptr= - reinterpret_cast(matrix->shptr); + Image_subheader const* const sub_header_ptr = reinterpret_cast(matrix->shptr); - if(sub_header_ptr->num_dimensions != 3) + if (sub_header_ptr->num_dimensions != 3) warning("%s: while reading matrix \"%d,1,%d,%d,%d\" in file %s:\n" - "Expected subheader_ptr->num_dimensions==3. Continuing\n", - warning_prefix, - frame_num, gate_num, data_num, bed_num, ECAT7_filename.c_str()); - dimensions = - CartesianCoordinate3D(matrix->zdim, - matrix->ydim, - matrix->xdim); - voxel_size = - CartesianCoordinate3D(matrix->z_size * 10, - matrix->y_size * 10, - matrix->pixel_size * 10); // convert to mm + "Expected subheader_ptr->num_dimensions==3. Continuing\n", + warning_prefix, frame_num, gate_num, data_num, bed_num, ECAT7_filename.c_str()); + dimensions = CartesianCoordinate3D(matrix->zdim, matrix->ydim, matrix->xdim); + voxel_size = CartesianCoordinate3D(matrix->z_size * 10, matrix->y_size * 10, + matrix->pixel_size * 10); // convert to mm // TODO: next line assumes that the index-range for the image is contracted in a particular way. We'd really need to check that. // At present, it will only be detected by test_OutputFileFormat - origin = - Coordinate3D(matrix->z_origin, - matrix->y_origin, - matrix->x_origin) * 10; // convert to mm - - + origin = Coordinate3D(matrix->z_origin, matrix->y_origin, + matrix->x_origin) * 10; // convert to mm + scale_factor = matrix->scale_factor; - + find_type_from_ECAT_data_type(type_of_numbers, byte_order, matrix->data_type); - - offset_in_file = - offset_in_ECAT_file(mptr, frame_num, 1, gate_num, data_num, bed_num, 0, NULL); - if (offset_in_ECAT_file<0) - { - free_matrix_data(matrix); - matrix_close(mptr); - warning("%s: while reading matrix \"%d,1,%d,%d,%d\" in file %s:\n" - "Error in determining offset into ECAT7 file %s.\n" - "%s", - warning_prefix, - frame_num, gate_num, data_num, bed_num, ECAT7_filename.c_str(), - warning_suffix); - return Succeeded::no; - } - + + offset_in_file = offset_in_ECAT_file(mptr, frame_num, 1, gate_num, data_num, bed_num, 0, NULL); + if (offset_in_ECAT_file < 0) { + free_matrix_data(matrix); + matrix_close(mptr); + warning("%s: while reading matrix \"%d,1,%d,%d,%d\" in file %s:\n" + "Error in determining offset into ECAT7 file %s.\n" + "%s", + warning_prefix, frame_num, gate_num, data_num, bed_num, ECAT7_filename.c_str(), warning_suffix); + return Succeeded::no; + } + free_matrix_data(matrix); matrix_close(mptr); return Succeeded::yes; } -VoxelsOnCartesianGrid * -ECAT7_to_VoxelsOnCartesianGrid(const string& ECAT7_filename, - const int frame_num, const int gate_num, const int data_num, const int bed_num) -{ - const char * const warning_prefix = "ECAT7_to_VoxelsOnCartesianGrid"; - const char * const warning_suffix = "I'm not reading any data...\n"; +VoxelsOnCartesianGrid* +ECAT7_to_VoxelsOnCartesianGrid(const string& ECAT7_filename, const int frame_num, const int gate_num, const int data_num, + const int bed_num) { + const char* const warning_prefix = "ECAT7_to_VoxelsOnCartesianGrid"; + const char* const warning_suffix = "I'm not reading any data...\n"; shared_ptr exam_info_sptr; CartesianCoordinate3D dimensions; @@ -1586,322 +1327,265 @@ ECAT7_to_VoxelsOnCartesianGrid(const string& ECAT7_filename, NumericType type_of_numbers; ByteOrder byte_order; long offset_in_file; - if (get_ECAT7_image_info(exam_info_sptr, - dimensions, voxel_size, origin, - scale_factor, type_of_numbers, byte_order, offset_in_file, - - ECAT7_filename, - frame_num, gate_num, data_num, bed_num, - warning_prefix, - warning_suffix) == - Succeeded::no) - { - return 0; - } + if (get_ECAT7_image_info( + exam_info_sptr, dimensions, voxel_size, origin, scale_factor, type_of_numbers, byte_order, offset_in_file, + + ECAT7_filename, frame_num, gate_num, data_num, bed_num, warning_prefix, warning_suffix) == Succeeded::no) { + return 0; + } // WARNING: this has to be consistent with the writing // in write_basic_interfile_header_for_ECAT7 and DiscretisedDensity_to_ECAT7 - const IndexRange3D range_3D (0,dimensions.z()-1, - -dimensions.y()/2,(-dimensions.y()/2)+dimensions.y()-1, - -dimensions.x()/2,(-dimensions.x()/2)+dimensions.x()-1); - VoxelsOnCartesianGrid* image_ptr = - new VoxelsOnCartesianGrid (exam_info_sptr, range_3D, origin, voxel_size); - + const IndexRange3D range_3D(0, dimensions.z() - 1, -dimensions.y() / 2, (-dimensions.y() / 2) + dimensions.y() - 1, + -dimensions.x() / 2, (-dimensions.x() / 2) + dimensions.x() - 1); + VoxelsOnCartesianGrid* image_ptr = new VoxelsOnCartesianGrid(exam_info_sptr, range_3D, origin, voxel_size); + std::ifstream data_in(ECAT7_filename.c_str(), ios::in | ios::binary); - if (!data_in) - { - warning("%s: cannot open %s using C++ ifstream.\n" - "%s", - warning_prefix, ECAT7_filename.c_str(), warning_suffix); - delete image_ptr; - return 0; - } + if (!data_in) { + warning("%s: cannot open %s using C++ ifstream.\n" + "%s", + warning_prefix, ECAT7_filename.c_str(), warning_suffix); + delete image_ptr; + return 0; + } data_in.seekg(static_cast(offset_in_file)); - if (!data_in) - { + if (!data_in) { + warning("%s: while reading %s:\n" + "error seeking to position of data.\n" + "%s", + warning_prefix, ECAT7_filename.c_str(), warning_suffix); + delete image_ptr; + return 0; + } + + { + float scale = float(1); + read_data(data_in, *image_ptr, type_of_numbers, scale, byte_order); + if (scale != 1) { warning("%s: while reading %s:\n" - "error seeking to position of data.\n" - "%s", - warning_prefix, ECAT7_filename.c_str(), warning_suffix); + "error in reading data with convertion to floats.\n", + "%s", warning_prefix, ECAT7_filename.c_str(), warning_suffix); delete image_ptr; return 0; } - - { - float scale = float(1); - read_data(data_in, *image_ptr, type_of_numbers, scale, byte_order); - if (scale != 1) - { - warning("%s: while reading %s:\n" - "error in reading data with convertion to floats.\n", - "%s", - warning_prefix, ECAT7_filename.c_str(), warning_suffix); - delete image_ptr; - return 0; - } } *image_ptr *= scale_factor; - + return image_ptr; } ProjDataFromStream* -ECAT7_to_PDFS(const string& ECAT7_filename, - const int frame_num, const int gate_num, const int data_num, const int bed_num) -{ - MatrixFile * const mptr = matrix_open( ECAT7_filename.c_str(), MAT_READ_ONLY, MAT_UNKNOWN_FTYPE); +ECAT7_to_PDFS(const string& ECAT7_filename, const int frame_num, const int gate_num, const int data_num, const int bed_num) { + MatrixFile* const mptr = matrix_open(ECAT7_filename.c_str(), MAT_READ_ONLY, MAT_UNKNOWN_FTYPE); if (!mptr) { - matrix_perror( ECAT7_filename.c_str()); + matrix_perror(ECAT7_filename.c_str()); return 0; } - const char * const warning_prefix = "ECAT7_to_PDFS"; - const char * const warning_suffix = "I'm not reading any data...\n"; + const char* const warning_prefix = "ECAT7_to_PDFS"; + const char* const warning_suffix = "I'm not reading any data...\n"; - if (mptr->mhptr->sw_version < V7) - { - warning("%s: %s seems to be an ECAT 6 file. " - "%s", warning_prefix, ECAT7_filename.c_str(), warning_suffix); - return 0; - } - const int matnum = mat_numcod (frame_num, 1, gate_num, data_num, bed_num); - MatrixData* matrix = matrix_read( mptr, matnum, MAT_SUB_HEADER); - - if (matrix==NULL) - { - matrix_close(mptr); - warning("%s: Matrix not found at \"%d,1,%d,%d,%d\" in file %s\n." - "%s", - warning_prefix, - frame_num, gate_num, data_num, bed_num, - ECAT7_filename.c_str(), warning_suffix); - return 0; - } - - shared_ptr stream_ptr( - new fstream(ECAT7_filename.c_str(), ios::in | ios::binary)); - - ProjDataFromStream * pdfs_ptr = - make_pdfs_from_matrix(mptr, matrix, stream_ptr); + if (mptr->mhptr->sw_version < V7) { + warning("%s: %s seems to be an ECAT 6 file. " + "%s", + warning_prefix, ECAT7_filename.c_str(), warning_suffix); + return 0; + } + const int matnum = mat_numcod(frame_num, 1, gate_num, data_num, bed_num); + MatrixData* matrix = matrix_read(mptr, matnum, MAT_SUB_HEADER); + + if (matrix == NULL) { + matrix_close(mptr); + warning("%s: Matrix not found at \"%d,1,%d,%d,%d\" in file %s\n." + "%s", + warning_prefix, frame_num, gate_num, data_num, bed_num, ECAT7_filename.c_str(), warning_suffix); + return 0; + } + + shared_ptr stream_ptr(new fstream(ECAT7_filename.c_str(), ios::in | ios::binary)); + + ProjDataFromStream* pdfs_ptr = make_pdfs_from_matrix(mptr, matrix, stream_ptr); free_matrix_data(matrix); matrix_close(mptr); return pdfs_ptr; } +Succeeded +write_basic_interfile_header_for_ECAT7(string& interfile_header_filename, const string& ECAT7_filename, const int frame_num, + const int gate_num, const int data_num, const int bed_num) { -Succeeded -write_basic_interfile_header_for_ECAT7(string& interfile_header_filename, - const string& ECAT7_filename, - const int frame_num, const int gate_num, const int data_num, const int bed_num) -{ - - MatrixFile * const mptr = matrix_open( ECAT7_filename.c_str(), MAT_READ_ONLY, MAT_UNKNOWN_FTYPE); + MatrixFile* const mptr = matrix_open(ECAT7_filename.c_str(), MAT_READ_ONLY, MAT_UNKNOWN_FTYPE); if (!mptr) { - matrix_perror( ECAT7_filename.c_str()); + matrix_perror(ECAT7_filename.c_str()); return Succeeded::no; } - const char * const warning_prefix = "write_basic_interfile_header_for_ECAT7"; - const char * const warning_suffix = "I'm not writing an Interfile header...\n"; + const char* const warning_prefix = "write_basic_interfile_header_for_ECAT7"; + const char* const warning_suffix = "I'm not writing an Interfile header...\n"; - if (mptr->mhptr->sw_version < V7) - { + if (mptr->mhptr->sw_version < V7) { warning("%s: '%s' seems to be an ECAT 6 file. " - "%s", warning_prefix, ECAT7_filename.c_str(), warning_suffix); - return Succeeded::no; + "%s", + warning_prefix, ECAT7_filename.c_str(), warning_suffix); + return Succeeded::no; } - - char *header_filename = new char[ECAT7_filename.size() + 100]; + char* header_filename = new char[ECAT7_filename.size() + 100]; { strcpy(header_filename, ECAT7_filename.c_str()); // keep extension, just in case we would have conflicts otherwise // but replace the . with a _ - const char * dot_ptr = strchr(find_filename(header_filename),'.'); + const char* dot_ptr = strchr(find_filename(header_filename), '.'); if (dot_ptr != NULL) header_filename[dot_ptr - header_filename] = '_'; // now add stuff to say which frame, gate, bed, data this was - sprintf(header_filename+strlen(header_filename), "_f%dg%dd%db%d", - frame_num, gate_num, data_num, bed_num); + sprintf(header_filename + strlen(header_filename), "_f%dg%dd%db%d", frame_num, gate_num, data_num, bed_num); } - - switch (mptr->mhptr->file_type) - { - //case PetImage: // TODO this probably has subheaders? + + switch (mptr->mhptr->file_type) { + // case PetImage: // TODO this probably has subheaders? case ByteVolume: - case PetVolume: - { - shared_ptr exam_info_sptr; - CartesianCoordinate3D dimensions; - CartesianCoordinate3D voxel_size; - Coordinate3D origin; - float scale_factor; - NumericType type_of_numbers; - ByteOrder byte_order; - long offset_in_file; - if (get_ECAT7_image_info(exam_info_sptr, - dimensions, voxel_size, origin, - scale_factor, type_of_numbers, byte_order, offset_in_file, - - ECAT7_filename, - frame_num, gate_num, data_num, bed_num, - warning_prefix, - warning_suffix) == - Succeeded::no) - { - matrix_close(mptr); - return Succeeded::no; - } - - VectorWithOffset scaling_factors(1); - VectorWithOffset file_offsets(1); - scaling_factors[0] = scale_factor; - file_offsets[0] = static_cast(offset_in_file); - strcat(header_filename, ".hv"); - interfile_header_filename = header_filename; - // WARNING: this has to be consistent with the reading - // in ECAT7_to_VoxelsOnCartesianGrid - const IndexRange3D range_3D (0,dimensions.z()-1, - -dimensions.y()/2,(-dimensions.y()/2)+dimensions.y()-1, - -dimensions.x()/2,(-dimensions.x()/2)+dimensions.x()-1); - write_basic_interfile_image_header(header_filename, ECAT7_filename, - *exam_info_sptr, - range_3D, voxel_size, origin, - type_of_numbers, byte_order, - scaling_factors, - file_offsets); - break; + case PetVolume: { + shared_ptr exam_info_sptr; + CartesianCoordinate3D dimensions; + CartesianCoordinate3D voxel_size; + Coordinate3D origin; + float scale_factor; + NumericType type_of_numbers; + ByteOrder byte_order; + long offset_in_file; + if (get_ECAT7_image_info( + exam_info_sptr, dimensions, voxel_size, origin, scale_factor, type_of_numbers, byte_order, offset_in_file, + + ECAT7_filename, frame_num, gate_num, data_num, bed_num, warning_prefix, warning_suffix) == Succeeded::no) { + matrix_close(mptr); + return Succeeded::no; } - - case AttenCor: + + VectorWithOffset scaling_factors(1); + VectorWithOffset file_offsets(1); + scaling_factors[0] = scale_factor; + file_offsets[0] = static_cast(offset_in_file); + strcat(header_filename, ".hv"); + interfile_header_filename = header_filename; + // WARNING: this has to be consistent with the reading + // in ECAT7_to_VoxelsOnCartesianGrid + const IndexRange3D range_3D(0, dimensions.z() - 1, -dimensions.y() / 2, (-dimensions.y() / 2) + dimensions.y() - 1, + -dimensions.x() / 2, (-dimensions.x() / 2) + dimensions.x() - 1); + write_basic_interfile_image_header(header_filename, ECAT7_filename, *exam_info_sptr, range_3D, voxel_size, origin, + type_of_numbers, byte_order, scaling_factors, file_offsets); + break; + } + + case AttenCor: case Byte3dSinogram: case Short3dSinogram: - case Float3dSinogram : - { - const int matnum = mat_numcod (frame_num, 1, gate_num, data_num, bed_num); - MatrixData* matrix = matrix_read( mptr, matnum, MAT_SUB_HEADER); - - if (matrix==NULL) - { - matrix_close(mptr); - warning("%s: Matrix not found at \"%d,1,%d,%d,%d\" in file '%s'.\n" - "%s", - warning_prefix, - frame_num, gate_num, data_num, bed_num, - ECAT7_filename.c_str(), warning_suffix); - return Succeeded::no; - } - - shared_ptr stream_ptr( - new fstream(ECAT7_filename.c_str(), ios::in | ios::binary)); - - shared_ptr pdfs_ptr(make_pdfs_from_matrix(mptr, matrix, stream_ptr)); - free_matrix_data(matrix); - - if (is_null_ptr(pdfs_ptr)) - { - matrix_close(mptr); - return Succeeded::no; - } - strcat(header_filename, ".hs"); - interfile_header_filename = header_filename; - write_basic_interfile_PDFS_header(header_filename, ECAT7_filename, *pdfs_ptr); + case Float3dSinogram: { + const int matnum = mat_numcod(frame_num, 1, gate_num, data_num, bed_num); + MatrixData* matrix = matrix_read(mptr, matnum, MAT_SUB_HEADER); + + if (matrix == NULL) { + matrix_close(mptr); + warning("%s: Matrix not found at \"%d,1,%d,%d,%d\" in file '%s'.\n" + "%s", + warning_prefix, frame_num, gate_num, data_num, bed_num, ECAT7_filename.c_str(), warning_suffix); + return Succeeded::no; + } + + shared_ptr stream_ptr(new fstream(ECAT7_filename.c_str(), ios::in | ios::binary)); + + shared_ptr pdfs_ptr(make_pdfs_from_matrix(mptr, matrix, stream_ptr)); + free_matrix_data(matrix); + + if (is_null_ptr(pdfs_ptr)) { + matrix_close(mptr); + return Succeeded::no; + } + strcat(header_filename, ".hs"); + interfile_header_filename = header_filename; + write_basic_interfile_PDFS_header(header_filename, ECAT7_filename, *pdfs_ptr); + + break; + } - break; - } - default: matrix_close(mptr); warning("%s: File type not handled for file '%s'.\n" - "%s", - warning_prefix, ECAT7_filename.c_str(), warning_suffix); + "%s", + warning_prefix, ECAT7_filename.c_str(), warning_suffix); return Succeeded::no; } - + delete[] header_filename; matrix_close(mptr); return Succeeded::yes; } +Succeeded +DiscretisedDensity_to_ECAT7(MatrixFile* mptr, DiscretisedDensity<3, float> const& density, const int frame_num, + const int gate_num, const int data_num, const int bed_num) { -Succeeded -DiscretisedDensity_to_ECAT7(MatrixFile *mptr, - DiscretisedDensity<3,float> const & density, - const int frame_num, const int gate_num, const int data_num, const int bed_num) -{ - const Main_header& mhead = *(mptr->mhptr); - DiscretisedDensityOnCartesianGrid<3,float> const & image = - dynamic_cast const&>(density); + DiscretisedDensityOnCartesianGrid<3, float> const& image = + dynamic_cast const&>(density); - - if (mhead.file_type!= PetVolume) - { + if (mhead.file_type != PetVolume) { warning("DiscretisedDensity_to_ECAT7: converting (f%d, g%d, d%d, b%d)\n" "Main header.file_type should be ImageFile\n", frame_num, gate_num, data_num, bed_num); return Succeeded::no; } - if (mhead.num_planes!=image.get_length()) - { + if (mhead.num_planes != image.get_length()) { warning("DiscretisedDensity_to_ECAT7: converting (f%d, g%d, d%d, b%d)\n" "Main header.num_planes should be %d\n", - frame_num, gate_num, data_num, bed_num,image.get_length()); + frame_num, gate_num, data_num, bed_num, image.get_length()); return Succeeded::no; } - const float voxel_size_z = image.get_grid_spacing()[1]/10;// convert to cm - const float voxel_size_y = image.get_grid_spacing()[2]/10; - const float voxel_size_x = image.get_grid_spacing()[3]/10; - if (fabs(mhead.plane_separation - voxel_size_z) > 1.E-4) - { + const float voxel_size_z = image.get_grid_spacing()[1] / 10; // convert to cm + const float voxel_size_y = image.get_grid_spacing()[2] / 10; + const float voxel_size_x = image.get_grid_spacing()[3] / 10; + if (fabs(mhead.plane_separation - voxel_size_z) > 1.E-4) { warning("DiscretisedDensity_to_ECAT7: converting (f%d, g%d, d%d, b%d)\n" "Main header.plane_separation should be %g\n", - frame_num, gate_num, data_num, bed_num,voxel_size_z); + frame_num, gate_num, data_num, bed_num, voxel_size_z); return Succeeded::no; } - - + Image_subheader ihead; img_subheader_zero_fill(ihead); - - const int z_size= image.get_length(); - const int y_size= image[0].get_length(); - const int x_size= image[0][0].get_length(); - + + const int z_size = image.get_length(); + const int y_size = image[0].get_length(); + const int x_size = image[0][0].get_length(); + // Setup subheader params // ihead.data_type set by save_volume7; - ihead.x_dimension= x_size; - ihead.y_dimension= y_size; - ihead.z_dimension= z_size; - ihead.x_pixel_size= voxel_size_x; - ihead.y_pixel_size= voxel_size_y; - ihead.z_pixel_size= voxel_size_z; - - ihead.num_dimensions= 3; - // ECAT7 origin is somewhere in the middle of the image. + ihead.x_dimension = x_size; + ihead.y_dimension = y_size; + ihead.z_dimension = z_size; + ihead.x_pixel_size = voxel_size_x; + ihead.y_pixel_size = voxel_size_y; + ihead.z_pixel_size = voxel_size_z; + + ihead.num_dimensions = 3; + // ECAT7 origin is somewhere in the middle of the image. // It seems at present consistent with the STIR origin. // WARNING this has to be consistent with reading (get_ECAT7_image_info) - const CartesianCoordinate3D ecat_origin = - image.get_physical_coordinates_for_indices(make_coordinate(0.F,0.F,0.F)); - ihead.x_offset= ecat_origin.x()/10; - ihead.y_offset= ecat_origin.y()/10; - ihead.z_offset= ecat_origin.z()/10; + const CartesianCoordinate3D ecat_origin = image.get_physical_coordinates_for_indices(make_coordinate(0.F, 0.F, 0.F)); + ihead.x_offset = ecat_origin.x() / 10; + ihead.y_offset = ecat_origin.y() / 10; + ihead.z_offset = ecat_origin.z() / 10; shared_ptr scanner_ptr; find_scanner(scanner_ptr, mhead); const float depth_of_interaction_factor = - 1 + - scanner_ptr->get_average_depth_of_interaction() / - scanner_ptr->get_inner_ring_radius(); + 1 + scanner_ptr->get_average_depth_of_interaction() / scanner_ptr->get_inner_ring_radius(); // note: CTI uses shead.x_resolution instead of mhead.bin_size - // but we don't have access to the sinogram here, and these 2 fields + // but we don't have access to the sinogram here, and these 2 fields // should be equal anyway. - ihead.recon_zoom= - mhead.bin_size/voxel_size_x * - scanner_ptr->get_default_num_arccorrected_bins()/ - float(image[0].size()) * - depth_of_interaction_factor; + ihead.recon_zoom = mhead.bin_size / voxel_size_x * scanner_ptr->get_default_num_arccorrected_bins() / float(image[0].size()) * + depth_of_interaction_factor; - ihead.decay_corr_fctr= 1; + ihead.decay_corr_fctr = 1; // set frame info (using the first frame in exam_info as we're writing that single image) set_time_frame_info(&ihead, mhead, density.get_exam_info(), 1U); @@ -1937,338 +1621,250 @@ DiscretisedDensity_to_ECAT7(MatrixFile *mptr, // use LLN function // easy, but wasteful: we need to copy the data first to a float buffer // then save_volume7 makes a short buffer... - const unsigned int buffer_size = - static_cast(x_size)* - static_cast(y_size)* - static_cast(z_size); + const unsigned int buffer_size = + static_cast(x_size) * static_cast(y_size) * static_cast(z_size); unique_ptr float_buffer(new float[buffer_size]); // save_volume7 does a swap in z, so we can't use the following - //copy(density.begin_all(), density.end_all(), float_buffer.get()); + // copy(density.begin_all(), density.end_all(), float_buffer.get()); { - float * current_buffer_pos = float_buffer.get(); - const unsigned int plane_size = - static_cast(x_size)* - static_cast(y_size); - for (int z=density.get_max_index(); z>= density.get_min_index(); --z) - { + float* current_buffer_pos = float_buffer.get(); + const unsigned int plane_size = static_cast(x_size) * static_cast(y_size); + for (int z = density.get_max_index(); z >= density.get_min_index(); --z) { copy(density[z].begin_all(), density[z].end_all(), current_buffer_pos); current_buffer_pos += plane_size; } } - if (save_volume7(mptr, &ihead, float_buffer.get(), - frame_num, gate_num,data_num, bed_num) != 0) - { + if (save_volume7(mptr, &ihead, float_buffer.get(), frame_num, gate_num, data_num, bed_num) != 0) { warning("Error writing image to ECAT7 file.\n" "No data written for frame %d, gate %d, data %d, bed %d\n", - frame_num, gate_num,data_num, bed_num); + frame_num, gate_num, data_num, bed_num); return Succeeded::no; - } - else - { + } else { return Succeeded::yes; } #endif } - -Succeeded -DiscretisedDensity_to_ECAT7(DiscretisedDensity<3,float> const & density, - string const & cti_name, string const&orig_name, - const Scanner& scanner, - const int frame_num, const int gate_num, const int data_num, const int bed_num) -{ +Succeeded +DiscretisedDensity_to_ECAT7(DiscretisedDensity<3, float> const& density, string const& cti_name, string const& orig_name, + const Scanner& scanner, const int frame_num, const int gate_num, const int data_num, + const int bed_num) { Main_header mhead; make_ECAT7_main_header(mhead, scanner, orig_name, density); - - MatrixFile* mptr= matrix_create (cti_name.c_str(), MAT_CREATE, &mhead); + MatrixFile* mptr = matrix_create(cti_name.c_str(), MAT_CREATE, &mhead); if (mptr == 0) return Succeeded::no; - Succeeded result = - DiscretisedDensity_to_ECAT7(mptr, - density, - frame_num, gate_num,data_num, bed_num); - - matrix_close(mptr); + Succeeded result = DiscretisedDensity_to_ECAT7(mptr, density, frame_num, gate_num, data_num, bed_num); + + matrix_close(mptr); return result; } - Succeeded -update_ECAT7_subheader(MatrixFile *mptr, Scan_subheader& shead, - const MatDir& matdir) -{ - const int ERROR=-1; +update_ECAT7_subheader(MatrixFile* mptr, Scan_subheader& shead, const MatDir& matdir) { + const int ERROR = -1; if (mptr->mhptr->file_type != #ifndef STIR_NO_NAMESPACES ::Sinogram #else CTISinogram #endif - ) + ) return Succeeded::no; - return - mat_write_scan_subheader(mptr->fptr, mptr->mhptr, matdir.strtblk, - &shead) == ERROR ? - Succeeded::no : Succeeded::yes; + return mat_write_scan_subheader(mptr->fptr, mptr->mhptr, matdir.strtblk, &shead) == ERROR ? Succeeded::no : Succeeded::yes; } - Succeeded -update_ECAT7_subheader(MatrixFile *mptr, Norm_subheader& shead, const MatDir& matdir) -{ - const int ERROR=-1; +update_ECAT7_subheader(MatrixFile* mptr, Norm_subheader& shead, const MatDir& matdir) { + const int ERROR = -1; if (mptr->mhptr->file_type != Normalization) return Succeeded::no; - return - mat_write_norm_subheader(mptr->fptr, mptr->mhptr, matdir.strtblk, - &shead) == ERROR ? - Succeeded::no : Succeeded::yes; + return mat_write_norm_subheader(mptr->fptr, mptr->mhptr, matdir.strtblk, &shead) == ERROR ? Succeeded::no : Succeeded::yes; } - Succeeded -update_ECAT7_subheader(MatrixFile *mptr, Image_subheader& shead, const MatDir& matdir) -{ - const int ERROR=-1; - if (!(mptr->mhptr->file_type == PetImage || - mptr->mhptr->file_type == ByteVolume || - mptr->mhptr->file_type == PetVolume)) +update_ECAT7_subheader(MatrixFile* mptr, Image_subheader& shead, const MatDir& matdir) { + const int ERROR = -1; + if (!(mptr->mhptr->file_type == PetImage || mptr->mhptr->file_type == ByteVolume || mptr->mhptr->file_type == PetVolume)) return Succeeded::no; - return - mat_write_image_subheader(mptr->fptr, mptr->mhptr, matdir.strtblk, - &shead) == ERROR ? - Succeeded::no : Succeeded::yes; + return mat_write_image_subheader(mptr->fptr, mptr->mhptr, matdir.strtblk, &shead) == ERROR ? Succeeded::no : Succeeded::yes; } Succeeded -update_ECAT7_subheader(MatrixFile *mptr, Attn_subheader& shead, const MatDir& matdir) -{ - const int ERROR=-1; +update_ECAT7_subheader(MatrixFile* mptr, Attn_subheader& shead, const MatDir& matdir) { + const int ERROR = -1; if (mptr->mhptr->file_type != AttenCor) return Succeeded::no; - return - mat_write_attn_subheader(mptr->fptr, mptr->mhptr, matdir.strtblk, - &shead) == ERROR ? - Succeeded::no : Succeeded::yes; + return mat_write_attn_subheader(mptr->fptr, mptr->mhptr, matdir.strtblk, &shead) == ERROR ? Succeeded::no : Succeeded::yes; } Succeeded -update_ECAT7_subheader(MatrixFile *mptr, Scan3D_subheader& shead, const MatDir& matdir) -{ - const int ERROR=-1; - if (!(mptr->mhptr->file_type == Byte3dSinogram || - mptr->mhptr->file_type == Short3dSinogram || - mptr->mhptr->file_type == Float3dSinogram)) +update_ECAT7_subheader(MatrixFile* mptr, Scan3D_subheader& shead, const MatDir& matdir) { + const int ERROR = -1; + if (!(mptr->mhptr->file_type == Byte3dSinogram || mptr->mhptr->file_type == Short3dSinogram || + mptr->mhptr->file_type == Float3dSinogram)) return Succeeded::no; - return - mat_write_Scan3D_subheader(mptr->fptr, mptr->mhptr, matdir.strtblk, - &shead) == ERROR ? - Succeeded::no : Succeeded::yes; + return mat_write_Scan3D_subheader(mptr->fptr, mptr->mhptr, matdir.strtblk, &shead) == ERROR ? Succeeded::no : Succeeded::yes; } template Succeeded -update_ECAT7_subheader(MatrixFile *mptr, SUBHEADER_TYPE& shead, - const int frame_num, const int gate_num, const int data_num, const int bed_num) -{ - const int ERROR=-1; - const int matnum = mat_numcod (frame_num, 1, gate_num, data_num, bed_num); - MatDir matdir; - if (matrix_find(mptr, matnum, &matdir) == ERROR) - return Succeeded::no; - - return update_ECAT7_subheader(mptr, shead, matdir); +update_ECAT7_subheader(MatrixFile* mptr, SUBHEADER_TYPE& shead, const int frame_num, const int gate_num, const int data_num, + const int bed_num) { + const int ERROR = -1; + const int matnum = mat_numcod(frame_num, 1, gate_num, data_num, bed_num); + MatDir matdir; + if (matrix_find(mptr, matnum, &matdir) == ERROR) + return Succeeded::no; + + return update_ECAT7_subheader(mptr, shead, matdir); } -namespace detail -{ +namespace detail { template -Succeeded -static -ProjData_to_ECAT7_help(MatrixFile *mptr, const NumericInfo& output_type_info, - ProjData const& proj_data, - const int frame_num, const int gate_num, - const int data_num, const int bed_num, - float scale_factor) -{ +Succeeded static ProjData_to_ECAT7_help(MatrixFile* mptr, const NumericInfo& output_type_info, + ProjData const& proj_data, const int frame_num, const int gate_num, const int data_num, + const int bed_num, float scale_factor) { const ByteOrder output_byte_order = - // output_type_info.integer_type() ? ByteOrder::get_native_order() : ByteOrder::big_endian; -ByteOrder::big_endian; + // output_type_info.integer_type() ? ByteOrder::get_native_order() : ByteOrder::big_endian; + ByteOrder::big_endian; - const short int cti_data_type = - find_ECAT_data_type(output_type_info.type_id(), output_byte_order); - if (cti_data_type==0) + const short int cti_data_type = find_ECAT_data_type(output_type_info.type_id(), output_byte_order); + if (cti_data_type == 0) return Succeeded::no; const Main_header& mhead = *(mptr->mhptr); { int num_planes = 0; - for(int segment_num=proj_data.get_min_segment_num(); - segment_num <= proj_data.get_max_segment_num(); - ++segment_num) - num_planes+= proj_data.get_num_axial_poss(segment_num); - - if (mhead.num_planes!=num_planes) // TODO check if this is the usual convention + for (int segment_num = proj_data.get_min_segment_num(); segment_num <= proj_data.get_max_segment_num(); ++segment_num) + num_planes += proj_data.get_num_axial_poss(segment_num); + + if (mhead.num_planes != num_planes) // TODO check if this is the usual convention { warning("ProjData_to_ECAT7: converting (f%d, g%d, d%d, b%d)\n" "Main header.num_planes should be %d", - frame_num, gate_num, data_num, bed_num,num_planes); + frame_num, gate_num, data_num, bed_num, num_planes); return Succeeded::no; } - } - + } // If scale_factor is not set (=0) then calculate the scale factor to apply. - if ( scale_factor == 0.0 ) { + if (scale_factor == 0.0) { scale_factor = 1; - + // If a integers are being written, and the suppress_scaling // find scale factor in case we're not writing floats - if (output_type_info.integer_type()) - { - scale_factor = 0;// set first to 0 to use maximum range of output type - for(int segment_num=proj_data.get_min_segment_num(); - segment_num <= proj_data.get_max_segment_num(); - ++segment_num) - { - const SegmentByView segment = - proj_data.get_segment_by_view(segment_num); - - find_scale_factor(scale_factor, - segment, - NumericInfo()); - } + if (output_type_info.integer_type()) { + scale_factor = 0; // set first to 0 to use maximum range of output type + for (int segment_num = proj_data.get_min_segment_num(); segment_num <= proj_data.get_max_segment_num(); ++segment_num) { + const SegmentByView segment = proj_data.get_segment_by_view(segment_num); + + find_scale_factor(scale_factor, segment, NumericInfo()); } + } } - + cout << "\nProjData_to_ECAT7: Will use scale factor " << scale_factor; - Scan3D_subheader scan3d_shead; + Scan3D_subheader scan3d_shead; Attn_subheader attn_shead; - if (mhead.file_type == AttenCor) - { + if (mhead.file_type == AttenCor) { make_subheader_for_ECAT7(attn_shead, mhead, *proj_data.get_proj_data_info_sptr()); // Setup remaining subheader params - attn_shead.data_type= cti_data_type; - attn_shead.scale_factor= scale_factor; + attn_shead.data_type = cti_data_type; + attn_shead.scale_factor = scale_factor; attn_shead.storage_order = ElAxVwRd; - } - else - { + } else { make_subheader_for_ECAT7(scan3d_shead, mhead, *proj_data.get_proj_data_info_sptr()); // Setup remaining subheader params - scan3d_shead.data_type= cti_data_type; - scan3d_shead.loss_correction_fctr= 1.F; - scan3d_shead.scale_factor= scale_factor; + scan3d_shead.data_type = cti_data_type; + scan3d_shead.loss_correction_fctr = 1.F; + scan3d_shead.scale_factor = scale_factor; scan3d_shead.storage_order = ElAxVwRd; // do frame times. - if (mhead.num_bed_pos>1) - { - // TODO not sure how to handle this - warning("Not filling in frame-start/duration for multi-bed position data in ECAT7 subheader"); - } - else - { - // set frame info (using the first frame in exam_info as we're writing that single proj_data) - set_time_frame_info(&scan3d_shead, mhead, proj_data.get_exam_info(), 1U); - } + if (mhead.num_bed_pos > 1) { + // TODO not sure how to handle this + warning("Not filling in frame-start/duration for multi-bed position data in ECAT7 subheader"); + } else { + // set frame info (using the first frame in exam_info as we're writing that single proj_data) + set_time_frame_info(&scan3d_shead, mhead, proj_data.get_exam_info(), 1U); + } } // allocate space in file, and write subheader { - const int ERROR=-1; - const int plane_size= proj_data.get_num_tangential_poss() * proj_data.get_num_views(); - + const int ERROR = -1; + const int plane_size = proj_data.get_num_tangential_poss() * proj_data.get_num_views(); + // TODO only ok if main_header.num_planes is set as above - int nblks = (mhead.num_planes*plane_size*sizeof(OutputType)+511)/512; - + int nblks = (mhead.num_planes * plane_size * sizeof(OutputType) + 511) / 512; + /* 3D sinograms subheader use one more block */ - if (mptr->mhptr->file_type == Byte3dSinogram || - mptr->mhptr->file_type == Short3dSinogram || - mptr->mhptr->file_type == Float3dSinogram) nblks += 1; - - int matnum = mat_numcod (frame_num, 1, gate_num, data_num, bed_num); + if (mptr->mhptr->file_type == Byte3dSinogram || mptr->mhptr->file_type == Short3dSinogram || + mptr->mhptr->file_type == Float3dSinogram) + nblks += 1; + + int matnum = mat_numcod(frame_num, 1, gate_num, data_num, bed_num); struct MatDir matdir; - if (matrix_find(mptr, matnum, &matdir) == ERROR) - { - int blkno = mat_enter(mptr->fptr, mptr->mhptr, matnum, nblks) ; - if( blkno == ERROR ) return( Succeeded::no ); - matdir.matnum = matnum ; - matdir.strtblk = blkno ; - matdir.endblk = matdir.strtblk + nblks - 1 ; - matdir.matstat = 1 ; - insert_mdir(matdir, mptr->dirlist) ; - } - - if (mhead.file_type == AttenCor) - { - if (mat_write_attn_subheader(mptr->fptr, mptr->mhptr, matdir.strtblk, - &attn_shead) == ERROR) - return Succeeded::no; + if (matrix_find(mptr, matnum, &matdir) == ERROR) { + int blkno = mat_enter(mptr->fptr, mptr->mhptr, matnum, nblks); + if (blkno == ERROR) + return (Succeeded::no); + matdir.matnum = matnum; + matdir.strtblk = blkno; + matdir.endblk = matdir.strtblk + nblks - 1; + matdir.matstat = 1; + insert_mdir(matdir, mptr->dirlist); } - else - { - if (mat_write_Scan3D_subheader(mptr->fptr, mptr->mhptr, matdir.strtblk, - &scan3d_shead) == ERROR) - return Succeeded::no; + + if (mhead.file_type == AttenCor) { + if (mat_write_attn_subheader(mptr->fptr, mptr->mhptr, matdir.strtblk, &attn_shead) == ERROR) + return Succeeded::no; + } else { + if (mat_write_Scan3D_subheader(mptr->fptr, mptr->mhptr, matdir.strtblk, &scan3d_shead) == ERROR) + return Succeeded::no; } - } - cout<<"\nProcessing segment number:"; - - for(int segment_num=proj_data.get_min_segment_num(); - segment_num <= proj_data.get_max_segment_num(); - ++segment_num) - { - cout<<" "< segment = - proj_data.get_segment_by_view(segment_num); + const SegmentByView segment = proj_data.get_segment_by_view(segment_num); - const long offset_in_file = - offset_in_ECAT_file(mptr, - frame_num, 1, gate_num, data_num, bed_num, - segment_num, NULL); + const long offset_in_file = offset_in_ECAT_file(mptr, frame_num, 1, gate_num, data_num, bed_num, segment_num, NULL); - if (offset_in_file<0) - { + if (offset_in_file < 0) { warning("ProjData_to_ECAT7: Error in determining offset into ECAT file for segment %d (f%d, g%d, d%d, b%d)\n" - "Maybe the file is too big?\n" - "No data written for this segment and all remaining segments", - segment_num, frame_num, gate_num, data_num, bed_num); - return Succeeded::no; + "Maybe the file is too big?\n" + "No data written for this segment and all remaining segments", + segment_num, frame_num, gate_num, data_num, bed_num); + return Succeeded::no; } - if (fseek(mptr->fptr, offset_in_file, SEEK_SET)) - { + if (fseek(mptr->fptr, offset_in_file, SEEK_SET)) { warning("\nProjData_to_ECAT7: error in fseek for segment %d (f%d, g%d, d%d, b%d)\n" - "No data written for this segment and all remaining segments\n", - segment_num, frame_num, gate_num, data_num, bed_num); + "No data written for this segment and all remaining segments\n", + segment_num, frame_num, gate_num, data_num, bed_num); return Succeeded::no; } - for (int view_num=proj_data.get_min_view_num(); view_num<=proj_data.get_max_view_num(); ++view_num) - for (int ax_pos_num=proj_data.get_min_axial_pos_num(segment_num); ax_pos_num <= proj_data.get_max_axial_pos_num(segment_num); ++ax_pos_num) - { - if (write_data_with_fixed_scale_factor(mptr->fptr, - segment[view_num][ax_pos_num], - output_type_info, - scale_factor, - output_byte_order, - /*can_corrupt_data=*/true) - == Succeeded::no) - { - warning("ProjData_to_ECAT7: error in writing segment %d (f%d, g%d, d%d, b%d)\n" - "Not all data written for this segment and none for all remaining segments\n", - segment_num, frame_num, gate_num, data_num, bed_num); - return Succeeded::no; - } + for (int view_num = proj_data.get_min_view_num(); view_num <= proj_data.get_max_view_num(); ++view_num) + for (int ax_pos_num = proj_data.get_min_axial_pos_num(segment_num); + ax_pos_num <= proj_data.get_max_axial_pos_num(segment_num); ++ax_pos_num) { + if (write_data_with_fixed_scale_factor(mptr->fptr, segment[view_num][ax_pos_num], output_type_info, scale_factor, + output_byte_order, + /*can_corrupt_data=*/true) == Succeeded::no) { + warning("ProjData_to_ECAT7: error in writing segment %d (f%d, g%d, d%d, b%d)\n" + "Not all data written for this segment and none for all remaining segments\n", + segment_num, frame_num, gate_num, data_num, bed_num); + return Succeeded::no; + } } // end of loop over ax_pos_num - - + } // end of loop on segments cout << endl; @@ -2277,64 +1873,47 @@ ByteOrder::big_endian; } // end of namespace detail -Succeeded -ProjData_to_ECAT7(MatrixFile *mptr, ProjData const& proj_data, - const int frame_num, const int gate_num, - const int data_num, const int bed_num, - float scale_factor) -{ - switch (mptr->mhptr->file_type) - { - case AttenCor: - // always use float to prevent problems with CTI utilities - return - detail::ProjData_to_ECAT7_help(mptr, NumericInfo(), proj_data, - frame_num, gate_num,data_num, bed_num, scale_factor); - case Float3dSinogram: - return - detail::ProjData_to_ECAT7_help(mptr, NumericInfo(), proj_data, - frame_num, gate_num,data_num, bed_num, scale_factor); - case Short3dSinogram: - // Note: this relies on sizeof(short)==2. However, find_ECAT_data_type will find out later if this is not true - return - detail::ProjData_to_ECAT7_help(mptr, NumericInfo(), proj_data, - frame_num, gate_num,data_num, bed_num, scale_factor); - case Byte3dSinogram: - return - detail::ProjData_to_ECAT7_help(mptr, NumericInfo(), proj_data, - frame_num, gate_num,data_num, bed_num, scale_factor); - default: - warning("ProjData_to_ECAT7: unsupported file type %d. No data written.", - mptr->mhptr->file_type); - return Succeeded::no; - } - +Succeeded +ProjData_to_ECAT7(MatrixFile* mptr, ProjData const& proj_data, const int frame_num, const int gate_num, const int data_num, + const int bed_num, float scale_factor) { + switch (mptr->mhptr->file_type) { + case AttenCor: + // always use float to prevent problems with CTI utilities + return detail::ProjData_to_ECAT7_help(mptr, NumericInfo(), proj_data, frame_num, gate_num, data_num, bed_num, + scale_factor); + case Float3dSinogram: + return detail::ProjData_to_ECAT7_help(mptr, NumericInfo(), proj_data, frame_num, gate_num, data_num, bed_num, + scale_factor); + case Short3dSinogram: + // Note: this relies on sizeof(short)==2. However, find_ECAT_data_type will find out later if this is not true + return detail::ProjData_to_ECAT7_help(mptr, NumericInfo(), proj_data, frame_num, gate_num, data_num, bed_num, + scale_factor); + case Byte3dSinogram: + return detail::ProjData_to_ECAT7_help(mptr, NumericInfo(), proj_data, frame_num, gate_num, data_num, bed_num, + scale_factor); + default: + warning("ProjData_to_ECAT7: unsupported file type %d. No data written.", mptr->mhptr->file_type); + return Succeeded::no; + } } -Succeeded -ProjData_to_ECAT7(ProjData const& proj_data, NumericType output_type, - string const & cti_name, string const & orig_name, - const int frame_num, const int gate_num, const int data_num, const int bed_num, - const bool write_as_attenuation, - float scale_factor) -{ +Succeeded +ProjData_to_ECAT7(ProjData const& proj_data, NumericType output_type, string const& cti_name, string const& orig_name, + const int frame_num, const int gate_num, const int data_num, const int bed_num, const bool write_as_attenuation, + float scale_factor) { Main_header mhead; - make_ECAT7_main_header(mhead, orig_name, - proj_data.get_exam_info(), - *proj_data.get_proj_data_info_sptr(), - write_as_attenuation, output_type); + make_ECAT7_main_header(mhead, orig_name, proj_data.get_exam_info(), *proj_data.get_proj_data_info_sptr(), write_as_attenuation, + output_type); - MatrixFile *mptr= matrix_create(cti_name.c_str(), MAT_CREATE, &mhead); + MatrixFile* mptr = matrix_create(cti_name.c_str(), MAT_CREATE, &mhead); - Succeeded result = - ProjData_to_ECAT7(mptr, proj_data, frame_num, gate_num,data_num, bed_num, scale_factor); - - matrix_close(mptr); + Succeeded result = ProjData_to_ECAT7(mptr, proj_data, frame_num, gate_num, data_num, bed_num, scale_factor); + + matrix_close(mptr); return result; } - END_NAMESPACE_ECAT7 END_NAMESPACE_ECAT END_NAMESPACE_STIR diff --git a/src/IO/stir_ecat_common.cxx b/src/IO/stir_ecat_common.cxx index 3e3b2a1e02..6596bc7034 100644 --- a/src/IO/stir_ecat_common.cxx +++ b/src/IO/stir_ecat_common.cxx @@ -4,7 +4,7 @@ \file \ingroup ECAT - \brief Implementation of routines which convert ECAT6, ECAT7 and ECAT8 things into our building blocks and vice versa. + \brief Implementation of routines which convert ECAT6, ECAT7 and ECAT8 things into our building blocks and vice versa. \author Kris Thielemans \author PARAPET project @@ -31,25 +31,23 @@ #include "stir/ByteOrder.h" #include "stir/NumericType.h" -#include "stir/Scanner.h" +#include "stir/Scanner.h" #include "stir/ProjDataInfo.h" #include "stir/IO/stir_ecat_common.h" START_NAMESPACE_STIR START_NAMESPACE_ECAT - -void find_type_from_ECAT_data_type(NumericType& type, ByteOrder& byte_order, const short data_type) -{ - switch(data_type) - { +void +find_type_from_ECAT_data_type(NumericType& type, ByteOrder& byte_order, const short data_type) { + switch (data_type) { case ECAT_Byte_data_type: type = NumericType("signed integer", 1); - byte_order=ByteOrder::little_endian; + byte_order = ByteOrder::little_endian; return; case ECAT_I2_little_endian_data_type: type = NumericType("signed integer", 2); - byte_order=ByteOrder::little_endian; + byte_order = ByteOrder::little_endian; return; case ECAT_I2_big_endian_data_type: type = NumericType("signed integer", 2); @@ -57,20 +55,20 @@ void find_type_from_ECAT_data_type(NumericType& type, ByteOrder& byte_order, con return; case ECAT_R4_VAX_data_type: type = NumericType("float", 4); - byte_order=ByteOrder::little_endian; + byte_order = ByteOrder::little_endian; return; case ECAT_R4_IEEE_big_endian_data_type: type = NumericType("float", 4); - byte_order=ByteOrder::big_endian; + byte_order = ByteOrder::big_endian; return; case ECAT_I4_little_endian_data_type: type = NumericType("signed integer", 4); - byte_order=ByteOrder::little_endian; + byte_order = ByteOrder::little_endian; return; case ECAT_I4_big_endian_data_type: type = NumericType("signed integer", 4); - byte_order=ByteOrder::big_endian; - return; + byte_order = ByteOrder::big_endian; + return; default: error("find_type_from_ecat_data_type: unsupported data_type: %d", data_type); // just to avoid compiler warnings @@ -78,62 +76,53 @@ void find_type_from_ECAT_data_type(NumericType& type, ByteOrder& byte_order, con } } -short find_ECAT_data_type(const NumericType& type, const ByteOrder& byte_order) -{ +short +find_ECAT_data_type(const NumericType& type, const ByteOrder& byte_order) { if (!type.signed_type()) warning("find_ecat_data_type: ecat data support only signed types. Using the signed equivalent\n"); - if (type.integer_type()) - { - switch(type.size_in_bytes()) - { + if (type.integer_type()) { + switch (type.size_in_bytes()) { case 1: return ECAT_Byte_data_type; case 2: - return byte_order==ByteOrder::big_endian ? ECAT_I2_big_endian_data_type : ECAT_I2_little_endian_data_type; + return byte_order == ByteOrder::big_endian ? ECAT_I2_big_endian_data_type : ECAT_I2_little_endian_data_type; case 4: - return byte_order==ByteOrder::big_endian ? ECAT_I4_big_endian_data_type : ECAT_I4_little_endian_data_type; - default: - { - // write error message below - } + return byte_order == ByteOrder::big_endian ? ECAT_I4_big_endian_data_type : ECAT_I4_little_endian_data_type; + default: { + // write error message below } - } - else - { - switch(type.size_in_bytes()) - { + } + } else { + switch (type.size_in_bytes()) { case 4: - return byte_order==ByteOrder::big_endian ? ECAT_R4_IEEE_big_endian_data_type : ECAT_R4_VAX_data_type; - default: - { - // write error message below - } + return byte_order == ByteOrder::big_endian ? ECAT_R4_IEEE_big_endian_data_type : ECAT_R4_VAX_data_type; + default: { + // write error message below + } } } std::string number_format; std::size_t size_in_bytes; type.get_Interfile_info(number_format, size_in_bytes); - error("find_ecat_data_type: ecat does not support data type '%s' of %d bytes.\n", - number_format.c_str(), size_in_bytes); + error("find_ecat_data_type: ecat does not support data type '%s' of %d bytes.\n", number_format.c_str(), size_in_bytes); // just to satisfy compilers return short(0); } -short find_ECAT_system_type(const Scanner& scanner) -{ - switch(scanner.get_type()) - { +short +find_ECAT_system_type(const Scanner& scanner) { + switch (scanner.get_type()) { case Scanner::E921: - return 921; + return 921; case Scanner::E925: - return 925; - + return 925; + case Scanner::E931: - return 931; - + return 931; + case Scanner::E951: - return 951; - + return 951; + case Scanner::E953: return 953; @@ -141,8 +130,8 @@ short find_ECAT_system_type(const Scanner& scanner) return 961; case Scanner::E962: - return 962; - + return 962; + case Scanner::E966: return 966; @@ -153,54 +142,50 @@ short find_ECAT_system_type(const Scanner& scanner) return 42; default: - warning("\nfind_ecat_system_type: scanner \"%s\" currently unsupported. Returning 0.\n", - scanner.get_name().c_str()); + warning("\nfind_ecat_system_type: scanner \"%s\" currently unsupported. Returning 0.\n", scanner.get_name().c_str()); return 0; } } -Scanner* find_scanner_from_ECAT_system_type(const short system_type) -{ - switch(system_type) - { - case 128 : +Scanner* +find_scanner_from_ECAT_system_type(const short system_type) { + switch (system_type) { + case 128: return new Scanner(Scanner::RPT); - case 921 : + case 921: return new Scanner(Scanner::E921); - case 925 : + case 925: return new Scanner(Scanner::E925); - case 931 : - case 12 : + case 931: + case 12: return new Scanner(Scanner::E931); - case 951 : + case 951: return new Scanner(Scanner::E951); - case 953 : + case 953: return new Scanner(Scanner::E953); - case 961 : + case 961: return new Scanner(Scanner::E961); - case 962 : + case 962: return new Scanner(Scanner::E962); - case 966 : + case 966: return new Scanner(Scanner::E966); case 42: return new Scanner(Scanner::RATPET); - default : + default: return new Scanner(Scanner::Unknown_scanner); } } std::vector -find_segment_sequence(const ProjDataInfo& pdi) -{ +find_segment_sequence(const ProjDataInfo& pdi) { const int max_segment_num = pdi.get_max_segment_num(); - std::vector segment_sequence(2*max_segment_num+1); + std::vector segment_sequence(2 * max_segment_num + 1); // KT 25/10/2000 swapped segment order // ECAT 7 always stores segments as 0, -1, +1, ... segment_sequence[0] = 0; - for (int segment_num = 1; segment_num<=max_segment_num; ++segment_num) - { - segment_sequence[2*segment_num-1] = -segment_num; - segment_sequence[2*segment_num] = segment_num; + for (int segment_num = 1; segment_num <= max_segment_num; ++segment_num) { + segment_sequence[2 * segment_num - 1] = -segment_num; + segment_sequence[2 * segment_num] = segment_num; } return segment_sequence; } diff --git a/src/Shape_buildblock/Box3D.cxx b/src/Shape_buildblock/Box3D.cxx index 32f6a7c026..3be69018fe 100644 --- a/src/Shape_buildblock/Box3D.cxx +++ b/src/Shape_buildblock/Box3D.cxx @@ -30,12 +30,10 @@ START_NAMESPACE_STIR -const char * const -Box3D::registered_name = "Box3D"; +const char* const Box3D::registered_name = "Box3D"; -void -Box3D::initialise_keymap() -{ +void +Box3D::initialise_keymap() { parser.add_start_key("box parameters"); parser.add_key("length-x (in mm)", &length_x); parser.add_key("length-y (in mm)", &length_y); @@ -45,58 +43,42 @@ Box3D::initialise_keymap() } void -Box3D::set_defaults() -{ +Box3D::set_defaults() { Shape3DWithOrientation::set_defaults(); - length_x=0; - length_y=0; - length_z=0; + length_x = 0; + length_y = 0; + length_z = 0; } bool -Box3D:: -post_processing() -{ - if (Shape3DWithOrientation::post_processing()==true) +Box3D::post_processing() { + if (Shape3DWithOrientation::post_processing() == true) return true; - if (length_x <= 0) - { - warning("length_x should be positive, but is %g\n", length_x); - return true; - } - if (length_y <= 0) - { - warning("length_y should be positive, but is %g\n", length_y); - return true; - } - if (length_z <= 0) - { - warning("length_z should be positive, but is %g\n", length_z); - return true; - } + if (length_x <= 0) { + warning("length_x should be positive, but is %g\n", length_x); + return true; + } + if (length_y <= 0) { + warning("length_y should be positive, but is %g\n", length_y); + return true; + } + if (length_z <= 0) { + warning("length_z should be positive, but is %g\n", length_z); + return true; + } return false; } -Box3D::Box3D() -{ - set_defaults(); -} +Box3D::Box3D() { set_defaults(); } -Box3D::Box3D(const float length_xv, - const float length_yv, - const float length_zv, - const CartesianCoordinate3D& centre_v, - const Array<2,float>& direction_vectors) - : - length_x(length_xv), - length_y(length_yv), - length_z(length_zv) -{ +Box3D::Box3D(const float length_xv, const float length_yv, const float length_zv, const CartesianCoordinate3D& centre_v, + const Array<2, float>& direction_vectors) + : length_x(length_xv), length_y(length_yv), length_z(length_zv) { this->set_origin(centre_v); if (this->set_direction_vectors(direction_vectors) == Succeeded::no) error("Box3D constructor called with wrong direction_vectors"); -} +} #if 0 Box3D::Box3D(const float length_xv, @@ -117,28 +99,22 @@ Box3D::Box3D(const float length_xv, #endif -bool Box3D::is_inside_shape(const CartesianCoordinate3D& coord) const -{ - const CartesianCoordinate3D r = - this->transform_to_shape_coords(coord); - - const float distance_along_x_axis= r.x(); - const float distance_along_y_axis= r.y(); - const float distance_along_z_axis= r.z(); - - return - fabs(distance_along_x_axis)& coord) const { + const CartesianCoordinate3D r = this->transform_to_shape_coords(coord); -float -Box3D:: -get_geometric_volume()const -{ - return static_cast(length_x*length_y*length_z) / this->get_volume_of_unit_cell(); + const float distance_along_x_axis = r.x(); + const float distance_along_y_axis = r.y(); + const float distance_along_z_axis = r.z(); + + return fabs(distance_along_x_axis) < length_x / 2 && fabs(distance_along_y_axis) < length_y / 2 && + fabs(distance_along_z_axis) < length_z / 2; } +float +Box3D::get_geometric_volume() const { + return static_cast(length_x * length_y * length_z) / this->get_volume_of_unit_cell(); +} #if 0 // doesn't take scaling into account @@ -151,34 +127,22 @@ get_geometric_area()const } #endif -Shape3D* -Box3D:: -clone() const -{ - return static_cast(new Box3D(*this)); +Shape3D* +Box3D::clone() const { + return static_cast(new Box3D(*this)); } bool -Box3D:: -operator==(const Box3D& box) const -{ - const float tolerance = - std::min(length_z, std::min(length_x, length_y))/1000; - return - std::fabs(this->length_x - box.length_x) < tolerance - && std::fabs(this->length_y - box.length_y) < tolerance - && std::fabs(this->length_z - box.length_z) < tolerance - && Shape3DWithOrientation::operator==(box); +Box3D::operator==(const Box3D& box) const { + const float tolerance = std::min(length_z, std::min(length_x, length_y)) / 1000; + return std::fabs(this->length_x - box.length_x) < tolerance && std::fabs(this->length_y - box.length_y) < tolerance && + std::fabs(this->length_z - box.length_z) < tolerance && Shape3DWithOrientation::operator==(box); } bool -Box3D:: -operator==(const Shape3D& shape) const -{ - Box3D const * box_ptr = - dynamic_cast(&shape); - return - box_ptr != 0 && (*this == *box_ptr); +Box3D::operator==(const Shape3D& shape) const { + Box3D const* box_ptr = dynamic_cast(&shape); + return box_ptr != 0 && (*this == *box_ptr); } END_NAMESPACE_STIR diff --git a/src/Shape_buildblock/DiscretisedShape3D.cxx b/src/Shape_buildblock/DiscretisedShape3D.cxx index 1163800da2..da40e1da08 100644 --- a/src/Shape_buildblock/DiscretisedShape3D.cxx +++ b/src/Shape_buildblock/DiscretisedShape3D.cxx @@ -32,150 +32,109 @@ START_NAMESPACE_STIR void -DiscretisedShape3D:: -set_origin(const CartesianCoordinate3D& new_origin) -{ +DiscretisedShape3D::set_origin(const CartesianCoordinate3D& new_origin) { assert(this->get_origin() == density_sptr->get_origin()); Shape3D::set_origin(new_origin); density_sptr->set_origin(new_origin); } // TODO check code -float -DiscretisedShape3D:: -get_voxel_weight( - const CartesianCoordinate3D& voxel_centre, - const CartesianCoordinate3D& voxel_size, - const CartesianCoordinate3D& num_samples) const -{ +float +DiscretisedShape3D::get_voxel_weight(const CartesianCoordinate3D& voxel_centre, + const CartesianCoordinate3D& voxel_size, + const CartesianCoordinate3D& num_samples) const { assert(this->get_origin() == density_sptr->get_origin()); assert(voxel_size == image().get_voxel_size()); - const CartesianCoordinate3D r = - this->density_sptr->get_index_coordinates_for_physical_coordinates(voxel_centre); + const CartesianCoordinate3D r = this->density_sptr->get_index_coordinates_for_physical_coordinates(voxel_centre); const int x = round(r.x()); const int y = round(r.y()); const int z = round(r.z()); // check that r points to the middle of a voxel - assert(fabs(x-r.x())<=1E-6); - assert(fabs(y-r.y())<=1E-6); - assert(fabs(z-r.z())<=1E-6); - if ( z <= image().get_max_z() && z >= image().get_min_z() && - y <= image().get_max_y() && y >= image().get_min_y() && - x <= image().get_max_x() && x >= image().get_min_x()) + assert(fabs(x - r.x()) <= 1E-6); + assert(fabs(y - r.y()) <= 1E-6); + assert(fabs(z - r.z()) <= 1E-6); + if (z <= image().get_max_z() && z >= image().get_min_z() && y <= image().get_max_y() && y >= image().get_min_y() && + x <= image().get_max_x() && x >= image().get_min_x()) return image()[z][y][x]; else return 0.F; } -bool -DiscretisedShape3D:: -is_inside_shape(const CartesianCoordinate3D& coord) const -{ +bool +DiscretisedShape3D::is_inside_shape(const CartesianCoordinate3D& coord) const { assert(this->get_origin() == density_sptr->get_origin()); - return - get_voxel_weight(coord, - image().get_voxel_size(), - CartesianCoordinate3D(1,1,1)) > 0; + return get_voxel_weight(coord, image().get_voxel_size(), CartesianCoordinate3D(1, 1, 1)) > 0; } -void -DiscretisedShape3D:: -construct_volume(VoxelsOnCartesianGrid &new_image, const CartesianCoordinate3D& num_samples) const -{ +void +DiscretisedShape3D::construct_volume(VoxelsOnCartesianGrid& new_image, + const CartesianCoordinate3D& num_samples) const { zoom_image(new_image, this->image(), ZoomOptions::preserve_values); } -Shape3D* -DiscretisedShape3D:: -clone() const -{ +Shape3D* +DiscretisedShape3D::clone() const { assert(this->get_origin() == density_sptr->get_origin()); return new DiscretisedShape3D(*this); } -DiscretisedDensity<3,float>& -DiscretisedShape3D:: -get_discretised_density() -{ +DiscretisedDensity<3, float>& +DiscretisedShape3D::get_discretised_density() { return *density_sptr; } -const DiscretisedDensity<3,float>& -DiscretisedShape3D:: -get_discretised_density() const -{ +const DiscretisedDensity<3, float>& +DiscretisedShape3D::get_discretised_density() const { return *density_sptr; } -DiscretisedShape3D:: -DiscretisedShape3D() -{ - set_defaults(); -} +DiscretisedShape3D::DiscretisedShape3D() { set_defaults(); } -DiscretisedShape3D:: -DiscretisedShape3D(const VoxelsOnCartesianGrid& image_v) - : density_sptr(image_v.clone()) -{ +DiscretisedShape3D::DiscretisedShape3D(const VoxelsOnCartesianGrid& image_v) : density_sptr(image_v.clone()) { this->set_origin(image_v.get_origin()); this->filename = "FROM MEMORY"; } - - -DiscretisedShape3D:: -DiscretisedShape3D(const shared_ptr >& density_sptr_v) - : density_sptr(density_sptr_v->clone()) -{ - if(dynamic_cast *>(density_sptr.get()) == NULL) - { - error("DiscretisedShape3D can currently only handle images of type VoxelsOnCartesianGrid.\n"); +DiscretisedShape3D::DiscretisedShape3D(const shared_ptr>& density_sptr_v) + : density_sptr(density_sptr_v->clone()) { + if (dynamic_cast*>(density_sptr.get()) == NULL) { + error("DiscretisedShape3D can currently only handle images of type VoxelsOnCartesianGrid.\n"); } this->set_origin(density_sptr_v->get_origin()); this->filename = "FROM MEMORY"; } - -void -DiscretisedShape3D::initialise_keymap() -{ +void +DiscretisedShape3D::initialise_keymap() { Shape3D::initialise_keymap(); parser.add_start_key("Discretised Shape3D Parameters"); parser.add_key("input filename", &filename); parser.add_stop_key("END"); } - - void -DiscretisedShape3D::set_defaults() -{ +DiscretisedShape3D::set_defaults() { Shape3D::set_defaults(); } bool -DiscretisedShape3D:: -post_processing() -{ - if (Shape3D::post_processing()==true) +DiscretisedShape3D::post_processing() { + if (Shape3D::post_processing() == true) return true; - density_sptr = read_from_file >(filename); - if (!is_null_ptr(density_sptr)) - { - if (this->get_origin() != density_sptr->get_origin()) - { - warning("DiscretisedShape3D: Shape3D::origin and image origin are inconsistent. Using origin from image\n"); - this->set_origin(density_sptr->get_origin()); - } + density_sptr = read_from_file>(filename); + if (!is_null_ptr(density_sptr)) { + if (this->get_origin() != density_sptr->get_origin()) { + warning("DiscretisedShape3D: Shape3D::origin and image origin are inconsistent. Using origin from image\n"); + this->set_origin(density_sptr->get_origin()); } + } return is_null_ptr(density_sptr); } -const char * const -DiscretisedShape3D::registered_name = "Discretised Shape3D"; +const char* const DiscretisedShape3D::registered_name = "Discretised Shape3D"; END_NAMESPACE_STIR diff --git a/src/Shape_buildblock/Ellipsoid.cxx b/src/Shape_buildblock/Ellipsoid.cxx index 22ac330b79..a303c73689 100644 --- a/src/Shape_buildblock/Ellipsoid.cxx +++ b/src/Shape_buildblock/Ellipsoid.cxx @@ -31,13 +31,10 @@ START_NAMESPACE_STIR -const char * const -Ellipsoid::registered_name = "Ellipsoid"; +const char* const Ellipsoid::registered_name = "Ellipsoid"; - -void -Ellipsoid::initialise_keymap() -{ +void +Ellipsoid::initialise_keymap() { parser.add_start_key("Ellipsoid Parameters"); parser.add_key("radius-x (in mm)", &radii.x()); parser.add_key("radius-y (in mm)", &radii.y()); @@ -46,74 +43,57 @@ Ellipsoid::initialise_keymap() Shape3DWithOrientation::initialise_keymap(); } - - void -Ellipsoid::set_defaults() -{ +Ellipsoid::set_defaults() { Shape3DWithOrientation::set_defaults(); radii.fill(0); } - bool -Ellipsoid:: -post_processing() -{ - if (Shape3DWithOrientation::post_processing()==true) +Ellipsoid::post_processing() { + if (Shape3DWithOrientation::post_processing() == true) return true; - if (radii.x() <= 0) - { - warning("radii.x() should be positive, but is %g\n", radii.x()); - return true; - } - if (radii.y() <= 0) - { - warning("radii.y() should be positive, but is %g\n", radii.y()); - return true; - } - if (radii.z() <= 0) - { - warning("radii.z() should be positive, but is %g\n", radii.z()); - return true; - } + if (radii.x() <= 0) { + warning("radii.x() should be positive, but is %g\n", radii.x()); + return true; + } + if (radii.y() <= 0) { + warning("radii.y() should be positive, but is %g\n", radii.y()); + return true; + } + if (radii.z() <= 0) { + warning("radii.z() should be positive, but is %g\n", radii.z()); + return true; + } return false; } -Ellipsoid::Ellipsoid() -{ - set_defaults(); -} +Ellipsoid::Ellipsoid() { set_defaults(); } -Ellipsoid::Ellipsoid(const CartesianCoordinate3D& radii_v, - const CartesianCoordinate3D& centre_v, - const Array<2,float>& direction_vectors) - : - radii(radii_v) -{ +Ellipsoid::Ellipsoid(const CartesianCoordinate3D& radii_v, const CartesianCoordinate3D& centre_v, + const Array<2, float>& direction_vectors) + : radii(radii_v) { assert(radii.x() > 0); assert(radii.y() > 0); assert(radii.z() > 0); this->set_origin(centre_v); if (this->set_direction_vectors(direction_vectors) == Succeeded::no) error("Ellipsoid constructor called with wrong direction_vectors"); -} - +} + void -Ellipsoid:: -set_radii(const CartesianCoordinate3D& new_radii) -{ - radii = new_radii; +Ellipsoid::set_radii(const CartesianCoordinate3D& new_radii) { + radii = new_radii; assert(radii.x() > 0); assert(radii.y() > 0); assert(radii.z() > 0); } -float Ellipsoid::get_geometric_volume() const - { - return static_cast((4*radii.x()*radii.y()*radii.z()*_PI)/3) / get_volume_of_unit_cell(); - } +float +Ellipsoid::get_geometric_volume() const { + return static_cast((4 * radii.x() * radii.y() * radii.z() * _PI) / 3) / get_volume_of_unit_cell(); +} #if 0 // formula is incorrect except when it's a sphere @@ -126,47 +106,36 @@ get_geometric_area()const } #endif -bool Ellipsoid::is_inside_shape(const CartesianCoordinate3D& coord) const +bool +Ellipsoid::is_inside_shape(const CartesianCoordinate3D& coord) const { - const CartesianCoordinate3D r = - this->transform_to_shape_coords(coord); - - if (norm_squared(r / this->radii)<=1) - return true; - else - return false; + const CartesianCoordinate3D r = this->transform_to_shape_coords(coord); + + if (norm_squared(r / this->radii) <= 1) + return true; + else + return false; } - -Shape3D* Ellipsoid:: clone() const -{ - return static_cast(new Ellipsoid(*this)); +Shape3D* +Ellipsoid::clone() const { + return static_cast(new Ellipsoid(*this)); } bool -Ellipsoid:: -operator==(const Ellipsoid& cylinder) const -{ - const float tolerance = - std::min(radii.z(), std::min(radii.x(), radii.y()))/1000; - return - std::fabs(this->radii.x() - cylinder.radii.x()) < tolerance - && std::fabs(this->radii.y() - cylinder.radii.y()) < tolerance - && std::fabs(this->radii.z() - cylinder.radii.z()) < tolerance - && Shape3DWithOrientation::operator==(cylinder); -; +Ellipsoid::operator==(const Ellipsoid& cylinder) const { + const float tolerance = std::min(radii.z(), std::min(radii.x(), radii.y())) / 1000; + return std::fabs(this->radii.x() - cylinder.radii.x()) < tolerance && + std::fabs(this->radii.y() - cylinder.radii.y()) < tolerance && + std::fabs(this->radii.z() - cylinder.radii.z()) < tolerance && Shape3DWithOrientation::operator==(cylinder); + ; } bool -Ellipsoid:: -operator==(const Shape3D& shape) const -{ - Ellipsoid const * cylinder_ptr = - dynamic_cast(&shape); - return - cylinder_ptr != 0 && (*this == *cylinder_ptr); +Ellipsoid::operator==(const Shape3D& shape) const { + Ellipsoid const* cylinder_ptr = dynamic_cast(&shape); + return cylinder_ptr != 0 && (*this == *cylinder_ptr); } - END_NAMESPACE_STIR diff --git a/src/Shape_buildblock/EllipsoidalCylinder.cxx b/src/Shape_buildblock/EllipsoidalCylinder.cxx index 6c8a87525f..93ae4a4784 100644 --- a/src/Shape_buildblock/EllipsoidalCylinder.cxx +++ b/src/Shape_buildblock/EllipsoidalCylinder.cxx @@ -24,8 +24,8 @@ \author Sanida Mustafovic \author Kris Thielemans - \author C. Ross Schmidtlein (Added new parsing commands and functions so that a - sector of a cylinder can be defined and updated the + \author C. Ross Schmidtlein (Added new parsing commands and functions so that a + sector of a cylinder can be defined and updated the associated tests and volume and area calculations.)_ */ @@ -37,12 +37,10 @@ START_NAMESPACE_STIR -const char * const -EllipsoidalCylinder::registered_name = "Ellipsoidal Cylinder"; +const char* const EllipsoidalCylinder::registered_name = "Ellipsoidal Cylinder"; -void -EllipsoidalCylinder::initialise_keymap() -{ +void +EllipsoidalCylinder::initialise_keymap() { parser.add_start_key("Ellipsoidal Cylinder Parameters"); parser.add_key("radius-x (in mm)", &radius_x); parser.add_key("radius-y (in mm)", &radius_y); @@ -54,219 +52,166 @@ EllipsoidalCylinder::initialise_keymap() } void -EllipsoidalCylinder::set_defaults() -{ +EllipsoidalCylinder::set_defaults() { Shape3DWithOrientation::set_defaults(); - radius_x=0; - radius_y=0; - length=0; - theta_1=0.0; - theta_2=360.0; + radius_x = 0; + radius_y = 0; + length = 0; + theta_1 = 0.0; + theta_2 = 360.0; } bool -EllipsoidalCylinder:: -post_processing() -{ - if (Shape3DWithOrientation::post_processing()==true) +EllipsoidalCylinder::post_processing() { + if (Shape3DWithOrientation::post_processing() == true) return true; - if (radius_x <= 0) - { - warning("radius_x should be positive, but is %g\n", radius_x); - return true; - } - if (radius_y <= 0) - { - warning("radius_y should be positive, but is %g\n", radius_y); - return true; - } - if (length <= 0) - { - warning("length-z should be positive, but is %g\n", length); - return true; - } - if ((theta_1 < 0)||(theta_1>=360)) - { - warning("initial theta should be positive or less than 360 deg., but is %g\n", theta_1); - return true; - } - if ((theta_2 < 0)||(theta_2>360)) - { - warning("final theta should be positive or less than 360 deg., but is %g\n", theta_2); - return true; - } + if (radius_x <= 0) { + warning("radius_x should be positive, but is %g\n", radius_x); + return true; + } + if (radius_y <= 0) { + warning("radius_y should be positive, but is %g\n", radius_y); + return true; + } + if (length <= 0) { + warning("length-z should be positive, but is %g\n", length); + return true; + } + if ((theta_1 < 0) || (theta_1 >= 360)) { + warning("initial theta should be positive or less than 360 deg., but is %g\n", theta_1); + return true; + } + if ((theta_2 < 0) || (theta_2 > 360)) { + warning("final theta should be positive or less than 360 deg., but is %g\n", theta_2); + return true; + } return false; } +EllipsoidalCylinder::EllipsoidalCylinder() { set_defaults(); } +EllipsoidalCylinder::EllipsoidalCylinder(const float length_v, const float radius_xv, const float radius_yv, + const CartesianCoordinate3D& centre_v, const Array<2, float>& direction_vectors) + : length(length_v), radius_x(radius_xv), radius_y(radius_yv), theta_1(0.0F), theta_2(360.0F) -EllipsoidalCylinder::EllipsoidalCylinder() -{ - set_defaults(); -} - -EllipsoidalCylinder::EllipsoidalCylinder(const float length_v, - const float radius_xv, - const float radius_yv, - const CartesianCoordinate3D& centre_v, - const Array<2,float>& direction_vectors) - : - length(length_v), - radius_x(radius_xv), - radius_y(radius_yv), - theta_1(0.0F), - theta_2(360.0F) - - { - assert(length>0); - assert(radius_x>0); - assert(radius_y>0); + assert(length > 0); + assert(radius_x > 0); + assert(radius_y > 0); this->set_origin(centre_v); if (this->set_direction_vectors(direction_vectors) == Succeeded::no) error("Ellipsoid constructor called with wrong direction_vectors"); -} - -EllipsoidalCylinder::EllipsoidalCylinder(const float length_v, - const float radius_xv, - const float radius_yv, - const float theta_1v, - const float theta_2v, - const CartesianCoordinate3D& centre_v, - const Array<2,float>& direction_vectors) - : - length(length_v), - radius_x(radius_xv), - radius_y(radius_yv), - theta_1(theta_1v), - theta_2(theta_2v) - - +} + +EllipsoidalCylinder::EllipsoidalCylinder(const float length_v, const float radius_xv, const float radius_yv, const float theta_1v, + const float theta_2v, const CartesianCoordinate3D& centre_v, + const Array<2, float>& direction_vectors) + : length(length_v), radius_x(radius_xv), radius_y(radius_yv), theta_1(theta_1v), theta_2(theta_2v) + { - assert(length>0); - assert(radius_x>0); - assert(radius_y>0); + assert(length > 0); + assert(radius_x > 0); + assert(radius_y > 0); this->set_origin(centre_v); if (this->set_direction_vectors(direction_vectors) == Succeeded::no) error("Ellipsoid constructor called with wrong direction_vectors"); -} +} void -EllipsoidalCylinder:: -set_length(const float new_length) -{ - assert(new_length>0); +EllipsoidalCylinder::set_length(const float new_length) { + assert(new_length > 0); length = new_length; } void -EllipsoidalCylinder:: -set_radius_x(const float new_radius_x) -{ - assert(new_radius_x>0); +EllipsoidalCylinder::set_radius_x(const float new_radius_x) { + assert(new_radius_x > 0); radius_x = new_radius_x; } void -EllipsoidalCylinder:: -set_radius_y(const float new_radius_y) -{ - assert(new_radius_y>0); +EllipsoidalCylinder::set_radius_y(const float new_radius_y) { + assert(new_radius_y > 0); radius_y = new_radius_y; } - - -bool EllipsoidalCylinder::is_inside_shape(const CartesianCoordinate3D& coord) const +bool +EllipsoidalCylinder::is_inside_shape(const CartesianCoordinate3D& coord) const { - const CartesianCoordinate3D r = - this->transform_to_shape_coords(coord); - - const float distance_along_axis= r.z(); - - if (fabs(distance_along_axis)(_PI*theta_1/180.0); - const float phi_2 = - static_cast(_PI*theta_2/180.0); - // Adding theta cuts for partial cylinders from theta_1 to theta_2 - float theta_r = atan2(y_pos,x_pos); - if (theta_r < 0.0) - theta_r = static_cast(2.0 * _PI + theta_r); - return - (((phi_1 < phi_2)&&((theta_r >= phi_1)&&(theta_r <= phi_2))) || - ((phi_1 > phi_2)&&((theta_r >= phi_1)||(theta_r <= phi_2)))); - } - else + const CartesianCoordinate3D r = this->transform_to_shape_coords(coord); + + const float distance_along_axis = r.z(); + + if (fabs(distance_along_axis) < length / 2) { + if (square(r.x() / radius_x) + square(r.y() / radius_y) <= 1) { + const float x_pos = r.x(); + const float y_pos = r.y(); + const float phi_1 = static_cast(_PI * theta_1 / 180.0); + const float phi_2 = static_cast(_PI * theta_2 / 180.0); + // Adding theta cuts for partial cylinders from theta_1 to theta_2 + float theta_r = atan2(y_pos, x_pos); + if (theta_r < 0.0) + theta_r = static_cast(2.0 * _PI + theta_r); + return (((phi_1 < phi_2) && ((theta_r >= phi_1) && (theta_r <= phi_2))) || + ((phi_1 > phi_2) && ((theta_r >= phi_1) || (theta_r <= phi_2)))); + } else return false; - } - else return false; + } else + return false; } -float -EllipsoidalCylinder:: -get_geometric_volume()const - { - const float volume_of_unit_cell = this->get_volume_of_unit_cell(); - float A1=0, A2=0; - float T1 = theta_1; - float T2 = theta_2; - if (theta_1 == theta_2) - { - T1 = 0.0; - T2 = 360.0; - } - if (theta_1 == 360.0) T1 = 0.0; - if (theta_2 == 0.0) T2 = 360.0; - const float phi_1 = static_cast(T1*_PI/180.0); - const float phi_2 = static_cast(T2*_PI/180.0); - - // Begin Volume Calculation - if (T1 == 0.0 && T2 == 360.0) - return static_cast(_PI*radius_x*radius_y*length/volume_of_unit_cell); - if (radius_x == radius_y) - { - if (T1 < T2) - return static_cast(_PI*radius_x*radius_y*length* - (T2-T1)/360.0/volume_of_unit_cell); - else - return static_cast(_PI*radius_x*radius_y*length* - (360.0-T1+T2)/360.0/volume_of_unit_cell); - } - - if (T2 >= 0.0 && T2 < 90.0) //branch one - A2 = static_cast(atan2(radius_x / radius_y * fabs(tan(phi_2)), 1)); - if (T2 >= 90.0 && T2 < 180.0) //branch two - A2 = static_cast(atan2(radius_x / radius_y * fabs(tan(phi_2)), -1)); - if (T2 >= 180.0 && T2 < 270.0) //branch three - A2 = static_cast(2*_PI + atan2(-radius_x/radius_y * fabs(tan(phi_2)),-1)); - if (T2 >= 270.0 && T2 <= 360.0) //branch four - A2 = static_cast(2*_PI + atan2(-radius_x/radius_y * fabs(tan(phi_2)),1)); - - if (T1 >= 0.0 && T1 < 90.0) //branch one - A1 = static_cast(atan2(radius_x/radius_y * fabs(tan(phi_1)),1)); - if (T1 >= 90.0 && T1 < 180.0) //branch two - A1 = static_cast(atan2(radius_x/radius_y * fabs(tan(phi_1)),-1)); - if (T1 >= 180.0 && T1 < 270.0) //branch three - A1 = static_cast(2*_PI + atan2(-radius_x/radius_y * fabs(tan(phi_1)),-1)); - if (T1 >= 270.0 && T1 <= 360.0) //branch four - A1 = static_cast(2*_PI + atan2(-radius_x/radius_y * fabs(tan(phi_1)),1)); - - if (T1 > T2) - return static_cast(radius_x*radius_y/2.0* - (2.0*_PI - A2 + A1)*length/volume_of_unit_cell); - - return static_cast(radius_x*radius_y/2.0*(A2-A1)*length/volume_of_unit_cell); - } +float +EllipsoidalCylinder::get_geometric_volume() const { + const float volume_of_unit_cell = this->get_volume_of_unit_cell(); + float A1 = 0, A2 = 0; + float T1 = theta_1; + float T2 = theta_2; + if (theta_1 == theta_2) { + T1 = 0.0; + T2 = 360.0; + } + if (theta_1 == 360.0) + T1 = 0.0; + if (theta_2 == 0.0) + T2 = 360.0; + const float phi_1 = static_cast(T1 * _PI / 180.0); + const float phi_2 = static_cast(T2 * _PI / 180.0); + + // Begin Volume Calculation + if (T1 == 0.0 && T2 == 360.0) + return static_cast(_PI * radius_x * radius_y * length / volume_of_unit_cell); + if (radius_x == radius_y) { + if (T1 < T2) + return static_cast(_PI * radius_x * radius_y * length * (T2 - T1) / 360.0 / volume_of_unit_cell); + else + return static_cast(_PI * radius_x * radius_y * length * (360.0 - T1 + T2) / 360.0 / volume_of_unit_cell); + } + + if (T2 >= 0.0 && T2 < 90.0) // branch one + A2 = static_cast(atan2(radius_x / radius_y * fabs(tan(phi_2)), 1)); + if (T2 >= 90.0 && T2 < 180.0) // branch two + A2 = static_cast(atan2(radius_x / radius_y * fabs(tan(phi_2)), -1)); + if (T2 >= 180.0 && T2 < 270.0) // branch three + A2 = static_cast(2 * _PI + atan2(-radius_x / radius_y * fabs(tan(phi_2)), -1)); + if (T2 >= 270.0 && T2 <= 360.0) // branch four + A2 = static_cast(2 * _PI + atan2(-radius_x / radius_y * fabs(tan(phi_2)), 1)); + + if (T1 >= 0.0 && T1 < 90.0) // branch one + A1 = static_cast(atan2(radius_x / radius_y * fabs(tan(phi_1)), 1)); + if (T1 >= 90.0 && T1 < 180.0) // branch two + A1 = static_cast(atan2(radius_x / radius_y * fabs(tan(phi_1)), -1)); + if (T1 >= 180.0 && T1 < 270.0) // branch three + A1 = static_cast(2 * _PI + atan2(-radius_x / radius_y * fabs(tan(phi_1)), -1)); + if (T1 >= 270.0 && T1 <= 360.0) // branch four + A1 = static_cast(2 * _PI + atan2(-radius_x / radius_y * fabs(tan(phi_1)), 1)); + + if (T1 > T2) + return static_cast(radius_x * radius_y / 2.0 * (2.0 * _PI - A2 + A1) * length / volume_of_unit_cell); + + return static_cast(radius_x * radius_y / 2.0 * (A2 - A1) * length / volume_of_unit_cell); +} #if 0 // scaling of axes does not simply scale area @@ -340,38 +285,25 @@ get_geometric_area()const } #endif -Shape3D* -EllipsoidalCylinder:: -clone() const -{ - return static_cast(new EllipsoidalCylinder(*this)); +Shape3D* +EllipsoidalCylinder::clone() const { + return static_cast(new EllipsoidalCylinder(*this)); } bool -EllipsoidalCylinder:: -operator==(const EllipsoidalCylinder& cylinder) const -{ - const float tolerance = - std::min(length, std::min(radius_x, radius_y))/1000; - return - std::fabs(this->length - cylinder.length) < tolerance - && std::fabs(this->radius_x - cylinder.radius_x) < tolerance - && std::fabs(this->radius_y - cylinder.radius_y) < tolerance - && std::fabs(theta_1 - cylinder.theta_1) < .1F - && std::fabs(theta_2 - cylinder.theta_2) < .1F - && Shape3DWithOrientation::operator==(cylinder); - -; +EllipsoidalCylinder::operator==(const EllipsoidalCylinder& cylinder) const { + const float tolerance = std::min(length, std::min(radius_x, radius_y)) / 1000; + return std::fabs(this->length - cylinder.length) < tolerance && std::fabs(this->radius_x - cylinder.radius_x) < tolerance && + std::fabs(this->radius_y - cylinder.radius_y) < tolerance && std::fabs(theta_1 - cylinder.theta_1) < .1F && + std::fabs(theta_2 - cylinder.theta_2) < .1F && Shape3DWithOrientation::operator==(cylinder); + + ; } bool -EllipsoidalCylinder:: -operator==(const Shape3D& shape) const -{ - EllipsoidalCylinder const * cylinder_ptr = - dynamic_cast(&shape); - return - cylinder_ptr != 0 && (*this == *cylinder_ptr); +EllipsoidalCylinder::operator==(const Shape3D& shape) const { + EllipsoidalCylinder const* cylinder_ptr = dynamic_cast(&shape); + return cylinder_ptr != 0 && (*this == *cylinder_ptr); } END_NAMESPACE_STIR diff --git a/src/Shape_buildblock/RegisteredObject.cxx b/src/Shape_buildblock/RegisteredObject.cxx index 000beadb7c..6995d8842e 100644 --- a/src/Shape_buildblock/RegisteredObject.cxx +++ b/src/Shape_buildblock/RegisteredObject.cxx @@ -32,27 +32,26 @@ #ifdef __STIR_REGISTRY_NOT_INLINE -#pragma message("instantiating RegisteredObject") -#include "stir/Shape/Shape3D.h" +# pragma message("instantiating RegisteredObject") +# include "stir/Shape/Shape3D.h" // and others START_NAMESPACE_STIR template -RegisteredObject::RegistryType& -RegisteredObject::registry () -{ +RegisteredObject::RegistryType& +RegisteredObject::registry() { static RegistryType the_registry("None", 0); return the_registry; } # ifdef _MSC_VER -// prevent warning message on reinstantiation, +// prevent warning message on reinstantiation, // note that we get a linking error if we don't have the explicit instantiation below -# pragma warning(disable:4660) +# pragma warning(disable : 4660) # endif -template RegisteredObject; +template RegisteredObject; END_NAMESPACE_STIR diff --git a/src/Shape_buildblock/Shape3D.cxx b/src/Shape_buildblock/Shape3D.cxx index cfec8b9772..45aa215a82 100644 --- a/src/Shape_buildblock/Shape3D.cxx +++ b/src/Shape_buildblock/Shape3D.cxx @@ -36,7 +36,6 @@ using std::cerr; using std::endl; #endif - // Check the sampled elements of the voxel START_NAMESPACE_STIR @@ -44,30 +43,23 @@ START_NAMESPACE_STIR Shape3D* Shape3D::read_from_file(const string& filename) { // at the moment only this one - return new + return new DiscretisedShape3D(DiscretisedDensity<3,float>::read_from_file(filename)); } */ void -Shape3D:: -set_origin(const CartesianCoordinate3D& new_origin) -{ +Shape3D::set_origin(const CartesianCoordinate3D& new_origin) { this->origin = new_origin; } - void -Shape3D:: -translate(const CartesianCoordinate3D& direction) -{ - this->set_origin(this->get_origin() + direction); +Shape3D::translate(const CartesianCoordinate3D& direction) { + this->set_origin(this->get_origin() + direction); } -float -Shape3D:: -get_geometric_volume() const -{ +float +Shape3D::get_geometric_volume() const { return -1.F; } @@ -80,37 +72,23 @@ get_geometric_area() const } #endif -float -Shape3D:: -get_voxel_weight( - const CartesianCoordinate3D& voxel_centre, - const CartesianCoordinate3D& voxel_size, - const CartesianCoordinate3D& num_samples) const -{ - int value=0; - - for (float zsmall = -float(num_samples.z()-1)/num_samples.z()/2.F; - zsmall<=0.5F; - zsmall+=1.F/num_samples.z()) - { - for (float ysmall =-float(num_samples.y()-1)/num_samples.y()/2.F; - ysmall<=0.5F; - ysmall+=1.F/num_samples.y()) - { - for(float xsmall=-float(num_samples.x()-1)/num_samples.x()/2.F; - xsmall<=0.5F; - xsmall+=1.F/num_samples.x()) - { - { - const CartesianCoordinate3D r(zsmall,ysmall,xsmall); - if(is_inside_shape(voxel_centre+r*voxel_size)) - value += 1; - } +float +Shape3D::get_voxel_weight(const CartesianCoordinate3D& voxel_centre, const CartesianCoordinate3D& voxel_size, + const CartesianCoordinate3D& num_samples) const { + int value = 0; + + for (float zsmall = -float(num_samples.z() - 1) / num_samples.z() / 2.F; zsmall <= 0.5F; zsmall += 1.F / num_samples.z()) { + for (float ysmall = -float(num_samples.y() - 1) / num_samples.y() / 2.F; ysmall <= 0.5F; ysmall += 1.F / num_samples.y()) { + for (float xsmall = -float(num_samples.x() - 1) / num_samples.x() / 2.F; xsmall <= 0.5F; xsmall += 1.F / num_samples.x()) { + { + const CartesianCoordinate3D r(zsmall, ysmall, xsmall); + if (is_inside_shape(voxel_centre + r * voxel_size)) + value += 1; + } } } - } - return float(value)/(num_samples.z()*num_samples.y()*num_samples.x()); + return float(value) / (num_samples.z() * num_samples.y() * num_samples.x()); } /* Construct the volume- use the convexity, e.g @@ -119,13 +97,11 @@ get_voxel_weight( \bug Objects which are only at the edge of the image can be missed */ -void -Shape3D::construct_volume(VoxelsOnCartesianGrid &image, - const CartesianCoordinate3D& num_samples) const -{ - const CartesianCoordinate3D& voxel_size= image.get_voxel_size(); - const CartesianCoordinate3D& origin= image.get_origin(); - //if (norm(origin)>.00001) +void +Shape3D::construct_volume(VoxelsOnCartesianGrid& image, const CartesianCoordinate3D& num_samples) const { + const CartesianCoordinate3D& voxel_size = image.get_voxel_size(); + const CartesianCoordinate3D& origin = image.get_origin(); + // if (norm(origin)>.00001) // error("Shape3D::construct_volume currently ignores image origin (not shape origin)\n"); const int min_z = image.get_min_z(); const int min_y = image.get_min_y(); @@ -134,67 +110,50 @@ Shape3D::construct_volume(VoxelsOnCartesianGrid &image, const int max_y = image.get_max_y(); const int max_x = image.get_max_x(); - CartesianCoordinate3D crude_num_samples(1,1,1); + CartesianCoordinate3D crude_num_samples(1, 1, 1); + + for (int z = min_z; z <= max_z; z++) { + for (int y = min_y; y <= max_y; y++) + for (int x = min_x; x <= max_x; x++) - for(int z = min_z;z<=max_z;z++) - { - for(int y =min_y;y<=max_y;y++) - for(int x=min_x;x<=max_x;x++) - { - const CartesianCoordinate3D - current_index(static_cast(z), - static_cast(y), - static_cast(x)); - - //image[z][y][x] = get_voxel_weight(current_point,voxel_size,crude_num_samples); - - image[z][y][x] = - (is_inside_shape(current_index*voxel_size+origin)) - ? 1.F : 0.F; + const CartesianCoordinate3D current_index(static_cast(z), static_cast(y), static_cast(x)); + + // image[z][y][x] = get_voxel_weight(current_point,voxel_size,crude_num_samples); + + image[z][y][x] = (is_inside_shape(current_index * voxel_size + origin)) ? 1.F : 0.F; } } - - if (num_samples.x() == 1 && num_samples.y() == 1 && num_samples.z() == 1) + + if (num_samples.x() == 1 && num_samples.y() == 1 && num_samples.z() == 1) return; int num_recomputed = 0; - for(int z =min_z;z<=max_z;z++) - for(int y =min_y;y<=max_y;y++) - for(int x=min_x;x<= max_x;x++) - { - const float current_value = image[z][y][x]; - - // first check if we're already at an edge voxel - // Note: this allow fuzzy boundaries - bool recompute = current_value<.999F && current_value>.00F; - if (!recompute) - { - // check neighbour values. If they are all equal, we'll assume it's ok. - for(int i = z-1;!recompute && (i<=z+1);i++) - for(int j= y-1;!recompute && (j<=y+1);j++) - for(int k=x-1;!recompute && (k<=x+1);k++) - { - const float value_of_neighbour = - ((i < min_z) || (i> max_z) || - (j < min_y) || (j> max_y) || - (k < min_x) || (k> max_x) - ) ? 0 : image[i][j][k]; - recompute = (value_of_neighbour!=current_value); - } - } - if (recompute) - { - num_recomputed++; - const CartesianCoordinate3D - current_index(static_cast(z), - static_cast(y), - static_cast(x)); - image[z][y][x] = get_voxel_weight(current_index*voxel_size+origin,voxel_size,num_samples); - } + for (int z = min_z; z <= max_z; z++) + for (int y = min_y; y <= max_y; y++) + for (int x = min_x; x <= max_x; x++) { + const float current_value = image[z][y][x]; + + // first check if we're already at an edge voxel + // Note: this allow fuzzy boundaries + bool recompute = current_value < .999F && current_value > .00F; + if (!recompute) { + // check neighbour values. If they are all equal, we'll assume it's ok. + for (int i = z - 1; !recompute && (i <= z + 1); i++) + for (int j = y - 1; !recompute && (j <= y + 1); j++) + for (int k = x - 1; !recompute && (k <= x + 1); k++) { + const float value_of_neighbour = + ((i < min_z) || (i > max_z) || (j < min_y) || (j > max_y) || (k < min_x) || (k > max_x)) ? 0 : image[i][j][k]; + recompute = (value_of_neighbour != current_value); + } + } + if (recompute) { + num_recomputed++; + const CartesianCoordinate3D current_index(static_cast(z), static_cast(y), static_cast(x)); + image[z][y][x] = get_voxel_weight(current_index * voxel_size + origin, voxel_size, num_samples); + } } info(boost::format("Number of voxels recomputed with finer sampling : %1%") % num_recomputed); - } #if 0 @@ -222,28 +181,22 @@ void Shape3D::construct_slice(PixelsOnCartesianGrid &plane, plane[y][x]= get_voxel_weight(current_point,voxel_size, num_samples); } } - + #endif -void -Shape3D:: -set_defaults() -{ - origin[3] = origin[2] = origin[1] =0; +void +Shape3D::set_defaults() { + origin[3] = origin[2] = origin[1] = 0; } -void -Shape3D:: -initialise_keymap() -{ +void +Shape3D::initialise_keymap() { this->parser.add_key("origin (in mm)", &origin); } -std::string -Shape3D::parameter_info() -{ +std::string +Shape3D::parameter_info() { return ParsingObject::parameter_info(); } - END_NAMESPACE_STIR diff --git a/src/Shape_buildblock/Shape3DWithOrientation.cxx b/src/Shape_buildblock/Shape3DWithOrientation.cxx index 029a92401e..0001657bea 100644 --- a/src/Shape_buildblock/Shape3DWithOrientation.cxx +++ b/src/Shape_buildblock/Shape3DWithOrientation.cxx @@ -33,7 +33,6 @@ START_NAMESPACE_STIR - #if 0 void Shape3DWithOrientation:: @@ -69,69 +68,51 @@ set_directions_from_Euler_angles( } #endif - - -Shape3DWithOrientation::Shape3DWithOrientation() -{} - +Shape3DWithOrientation::Shape3DWithOrientation() {} Shape3DWithOrientation::Shape3DWithOrientation(const CartesianCoordinate3D& origin, - const Array<2,float>& direction_vectors) -: Shape3D(origin) -{ + const Array<2, float>& direction_vectors) + : Shape3D(origin) { if (this->set_direction_vectors(direction_vectors) == Succeeded::no) error("Shaped3DWithOrientation constructor called with wrong direction_vectors"); } Succeeded -Shape3DWithOrientation:: -set_direction_vectors(const Array<2,float>& directions) -{ +Shape3DWithOrientation::set_direction_vectors(const Array<2, float>& directions) { this->_directions = directions; if (this->_directions.size() != 3) return Succeeded::no; // set index offset to 1, such that matrix_multiply can be used with BasicCoordinate this->_directions.set_min_index(1); - for (int i=1; i<=this->_directions.get_max_index(); ++i) - { - this->_directions[i].set_min_index(1); - if (this->_directions[i].size() != 3) - return Succeeded::no; - } + for (int i = 1; i <= this->_directions.get_max_index(); ++i) { + this->_directions[i].set_min_index(1); + if (this->_directions[i].size() != 3) + return Succeeded::no; + } return Succeeded::yes; } bool -Shape3DWithOrientation:: -operator==(const Shape3DWithOrientation& s) const -{ +Shape3DWithOrientation::operator==(const Shape3DWithOrientation& s) const { const float tolerance = .001F; - return - norm(this->get_origin() - s.get_origin()) < tolerance - && norm(this->_directions[1] - s._directions[1]) < tolerance - && norm(this->_directions[2] - s._directions[2]) < tolerance - && norm(this->_directions[3] - s._directions[3]) < tolerance - && base_type::operator==(s); + return norm(this->get_origin() - s.get_origin()) < tolerance && norm(this->_directions[1] - s._directions[1]) < tolerance && + norm(this->_directions[2] - s._directions[2]) < tolerance && norm(this->_directions[3] - s._directions[3]) < tolerance && + base_type::operator==(s); } -float -Shape3DWithOrientation:: -get_volume_of_unit_cell() const -{ +float +Shape3DWithOrientation::get_volume_of_unit_cell() const { return std::fabs(determinant(this->get_direction_vectors())); } CartesianCoordinate3D -Shape3DWithOrientation:: -transform_to_shape_coords(const CartesianCoordinate3D& coord) const -{ - return - matrix_multiply(this->get_direction_vectors(), coord - this->get_origin()); +Shape3DWithOrientation::transform_to_shape_coords(const CartesianCoordinate3D& coord) const { + return matrix_multiply(this->get_direction_vectors(), coord - this->get_origin()); } -void Shape3DWithOrientation::scale(const CartesianCoordinate3D& scale3D) -{ +void +Shape3DWithOrientation::scale(const CartesianCoordinate3D& scale3D) { this->_directions[1] /= scale3D[1]; this->_directions[2] /= scale3D[2]; this->_directions[3] /= scale3D[3]; @@ -155,13 +136,11 @@ float Shape3DWithOrientation::get_angle_gamma()const return atan2(-dir_y.z(),_directions.x().z()); } #endif - -void -Shape3DWithOrientation:: -set_defaults() -{ + +void +Shape3DWithOrientation::set_defaults() { Shape3D::set_defaults(); - this->set_direction_vectors(diagonal_matrix(3,1.F)); + this->set_direction_vectors(diagonal_matrix(3, 1.F)); #if 0 // set alpha,beta,gamma to non-sensical values for parsing @@ -171,10 +150,8 @@ set_defaults() #endif } -void -Shape3DWithOrientation:: -initialise_keymap() -{ +void +Shape3DWithOrientation::initialise_keymap() { Shape3D::initialise_keymap(); #if 0 parser.add_key("Euler angle alpha (in degrees)", &alpha_in_degrees); @@ -185,9 +162,7 @@ initialise_keymap() } bool -Shape3DWithOrientation:: -post_processing() -{ +Shape3DWithOrientation::post_processing() { #if 0 if (alpha_in_degrees != 10000000.F || beta_in_degrees != 10000000.F @@ -212,19 +187,16 @@ post_processing() } #endif // make sure that indices etc are ok - if (this->set_direction_vectors(_directions) == Succeeded::no) - { - warning("Direction vectors should be a 3x3 matrix"); - return true; - } + if (this->set_direction_vectors(_directions) == Succeeded::no) { + warning("Direction vectors should be a 3x3 matrix"); + return true; + } return Shape3D::post_processing(); } void -Shape3DWithOrientation:: -set_key_values() -{ +Shape3DWithOrientation::set_key_values() { base_type::set_key_values(); #if 0 alpha_in_degrees = static_cast(get_angle_alpha()*180./_PI); diff --git a/src/Shape_buildblock/Shape_buildblock_registries.cxx b/src/Shape_buildblock/Shape_buildblock_registries.cxx index 576dba1f93..e123833a7e 100644 --- a/src/Shape_buildblock/Shape_buildblock_registries.cxx +++ b/src/Shape_buildblock/Shape_buildblock_registries.cxx @@ -25,7 +25,7 @@ \author Kris Thielemans \author C. Ross Schmidtlein (added stir::Box3D Shape class) - + */ #include "stir/Shape/Ellipsoid.h" diff --git a/src/SimSET/conv_SimSET_projdata_to_STIR.cxx b/src/SimSET/conv_SimSET_projdata_to_STIR.cxx index a682478d4f..f024d82308 100644 --- a/src/SimSET/conv_SimSET_projdata_to_STIR.cxx +++ b/src/SimSET/conv_SimSET_projdata_to_STIR.cxx @@ -15,8 +15,8 @@ See STIR/LICENSE.txt for details */ /*! - \file - \ingroup SimSET + \file + \ingroup SimSET \brief This program converts SimSET 3D sinograms to STIR format This program should normally be called from the conv_SimSET_projdata_to_STIR.sh script. @@ -24,7 +24,7 @@ \author Pablo Aguiar \author Charalampos Tsoumpas - \author Kris Thielemans + \author Kris Thielemans */ #include "stir/ProjDataInterfile.h" @@ -42,50 +42,48 @@ #include #define NUMARG 12 - -int main(int argc,char **argv) -{ +int +main(int argc, char** argv) { using namespace stir; - static const char * const options[]={ - "argv[1] SimSET file\n", - "argv[2] SimSET file format\n", - "argv[3] Angles in SimSET file\n", - "argv[4] Bins in SimSET filele\n", - "argv[5] Axial slices in SimSET file\n", - "argv[6] FOV_radius in cm as given to simset binning module (max_td)\n", - "argv[7] range on Z value in cm as given to simset binning module\n", - "argv[8] STIR scanner name\n", - "argv[9] maximum ring difference to use for writing\n", - "argv[10] index of 3d-sinogram in file (0-based)\n", - "argv[11] STIR file name\n" - }; - if (argc!=NUMARG){ + static const char* const options[] = {"argv[1] SimSET file\n", + "argv[2] SimSET file format\n", + "argv[3] Angles in SimSET file\n", + "argv[4] Bins in SimSET filele\n", + "argv[5] Axial slices in SimSET file\n", + "argv[6] FOV_radius in cm as given to simset binning module (max_td)\n", + "argv[7] range on Z value in cm as given to simset binning module\n", + "argv[8] STIR scanner name\n", + "argv[9] maximum ring difference to use for writing\n", + "argv[10] index of 3d-sinogram in file (0-based)\n", + "argv[11] STIR file name\n"}; + if (argc != NUMARG) { std::cerr << "\n\nConvert SimSET to STIR\n\n"; std::cerr << "Not enough arguments !!! ..\n"; - for (int i=1;i(atof(argv[6])*10); // times 10 for mm - const float scanner_length = static_cast(atof(argv[7])*10); // times 10 for mm - const char * const scanner_name = argv[8]; - const int max_ring_difference=atoi(argv[9]); + const char* const simset_filename = argv[1]; + const char* const input_data_type = argv[2]; + const int num_views = atoi(argv[3]); + const int num_tangential_poss = atoi(argv[4]); + const int num_rings = atoi(argv[5]); + const float FOV_radius = static_cast(atof(argv[6]) * 10); // times 10 for mm + const float scanner_length = static_cast(atof(argv[7]) * 10); // times 10 for mm + const char* const scanner_name = argv[8]; + const int max_ring_difference = atoi(argv[9]); const int dataset_num = atoi(argv[10]); - const char * const stir_filename = argv[11]; - const int nitems=num_views*num_tangential_poss; + const char* const stir_filename = argv[11]; + const int nitems = num_views * num_tangential_poss; - if (num_tangential_poss%2 != 1) + if (num_tangential_poss % 2 != 1) warning("STIR can at present not handle simset data with an even " - "number of tangential positions.\n" - "Proceed at your own risk (but you will get artifacts in the images"); - FILE *file; - if( (file=fopen(simset_filename,"rb")) ==NULL){ + "number of tangential positions.\n" + "Proceed at your own risk (but you will get artifacts in the images"); + FILE* file; + if ((file = fopen(simset_filename, "rb")) == NULL) { error("Cannot open the simset file %s", simset_filename); } shared_ptr scanner_sptr(Scanner::get_scanner_from_name(scanner_name)); @@ -93,100 +91,75 @@ int main(int argc,char **argv) error("Scanner '%s' is not a valid name", scanner_name); { - const float STIR_scanner_length = - scanner_sptr->get_num_rings() * scanner_sptr->get_ring_spacing(); - if (fabs(STIR_scanner_length - scanner_length)>1.0) - { - warning("scanner length from SimSET %g does not match STIR scanner length %g.\n" - "Continuing anyway, but this is bad.", - scanner_length, STIR_scanner_length); - } + const float STIR_scanner_length = scanner_sptr->get_num_rings() * scanner_sptr->get_ring_spacing(); + if (fabs(STIR_scanner_length - scanner_length) > 1.0) { + warning("scanner length from SimSET %g does not match STIR scanner length %g.\n" + "Continuing anyway, but this is bad.", + scanner_length, STIR_scanner_length); + } } scanner_sptr->set_num_rings(num_rings); - scanner_sptr->set_ring_spacing(scanner_length/num_rings); - scanner_sptr->set_num_detectors_per_ring(num_views*2); - shared_ptr proj_data_info_sptr( - ProjDataInfo::ProjDataInfoCTI( scanner_sptr, - /*span=*/1, - /*max_delta=*/max_ring_difference, - num_views, - num_tangential_poss, - /*arc_corrected =*/ true)); - dynamic_cast(*proj_data_info_sptr). - set_tangential_sampling(2*FOV_radius/num_tangential_poss); + scanner_sptr->set_ring_spacing(scanner_length / num_rings); + scanner_sptr->set_num_detectors_per_ring(num_views * 2); + shared_ptr proj_data_info_sptr(ProjDataInfo::ProjDataInfoCTI(scanner_sptr, + /*span=*/1, + /*max_delta=*/max_ring_difference, num_views, + num_tangential_poss, + /*arc_corrected =*/true)); + dynamic_cast(*proj_data_info_sptr) + .set_tangential_sampling(2 * FOV_radius / num_tangential_poss); shared_ptr exam_info_sptr(new ExamInfo); - ProjDataInterfile proj_data(exam_info_sptr, proj_data_info_sptr, - stir_filename, std::ios::out); - - - if(strncmp(input_data_type,"fl",2)==0) - { - } - else - { - error("file format %s not valid. Only fl at present", input_data_type); - } + ProjDataInterfile proj_data(exam_info_sptr, proj_data_info_sptr, stir_filename, std::ios::out); + if (strncmp(input_data_type, "fl", 2) == 0) { + } else { + error("file format %s not valid. Only fl at present", input_data_type); + } // skip simset header - const long offset = 32768 + dataset_num*num_rings*(long(num_rings*nitems*4)); + const long offset = 32768 + dataset_num * num_rings * (long(num_rings * nitems * 4)); if (fseek(file, offset, SEEK_SET) != 0) - error("Error while skipping simset header and data sets (%ld). Maybe file too short?", offset); - Array<1,float> seq(0,(num_rings*num_rings*nitems)-1); + error("Error while skipping simset header and data sets (%ld). Maybe file too short?", offset); + Array<1, float> seq(0, (num_rings * num_rings * nitems) - 1); read_data(file, seq /*, byteorder */); - int i_ring_difference=0; - int n=0; - while(i_ring_difference<=max_ring_difference) + int i_ring_difference = 0; + int n = 0; + while (i_ring_difference <= max_ring_difference) { + int lim_down = 0; + int lim_up = lim_down + ((num_rings - i_ring_difference - 1) * (num_rings + 1)); + for (n = lim_down; n <= lim_up;) // Extraccion de los sinogramas de una serie !!! { - int lim_down=0; - int lim_up=lim_down+((num_rings-i_ring_difference-1)*(num_rings+1)); - for(n=lim_down;n<=lim_up;) //Extraccion de los sinogramas de una serie !!! - { - - Sinogram pos_sino = proj_data.get_empty_sinogram((n-lim_down)/(num_rings+1), i_ring_difference); - Sinogram neg_sino = proj_data.get_empty_sinogram((n-lim_down)/(num_rings+1), -i_ring_difference); - Sinogram::full_iterator pos_sino_iter = pos_sino.begin_all(); - Sinogram::full_iterator neg_sino_iter = neg_sino.begin_all(); - - int ii=nitems-1; - const int i_r1r2=(n + i_ring_difference)*nitems; - const int i_r2r1=(n + i_ring_difference*num_rings)*nitems; - - // get 2 sinograms from simset data - for(int i=0;i pos_sino = proj_data.get_empty_sinogram((n - lim_down) / (num_rings + 1), i_ring_difference); + Sinogram neg_sino = proj_data.get_empty_sinogram((n - lim_down) / (num_rings + 1), -i_ring_difference); + Sinogram::full_iterator pos_sino_iter = pos_sino.begin_all(); + Sinogram::full_iterator neg_sino_iter = neg_sino.begin_all(); + + int ii = nitems - 1; + const int i_r1r2 = (n + i_ring_difference) * nitems; + const int i_r2r1 = (n + i_ring_difference * num_rings) * nitems; + + // get 2 sinograms from simset data + for (int i = 0; i < nitems / 2; ++i) { + *pos_sino_iter++ = seq[i_r1r2 + ii]; + *neg_sino_iter++ = seq[i_r2r1 + ii]; + ii = ii - 1; + } + for (int i = nitems / 2; i < nitems; ++i) { + *neg_sino_iter++ = seq[i_r1r2 + ii]; + *pos_sino_iter++ = seq[i_r2r1 + ii]; + ii = ii - 1; + } + n = n + num_rings + 1; + proj_data.set_sinogram(pos_sino); + proj_data.set_sinogram(neg_sino); } + i_ring_difference = i_ring_difference + 1; + } fclose(file); return EXIT_SUCCESS; } - - - - - - - - - - - - diff --git a/src/SimSET/conv_to_SimSET_att_image.cxx b/src/SimSET/conv_to_SimSET_att_image.cxx index fa173ef38c..8cb69348c9 100644 --- a/src/SimSET/conv_to_SimSET_att_image.cxx +++ b/src/SimSET/conv_to_SimSET_att_image.cxx @@ -9,12 +9,12 @@ it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. - + This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - + See STIR/LICENSE.txt for details */ /*! @@ -24,16 +24,16 @@ \author Charalampos Tsoumpas \author Kris Thielemans - - + + \par Usage: \code conv_to_SimSET_image [SimSET_image_filename][original_image] \endcode - Output: "SimSET_image_filename".hv AND "SimSET_image_filename".v + Output: "SimSET_image_filename".hv AND "SimSET_image_filename".v - This is a utility program converts a transmission image (in units of cm^-1 and - for 511 keV) into a SimSET attenuation input file with index values + This is a utility program converts a transmission image (in units of cm^-1 and + for 511 keV) into a SimSET attenuation input file with index values from the table below (http://depts.washington.edu/simset/html/user_guide/user_guide_index.html). This is done by a very simple segmentation of the transmission image. \verbatim @@ -42,8 +42,8 @@ Air 0 //Implemented Water 1 //Implemented Blood 2 //implemented - Bone 3 //Implemented - Brain 4 + Bone 3 //Implemented + Brain 4 Heart 5 Lung 6 //Implemented Muscle 7 @@ -63,16 +63,16 @@ Tungsten 21 Liver 22 Fat 23 - LaBr3 24 - Low viscosity polycarbonate 25 - NEMA polyethylene 26 - Polymethyl methylcrylate 27 - Polystyrene fibers 28 + LaBr3 24 + Low viscosity polycarbonate 25 + NEMA polyethylene 26 + Polymethyl methylcrylate 27 + Polystyrene fibers 28 \endverbatim See the SimSET documentation for more details. - - If at least one attenuation value in the transmission image is not segmented either - implement the new attenuation indices or change the input image. + + If at least one attenuation value in the transmission image is not segmented either + implement the new attenuation indices or change the input image. HINT: If the image is produced by the STIR utility: generate_image, the subsampling parameters should be set to 1 when small voxels sizes are used. */ @@ -81,60 +81,52 @@ #include "stir/IO/interfile.h" #include "stir/IO/read_from_file.h" #include "stir/Succeeded.h" -#include -/***********************************************************/ -int main(int argc, char *argv[]) -{ +#include +/***********************************************************/ +int +main(int argc, char* argv[]) { USING_NAMESPACE_STIR; - if (argc!=3) - { - std::cerr << "Usage:" << argv[0] - << " SimSET_image_filename original_image\n"; - return EXIT_FAILURE; - } - shared_ptr< DiscretisedDensity<3,float> > - input_image_sptr(read_from_file >(argv[2])); + if (argc != 3) { + std::cerr << "Usage:" << argv[0] << " SimSET_image_filename original_image\n"; + return EXIT_FAILURE; + } + shared_ptr> input_image_sptr(read_from_file>(argv[2])); std::string output_image_filename(argv[1]); - shared_ptr > - output_image_sptr(input_image_sptr->clone()); - bool is_implemented=true; - DiscretisedDensity<3,float>::full_iterator out_iter = output_image_sptr->begin_all(); - DiscretisedDensity<3,float>::const_full_iterator in_iter = input_image_sptr->begin_all_const(); - while( in_iter != input_image_sptr->end_all_const()) - { - // values from standard Simset file at 511keV - if (fabs(*in_iter-0.096)<0.004) // Water - *out_iter = 1.F; - else if (fabs(*in_iter-0.102)<0.004) // Blood - *out_iter = 2.F; - else if (fabs(*in_iter-0.01)<0.010001) // Air - *out_iter = 0.F; - else if (fabs(*in_iter-0.19669)<0.004) // Bone - *out_iter = 3.F; - else if (fabs(*in_iter-0.02468)<0.005) // Lung - *out_iter = 6.F; - else if (fabs(*in_iter-0.0011)<0.005) // air - *out_iter = 30.F; - else if (fabs(*in_iter-0.22548)<0.005) // Aluminum - *out_iter = 20.F; - else - { - is_implemented=false; - std::cerr << "\t" << *in_iter ; - } - ++in_iter; ++out_iter; + shared_ptr> output_image_sptr(input_image_sptr->clone()); + bool is_implemented = true; + DiscretisedDensity<3, float>::full_iterator out_iter = output_image_sptr->begin_all(); + DiscretisedDensity<3, float>::const_full_iterator in_iter = input_image_sptr->begin_all_const(); + while (in_iter != input_image_sptr->end_all_const()) { + // values from standard Simset file at 511keV + if (fabs(*in_iter - 0.096) < 0.004) // Water + *out_iter = 1.F; + else if (fabs(*in_iter - 0.102) < 0.004) // Blood + *out_iter = 2.F; + else if (fabs(*in_iter - 0.01) < 0.010001) // Air + *out_iter = 0.F; + else if (fabs(*in_iter - 0.19669) < 0.004) // Bone + *out_iter = 3.F; + else if (fabs(*in_iter - 0.02468) < 0.005) // Lung + *out_iter = 6.F; + else if (fabs(*in_iter - 0.0011) < 0.005) // air + *out_iter = 30.F; + else if (fabs(*in_iter - 0.22548) < 0.005) // Aluminum + *out_iter = 20.F; + else { + is_implemented = false; + std::cerr << "\t" << *in_iter; } + ++in_iter; + ++out_iter; + } - if(is_implemented==false) + if (is_implemented == false) std::cerr << "\nAt least one attenuation value (shown above) does not" - << "\ncorrespond to a SimSET attenuation index in the segmentation table." - << "\nImplement the new attenuation indices or change the input image. \n" - << "HINT: If produced by generate_image set the subsampling parameters to 1, \n." - << "when small voxels sizes are used.\n"; + << "\ncorrespond to a SimSET attenuation index in the segmentation table." + << "\nImplement the new attenuation indices or change the input image. \n" + << "HINT: If produced by generate_image set the subsampling parameters to 1, \n." + << "when small voxels sizes are used.\n"; // write to file as 1 byte without changing the scale - Succeeded success = - write_basic_interfile(output_image_filename, *output_image_sptr, - NumericType::UCHAR, 1.F); + Succeeded success = write_basic_interfile(output_image_filename, *output_image_sptr, NumericType::UCHAR, 1.F); return success == Succeeded::yes ? EXIT_SUCCESS : EXIT_FAILURE; } - diff --git a/src/SimSET/write_phg_image_info.c b/src/SimSET/write_phg_image_info.c index f3d6688604..8c9dc915a5 100644 --- a/src/SimSET/write_phg_image_info.c +++ b/src/SimSET/write_phg_image_info.c @@ -16,7 +16,7 @@ See STIR/LICENSE.txt for details */ /*! - \file + \file \ingroup simset \brief Write object-spec for PHG parameter file for SimSET \author Pablo Aguiar @@ -24,81 +24,77 @@ */ #include -#include +#include #include - + #define N_INPUTS 10 /* parse cmd-line and convert to cm */ -void assign_inputs(char **arg,int *nslices,int *xbins,int *ybins, - float *xMin,float *xMax,float *yMin,float *yMax, - float *zMin, float *zMax); +void assign_inputs(char** arg, int* nslices, int* xbins, int* ybins, float* xMin, float* xMax, float* yMin, float* yMax, + float* zMin, float* zMax); -int main(int argc,char **argv) -{ +int +main(int argc, char** argv) { int i; - double dz; - int nslices,xbins,ybins; - float xMin,xMax,yMin,yMax,zMin,zMax; + double dz; + int nslices, xbins, ybins; + float xMin, xMax, yMin, yMax, zMin, zMax; - char *inputs[]={ - "Number slices in object", - "Number of pixels in x-direction", - "Number of pixels in y-direction", - "xMin (mm)", - "xMax (mm)", - "yMin (mm)", - "yMax (mm)", - "zMin (mm)", - "zMax (mm)" - }; + char* inputs[] = {"Number slices in object", + "Number of pixels in x-direction", + "Number of pixels in y-direction", + "xMin (mm)", + "xMax (mm)", + "yMin (mm)", + "yMax (mm)", + "zMin (mm)", + "zMax (mm)"}; - if (argc==N_INPUTS) assign_inputs (argv,&nslices,&xbins,&ybins,&xMin,&xMax,&yMin,&yMax,&zMin,&zMax); - else - { - fprintf(stderr,"\nThis program writes the object-spec to stdout to\nhelp constructing a PHG parameter file for SimSET.\n"); - fprintf(stderr,"\nUsage:\nwrite_phg_image_info"); - for(i=0;i1?argv[1]:""); +int +Main(int argc, char** argv) { + FBP2DReconstruction reconstruction_object(argc > 1 ? argv[1] : ""); - return reconstruction_object.reconstruct() == Succeeded::yes ? - EXIT_SUCCESS : EXIT_FAILURE; -} - - + return reconstruction_object.reconstruct() == Succeeded::yes ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/src/analytic/FBP2D/FBP2DReconstruction.cxx b/src/analytic/FBP2D/FBP2DReconstruction.cxx index f734618498..78602558f9 100644 --- a/src/analytic/FBP2D/FBP2DReconstruction.cxx +++ b/src/analytic/FBP2D/FBP2DReconstruction.cxx @@ -46,61 +46,53 @@ #include #ifdef STIR_OPENMP -#include +# include #endif #include "stir/num_threads.h" START_NAMESPACE_STIR -const char * const -FBP2DReconstruction::registered_name = - "FBP2D"; +const char* const FBP2DReconstruction::registered_name = "FBP2D"; -void -FBP2DReconstruction:: -set_defaults() -{ +void +FBP2DReconstruction::set_defaults() { base_type::set_defaults(); alpha_ramp = 1; fc_ramp = 0.5; - pad_in_s=1; - display_level=0; // no display + pad_in_s = 1; + display_level = 0; // no display num_segments_to_combine = -1; back_projector_sptr.reset(new BackProjectorByBinUsingInterpolation( - /*use_piecewise_linear_interpolation = */true, - /*use_exact_Jacobian = */ false)); - + /*use_piecewise_linear_interpolation = */ true, + /*use_exact_Jacobian = */ false)); } -void -FBP2DReconstruction::initialise_keymap() -{ +void +FBP2DReconstruction::initialise_keymap() { base_type::initialise_keymap(); parser.add_start_key("FBP2DParameters"); parser.add_stop_key("End"); parser.add_key("num_segments_to_combine with SSRB", &num_segments_to_combine); - parser.add_key("Alpha parameter for Ramp filter", &alpha_ramp); - parser.add_key("Cut-off for Ramp filter (in cycles)",&fc_ramp); + parser.add_key("Alpha parameter for Ramp filter", &alpha_ramp); + parser.add_key("Cut-off for Ramp filter (in cycles)", &fc_ramp); parser.add_key("Transaxial extension for FFT", &pad_in_s); - parser.add_key("Display level",&display_level); + parser.add_key("Display level", &display_level); parser.add_parsing_key("Back projector type", &back_projector_sptr); } -void -FBP2DReconstruction:: -ask_parameters() -{ - +void +FBP2DReconstruction::ask_parameters() { + base_type::ask_parameters(); - num_segments_to_combine = ask_num("num_segments_to_combine (must be odd)",-1,101,-1); - alpha_ramp = ask_num(" Alpha parameter for Ramp filter ? ",0.,1., 1.); - fc_ramp = ask_num(" Cut-off frequency for Ramp filter ? ",0.,.5, 0.5); - pad_in_s = ask_num(" Transaxial extension for FFT : ",0,1, 1); - display_level = ask_num("Which images would you like to display \n\t(0: None, 1: Final, 2: filtered viewgrams) ? ", 0,2,0); + num_segments_to_combine = ask_num("num_segments_to_combine (must be odd)", -1, 101, -1); + alpha_ramp = ask_num(" Alpha parameter for Ramp filter ? ", 0., 1., 1.); + fc_ramp = ask_num(" Cut-off frequency for Ramp filter ? ", 0., .5, 0.5); + pad_in_s = ask_num(" Transaxial extension for FFT : ", 0, 1, 1); + display_level = ask_num("Which images would you like to display \n\t(0: None, 1: Final, 2: filtered viewgrams) ? ", 0, 2, 0); #if 0 // do not ask the user for the projectors to prevent them entering @@ -111,55 +103,48 @@ ask_parameters() BackProjectorByBin::ask_type_and_parameters(); } #endif - } -bool FBP2DReconstruction::post_processing() -{ +bool +FBP2DReconstruction::post_processing() { return base_type::post_processing(); } Succeeded -FBP2DReconstruction:: -set_up(shared_ptr const& target_data_sptr) -{ +FBP2DReconstruction::set_up(shared_ptr const& target_data_sptr) { if (base_type::set_up(target_data_sptr) == Succeeded::no) return Succeeded::no; - if (fc_ramp<=0 || fc_ramp>.5000000001) + if (fc_ramp <= 0 || fc_ramp > .5000000001) error(boost::format("Cut-off frequency has to be between 0 and .5 but is %g") % fc_ramp); - if (alpha_ramp<=0 || alpha_ramp>1.000000001) + if (alpha_ramp <= 0 || alpha_ramp > 1.000000001) error(boost::format("Alpha parameter for ramp has to be between 0 and 1 but is %g") % alpha_ramp); - if (pad_in_s<0 || pad_in_s>2) + if (pad_in_s < 0 || pad_in_s > 2) error(boost::format("padding factor has to be between 0 and 2 but is %d") % pad_in_s); - if (pad_in_s<1) + if (pad_in_s < 1) warning("Transaxial extension for FFT:=0 should ONLY be used when the non-zero data\n" "occupy only half of the FOV. Otherwise aliasing will occur!"); - if (num_segments_to_combine>=0 && num_segments_to_combine%2==0) + if (num_segments_to_combine >= 0 && num_segments_to_combine % 2 == 0) error(boost::format("num_segments_to_combine has to be odd (or -1), but is %d") % num_segments_to_combine); - if (num_segments_to_combine==-1) - { - const shared_ptr proj_data_info_cyl_sptr = - dynamic_pointer_cast(proj_data_ptr->get_proj_data_info_sptr()); + if (num_segments_to_combine == -1) { + const shared_ptr proj_data_info_cyl_sptr = + dynamic_pointer_cast(proj_data_ptr->get_proj_data_info_sptr()); - if (is_null_ptr(proj_data_info_cyl_sptr)) - num_segments_to_combine = 1; //cannot SSRB non-cylindrical data yet + if (is_null_ptr(proj_data_info_cyl_sptr)) + num_segments_to_combine = 1; // cannot SSRB non-cylindrical data yet + else { + if (proj_data_info_cyl_sptr->get_min_ring_difference(0) != proj_data_info_cyl_sptr->get_max_ring_difference(0) || + proj_data_info_cyl_sptr->get_num_segments() == 1) + num_segments_to_combine = 1; else - { - if (proj_data_info_cyl_sptr->get_min_ring_difference(0) != - proj_data_info_cyl_sptr->get_max_ring_difference(0) - || - proj_data_info_cyl_sptr->get_num_segments()==1) - num_segments_to_combine = 1; - else - num_segments_to_combine = 3; - } + num_segments_to_combine = 3; } + } if (is_null_ptr(back_projector_sptr)) error("Back projector not set."); @@ -167,31 +152,20 @@ set_up(shared_ptr const& target_data_sptr) return Succeeded::yes; } -std::string FBP2DReconstruction::method_info() const -{ +std::string +FBP2DReconstruction::method_info() const { return "FBP2D"; } -FBP2DReconstruction:: -FBP2DReconstruction(const std::string& parameter_filename) -{ +FBP2DReconstruction::FBP2DReconstruction(const std::string& parameter_filename) { initialise(parameter_filename); info(boost::format("%1%") % parameter_info()); } -FBP2DReconstruction::FBP2DReconstruction() -{ - set_defaults(); -} +FBP2DReconstruction::FBP2DReconstruction() { set_defaults(); } -FBP2DReconstruction:: -FBP2DReconstruction(const shared_ptr& proj_data_ptr_v, - const double alpha_ramp_v, - const double fc_ramp_v, - const int pad_in_s_v, - const int num_segments_to_combine_v -) -{ +FBP2DReconstruction::FBP2DReconstruction(const shared_ptr& proj_data_ptr_v, const double alpha_ramp_v, + const double fc_ramp_v, const int pad_in_s_v, const int num_segments_to_combine_v) { set_defaults(); alpha_ramp = alpha_ramp_v; @@ -201,44 +175,33 @@ FBP2DReconstruction(const shared_ptr& proj_data_ptr_v, proj_data_ptr = proj_data_ptr_v; } -Succeeded -FBP2DReconstruction:: -actual_reconstruct(shared_ptr > const & density_ptr) -{ +Succeeded +FBP2DReconstruction::actual_reconstruct(shared_ptr> const& density_ptr) { // perform SSRB - if (num_segments_to_combine>1) - { - const ProjDataInfoCylindrical& proj_data_info_cyl = - dynamic_cast - (*proj_data_ptr->get_proj_data_info_sptr()); - - // full_log << "SSRB combining " << num_segments_to_combine - // << " segments in input file to a new segment 0\n" << std::endl; - - shared_ptr - ssrb_info_sptr(SSRB(proj_data_info_cyl, - num_segments_to_combine, - 1, 0, - (num_segments_to_combine-1)/2 )); - shared_ptr - proj_data_to_FBP_ptr(new ProjDataInMemory (proj_data_ptr->get_exam_info_sptr(), ssrb_info_sptr)); - SSRB(*proj_data_to_FBP_ptr, *proj_data_ptr); - proj_data_ptr = proj_data_to_FBP_ptr; - } - else - { - // just use the proj_data_ptr we have already - } + if (num_segments_to_combine > 1) { + const ProjDataInfoCylindrical& proj_data_info_cyl = + dynamic_cast(*proj_data_ptr->get_proj_data_info_sptr()); + + // full_log << "SSRB combining " << num_segments_to_combine + // << " segments in input file to a new segment 0\n" << std::endl; + + shared_ptr ssrb_info_sptr( + SSRB(proj_data_info_cyl, num_segments_to_combine, 1, 0, (num_segments_to_combine - 1) / 2)); + shared_ptr proj_data_to_FBP_ptr(new ProjDataInMemory(proj_data_ptr->get_exam_info_sptr(), ssrb_info_sptr)); + SSRB(*proj_data_to_FBP_ptr, *proj_data_ptr); + proj_data_ptr = proj_data_to_FBP_ptr; + } else { + // just use the proj_data_ptr we have already + } // check if segment 0 has direct sinograms { - const float tan_theta = proj_data_ptr->get_proj_data_info_sptr()->get_tantheta(Bin(0,0,0,0)); - if(fabs(tan_theta ) > 1.E-4) - { - warning("FBP2D: segment 0 has non-zero tan(theta) %g", tan_theta); - return Succeeded::no; - } + const float tan_theta = proj_data_ptr->get_proj_data_info_sptr()->get_tantheta(Bin(0, 0, 0, 0)); + if (fabs(tan_theta) > 1.E-4) { + warning("FBP2D: segment 0 has non-zero tan(theta) %g", tan_theta); + return Succeeded::no; + } } float tangential_sampling; @@ -249,145 +212,113 @@ actual_reconstruct(shared_ptr > const & density_ptr) // arc-correction if necessary ArcCorrection arc_correction; bool do_arc_correction = false; - if (!is_null_ptr(dynamic_pointer_cast - (proj_data_ptr->get_proj_data_info_sptr()))) - { - // it's already arc-corrected - arc_corrected_proj_data_info_sptr = - proj_data_ptr->get_proj_data_info_sptr()->create_shared_clone(); - tangential_sampling = - dynamic_cast - (*proj_data_ptr->get_proj_data_info_sptr()).get_tangential_sampling(); - } - else - { - // TODO arc-correct to voxel_size - if (arc_correction.set_up(proj_data_ptr->get_proj_data_info_sptr()->create_shared_clone()) == - Succeeded::no) - return Succeeded::no; - do_arc_correction = true; - // TODO full_log - warning("FBP2D will arc-correct data first"); - arc_corrected_proj_data_info_sptr = - arc_correction.get_arc_corrected_proj_data_info_sptr(); - tangential_sampling = - arc_correction.get_arc_corrected_proj_data_info().get_tangential_sampling(); - } - //ProjDataInterfile ramp_filtered_proj_data(arc_corrected_proj_data_info_sptr,"ramp_filtered"); - - VoxelsOnCartesianGrid& image = - dynamic_cast&>(*density_ptr); + if (!is_null_ptr(dynamic_pointer_cast(proj_data_ptr->get_proj_data_info_sptr()))) { + // it's already arc-corrected + arc_corrected_proj_data_info_sptr = proj_data_ptr->get_proj_data_info_sptr()->create_shared_clone(); + tangential_sampling = + dynamic_cast(*proj_data_ptr->get_proj_data_info_sptr()).get_tangential_sampling(); + } else { + // TODO arc-correct to voxel_size + if (arc_correction.set_up(proj_data_ptr->get_proj_data_info_sptr()->create_shared_clone()) == Succeeded::no) + return Succeeded::no; + do_arc_correction = true; + // TODO full_log + warning("FBP2D will arc-correct data first"); + arc_corrected_proj_data_info_sptr = arc_correction.get_arc_corrected_proj_data_info_sptr(); + tangential_sampling = arc_correction.get_arc_corrected_proj_data_info().get_tangential_sampling(); + } + // ProjDataInterfile ramp_filtered_proj_data(arc_corrected_proj_data_info_sptr,"ramp_filtered"); + VoxelsOnCartesianGrid& image = dynamic_cast&>(*density_ptr); // set projector to be used for the calculations - back_projector_sptr->set_up(arc_corrected_proj_data_info_sptr, - density_ptr); - + back_projector_sptr->set_up(arc_corrected_proj_data_info_sptr, density_ptr); // set ramp filter with appropriate sizes - const int fft_size = - round(pow(2., ceil(log((double)(pad_in_s + 1)* arc_corrected_proj_data_info_sptr->get_num_tangential_poss()) / log(2.)))); - - RampFilter filter(tangential_sampling, - fft_size, - float(alpha_ramp), float(fc_ramp)); + const int fft_size = + round(pow(2., ceil(log((double)(pad_in_s + 1) * arc_corrected_proj_data_info_sptr->get_num_tangential_poss()) / log(2.)))); + + RampFilter filter(tangential_sampling, fft_size, float(alpha_ramp), float(fc_ramp)); back_projector_sptr->start_accumulating_in_new_target(); - shared_ptr - symmetries_sptr(back_projector_sptr->get_symmetries_used()->clone()); - + shared_ptr symmetries_sptr(back_projector_sptr->get_symmetries_used()->clone()); + set_num_threads(); { #ifdef STIR_OPENMP -#pragma omp for schedule(runtime) +# pragma omp for schedule(runtime) #endif - for (int view_num=proj_data_ptr->get_min_view_num(); view_num <= proj_data_ptr->get_max_view_num(); ++view_num) - { - const ViewSegmentNumbers vs_num(view_num, 0); - + for (int view_num = proj_data_ptr->get_min_view_num(); view_num <= proj_data_ptr->get_max_view_num(); ++view_num) { + const ViewSegmentNumbers vs_num(view_num, 0); + #ifndef NDEBUG -#ifdef STIR_OPENMP - info(boost::format("Thread %1% calculating view_num: %2%") % omp_get_thread_num() % view_num); -#endif +# ifdef STIR_OPENMP + info(boost::format("Thread %1% calculating view_num: %2%") % omp_get_thread_num() % view_num); +# endif #endif - - if (!symmetries_sptr->is_basic(vs_num)) - continue; - RelatedViewgrams viewgrams; + if (!symmetries_sptr->is_basic(vs_num)) + continue; + + RelatedViewgrams viewgrams; #ifdef STIR_OPENMP -#pragma omp critical(FBP2D_get_viewgrams) +# pragma omp critical(FBP2D_get_viewgrams) #endif - { - viewgrams = - proj_data_ptr->get_related_viewgrams(vs_num, symmetries_sptr); - } - - if (do_arc_correction) - viewgrams = - arc_correction.do_arc_correction(viewgrams); - - // now filter - for (RelatedViewgrams::iterator viewgram_iter = viewgrams.begin(); - viewgram_iter != viewgrams.end(); - ++viewgram_iter) - { + { viewgrams = proj_data_ptr->get_related_viewgrams(vs_num, symmetries_sptr); } + + if (do_arc_correction) + viewgrams = arc_correction.do_arc_correction(viewgrams); + + // now filter + for (RelatedViewgrams::iterator viewgram_iter = viewgrams.begin(); viewgram_iter != viewgrams.end(); + ++viewgram_iter) { #ifdef NRFFT - filter.apply(*viewgram_iter); + filter.apply(*viewgram_iter); #else - std::for_each(viewgram_iter->begin(), viewgram_iter->end(), - filter); + std::for_each(viewgram_iter->begin(), viewgram_iter->end(), filter); #endif - } - // ramp_filtered_proj_data.set_related_viewgrams(viewgrams); + } + // ramp_filtered_proj_data.set_related_viewgrams(viewgrams); - if(display_level>1) - display( viewgrams,viewgrams.find_max(),"Ramp filter"); + if (display_level > 1) + display(viewgrams, viewgrams.find_max(), "Ramp filter"); - // and backproject - back_projector_sptr->back_project(viewgrams); - } + // and backproject + back_projector_sptr->back_project(viewgrams); + } } // end of OPENMP pragma back_projector_sptr->get_output(*density_ptr); - + // Normalise the image const ProjDataInfoCylindrical& proj_data_info_cyl = - dynamic_cast - (*proj_data_ptr->get_proj_data_info_sptr()); + dynamic_cast(*proj_data_ptr->get_proj_data_info_sptr()); float magic_number = 1.F; - if (dynamic_cast(back_projector_sptr.get()) != 0) - { - // KT & Darren Hogg 17/05/2000 finally found the scale factor! - // TODO remove magic, is a scale factor in the backprojector - magic_number=2*proj_data_info_cyl.get_ring_radius()*proj_data_info_cyl.get_num_views()/proj_data_info_cyl.get_ring_spacing(); - } - else - { - if (proj_data_info_cyl.get_min_ring_difference(0)!= - proj_data_info_cyl.get_max_ring_difference(0)) - { - magic_number=.5F; - } + if (dynamic_cast(back_projector_sptr.get()) != 0) { + // KT & Darren Hogg 17/05/2000 finally found the scale factor! + // TODO remove magic, is a scale factor in the backprojector + magic_number = + 2 * proj_data_info_cyl.get_ring_radius() * proj_data_info_cyl.get_num_views() / proj_data_info_cyl.get_ring_spacing(); + } else { + if (proj_data_info_cyl.get_min_ring_difference(0) != proj_data_info_cyl.get_max_ring_difference(0)) { + magic_number = .5F; } + } #ifdef NEWSCALE // added binsize etc here to get units ok // only do this when the forward projector units are appropriate - image *= magic_number / proj_data_ptr->get_num_views() * - tangential_sampling/ - (image.get_voxel_size().x()*image.get_voxel_size().y()); + image *= magic_number / proj_data_ptr->get_num_views() * tangential_sampling / + (image.get_voxel_size().x() * image.get_voxel_size().y()); #else image *= magic_number / proj_data_ptr->get_num_views(); #endif - if (display_level>0) + if (display_level > 0) display(image, image.find_max(), "FBP image"); return Succeeded::yes; } - - END_NAMESPACE_STIR diff --git a/src/analytic/FBP2D/RampFilter.cxx b/src/analytic/FBP2D/RampFilter.cxx index 62c7cce4e7..95c6751daa 100644 --- a/src/analytic/FBP2D/RampFilter.cxx +++ b/src/analytic/FBP2D/RampFilter.cxx @@ -36,9 +36,9 @@ #include #include #ifdef BOOST_NO_STRINGSTREAM -#include +# include #else -#include +# include #endif #include @@ -52,77 +52,61 @@ START_NAMESPACE_STIR // by computing the analytic inverse Fourier transform of a cut-off ramp // times a Hamming window. -static inline float -ramp_filter_in_space(const int n, - const float sampledist, - const int length, - const float alpha, - const float fc) -{ - const double x = n*2*fc; +static inline float +ramp_filter_in_space(const int n, const float sampledist, const int length, const float alpha, const float fc) { + const double x = n * 2 * fc; // KT&Darren Hogg 17/05/2000 removed square(sampledist) as this introduced a scaling factor in the reconstructions - if (n==0) - return - static_cast((2*square(fc)*(-4 + alpha*(4 + square(_PI))))/(_PI/* *square(sampledist) */)); - else if (fabs(fabs(x)-1) < 1E-6) - return - static_cast( - -(square(2*fc)*(8*alpha + (-1 + alpha)*square(_PI)))/ - (4*_PI/* *square(sampledist) */)); + if (n == 0) + return static_cast((2 * square(fc) * (-4 + alpha * (4 + square(_PI)))) / (_PI /* *square(sampledist) */)); + else if (fabs(fabs(x) - 1) < 1E-6) + return static_cast(-(square(2 * fc) * (8 * alpha + (-1 + alpha) * square(_PI))) / (4 * _PI /* *square(sampledist) */)); else - return - static_cast( - square(2*fc)*( - -alpha - square(x) + 3*alpha*square(x) - square(square(x)) + - _PI*x*sin(_PI*x)*(-1 + square(x))* - (-alpha + (-1 + 2*alpha)*square(x)) + - cos(_PI*x)*(alpha - (1 + alpha)*square(x) + - (-1 + 2*alpha)*square(square(x))) - )/ - (_PI/* *square(sampledist) */*square(-1 + x)*square(x)*square(1 + x)) - ); + return static_cast(square(2 * fc) * + (-alpha - square(x) + 3 * alpha * square(x) - square(square(x)) + + _PI * x * sin(_PI * x) * (-1 + square(x)) * (-alpha + (-1 + 2 * alpha) * square(x)) + + cos(_PI * x) * (alpha - (1 + alpha) * square(x) + (-1 + 2 * alpha) * square(square(x)))) / + (_PI /* *square(sampledist) */ * square(-1 + x) * square(x) * square(1 + x))); } // KT&CL 03/08/99 insert max value for fc -RampFilter::RampFilter(float sampledist_v, int length , float alpha_v, float fc_v) - : +RampFilter::RampFilter(float sampledist_v, int length, float alpha_v, float fc_v) + : #ifdef NRFFT - Filter1D (length), + Filter1D(length), #endif - fc(std::min(fc_v, .5F)), alpha(alpha_v), sampledist(sampledist_v) -{ + fc(std::min(fc_v, .5F)), alpha(alpha_v), sampledist(sampledist_v) { start_timers(); // Necessary exit for the silly case when length==0 - if (length==0) + if (length == 0) return; #ifdef OLDRAMP - /* This is the straightforward implementation: - define it in complex space as abs(frequency). - This has a well-known problem that DC values are wrong. This is essentially because - the ramp filter is a continuous filter. Discrete convolution should be done - with the samples of the continuous fourier transform of the ramp. - */ - // KT&DH 17/05/2000 TODO: highly suspect that the scale factor is inappropriate -#error check scale factor in ramp filter! -/* As realft uses only positive frequencies, the filter needs to be defined - only for those frequencies, so it has length/2 elements. - However, in general the values are complex, so the numbers of - real numbers is 2*length/2==length. - */ - float f = 0.0; + /* This is the straightforward implementation: + define it in complex space as abs(frequency). + This has a well-known problem that DC values are wrong. This is essentially because + the ramp filter is a continuous filter. Discrete convolution should be done + with the samples of the continuous fourier transform of the ramp. + */ + // KT&DH 17/05/2000 TODO: highly suspect that the scale factor is inappropriate +# error check scale factor in ramp filter! + /* As realft uses only positive frequencies, the filter needs to be defined + only for those frequencies, so it has length/2 elements. + However, in general the values are complex, so the numbers of + real numbers is 2*length/2==length. + */ + float f = 0.0; for (Int i = 1; i <= length - 1; i += 2) { - f = (float) ((float) 0.5 * (i - 1) / length); - float nu_a = f ; + f = (float)((float)0.5 * (i - 1) / length); + float nu_a = f; if (f <= fc) filter[i] = nu_a * (alpha + (1. - alpha) * cos(_PI * f / fc)); else filter[i] = 0.0; - filter[i + 1] = 0.0; /* imaginary part */ + filter[i + 1] = 0.0; /* imaginary part */ } - if (0.5 <= fc) /* see realft for an explanation:data[2]=last real */ + if (0.5 <= fc) /* see realft for an explanation:data[2]=last real */ filter[2] = (0.5) * (alpha + (1. - alpha) * cos(_PI * f / fc)); else filter[2] = 0.; @@ -132,71 +116,63 @@ RampFilter::RampFilter(float sampledist_v, int length , float alpha_v, float fc_ - perform a discrete FT to find values in the frequency domain This gives better agreement with the filtering of a band-limited function with the analytic ramp (with cut-off). - In particular, it solves a problem with the DC component of the - filter. Sampling the ramp in the frequency domain gives 0 for + In particular, it solves a problem with the DC component of the + filter. Sampling the ramp in the frequency domain gives 0 for DC component, resulting in images with negative tails. */ - assert(length%2==0); + assert(length % 2 == 0); // first construct filter in 'real' space -#ifdef NRFFT +# ifdef NRFFT filter.set_offset(0); -#else - Array<1,float> filter(length); -#endif +# else + Array<1, float> filter(length); +# endif filter[0] = ramp_filter_in_space(0, sampledist, length, alpha, fc); // note: filter[length/2] is set twice for even length, but that's fine - for (int n = 1; n <= length/2; n += 1) - { + for (int n = 1; n <= length / 2; n += 1) { filter[n] = ramp_filter_in_space(n, sampledist, length, alpha, fc); - filter[length-n] = filter[n]; + filter[length - n] = filter[n]; } - //std::cerr <<"Ramp filter in real space = " < -#include +# include "stir/display.h" +# include "stir/IO/write_data.h" +# include +# include #endif #include #ifdef BOOST_NO_STRINGSTREAM -#include +# include #else -#include +# include #endif START_NAMESPACE_STIR -std::string ColsherFilter::parameter_info() const -{ +std::string +ColsherFilter::parameter_info() const { #ifdef BOOST_NO_STRINGSTREAM // dangerous for out-of-range, but 'old-style' ostrstream seems to need this char str[2000]; ostrstream s(str, 2000); #else std::ostringstream s; -#endif - s << "\nColsherFilter Parameters :=" +#endif + s << "\nColsherFilter Parameters :=" #ifdef NRFFT - << "\nFilter height := "<< height - << "\nFilter width := "<< width + << "\nFilter height := " << height << "\nFilter width := " << width #endif - << "\nMaximum aperture (theta_max) := "<< theta_max - << "\nCut-off in cycles along planar direction:= "<< fc_planar - << "\nCut-off in cycles along axial direction:= "<< fc_axial - << "\nAlpha parameter along planar direction := "< theta_max + .001F) { + warning("ColsherFilter::set_up called with theta %g larger than theta_max %g", theta, theta_max); + return Succeeded::no; + } start_timers(); - //*********** first construct filter on large grid + //*********** first construct filter on large grid /* - The Colsher filter is real-valued and symmetric. As we use - fourier_for_real_data, we have to arrange it in wrap-around order in the - axial dimension, but we need only the positive frequencies in + The Colsher filter is real-valued and symmetric. As we use + fourier_for_real_data, we have to arrange it in wrap-around order in the + axial dimension, but we need only the positive frequencies in tangential direction. */ - Array<2,std::complex > filter(IndexRange2D(height,width/2+1)); + Array<2, std::complex> filter(IndexRange2D(height, width / 2 + 1)); - // KT&Darren Hogg 03/07/2001 inserted correct scale factor + // KT&Darren Hogg 03/07/2001 inserted correct scale factor // TODO this assumes current value for the magic_number in backprojector - const float scale_factor = static_cast(4*_PI*d_a); - - for (int j = 0; j <= height/2; ++j) - { - const float fb = static_cast(j) / height; - const float nu_b = fb / d_b; - for (int k = 0; k <= width/2; ++k) - { - const float fa = (float) k / width; - const float nu_a = fa / d_a; - - float fil = 0; - if (fa < fc_planar && fb < fc_axial) - { - /* Colsher filter */ - const float omega = atan2(nu_b, nu_a); - const float psi = acos(sin(omega) * cos(theta)); - const float mod_nu = sqrt(nu_a * nu_a + nu_b * nu_b); - - if (cos(psi) >= cos(theta_max)) - fil = static_cast(mod_nu / 2. / _PI); - else - fil = static_cast(mod_nu / 4. / asin(sin(theta_max) / sin(psi))); - /* Apodizing Hanning window */; - fil *= - static_cast( - (alpha_planar + (1. - alpha_planar) * cos(_PI * fa / fc_planar)) - *(alpha_axial + (1. - alpha_axial)* cos(_PI * fb / fc_axial))); - fil *= scale_factor; - } - filter[j][k] = fil; - if (j>0) - filter[height-j][k] = fil; - } + const float scale_factor = static_cast(4 * _PI * d_a); + + for (int j = 0; j <= height / 2; ++j) { + const float fb = static_cast(j) / height; + const float nu_b = fb / d_b; + for (int k = 0; k <= width / 2; ++k) { + const float fa = (float)k / width; + const float nu_a = fa / d_a; + + float fil = 0; + if (fa < fc_planar && fb < fc_axial) { + /* Colsher filter */ + const float omega = atan2(nu_b, nu_a); + const float psi = acos(sin(omega) * cos(theta)); + const float mod_nu = sqrt(nu_a * nu_a + nu_b * nu_b); + + if (cos(psi) >= cos(theta_max)) + fil = static_cast(mod_nu / 2. / _PI); + else + fil = static_cast(mod_nu / 4. / asin(sin(theta_max) / sin(psi))); + /* Apodizing Hanning window */; + fil *= static_cast((alpha_planar + (1. - alpha_planar) * cos(_PI * fa / fc_planar)) * + (alpha_axial + (1. - alpha_axial) * cos(_PI * fb / fc_axial))); + fil *= scale_factor; + } + filter[j][k] = fil; + if (j > 0) + filter[height - j][k] = fil; } + } //*********** now find it on normal grid by passing to 'real' space - if (stretch_factor_planar>1 || stretch_factor_axial>1) - { - Array<2,float > colsher_real= - inverse_fourier_for_real_data_corrupting_input(filter); - // cut out tails. unfortunately that's a bit complicated because of wrap-around - colsher_real.resize(IndexRange2D(target_height,target_width)); - for (int j = 0; j < target_height/2; ++j) - { - for (int k = 0; k < target_width/2; k++) - { - if (j!=0) colsher_real[target_height-j][k] = colsher_real[j][k]; - if (j!=0 &&k!=0) colsher_real[target_height-j][target_width-k] = colsher_real[j][k]; - if (k!=0) colsher_real[j][target_width-k] = colsher_real[j][k]; - } - } - filter = fourier_for_real_data(colsher_real); + if (stretch_factor_planar > 1 || stretch_factor_axial > 1) { + Array<2, float> colsher_real = inverse_fourier_for_real_data_corrupting_input(filter); + // cut out tails. unfortunately that's a bit complicated because of wrap-around + colsher_real.resize(IndexRange2D(target_height, target_width)); + for (int j = 0; j < target_height / 2; ++j) { + for (int k = 0; k < target_width / 2; k++) { + if (j != 0) + colsher_real[target_height - j][k] = colsher_real[j][k]; + if (j != 0 && k != 0) + colsher_real[target_height - j][target_width - k] = colsher_real[j][k]; + if (k != 0) + colsher_real[j][target_width - k] = colsher_real[j][k]; + } } + filter = fourier_for_real_data(colsher_real); + } //*********** set kernel used for filtering - const Succeeded success= set_kernel_in_frequency_space(filter); + const Succeeded success = set_kernel_in_frequency_space(filter); stop_timers(); -#ifdef __DEBUG_COLSHER +# ifdef __DEBUG_COLSHER { // write to file /* a bit complicated because we can only write Array's of real numbers. Luckily, the Colsher filter is real in frequency space, so we can copy its real part into an Array of floats. */ - Array<2,float > real_filter(IndexRange2D(target_height,target_width/2+1)); - std::transform(filter.begin_all(), filter.end_all(), real_filter.begin_all(), - real_part/*std::real >*/); + Array<2, float> real_filter(IndexRange2D(target_height, target_width / 2 + 1)); + std::transform(filter.begin_all(), filter.end_all(), real_filter.begin_all(), real_part /*std::real >*/); char file[200]; - sprintf(file,"%s_%d_%d_%g.dat","new_colsher",target_width,target_height,theta); + sprintf(file, "%s_%d_%d_%g.dat", "new_colsher", target_width, target_height, theta); std::cout << "Saving filter : " << file << std::endl; std::ofstream s(file); - write_data(s,real_filter); + write_data(s, real_filter); } -#endif -#ifdef __DEBUG_COLSHER +# endif +# ifdef __DEBUG_COLSHER { - Array<2,float > PSF(IndexRange2D(-0,target_height/3,-target_width/3,target_width/3)); + Array<2, float> PSF(IndexRange2D(-0, target_height / 3, -target_width / 3, target_width / 3)); PSF.fill(0); - PSF[0][0]=1; + PSF[0][0] = 1; this->operator()(PSF); - display(PSF,"PSF",PSF.find_max()/20); + display(PSF, "PSF", PSF.find_max() / 20); } -#endif +# endif return success; } - -#else //NRFFT - -ColsherFilter::ColsherFilter(int height_v, int width_v, float gamma_v, float theta_max_v, - float d_a_v, float d_b_v, - float alpha_colsher_axial_v, float fc_colsher_axial_v, - float alpha_colsher_planar_v, float fc_colsher_planar_v) - : Filter2D(height_v, width_v),gamma(gamma_v), theta_max(theta_max_v), d_a(d_a_v), d_b(d_b_v), - alpha_axial(alpha_colsher_axial_v), fc_axial(fc_colsher_axial_v), - alpha_planar(alpha_colsher_planar_v), fc_planar(fc_colsher_planar_v) -{ - - if (height==0 || width==0) + +#else // NRFFT + +ColsherFilter::ColsherFilter(int height_v, int width_v, float gamma_v, float theta_max_v, float d_a_v, float d_b_v, + float alpha_colsher_axial_v, float fc_colsher_axial_v, float alpha_colsher_planar_v, + float fc_colsher_planar_v) + : Filter2D(height_v, width_v), gamma(gamma_v), theta_max(theta_max_v), d_a(d_a_v), d_b(d_b_v), + alpha_axial(alpha_colsher_axial_v), fc_axial(fc_colsher_axial_v), alpha_planar(alpha_colsher_planar_v), + fc_planar(fc_colsher_planar_v) { + + if (height == 0 || width == 0) return; - int k, j; - float fa, fb, omega, psi; - float mod_nu, nu_a, nu_b; - double fil; - /* - * The Colsher filter is real-valued, so it has only height*width elements, - * going from [1..height*width]. It is arranged in wrap-around order - * in both dimensions, see Num.Rec.C, page 523 - */ - - int ii = 1;//float fmax = 0.F; - - // TODO don't use 0.5* for upper boundary - for (j = 0; j <= 0.5 * height; j++) { - fb = (float) j / height; - nu_b = fb / d_b; - for (k = 0; k <= 0.5 * width; k++) { - fa = (float) k / width; - nu_a = fa / d_a; - if (fa == 0. && fb == 0.) { - filter[ii++] = 0.F; - continue; - } - - omega = atan2(nu_b, nu_a); - psi = acos(sin(omega) * sin(gamma)); - mod_nu = sqrt(nu_a * nu_a + nu_b * nu_b); - - /* Colsher formula = fil = mod_nu / 4. / asin(sin(theta_max) / sin(psi)); */ - if (cos(psi) >= cos(theta_max)) - fil = mod_nu / 2. / _PI; - else - fil = mod_nu / 4. / asin(sin(theta_max) / sin(psi)); - - - /* Apodizing Hanning window */; - //CL 250899 In order to make similar to the CTI program, we use a damping window - //for both planar and axial direction - - if (fa < fc_planar && fb < fc_axial) - filter[ii++] = - static_cast( - fil * (alpha_planar + (1. - alpha_planar) * cos(_PI * fa / fc_planar)) - *(alpha_axial + (1. - alpha_axial)* cos(_PI * fb / fc_axial))); - else - filter[ii++] = 0.F; - - } - - - for (k = int (-0.5 * width) + 1; k <= -1; k++) { - fa = (float) k / width; - nu_a = fa / d_a; - omega = atan2(nu_b, -nu_a); - psi = acos(sin(omega) * sin(gamma)); - - - mod_nu = sqrt(nu_a * nu_a + nu_b * nu_b); - - if (cos(psi) >= cos(theta_max)) - fil = mod_nu / 2. / _PI; - else - fil = mod_nu / 4. / asin(sin(theta_max) / sin(psi)); - - - if (-fa < fc_planar && fb < fc_axial) - filter[ii++] = - static_cast(fil * (alpha_planar + (1. - alpha_planar) * cos(_PI * (-fa) / fc_planar)) - *(alpha_axial + (1. - alpha_axial)* cos(_PI * fb / fc_axial))); - else - filter[ii++] = 0.F; - - } + int k, j; + float fa, fb, omega, psi; + float mod_nu, nu_a, nu_b; + double fil; + /* + * The Colsher filter is real-valued, so it has only height*width elements, + * going from [1..height*width]. It is arranged in wrap-around order + * in both dimensions, see Num.Rec.C, page 523 + */ + + int ii = 1; // float fmax = 0.F; + + // TODO don't use 0.5* for upper boundary + for (j = 0; j <= 0.5 * height; j++) { + fb = (float)j / height; + nu_b = fb / d_b; + for (k = 0; k <= 0.5 * width; k++) { + fa = (float)k / width; + nu_a = fa / d_a; + if (fa == 0. && fb == 0.) { + filter[ii++] = 0.F; + continue; + } + + omega = atan2(nu_b, nu_a); + psi = acos(sin(omega) * sin(gamma)); + mod_nu = sqrt(nu_a * nu_a + nu_b * nu_b); + + /* Colsher formula = fil = mod_nu / 4. / asin(sin(theta_max) / sin(psi)); */ + if (cos(psi) >= cos(theta_max)) + fil = mod_nu / 2. / _PI; + else + fil = mod_nu / 4. / asin(sin(theta_max) / sin(psi)); + + /* Apodizing Hanning window */; + // CL 250899 In order to make similar to the CTI program, we use a damping window + // for both planar and axial direction + + if (fa < fc_planar && fb < fc_axial) + filter[ii++] = static_cast(fil * (alpha_planar + (1. - alpha_planar) * cos(_PI * fa / fc_planar)) * + (alpha_axial + (1. - alpha_axial) * cos(_PI * fb / fc_axial))); + else + filter[ii++] = 0.F; } - - for (j = int (-0.5 * height) + 1; j <= -1; j++) { - fb = (float) j / height; - nu_b = fb / d_b; - for (k = 0; k <= 0.5 * width; k++) { - fa = (float) k / width; - nu_a = fa / d_a; - omega = atan2(-nu_b, nu_a); - psi = acos(sin(omega) * sin(gamma)); - - mod_nu = sqrt(nu_a * nu_a + nu_b * nu_b); - - if (cos(psi) >= cos(theta_max)) - fil = mod_nu / 2. / _PI; - else - fil = mod_nu / 4. / asin(sin(theta_max) / sin(psi)); - - - if (fa < fc_planar && -fb < fc_axial) - filter[ii++] = - static_cast(fil * (alpha_planar + (1. - alpha_planar) * cos(_PI * fa / fc_planar)) - *(alpha_axial + (1. - alpha_axial)* cos(_PI * (-fb) / fc_axial))); - else - filter[ii++] = 0.F; - - } - - for (k = int (-0.5 * width) + 1; k <= -1; k++) { - fa = (float) k / width; - nu_a = fa / d_a; - omega = atan2(-nu_b, -nu_a); - psi = acos(sin(omega) * sin(gamma)); - - mod_nu = sqrt(nu_a * nu_a + nu_b * nu_b); - - if (cos(psi) >= cos(theta_max)) - fil = mod_nu / 2. / _PI; - else - fil = mod_nu / 4. / asin(sin(theta_max) / sin(psi)); - - if (-fa < fc_planar && -fb < fc_axial) - filter[ii++] = - static_cast(fil * (alpha_planar + (1. - alpha_planar) * cos(_PI * (-fa) / fc_planar)) - *(alpha_axial + (1. - alpha_axial)* cos(_PI * (-fb) / fc_axial))); - else - filter[ii++] = 0.F; - - - } + + for (k = int(-0.5 * width) + 1; k <= -1; k++) { + fa = (float)k / width; + nu_a = fa / d_a; + omega = atan2(nu_b, -nu_a); + psi = acos(sin(omega) * sin(gamma)); + + mod_nu = sqrt(nu_a * nu_a + nu_b * nu_b); + + if (cos(psi) >= cos(theta_max)) + fil = mod_nu / 2. / _PI; + else + fil = mod_nu / 4. / asin(sin(theta_max) / sin(psi)); + + if (-fa < fc_planar && fb < fc_axial) + filter[ii++] = static_cast(fil * (alpha_planar + (1. - alpha_planar) * cos(_PI * (-fa) / fc_planar)) * + (alpha_axial + (1. - alpha_axial) * cos(_PI * fb / fc_axial))); + else + filter[ii++] = 0.F; } + } - // KT&Darren Hogg 03/07/2001 inserted correct scale factor - // TODO this assumes current value for the magic_number in backprojector - filter *= static_cast(4*_PI*d_a); + for (j = int(-0.5 * height) + 1; j <= -1; j++) { + fb = (float)j / height; + nu_b = fb / d_b; + for (k = 0; k <= 0.5 * width; k++) { + fa = (float)k / width; + nu_a = fa / d_a; + omega = atan2(-nu_b, nu_a); + psi = acos(sin(omega) * sin(gamma)); + + mod_nu = sqrt(nu_a * nu_a + nu_b * nu_b); + + if (cos(psi) >= cos(theta_max)) + fil = mod_nu / 2. / _PI; + else + fil = mod_nu / 4. / asin(sin(theta_max) / sin(psi)); + + if (fa < fc_planar && -fb < fc_axial) + filter[ii++] = static_cast(fil * (alpha_planar + (1. - alpha_planar) * cos(_PI * fa / fc_planar)) * + (alpha_axial + (1. - alpha_axial) * cos(_PI * (-fb) / fc_axial))); + else + filter[ii++] = 0.F; + } - -#ifdef __DEBUG_COLSHER + for (k = int(-0.5 * width) + 1; k <= -1; k++) { + fa = (float)k / width; + nu_a = fa / d_a; + omega = atan2(-nu_b, -nu_a); + psi = acos(sin(omega) * sin(gamma)); + + mod_nu = sqrt(nu_a * nu_a + nu_b * nu_b); + + if (cos(psi) >= cos(theta_max)) + fil = mod_nu / 2. / _PI; + else + fil = mod_nu / 4. / asin(sin(theta_max) / sin(psi)); + + if (-fa < fc_planar && -fb < fc_axial) + filter[ii++] = static_cast(fil * (alpha_planar + (1. - alpha_planar) * cos(_PI * (-fa) / fc_planar)) * + (alpha_axial + (1. - alpha_axial) * cos(_PI * (-fb) / fc_axial))); + else + filter[ii++] = 0.F; + } + } + + // KT&Darren Hogg 03/07/2001 inserted correct scale factor + // TODO this assumes current value for the magic_number in backprojector + filter *= static_cast(4 * _PI * d_a); + +# ifdef __DEBUG_COLSHER { char file[200]; - sprintf(file,"%s_%d_%d_%g.dat","old_colsher",width,height,_PI/2-gamma); + sprintf(file, "%s_%d_%d_%g.dat", "old_colsher", width, height, _PI / 2 - gamma); std::cout << "Saving filter : " << file << std::endl; std::ofstream s(file); - write_data(s,filter); + write_data(s, filter); } -#endif +# endif } -void Filter_proj_Colsher(Viewgram & view_i, - Viewgram & view_i1, - ColsherFilter& CFilter, - int PadS, int PadZ) -{ +void +Filter_proj_Colsher(Viewgram& view_i, Viewgram& view_i1, ColsherFilter& CFilter, int PadS, int PadZ) { // start_timers(); - + const int rmin = view_i.get_min_axial_pos_num(); const int rmax = view_i.get_max_axial_pos_num(); - int nrings = rmax - rmin + 1; + int nrings = rmax - rmin + 1; int nprojs = view_i.get_num_tangential_poss(); - - int width = (int) pow(2, ((int) ceil(log((PadS + 1.) * nprojs) / log(2.)))); - int height = (int) pow(2, ((int) ceil(log((PadZ + 1.) * nrings) / log(2.)))); - + + int width = (int)pow(2, ((int)ceil(log((PadS + 1.) * nprojs) / log(2.)))); + int height = (int)pow(2, ((int)ceil(log((PadZ + 1.) * nrings) / log(2.)))); + const int maxproj = view_i.get_max_tangential_pos_num(); const int minproj = view_i.get_min_tangential_pos_num(); - - int roffset = -rmin * width *2; - Array<1,float> data(1,2 * height * width); - - - for (int j = rmin; j <= rmax; j++) - { + + int roffset = -rmin * width * 2; + Array<1, float> data(1, 2 * height * width); + + for (int j = rmin; j <= rmax; j++) { for (int k = minproj; k <= maxproj; k++) { data[roffset + 2 * j * width + 2 * (k - minproj) + 1] = view_i[j][k]; data[roffset + 2 * j * width + 2 * (k - minproj) + 2] = view_i1[j][k]; } } - { - - CFilter.apply(data); - } - - - - - for (int j = rmin; j <= rmax; j++) - { - for (int k = minproj; k <= maxproj; k++) - { + { CFilter.apply(data); } + + for (int j = rmin; j <= rmax; j++) { + for (int k = minproj; k <= maxproj; k++) { view_i[j][k] = data[roffset + 2 * j * width + 2 * (k - minproj) + 1]; - view_i1[j][k] =data[roffset + 2 * j * width + 2 * (k - minproj) + 2]; + view_i1[j][k] = data[roffset + 2 * j * width + 2 * (k - minproj) + 2]; } } - + // stop_timers(); - } -#endif //NRFFT +#endif // NRFFT END_NAMESPACE_STIR diff --git a/src/analytic/FBP3DRP/FBP3DRP.cxx b/src/analytic/FBP3DRP/FBP3DRP.cxx index a21749f65e..e221d23c2f 100644 --- a/src/analytic/FBP3DRP/FBP3DRP.cxx +++ b/src/analytic/FBP3DRP/FBP3DRP.cxx @@ -1,9 +1,9 @@ // // -/*! - \file +/*! + \file \ingroup reconstructors - \brief Main program for FBP3DRP reconstruction + \brief Main program for FBP3DRP reconstruction \author Claire LABBE \author PARAPET project */ @@ -29,26 +29,21 @@ #include "stir/analytic/FBP3DRP/FBP3DRPReconstruction.h" #ifndef PARALLEL -#define Main main +# define Main main #else -#define Main master_main +# define Main master_main #endif #ifndef STIR_NO_NAMESPACE using std::endl; using std::cerr; -#endif +#endif USING_NAMESPACE_STIR - -int Main(int argc, char **argv) -{ - FBP3DRPReconstruction - reconstruction_object(argc>1?argv[1]:""); - - return reconstruction_object.reconstruct() == Succeeded::yes ? - EXIT_SUCCESS : EXIT_FAILURE; -} - +int +Main(int argc, char** argv) { + FBP3DRPReconstruction reconstruction_object(argc > 1 ? argv[1] : ""); + return reconstruction_object.reconstruct() == Succeeded::yes ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/src/analytic/FBP3DRP/FBP3DRPReconstruction.cxx b/src/analytic/FBP3DRP/FBP3DRPReconstruction.cxx index dfea6caed1..b64d6fbad1 100644 --- a/src/analytic/FBP3DRP/FBP3DRPReconstruction.cxx +++ b/src/analytic/FBP3DRP/FBP3DRPReconstruction.cxx @@ -1,5 +1,5 @@ -/*! - \file +/*! + \file \ingroup FBP3DRP \brief FBP3DRP reconstruction implementation \author Kris Thielemans @@ -33,9 +33,9 @@ KT 05/10/2003 - decrease dependency on symmetries by using symmetries_ptr->is_basic(). - Before this, we relied explicitly on the range + Before this, we relied explicitly on the range 0<=segment_num, 0<=view_num<=num_views()/4 - This range was fine when using the interpolating backprojector + This range was fine when using the interpolating backprojector and x and y voxel size are equal. - moved some 2D-reconstruction stuff to FBP2DReconstruction class. - merged Parameter class into Reconstruction class @@ -46,11 +46,11 @@ - corrected bug in virtual_ring_offset in case of images with an even number of planes - some adjustements to allow for even number of planes - - make sure that everything works when there are no missing projections + - make sure that everything works when there are no missing projections in the data (i.e. rmin>rmin_orig) KT 11/04/2000 - removed (old!) bug by adjusting range for rmin (and hence rmax) - to use 'floor' instead of 'ceil'. Result was that sometimes 1 + to use 'floor' instead of 'ceil'. Result was that sometimes 1 missing projection was not filled in. So, better axial uniformity now. - moved rmin,rmax determination to a separate function, as this is now more complicated They are now determined in virtual_ring_units, even for the span case. span case @@ -64,12 +64,12 @@ - approximate analytic integral over delta by having a 1/2 in the backprojection of the last segment */ -//CL 1st June 1999 +// CL 1st June 1999 // DIstinguish the alpha and Nyquist parameters from RAmp and Colsher filter // by alpha_ramp and alpha_colsher (i.e for fc) -//CL 15 MARCH 1999 -//NOW DONE: +// CL 15 MARCH 1999 +// NOW DONE: // 1. Remove the write_PSOV_interfile_header as it is not needed // 2. Change the defaut value of mashing to 1 instead of 0 // 3. Change the fovrad formula for calculating rmin and rmax @@ -86,7 +86,7 @@ #include "stir/Coordinate3D.h" #include "stir/Succeeded.h" -#include "stir/analytic/FBP3DRP/ColsherFilter.h" +#include "stir/analytic/FBP3DRP/ColsherFilter.h" #include "stir/display.h" //#include "stir/recon_buildblock/distributable.h" //#include "stir/FBP3DRP/process_viewgrams.h" @@ -104,7 +104,7 @@ #include #include #include -#include +#include // for asctime() #include @@ -120,77 +120,63 @@ using std::ios; START_NAMESPACE_STIR - - // should be private member, TODO static ofstream full_log; // terribly ugly. can be replaced using LORCoordinates stuff (TODO) -static void find_rmin_rmax(int& rmin, int& rmax, - const ProjDataInfoCylindrical& proj_data_info_cyl, - const int seg_num, - const VoxelsOnCartesianGrid& image) -{ - - const float fovrad = - proj_data_info_cyl.get_s(Bin(0,0,0,proj_data_info_cyl.get_num_tangential_poss()/2 - 1)); +static void +find_rmin_rmax(int& rmin, int& rmax, const ProjDataInfoCylindrical& proj_data_info_cyl, const int seg_num, + const VoxelsOnCartesianGrid& image) { + + const float fovrad = proj_data_info_cyl.get_s(Bin(0, 0, 0, proj_data_info_cyl.get_num_tangential_poss() / 2 - 1)); // Compute minimum and maximum rings of 'missing' projections - - const float delta=proj_data_info_cyl.get_average_ring_difference(seg_num); - + + const float delta = proj_data_info_cyl.get_average_ring_difference(seg_num); + // find correspondence between ring coordinates and image coordinates: // z = num_planes_per_virtual_ring * ring + virtual_ring_offset - // compute the offset by matching up the centre of the scanner + // compute the offset by matching up the centre of the scanner // in the 2 coordinate systems // TODO get all this from ProjDataInfo or so const int num_planes_per_virtual_ring = - (proj_data_info_cyl.get_max_ring_difference(seg_num) == proj_data_info_cyl.get_min_ring_difference(seg_num)) ? 2 : 1; + (proj_data_info_cyl.get_max_ring_difference(seg_num) == proj_data_info_cyl.get_min_ring_difference(seg_num)) ? 2 : 1; const int num_virtual_rings_per_physical_ring = - (proj_data_info_cyl.get_max_ring_difference(seg_num) == proj_data_info_cyl.get_min_ring_difference(seg_num)) ? 1 : 2; - - - const float virtual_ring_offset = - (image.get_max_z() + image.get_min_z())/2.F - - num_planes_per_virtual_ring - *(proj_data_info_cyl.get_max_axial_pos_num(seg_num)+ num_virtual_rings_per_physical_ring*delta - + proj_data_info_cyl.get_min_axial_pos_num(seg_num))/2; - - + (proj_data_info_cyl.get_max_ring_difference(seg_num) == proj_data_info_cyl.get_min_ring_difference(seg_num)) ? 1 : 2; + + const float virtual_ring_offset = + (image.get_max_z() + image.get_min_z()) / 2.F - + num_planes_per_virtual_ring * + (proj_data_info_cyl.get_max_axial_pos_num(seg_num) + num_virtual_rings_per_physical_ring * delta + + proj_data_info_cyl.get_min_axial_pos_num(seg_num)) / + 2; + // we first consider the LOR at s=0, phi=0 which goes through z=0,y=0, x=fovrad // later on, we will shift it to the 'left'most edge of the FOV. - - // find z position of intersection of this LOR with the detector radius + + // find z position of intersection of this LOR with the detector radius // (i.e. y=0, x=-ring_radius) // use image coordinates first - float z_in_image_coordinates = - -fabs(delta)*num_planes_per_virtual_ring*num_virtual_rings_per_physical_ring* - (fovrad + proj_data_info_cyl.get_ring_radius())/(2*proj_data_info_cyl.get_ring_radius()); - // now shift it to the edge of the FOV + float z_in_image_coordinates = -fabs(delta) * num_planes_per_virtual_ring * num_virtual_rings_per_physical_ring * + (fovrad + proj_data_info_cyl.get_ring_radius()) / (2 * proj_data_info_cyl.get_ring_radius()); + // now shift it to the edge of the FOV // (taking into account that z==get_min_z() is in the middle of the voxel) z_in_image_coordinates += image.get_min_z() - .5F; - + // now convert to virtual_ring_coordinates using z = num_planes_per_virtual_ring * ring + virtual_ring_offset - const float z_in_virtual_ring_coordinates = - (z_in_image_coordinates - virtual_ring_offset)/num_planes_per_virtual_ring; + const float z_in_virtual_ring_coordinates = (z_in_image_coordinates - virtual_ring_offset) / num_planes_per_virtual_ring; // finally find the 'ring' number rmin = static_cast(floor(z_in_virtual_ring_coordinates)); - - - // rmax is determined by using symmetry: at both ends there are just as many missing rings - rmax = proj_data_info_cyl.get_max_axial_pos_num(seg_num) + (proj_data_info_cyl.get_min_axial_pos_num(seg_num) - rmin); -} + // rmax is determined by using symmetry: at both ends there are just as many missing rings + rmax = proj_data_info_cyl.get_max_axial_pos_num(seg_num) + (proj_data_info_cyl.get_min_axial_pos_num(seg_num) - rmin); +} -const char * const -FBP3DRPReconstruction::registered_name = - "FBP3DRP"; +const char* const FBP3DRPReconstruction::registered_name = "FBP3DRP"; -void -FBP3DRPReconstruction:: -set_defaults() -{ +void +FBP3DRPReconstruction::set_defaults() { base_type::set_defaults(); alpha_colsher_axial = 1; @@ -199,29 +185,26 @@ set_defaults() fc_colsher_planar = 0.5; alpha_ramp = 1; fc_ramp = 0.5; - + num_segments_to_combine = -1; PadS = 1; PadZ = 1; - colsher_stretch_factor_planar=2; - colsher_stretch_factor_axial=2; - - display_level=0; - save_intermediate_files=0; - - forward_projector_sptr. - reset(new ForwardProjectorByBinUsingRayTracing); - back_projector_sptr. - reset(new BackProjectorByBinUsingInterpolation( - /*use_piecewise_linear_interpolation = */false, - /*use_exact_Jacobian = */ false)); + colsher_stretch_factor_planar = 2; + colsher_stretch_factor_axial = 2; + + display_level = 0; + save_intermediate_files = 0; + + forward_projector_sptr.reset(new ForwardProjectorByBinUsingRayTracing); + back_projector_sptr.reset(new BackProjectorByBinUsingInterpolation( + /*use_piecewise_linear_interpolation = */ false, + /*use_exact_Jacobian = */ false)); } -void -FBP3DRPReconstruction::initialise_keymap() -{ +void +FBP3DRPReconstruction::initialise_keymap() { base_type::initialise_keymap(); parser.add_start_key("FBP3DRPParameters"); @@ -233,81 +216,70 @@ FBP3DRPReconstruction::initialise_keymap() // TODO move to 2D recon parser.add_key("num_segments_to_combine with SSRB", &num_segments_to_combine); - parser.add_key("Alpha parameter for Ramp filter", &alpha_ramp); - parser.add_key("Cut-off for Ramp filter (in cycles)",&fc_ramp); - + parser.add_key("Alpha parameter for Ramp filter", &alpha_ramp); + parser.add_key("Cut-off for Ramp filter (in cycles)", &fc_ramp); + parser.add_key("Transaxial extension for FFT", &PadS); parser.add_key("Axial extension for FFT", &PadZ); - - parser.add_key("Alpha parameter for Colsher filter in axial direction", - &alpha_colsher_axial); - parser.add_key("Cut-off for Colsher filter in axial direction (in cycles)", - &fc_colsher_axial); - parser.add_key("Alpha parameter for Colsher filter in planar direction", - &alpha_colsher_planar); - parser.add_key("Cut-off for Colsher filter in planar direction (in cycles)", - &fc_colsher_planar); - parser.add_key("Stretch factor for Colsher filter definition in axial direction", - &colsher_stretch_factor_axial); - parser.add_key("Stretch factor for Colsher filter definition in planar direction", - &colsher_stretch_factor_planar); + + parser.add_key("Alpha parameter for Colsher filter in axial direction", &alpha_colsher_axial); + parser.add_key("Cut-off for Colsher filter in axial direction (in cycles)", &fc_colsher_axial); + parser.add_key("Alpha parameter for Colsher filter in planar direction", &alpha_colsher_planar); + parser.add_key("Cut-off for Colsher filter in planar direction (in cycles)", &fc_colsher_planar); + parser.add_key("Stretch factor for Colsher filter definition in axial direction", &colsher_stretch_factor_axial); + parser.add_key("Stretch factor for Colsher filter definition in planar direction", &colsher_stretch_factor_planar); parser.add_parsing_key("Back projector type", &back_projector_sptr); parser.add_parsing_key("Forward projector type", &forward_projector_sptr); parser.add_key("Save intermediate images", &save_intermediate_files); - parser.add_key("Display level",&display_level); + parser.add_key("Display level", &display_level); } +void +FBP3DRPReconstruction::ask_parameters() { - -void -FBP3DRPReconstruction::ask_parameters() -{ - base_type::ask_parameters(); - - // bool on_disk = !ask("(1) Read data into memory all at once ?", false); -// TODO move to Reconstruction - -// PARAMETERS => DISPLAY_LEVEL - - display_level = ask_num("Which images would you like to display \n\t(0: None, 1: Final, 2: intermediate, 3: after each view) ? ", 0,3,0); + // bool on_disk = !ask("(1) Read data into memory all at once ?", false); + // TODO move to Reconstruction - save_intermediate_files =ask_num("Would you like to save all the intermediate images ? ",0,1,0 ); + // PARAMETERS => DISPLAY_LEVEL - image_for_reprojection_filename = - ask_string("filename of image to be reprojected (empty for using FBP):"); + display_level = + ask_num("Which images would you like to display \n\t(0: None, 1: Final, 2: intermediate, 3: after each view) ? ", 0, 3, 0); -// PARAMETERS => ZEROES-PADDING IN FFT (PADS, PADZ) - cerr << "\nFilter parameters for 2D and 3D reconstruction"; + save_intermediate_files = ask_num("Would you like to save all the intermediate images ? ", 0, 1, 0); + + image_for_reprojection_filename = ask_string("filename of image to be reprojected (empty for using FBP):"); + + // PARAMETERS => ZEROES-PADDING IN FFT (PADS, PADZ) + cerr << "\nFilter parameters for 2D and 3D reconstruction"; #if 0 PadS = ask_num(" Transaxial extension for FFT : ",0,2, 1); PadZ = ask_num(" Axial extension for FFT :",0,2, 1); #endif -// PARAMETERS => 2D RECONSTRUCTION RAMP FILTER (ALPHA, FC) - cerr << endl << "For 2D reconstruction filtering (Ramp filter) : " ; + // PARAMETERS => 2D RECONSTRUCTION RAMP FILTER (ALPHA, FC) + cerr << endl << "For 2D reconstruction filtering (Ramp filter) : "; + + num_segments_to_combine = ask_num( + "num_segments_to_combine (must be odd).\nDefault means 1 or 3 depending on axial compression of input", -1, 101, -1); + // TODO check odd + alpha_ramp = ask_num(" Alpha parameter for Ramp filter ? ", 0., 1., 1.); + + fc_ramp = ask_num(" Cut-off frequency for Ramp filter ? ", 0., .5, 0.5); + + // PARAMETERS => 3D RECONSTRUCTION COLSHER FILTER (ALPHA, FC) + cerr << "\nFor 3D reconstruction filtering (Colsher filter) : "; - num_segments_to_combine = ask_num("num_segments_to_combine (must be odd).\nDefault means 1 or 3 depending on axial compression of input",-1,101,-1); - // TODO check odd - alpha_ramp = ask_num(" Alpha parameter for Ramp filter ? ",0.,1., 1.); - - fc_ramp = ask_num(" Cut-off frequency for Ramp filter ? ",0.,.5, 0.5); + alpha_colsher_axial = ask_num(" Alpha parameter for Colsher filter in axial direction ? ", 0., 1., 1.); -// PARAMETERS => 3D RECONSTRUCTION COLSHER FILTER (ALPHA, FC) - cerr << "\nFor 3D reconstruction filtering (Colsher filter) : "; - - alpha_colsher_axial = ask_num(" Alpha parameter for Colsher filter in axial direction ? ",0.,1., 1.); - - fc_colsher_axial = ask_num(" Cut-off frequency for Colsher filter in axial direction ? ",0.,.5, 0.5); + fc_colsher_axial = ask_num(" Cut-off frequency for Colsher filter in axial direction ? ", 0., .5, 0.5); - - alpha_colsher_planar = ask_num(" Alpha parameter for Colsher filter in planar direction ? ",0.,1., 1.); - - fc_colsher_planar = ask_num(" Cut-off frequency fo Colsher filter in planar direction ? ",0.,.5, 0.5); + alpha_colsher_planar = ask_num(" Alpha parameter for Colsher filter in planar direction ? ", 0., 1., 1.); + fc_colsher_planar = ask_num(" Cut-off frequency fo Colsher filter in planar direction ? ", 0., .5, 0.5); #if 0 // do not ask the user for the projectors to prevent entering silly things @@ -327,58 +299,43 @@ FBP3DRPReconstruction::ask_parameters() } std::string -FBP3DRPReconstruction:: -method_info() const -{ return("FBP3DRP"); } +FBP3DRPReconstruction::method_info() const { + return ("FBP3DRP"); +} -FBP3DRPReconstruction::~FBP3DRPReconstruction() -{} +FBP3DRPReconstruction::~FBP3DRPReconstruction() {} -VoxelsOnCartesianGrid& -FBP3DRPReconstruction::estimated_image() -{ +VoxelsOnCartesianGrid& +FBP3DRPReconstruction::estimated_image() { return static_cast&>(*image_estimate_density_ptr); } -const VoxelsOnCartesianGrid& -FBP3DRPReconstruction::estimated_image() const -{ +const VoxelsOnCartesianGrid& +FBP3DRPReconstruction::estimated_image() const { return static_cast&>(*image_estimate_density_ptr); } -const ProjDataInfoCylindrical& -FBP3DRPReconstruction::input_proj_data_info_cyl() const -{ - return - static_cast - (*proj_data_ptr->get_proj_data_info_sptr()); +const ProjDataInfoCylindrical& +FBP3DRPReconstruction::input_proj_data_info_cyl() const { + return static_cast(*proj_data_ptr->get_proj_data_info_sptr()); } -FBP3DRPReconstruction:: -FBP3DRPReconstruction(const std::string& parameter_filename) -{ - initialise(parameter_filename); -} +FBP3DRPReconstruction::FBP3DRPReconstruction(const std::string& parameter_filename) { initialise(parameter_filename); } -FBP3DRPReconstruction::FBP3DRPReconstruction() -{ - set_defaults(); -} +FBP3DRPReconstruction::FBP3DRPReconstruction() { set_defaults(); } Succeeded -FBP3DRPReconstruction:: -set_up(shared_ptr > const& target_image_sptr) -{ +FBP3DRPReconstruction::set_up(shared_ptr> const& target_image_sptr) { if (base_type::set_up(target_image_sptr) == Succeeded::no) return Succeeded::no; - if (dynamic_cast (proj_data_ptr->get_proj_data_info_sptr().get()) == 0) + if (dynamic_cast(proj_data_ptr->get_proj_data_info_sptr().get()) == 0) error("FBP3DRP currently needs cylindrical projection data. Sorry"); - if (colsher_stretch_factor_planar<1 || colsher_stretch_factor_axial<1) + if (colsher_stretch_factor_planar < 1 || colsher_stretch_factor_axial < 1) error("stretch factors for Colsher filter have to be at least 1"); - if (PadS<1 || PadZ<1) + if (PadS < 1 || PadZ < 1) warning("Transaxial extension for FFT:=0 (or axial) should \n" "ONLY be used when the non-zero data\n" "occupy only half of the FOV. Otherwise aliasing will occur!"); @@ -391,21 +348,18 @@ set_up(shared_ptr > const& target_image_sptr) return Succeeded::yes; } -Succeeded -FBP3DRPReconstruction:: -actual_reconstruct(shared_ptr > const& target_image_ptr) -{ +Succeeded +FBP3DRPReconstruction::actual_reconstruct(shared_ptr> const& target_image_ptr) { this->check(*target_image_ptr); - VoxelsOnCartesianGrid& image = - dynamic_cast &>(*target_image_ptr); + VoxelsOnCartesianGrid& image = dynamic_cast&>(*target_image_ptr); // set default values such that it will work also in the case of already_2D_recon alpha_fit = 1.0F; beta_fit = 0.0F; start_timers(); { - //char file[max_filename_length]; - //sprintf(file,"%s.full_log",output_filename_prefix.c_str()); + // char file[max_filename_length]; + // sprintf(file,"%s.full_log",output_filename_prefix.c_str()); std::string file = output_filename_prefix; file += ".full_log"; full_log.open(file.c_str(), ios::out); @@ -415,39 +369,31 @@ actual_reconstruct(shared_ptr > const& target_image_ full_log << parameter_info(); full_log << "\n\n********** PROCESSING FBP3DRP RECONSTRUCTION *************" << endl; - + const int old_max_segment_num_to_process = max_segment_num_to_process; - + // Use funny convention that -1 means 'use maximum available' - if (max_segment_num_to_process<0) + if (max_segment_num_to_process < 0) max_segment_num_to_process = proj_data_ptr->get_max_segment_num(); - else if (max_segment_num_to_process>proj_data_ptr->get_max_segment_num()) - { - warning("max_segment_num_to_process was too large (%d) for this data, setting it to %d", - max_segment_num_to_process, - proj_data_ptr->get_max_segment_num()); - max_segment_num_to_process = proj_data_ptr->get_max_segment_num(); - } + else if (max_segment_num_to_process > proj_data_ptr->get_max_segment_num()) { + warning("max_segment_num_to_process was too large (%d) for this data, setting it to %d", max_segment_num_to_process, + proj_data_ptr->get_max_segment_num()); + max_segment_num_to_process = proj_data_ptr->get_max_segment_num(); + } #ifndef NRFFT - const float theta_max = - atan(proj_data_ptr->get_proj_data_info_sptr()-> - get_tantheta(Bin(max_segment_num_to_process,0,0,0))); - - colsher_filter = - ColsherFilter(theta_max, - static_cast(alpha_colsher_axial), static_cast(fc_colsher_axial), - static_cast(alpha_colsher_planar), static_cast(fc_colsher_planar), - colsher_stretch_factor_planar, - colsher_stretch_factor_axial); + const float theta_max = atan(proj_data_ptr->get_proj_data_info_sptr()->get_tantheta(Bin(max_segment_num_to_process, 0, 0, 0))); + + colsher_filter = ColsherFilter(theta_max, static_cast(alpha_colsher_axial), static_cast(fc_colsher_axial), + static_cast(alpha_colsher_planar), static_cast(fc_colsher_planar), + colsher_stretch_factor_planar, colsher_stretch_factor_axial); #else warning("Using NRFFT"); #endif - - if(image_for_reprojection_filename == "") - { + + if (image_for_reprojection_filename == "") { do_2D_reconstruction(); - + #if 0 if (fit_projections==1){//Fitting between measured and estimaed sinograms full_log << " - Fitting projections" << endl; //CL 010699 Forward project measured sinograms and fitting with alpha and beta @@ -488,11 +434,9 @@ actual_reconstruct(shared_ptr > const& target_image_ }else{ alpha_fit = 1.F; beta_fit = 0.F; - } + } #endif - } - else - { + } else { do_read_image2D(); // TODO set fit parameters } @@ -500,57 +444,41 @@ actual_reconstruct(shared_ptr > const& target_image_ // find out if arc-correction if necessary // and initialise proj_data_info_with_missing_data_sptr accordingly { - if (!is_null_ptr(dynamic_pointer_cast - (proj_data_ptr->get_proj_data_info_sptr()))) - { - // it's already arc-corrected - arc_correction_sptr.reset(); // just rest to make sure in case we run the reconstruction twice - proj_data_info_with_missing_data_sptr = - proj_data_ptr->get_proj_data_info_sptr()->create_shared_clone(); - } - else - { - arc_correction_sptr.reset(new ArcCorrection); - // TODO arc-correct to voxel_size - if (arc_correction_sptr->set_up(proj_data_ptr->get_proj_data_info_sptr()->create_shared_clone()) == - Succeeded::no) - return Succeeded::no; - - full_log << "FBP3DRP will arc-correct data first\n"; - // warning: need to use clone() as we're modifying it later on - proj_data_info_with_missing_data_sptr = - arc_correction_sptr->get_arc_corrected_proj_data_info_sptr()->create_shared_clone(); - } + if (!is_null_ptr(dynamic_pointer_cast(proj_data_ptr->get_proj_data_info_sptr()))) { + // it's already arc-corrected + arc_correction_sptr.reset(); // just rest to make sure in case we run the reconstruction twice + proj_data_info_with_missing_data_sptr = proj_data_ptr->get_proj_data_info_sptr()->create_shared_clone(); + } else { + arc_correction_sptr.reset(new ArcCorrection); + // TODO arc-correct to voxel_size + if (arc_correction_sptr->set_up(proj_data_ptr->get_proj_data_info_sptr()->create_shared_clone()) == Succeeded::no) + return Succeeded::no; + + full_log << "FBP3DRP will arc-correct data first\n"; + // warning: need to use clone() as we're modifying it later on + proj_data_info_with_missing_data_sptr = arc_correction_sptr->get_arc_corrected_proj_data_info_sptr()->create_shared_clone(); + } } - // make space for missing data + // make space for missing data { - proj_data_info_with_missing_data_sptr-> - reduce_segment_range(-max_segment_num_to_process, - max_segment_num_to_process); - for (int segment_num= proj_data_info_with_missing_data_sptr->get_min_segment_num(); - segment_num<= proj_data_info_with_missing_data_sptr->get_max_segment_num(); - ++segment_num) - { - // note: initialisation to 0 to avoid compiler warnings, - // but will be set by find_rmin_rmax - int new_min_axial_pos_num = 0; - int new_max_axial_pos_num = 0; - find_rmin_rmax(new_min_axial_pos_num, new_max_axial_pos_num, - input_proj_data_info_cyl(), segment_num, image); - proj_data_info_with_missing_data_sptr-> - set_min_axial_pos_num(new_min_axial_pos_num, segment_num); - proj_data_info_with_missing_data_sptr-> - set_max_axial_pos_num(new_max_axial_pos_num, segment_num); - } + proj_data_info_with_missing_data_sptr->reduce_segment_range(-max_segment_num_to_process, max_segment_num_to_process); + for (int segment_num = proj_data_info_with_missing_data_sptr->get_min_segment_num(); + segment_num <= proj_data_info_with_missing_data_sptr->get_max_segment_num(); ++segment_num) { + // note: initialisation to 0 to avoid compiler warnings, + // but will be set by find_rmin_rmax + int new_min_axial_pos_num = 0; + int new_max_axial_pos_num = 0; + find_rmin_rmax(new_min_axial_pos_num, new_max_axial_pos_num, input_proj_data_info_cyl(), segment_num, image); + proj_data_info_with_missing_data_sptr->set_min_axial_pos_num(new_min_axial_pos_num, segment_num); + proj_data_info_with_missing_data_sptr->set_max_axial_pos_num(new_max_axial_pos_num, segment_num); + } } { // set projectors to be used for the calculations - forward_projector_sptr->set_up(proj_data_info_with_missing_data_sptr, - image_estimate_density_ptr); - back_projector_sptr->set_up(proj_data_info_with_missing_data_sptr, - target_image_ptr); + forward_projector_sptr->set_up(proj_data_info_with_missing_data_sptr, image_estimate_density_ptr); + back_projector_sptr->set_up(proj_data_info_with_missing_data_sptr, target_image_ptr); #if 0 do when enabling callbacks set_projectors_and_symmetries(forward_projector_sptr, @@ -559,234 +487,199 @@ actual_reconstruct(shared_ptr > const& target_image_ #endif } - if(max_segment_num_to_process!=0) - do_3D_Reconstruction(image ); - else - { - // TODO - warning("\nOutput image will NOT be zoomed.\n"); - image = estimated_image(); - } - if(display_level>0) + if (max_segment_num_to_process != 0) + do_3D_Reconstruction(image); + else { + // TODO + warning("\nOutput image will NOT be zoomed.\n"); + image = estimated_image(); + } + if (display_level > 0) display(image, image.find_max(), "Final image"); stop_timers(); do_log_file(image); full_log.close(); - + // restore max_segment_num_to_process to its original value, // just in case someone wants to use the reconstruction object twice max_segment_num_to_process = old_max_segment_num_to_process; return Succeeded::yes; } - - - -void FBP3DRPReconstruction::do_2D_reconstruction() -{ // SSRB+2D FBP with ramp filter +void +FBP3DRPReconstruction::do_2D_reconstruction() { // SSRB+2D FBP with ramp filter full_log << "\n---------------------------------------------------------\n"; - full_log << "2D FBP OF DIRECT SINOGRAMS (=> IMAGE_ESTIMATE)\n" << endl; - - + full_log << "2D FBP OF DIRECT SINOGRAMS (=> IMAGE_ESTIMATE)\n" << endl; + // image_estimate should have 'default' dimensions, origin and voxel_size - image_estimate_density_ptr. - reset(new VoxelsOnCartesianGrid(*proj_data_ptr->get_proj_data_info_sptr())); - - { - FBP2DReconstruction recon2d(proj_data_ptr, - alpha_ramp, fc_ramp, PadS, - num_segments_to_combine); + image_estimate_density_ptr.reset(new VoxelsOnCartesianGrid(*proj_data_ptr->get_proj_data_info_sptr())); + + { + FBP2DReconstruction recon2d(proj_data_ptr, alpha_ramp, fc_ramp, PadS, num_segments_to_combine); full_log << "Parameters of the 2D FBP reconstruction" << endl; - full_log << recon2d.parameter_info()<< endl; + full_log << recon2d.parameter_info() << endl; recon2d.set_up(image_estimate_density_ptr); recon2d.reconstruct(image_estimate_density_ptr); } - full_log << " - min and max in SSRB+FBP image " << estimated_image().find_min() - << " " << estimated_image().find_max() << " SUM= " << estimated_image().sum() << endl; - - if(display_level>1) { + full_log << " - min and max in SSRB+FBP image " << estimated_image().find_min() << " " << estimated_image().find_max() + << " SUM= " << estimated_image().sum() << endl; + + if (display_level > 1) { full_log << " - Displaying estimated image" << endl; - display(estimated_image(),estimated_image().find_max(), "Image estimate"); + display(estimated_image(), estimated_image().find_max(), "Image estimate"); } - - if (save_intermediate_files && !_disable_output) - { - char file[max_filename_length]; - sprintf(file,"%s_estimated",output_filename_prefix.c_str()); - do_save_img(file,estimated_image() ); - } -} + if (save_intermediate_files && !_disable_output) { + char file[max_filename_length]; + sprintf(file, "%s_estimated", output_filename_prefix.c_str()); + do_save_img(file, estimated_image()); + } +} - +void +FBP3DRPReconstruction::do_save_img(const char* file, const VoxelsOnCartesianGrid& data) const { + full_log << " - Saving " << file << endl; + output_file_format_ptr->write_to_file(file, data); + full_log << " Min= " << data.find_min() << " Max = " << data.find_max() << " Sum = " << data.sum() << endl; +} -void FBP3DRPReconstruction::do_save_img(const char *file, const VoxelsOnCartesianGrid &data) const -{ - full_log <<" - Saving " << file << endl; - output_file_format_ptr->write_to_file( file, data); - full_log << " Min= " << data.find_min() - << " Max = " << data.find_max() - << " Sum = " << data.sum() << endl; +void +FBP3DRPReconstruction::do_read_image2D() { + full_log << " - Reading estimated image : " << image_for_reprojection_filename << endl; -} - -void FBP3DRPReconstruction::do_read_image2D() -{ - full_log <<" - Reading estimated image : "<< image_for_reprojection_filename << endl; - - image_estimate_density_ptr = - read_from_file >(image_for_reprojection_filename.c_str() ); + image_estimate_density_ptr = read_from_file>(image_for_reprojection_filename.c_str()); - // TODO do scale checks - + // TODO do scale checks } - - -void FBP3DRPReconstruction::do_3D_Reconstruction( - VoxelsOnCartesianGrid &image) -{ +void +FBP3DRPReconstruction::do_3D_Reconstruction(VoxelsOnCartesianGrid& image) { full_log << "\n---------------------------------------------------------\n"; full_log << "3D PROCESSING\n" << endl; - + do_byview_initialise(image); // TODO check if forward projector and back projector have compatible symmetries - shared_ptr symmetries_sptr( - back_projector_sptr->get_symmetries_used()->clone()); + shared_ptr symmetries_sptr(back_projector_sptr->get_symmetries_used()->clone()); forward_projector_sptr->set_input(estimated_image()); back_projector_sptr->start_accumulating_in_new_target(); - for (int seg_num= -max_segment_num_to_process; seg_num <= max_segment_num_to_process; seg_num++) - { + for (int seg_num = -max_segment_num_to_process; seg_num <= max_segment_num_to_process; seg_num++) { // a bool value that will be used to determine if we are starting processing for this segment bool first_view_in_segment = true; - + const int orig_min_axial_pos_num = proj_data_ptr->get_min_axial_pos_num(seg_num); const int orig_max_axial_pos_num = proj_data_ptr->get_max_axial_pos_num(seg_num); - - for (int view_num=proj_data_ptr->get_min_view_num(); view_num <= proj_data_ptr->get_max_view_num(); ++view_num) { + + for (int view_num = proj_data_ptr->get_min_view_num(); view_num <= proj_data_ptr->get_max_view_num(); ++view_num) { const ViewSegmentNumbers vs_num(view_num, seg_num); if (!symmetries_sptr->is_basic(vs_num)) - continue; - - const int new_min_axial_pos_num = - proj_data_info_with_missing_data_sptr->get_min_axial_pos_num(seg_num); - const int new_max_axial_pos_num = - proj_data_info_with_missing_data_sptr->get_max_axial_pos_num(seg_num); - - if (first_view_in_segment) - { - full_log << "\n--------------------------------\n"; - full_log << "PROCESSING SEGMENT No " << seg_num << endl ; - - full_log << "Average delta= " << input_proj_data_info_cyl().get_average_ring_difference(seg_num) - << " with span= " << input_proj_data_info_cyl().get_max_ring_difference(seg_num) - input_proj_data_info_cyl().get_min_ring_difference(seg_num) +1 - << " and extended axial position numbers: min= " << new_min_axial_pos_num << " and max= " << new_max_axial_pos_num <get_min_axial_pos_num(seg_num); + const int new_max_axial_pos_num = proj_data_info_with_missing_data_sptr->get_max_axial_pos_num(seg_num); + + if (first_view_in_segment) { + full_log << "\n--------------------------------\n"; + full_log << "PROCESSING SEGMENT No " << seg_num << endl; + + full_log << "Average delta= " << input_proj_data_info_cyl().get_average_ring_difference(seg_num) << " with span= " + << input_proj_data_info_cyl().get_max_ring_difference(seg_num) - + input_proj_data_info_cyl().get_min_ring_difference(seg_num) + 1 + << " and extended axial position numbers: min= " << new_min_axial_pos_num + << " and max= " << new_max_axial_pos_num << endl; + + first_view_in_segment = false; + } full_log << "\n*************************************************************"; - full_log << "\n Processing view " << vs_num.view_num() - << " of segment " << vs_num.segment_num() << endl; - - full_log << "\n - Getting related viewgrams" << endl; - - RelatedViewgrams viewgrams = - proj_data_ptr->get_related_viewgrams(vs_num, symmetries_sptr); - - do_process_viewgrams( - viewgrams, - new_min_axial_pos_num, new_max_axial_pos_num, orig_min_axial_pos_num, orig_max_axial_pos_num); - - - } + full_log << "\n Processing view " << vs_num.view_num() << " of segment " << vs_num.segment_num() << endl; + + full_log << "\n - Getting related viewgrams" << endl; + + RelatedViewgrams viewgrams = proj_data_ptr->get_related_viewgrams(vs_num, symmetries_sptr); + + do_process_viewgrams(viewgrams, new_min_axial_pos_num, new_max_axial_pos_num, orig_min_axial_pos_num, + orig_max_axial_pos_num); + } // do some logging etc, but only when this segment had any processing // (some segment_nums might not because of the symmetries) - if (!first_view_in_segment) - { - full_log << "\n*************************************************************"; - full_log << "\nEnd of this segment. Current image values:\n" - << "Min= " << image.find_min() - << " Max = " << image.find_max() - << " Sum = " << image.sum() << endl; + if (!first_view_in_segment) { + full_log << "\n*************************************************************"; + full_log << "\nEnd of this segment. Current image values:\n" + << "Min= " << image.find_min() << " Max = " << image.find_max() << " Sum = " << image.sum() << endl; #ifndef PARALLEL - if(save_intermediate_files && !_disable_output){ - char *file = new char[output_filename_prefix.size() + 20]; - sprintf(file,"%s_afterseg%d",output_filename_prefix.c_str(),seg_num); - back_projector_sptr->get_output(image); - do_save_img(file, image); - delete[] file; - } + if (save_intermediate_files && !_disable_output) { + char* file = new char[output_filename_prefix.size() + 20]; + sprintf(file, "%s_afterseg%d", output_filename_prefix.c_str(), seg_num); + back_projector_sptr->get_output(image); + do_save_img(file, image); + delete[] file; } -#endif + } +#endif } back_projector_sptr->get_output(image); // Normalise the image - if (dynamic_cast(back_projector_sptr.get()) == 0) - { - // TODO remove magic, is a scale factor in the interpolating backprojector (for which we compensate in the Colsher filter) - const float magic_number=2*input_proj_data_info_cyl().get_ring_radius()*input_proj_data_info_cyl().get_num_views()/input_proj_data_info_cyl().get_ring_spacing(); - image /= magic_number; - } + if (dynamic_cast(back_projector_sptr.get()) == 0) { + // TODO remove magic, is a scale factor in the interpolating backprojector (for which we compensate in the Colsher filter) + const float magic_number = 2 * input_proj_data_info_cyl().get_ring_radius() * input_proj_data_info_cyl().get_num_views() / + input_proj_data_info_cyl().get_ring_spacing(); + image /= magic_number; + } do_byview_finalise(image); - - } - // CL 010699 NEW function -void FBP3DRPReconstruction::do_best_fit(const Sinogram &sino_measured,const Sinogram &sino_calculated) -{ +void +FBP3DRPReconstruction::do_best_fit(const Sinogram& sino_measured, const Sinogram& sino_calculated) { float meas_calc = 0.F; - float meas_square = 0.F; + float meas_square = 0.F; float calc_square = 0.F; float sigma_square = 0.F; full_log << " - Fitting estimated sinograms with the measured ones (Max in measured sino = " << sino_measured.find_max() - << " Max in fwd sino = " << sino_calculated.find_max() << ")" <forward_project(viewgrams, - new_min_axial_pos_num ,orig_min_axial_pos_num-1); + forward_projector_sptr->forward_project(viewgrams, new_min_axial_pos_num, orig_min_axial_pos_num - 1); + } - } + if (orig_max_axial_pos_num + 1 <= new_max_axial_pos_num) { + full_log << " - Forward projection from ring No " << orig_max_axial_pos_num + 1 << " to " << new_max_axial_pos_num << endl; - if (orig_max_axial_pos_num+1 <= new_max_axial_pos_num) - { - full_log << " - Forward projection from ring No " - << orig_max_axial_pos_num+1 - << " to " << new_max_axial_pos_num << endl; - - forward_projector_sptr->forward_project(viewgrams, - orig_max_axial_pos_num+1, new_max_axial_pos_num); - - } + forward_projector_sptr->forward_project(viewgrams, orig_max_axial_pos_num + 1, new_max_axial_pos_num); + } #if 0 if (fit_projections) { @@ -854,194 +733,158 @@ void FBP3DRPReconstruction::do_forward_project_view(RelatedViewgrams & vi } #endif - if(display_level>2) { - display( viewgrams,viewgrams.find_max(),"Original+Forward projected"); + if (display_level > 2) { + display(viewgrams, viewgrams.find_max(), "Original+Forward projected"); } } - - -void FBP3DRPReconstruction::do_colsher_filter_view( RelatedViewgrams & viewgrams) -{ - assert(!is_null_ptr(dynamic_pointer_cast - (viewgrams.get_proj_data_info_sptr()))); +void +FBP3DRPReconstruction::do_colsher_filter_view(RelatedViewgrams& viewgrams) { + + assert(!is_null_ptr(dynamic_pointer_cast(viewgrams.get_proj_data_info_sptr()))); // TODO make into object member instead of static - static int prev_seg_num = viewgrams.get_proj_data_info_sptr()->get_min_segment_num()-1; + static int prev_seg_num = viewgrams.get_proj_data_info_sptr()->get_min_segment_num() - 1; #ifdef NRFFT - static ColsherFilter colsher_filter(0,0,0,0,0,0,0,0,0,0); + static ColsherFilter colsher_filter(0, 0, 0, 0, 0, 0, 0, 0, 0, 0); #endif const int seg_num = viewgrams.get_basic_segment_num(); - if (prev_seg_num != seg_num) - { + if (prev_seg_num != seg_num) { prev_seg_num = seg_num; full_log << " - Constructing Colsher filter for this segment\n"; - const int nrings = viewgrams.get_num_axial_poss(); + const int nrings = viewgrams.get_num_axial_poss(); const int nprojs = viewgrams.get_num_tangential_poss(); - - const int width = (int) pow(2., ((int) ceil(log((PadS + 1.) * nprojs) / log(2.)))); - const int height = (int) pow(2., ((int) ceil(log((PadZ + 1.) * nrings) / log(2.)))); - - - const float theta_max = atan(viewgrams.get_proj_data_info_sptr()->get_tantheta(Bin(max_segment_num_to_process,0,0,0))); - - const float theta = - static_cast(atan(viewgrams.get_proj_data_info_sptr()->get_tantheta(Bin(seg_num,0,0,0)))); - - const float sampling_in_s = - viewgrams.get_proj_data_info_sptr()->get_sampling_in_s(Bin(seg_num,0,0,0)); - const float sampling_in_t = - viewgrams.get_proj_data_info_sptr()->get_sampling_in_t(Bin(seg_num,0,0,0)); - full_log << "Colsher filter theta_max = " << theta_max << " theta = " << theta - << " d_a = " << sampling_in_s - << " d_b = " << sampling_in_t << endl; - - + + const int width = (int)pow(2., ((int)ceil(log((PadS + 1.) * nprojs) / log(2.)))); + const int height = (int)pow(2., ((int)ceil(log((PadZ + 1.) * nrings) / log(2.)))); + + const float theta_max = atan(viewgrams.get_proj_data_info_sptr()->get_tantheta(Bin(max_segment_num_to_process, 0, 0, 0))); + + const float theta = static_cast(atan(viewgrams.get_proj_data_info_sptr()->get_tantheta(Bin(seg_num, 0, 0, 0)))); + + const float sampling_in_s = viewgrams.get_proj_data_info_sptr()->get_sampling_in_s(Bin(seg_num, 0, 0, 0)); + const float sampling_in_t = viewgrams.get_proj_data_info_sptr()->get_sampling_in_t(Bin(seg_num, 0, 0, 0)); + full_log << "Colsher filter theta_max = " << theta_max << " theta = " << theta << " d_a = " << sampling_in_s + << " d_b = " << sampling_in_t << endl; + #ifdef NRFFT - colsher_filter = - ColsherFilter(height, width, _PI/2 - theta, theta_max, - sampling_in_s, - sampling_in_t, - alpha_colsher_axial, fc_colsher_axial, - alpha_colsher_planar, fc_colsher_planar); + colsher_filter = ColsherFilter(height, width, _PI / 2 - theta, theta_max, sampling_in_s, sampling_in_t, alpha_colsher_axial, + fc_colsher_axial, alpha_colsher_planar, fc_colsher_planar); #else - if (colsher_filter.set_up(height, width, - theta, - sampling_in_s, - sampling_in_t) - != Succeeded::yes) - error("Exiting"); + if (colsher_filter.set_up(height, width, theta, sampling_in_s, sampling_in_t) != Succeeded::yes) + error("Exiting"); #endif } full_log << " - Apply Colsher filter to complete oblique sinograms" << endl; #ifdef NRFFT - assert(viewgrams.get_num_viewgrams()%2 == 0); - + assert(viewgrams.get_num_viewgrams() % 2 == 0); + RelatedViewgrams::iterator viewgram_iter = viewgrams.begin(); - for (; viewgram_iter != viewgrams.end(); viewgram_iter+=2) - Filter_proj_Colsher(*viewgram_iter, *(viewgram_iter+1), - colsher_filter, - PadS, PadZ); + for (; viewgram_iter != viewgrams.end(); viewgram_iter += 2) + Filter_proj_Colsher(*viewgram_iter, *(viewgram_iter + 1), colsher_filter, PadS, PadZ); #else - // do not use std::for_each. at present on gcc it copies the filter for every viewgram - // std::for_each(viewgrams.begin(), viewgrams.end(), - // colsher_filter); - RelatedViewgrams::iterator viewgram_iter = viewgrams.begin(); - for (; viewgram_iter != viewgrams.end(); ++viewgram_iter) - colsher_filter(*viewgram_iter); + // do not use std::for_each. at present on gcc it copies the filter for every viewgram + // std::for_each(viewgrams.begin(), viewgrams.end(), + // colsher_filter); + RelatedViewgrams::iterator viewgram_iter = viewgrams.begin(); + for (; viewgram_iter != viewgrams.end(); ++viewgram_iter) + colsher_filter(*viewgram_iter); #endif /* If the segment is really an amalgam of different ring differences, - we have to multiply it with the number of ring differences + we have to multiply it with the number of ring differences in the segment. - This is to assure that backprojecting each ring difference + This is to assure that backprojecting each ring difference on its own would give roughly the same result. TODO: should this be put in the backprojector itself ? - */ - { - const int num_ring_differences = - input_proj_data_info_cyl().get_max_ring_difference(seg_num) - - input_proj_data_info_cyl().get_min_ring_difference(seg_num) + 1; - full_log << " - Multiplying filtered projections by " << num_ring_differences << endl; - if (num_ring_differences != 1){ - viewgrams *= static_cast(num_ring_differences); - } - - } - if(display_level>2) { - display( viewgrams,viewgrams.find_max(), "Colsher filtered"); + */ + { + const int num_ring_differences = input_proj_data_info_cyl().get_max_ring_difference(seg_num) - + input_proj_data_info_cyl().get_min_ring_difference(seg_num) + 1; + full_log << " - Multiplying filtered projections by " << num_ring_differences << endl; + if (num_ring_differences != 1) { + viewgrams *= static_cast(num_ring_differences); } + } + if (display_level > 2) { + display(viewgrams, viewgrams.find_max(), "Colsher filtered"); + } } +void +FBP3DRPReconstruction::do_3D_backprojection_view(const RelatedViewgrams& viewgrams, int new_min_axial_pos_num, + int new_max_axial_pos_num) { + full_log << " - Backproject the filtered Colsher complete sinograms" << endl; -void FBP3DRPReconstruction::do_3D_backprojection_view(const RelatedViewgrams & viewgrams, - int new_min_axial_pos_num, int new_max_axial_pos_num) -{ - full_log << " - Backproject the filtered Colsher complete sinograms" << endl; - - back_projector_sptr->back_project(viewgrams,new_min_axial_pos_num, new_max_axial_pos_num); - + back_projector_sptr->back_project(viewgrams, new_min_axial_pos_num, new_max_axial_pos_num); } +void +FBP3DRPReconstruction::do_log_file(const VoxelsOnCartesianGrid& image) { + char file[max_filename_length]; + sprintf(file, "%s.log", output_filename_prefix.c_str()); - -void FBP3DRPReconstruction::do_log_file(const VoxelsOnCartesianGrid &image) -{ - char file[max_filename_length]; - sprintf(file,"%s.log",output_filename_prefix.c_str()); - - full_log << endl << "- WRITE LOGFILE (" - << file << ")" << endl; - - ofstream logfile(file); - - if (logfile.fail() || logfile.bad()) { - warning("Error opening log file\n"); - return; - } - full_log << endl ; + full_log << endl << "- WRITE LOGFILE (" << file << ")" << endl; + + ofstream logfile(file); + + if (logfile.fail() || logfile.bad()) { + warning("Error opening log file\n"); + return; + } + full_log << endl; - const time_t now = time(NULL); + const time_t now = time(NULL); - logfile << "Date of the image reconstruction : " << asctime(localtime(&now)) - << parameter_info() ; - + logfile << "Date of the image reconstruction : " << asctime(localtime(&now)) << parameter_info(); #ifndef PARALLEL - logfile << "\n\n TIMING RESULTS :\n" - << "Total CPU time : " << get_CPU_timer_value() << '\n' - << "forward projection CPU time : " << forward_projector_sptr->get_CPU_timer_value() << '\n' - << "back projection CPU time : " << back_projector_sptr->get_CPU_timer_value() << '\n' -#ifndef NRFFT - << "Colsher filter set-up CPU time : " << colsher_filter.get_CPU_timer_value() << '\n' -#endif + logfile << "\n\n TIMING RESULTS :\n" + << "Total CPU time : " << get_CPU_timer_value() << '\n' + << "forward projection CPU time : " << forward_projector_sptr->get_CPU_timer_value() << '\n' + << "back projection CPU time : " << back_projector_sptr->get_CPU_timer_value() << '\n' +# ifndef NRFFT + << "Colsher filter set-up CPU time : " << colsher_filter.get_CPU_timer_value() << '\n' +# endif ; -#endif +#endif } +void +FBP3DRPReconstruction::do_process_viewgrams(RelatedViewgrams& viewgrams, int new_min_axial_pos_num, + int new_max_axial_pos_num, int orig_min_axial_pos_num, int orig_max_axial_pos_num) { + do_arc_correction(viewgrams); + + do_grow3D_viewgram(viewgrams, new_min_axial_pos_num, new_max_axial_pos_num); + + do_forward_project_view(viewgrams, new_min_axial_pos_num, new_max_axial_pos_num, orig_min_axial_pos_num, + orig_max_axial_pos_num); + + do_colsher_filter_view(viewgrams); + + /* The backprojection here is really an approximation of a continuous integral + over delta and phi, where + -max_delta <= delta <= max_delta + 0 <= phi < pi + We discretise the integral over delta by summing values + at discrete ring differences. However, we include the boundary points. + The appropriate formula for the integral is then + f(-max_delta)/2 + f(-max_delta+1) + ... f(max_delta-1) + f(max_delta/2) + Note the factors 1/2 at the boundary. + These are inserted below + */ + if (abs(viewgrams.get_basic_segment_num()) == max_segment_num_to_process) { + viewgrams /= 2; + } -void FBP3DRPReconstruction::do_process_viewgrams(RelatedViewgrams & viewgrams, - int new_min_axial_pos_num, int new_max_axial_pos_num, - int orig_min_axial_pos_num, int orig_max_axial_pos_num) -{ - do_arc_correction(viewgrams); - - do_grow3D_viewgram(viewgrams, new_min_axial_pos_num, new_max_axial_pos_num); - - do_forward_project_view(viewgrams, - new_min_axial_pos_num, new_max_axial_pos_num, orig_min_axial_pos_num, orig_max_axial_pos_num); - - do_colsher_filter_view(viewgrams); - - - - /* The backprojection here is really an approximation of a continuous integral - over delta and phi, where - -max_delta <= delta <= max_delta - 0 <= phi < pi - We discretise the integral over delta by summing values - at discrete ring differences. However, we include the boundary points. - The appropriate formula for the integral is then - f(-max_delta)/2 + f(-max_delta+1) + ... f(max_delta-1) + f(max_delta/2) - Note the factors 1/2 at the boundary. - These are inserted below - */ - if (abs(viewgrams.get_basic_segment_num()) == max_segment_num_to_process) - { - viewgrams /= 2; - } - - do_3D_backprojection_view(viewgrams, - new_min_axial_pos_num, new_max_axial_pos_num); - + do_3D_backprojection_view(viewgrams, new_min_axial_pos_num, new_max_axial_pos_num); } - END_NAMESPACE_STIR diff --git a/src/buildblock/ArcCorrection.cxx b/src/buildblock/ArcCorrection.cxx index bcd4c39422..3f3accd90e 100644 --- a/src/buildblock/ArcCorrection.cxx +++ b/src/buildblock/ArcCorrection.cxx @@ -40,263 +40,170 @@ #include START_NAMESPACE_STIR -ArcCorrection:: -ArcCorrection() -{} +ArcCorrection::ArcCorrection() {} const ProjDataInfoCylindricalNoArcCorr& -ArcCorrection:: -get_not_arc_corrected_proj_data_info() const -{ - return - static_cast(*_noarc_corr_proj_data_info_sptr); +ArcCorrection::get_not_arc_corrected_proj_data_info() const { + return static_cast(*_noarc_corr_proj_data_info_sptr); } const ProjDataInfoCylindricalArcCorr& -ArcCorrection:: -get_arc_corrected_proj_data_info() const -{ - return - static_cast(*_arc_corr_proj_data_info_sptr); +ArcCorrection::get_arc_corrected_proj_data_info() const { + return static_cast(*_arc_corr_proj_data_info_sptr); } -shared_ptr -ArcCorrection:: -get_arc_corrected_proj_data_info_sptr() const -{ +shared_ptr +ArcCorrection::get_arc_corrected_proj_data_info_sptr() const { return _arc_corr_proj_data_info_sptr; } -shared_ptr -ArcCorrection:: -get_not_arc_corrected_proj_data_info_sptr() const -{ +shared_ptr +ArcCorrection::get_not_arc_corrected_proj_data_info_sptr() const { return _noarc_corr_proj_data_info_sptr; } - Succeeded -ArcCorrection:: -set_up(const shared_ptr& noarc_corr_proj_data_info_sptr, - const int num_arccorrected_tangential_poss, - const float bin_size) -{ - if (dynamic_cast - (noarc_corr_proj_data_info_sptr.get()) == 0) - { - // give friendly warning message - if (dynamic_cast - (noarc_corr_proj_data_info_sptr.get()) != 0) - warning("ArcCorrection called with arc-corrected proj_data_info"); - else - warning("ArcCorrection called with proj_data_info of the wrong type:\n\t%s", - typeid(*noarc_corr_proj_data_info_sptr).name()); - return Succeeded::no; - } +ArcCorrection::set_up(const shared_ptr& noarc_corr_proj_data_info_sptr, + const int num_arccorrected_tangential_poss, const float bin_size) { + if (dynamic_cast(noarc_corr_proj_data_info_sptr.get()) == 0) { + // give friendly warning message + if (dynamic_cast(noarc_corr_proj_data_info_sptr.get()) != 0) + warning("ArcCorrection called with arc-corrected proj_data_info"); + else + warning("ArcCorrection called with proj_data_info of the wrong type:\n\t%s", + typeid(*noarc_corr_proj_data_info_sptr).name()); + return Succeeded::no; + } _noarc_corr_proj_data_info_sptr = noarc_corr_proj_data_info_sptr; - const int min_segment_num = - _noarc_corr_proj_data_info_sptr->get_min_segment_num(); - const int max_segment_num = - _noarc_corr_proj_data_info_sptr->get_max_segment_num(); - - VectorWithOffset min_ring_diff(min_segment_num, max_segment_num); + const int min_segment_num = _noarc_corr_proj_data_info_sptr->get_min_segment_num(); + const int max_segment_num = _noarc_corr_proj_data_info_sptr->get_max_segment_num(); + + VectorWithOffset min_ring_diff(min_segment_num, max_segment_num); VectorWithOffset max_ring_diff(min_segment_num, max_segment_num); VectorWithOffset num_axial_pos_per_segment(min_segment_num, max_segment_num); - for (int segment_num=min_segment_num; segment_num<=max_segment_num; ++segment_num) - { - min_ring_diff[segment_num] = - get_not_arc_corrected_proj_data_info().get_min_ring_difference(segment_num); - max_ring_diff[segment_num] = - get_not_arc_corrected_proj_data_info().get_max_ring_difference(segment_num); - num_axial_pos_per_segment[segment_num] = - _noarc_corr_proj_data_info_sptr->get_num_axial_poss(segment_num); - } + for (int segment_num = min_segment_num; segment_num <= max_segment_num; ++segment_num) { + min_ring_diff[segment_num] = get_not_arc_corrected_proj_data_info().get_min_ring_difference(segment_num); + max_ring_diff[segment_num] = get_not_arc_corrected_proj_data_info().get_max_ring_difference(segment_num); + num_axial_pos_per_segment[segment_num] = _noarc_corr_proj_data_info_sptr->get_num_axial_poss(segment_num); + } // create new scanner shared_ptr to avoid unexpected problems with people // modifying the scanner_sptr of the old/new object shared_ptr new_scanner_sptr(new Scanner(*_noarc_corr_proj_data_info_sptr->get_scanner_ptr())); _arc_corr_proj_data_info_sptr.reset( - new ProjDataInfoCylindricalArcCorr( - new_scanner_sptr, - bin_size, - num_axial_pos_per_segment, - min_ring_diff, - max_ring_diff, - noarc_corr_proj_data_info_sptr->get_num_views(), - num_arccorrected_tangential_poss)); - - tangential_sampling = - get_arc_corrected_proj_data_info().get_tangential_sampling(); + new ProjDataInfoCylindricalArcCorr(new_scanner_sptr, bin_size, num_axial_pos_per_segment, min_ring_diff, max_ring_diff, + noarc_corr_proj_data_info_sptr->get_num_views(), num_arccorrected_tangential_poss)); + + tangential_sampling = get_arc_corrected_proj_data_info().get_tangential_sampling(); _noarccorr_coords.resize(_noarc_corr_proj_data_info_sptr->get_min_tangential_pos_num(), - _noarc_corr_proj_data_info_sptr->get_max_tangential_pos_num()+1); + _noarc_corr_proj_data_info_sptr->get_max_tangential_pos_num() + 1); _noarccorr_bin_sizes.resize(_noarc_corr_proj_data_info_sptr->get_min_tangential_pos_num(), - _noarc_corr_proj_data_info_sptr->get_max_tangential_pos_num()); - { - const float angular_increment = - get_not_arc_corrected_proj_data_info().get_angular_increment(); - const float ring_radius = - get_not_arc_corrected_proj_data_info().get_ring_radius(); + _noarc_corr_proj_data_info_sptr->get_max_tangential_pos_num()); + { + const float angular_increment = get_not_arc_corrected_proj_data_info().get_angular_increment(); + const float ring_radius = get_not_arc_corrected_proj_data_info().get_ring_radius(); int tang_pos_num = _noarc_corr_proj_data_info_sptr->get_min_tangential_pos_num(); - _noarccorr_coords[tang_pos_num] = - ring_radius * sin((tang_pos_num-.5F)*angular_increment); - for (; - tang_pos_num <= _noarc_corr_proj_data_info_sptr->get_max_tangential_pos_num(); - ++tang_pos_num) - { - _noarccorr_coords[tang_pos_num+1] = - ring_radius * sin((tang_pos_num+.5F)*angular_increment); - _noarccorr_bin_sizes[tang_pos_num] = - _noarccorr_coords[tang_pos_num+1] - - _noarccorr_coords[tang_pos_num]; - } + _noarccorr_coords[tang_pos_num] = ring_radius * sin((tang_pos_num - .5F) * angular_increment); + for (; tang_pos_num <= _noarc_corr_proj_data_info_sptr->get_max_tangential_pos_num(); ++tang_pos_num) { + _noarccorr_coords[tang_pos_num + 1] = ring_radius * sin((tang_pos_num + .5F) * angular_increment); + _noarccorr_bin_sizes[tang_pos_num] = _noarccorr_coords[tang_pos_num + 1] - _noarccorr_coords[tang_pos_num]; + } } _arccorr_coords.resize(_arc_corr_proj_data_info_sptr->get_min_tangential_pos_num(), - _arc_corr_proj_data_info_sptr->get_max_tangential_pos_num()+1); + _arc_corr_proj_data_info_sptr->get_max_tangential_pos_num() + 1); { int tang_pos_num; for (tang_pos_num = _arc_corr_proj_data_info_sptr->get_min_tangential_pos_num(); - tang_pos_num <= _arc_corr_proj_data_info_sptr->get_max_tangential_pos_num(); - ++tang_pos_num) - { - _arccorr_coords[tang_pos_num] = - (tang_pos_num-.5F)*tangential_sampling; - } - _arccorr_coords[tang_pos_num] = - (tang_pos_num+.5F)*tangential_sampling; + tang_pos_num <= _arc_corr_proj_data_info_sptr->get_max_tangential_pos_num(); ++tang_pos_num) { + _arccorr_coords[tang_pos_num] = (tang_pos_num - .5F) * tangential_sampling; + } + _arccorr_coords[tang_pos_num] = (tang_pos_num + .5F) * tangential_sampling; } return Succeeded::yes; } - + Succeeded -ArcCorrection:: - set_up(const shared_ptr& noarc_corr_proj_data_info_sptr, - const int num_arccorrected_tangential_poss) -{ - float tangential_sampling = - noarc_corr_proj_data_info_sptr->get_scanner_ptr()->get_default_bin_size(); - if (tangential_sampling<=0) - { - tangential_sampling = - noarc_corr_proj_data_info_sptr-> - get_sampling_in_s(Bin(0,0,0,0)); - warning("ArcCorrection::set_up called for a scanner with default\n" - "tangential sampling (aka bin size) equal to 0.\n" - "Using the central bin size %g.", - tangential_sampling); - } - return - set_up(noarc_corr_proj_data_info_sptr, - num_arccorrected_tangential_poss, - tangential_sampling); +ArcCorrection::set_up(const shared_ptr& noarc_corr_proj_data_info_sptr, + const int num_arccorrected_tangential_poss) { + float tangential_sampling = noarc_corr_proj_data_info_sptr->get_scanner_ptr()->get_default_bin_size(); + if (tangential_sampling <= 0) { + tangential_sampling = noarc_corr_proj_data_info_sptr->get_sampling_in_s(Bin(0, 0, 0, 0)); + warning("ArcCorrection::set_up called for a scanner with default\n" + "tangential sampling (aka bin size) equal to 0.\n" + "Using the central bin size %g.", + tangential_sampling); + } + return set_up(noarc_corr_proj_data_info_sptr, num_arccorrected_tangential_poss, tangential_sampling); } - Succeeded -ArcCorrection:: -set_up(const shared_ptr& noarc_corr_proj_data_info_sptr) -{ - float tangential_sampling = - noarc_corr_proj_data_info_sptr->get_scanner_ptr()->get_default_bin_size(); - if (tangential_sampling<=0) - { - tangential_sampling = - noarc_corr_proj_data_info_sptr-> - get_sampling_in_s(Bin(0,0,0,0)); - warning("ArcCorrection::set_up called for a scanner with default\n" - "tangential sampling (aka bin size) equal to 0.\n" - "Using the central bin size %g.", - tangential_sampling); - } - const float max_s = - std::max( - noarc_corr_proj_data_info_sptr-> - get_s(Bin(0,0,0, - noarc_corr_proj_data_info_sptr-> - get_max_tangential_pos_num()+2)), - -noarc_corr_proj_data_info_sptr-> - get_s(Bin(0,0,0, - noarc_corr_proj_data_info_sptr-> - get_min_tangential_pos_num()-2)) - ); - const int max_arccorr_tangential_pos_num = - static_cast(ceil(max_s/tangential_sampling)); - return - set_up(noarc_corr_proj_data_info_sptr, - 2*max_arccorr_tangential_pos_num+1, - tangential_sampling); +ArcCorrection::set_up(const shared_ptr& noarc_corr_proj_data_info_sptr) { + float tangential_sampling = noarc_corr_proj_data_info_sptr->get_scanner_ptr()->get_default_bin_size(); + if (tangential_sampling <= 0) { + tangential_sampling = noarc_corr_proj_data_info_sptr->get_sampling_in_s(Bin(0, 0, 0, 0)); + warning("ArcCorrection::set_up called for a scanner with default\n" + "tangential sampling (aka bin size) equal to 0.\n" + "Using the central bin size %g.", + tangential_sampling); + } + const float max_s = std::max( + noarc_corr_proj_data_info_sptr->get_s(Bin(0, 0, 0, noarc_corr_proj_data_info_sptr->get_max_tangential_pos_num() + 2)), + -noarc_corr_proj_data_info_sptr->get_s(Bin(0, 0, 0, noarc_corr_proj_data_info_sptr->get_min_tangential_pos_num() - 2))); + const int max_arccorr_tangential_pos_num = static_cast(ceil(max_s / tangential_sampling)); + return set_up(noarc_corr_proj_data_info_sptr, 2 * max_arccorr_tangential_pos_num + 1, tangential_sampling); } -void -ArcCorrection:: -do_arc_correction(Array<1,float>& out, const Array<1,float>& in) const -{ +void +ArcCorrection::do_arc_correction(Array<1, float>& out, const Array<1, float>& in) const { assert(in.get_index_range() == _noarccorr_bin_sizes.get_index_range()); assert(out.get_min_index() == _arccorr_coords.get_min_index()); - assert(out.get_max_index() == _arccorr_coords.get_max_index()-1); + assert(out.get_max_index() == _arccorr_coords.get_max_index() - 1); - overlap_interpolate(out.begin(), out.end(), - _arccorr_coords.begin(), _arccorr_coords.end(), - in.begin(), in.end(), - _noarccorr_coords.begin(), _noarccorr_coords.end()); + overlap_interpolate(out.begin(), out.end(), _arccorr_coords.begin(), _arccorr_coords.end(), in.begin(), in.end(), + _noarccorr_coords.begin(), _noarccorr_coords.end()); out /= tangential_sampling; } void -ArcCorrection:: -do_arc_correction(Sinogram& out, const Sinogram& in) const -{ +ArcCorrection::do_arc_correction(Sinogram& out, const Sinogram& in) const { 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_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) + 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]); } Sinogram -ArcCorrection:: -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_timing_pos_num()); +ArcCorrection::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_timing_pos_num()); do_arc_correction(out, in); return out; } void -ArcCorrection:: -do_arc_correction(Viewgram& out, const Viewgram& in) const -{ +ArcCorrection::do_arc_correction(Viewgram& out, const Viewgram& in) const { 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_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) + 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]); } Viewgram -ArcCorrection:: -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_timing_pos_num()); +ArcCorrection::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_timing_pos_num()); do_arc_correction(out, in); return out; } void -ArcCorrection:: -do_arc_correction(RelatedViewgrams& out, const RelatedViewgrams& in) const -{ +ArcCorrection::do_arc_correction(RelatedViewgrams& out, const RelatedViewgrams& in) const { RelatedViewgrams::iterator out_iter = out.begin(); RelatedViewgrams::const_iterator in_iter = in.begin(); for (; out_iter != out.end(); ++out_iter, ++in_iter) @@ -304,85 +211,65 @@ do_arc_correction(RelatedViewgrams& out, const RelatedViewgrams& i } RelatedViewgrams -ArcCorrection:: -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(), false, in.get_basic_timing_pos_num()); +ArcCorrection::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(), false, in.get_basic_timing_pos_num()); do_arc_correction(out, in); return out; } void -ArcCorrection:: -do_arc_correction(SegmentBySinogram& out, const SegmentBySinogram& in) const -{ +ArcCorrection::do_arc_correction(SegmentBySinogram& out, const SegmentBySinogram& in) const { 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) + 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]); } -SegmentBySinogram -ArcCorrection:: -do_arc_correction(const SegmentBySinogram& in) const -{ - SegmentBySinogram out(_arc_corr_proj_data_info_sptr, - in.get_segment_num(), in.get_timing_pos_num()); +SegmentBySinogram +ArcCorrection::do_arc_correction(const SegmentBySinogram& in) const { + SegmentBySinogram out(_arc_corr_proj_data_info_sptr, in.get_segment_num(), in.get_timing_pos_num()); do_arc_correction(out, in); return out; } - void -ArcCorrection:: -do_arc_correction(SegmentByView& out, const SegmentByView& in) const -{ +ArcCorrection::do_arc_correction(SegmentByView& out, const SegmentByView& in) const { 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) + 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]); } - -SegmentByView -ArcCorrection:: -do_arc_correction(const SegmentByView& in) const -{ - SegmentByView out(_arc_corr_proj_data_info_sptr, - in.get_segment_num(), in.get_timing_pos_num()); +SegmentByView +ArcCorrection::do_arc_correction(const SegmentByView& in) const { + SegmentByView out(_arc_corr_proj_data_info_sptr, in.get_segment_num(), in.get_timing_pos_num()); do_arc_correction(out, in); return out; } - Succeeded -ArcCorrection:: -do_arc_correction(ProjData& out, const ProjData& in) const -{ +ArcCorrection::do_arc_correction(ProjData& out, const ProjData& in) const { 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); // 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(),in.get_min_tof_pos_num()); + Viewgram viewgram = _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; - } + 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/Array.cxx b/src/buildblock/Array.cxx index 5781bebbcf..1d769fc740 100644 --- a/src/buildblock/Array.cxx +++ b/src/buildblock/Array.cxx @@ -16,17 +16,17 @@ See STIR/LICENSE.txt for details */ /*! - \file - \ingroup Array - \brief non-inline implementations for the Array class + \file + \ingroup Array + \brief non-inline implementations for the Array class - \author Kris Thielemans + \author Kris Thielemans \author PARAPET project This file could be empty. However, it contains - contains instantiations for some common cases. + contains instantiations for some common cases. This might reduce the size of the executable a bit if the compiler cannot inline certain functions. */ @@ -35,31 +35,30 @@ START_NAMESPACE_STIR - /************************************************** instantiations **************************************************/ #ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION // add any other types you need -template class Array<1,signed char>; -template class Array<1,short>; -template class Array<1,unsigned short>; -template class Array<1,float>; +template class Array<1, signed char>; +template class Array<1, short>; +template class Array<1, unsigned short>; +template class Array<1, float>; #endif -template class Array<2,signed char>; -template class Array<2,short>; -template class Array<2,unsigned short>; -template class Array<2,float>; +template class Array<2, signed char>; +template class Array<2, short>; +template class Array<2, unsigned short>; +template class Array<2, float>; template class Array<3, signed char>; template class Array<3, short>; -template class Array<3,unsigned short>; -template class Array<3,float>; +template class Array<3, unsigned short>; +template class Array<3, float>; template class Array<4, short>; -template class Array<4,unsigned short>; -template class Array<4,float>; +template class Array<4, unsigned short>; +template class Array<4, float>; END_NAMESPACE_STIR diff --git a/src/buildblock/ArrayFilter1DUsingConvolution.cxx b/src/buildblock/ArrayFilter1DUsingConvolution.cxx index cb607c8811..06563ddc6a 100644 --- a/src/buildblock/ArrayFilter1DUsingConvolution.cxx +++ b/src/buildblock/ArrayFilter1DUsingConvolution.cxx @@ -39,179 +39,138 @@ using std::max; START_NAMESPACE_STIR template -ArrayFilter1DUsingConvolution:: -ArrayFilter1DUsingConvolution() - : filter_coefficients(), _bc(BoundaryConditions::zero) -{ - -} +ArrayFilter1DUsingConvolution::ArrayFilter1DUsingConvolution() : filter_coefficients(), _bc(BoundaryConditions::zero) {} template -ArrayFilter1DUsingConvolution:: -ArrayFilter1DUsingConvolution(const VectorWithOffset &filter_coefficients_v, const BoundaryConditions::BC bc) - : filter_coefficients(filter_coefficients_v), _bc(bc) -{ +ArrayFilter1DUsingConvolution::ArrayFilter1DUsingConvolution(const VectorWithOffset& filter_coefficients_v, + const BoundaryConditions::BC bc) + : filter_coefficients(filter_coefficients_v), _bc(bc) { // TODO: remove 0 elements at the outside } - template -bool -ArrayFilter1DUsingConvolution:: -is_trivial() const -{ - return - filter_coefficients.get_length() == 0 || - (filter_coefficients.get_length()==1 && filter_coefficients.get_min_index()==0 && - filter_coefficients[0] == 1); +bool +ArrayFilter1DUsingConvolution::is_trivial() const { + return filter_coefficients.get_length() == 0 || + (filter_coefficients.get_length() == 1 && filter_coefficients.get_min_index() == 0 && filter_coefficients[0] == 1); } - template -Succeeded -ArrayFilter1DUsingConvolution:: -get_influencing_indices(IndexRange<1>& influencing_index_range, - const IndexRange<1>& input_index_range) const -{ - influencing_index_range = - (filter_coefficients.get_length() == 0) - ? input_index_range - : IndexRange<1>(input_index_range.get_min_index() - filter_coefficients.get_max_index(), - input_index_range.get_max_index() - filter_coefficients.get_min_index()); +Succeeded +ArrayFilter1DUsingConvolution::get_influencing_indices(IndexRange<1>& influencing_index_range, + const IndexRange<1>& input_index_range) const { + influencing_index_range = (filter_coefficients.get_length() == 0) + ? input_index_range + : IndexRange<1>(input_index_range.get_min_index() - filter_coefficients.get_max_index(), + input_index_range.get_max_index() - filter_coefficients.get_min_index()); return Succeeded::yes; } template -Succeeded -ArrayFilter1DUsingConvolution:: -get_influenced_indices(IndexRange<1>& influenced_index_range, - const IndexRange<1>& input_index_range) const -{ - influenced_index_range = - (filter_coefficients.get_length() == 0) - ? input_index_range - : IndexRange<1>(input_index_range.get_min_index() + filter_coefficients.get_min_index(), - input_index_range.get_max_index() + filter_coefficients.get_max_index()); +Succeeded +ArrayFilter1DUsingConvolution::get_influenced_indices(IndexRange<1>& influenced_index_range, + const IndexRange<1>& input_index_range) const { + influenced_index_range = (filter_coefficients.get_length() == 0) + ? input_index_range + : IndexRange<1>(input_index_range.get_min_index() + filter_coefficients.get_min_index(), + input_index_range.get_max_index() + filter_coefficients.get_max_index()); return Succeeded::yes; } template void -ArrayFilter1DUsingConvolution:: -do_it(Array<1,elemT>& out_array, const Array<1,elemT>& in_array) const -{ +ArrayFilter1DUsingConvolution::do_it(Array<1, elemT>& out_array, const Array<1, elemT>& in_array) const { const int in_min = in_array.get_min_index(); const int in_max = in_array.get_max_index(); const int out_min = out_array.get_min_index(); const int out_max = out_array.get_max_index(); - if (is_trivial()) - { - int i=out_min; - - switch (this->_bc) - { - case BoundaryConditions::zero: - { - for (; i<=min(in_min-1,out_max); ++i) - out_array[i] = 0; - break; - } - case BoundaryConditions::constant: - { - for (; i<=min(in_min-1,out_max); ++i) - out_array[i] = in_array[in_min]; - break; - } - default: - { - error("ArrayFilter1DUsingConvolution: cannot handle this boundary condition yet. sorry"); - } - } + if (is_trivial()) { + int i = out_min; + + switch (this->_bc) { + case BoundaryConditions::zero: { + for (; i <= min(in_min - 1, out_max); ++i) + out_array[i] = 0; + break; + } + case BoundaryConditions::constant: { + for (; i <= min(in_min - 1, out_max); ++i) + out_array[i] = in_array[in_min]; + break; + } + default: { + error("ArrayFilter1DUsingConvolution: cannot handle this boundary condition yet. sorry"); + } + } { - for (; i<=min(in_max,out_max); ++i) - { - out_array[i] = in_array[i]; - } - } - switch (this->_bc) - { - case BoundaryConditions::zero: - { - for (; i<=out_max; ++i) - out_array[i] = 0; - break; - } - case BoundaryConditions::constant: - { - for (; i<=out_max; ++i) - out_array[i] = in_array[in_max]; - break; - } - default: - { - // should never get here, but without default: the compiler might issue a warning - } + for (; i <= min(in_max, out_max); ++i) { + out_array[i] = in_array[i]; } + } + switch (this->_bc) { + case BoundaryConditions::zero: { + for (; i <= out_max; ++i) + out_array[i] = 0; + break; + } + case BoundaryConditions::constant: { + for (; i <= out_max; ++i) + out_array[i] = in_array[in_max]; + break; + } + default: { + // should never get here, but without default: the compiler might issue a warning + } + } return; } const int j_min = filter_coefficients.get_min_index(); const int j_max = filter_coefficients.get_max_index(); - - for (int i=out_min; i<=out_max; i++) - { + for (int i = out_min; i <= out_max; i++) { out_array[i] = 0; - int j=j_min; + int j = j_min; // first do right edge - switch (this->_bc) - { - case BoundaryConditions::zero: - { - j=max(j_min, i-in_max); - break; - } - case BoundaryConditions::constant: - { - //i_in=i-j> in_max, hence j< i-in_max - for (; j< min(j_max+1, i-in_max); ++j) - out_array[i] += filter_coefficients[j]*in_array[in_max /*i-j*/]; - break; - } - default: - error("ArrayFilter1DUsingConvolution: unsupported boundary condition"); - } + switch (this->_bc) { + case BoundaryConditions::zero: { + j = max(j_min, i - in_max); + break; + } + case BoundaryConditions::constant: { + // i_in=i-j> in_max, hence j< i-in_max + for (; j < min(j_max + 1, i - in_max); ++j) + out_array[i] += filter_coefficients[j] * in_array[in_max /*i-j*/]; + break; + } + default: + error("ArrayFilter1DUsingConvolution: unsupported boundary condition"); + } // region unaffected by boundary { - for (; j<=min(j_max, i-in_min); ++j) - out_array[i] += filter_coefficients[j]*in_array[i-j]; + for (; j <= min(j_max, i - in_min); ++j) + out_array[i] += filter_coefficients[j] * in_array[i - j]; } // left edge - switch (this->_bc) - { - case BoundaryConditions::zero: - { - // nothing to do - break; - } - case BoundaryConditions::constant: - { - //i_in=i-j< in_min, hence j> i-in_min - for (; j<= j_max; ++j) - out_array[i] += filter_coefficients[j]*in_array[in_min /*i-j*/]; - break; - } - default: - { - // should never get here, but without default: the compiler might issue a warning - } - } + switch (this->_bc) { + case BoundaryConditions::zero: { + // nothing to do + break; + } + case BoundaryConditions::constant: { + // i_in=i-j< in_min, hence j> i-in_min + for (; j <= j_max; ++j) + out_array[i] += filter_coefficients[j] * in_array[in_min /*i-j*/]; + break; + } + default: { + // should never get here, but without default: the compiler might issue a warning + } + } } - } // instantiation template class ArrayFilter1DUsingConvolution; END_NAMESPACE_STIR - diff --git a/src/buildblock/ArrayFilter1DUsingConvolutionSymmetricKernel.cxx b/src/buildblock/ArrayFilter1DUsingConvolutionSymmetricKernel.cxx index 417faa3dd6..1c1d09ff3c 100644 --- a/src/buildblock/ArrayFilter1DUsingConvolutionSymmetricKernel.cxx +++ b/src/buildblock/ArrayFilter1DUsingConvolutionSymmetricKernel.cxx @@ -8,7 +8,7 @@ \author Kris Thielemans \author Sanida Mustafovic - + */ /* Copyright (C) 2000- 2009, Hammersmith Imanet Ltd @@ -36,39 +36,28 @@ using std::min; START_NAMESPACE_STIR template -ArrayFilter1DUsingConvolutionSymmetricKernel:: -ArrayFilter1DUsingConvolutionSymmetricKernel(const VectorWithOffset &filter_coefficients_v) -: filter_coefficients(filter_coefficients_v) -{ +ArrayFilter1DUsingConvolutionSymmetricKernel::ArrayFilter1DUsingConvolutionSymmetricKernel( + const VectorWithOffset& filter_coefficients_v) + : filter_coefficients(filter_coefficients_v) { // TODO: remove 0 elements at the outside - assert(filter_coefficients.get_length() == 0 || - filter_coefficients.get_min_index()==0); + assert(filter_coefficients.get_length() == 0 || filter_coefficients.get_min_index() == 0); } - template -bool -ArrayFilter1DUsingConvolutionSymmetricKernel:: -is_trivial() const -{ - return - filter_coefficients.get_length() == 0 || - (filter_coefficients.get_length()==1 && - filter_coefficients[0] == 1); +bool +ArrayFilter1DUsingConvolutionSymmetricKernel::is_trivial() const { + return filter_coefficients.get_length() == 0 || (filter_coefficients.get_length() == 1 && filter_coefficients[0] == 1); } // TODO generalise to arbitrary index ranges template void -ArrayFilter1DUsingConvolutionSymmetricKernel:: -do_it(Array<1,elemT>& out_array, const Array<1,elemT>& in_array) const -{ +ArrayFilter1DUsingConvolutionSymmetricKernel::do_it(Array<1, elemT>& out_array, const Array<1, elemT>& in_array) const { assert(out_array.get_min_index() == in_array.get_min_index()); assert(out_array.get_max_index() == in_array.get_max_index()); - if (is_trivial()) - { + if (is_trivial()) { out_array = in_array; return; } @@ -77,29 +66,27 @@ do_it(Array<1,elemT>& out_array, const Array<1,elemT>& in_array) const const int in_max = in_array.get_max_index(); const int j_max = filter_coefficients.get_max_index(); - for (int i=in_array.get_min_index(); - i<=in_array.get_max_index(); i++) - { - out_array[i] = filter_coefficients[0]*in_array[i]; - int j=1; + for (int i = in_array.get_min_index(); i <= in_array.get_max_index(); i++) { + out_array[i] = filter_coefficients[0] * in_array[i]; + int j = 1; // first do range where both i-j and i+j indices are valid for in_array - for (; j<=min(j_max,min(in_max-i, i-in_min)); j++) - out_array[i] += filter_coefficients[j]*(in_array[i-j]+in_array[i+j]); - //if (j>j_max) return; + for (; j <= min(j_max, min(in_max - i, i - in_min)); j++) + out_array[i] += filter_coefficients[j] * (in_array[i - j] + in_array[i + j]); + // if (j>j_max) return; // now do rest of i+j separate // next conditional is not necessary - //if (i-in_min < in_max-i) + // if (i-in_min < in_max-i) { - for (; j<=min(j_max,in_max-i); j++) - out_array[i] += filter_coefficients[j]*(in_array[i+j]); + for (; j <= min(j_max, in_max - i); j++) + out_array[i] += filter_coefficients[j] * (in_array[i + j]); } // now do rest of i-j separate // next conditional is not necessary - //else + // else { - for (; j<=min(j_max,i-in_min); j++) - out_array[i] += filter_coefficients[j]*(in_array[i-j]); + for (; j <= min(j_max, i - in_min); j++) + out_array[i] += filter_coefficients[j] * (in_array[i - j]); } } #if 0 @@ -161,7 +148,6 @@ do_it(Array<1,elemT>& out_array, const Array<1,elemT>& in_array) const #endif } - #if 0 template static void @@ -179,4 +165,3 @@ cir_shift_to_right(VectorWithOffset&output,const VectorWithOffset& template class ArrayFilter1DUsingConvolutionSymmetricKernel; END_NAMESPACE_STIR - diff --git a/src/buildblock/ArrayFilter2DUsingConvolution.cxx b/src/buildblock/ArrayFilter2DUsingConvolution.cxx index 8c15a8f221..616a5191fa 100644 --- a/src/buildblock/ArrayFilter2DUsingConvolution.cxx +++ b/src/buildblock/ArrayFilter2DUsingConvolution.cxx @@ -28,119 +28,84 @@ using std::min; START_NAMESPACE_STIR template -ArrayFilter2DUsingConvolution:: -ArrayFilter2DUsingConvolution() -: filter_coefficients() -{ - -} +ArrayFilter2DUsingConvolution::ArrayFilter2DUsingConvolution() : filter_coefficients() {} template -ArrayFilter2DUsingConvolution:: -ArrayFilter2DUsingConvolution(const Array <2, float> &filter_coefficients_v) -: filter_coefficients(filter_coefficients_v) -{ +ArrayFilter2DUsingConvolution::ArrayFilter2DUsingConvolution(const Array<2, float>& filter_coefficients_v) + : filter_coefficients(filter_coefficients_v) { // TODO: remove 0 elements at the outside } - template -bool -ArrayFilter2DUsingConvolution:: -is_trivial() const -{ - return - filter_coefficients.get_length() == 0 || - (filter_coefficients.get_length()==1 && filter_coefficients.get_min_index()==0 && - filter_coefficients[0][0] == 1); +bool +ArrayFilter2DUsingConvolution::is_trivial() const { + return filter_coefficients.get_length() == 0 || + (filter_coefficients.get_length() == 1 && filter_coefficients.get_min_index() == 0 && filter_coefficients[0][0] == 1); } - template -Succeeded -ArrayFilter2DUsingConvolution:: -get_influencing_indices(IndexRange<1>& influencing_index_range, - const IndexRange<1>& input_index_range) const -{ - influencing_index_range = - (filter_coefficients.get_length() == 0) - ? input_index_range - : IndexRange<1>(input_index_range.get_min_index() - filter_coefficients.get_max_index(), - input_index_range.get_max_index() - filter_coefficients.get_min_index()); +Succeeded +ArrayFilter2DUsingConvolution::get_influencing_indices(IndexRange<1>& influencing_index_range, + const IndexRange<1>& input_index_range) const { + influencing_index_range = (filter_coefficients.get_length() == 0) + ? input_index_range + : IndexRange<1>(input_index_range.get_min_index() - filter_coefficients.get_max_index(), + input_index_range.get_max_index() - filter_coefficients.get_min_index()); return Succeeded::yes; } template -Succeeded -ArrayFilter2DUsingConvolution:: -get_influenced_indices(IndexRange<1>& influenced_index_range, - const IndexRange<1>& output_index_range) const -{ - influenced_index_range = - (filter_coefficients.get_length() == 0) - ? output_index_range - : IndexRange<1>(output_index_range.get_min_index() + filter_coefficients.get_min_index(), - output_index_range.get_max_index() + filter_coefficients.get_max_index()); +Succeeded +ArrayFilter2DUsingConvolution::get_influenced_indices(IndexRange<1>& influenced_index_range, + const IndexRange<1>& output_index_range) const { + influenced_index_range = (filter_coefficients.get_length() == 0) + ? output_index_range + : IndexRange<1>(output_index_range.get_min_index() + filter_coefficients.get_min_index(), + output_index_range.get_max_index() + filter_coefficients.get_max_index()); return Succeeded::yes; } - template void -ArrayFilter2DUsingConvolution:: -do_it(Array<2,elemT>& out_array, const Array<2,elemT>& in_array) const -{ +ArrayFilter2DUsingConvolution::do_it(Array<2, elemT>& out_array, const Array<2, elemT>& in_array) const { const int in_min_y = in_array.get_min_index(); const int in_max_y = in_array.get_max_index(); const int in_min_x = in_array[in_min_y].get_min_index(); const int in_max_x = in_array[in_min_y].get_max_index(); - - + const int out_min_y = out_array.get_min_index(); const int out_max_y = out_array.get_max_index(); const int out_min_x = out_array[out_min_y].get_min_index(); const int out_max_x = out_array[out_min_y].get_max_index(); - - - - if (is_trivial()) - { - for (int y=out_min_y; y<=out_max_y; y++) - for (int x=out_min_x; x<=out_max_x; x++) - - { - out_array[y][x] = ((y>=in_min_y && y <= in_max_y ) && - (x>=in_min_x && x <= in_max_x ) ? in_array[y][x] : 0); - } - return; + + if (is_trivial()) { + for (int y = out_min_y; y <= out_max_y; y++) + for (int x = out_min_x; x <= out_max_x; x++) + + { + out_array[y][x] = ((y >= in_min_y && y <= in_max_y) && (x >= in_min_x && x <= in_max_x) ? in_array[y][x] : 0); + } + return; } - + const int j_min = filter_coefficients.get_min_index(); const int j_max = filter_coefficients.get_max_index(); const int i_min = filter_coefficients[j_min].get_min_index(); const int i_max = filter_coefficients[j_min].get_max_index(); - - - - for (int y=out_min_y; y<=out_max_y; y++) - for (int x=out_min_x; x<=out_max_x; x++) - { - out_array[y][x] = 0; - - for (int j=max(j_min, y-in_max_y); j<=min(j_max, y-in_min_y); j++) - for (int i=max(i_min, x-in_max_x); i<=min(i_max, x-in_min_x); i++) - - out_array[y][x] += filter_coefficients[j][i]*in_array[y-j][x-i]; - } - - -} + for (int y = out_min_y; y <= out_max_y; y++) + for (int x = out_min_x; x <= out_max_x; x++) { + out_array[y][x] = 0; + + for (int j = max(j_min, y - in_max_y); j <= min(j_max, y - in_min_y); j++) + for (int i = max(i_min, x - in_max_x); i <= min(i_max, x - in_min_x); i++) + out_array[y][x] += filter_coefficients[j][i] * in_array[y - j][x - i]; + } +} // instantiation template class ArrayFilter2DUsingConvolution; END_NAMESPACE_STIR - diff --git a/src/buildblock/ArrayFilter3DUsingConvolution.cxx b/src/buildblock/ArrayFilter3DUsingConvolution.cxx index 0bea260c1e..f477b7bde8 100644 --- a/src/buildblock/ArrayFilter3DUsingConvolution.cxx +++ b/src/buildblock/ArrayFilter3DUsingConvolution.cxx @@ -37,72 +37,50 @@ using std::cerr; using std::endl; #endif - START_NAMESPACE_STIR template -ArrayFilter3DUsingConvolution:: -ArrayFilter3DUsingConvolution() -: filter_coefficients() -{ - -} +ArrayFilter3DUsingConvolution::ArrayFilter3DUsingConvolution() : filter_coefficients() {} template -ArrayFilter3DUsingConvolution:: -ArrayFilter3DUsingConvolution(const Array <3, float> &filter_coefficients_v) -: filter_coefficients(filter_coefficients_v) -{ +ArrayFilter3DUsingConvolution::ArrayFilter3DUsingConvolution(const Array<3, float>& filter_coefficients_v) + : filter_coefficients(filter_coefficients_v) { // TODO: remove 0 elements at the outside } - template -bool -ArrayFilter3DUsingConvolution:: -is_trivial() const -{ - return - filter_coefficients.get_length() == 0 || - (filter_coefficients.get_length()==1 && filter_coefficients.get_min_index()==0 && - filter_coefficients[0][0][0] == 1); +bool +ArrayFilter3DUsingConvolution::is_trivial() const { + return filter_coefficients.get_length() == 0 || + (filter_coefficients.get_length() == 1 && filter_coefficients.get_min_index() == 0 && filter_coefficients[0][0][0] == 1); } - template -Succeeded -ArrayFilter3DUsingConvolution:: -get_influencing_indices(IndexRange<1>& influencing_index_range, - const IndexRange<1>& input_index_range) const -{ - influencing_index_range = - (filter_coefficients.get_length() == 0) - ? input_index_range - : IndexRange<1>(input_index_range.get_min_index() - filter_coefficients.get_max_index(), - input_index_range.get_max_index() - filter_coefficients.get_min_index()); +Succeeded +ArrayFilter3DUsingConvolution::get_influencing_indices(IndexRange<1>& influencing_index_range, + const IndexRange<1>& input_index_range) const { + influencing_index_range = (filter_coefficients.get_length() == 0) + ? input_index_range + : IndexRange<1>(input_index_range.get_min_index() - filter_coefficients.get_max_index(), + input_index_range.get_max_index() - filter_coefficients.get_min_index()); return Succeeded::yes; } template -Succeeded -ArrayFilter3DUsingConvolution:: -get_influenced_indices(IndexRange<1>& influenced_index_range, - const IndexRange<1>& output_index_range) const -{ - influenced_index_range = - (filter_coefficients.get_length() == 0) - ? output_index_range - : IndexRange<1>(output_index_range.get_min_index() + filter_coefficients.get_min_index(), - output_index_range.get_max_index() + filter_coefficients.get_max_index()); +Succeeded +ArrayFilter3DUsingConvolution::get_influenced_indices(IndexRange<1>& influenced_index_range, + const IndexRange<1>& output_index_range) const { + influenced_index_range = (filter_coefficients.get_length() == 0) + ? output_index_range + : IndexRange<1>(output_index_range.get_min_index() + filter_coefficients.get_min_index(), + output_index_range.get_max_index() + filter_coefficients.get_max_index()); return Succeeded::yes; } #if 1 template void -ArrayFilter3DUsingConvolution:: -do_it(Array<3,elemT>& out_array, const Array<3,elemT>& in_array) const -{ +ArrayFilter3DUsingConvolution::do_it(Array<3, elemT>& out_array, const Array<3, elemT>& in_array) const { const int in_min_z = in_array.get_min_index(); const int in_max_z = in_array.get_max_index(); @@ -110,69 +88,62 @@ do_it(Array<3,elemT>& out_array, const Array<3,elemT>& in_array) const const int in_max_y = in_array[in_min_z].get_max_index(); const int in_min_x = in_array[in_min_z][in_min_y].get_min_index(); const int in_max_x = in_array[in_min_z][in_min_y].get_max_index(); - - + const int out_min_z = out_array.get_min_index(); const int out_max_z = out_array.get_max_index(); const int out_min_y = out_array[out_min_z].get_min_index(); const int out_max_y = out_array[out_min_z].get_max_index(); const int out_min_x = out_array[out_min_z][out_min_y].get_min_index(); const int out_max_x = out_array[out_min_z][out_min_y].get_max_index(); - - if (is_trivial()) - { - for (int z=out_min_z; z<=out_max_z; z++) - for (int y=out_min_y; y<=out_max_y; y++) - for (int x=out_min_x; x<=out_max_x; x++) - - { - out_array[z][y][x] = ((z>=in_min_z && z <= in_max_z ) && (y>=in_min_y && y <= in_max_y ) && - (x>=in_min_x && x <= in_max_x ) ? in_array[z][y][x] : 0); - } - return; - } - + + if (is_trivial()) { + for (int z = out_min_z; z <= out_max_z; z++) + for (int y = out_min_y; y <= out_max_y; y++) + for (int x = out_min_x; x <= out_max_x; x++) + + { + out_array[z][y][x] = + ((z >= in_min_z && z <= in_max_z) && (y >= in_min_y && y <= in_max_y) && (x >= in_min_x && x <= in_max_x) + ? in_array[z][y][x] + : 0); + } + return; + } + const int k_min = filter_coefficients.get_min_index(); const int k_max = filter_coefficients.get_max_index(); - + const int j_min = filter_coefficients[k_min].get_min_index(); const int j_max = filter_coefficients[k_min].get_max_index(); const int i_min = filter_coefficients[k_min][j_min].get_min_index(); const int i_max = filter_coefficients[k_min][j_min].get_max_index(); - - - if (true)//k_min != k_max) - { - - for (int z=out_min_z; z<=out_max_z; z++) - for (int y=out_min_y; y<=out_max_y; y++) - for (int x=out_min_x; x<=out_max_x; x++) - { - out_array[z][y][x] = 0; - - for (int k=max(k_min, z-in_max_z); k<=min(k_max, z-in_min_z); k++) - for (int j=max(j_min, y-in_max_y); j<=min(j_max, y-in_min_y); j++) - for (int i=max(i_min, x-in_max_x); i<=min(i_max, x-in_min_x); i++) - - out_array[z][y][x] += filter_coefficients[k][j][i]*in_array[z-k][y-j][x-i]; - } - - } - else + + if (true) // k_min != k_max) { - if (out_min_y!= out_max_y || out_min_x!=out_max_x) + + for (int z = out_min_z; z <= out_max_z; z++) + for (int y = out_min_y; y <= out_max_y; y++) + for (int x = out_min_x; x <= out_max_x; x++) { + out_array[z][y][x] = 0; + + for (int k = max(k_min, z - in_max_z); k <= min(k_max, z - in_min_z); k++) + for (int j = max(j_min, y - in_max_y); j <= min(j_max, y - in_min_y); j++) + for (int i = max(i_min, x - in_max_x); i <= min(i_max, x - in_min_x); i++) + + out_array[z][y][x] += filter_coefficients[k][j][i] * in_array[z - k][y - j][x - i]; + } + + } else { + if (out_min_y != out_max_y || out_min_x != out_max_x) error("3D convolution. check code\n"); - Array<2,float> array_out_tmp (IndexRange2D(out_min_y,out_max_y,out_min_x,out_max_x)); + Array<2, float> array_out_tmp(IndexRange2D(out_min_y, out_max_y, out_min_x, out_max_x)); do_it_2d(array_out_tmp, in_array[out_min_z]); - out_array[out_min_z][out_min_y][out_min_x] = array_out_tmp[out_min_y][out_min_x]; - + out_array[out_min_z][out_min_y][out_min_x] = array_out_tmp[out_min_y][out_min_x]; } - } #endif - #if 0 template void @@ -234,65 +205,49 @@ do_it(Array<3,elemT>& out_array, const Array<3,elemT>& in_array) const #endif - template void -ArrayFilter3DUsingConvolution:: -do_it_2d(Array<2,elemT>& out_array, const Array<2,elemT>& in_array) const -{ +ArrayFilter3DUsingConvolution::do_it_2d(Array<2, elemT>& out_array, const Array<2, elemT>& in_array) const { const int in_min_y = in_array.get_min_index(); const int in_max_y = in_array.get_max_index(); const int in_min_x = in_array[in_min_y].get_min_index(); const int in_max_x = in_array[in_min_y].get_max_index(); - - + const int out_min_y = out_array.get_min_index(); const int out_max_y = out_array.get_max_index(); const int out_min_x = out_array[out_min_y].get_min_index(); const int out_max_x = out_array[out_min_y].get_max_index(); - - - - if (is_trivial()) - { - for (int y=out_min_y; y<=out_max_y; y++) - for (int x=out_min_x; x<=out_max_x; x++) - + + if (is_trivial()) { + for (int y = out_min_y; y <= out_max_y; y++) + for (int x = out_min_x; x <= out_max_x; x++) + { - out_array[y][x] = ((y>=in_min_y && y <= in_max_y ) && - (x>=in_min_x && x <= in_max_x ) ? in_array[y][x] : 0); + out_array[y][x] = ((y >= in_min_y && y <= in_max_y) && (x >= in_min_x && x <= in_max_x) ? in_array[y][x] : 0); } - return; + return; } - - + const int k_min = filter_coefficients.get_min_index(); - //const int k_max = filter_coefficients.get_max_index(); + // const int k_max = filter_coefficients.get_max_index(); const int j_min = filter_coefficients[k_min].get_min_index(); const int j_max = filter_coefficients[k_min].get_max_index(); const int i_min = filter_coefficients[k_min][j_min].get_min_index(); const int i_max = filter_coefficients[k_min][j_min].get_max_index(); - - - - for (int y=out_min_y; y<=out_max_y; y++) - for (int x=out_min_x; x<=out_max_x; x++) - { + + for (int y = out_min_y; y <= out_max_y; y++) + for (int x = out_min_x; x <= out_max_x; x++) { out_array[y][x] = 0; - - for (int j=max(j_min, y-in_max_y); j<=min(j_max, y-in_min_y); j++) - for (int i=max(i_min, x-in_max_x); i<=min(i_max, x-in_min_x); i++) - - out_array[y][x] += filter_coefficients[filter_coefficients.get_min_index()][j][i]*in_array[y-j][x-i]; + + for (int j = max(j_min, y - in_max_y); j <= min(j_max, y - in_min_y); j++) + for (int i = max(i_min, x - in_max_x); i <= min(i_max, x - in_min_x); i++) + + out_array[y][x] += filter_coefficients[filter_coefficients.get_min_index()][j][i] * in_array[y - j][x - i]; } - - } - // instantiation template class ArrayFilter3DUsingConvolution; END_NAMESPACE_STIR - diff --git a/src/buildblock/ArrayFilterUsingRealDFTWithPadding.cxx b/src/buildblock/ArrayFilterUsingRealDFTWithPadding.cxx index a2e2b3320d..7d4202055b 100644 --- a/src/buildblock/ArrayFilterUsingRealDFTWithPadding.cxx +++ b/src/buildblock/ArrayFilterUsingRealDFTWithPadding.cxx @@ -34,141 +34,110 @@ #include "stir/modulo.h" #include - START_NAMESPACE_STIR template -ArrayFilterUsingRealDFTWithPadding:: -ArrayFilterUsingRealDFTWithPadding() -{} +ArrayFilterUsingRealDFTWithPadding::ArrayFilterUsingRealDFTWithPadding() {} template -ArrayFilterUsingRealDFTWithPadding:: -ArrayFilterUsingRealDFTWithPadding(const Array& real_filter_kernel) -{ - if (set_kernel(real_filter_kernel) == Succeeded::no) +ArrayFilterUsingRealDFTWithPadding::ArrayFilterUsingRealDFTWithPadding( + const Array& real_filter_kernel) { + if (set_kernel(real_filter_kernel) == Succeeded::no) error("Error constructing ArrayFilterUsingRealDFTWithPadding\n"); } #ifndef __stir_ArrayFilterUsingRealDFTWithPadding_no_complex_kernel__ template -ArrayFilterUsingRealDFTWithPadding:: -ArrayFilterUsingRealDFTWithPadding(const Array >& kernel_in_frequency_space) -{ - if (set_kernel_in_frequency_space(kernel_in_frequency_space) == Succeeded::no) +ArrayFilterUsingRealDFTWithPadding::ArrayFilterUsingRealDFTWithPadding( + const Array>& kernel_in_frequency_space) { + if (set_kernel_in_frequency_space(kernel_in_frequency_space) == Succeeded::no) error("Error constructing ArrayFilterUsingRealDFTWithPadding\n"); } #endif template Succeeded -ArrayFilterUsingRealDFTWithPadding:: -set_padding_range() -{ +ArrayFilterUsingRealDFTWithPadding::set_padding_range() { BasicCoordinate min_indices, max_indices; if (!kernel_in_frequency_space.get_regular_range(min_indices, max_indices)) return Succeeded::no; // check if kernel_in_frequency_space is 0-based, as currently required by fourier // TODO we could wrap-around if not - for (int d=1; d<=num_dimensions; ++d) - { - if (min_indices[d]!=0) - return Succeeded::no; - } - max_indices[num_dimensions] = 2*max_indices[num_dimensions] - 1; - this->padding_range = IndexRange(min_indices, max_indices); - this->padded_sizes = max_indices - min_indices +1; + for (int d = 1; d <= num_dimensions; ++d) { + if (min_indices[d] != 0) + return Succeeded::no; + } + max_indices[num_dimensions] = 2 * max_indices[num_dimensions] - 1; + this->padding_range = IndexRange(min_indices, max_indices); + this->padded_sizes = max_indices - min_indices + 1; return Succeeded::yes; } template -Succeeded -ArrayFilterUsingRealDFTWithPadding:: -set_kernel(const Array& real_filter_kernel) -{ +Succeeded +ArrayFilterUsingRealDFTWithPadding::set_kernel(const Array& real_filter_kernel) { BasicCoordinate min_indices, max_indices; if (!real_filter_kernel.get_regular_range(min_indices, max_indices)) return Succeeded::no; // check if we need to use wrap-around - if (norm(min_indices)<.01) // i.e. min_indices==0 - { - kernel_in_frequency_space = - fourier_for_real_data(real_filter_kernel); - }\ - else - { - // copy data to new kernel using wrap-around - const BasicCoordinate sizes = - max_indices - min_indices + 1; - const IndexRange range(sizes); - Array real_filter_kernel_from_0(range); - transform_array_to_periodic_indices(real_filter_kernel_from_0, - real_filter_kernel); - - // do DFT on this array - kernel_in_frequency_space = - fourier_for_real_data(real_filter_kernel_from_0); - } + if (norm(min_indices) < .01) // i.e. min_indices==0 + { + kernel_in_frequency_space = fourier_for_real_data(real_filter_kernel); + } else { + // copy data to new kernel using wrap-around + const BasicCoordinate sizes = max_indices - min_indices + 1; + const IndexRange range(sizes); + Array real_filter_kernel_from_0(range); + transform_array_to_periodic_indices(real_filter_kernel_from_0, real_filter_kernel); + + // do DFT on this array + kernel_in_frequency_space = fourier_for_real_data(real_filter_kernel_from_0); + } return this->set_padding_range(); } template Succeeded -ArrayFilterUsingRealDFTWithPadding:: -set_kernel_in_frequency_space(const Array >& kernel_in_frequency_space_v) -{ +ArrayFilterUsingRealDFTWithPadding::set_kernel_in_frequency_space( + const Array>& kernel_in_frequency_space_v) { kernel_in_frequency_space = kernel_in_frequency_space_v; return this->set_padding_range(); } template -bool ArrayFilterUsingRealDFTWithPadding:: -is_trivial() const -{ - return - kernel_in_frequency_space.size_all()==0 || - (kernel_in_frequency_space.size_all()==1 && - (*kernel_in_frequency_space.begin_all()) == std::complex(1,0)); +bool +ArrayFilterUsingRealDFTWithPadding::is_trivial() const { + return kernel_in_frequency_space.size_all() == 0 || + (kernel_in_frequency_space.size_all() == 1 && (*kernel_in_frequency_space.begin_all()) == std::complex(1, 0)); } - template -void -ArrayFilterUsingRealDFTWithPadding:: -do_it(Array& out_array, const Array& in_array) const -{ - if (in_array.get_index_range() == this->padding_range && - out_array.get_index_range() == this->padding_range) - { - // convolution using DFT - { - Array > tmp = - fourier_for_real_data(in_array); - tmp *= kernel_in_frequency_space; - out_array = inverse_fourier_for_real_data_corrupting_input(tmp); - } - } - else +void +ArrayFilterUsingRealDFTWithPadding::do_it(Array& out_array, + const Array& in_array) const { + if (in_array.get_index_range() == this->padding_range && out_array.get_index_range() == this->padding_range) { + // convolution using DFT { - // copy input into padded_array using wrap-around - - Array padded_array(this->padding_range); - transform_array_to_periodic_indices(padded_array, in_array); - // call do_it with padded_array - do_it(padded_array, padded_array); - // Now copy result in out_array using wrap-around - transform_array_from_periodic_indices(out_array, padded_array); + Array> tmp = fourier_for_real_data(in_array); + tmp *= kernel_in_frequency_space; + out_array = inverse_fourier_for_real_data_corrupting_input(tmp); } + } else { + // copy input into padded_array using wrap-around + + Array padded_array(this->padding_range); + transform_array_to_periodic_indices(padded_array, in_array); + // call do_it with padded_array + do_it(padded_array, padded_array); + // Now copy result in out_array using wrap-around + transform_array_from_periodic_indices(out_array, padded_array); + } } -template class ArrayFilterUsingRealDFTWithPadding<1,float>; -template class ArrayFilterUsingRealDFTWithPadding<2,float>; -template class ArrayFilterUsingRealDFTWithPadding<3,float>; +template class ArrayFilterUsingRealDFTWithPadding<1, float>; +template class ArrayFilterUsingRealDFTWithPadding<2, float>; +template class ArrayFilterUsingRealDFTWithPadding<3, float>; END_NAMESPACE_STIR - - - diff --git a/src/buildblock/ByteOrder.cxx b/src/buildblock/ByteOrder.cxx index e1934737c2..c43f92256b 100644 --- a/src/buildblock/ByteOrder.cxx +++ b/src/buildblock/ByteOrder.cxx @@ -1,7 +1,7 @@ // // /*! - \file + \file \ingroup buildblock \brief This file initialises ByteOrder::native_order. @@ -37,7 +37,6 @@ START_NAMESPACE_STIR - /* A somewhat complicated way to determine the byteorder. The advantage is that it doesn't need ntohs (and so any compiler specific definitions, libraries or whatever). @@ -45,22 +44,19 @@ START_NAMESPACE_STIR by casting its address as a char *. The reinterpret_cash is to make it typesafe for C++. - First we do a (paranoid) check : + First we do a (paranoid) check : sizeof(unsigned long) - sizeof(unsigned char) > 0 - This is done via a 'compile-time assertion', i.e. it breaks + This is done via a 'compile-time assertion', i.e. it breaks at compile time when the assertion is false. The line below relies on the fact that you cannot have an array with zero (or less) elements. */ -typedef char - assert_unsigned_long_size[sizeof(unsigned long) - sizeof(unsigned char)]; +typedef char assert_unsigned_long_size[sizeof(unsigned long) - sizeof(unsigned char)]; static const unsigned long magic = 1; const ByteOrder::Order ByteOrder::native_order = - *(reinterpret_cast(&magic) ) == 1 ? - little_endian : big_endian; - + *(reinterpret_cast(&magic)) == 1 ? little_endian : big_endian; END_NAMESPACE_STIR diff --git a/src/buildblock/ChainedDataProcessor.cxx b/src/buildblock/ChainedDataProcessor.cxx index c8bfc1fa2e..88f192a1b7 100644 --- a/src/buildblock/ChainedDataProcessor.cxx +++ b/src/buildblock/ChainedDataProcessor.cxx @@ -26,93 +26,67 @@ */ #include "stir/ChainedDataProcessor.h" #include "stir/DiscretisedDensity.h" -#include "stir/modelling/ParametricDiscretisedDensity.h" -#include "stir/modelling/KineticParameters.h" +#include "stir/modelling/ParametricDiscretisedDensity.h" +#include "stir/modelling/KineticParameters.h" #include "stir/is_null_ptr.h" #include #include "stir/unique_ptr.h" START_NAMESPACE_STIR - template Succeeded -ChainedDataProcessor:: -virtual_set_up(const DataT& data) -{ - if (!is_null_ptr(this->apply_first)) - { - // note that we cannot really build the filter for the 2nd - // as we don't know what the first will do to the dimensions etc. of the data - return this->apply_first->set_up(data); - } - else if (!is_null_ptr(this->apply_second)) +ChainedDataProcessor::virtual_set_up(const DataT& data) { + if (!is_null_ptr(this->apply_first)) { + // note that we cannot really build the filter for the 2nd + // as we don't know what the first will do to the dimensions etc. of the data + return this->apply_first->set_up(data); + } else if (!is_null_ptr(this->apply_second)) return this->apply_second->set_up(data); else - return Succeeded::yes; + return Succeeded::yes; } - template void -ChainedDataProcessor:: -virtual_apply(DataT& data) const -{ +ChainedDataProcessor::virtual_apply(DataT& data) const { if (!is_null_ptr(this->apply_first)) this->apply_first->apply(data); if (!is_null_ptr(this->apply_second)) this->apply_second->apply(data); } - template void -ChainedDataProcessor:: -virtual_apply(DataT& out_data, - const DataT& in_data) const -{ - if (!is_null_ptr(this->apply_first)) - { - if (!is_null_ptr(this->apply_second)) - { - // a bit complicated because we need a temporary data copy - unique_ptr< DataT> temp_data_ptr - (in_data.get_empty_copy()); - this->apply_first->apply(*temp_data_ptr, in_data); - this->apply_second->apply(out_data, *temp_data_ptr); - } - else - this->apply_first->apply(out_data, in_data); - } - else - if (!is_null_ptr(this->apply_second)) - this->apply_second->apply(out_data, in_data); - +ChainedDataProcessor::virtual_apply(DataT& out_data, const DataT& in_data) const { + if (!is_null_ptr(this->apply_first)) { + if (!is_null_ptr(this->apply_second)) { + // a bit complicated because we need a temporary data copy + unique_ptr temp_data_ptr(in_data.get_empty_copy()); + this->apply_first->apply(*temp_data_ptr, in_data); + this->apply_second->apply(out_data, *temp_data_ptr); + } else + this->apply_first->apply(out_data, in_data); + } else if (!is_null_ptr(this->apply_second)) + this->apply_second->apply(out_data, in_data); } template -ChainedDataProcessor:: -ChainedDataProcessor(shared_ptr > apply_first_v, - shared_ptr > apply_second_v) - : apply_first(apply_first_v), - apply_second(apply_second_v) -{ +ChainedDataProcessor::ChainedDataProcessor(shared_ptr> apply_first_v, + shared_ptr> apply_second_v) + : apply_first(apply_first_v), apply_second(apply_second_v) { this->set_defaults(); } template void -ChainedDataProcessor:: -set_defaults() -{ +ChainedDataProcessor::set_defaults() { base_type::set_defaults(); } template -void -ChainedDataProcessor:: -initialise_keymap() -{ +void +ChainedDataProcessor::initialise_keymap() { base_type::initialise_keymap(); this->parser.add_start_key("Chained Data Processor Parameters"); this->parser.add_parsing_key("Data Processor to apply first", &this->apply_first); @@ -120,29 +94,19 @@ initialise_keymap() this->parser.add_stop_key("END Chained Data Processor Parameters"); } - - template -const char * const -ChainedDataProcessor::registered_name = - "Chained Data Processor"; - +const char* const ChainedDataProcessor::registered_name = "Chained Data Processor"; -# ifdef _MSC_VER -// prevent warning message on reinstantiation, +#ifdef _MSC_VER +// prevent warning message on reinstantiation, // note that we get a linking error if we don't have the explicit instantiation below -# pragma warning(disable:4660) -# endif +# pragma warning(disable : 4660) +#endif // Register this class in the DataProcessor registry // static ChainedDataProcessor::RegisterIt dummy; // have the above variable in a separate file, which you need to pass at link time -template class ChainedDataProcessor >; -template class ChainedDataProcessor; +template class ChainedDataProcessor>; +template class ChainedDataProcessor; END_NAMESPACE_STIR - - - - - diff --git a/src/buildblock/DataSymmetriesForViewSegmentNumbers.cxx b/src/buildblock/DataSymmetriesForViewSegmentNumbers.cxx index a40108d255..6adb57fd35 100644 --- a/src/buildblock/DataSymmetriesForViewSegmentNumbers.cxx +++ b/src/buildblock/DataSymmetriesForViewSegmentNumbers.cxx @@ -17,7 +17,7 @@ */ /*! \file - \ingroup projdata + \ingroup projdata \brief Implementations for class stir::DataSymmetriesForViewSegmentNumbers \author Kris Thielemans @@ -32,50 +32,34 @@ using std::vector; START_NAMESPACE_STIR -DataSymmetriesForViewSegmentNumbers:: -~DataSymmetriesForViewSegmentNumbers() -{} +DataSymmetriesForViewSegmentNumbers::~DataSymmetriesForViewSegmentNumbers() {} /*! Default implementation always returns \c true. Needs to be overloaded. */ bool -DataSymmetriesForViewSegmentNumbers:: -blindly_equals(const root_type * const) const -{ +DataSymmetriesForViewSegmentNumbers::blindly_equals(const root_type* const) const { return true; } bool -DataSymmetriesForViewSegmentNumbers:: -operator ==(const root_type& that) const -{ - return - typeid(*this) == typeid(that) && - (this == &that || - this->blindly_equals(&that) - ); +DataSymmetriesForViewSegmentNumbers::operator==(const root_type& that) const { + return typeid(*this) == typeid(that) && (this == &that || this->blindly_equals(&that)); } bool -DataSymmetriesForViewSegmentNumbers:: -operator !=(const root_type& that) const -{ +DataSymmetriesForViewSegmentNumbers::operator!=(const root_type& that) const { return !((*this) == that); } - int -DataSymmetriesForViewSegmentNumbers::num_related_view_segment_numbers(const ViewSegmentNumbers& vs) const -{ +DataSymmetriesForViewSegmentNumbers::num_related_view_segment_numbers(const ViewSegmentNumbers& vs) const { vector rel_vs; get_related_view_segment_numbers(rel_vs, vs); return static_cast(rel_vs.size()); } bool -DataSymmetriesForViewSegmentNumbers:: -is_basic(const ViewSegmentNumbers& v_s) const -{ +DataSymmetriesForViewSegmentNumbers::is_basic(const ViewSegmentNumbers& v_s) const { ViewSegmentNumbers copy = v_s; return !find_basic_view_segment_numbers(copy); } diff --git a/src/buildblock/DiscretisedDensity.cxx b/src/buildblock/DiscretisedDensity.cxx index 704452ff4e..e1c457c326 100644 --- a/src/buildblock/DiscretisedDensity.cxx +++ b/src/buildblock/DiscretisedDensity.cxx @@ -2,12 +2,12 @@ // /*! - \file + \file \ingroup densitydata - + \brief Implementations of non-inline functions of class stir::DiscretisedDensity - \author Kris Thielemans + \author Kris Thielemans \author Ashley Gillman \author PARAPET project @@ -33,19 +33,19 @@ */ #include "stir/DiscretisedDensity.h" #if 1 -#include "stir/IO/read_from_file.h" +# include "stir/IO/read_from_file.h" #else -#include "stir/IO/interfile.h" -#ifdef HAVE_LLN_MATRIX -#include "stir/IO/ecat6_utils.h" -#include "stir/IO/stir_ecat6.h" -#include "stir/IO/stir_ecat7.h" -#endif -#include "stir/VoxelsOnCartesianGrid.h" -#include "stir/is_null_ptr.h" -#ifdef STIR_USE_GE_IO -#include "stir_experimental/IO/GE/niff.h" -#endif +# include "stir/IO/interfile.h" +# ifdef HAVE_LLN_MATRIX +# include "stir/IO/ecat6_utils.h" +# include "stir/IO/stir_ecat6.h" +# include "stir/IO/stir_ecat7.h" +# endif +# include "stir/VoxelsOnCartesianGrid.h" +# include "stir/is_null_ptr.h" +# ifdef STIR_USE_GE_IO +# include "stir_experimental/IO/GE/niff.h" +# endif #endif #include @@ -62,35 +62,33 @@ START_NAMESPACE_STIR // sadly, gcc 2.95.* does not support local namespaces as used below // This is slightly funny as it does work in ProjData.cxx. // Maybe because here it's in a template? -# if __GNUC__ == 2 -#ifdef HAVE_LLN_MATRIX +# if __GNUC__ == 2 +# ifdef HAVE_LLN_MATRIX USING_NAMESPACE_ECAT USING_NAMESPACE_ECAT7 USING_NAMESPACE_ECAT6 -#endif -#endif +# endif +# endif #endif -/*! +/*! \deprecated This function just calls stir::read_from_file. */ -template -DiscretisedDensity * -DiscretisedDensity:: - read_from_file(const string& filename) -{ +template +DiscretisedDensity* +DiscretisedDensity::read_from_file(const string& filename) { #if 1 - unique_ptr > density_aptr - (stir::read_from_file >(filename)); + unique_ptr> density_aptr( + stir::read_from_file>(filename)); return density_aptr.release(); #else if (num_dimensions != 3 || typeid(elemT) != typeid(float)) error("DiscretisedDensity::read_from_file currently only supports 3d float images\n"); - const int max_length=300; + const int max_length = 300; char signature[max_length]; // read signature @@ -98,144 +96,118 @@ DiscretisedDensity:: fstream input(filename.c_str(), ios::in | ios::binary); if (!input) error("DiscretisedDensity::read_from_file: error opening file %s\n", filename.c_str()); - + input.read(signature, max_length); - signature[max_length-1]='\0'; + signature[max_length - 1] = '\0'; } // Interfile - if (is_interfile_signature(signature)) - { -#ifndef NDEBUG - warning("DiscretisedDensity::read_from_file trying to read %s as Interfile\n", - filename.c_str()); -#endif - DiscretisedDensity * density_ptr = - read_interfile_image(filename); + if (is_interfile_signature(signature)) { +# ifndef NDEBUG + warning("DiscretisedDensity::read_from_file trying to read %s as Interfile\n", filename.c_str()); +# endif + DiscretisedDensity* density_ptr = read_interfile_image(filename); if (!is_null_ptr(density_ptr)) return density_ptr; } - - -#ifdef HAVE_LLN_MATRIX - if (strncmp(signature, "MATRIX", 6) == 0) - { -#ifndef NDEBUG +# ifdef HAVE_LLN_MATRIX + if (strncmp(signature, "MATRIX", 6) == 0) { +# ifndef NDEBUG warning("DiscretisedDensity::read_from_file trying to read %s as ECAT7\n", filename.c_str()); -#endif +# endif USING_NAMESPACE_ECAT USING_NAMESPACE_ECAT7 - if (is_ECAT7_image_file(filename)) - { - warning("\nReading frame 1, gate 1, data 0, bed 0 from file %s\n", - filename.c_str()); - DiscretisedDensity * density_ptr = - ECAT7_to_VoxelsOnCartesianGrid(filename, - /*frame_num, gate_num, data_num, bed_num*/1,1,0,0); + if (is_ECAT7_image_file(filename)) { + warning("\nReading frame 1, gate 1, data 0, bed 0 from file %s\n", filename.c_str()); + DiscretisedDensity* density_ptr = + ECAT7_to_VoxelsOnCartesianGrid(filename, + /*frame_num, gate_num, data_num, bed_num*/ 1, 1, 0, 0); if (!is_null_ptr(density_ptr)) - return density_ptr; - } - else - { + return density_ptr; + } else { if (is_ECAT7_file(filename)) - warning("DiscretisedDensity::read_from_file ECAT7 file %s is of unsupported file type\n", filename.c_str()); + warning("DiscretisedDensity::read_from_file ECAT7 file %s is of unsupported file type\n", filename.c_str()); } - } -#endif // HAVE_LLN_MATRIX +# endif // HAVE_LLN_MATRIX -#ifdef STIR_USE_GE_IO +# ifdef STIR_USE_GE_IO // NIFF file format (a simple almost raw format from GE) { using namespace GE_IO; - if (Niff::isReadableNiffFile(filename)) + if (Niff::isReadableNiffFile(filename)) { + Niff::Niff input_niff(filename, std::ios::in | std::ios::out); + if (input_niff.get_num_dimensions() != 3) { + warning("DiscretisedDensity::read_from_file:\n" + "File %s is a NIFF file but has %d dimensions (should be 3)", + filename.c_str(), input_niff.get_num_dimensions()); + return 0; + } + CartesianCoordinate3D voxel_size; + { + std::vector pixel_spacing = input_niff.get_pixel_spacing(); + std::copy(pixel_spacing.begin(), pixel_spacing.end(), voxel_size.begin()); + } + Array<3, float> data; { - Niff::Niff input_niff(filename, std::ios::in | std::ios::out); - if (input_niff.get_num_dimensions() != 3) - { - warning("DiscretisedDensity::read_from_file:\n" - "File %s is a NIFF file but has %d dimensions (should be 3)", - filename.c_str(), input_niff.get_num_dimensions()); - return 0; - } - CartesianCoordinate3D voxel_size; - { - std::vector pixel_spacing = input_niff.get_pixel_spacing(); - std::copy(pixel_spacing.begin(), pixel_spacing.end(), voxel_size.begin()); - } - Array<3, float> data; - { - input_niff.fill_array(data); - // change sizes to standard STIR convention - for (int z=data.get_min_index(); z<= data.get_max_index(); ++z) - { - data[z].set_min_index(-(data[z].get_length()/2)); - for (int y=data[z].get_min_index(); y<= data[z].get_max_index(); ++y) - { - data[z][y].set_min_index(-(data[z][y].get_length()/2)); - } - } - } - CartesianCoordinate3D origin; - origin.fill(0); - - return - new VoxelsOnCartesianGrid(data, - origin, - voxel_size); + input_niff.fill_array(data); + // change sizes to standard STIR convention + for (int z = data.get_min_index(); z <= data.get_max_index(); ++z) { + data[z].set_min_index(-(data[z].get_length() / 2)); + for (int y = data[z].get_min_index(); y <= data[z].get_max_index(); ++y) { + data[z][y].set_min_index(-(data[z][y].get_length() / 2)); + } + } } + CartesianCoordinate3D origin; + origin.fill(0); + + return new VoxelsOnCartesianGrid(data, origin, voxel_size); + } } -#endif +# endif -#ifdef HAVE_LLN_MATRIX +# ifdef HAVE_LLN_MATRIX { // Try ECAT6 // ECAT6 does not have a signature -#ifndef NDEBUG +# ifndef NDEBUG warning("DiscretisedDensity::read_from_file trying to read %s as ECAT6\n", filename.c_str()); -#endif +# endif USING_NAMESPACE_ECAT; USING_NAMESPACE_ECAT6; - if (is_ECAT6_image_file(filename)) - { - ECAT6_Main_header mhead; - FILE * cti_fptr=fopen(filename.c_str(), "rb"); - if(cti_read_ECAT6_Main_header(cti_fptr, &mhead)!=EXIT_SUCCESS) - { - if (cti_fptr!=NULL) - fclose(cti_fptr); - error ("error reading main header in ECAT 6 file %s\n", filename.c_str()); - } - - warning("\nReading frame 1, gate 1, data 0, bed 0 from file %s\n", - filename.c_str()); - VoxelsOnCartesianGrid * tmp = - ECAT6_to_VoxelsOnCartesianGrid(/*frame_num, gate_num, data_num, bed_num*/1,1,0,0, - cti_fptr, mhead); - fclose(cti_fptr); - return tmp; + if (is_ECAT6_image_file(filename)) { + ECAT6_Main_header mhead; + FILE* cti_fptr = fopen(filename.c_str(), "rb"); + if (cti_read_ECAT6_Main_header(cti_fptr, &mhead) != EXIT_SUCCESS) { + if (cti_fptr != NULL) + fclose(cti_fptr); + error("error reading main header in ECAT 6 file %s\n", filename.c_str()); } - } -#endif // HAVE_LLN_MATRIX + warning("\nReading frame 1, gate 1, data 0, bed 0 from file %s\n", filename.c_str()); + VoxelsOnCartesianGrid* tmp = + ECAT6_to_VoxelsOnCartesianGrid(/*frame_num, gate_num, data_num, bed_num*/ 1, 1, 0, 0, cti_fptr, mhead); + fclose(cti_fptr); + return tmp; + } + } +# endif // HAVE_LLN_MATRIX - error("DiscretisedDensity::read_from_file: %s seems to be in an unsupported file format\n", - filename.c_str()); + error("DiscretisedDensity::read_from_file: %s seems to be in an unsupported file format\n", filename.c_str()); return 0; #endif - } - /****************************** instantiations *****************************/ #ifdef _MSC_VER // disable warning on pure virtuals which are not defined -#pragma warning(disable: 4661) +# pragma warning(disable : 4661) #endif -template class DiscretisedDensity<3,float>; +template class DiscretisedDensity<3, float>; END_NAMESPACE_STIR diff --git a/src/buildblock/DynamicDiscretisedDensity.cxx b/src/buildblock/DynamicDiscretisedDensity.cxx index a2f24a568d..4b89211f3d 100644 --- a/src/buildblock/DynamicDiscretisedDensity.cxx +++ b/src/buildblock/DynamicDiscretisedDensity.cxx @@ -7,7 +7,7 @@ \author Kris Thielemans \author Charalampos Tsoumpas \author Richard Brown - + */ /* Copyright (C) 2005- 2011, Hammersmith Imanet Ltd @@ -44,191 +44,165 @@ using std::string; START_NAMESPACE_STIR -DynamicDiscretisedDensity:: -DynamicDiscretisedDensity(const DynamicDiscretisedDensity& argument) -{ - (*this) = argument; -} +DynamicDiscretisedDensity::DynamicDiscretisedDensity(const DynamicDiscretisedDensity& argument) { (*this) = argument; } DynamicDiscretisedDensity& -DynamicDiscretisedDensity:: -operator=(const DynamicDiscretisedDensity& argument) -{ +DynamicDiscretisedDensity::operator=(const DynamicDiscretisedDensity& argument) { this->set_exam_info(argument.get_exam_info()); this->_densities.resize(argument._densities.size()); - for (unsigned int i=0; i_densities[i].reset(argument._densities[i]->clone()); this->_scanner_sptr = argument._scanner_sptr; -// this->exam_info_sptr->set_calibration_factor(argument.get_calibration_factor()); + // this->exam_info_sptr->set_calibration_factor(argument.get_calibration_factor()); this->_isotope_halflife = argument._isotope_halflife; this->_is_decay_corrected = argument._is_decay_corrected; return *this; } -void -DynamicDiscretisedDensity:: -set_density(const DiscretisedDensity<3,float>& density, - const unsigned int frame_num) -{ - // scan start should be the same - if (fabs(this->get_exam_info().start_time_in_secs_since_1970 - - density.get_exam_info().start_time_in_secs_since_1970) > .5) - error("DynamicDiscretisedDensity::set_density: Density should have same start_time_secs"); - // The added density should only contain 1 time frame - if(density.get_exam_info().time_frame_definitions.get_num_time_frames() != 1) - error("DynamicDiscretisedDensity::set_density: Density should contain 1 time frame"); - if(this->get_exam_info_sptr()->time_frame_definitions.get_num_time_frames() < frame_num) - error("DynamicDiscretisedDensity::set_density: Set DynamicDiscretisedDensity time frame definition before using set_density"); - - // Check the starts and ends match - double dyn_start = this->exam_info_sptr->time_frame_definitions.get_start_time(frame_num); - double dis_start = density.get_exam_info().time_frame_definitions.get_start_time(1); - double dyn_end = this->exam_info_sptr->time_frame_definitions.get_end_time(frame_num); - double dis_end = density.get_exam_info().time_frame_definitions.get_end_time(1); - - if (fabs(dyn_start - dis_start) > 1e-10) - error(boost::format("DynamicDiscretisedDensity::set_density: Time frame start should match (is %1% but expected %2%)") - % dis_start % dyn_start); - - if (fabs(dyn_end - dis_end) > 1e-10) - error(boost::format("DynamicDiscretisedDensity::set_density: Time frame end should match (is %1% but expected %2%)") - % dis_end % dyn_end); - - this->_densities.at(frame_num-1).reset(density.clone()); -} - -const std::vector > > & -DynamicDiscretisedDensity:: -get_densities() const -{ return this->_densities ; } - -const DiscretisedDensity<3,float> & -DynamicDiscretisedDensity:: -get_density(const unsigned int frame_num) const -{ return *this->_densities.at(frame_num-1) ; } - -DiscretisedDensity<3,float> & -DynamicDiscretisedDensity:: -get_density(const unsigned int frame_num) -{ return *this->_densities.at(frame_num-1) ; } - -const float -DynamicDiscretisedDensity:: -get_isotope_halflife() const -{ return this->_isotope_halflife; } - -const float -DynamicDiscretisedDensity:: -get_scanner_default_bin_size() const -{ +void +DynamicDiscretisedDensity::set_density(const DiscretisedDensity<3, float>& density, const unsigned int frame_num) { + // scan start should be the same + if (fabs(this->get_exam_info().start_time_in_secs_since_1970 - density.get_exam_info().start_time_in_secs_since_1970) > .5) + error("DynamicDiscretisedDensity::set_density: Density should have same start_time_secs"); + // The added density should only contain 1 time frame + if (density.get_exam_info().time_frame_definitions.get_num_time_frames() != 1) + error("DynamicDiscretisedDensity::set_density: Density should contain 1 time frame"); + if (this->get_exam_info_sptr()->time_frame_definitions.get_num_time_frames() < frame_num) + error("DynamicDiscretisedDensity::set_density: Set DynamicDiscretisedDensity time frame definition before using set_density"); + + // Check the starts and ends match + double dyn_start = this->exam_info_sptr->time_frame_definitions.get_start_time(frame_num); + double dis_start = density.get_exam_info().time_frame_definitions.get_start_time(1); + double dyn_end = this->exam_info_sptr->time_frame_definitions.get_end_time(frame_num); + double dis_end = density.get_exam_info().time_frame_definitions.get_end_time(1); + + if (fabs(dyn_start - dis_start) > 1e-10) + error(boost::format("DynamicDiscretisedDensity::set_density: Time frame start should match (is %1% but expected %2%)") % + dis_start % dyn_start); + + if (fabs(dyn_end - dis_end) > 1e-10) + error(boost::format("DynamicDiscretisedDensity::set_density: Time frame end should match (is %1% but expected %2%)") % + dis_end % dyn_end); + + this->_densities.at(frame_num - 1).reset(density.clone()); +} + +const std::vector>>& +DynamicDiscretisedDensity::get_densities() const { + return this->_densities; +} + +const DiscretisedDensity<3, float>& +DynamicDiscretisedDensity::get_density(const unsigned int frame_num) const { + return *this->_densities.at(frame_num - 1); +} + +DiscretisedDensity<3, float>& +DynamicDiscretisedDensity::get_density(const unsigned int frame_num) { + return *this->_densities.at(frame_num - 1); +} + +const float +DynamicDiscretisedDensity::get_isotope_halflife() const { + return this->_isotope_halflife; +} + +const float +DynamicDiscretisedDensity::get_scanner_default_bin_size() const { if (!this->_scanner_sptr) error("DynamicDiscretisedDensity::get_scanner_default_bin_size(): scanner not set"); return this->_scanner_sptr->get_default_bin_size(); } - float -DynamicDiscretisedDensity:: -get_calibration_factor() const -{ return this->exam_info_sptr->get_calibration_factor(); } - -const TimeFrameDefinitions & -DynamicDiscretisedDensity:: -get_time_frame_definitions() const -{ return this->get_exam_info().get_time_frame_definitions(); } +float +DynamicDiscretisedDensity::get_calibration_factor() const { + return this->exam_info_sptr->get_calibration_factor(); +} +const TimeFrameDefinitions& +DynamicDiscretisedDensity::get_time_frame_definitions() const { + return this->get_exam_info().get_time_frame_definitions(); +} const double -DynamicDiscretisedDensity:: -get_start_time_in_secs_since_1970() const -{ return this->get_exam_info().start_time_in_secs_since_1970; } +DynamicDiscretisedDensity::get_start_time_in_secs_since_1970() const { + return this->get_exam_info().start_time_in_secs_since_1970; +} DynamicDiscretisedDensity* -DynamicDiscretisedDensity:: -read_from_file(const string& filename) // The written image is read in respect to its center as origin!!! +DynamicDiscretisedDensity::read_from_file( + const string& filename) // The written image is read in respect to its center as origin!!! { - unique_ptr dyn_sptr - (stir::read_from_file(filename)); + unique_ptr dyn_sptr(stir::read_from_file(filename)); return dyn_sptr.release(); } -Succeeded -DynamicDiscretisedDensity:: -write_to_ecat7(const string& filename) const -{ +Succeeded +DynamicDiscretisedDensity::write_to_ecat7(const string& filename) const { #ifndef HAVE_LLN_MATRIX return Succeeded::no; #else Main_header mhead; - ecat::ecat7::make_ECAT7_main_header(mhead, *_scanner_sptr, filename, get_density(1) ); + ecat::ecat7::make_ECAT7_main_header(mhead, *_scanner_sptr, filename, get_density(1)); mhead.num_frames = get_time_frame_definitions().get_num_frames(); - mhead.acquisition_type = - mhead.num_frames>1 ? DynamicEmission : StaticEmission; - mhead.calibration_factor=_calibration_factor; - mhead.isotope_halflife=_isotope_halflife; + mhead.acquisition_type = mhead.num_frames > 1 ? DynamicEmission : StaticEmission; + mhead.calibration_factor = _calibration_factor; + mhead.isotope_halflife = _isotope_halflife; round_to(mhead.scan_start_time, floor(this->get_start_time_in_secs_since_1970())); - MatrixFile* mptr= matrix_create (filename.c_str(), MAT_CREATE, &mhead); - if (mptr == 0) - { - warning("DynamicDiscretisedDensity::write_to_ecat7 cannot write output file %s\n", filename.c_str()); + MatrixFile* mptr = matrix_create(filename.c_str(), MAT_CREATE, &mhead); + if (mptr == 0) { + warning("DynamicDiscretisedDensity::write_to_ecat7 cannot write output file %s\n", filename.c_str()); + return Succeeded::no; + } + for (unsigned int frame_num = 1; frame_num <= get_time_frame_definitions().get_num_frames(); ++frame_num) { + if (ecat::ecat7::DiscretisedDensity_to_ECAT7(mptr, get_density(frame_num), frame_num) == Succeeded::no) { + matrix_close(mptr); return Succeeded::no; } - for ( unsigned int frame_num = 1 ; frame_num<=get_time_frame_definitions().get_num_frames() ; ++frame_num ) - { - if (ecat::ecat7::DiscretisedDensity_to_ECAT7(mptr, - get_density(frame_num), - frame_num) - == Succeeded::no) - { - matrix_close(mptr); - return Succeeded::no; - } - } + } matrix_close(mptr); return Succeeded::yes; #endif // end of HAVE_LLN_MATRIX } - void DynamicDiscretisedDensity:: - calibrate_frames() const -{ - for ( unsigned int frame_num = 1 ; frame_num<=get_time_frame_definitions().get_num_frames() ; ++frame_num ) - { - *(_densities[frame_num-1])*=exam_info_sptr->get_calibration_factor(); - } +void +DynamicDiscretisedDensity::calibrate_frames() const { + for (unsigned int frame_num = 1; frame_num <= get_time_frame_definitions().get_num_frames(); ++frame_num) { + *(_densities[frame_num - 1]) *= exam_info_sptr->get_calibration_factor(); + } } -void DynamicDiscretisedDensity:: -set_calibration_factor(const float calibration_factor) -{ - auto new_exam_info_sptr = std::make_shared(this->get_exam_info()); - new_exam_info_sptr->set_calibration_factor(calibration_factor); - this->set_exam_info(*new_exam_info_sptr); +void +DynamicDiscretisedDensity::set_calibration_factor(const float calibration_factor) { + auto new_exam_info_sptr = std::make_shared(this->get_exam_info()); + new_exam_info_sptr->set_calibration_factor(calibration_factor); + this->set_exam_info(*new_exam_info_sptr); } -void DynamicDiscretisedDensity:: -set_if_decay_corrected(const bool is_decay_corrected) -{ this->_is_decay_corrected=is_decay_corrected; } +void +DynamicDiscretisedDensity::set_if_decay_corrected(const bool is_decay_corrected) { + this->_is_decay_corrected = is_decay_corrected; +} -void DynamicDiscretisedDensity:: -set_isotope_halflife(const float isotope_halflife) -{ _isotope_halflife=isotope_halflife; } +void +DynamicDiscretisedDensity::set_isotope_halflife(const float isotope_halflife) { + _isotope_halflife = isotope_halflife; +} - void DynamicDiscretisedDensity:: - decay_correct_frames() -{ - if (_is_decay_corrected==true) +void +DynamicDiscretisedDensity::decay_correct_frames() { + if (_is_decay_corrected == true) warning("DynamicDiscretisedDensity is already decay corrected"); - else - { - for ( unsigned int frame_num = 1 ; frame_num<=get_time_frame_definitions().get_num_frames() ; ++frame_num ) - { - *(_densities[frame_num-1])*= - static_cast(decay_correction_factor(_isotope_halflife,get_time_frame_definitions().get_start_time(frame_num),get_time_frame_definitions().get_end_time(frame_num))); - } - _is_decay_corrected=true; + else { + for (unsigned int frame_num = 1; frame_num <= get_time_frame_definitions().get_num_frames(); ++frame_num) { + *(_densities[frame_num - 1]) *= + static_cast(decay_correction_factor(_isotope_halflife, get_time_frame_definitions().get_start_time(frame_num), + get_time_frame_definitions().get_end_time(frame_num))); } + _is_decay_corrected = true; + } } END_NAMESPACE_STIR diff --git a/src/buildblock/DynamicProjData.cxx b/src/buildblock/DynamicProjData.cxx index 056df7be88..64f486896d 100644 --- a/src/buildblock/DynamicProjData.cxx +++ b/src/buildblock/DynamicProjData.cxx @@ -53,49 +53,41 @@ using std::istream; START_NAMESPACE_STIR // declaration of helper function -static -DynamicProjData* -read_interfile_DPDFS(const string& filename, - const std::ios::openmode open_mode = std::ios::in); +static DynamicProjData* read_interfile_DPDFS(const string& filename, const std::ios::openmode open_mode = std::ios::in); const double -DynamicProjData:: -get_start_time_in_secs_since_1970() const -{ return this->get_exam_info().start_time_in_secs_since_1970; } - -void -DynamicProjData:: -set_start_time_in_secs_since_1970(const double start_time) -{ +DynamicProjData::get_start_time_in_secs_since_1970() const { + return this->get_exam_info().start_time_in_secs_since_1970; +} + +void +DynamicProjData::set_start_time_in_secs_since_1970(const double start_time) { shared_ptr sptr = this->exam_info_sptr->create_shared_clone(); - sptr->start_time_in_secs_since_1970=start_time; + sptr->start_time_in_secs_since_1970 = start_time; this->exam_info_sptr = sptr; } void -DynamicProjData:: -set_time_frame_definitions(const TimeFrameDefinitions& time_frame_definitions) -{ +DynamicProjData::set_time_frame_definitions(const TimeFrameDefinitions& time_frame_definitions) { shared_ptr sptr = this->exam_info_sptr->create_shared_clone(); - sptr->time_frame_definitions=time_frame_definitions; + sptr->time_frame_definitions = time_frame_definitions; this->exam_info_sptr = sptr; } -const TimeFrameDefinitions& -DynamicProjData:: -get_time_frame_definitions() const -{ return this->exam_info_sptr->time_frame_definitions; } +const TimeFrameDefinitions& +DynamicProjData::get_time_frame_definitions() const { + return this->exam_info_sptr->time_frame_definitions; +} unique_ptr -DynamicProjData:: -read_from_file(const string& filename) // The written projection data is read in respect to its center as origin!!! +DynamicProjData::read_from_file( + const string& filename) // The written projection data is read in respect to its center as origin!!! { const FileSignature file_signature(filename); - const char * signature = file_signature.get_signature(); + const char* signature = file_signature.get_signature(); // Interfile - if (is_interfile_signature(signature)) - { + if (is_interfile_signature(signature)) { #ifndef NDEBUG info(boost::format("DynamicProjData::read_from_file trying to read %s as Interfile") % filename); #endif @@ -107,109 +99,89 @@ read_from_file(const string& filename) // The written projection data is read in } #ifdef HAVE_LLN_MATRIX - if (strncmp(signature, "MATRIX", 6) == 0) - { -#ifndef NDEBUG + if (strncmp(signature, "MATRIX", 6) == 0) { +# ifndef NDEBUG warning("DynamicProjData::read_from_file trying to read '%s' as ECAT7", filename.c_str()); -#endif +# endif USING_NAMESPACE_ECAT USING_NAMESPACE_ECAT7 - if (is_ECAT7_emission_file(filename)) - { + if (is_ECAT7_emission_file(filename)) { Main_header mhead; - if (read_ECAT7_main_header(mhead, filename) == Succeeded::no) - { - warning("DynamicProjData::read_from_file cannot read '%s' as ECAT7", filename.c_str()); - return unique_ptr(); - } + if (read_ECAT7_main_header(mhead, filename) == Succeeded::no) { + warning("DynamicProjData::read_from_file cannot read '%s' as ECAT7", filename.c_str()); + return unique_ptr(); + } shared_ptr exam_info_sptr(read_ECAT7_exam_info(filename)); unique_ptr dynamic_proj_data_ptr(new DynamicProjData(exam_info_sptr)); // we no longer have a _scanner_sptr member, so next lines are commented out - //dynamic_proj_data_ptr->_scanner_sptr.reset( + // dynamic_proj_data_ptr->_scanner_sptr.reset( // find_scanner_from_ECAT_system_type(mhead.system_type)); - const unsigned int num_frames = - static_cast(mhead.num_frames); - dynamic_proj_data_ptr->_proj_datas.resize(num_frames); - - for (unsigned int frame_num=1; frame_num <= num_frames; ++ frame_num) - { - dynamic_proj_data_ptr->_proj_datas[frame_num-1].reset( - ECAT7_to_PDFS(filename, - frame_num, - /*gate*/1, - /* data_num, bed_num, */ 0,0)); - } + const unsigned int num_frames = static_cast(mhead.num_frames); + dynamic_proj_data_ptr->_proj_datas.resize(num_frames); + + for (unsigned int frame_num = 1; frame_num <= num_frames; ++frame_num) { + dynamic_proj_data_ptr->_proj_datas[frame_num - 1].reset(ECAT7_to_PDFS(filename, frame_num, + /*gate*/ 1, + /* data_num, bed_num, */ 0, 0)); + } if (is_null_ptr(dynamic_proj_data_ptr->_proj_datas[0])) - error("DynamicProjData: No frame available\n"); + error("DynamicProjData: No frame available\n"); return dynamic_proj_data_ptr; + } else { + if (is_ECAT7_file(filename)) { + warning("DynamicProjData::read_from_file ECAT7 file '%s' should be projection data.", filename.c_str()); + return unique_ptr(); + } } - else - { - if (is_ECAT7_file(filename)) - { - warning("DynamicProjData::read_from_file ECAT7 file '%s' should be projection data.", filename.c_str()); - return unique_ptr(); - } - } + } else { + warning( + "DynamicProjData::read_from_file '%s' seems to correspond to ECAT projection data, but not ECAT7. I cannot read this.", + filename.c_str()); + return unique_ptr(); } - else - { - warning("DynamicProjData::read_from_file '%s' seems to correspond to ECAT projection data, but not ECAT7. I cannot read this.", filename.c_str()); - return unique_ptr(); - } #endif // end of HAVE_LLN_MATRIX if (strncmp(signature, "Multi", 5) == 0) { #ifndef NDEBUG - info(boost::format("DynamicProjData::read_from_file trying to read %s as a Multi file.") % filename); + info(boost::format("DynamicProjData::read_from_file trying to read %s as a Multi file.") % filename); #endif - unique_ptr multi_proj_data(MultipleProjData::read_from_file(filename)); - unique_ptr dynamic_proj_data(new DynamicProjData(*multi_proj_data)); + unique_ptr multi_proj_data(MultipleProjData::read_from_file(filename)); + unique_ptr dynamic_proj_data(new DynamicProjData(*multi_proj_data)); - return dynamic_proj_data; + return dynamic_proj_data; } - + // return a zero pointer if we get here warning(boost::format("DynamicProjData::read_from_file cannot read '%s'. Unsupported file format?") % filename); return unique_ptr(); } -Succeeded -DynamicProjData:: -write_to_ecat7(const string& filename) const -{ +Succeeded +DynamicProjData::write_to_ecat7(const string& filename) const { #ifndef HAVE_LLN_MATRIX return Succeeded::no; #else Main_header mhead; - ecat::ecat7::make_ECAT7_main_header(mhead, filename, - get_exam_info(), - *get_proj_data(1).get_proj_data_info_sptr() ); - - MatrixFile* mptr= matrix_create (filename.c_str(), MAT_CREATE, &mhead); - if (mptr == 0) - { - warning("DynamicProjData::write_to_ecat7 cannot write output file %s\n", filename.c_str()); + ecat::ecat7::make_ECAT7_main_header(mhead, filename, get_exam_info(), *get_proj_data(1).get_proj_data_info_sptr()); + + MatrixFile* mptr = matrix_create(filename.c_str(), MAT_CREATE, &mhead); + if (mptr == 0) { + warning("DynamicProjData::write_to_ecat7 cannot write output file %s\n", filename.c_str()); + return Succeeded::no; + } + for (unsigned int frame_num = 1; frame_num <= this->get_num_frames(); ++frame_num) { + if (ecat::ecat7::ProjData_to_ECAT7(mptr, get_proj_data(frame_num), frame_num) == Succeeded::no) { + matrix_close(mptr); return Succeeded::no; } - for ( unsigned int frame_num = 1 ; frame_num<=this->get_num_frames() ; ++frame_num ) - { - if (ecat::ecat7::ProjData_to_ECAT7(mptr, - get_proj_data(frame_num), - frame_num) - == Succeeded::no) - { - matrix_close(mptr); - return Succeeded::no; - } - } + } matrix_close(mptr); return Succeeded::yes; #endif // end of HAVE_LLN_MATRIX @@ -218,132 +190,100 @@ write_to_ecat7(const string& filename) const // local helper function to read the data from Interfile // It is largely a copy of read_interfile_PDFS (for a single time frame) // This needs to be merged of course. -static -DynamicProjData* -read_interfile_DPDFS(istream& input, - const string& directory_for_data, - const std::ios::openmode open_mode) -{ - - InterfilePDFSHeader hdr; +static DynamicProjData* +read_interfile_DPDFS(istream& input, const string& directory_for_data, const std::ios::openmode open_mode) { - if (!hdr.parse(input)) - { - return 0; // KT 10122001 do not call ask_parameters anymore - } + InterfilePDFSHeader hdr; + + if (!hdr.parse(input)) { + return 0; // KT 10122001 do not call ask_parameters anymore + } // KT 14/01/2000 added directory capability // prepend directory_for_data to the data_file_name from the header char full_data_file_name[max_filename_length]; strcpy(full_data_file_name, hdr.data_file_name.c_str()); - prepend_directory_name(full_data_file_name, directory_for_data.c_str()); - - for (unsigned int i=1; i data_in(new std::fstream(full_data_file_name, open_mode | std::ios::binary)); + if (!data_in->good()) { + warning("interfile parsing: error opening file %s", full_data_file_name); + return 0; + } + shared_ptr proj_data_info_sptr(hdr.data_info_sptr->create_shared_clone()); + unsigned long data_offset = hdr.data_offset_each_dataset[0]; + // offset in file between time frames + unsigned long data_offset_increment = 0UL; // we will find it below + DynamicProjData* dynamic_proj_data_ptr = new DynamicProjData(hdr.get_exam_info_sptr()); + if (is_null_ptr(dynamic_proj_data_ptr)) { + error(boost::format("DynamicProjData: error allocating memory for new object (for file \"%1%\")") % full_data_file_name); + } + dynamic_proj_data_ptr->resize(hdr.get_exam_info().time_frame_definitions.get_num_time_frames()); + + // now set all proj_data_sptrs + for (unsigned int frame_num = 1; frame_num <= dynamic_proj_data_ptr->get_num_frames(); ++frame_num) { + info(boost::format("Using data offset %1% for frame %2% in file %3%\n") % data_offset % frame_num % full_data_file_name); + shared_ptr current_exam_info_sptr(new ExamInfo(*hdr.get_exam_info_sptr())); + std::vector> frame_times; + frame_times.push_back(std::make_pair(hdr.get_exam_info().time_frame_definitions.get_start_time(frame_num), + hdr.get_exam_info().time_frame_definitions.get_end_time(frame_num))); + TimeFrameDefinitions time_frame_defs(frame_times); + current_exam_info_sptr->set_time_frame_definitions(time_frame_defs); + + shared_ptr proj_data_sptr(new ProjDataFromStream( + current_exam_info_sptr, proj_data_info_sptr, data_in, data_offset, hdr.segment_sequence, hdr.storage_order, + hdr.type_of_numbers, hdr.file_byte_order, static_cast(hdr.image_scaling_factors[0][0]))); + if (is_null_ptr(proj_data_sptr)) { + error(boost::format("DynamicProjData: error reading frame %1% from file '%2%'") % frame_num % full_data_file_name); + } + + dynamic_proj_data_ptr->set_proj_data_sptr(proj_data_sptr, frame_num); + + // TODO, move offset code to InterfileHeader.cxx + // find data offset increment + if (frame_num == 1) { + int num_sinos = 0; + for (int s = proj_data_info_sptr->get_min_segment_num(); s <= proj_data_info_sptr->get_max_segment_num(); ++s) { + num_sinos += proj_data_info_sptr->get_num_axial_poss(s); } - - assert(!is_null_ptr(hdr.data_info_sptr)); - - shared_ptr data_in(new std::fstream (full_data_file_name, open_mode | std::ios::binary)); - if (!data_in->good()) - { - warning("interfile parsing: error opening file %s",full_data_file_name); - return 0; - } - shared_ptr proj_data_info_sptr(hdr.data_info_sptr->create_shared_clone()); - unsigned long data_offset = hdr.data_offset_each_dataset[0]; - // offset in file between time frames - unsigned long data_offset_increment = 0UL; // we will find it below - DynamicProjData * dynamic_proj_data_ptr = new DynamicProjData(hdr.get_exam_info_sptr()); - if (is_null_ptr(dynamic_proj_data_ptr)) - { - error(boost::format("DynamicProjData: error allocating memory for new object (for file \"%1%\")") - % full_data_file_name); - } - dynamic_proj_data_ptr->resize(hdr.get_exam_info().time_frame_definitions.get_num_time_frames()); - - // now set all proj_data_sptrs - for (unsigned int frame_num=1; frame_num <= dynamic_proj_data_ptr->get_num_frames(); ++frame_num) - { - info(boost::format("Using data offset %1% for frame %2% in file %3%\n") % data_offset % frame_num % full_data_file_name); - shared_ptr current_exam_info_sptr(new ExamInfo(*hdr.get_exam_info_sptr())); - std::vector > frame_times; - frame_times.push_back(std::make_pair(hdr.get_exam_info().time_frame_definitions.get_start_time(frame_num), - hdr.get_exam_info().time_frame_definitions.get_end_time(frame_num))); - TimeFrameDefinitions time_frame_defs(frame_times); - current_exam_info_sptr->set_time_frame_definitions(time_frame_defs); - - shared_ptr - proj_data_sptr(new ProjDataFromStream(current_exam_info_sptr, - proj_data_info_sptr, - data_in, - data_offset, - hdr.segment_sequence, - hdr.storage_order, - hdr.type_of_numbers, - hdr.file_byte_order, - static_cast(hdr.image_scaling_factors[0][0]))); - if (is_null_ptr(proj_data_sptr)) - { - error(boost::format("DynamicProjData: error reading frame %1% from file '%2%'") - % frame_num % full_data_file_name); - } - - dynamic_proj_data_ptr->set_proj_data_sptr(proj_data_sptr, frame_num); - - // TODO, move offset code to InterfileHeader.cxx - // find data offset increment - if (frame_num==1) - { - int num_sinos = 0; - for (int s=proj_data_info_sptr->get_min_segment_num(); - s<=proj_data_info_sptr->get_max_segment_num(); - ++s) - { - num_sinos += proj_data_info_sptr->get_num_axial_poss(s); - } - data_offset_increment = - static_cast(num_sinos * - proj_data_info_sptr->get_num_tangential_poss() * proj_data_info_sptr->get_num_views() * - hdr.type_of_numbers.size_in_bytes()); - } - - // find offset of next frame - if (frame_num < dynamic_proj_data_ptr->get_num_frames()) - { - // note that hdr.data_offset uses zero-based indexing, so next line finds the offset for frame frame_num+1 - if (hdr.data_offset_each_dataset[frame_num]>0) - { - if (fabs(static_cast(data_offset) - hdr.data_offset_each_dataset[frame_num]) < data_offset_increment) - { - error(boost::format("data offset for frame %1% is too small. Difference in offsets needs to be at least %2%") - % (frame_num+1) % data_offset_increment); - } - data_offset = hdr.data_offset_each_dataset[frame_num]; - } - else - data_offset += data_offset_increment; - } - } // end loop over frames - - return dynamic_proj_data_ptr; + data_offset_increment = + static_cast(num_sinos * proj_data_info_sptr->get_num_tangential_poss() * + proj_data_info_sptr->get_num_views() * hdr.type_of_numbers.size_in_bytes()); + } + + // find offset of next frame + if (frame_num < dynamic_proj_data_ptr->get_num_frames()) { + // note that hdr.data_offset uses zero-based indexing, so next line finds the offset for frame frame_num+1 + if (hdr.data_offset_each_dataset[frame_num] > 0) { + if (fabs(static_cast(data_offset) - hdr.data_offset_each_dataset[frame_num]) < data_offset_increment) { + error(boost::format("data offset for frame %1% is too small. Difference in offsets needs to be at least %2%") % + (frame_num + 1) % data_offset_increment); + } + data_offset = hdr.data_offset_each_dataset[frame_num]; + } else + data_offset += data_offset_increment; + } + } // end loop over frames + + return dynamic_proj_data_ptr; } -static -DynamicProjData* -read_interfile_DPDFS(const string& filename, - const std::ios::openmode open_mode) -{ +static DynamicProjData* +read_interfile_DPDFS(const string& filename, const std::ios::openmode open_mode) { std::ifstream image_stream(filename.c_str(), open_mode); - if (!image_stream) - { - error(boost::format("DynamicProjData: couldn't open file '%s'") % filename); - } - + if (!image_stream) { + error(boost::format("DynamicProjData: couldn't open file '%s'") % filename); + } + return read_interfile_DPDFS(image_stream, get_directory_name(filename), open_mode); } diff --git a/src/buildblock/ExamData.cxx b/src/buildblock/ExamData.cxx index fb1389eb1b..628446cfe6 100644 --- a/src/buildblock/ExamData.cxx +++ b/src/buildblock/ExamData.cxx @@ -23,45 +23,32 @@ */ #include "stir/ExamData.h" - #include START_NAMESPACE_STIR -ExamData:: -ExamData(): - exam_info_sptr(new ExamInfo()) -{} - - -ExamData::ExamData(const shared_ptr &_this_exam) : - exam_info_sptr(_this_exam) -{} +ExamData::ExamData() : exam_info_sptr(new ExamInfo()) {} +ExamData::ExamData(const shared_ptr& _this_exam) : exam_info_sptr(_this_exam) {} -ExamData::~ExamData() -{} +ExamData::~ExamData() {} void -ExamData::set_exam_info(ExamInfo const& new_exam_info) -{ +ExamData::set_exam_info(ExamInfo const& new_exam_info) { this->exam_info_sptr.reset(new ExamInfo(new_exam_info)); } void -ExamData::set_exam_info_sptr(shared_ptr new_exam_info_sptr) -{ - this->exam_info_sptr=new_exam_info_sptr; +ExamData::set_exam_info_sptr(shared_ptr new_exam_info_sptr) { + this->exam_info_sptr = new_exam_info_sptr; } const ExamInfo& -ExamData::get_exam_info() const -{ +ExamData::get_exam_info() const { return *exam_info_sptr.get(); } shared_ptr -ExamData::get_exam_info_sptr() const -{ +ExamData::get_exam_info_sptr() const { return exam_info_sptr; } diff --git a/src/buildblock/ExamInfo.cxx b/src/buildblock/ExamInfo.cxx index 82db666a9f..a2853d2caa 100644 --- a/src/buildblock/ExamInfo.cxx +++ b/src/buildblock/ExamInfo.cxx @@ -25,16 +25,15 @@ #include "stir/date_time_functions.h" #ifdef BOOST_NO_STRINGSTREAM -#include +# include #else -#include +# include #endif START_NAMESPACE_STIR std::string -ExamInfo::parameter_info() const -{ +ExamInfo::parameter_info() const { #ifdef BOOST_NO_STRINGSTREAM // dangerous for out-of-range, but 'old-style' ostrstream seems to need this char str[30000]; @@ -47,29 +46,19 @@ ExamInfo::parameter_info() const s << "Radionuclide: " << this->radionuclide << '\n'; s << "Patient position: " << this->patient_position.get_position_as_string() << '\n'; s << "Scan start time: " << this->start_time_in_secs_since_1970 << '\n'; - if (this->start_time_in_secs_since_1970>0) - { - DateTimeStrings time = secs_since_Unix_epoch_to_Interfile_datetime(this->start_time_in_secs_since_1970); - s << " which is " << time.date << " " << time.time << '\n'; - } - if (this->time_frame_definitions.get_num_time_frames() == 1) - { - s << "Time frame start - end (duration), all in secs: " - << this->time_frame_definitions.get_start_time(1) - << " - " - << this->time_frame_definitions.get_end_time(1) - << " (" - << this->time_frame_definitions.get_duration(1) - << ")\n"; - } + if (this->start_time_in_secs_since_1970 > 0) { + DateTimeStrings time = secs_since_Unix_epoch_to_Interfile_datetime(this->start_time_in_secs_since_1970); + s << " which is " << time.date << " " << time.time << '\n'; + } + if (this->time_frame_definitions.get_num_time_frames() == 1) { + s << "Time frame start - end (duration), all in secs: " << this->time_frame_definitions.get_start_time(1) << " - " + << this->time_frame_definitions.get_end_time(1) << " (" << this->time_frame_definitions.get_duration(1) << ")\n"; + } s << "number of energy windows:=1\n" - << "energy window lower level[1] := " - << this->get_low_energy_thres() << '\n' - << "energy window upper level[1] := " - << this->get_high_energy_thres() << '\n'; - + << "energy window lower level[1] := " << this->get_low_energy_thres() << '\n' + << "energy window upper level[1] := " << this->get_high_energy_thres() << '\n'; + return s.str(); } - END_NAMESPACE_STIR diff --git a/src/buildblock/FilePath.cxx b/src/buildblock/FilePath.cxx index a6cc07d577..4737b609d4 100644 --- a/src/buildblock/FilePath.cxx +++ b/src/buildblock/FilePath.cxx @@ -43,359 +43,321 @@ #include #if defined(__OS_WIN__) - #include - #include // required for stat.h - #include +# include +# include // required for stat.h +# include #else - #include +# include #endif #include START_NAMESPACE_STIR -FilePath::FilePath(){ - initSeparator(); -} +FilePath::FilePath() { initSeparator(); } -FilePath::FilePath(const std::string &__str, bool _run_checks) -{ - if(__str.size() == 0) - error(boost::format("FilePath: Cannot initialise empty path.")); +FilePath::FilePath(const std::string& __str, bool _run_checks) { + if (__str.size() == 0) + error(boost::format("FilePath: Cannot initialise empty path.")); - my_string = __str; + my_string = __str; - initSeparator(); + initSeparator(); - run_checks = _run_checks; + run_checks = _run_checks; - // Checks - checks(); + // Checks + checks(); } -bool FilePath::is_directory() const -{ +bool +FilePath::is_directory() const { #if defined(__OS_WIN__) - DWORD dwAttrib = GetFileAttributes(my_string.c_str()); + DWORD dwAttrib = GetFileAttributes(my_string.c_str()); - return (dwAttrib != INVALID_FILE_ATTRIBUTES && - dwAttrib & FILE_ATTRIBUTE_DIRECTORY); + return (dwAttrib != INVALID_FILE_ATTRIBUTES && dwAttrib & FILE_ATTRIBUTE_DIRECTORY); #else - struct stat info; + struct stat info; - if( stat( my_string.c_str(), &info ) != 0 ) - error(boost::format("FilePath: Cannot access %1%.")%my_string); - else if( info.st_mode & S_IFDIR ) - return true; + if (stat(my_string.c_str(), &info) != 0) + error(boost::format("FilePath: Cannot access %1%.") % my_string); + else if (info.st_mode & S_IFDIR) + return true; #endif - return false; + return false; } - -bool FilePath::is_regular_file() const -{ +bool +FilePath::is_regular_file() const { #if defined(__OS_WIN__) - DWORD dwAttrib = GetFileAttributes(my_string.c_str()); + DWORD dwAttrib = GetFileAttributes(my_string.c_str()); - return (dwAttrib != INVALID_FILE_ATTRIBUTES && - (dwAttrib & FILE_ATTRIBUTE_NORMAL)); + return (dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_NORMAL)); #else - struct stat info; + struct stat info; - if( stat( my_string.c_str(), &info ) != 0 ) - error(boost::format("FilePath: Cannot access %1%.")%my_string); - else if( info.st_mode & S_IFREG ) - return true; + if (stat(my_string.c_str(), &info) != 0) + error(boost::format("FilePath: Cannot access %1%.") % my_string); + else if (info.st_mode & S_IFREG) + return true; #endif - return false; + return false; } -bool FilePath::is_writable() const -{ +bool +FilePath::is_writable() const { #if defined(__OS_WIN__) - DWORD dwAttrib = GetFileAttributes(my_string.c_str()); + DWORD dwAttrib = GetFileAttributes(my_string.c_str()); - return (dwAttrib != INVALID_FILE_ATTRIBUTES); -#else - if( access(my_string.c_str(), 0) == 0 ) - return true; - else - return false; + return (dwAttrib != INVALID_FILE_ATTRIBUTES); +#else + if (access(my_string.c_str(), 0) == 0) + return true; + else + return false; #endif } -bool FilePath::exists(const std::string& s) -{ +bool +FilePath::exists(const std::string& s) { #if defined(__OS_WIN__) - DWORD dwAttrib = GetFileAttributes(s.c_str()); + DWORD dwAttrib = GetFileAttributes(s.c_str()); - return (dwAttrib != INVALID_FILE_ATTRIBUTES); + return (dwAttrib != INVALID_FILE_ATTRIBUTES); #else - struct stat info; - - if (s.size()>0) - { - if (stat(s.c_str(), &info) != 0) - return false; - else - return true; - } - return false; + struct stat info; + + if (s.size() > 0) { + if (stat(s.c_str(), &info) != 0) + return false; + else + return true; + } + return false; #endif } -FilePath FilePath::append(const FilePath &p) -{ - return append(p.get_path()); +FilePath +FilePath::append(const FilePath& p) { + return append(p.get_path()); } -FilePath FilePath::append(const std::string &p) -{ - // Check permissions - if (!is_writable()) - error(boost::format("FilePath: %1% is not writable.")%my_string); - - std::string new_path = my_string; - - //Check if this a directory or it contains a filename, too - if(!is_directory()) - { - if(!is_regular_file()) - { - error(boost::format("FilePath: Cannot find a directory in %1%.")%my_string); - } - else - { - new_path = get_path(); - } +FilePath +FilePath::append(const std::string& p) { + // Check permissions + if (!is_writable()) + error(boost::format("FilePath: %1% is not writable.") % my_string); + + std::string new_path = my_string; + + // Check if this a directory or it contains a filename, too + if (!is_directory()) { + if (!is_regular_file()) { + error(boost::format("FilePath: Cannot find a directory in %1%.") % my_string); + } else { + new_path = get_path(); } + } + + // Try to accomondate multiple sub paths + // Find if string p has more than one levels and store them in a vector. + std::vector v = split(p, separator.c_str()); + + // Run over the vector creating the subfolders recrusively. + for (unsigned int i = 0; i < v.size(); i++) { + new_path = merge(new_path, v.at(i)); + FilePath::append_separator(new_path); - // Try to accomondate multiple sub paths - // Find if string p has more than one levels and store them in a vector. - std::vector v = split(p, separator.c_str()); - - // Run over the vector creating the subfolders recrusively. - for (unsigned int i = 0; i < v.size(); i++) - { - new_path = merge(new_path, v.at(i)); - FilePath::append_separator(new_path); - - // if current level already exists move to the next. - if (FilePath::exists(new_path) == true) - { - warning(boost::format("FilePath: Path %1% already exists.")%new_path); - continue; - } - int nError; + // if current level already exists move to the next. + if (FilePath::exists(new_path) == true) { + warning(boost::format("FilePath: Path %1% already exists.") % new_path); + continue; + } + int nError; #if defined(__OS_WIN__) - nError = _mkdir(new_path.c_str()); + nError = _mkdir(new_path.c_str()); #else - nError = mkdir(new_path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + nError = mkdir(new_path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); #endif - if (-1 == nError) - { - error("FilePath: Error creating directory!"); - } + if (-1 == nError) { + error("FilePath: Error creating directory!"); } - return FilePath(new_path); + } + return FilePath(new_path); } void -FilePath::add_extension(const std::string &e) -{ - std::string::size_type pos = find_pos_of_extension(); - if (pos == std::string::npos) - my_string += e; +FilePath::add_extension(const std::string& e) { + std::string::size_type pos = find_pos_of_extension(); + if (pos == std::string::npos) + my_string += e; } void -FilePath::replace_extension(const std::string& e) -{ - std::string::size_type pos = find_pos_of_extension(); - if (pos != std::string::npos) - { - my_string.erase(pos); - my_string += e; - } +FilePath::replace_extension(const std::string& e) { + std::string::size_type pos = find_pos_of_extension(); + if (pos != std::string::npos) { + my_string.erase(pos); + my_string += e; + } } - std::string -FilePath::get_path() const -{ - std::size_t i = my_string.rfind(separator, my_string.length()); - if (i != std::string::npos) - { - return(my_string.substr(0, i+1)); - } +FilePath::get_path() const { + std::size_t i = my_string.rfind(separator, my_string.length()); + if (i != std::string::npos) { + return (my_string.substr(0, i + 1)); + } - return(my_string); + return (my_string); } std::string -FilePath::get_path_only() const -{ - std::size_t i = my_string.rfind(separator, my_string.length()); - if (i != std::string::npos) - { - return(my_string.substr(0, i+1)); - } - - std::string empty; - return empty; +FilePath::get_path_only() const { + std::size_t i = my_string.rfind(separator, my_string.length()); + if (i != std::string::npos) { + return (my_string.substr(0, i + 1)); + } + + std::string empty; + return empty; } - std::string -FilePath::get_filename() const -{ - std::size_t i = 0; +FilePath::get_filename() const { + std::size_t i = 0; #if defined(__OS_WIN__) - i = my_string.rfind(separator, my_string.length()); - if (i == std::string::npos) - i = my_string.rfind('/', my_string.length()); - if (i == std::string::npos) - i = my_string.rfind(':', my_string.length()); + i = my_string.rfind(separator, my_string.length()); + if (i == std::string::npos) + i = my_string.rfind('/', my_string.length()); + if (i == std::string::npos) + i = my_string.rfind(':', my_string.length()); #else - i = my_string.rfind(separator, my_string.length()); + i = my_string.rfind(separator, my_string.length()); #endif - if (i != std::string::npos) - { - return(my_string.substr(i+1, my_string.length() - i)); - } - return(my_string); + if (i != std::string::npos) { + return (my_string.substr(i + 1, my_string.length() - i)); + } + return (my_string); } std::string -FilePath::get_filename_no_extension() const -{ - std::string ret = get_filename(); - - std::size_t i = find_pos_of_extension(); - if (i != std::string::npos) - { - return(ret.substr(0, i)); - } - return(""); - +FilePath::get_filename_no_extension() const { + std::string ret = get_filename(); + + std::size_t i = find_pos_of_extension(); + if (i != std::string::npos) { + return (ret.substr(0, i)); + } + return (""); } std::string -FilePath::get_extension() const -{ - std::size_t i = find_pos_of_extension(); - if (i != std::string::npos) - { - return(my_string.substr(i+1, my_string.length() - i)); - } - return(""); +FilePath::get_extension() const { + std::size_t i = find_pos_of_extension(); + if (i != std::string::npos) { + return (my_string.substr(i + 1, my_string.length() - i)); + } + return (""); } std::string -FilePath::get_as_string() const -{ - return my_string; +FilePath::get_as_string() const { + return my_string; } -void FilePath::checks() const -{ - if (!run_checks) - return; +void +FilePath::checks() const { + if (!run_checks) + return; #if defined(__OS_WIN__) - DWORD dwAttrib = GetFileAttributes(my_string.c_str()); + DWORD dwAttrib = GetFileAttributes(my_string.c_str()); - if (dwAttrib != INVALID_FILE_ATTRIBUTES && - !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)) - { - error(boost::format("FilePath: File %1% is neither a directory nor a file")%my_string); - } + if (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)) { + error(boost::format("FilePath: File %1% is neither a directory nor a file") % my_string); + } #else - struct stat s; - if( stat(my_string.c_str(),&s) == 0 ) - { - if ((s.st_mode & S_IFDIR ) && - ( s.st_mode & S_IFREG ) ) - { - error(boost::format("FilePath: File %1% is neither a directory nor a file")%my_string); - } - } - else - { - error(boost::format("FilePath: Maybe %1% does not exist?")%my_string); + struct stat s; + if (stat(my_string.c_str(), &s) == 0) { + if ((s.st_mode & S_IFDIR) && (s.st_mode & S_IFREG)) { + error(boost::format("FilePath: File %1% is neither a directory nor a file") % my_string); } + } else { + error(boost::format("FilePath: Maybe %1% does not exist?") % my_string); + } #endif } -//On Windows there's GetCurrentDirectory -std::string FilePath::get_current_working_directory() -{ - char buffer[max_filename_length]; - char *ptr = getcwd(buffer, sizeof(buffer)); - std::string s_cwd; - if (ptr) - s_cwd = ptr; - - return s_cwd; +// On Windows there's GetCurrentDirectory +std::string +FilePath::get_current_working_directory() { + char buffer[max_filename_length]; + char* ptr = getcwd(buffer, sizeof(buffer)); + std::string s_cwd; + if (ptr) + s_cwd = ptr; + + return s_cwd; } -const std::vector FilePath::split(const std::string& s, const char* c) -{ - std::string buff = ""; - std::vector v; +const std::vector +FilePath::split(const std::string& s, const char* c) { + std::string buff = ""; + std::vector v; - if (strlen(c) == 0) - c = separator.c_str(); + if (strlen(c) == 0) + c = separator.c_str(); - for(unsigned int i = 0; i < s.size(); i++) - { - char n = s.at(i); + for (unsigned int i = 0; i < s.size(); i++) { + char n = s.at(i); - if(n != *c) buff+=n; else - if(n == *c && buff != "") { v.push_back(buff); buff = ""; } + if (n != *c) + buff += n; + else if (n == *c && buff != "") { + v.push_back(buff); + buff = ""; } - if(buff != "") v.push_back(buff); + } + if (buff != "") + v.push_back(buff); - return v; + return v; } std::string::size_type -FilePath::find_pos_of_filename() const -{ - std::string::size_type pos; +FilePath::find_pos_of_filename() const { + std::string::size_type pos; #if defined(__OS_VAX__) - pos = my_string.find_last_of( ']'); - if (pos==std::string::npos) - pos = my_string.find_last_of(separator); + pos = my_string.find_last_of(']'); + if (pos == std::string::npos) + pos = my_string.find_last_of(separator); #elif defined(__OS_WIN__) - pos = my_string.find_last_of( '\\'); - if (pos==std::string::npos) - pos = my_string.find_last_of( '/'); - if (pos==std::string::npos) - pos = my_string.find_last_of( ':'); + pos = my_string.find_last_of('\\'); + if (pos == std::string::npos) + pos = my_string.find_last_of('/'); + if (pos == std::string::npos) + pos = my_string.find_last_of(':'); #else - pos = my_string.find_last_of(separator); + pos = my_string.find_last_of(separator); #endif - if (pos != std::string::npos) - return pos+1; - else - return 0; + if (pos != std::string::npos) + return pos + 1; + else + return 0; } std::string::size_type -FilePath::find_pos_of_extension() const -{ - std::string::size_type pos_of_dot = - my_string.find_last_of('.'); - std::string::size_type pos_of_filename = - find_pos_of_filename(); +FilePath::find_pos_of_extension() const { + std::string::size_type pos_of_dot = my_string.find_last_of('.'); + std::string::size_type pos_of_filename = find_pos_of_filename(); if (pos_of_dot >= pos_of_filename) return pos_of_dot; @@ -403,133 +365,119 @@ FilePath::find_pos_of_extension() const return std::string::npos; } -void FilePath::append_separator(std::string& s) -{ +void +FilePath::append_separator(std::string& s) { #if defined(__OS_VAX__) - s += ":"; + s += ":"; #elif defined(__OS_WIN__) - s += "\\"; + s += "\\"; #elif defined(__OS_MAC__) - s += ":" ; + s += ":"; #else // defined(__OS_UNIX__) - s += "/" ; + s += "/"; #endif } bool -FilePath::is_absolute(const std::string& _filename_with_directory) -{ - const char* filename_with_directory = _filename_with_directory.c_str(); +FilePath::is_absolute(const std::string& _filename_with_directory) { + const char* filename_with_directory = _filename_with_directory.c_str(); #if defined(__OS_VAX__) - // relative names either contain no '[', or have '[.' - const char * const ptr = strchr(filename_with_directory,'['); - if (ptr==NULL) - return false; - else - return *(ptr+1) != '.'; + // relative names either contain no '[', or have '[.' + const char* const ptr = strchr(filename_with_directory, '['); + if (ptr == NULL) + return false; + else + return *(ptr + 1) != '.'; #elif defined(__OS_WIN__) - // relative names do not start with '\' or '?:\' - if (filename_with_directory[0] == '\\' || - filename_with_directory[0] == '/') - return true; - else - return (strlen(filename_with_directory)>3 && - filename_with_directory[1] == ':' && - (filename_with_directory[2] == '\\' || - filename_with_directory[2] == '/') - ); + // relative names do not start with '\' or '?:\' + if (filename_with_directory[0] == '\\' || filename_with_directory[0] == '/') + return true; + else + return (strlen(filename_with_directory) > 3 && filename_with_directory[1] == ':' && + (filename_with_directory[2] == '\\' || filename_with_directory[2] == '/')); #elif defined(__OS_MAC__) - // relative names either have no ':' or do not start with ':' - const char * const ptr = strchr(filename_with_directory,':'); - if (ptr == NULL) - return false; - else - return ptr != filename_with_directory; + // relative names either have no ':' or do not start with ':' + const char* const ptr = strchr(filename_with_directory, ':'); + if (ptr == NULL) + return false; + else + return ptr != filename_with_directory; #else // defined(__OS_UNIX__) - // absolute names start with '/' - return filename_with_directory[0] == '/'; + // absolute names start with '/' + return filename_with_directory[0] == '/'; #endif } -void FilePath::prepend_directory_name(const std::string &p) -{ +void +FilePath::prepend_directory_name(const std::string& p) { - if (FilePath::is_absolute(my_string) || - p.size() == 0) - return; + if (FilePath::is_absolute(my_string) || p.size() == 0) + return; - std::string new_name; + std::string new_name; #if defined(__OS_VAX__) - // relative names either contain no '[', or have '[.' - if (my_string[0] != '[' || - p[p.length()-1] != ']') - else - { - // peel of the ][ pair - p.erase[p.length()-1]; + // relative names either contain no '[', or have '[.' + if (my_string[0] != '[' || p[p.length() - 1] != ']') + else { + // peel of the ][ pair + p.erase[p.length() - 1]; } - new_name = p + separator + my_string; + new_name = p + separator + my_string; #elif defined(__OS_WIN__) - new_name = merge(p, my_string); + new_name = merge(p, my_string); #elif defined(__OS_MAC__) - // relative names either have no ':' or do not start with ':' - // append : if necessary - if (p[p.length()-1] != ':') - p.push_back(":"); - // do not copy starting ':' of filename - if (my_string[0] == ':') - my_string.erase(0); - new_name = p + separator + my_string; + // relative names either have no ':' or do not start with ':' + // append : if necessary + if (p[p.length() - 1] != ':') + p.push_back(":"); + // do not copy starting ':' of filename + if (my_string[0] == ':') + my_string.erase(0); + new_name = p + separator + my_string; #else // defined(__OS_UNIX__) - // append / if necessary - new_name = merge(p, my_string); + // append / if necessary + new_name = merge(p, my_string); #endif - my_string = new_name; + my_string = new_name; } -std::string FilePath::merge(const std::string &first, const std::string &sec) -{ - std::string sep = separator; +std::string +FilePath::merge(const std::string& first, const std::string& sec) { + std::string sep = separator; #if defined(__OS_WIN__) - //Check for the appropriate separator. - // Again, in utilies when windows all separators are checked. - if (first[first.length() - 1] != *sep.c_str()) - { - if (first[first.length() - 1] == '/') - sep = '/'; - if (first[first.length() - 1] == ':') - sep = ':'; - } - - if (sec[0] != *sep.c_str()) - { - if (sec[0] == '/') - sep = '/'; - if (sec[0] == ':') - sep = ':'; - } + // Check for the appropriate separator. + // Again, in utilies when windows all separators are checked. + if (first[first.length() - 1] != *sep.c_str()) { + if (first[first.length() - 1] == '/') + sep = '/'; + if (first[first.length() - 1] == ':') + sep = ':'; + } + + if (sec[0] != *sep.c_str()) { + if (sec[0] == '/') + sep = '/'; + if (sec[0] == ':') + sep = ':'; + } #endif - // Just append a separator - if (sec.size() == 0) - return first + sep; + // Just append a separator + if (sec.size() == 0) + return first + sep; - if (first[first.length()-1] == *sep.c_str() && sec[0] == *sep.c_str()) - { - return first.substr(0, first.length()-1) + sec; - } - else if ((first[first.length()-1] == *sep.c_str() && sec[0] != *sep.c_str()) || - (first[first.length()-1] != *sep.c_str() && sec[0] == *sep.c_str())) - { - return first + sec; - } - else /*( (first.back() != separator + if (first[first.length() - 1] == *sep.c_str() && sec[0] == *sep.c_str()) { + return first.substr(0, first.length() - 1) + sec; + } else if ((first[first.length() - 1] == *sep.c_str() && sec[0] != *sep.c_str()) || + (first[first.length() - 1] != *sep.c_str() && sec[0] == *sep.c_str())) { + return first + sec; + } else /*( (first.back() != separator && sec.front() != separator))*/ - { - return first + sep + sec; - } + { + return first + sep + sec; + } } END_NAMESPACE_STIR diff --git a/src/buildblock/GatedDiscretisedDensity.cxx b/src/buildblock/GatedDiscretisedDensity.cxx index 87f1aad230..0541a34afd 100644 --- a/src/buildblock/GatedDiscretisedDensity.cxx +++ b/src/buildblock/GatedDiscretisedDensity.cxx @@ -3,26 +3,26 @@ Copyright (C) 2005- 2009, Hammersmith Imanet Ltd Copyright (C) 2009- 2013, King's College London This file is part of STIR. - + This file is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.3 of the License, or (at your option) any later version. - + This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - + See STIR/LICENSE.txt for details - */ + */ /*! \file \ingroup buildblock \brief Implementation of class stir::GatedDiscretisedDensity \author Charalampos Tsoumpas \author Kris Thielemans - + */ #include "stir/GatedDiscretisedDensity.h" @@ -43,164 +43,143 @@ using std::string; START_NAMESPACE_STIR -GatedDiscretisedDensity:: -GatedDiscretisedDensity(const GatedDiscretisedDensity& argument) -{ - (*this) = argument; -} +GatedDiscretisedDensity::GatedDiscretisedDensity(const GatedDiscretisedDensity& argument) { (*this) = argument; } GatedDiscretisedDensity& -GatedDiscretisedDensity:: -operator=(const GatedDiscretisedDensity& argument) -{ +GatedDiscretisedDensity::operator=(const GatedDiscretisedDensity& argument) { this->_time_gate_definitions = argument._time_gate_definitions; this->_densities.resize(argument._densities.size()); - for (unsigned int i=0; i_densities[i].reset(argument._densities[i]->clone()); return *this; } -GatedDiscretisedDensity:: -GatedDiscretisedDensity(const string& filename) -{ - const string gdef_filename=filename+".gdef"; +GatedDiscretisedDensity::GatedDiscretisedDensity(const string& filename) { + const string gdef_filename = filename + ".gdef"; std::cout << "GatedDiscretisedDensity: Reading gate definitions " << gdef_filename.c_str() << std::endl; this->_time_gate_definitions.read_gdef_file(gdef_filename); this->_densities.resize(this->_time_gate_definitions.get_num_gates()); - for ( unsigned int num = 1 ; num<=(this->_time_gate_definitions).get_num_gates() ; ++num ) - { - std::stringstream gate_num_stream; - gate_num_stream << this->_time_gate_definitions.get_gate_num(num); - const string input_filename=filename+"_g"+gate_num_stream.str()+".hv"; - // const shared_ptr > read_sptr = new VoxelsOnCartesianGrid ; - std::cout << "GatedDiscretisedDensity: Reading gate file: " << input_filename.c_str() << std::endl; - this->_densities[num-1].reset(DiscretisedDensity<3,float>::read_from_file(input_filename)); - } + for (unsigned int num = 1; num <= (this->_time_gate_definitions).get_num_gates(); ++num) { + std::stringstream gate_num_stream; + gate_num_stream << this->_time_gate_definitions.get_gate_num(num); + const string input_filename = filename + "_g" + gate_num_stream.str() + ".hv"; + // const shared_ptr > read_sptr = new VoxelsOnCartesianGrid ; + std::cout << "GatedDiscretisedDensity: Reading gate file: " << input_filename.c_str() << std::endl; + this->_densities[num - 1].reset(DiscretisedDensity<3, float>::read_from_file(input_filename)); + } } -GatedDiscretisedDensity:: -GatedDiscretisedDensity(const shared_ptr >& density_sptr, - const unsigned int num_gates) -{ +GatedDiscretisedDensity::GatedDiscretisedDensity(const shared_ptr>& density_sptr, + const unsigned int num_gates) { this->_densities.resize(num_gates); - for ( unsigned int num = 1 ; num<=num_gates; ++num ) - this->_densities[num-1].reset(density_sptr->get_empty_discretised_density()); + for (unsigned int num = 1; num <= num_gates; ++num) + this->_densities[num - 1].reset(density_sptr->get_empty_discretised_density()); } +void +GatedDiscretisedDensity::set_density_sptr(const shared_ptr>& density_sptr, + const unsigned int gate_num) { + this->_densities[gate_num - 1] = density_sptr; +} -void -GatedDiscretisedDensity:: -set_density_sptr(const shared_ptr >& density_sptr, - const unsigned int gate_num) -{ this->_densities[gate_num-1]=density_sptr; } - -const std::vector > > & -GatedDiscretisedDensity:: -get_densities() const -{ return this->_densities ; } +const std::vector>>& +GatedDiscretisedDensity::get_densities() const { + return this->_densities; +} -const DiscretisedDensity<3,float> & -GatedDiscretisedDensity:: -get_density(const unsigned int gate_num) const -{ return *this->_densities[gate_num-1] ; } +const DiscretisedDensity<3, float>& +GatedDiscretisedDensity::get_density(const unsigned int gate_num) const { + return *this->_densities[gate_num - 1]; +} -DiscretisedDensity<3,float> & -GatedDiscretisedDensity:: -get_density(const unsigned int gate_num) -{ return *this->_densities[gate_num-1] ; } +DiscretisedDensity<3, float>& +GatedDiscretisedDensity::get_density(const unsigned int gate_num) { + return *this->_densities[gate_num - 1]; +} -const TimeGateDefinitions & -GatedDiscretisedDensity:: -get_time_gate_definitions() const -{ return this->_time_gate_definitions; } +const TimeGateDefinitions& +GatedDiscretisedDensity::get_time_gate_definitions() const { + return this->_time_gate_definitions; +} -void GatedDiscretisedDensity:: -fill_with_zero() -{ - for (unsigned int gate_num=0; gate_num_time_gate_definitions.get_num_gates(); ++gate_num) +void +GatedDiscretisedDensity::fill_with_zero() { + for (unsigned int gate_num = 0; gate_num < this->_time_gate_definitions.get_num_gates(); ++gate_num) this->_densities[gate_num].reset((this->_densities[gate_num])->get_empty_discretised_density()); } GatedDiscretisedDensity* -GatedDiscretisedDensity:: -read_from_files(const string& filename) // The written image is read in respect to its center as origin!!! +GatedDiscretisedDensity::read_from_files( + const string& filename) // The written image is read in respect to its center as origin!!! { - const string gdef_filename=filename+".gdef"; + const string gdef_filename = filename + ".gdef"; std::cout << "GatedDiscretisedDensity: Reading gate definitions " << gdef_filename.c_str() << std::endl; - GatedDiscretisedDensity * gated_image_ptr = 0; + GatedDiscretisedDensity* gated_image_ptr = 0; gated_image_ptr = new GatedDiscretisedDensity; gated_image_ptr->_time_gate_definitions.read_gdef_file(gdef_filename); gated_image_ptr->_densities.resize(gated_image_ptr->_time_gate_definitions.get_num_gates()); - for ( unsigned int num = 1 ; num<=(gated_image_ptr->_time_gate_definitions).get_num_gates() ; ++num ) - { - std::stringstream gate_num_stream; - gate_num_stream << gated_image_ptr->_time_gate_definitions.get_gate_num(num); - const string input_filename=filename+"_g"+gate_num_stream.str()+".hv"; - const shared_ptr > read_sptr(new VoxelsOnCartesianGrid ); - std::cout << "GatedDiscretisedDensity: Reading gate file: " << input_filename.c_str() << std::endl; - gated_image_ptr->_densities[num-1].reset(DiscretisedDensity<3,float>::read_from_file(input_filename)); - } + for (unsigned int num = 1; num <= (gated_image_ptr->_time_gate_definitions).get_num_gates(); ++num) { + std::stringstream gate_num_stream; + gate_num_stream << gated_image_ptr->_time_gate_definitions.get_gate_num(num); + const string input_filename = filename + "_g" + gate_num_stream.str() + ".hv"; + const shared_ptr> read_sptr(new VoxelsOnCartesianGrid); + std::cout << "GatedDiscretisedDensity: Reading gate file: " << input_filename.c_str() << std::endl; + gated_image_ptr->_densities[num - 1].reset(DiscretisedDensity<3, float>::read_from_file(input_filename)); + } return gated_image_ptr; } GatedDiscretisedDensity* -GatedDiscretisedDensity:: -read_from_files(const string& filename,const string& suffix) // The written image is read in respect to its center as origin!!! +GatedDiscretisedDensity::read_from_files(const string& filename, + const string& suffix) // The written image is read in respect to its center as origin!!! { - const string gdef_filename=filename+".gdef"; + const string gdef_filename = filename + ".gdef"; std::cout << "GatedDiscretisedDensity: Reading gate definitions " << gdef_filename.c_str() << std::endl; - GatedDiscretisedDensity * gated_image_ptr = 0; + GatedDiscretisedDensity* gated_image_ptr = 0; gated_image_ptr = new GatedDiscretisedDensity; gated_image_ptr->_time_gate_definitions.read_gdef_file(gdef_filename); gated_image_ptr->_densities.resize(gated_image_ptr->_time_gate_definitions.get_num_gates()); - for ( unsigned int num = 1 ; num<=(gated_image_ptr->_time_gate_definitions).get_num_gates() ; ++num ) - { - std::stringstream gate_num_stream; - gate_num_stream << gated_image_ptr->_time_gate_definitions.get_gate_num(num); - const string input_filename=filename+"_g"+gate_num_stream.str()+suffix+".hv"; - // const shared_ptr > read_sptr = new VoxelsOnCartesianGrid ; - std::cout << "GatedDiscretisedDensity: Reading gate file: " << input_filename.c_str() << std::endl; - gated_image_ptr->_densities[num-1].reset(DiscretisedDensity<3,float>::read_from_file(input_filename)); - } + for (unsigned int num = 1; num <= (gated_image_ptr->_time_gate_definitions).get_num_gates(); ++num) { + std::stringstream gate_num_stream; + gate_num_stream << gated_image_ptr->_time_gate_definitions.get_gate_num(num); + const string input_filename = filename + "_g" + gate_num_stream.str() + suffix + ".hv"; + // const shared_ptr > read_sptr = new VoxelsOnCartesianGrid ; + std::cout << "GatedDiscretisedDensity: Reading gate file: " << input_filename.c_str() << std::endl; + gated_image_ptr->_densities[num - 1].reset(DiscretisedDensity<3, float>::read_from_file(input_filename)); + } return gated_image_ptr; } -Succeeded -GatedDiscretisedDensity:: -write_to_files(const string& filename) const -{ - for ( unsigned int num = 1 ; num<=(_time_gate_definitions).get_num_gates() ; ++num ) - { - std::stringstream gate_num_stream; - gate_num_stream << _time_gate_definitions.get_gate_num(num); - const string output_filename=filename+"_g"+gate_num_stream.str()+".hv"; - std::cout << "GatedDiscretisedDensity: Writing new gate file: " << output_filename.c_str() << std::endl; - if(OutputFileFormat >::default_sptr()->write_to_file(output_filename, this->get_density(num)) - == Succeeded::no) - return Succeeded::no; - } - if((this->_time_gate_definitions).get_num_gates()==0) - std::cout << "GatedDiscretisedDensity: No gates to write, please check!!" << std::endl; - return Succeeded::yes; +Succeeded +GatedDiscretisedDensity::write_to_files(const string& filename) const { + for (unsigned int num = 1; num <= (_time_gate_definitions).get_num_gates(); ++num) { + std::stringstream gate_num_stream; + gate_num_stream << _time_gate_definitions.get_gate_num(num); + const string output_filename = filename + "_g" + gate_num_stream.str() + ".hv"; + std::cout << "GatedDiscretisedDensity: Writing new gate file: " << output_filename.c_str() << std::endl; + if (OutputFileFormat>::default_sptr()->write_to_file(output_filename, this->get_density(num)) == + Succeeded::no) + return Succeeded::no; + } + if ((this->_time_gate_definitions).get_num_gates() == 0) + std::cout << "GatedDiscretisedDensity: No gates to write, please check!!" << std::endl; + return Succeeded::yes; } -Succeeded -GatedDiscretisedDensity:: -write_to_files(const string& filename,const string& suffix) const -{ - for ( unsigned int num = 1 ; num<=(_time_gate_definitions).get_num_gates() ; ++num ) - { - std::stringstream gate_num_stream; - gate_num_stream << _time_gate_definitions.get_gate_num(num); - const string output_filename=filename+"_g"+gate_num_stream.str()+suffix+".hv"; - std::cout << "GatedDiscretisedDensity: Writing new gate file: " << output_filename.c_str() << std::endl; - if(OutputFileFormat >::default_sptr()->write_to_file(output_filename, this->get_density(num)) - == Succeeded::no) - return Succeeded::no; - } - if((this->_time_gate_definitions).get_num_gates()==0) - std::cout << "GatedDiscretisedDensity: No gates to write, please check!!" << std::endl; - return Succeeded::yes; +Succeeded +GatedDiscretisedDensity::write_to_files(const string& filename, const string& suffix) const { + for (unsigned int num = 1; num <= (_time_gate_definitions).get_num_gates(); ++num) { + std::stringstream gate_num_stream; + gate_num_stream << _time_gate_definitions.get_gate_num(num); + const string output_filename = filename + "_g" + gate_num_stream.str() + suffix + ".hv"; + std::cout << "GatedDiscretisedDensity: Writing new gate file: " << output_filename.c_str() << std::endl; + if (OutputFileFormat>::default_sptr()->write_to_file(output_filename, this->get_density(num)) == + Succeeded::no) + return Succeeded::no; + } + if ((this->_time_gate_definitions).get_num_gates() == 0) + std::cout << "GatedDiscretisedDensity: No gates to write, please check!!" << std::endl; + return Succeeded::yes; } END_NAMESPACE_STIR diff --git a/src/buildblock/GatedProjData.cxx b/src/buildblock/GatedProjData.cxx index 57ffa20747..71ac97e91a 100644 --- a/src/buildblock/GatedProjData.cxx +++ b/src/buildblock/GatedProjData.cxx @@ -41,176 +41,143 @@ using std::string; START_NAMESPACE_STIR unique_ptr -GatedProjData:: -read_from_file(const string& filename) // The written image is read in respect to its center as origin!!! +GatedProjData::read_from_file(const string& filename) // The written image is read in respect to its center as origin!!! { std::fstream input(filename.c_str(), std::ios::in | std::ios::binary); - if (!input) - { - warning("GatedProjData::read_from_file cannot read file '%s'. Will now attempt to append .gdef", filename.c_str()); - return unique_ptr(read_from_gdef(filename)); - } + if (!input) { + warning("GatedProjData::read_from_file cannot read file '%s'. Will now attempt to append .gdef", filename.c_str()); + return unique_ptr(read_from_gdef(filename)); + } const FileSignature file_signature(input); - const char * signature = file_signature.get_signature(); + const char* signature = file_signature.get_signature(); unique_ptr gated_proj_data_sptr; #ifdef HAVE_LLN_MATRIX - if (strncmp(signature, "MATRIX", 6) == 0) - { -#ifndef NDEBUG + if (strncmp(signature, "MATRIX", 6) == 0) { +# ifndef NDEBUG warning("GatedProjData::read_from_file trying to read %s as ECAT7\n", filename.c_str()); -#endif +# endif USING_NAMESPACE_ECAT USING_NAMESPACE_ECAT7 - if (is_ECAT7_emission_file(filename)) - { + if (is_ECAT7_emission_file(filename)) { Main_header mhead; - if (read_ECAT7_main_header(mhead, filename) == Succeeded::no) - { - warning("GatedProjData::read_from_file cannot read %s as ECAT7\n", filename.c_str()); - return unique_ptr(); - } + if (read_ECAT7_main_header(mhead, filename) == Succeeded::no) { + warning("GatedProjData::read_from_file cannot read %s as ECAT7\n", filename.c_str()); + return unique_ptr(); + } gated_proj_data_sptr.reset(new GatedProjData); - const unsigned int num_gates = - static_cast(mhead.num_gates); // TODO +1? - gated_proj_data_sptr->_proj_datas.resize(num_gates); + const unsigned int num_gates = static_cast(mhead.num_gates); // TODO +1? + gated_proj_data_sptr->_proj_datas.resize(num_gates); - for (unsigned int gate_num=1; gate_num <= num_gates; ++ gate_num) - { - gated_proj_data_sptr->_proj_datas[gate_num-1].reset( - ECAT7_to_PDFS(filename, - 1, - gate_num, - /* data_num, bed_num, */ 0,0)); - } + for (unsigned int gate_num = 1; gate_num <= num_gates; ++gate_num) { + gated_proj_data_sptr->_proj_datas[gate_num - 1].reset(ECAT7_to_PDFS(filename, 1, gate_num, + /* data_num, bed_num, */ 0, 0)); + } if (is_null_ptr(gated_proj_data_sptr->_proj_datas[0])) - error("GatedProjData: No gate available\n"); + error("GatedProjData: No gate available\n"); // Get the exam info (from the first ProjData) - if (num_gates>0) + if (num_gates > 0) gated_proj_data_sptr->set_exam_info(gated_proj_data_sptr->_proj_datas[0]->get_exam_info()); - } - else - { + } else { if (is_ECAT7_file(filename)) - warning("GatedProjData::read_from_file ECAT7 file %s should be an emission sinogram\n", filename.c_str()); + warning("GatedProjData::read_from_file ECAT7 file %s should be an emission sinogram\n", filename.c_str()); } - } - else + } else #endif // end of HAVE_LLN_MATRIX - if (strncmp(signature, "Multigate", 9) == 0) - { - //#ifndef NDEBUG - warning("GatedProjData::read_from_file trying to read %s as Multigate", filename.c_str()); - //#endif - //return read_multi_gated_proj_data(filename); - - std::vector filenames; - KeyParser parser; - parser.add_start_key("Multigate"); - parser.add_stop_key("end"); - parser.add_key("filenames", &filenames); - if (parser.parse(filename.c_str()) == false) - { - warning("GatedProjData:::read_from_file: Error parsing %s", filename.c_str()); - return unique_ptr(); - } - - gated_proj_data_sptr.reset(new GatedProjData); - const unsigned int num_gates = - static_cast(filenames.size()); - gated_proj_data_sptr->_proj_datas.resize(num_gates); - - for (unsigned int gate_num=1; gate_num <= num_gates; ++ gate_num) - { - std::cerr<<" Reading " << filenames[gate_num-1]<<'\n'; - gated_proj_data_sptr->_proj_datas[gate_num-1] = - ProjData::read_from_file(filenames[gate_num-1]); - } - // Get the exam info (from the first ProjData) - if (num_gates>0) - gated_proj_data_sptr->set_exam_info(gated_proj_data_sptr->_proj_datas[0]->get_exam_info()); - return gated_proj_data_sptr; - } + if (strncmp(signature, "Multigate", 9) == 0) { + //#ifndef NDEBUG + warning("GatedProjData::read_from_file trying to read %s as Multigate", filename.c_str()); + //#endif + // return read_multi_gated_proj_data(filename); + + std::vector filenames; + KeyParser parser; + parser.add_start_key("Multigate"); + parser.add_stop_key("end"); + parser.add_key("filenames", &filenames); + if (parser.parse(filename.c_str()) == false) { + warning("GatedProjData:::read_from_file: Error parsing %s", filename.c_str()); + return unique_ptr(); + } + + gated_proj_data_sptr.reset(new GatedProjData); + const unsigned int num_gates = static_cast(filenames.size()); + gated_proj_data_sptr->_proj_datas.resize(num_gates); + + for (unsigned int gate_num = 1; gate_num <= num_gates; ++gate_num) { + std::cerr << " Reading " << filenames[gate_num - 1] << '\n'; + gated_proj_data_sptr->_proj_datas[gate_num - 1] = ProjData::read_from_file(filenames[gate_num - 1]); + } + // Get the exam info (from the first ProjData) + if (num_gates > 0) + gated_proj_data_sptr->set_exam_info(gated_proj_data_sptr->_proj_datas[0]->get_exam_info()); + return gated_proj_data_sptr; + } if (strncmp(signature, "Multi", 5) == 0) { #ifndef NDEBUG - info(boost::format("GatedProjData::read_from_file trying to read %s as a Multi file.") % filename); + info(boost::format("GatedProjData::read_from_file trying to read %s as a Multi file.") % filename); #endif - unique_ptr multi_proj_data(MultipleProjData::read_from_file(filename)); - gated_proj_data_sptr.reset(new GatedProjData(*multi_proj_data)); + unique_ptr multi_proj_data(MultipleProjData::read_from_file(filename)); + gated_proj_data_sptr.reset(new GatedProjData(*multi_proj_data)); } - + if (is_null_ptr(gated_proj_data_sptr)) - error("GatedProjData::read_from_file unrecognised file format for file '%s'", - filename.c_str()); + error("GatedProjData::read_from_file unrecognised file format for file '%s'", filename.c_str()); return gated_proj_data_sptr; } -GatedProjData* -GatedProjData::read_from_gdef(const string& filename) -{ - const string gdef_filename=filename+".gdef"; +GatedProjData* +GatedProjData::read_from_gdef(const string& filename) { + const string gdef_filename = filename + ".gdef"; std::cout << "GatedProjData: Reading gate definitions " << gdef_filename.c_str() << std::endl; - GatedProjData * gated_proj_data_ptr = new GatedProjData; + GatedProjData* gated_proj_data_ptr = new GatedProjData; gated_proj_data_ptr->_time_gate_definitions.read_gdef_file(gdef_filename); gated_proj_data_ptr->_proj_datas.resize(gated_proj_data_ptr->_time_gate_definitions.get_num_gates()); - for ( unsigned int num = 1 ; num<=(gated_proj_data_ptr->_time_gate_definitions).get_num_gates() ; ++num ) - { - std::stringstream gate_num_stream; - gate_num_stream << gated_proj_data_ptr->_time_gate_definitions.get_gate_num(num); - const string input_filename=filename+"_g"+gate_num_stream.str()+".hs"; - std::cout << "GatedProjData: Reading gate projection file: " << input_filename.c_str() << std::endl; - gated_proj_data_ptr->_proj_datas[num-1] = ProjData::read_from_file(input_filename); - } - if (is_null_ptr(gated_proj_data_ptr)) - error("GatedProjData::read_from_file unrecognised file format for projection files with prefix '%s'", - filename.c_str()); + for (unsigned int num = 1; num <= (gated_proj_data_ptr->_time_gate_definitions).get_num_gates(); ++num) { + std::stringstream gate_num_stream; + gate_num_stream << gated_proj_data_ptr->_time_gate_definitions.get_gate_num(num); + const string input_filename = filename + "_g" + gate_num_stream.str() + ".hs"; + std::cout << "GatedProjData: Reading gate projection file: " << input_filename.c_str() << std::endl; + gated_proj_data_ptr->_proj_datas[num - 1] = ProjData::read_from_file(input_filename); + } + if (is_null_ptr(gated_proj_data_ptr)) + error("GatedProjData::read_from_file unrecognised file format for projection files with prefix '%s'", filename.c_str()); // Get the exam info (from the first ProjData) - if (gated_proj_data_ptr->get_num_gates()>0) - gated_proj_data_ptr->set_exam_info(gated_proj_data_ptr->_proj_datas[0]->get_exam_info()); - return gated_proj_data_ptr; + if (gated_proj_data_ptr->get_num_gates() > 0) + gated_proj_data_ptr->set_exam_info(gated_proj_data_ptr->_proj_datas[0]->get_exam_info()); + return gated_proj_data_ptr; } -Succeeded -GatedProjData:: -write_to_ecat7(const string& filename) const -{ +Succeeded +GatedProjData::write_to_ecat7(const string& filename) const { #ifndef HAVE_LLN_MATRIX return Succeeded::no; #else Main_header mhead; - ecat::ecat7::make_ECAT7_main_header(mhead, filename, - get_proj_data(1).get_exam_info(), - *get_proj_data(1).get_proj_data_info_sptr() ); + ecat::ecat7::make_ECAT7_main_header(mhead, filename, get_proj_data(1).get_exam_info(), + *get_proj_data(1).get_proj_data_info_sptr()); mhead.num_gates = 1; mhead.num_gates = this->get_num_gates(); - mhead.acquisition_type = - mhead.num_gates>1 ? DynamicEmission : StaticEmission; + mhead.acquisition_type = mhead.num_gates > 1 ? DynamicEmission : StaticEmission; - MatrixFile* mptr= matrix_create (filename.c_str(), MAT_CREATE, &mhead); - if (mptr == 0) - { - warning("GatedProjData::write_to_ecat7 cannot write output file %s\n", filename.c_str()); + MatrixFile* mptr = matrix_create(filename.c_str(), MAT_CREATE, &mhead); + if (mptr == 0) { + warning("GatedProjData::write_to_ecat7 cannot write output file %s\n", filename.c_str()); + return Succeeded::no; + } + for (unsigned int gate_num = 1; gate_num <= this->get_num_gates(); ++gate_num) { + if (ecat::ecat7::ProjData_to_ECAT7(mptr, get_proj_data(gate_num), 1, gate_num) == Succeeded::no) { + matrix_close(mptr); return Succeeded::no; } - for ( unsigned int gate_num = 1 ; gate_num<=this->get_num_gates() ; ++gate_num ) - { - if (ecat::ecat7::ProjData_to_ECAT7(mptr, - get_proj_data(gate_num), - 1, - gate_num) - == Succeeded::no) - { - matrix_close(mptr); - return Succeeded::no; - } - } + } matrix_close(mptr); return Succeeded::yes; #endif // end of HAVE_LLN_MATRIX diff --git a/src/buildblock/GeneralisedPoissonNoiseGenerator.cxx b/src/buildblock/GeneralisedPoissonNoiseGenerator.cxx index 2c3af94c97..7adcf711af 100644 --- a/src/buildblock/GeneralisedPoissonNoiseGenerator.cxx +++ b/src/buildblock/GeneralisedPoissonNoiseGenerator.cxx @@ -18,7 +18,7 @@ /*! \file - \ingroup buildblock + \ingroup buildblock \brief Implements stir::GeneralisedPoissonNoiseGenerator \author Kris Thielemans \author Sanida Mustafovic @@ -38,38 +38,29 @@ START_NAMESPACE_STIR GeneralisedPoissonNoiseGenerator::base_generator_type GeneralisedPoissonNoiseGenerator::generator; -GeneralisedPoissonNoiseGenerator:: -GeneralisedPoissonNoiseGenerator(const float scaling_factor, - const bool preserve_mean) - : scaling_factor(scaling_factor), - preserve_mean(preserve_mean) -{ +GeneralisedPoissonNoiseGenerator::GeneralisedPoissonNoiseGenerator(const float scaling_factor, const bool preserve_mean) + : scaling_factor(scaling_factor), preserve_mean(preserve_mean) { this->seed(43u); } void -GeneralisedPoissonNoiseGenerator:: -seed(unsigned int value) -{ - if (value==unsigned(0)) - error("Seed value has to be non-zero"); +GeneralisedPoissonNoiseGenerator::seed(unsigned int value) { + if (value == unsigned(0)) + error("Seed value has to be non-zero"); this->generator.seed(static_cast(value)); } // function that generates a Poisson noise realisation, i.e. without // using the scaling_factor unsigned int -GeneralisedPoissonNoiseGenerator:: -generate_poisson_random(const float mu) -{ +GeneralisedPoissonNoiseGenerator::generate_poisson_random(const float mu) { static boost::uniform_01 random01(generator); // normal distribution with mean=0 and sigma=1 static boost::normal_distribution normal_distrib01(0., 1.); // check if mu is large. If so, use the normal distribution // note: the threshold must be such that exp(threshold) is still a floating point number - if (mu > 60.F) - { + if (mu > 60.F) { // get random number of normal distribution of mean=mu and sigma=sqrt(mu) // get random with mean=0, sigma=1 and use scaling with sqrt(mu) and addition of mu @@ -77,76 +68,55 @@ generate_poisson_random(const float mu) // object every time. This will speed things up, especially because the // normal_distribution is implemented using with a polar method that calls // generator::operator() twice only on 'odd'- number of invocations - const double random=normal_distrib01(random01)*sqrt(mu) + mu; + const double random = normal_distrib01(random01) * sqrt(mu) + mu; - return static_cast(random<=0 ? 0 : round(random)); - } - else - { + return static_cast(random <= 0 ? 0 : round(random)); + } else { double u = random01(); - - // prevent problems of n growing too large (or even to infinity) + + // prevent problems of n growing too large (or even to infinity) // when u is very close to 1 - if (u>1-1.E-6) - u = 1-1.E-6; - - const double upper = exp(mu)*u; + if (u > 1 - 1.E-6) + u = 1 - 1.E-6; + + const double upper = exp(mu) * u; double accum = 1.; - double term = 1.; + double term = 1.; unsigned int n = 1; - - while(accum (random_poisson); +GeneralisedPoissonNoiseGenerator::generate_scaled_poisson_random(const float mu, const float scaling_factor, + const bool preserve_mean) { + const unsigned int random_poisson = generate_poisson_random(mu * scaling_factor); + return preserve_mean ? random_poisson / scaling_factor : static_cast(random_poisson); } float -GeneralisedPoissonNoiseGenerator:: -generate_random(const float mu) -{ - return - generate_scaled_poisson_random(mu, scaling_factor, preserve_mean); +GeneralisedPoissonNoiseGenerator::generate_random(const float mu) { + return generate_scaled_poisson_random(mu, scaling_factor, preserve_mean); } - -void -GeneralisedPoissonNoiseGenerator:: -generate_random(ProjData& output_projdata, - const ProjData& input_projdata) -{ - for (int seg= input_projdata.get_min_segment_num(); - seg<=input_projdata.get_max_segment_num(); - seg++) - { - 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"); - } +void +GeneralisedPoissonNoiseGenerator::generate_random(ProjData& output_projdata, const ProjData& input_projdata) { + for (int seg = input_projdata.get_min_segment_num(); seg <= input_projdata.get_max_segment_num(); seg++) { + 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/HUToMuImageProcessor.cxx b/src/buildblock/HUToMuImageProcessor.cxx index 337db22b51..f3ee9c471a 100644 --- a/src/buildblock/HUToMuImageProcessor.cxx +++ b/src/buildblock/HUToMuImageProcessor.cxx @@ -1,12 +1,12 @@ /*! \file - \ingroup ImageProcessor + \ingroup ImageProcessor \brief Implementation of class stir::HUToMuImageProcessor - + \author Kris Thielemans \author Benjamin A. Thomas - + */ /* Copyright (C) 2019, 2020, UCL @@ -33,49 +33,37 @@ START_NAMESPACE_STIR template -HUToMuImageProcessor:: -HUToMuImageProcessor() -{ +HUToMuImageProcessor::HUToMuImageProcessor() { this->set_defaults(); } template void -HUToMuImageProcessor:: -set_slope_filename(const std::string& arg) -{ +HUToMuImageProcessor::set_slope_filename(const std::string& arg) { this->filename = arg; } template void -HUToMuImageProcessor:: -set_manufacturer_name(const std::string& arg) -{ +HUToMuImageProcessor::set_manufacturer_name(const std::string& arg) { this->manufacturer_name = arg; } template void -HUToMuImageProcessor:: -set_kilovoltage_peak(const float arg) -{ +HUToMuImageProcessor::set_kilovoltage_peak(const float arg) { this->kilovoltage_peak = arg; } template void -HUToMuImageProcessor:: -set_target_photon_energy(const float arg) -{ +HUToMuImageProcessor::set_target_photon_energy(const float arg) { this->target_photon_energy = arg; } template void -HUToMuImageProcessor:: -set_defaults() -{ +HUToMuImageProcessor::set_defaults() { base_type::set_defaults(); manufacturer_name = "GENERIC"; kilovoltage_peak = 120.F; @@ -84,9 +72,7 @@ set_defaults() template void -HUToMuImageProcessor:: -initialise_keymap() -{ +HUToMuImageProcessor::initialise_keymap() { base_type::initialise_keymap(); this->parser.add_start_key(std::string(this->registered_name) + " Parameters"); this->parser.add_stop_key(std::string("End ") + this->registered_name + " Parameters"); @@ -98,75 +84,68 @@ initialise_keymap() template bool -HUToMuImageProcessor:: -post_processing() -{ +HUToMuImageProcessor::post_processing() { return base_type::post_processing(); } template Succeeded -HUToMuImageProcessor:: -virtual_set_up(const TargetT& image) -{ +HUToMuImageProcessor::virtual_set_up(const TargetT& image) { this->get_record_from_json(); return Succeeded::yes; } template void -HUToMuImageProcessor:: -get_record_from_json() -{ +HUToMuImageProcessor::get_record_from_json() { if (this->filename.empty()) error("HUToMu: no filename set for the slope info"); if (this->manufacturer_name.empty()) error("HUToMu: no manufacturer set for the slope info"); - //Read slope file + // Read slope file std::ifstream slope_json_file_stream(this->filename); nlohmann::json slope_json; slope_json_file_stream >> slope_json; - if (slope_json.find("scale") == slope_json.end()) - { - error("HUToMu: No or incorrect JSON slopes set (could not find \"scale\" in file \"" - + filename + "\")"); - } - //Put user-specified manufacturer into upper case. + if (slope_json.find("scale") == slope_json.end()) { + error("HUToMu: No or incorrect JSON slopes set (could not find \"scale\" in file \"" + filename + "\")"); + } + // Put user-specified manufacturer into upper case. std::string manufacturer_upper_case = this->manufacturer_name; std::locale loc; - for (std::string::size_type i=0; itarget_photon_energy); - //Get desired kVp as integer value + // Get desired kVp as integer value const int kVp = stir::round(this->kilovoltage_peak); - stir::info(boost::format("HUToMu: finding record with manufacturer: '%s', keV=%d, kVp=%d in file '%s'") - % manufacturer_upper_case % keV % kVp % this->filename, 2); + stir::info(boost::format("HUToMu: finding record with manufacturer: '%s', keV=%d, kVp=%d in file '%s'") % + manufacturer_upper_case % keV % kVp % this->filename, + 2); - //Extract appropriate chunk of JSON file for given manufacturer. + // Extract appropriate chunk of JSON file for given manufacturer. nlohmann::json target = slope_json["scale"][manufacturer_upper_case]["transform"]; int location = -1; int pos = 0; - for (auto entry : target){ - if ( (stir::round(float(entry["kev"])) == keV) && (stir::round(float(entry["kvp"])) == kVp) ) + for (auto entry : target) { + if ((stir::round(float(entry["kev"])) == keV) && (stir::round(float(entry["kvp"])) == kVp)) location = pos; pos++; } - if (location == -1){ + if (location == -1) { stir::error("HUToMu: Desired slope not found!"); } - //Extract transform for specific keV and kVp. + // Extract transform for specific keV and kVp. nlohmann::json transform = target[location]; { std::stringstream str; str << transform.dump(4); - info("HUToMu: JSON record found:" + str.str(),2); + info("HUToMu: JSON record found:" + str.str(), 2); } this->a1 = transform["a1"]; this->b1 = transform["b1"]; @@ -176,63 +155,51 @@ get_record_from_json() this->breakPoint = transform["break"]; - //std::cout << transform.dump(4); + // std::cout << transform.dump(4); } template void -HUToMuImageProcessor:: -apply_scaling_to_HU(TargetT& output_image, - const TargetT& input_image) const -{ +HUToMuImageProcessor::apply_scaling_to_HU(TargetT& output_image, const TargetT& input_image) const { auto out_iter = output_image.begin_all(); auto in_iter = input_image.begin_all(); + while (in_iter != input_image.end_all()) { + if (*in_iter < breakPoint) { + const float mu = a1 + b1 * (*in_iter); + *out_iter = (mu < 0.0f) ? 0.0f : mu; + } else { + *out_iter = a2 + b2 * (*in_iter); + } - while( in_iter != input_image.end_all()) - { - if (*in_iter < breakPoint) - { - const float mu = a1 + b1 *(*in_iter); - *out_iter = (mu < 0.0f) ? 0.0f : mu; - } else - { - *out_iter = a2 + b2 * (*in_iter); - } - - ++in_iter; ++out_iter; + ++in_iter; + ++out_iter; } } template void -HUToMuImageProcessor:: -virtual_apply(TargetT& out_density, const TargetT& in_density) const -{ +HUToMuImageProcessor::virtual_apply(TargetT& out_density, const TargetT& in_density) const { this->apply_scaling_to_HU(out_density, in_density); } template void -HUToMuImageProcessor:: - virtual_apply(TargetT& density) const -{ - shared_ptr copy_sptr(density.clone()); +HUToMuImageProcessor::virtual_apply(TargetT& density) const { + shared_ptr copy_sptr(density.clone()); this->apply_scaling_to_HU(density, *copy_sptr); } -# ifdef _MSC_VER -// prevent warning message on reinstantiation, +#ifdef _MSC_VER +// prevent warning message on reinstantiation, // note that we get a linking error if we don't have the explicit instantiation below -# pragma warning(disable:4660) -# endif +# pragma warning(disable : 4660) +#endif // Register this class in the ImageProcessor registry // static HUToMuImageProcessor>::RegisterIt dummy; // have the above variable in a separate file, which you need to pass at link time -template class HUToMuImageProcessor>; +template class HUToMuImageProcessor>; END_NAMESPACE_STIR - - diff --git a/src/buildblock/IndexRange.cxx b/src/buildblock/IndexRange.cxx index 6b7f6f750a..e66ff3b513 100644 --- a/src/buildblock/IndexRange.cxx +++ b/src/buildblock/IndexRange.cxx @@ -1,8 +1,8 @@ // // /*! - \file - \ingroup Array + \file + \ingroup Array \brief implementations for the stir::IndexRange class \author Kris Thielemans @@ -34,24 +34,20 @@ START_NAMESPACE_STIR #ifndef STIR_NO_MUTABLE - template bool -IndexRange::get_regular_range( - BasicCoordinate& min, - BasicCoordinate& max) const -{ - // check if empty range - if (base_type::begin() == base_type::end()) - { -#ifndef STIR_NO_NAMESPACES +IndexRange::get_regular_range(BasicCoordinate& min, + BasicCoordinate& max) const { + // check if empty range + if (base_type::begin() == base_type::end()) { +# ifndef STIR_NO_NAMESPACES std::fill(min.begin(), min.end(), 0); - std::fill(max.begin(), max.end(),-1); -#else + std::fill(max.begin(), max.end(), -1); +# else // gcc 2.8.1 needs ::fill, otherwise it gets confused with VectorWithOffset::fill ::fill(min.begin(), min.end(), 0); - ::fill(max.begin(), max.end(),-1); -#endif + ::fill(max.begin(), max.end(), -1); +# endif return true; } @@ -59,59 +55,56 @@ IndexRange::get_regular_range( if (is_regular_range == regular_false) return false; - typename base_type::const_iterator iter=base_type::begin(); + typename base_type::const_iterator iter = base_type::begin(); - BasicCoordinate lower_dim_min; - BasicCoordinate lower_dim_max; + BasicCoordinate lower_dim_min; + BasicCoordinate lower_dim_max; if (!iter->get_regular_range(lower_dim_min, lower_dim_max)) return false; - if (is_regular_range == regular_to_do) - { - // check if all lower dimensional ranges have same regular range - BasicCoordinate lower_dim_min_try; - BasicCoordinate lower_dim_max_try; - - for (++iter; iter != base_type::end(); ++iter) - { - if (!iter->get_regular_range(lower_dim_min_try, lower_dim_max_try)) - { - is_regular_range = regular_false; - return false; + if (is_regular_range == regular_to_do) { + // check if all lower dimensional ranges have same regular range + BasicCoordinate lower_dim_min_try; + BasicCoordinate lower_dim_max_try; + + for (++iter; iter != base_type::end(); ++iter) { + if (!iter->get_regular_range(lower_dim_min_try, lower_dim_max_try)) { + is_regular_range = regular_false; + return false; } -#ifndef STIR_NO_NAMESPACES +# ifndef STIR_NO_NAMESPACES if (!std::equal(lower_dim_min.begin(), lower_dim_min.end(), lower_dim_min_try.begin()) || - !std::equal(lower_dim_max.begin(), lower_dim_max.end(), lower_dim_max_try.begin())) -#else - if (!equal(lower_dim_min.begin(), lower_dim_min.end(), lower_dim_min_try.begin()) || - !equal(lower_dim_max.begin(), lower_dim_max.end(), lower_dim_max_try.begin())) -#endif + !std::equal(lower_dim_max.begin(), lower_dim_max.end(), lower_dim_max_try.begin())) +# else + if (!equal(lower_dim_min.begin(), lower_dim_min.end(), lower_dim_min_try.begin()) || + !equal(lower_dim_max.begin(), lower_dim_max.end(), lower_dim_max_try.begin())) +# endif { - is_regular_range = regular_false; - return false; + is_regular_range = regular_false; + return false; } } // yes, they do is_regular_range = regular_true; } -#if defined(_MSC_VER) && _MSC_VER<1200 +# if defined(_MSC_VER) && _MSC_VER < 1200 // bug in VC++ 5.0, needs explicit template args - min = join(base_type::get_min_index(), lower_dim_min); - max = join(base_type::get_max_index(), lower_dim_max); -#elif defined( __GNUC__) && (__GNUC__ == 2 && __GNUC_MINOR__ < 9) - // work around gcc 2.8.1 bug. + min = join(base_type::get_min_index(), lower_dim_min); + max = join(base_type::get_max_index(), lower_dim_max); +# elif defined(__GNUC__) && (__GNUC__ == 2 && __GNUC_MINOR__ < 9) + // work around gcc 2.8.1 bug. // It cannot call 'join' (it generates a bad mangled name for the function) // So, we explicitly insert the code here *min.begin() = base_type::get_min_index(); - copy(lower_dim_min.begin(), lower_dim_min.end(), min.begin()+1); + copy(lower_dim_min.begin(), lower_dim_min.end(), min.begin() + 1); *max.begin() = base_type::get_max_index(); - copy(lower_dim_max.begin(), lower_dim_max.end(), max.begin()+1); -#else - // lines for good compilers... + copy(lower_dim_max.begin(), lower_dim_max.end(), max.begin() + 1); +# else + // lines for good compilers... min = join(base_type::get_min_index(), lower_dim_min); - max = join(base_type::get_max_index(), lower_dim_max); -#endif + max = join(base_type::get_max_index(), lower_dim_max); +# endif return true; } @@ -119,20 +112,17 @@ IndexRange::get_regular_range( template bool -IndexRange::get_regular_range( - BasicCoordinate& min, - BasicCoordinate& max) const -{ - // check if empty range - if (base_type::begin() == base_type::end()) - { -#ifndef STIR_NO_NAMESPACES +IndexRange::get_regular_range(BasicCoordinate& min, + BasicCoordinate& max) const { + // check if empty range + if (base_type::begin() == base_type::end()) { +# ifndef STIR_NO_NAMESPACES std::fill(min.begin(), min.end(), 0); - std::fill(max.begin(), max.end(),-1); -#else - fill(min,base_type::begin(), min.end(), 0); - fill(max,base_type::begin(), max.end(),-1); -#endif + std::fill(max.begin(), max.end(), -1); +# else + fill(min, base_type::begin(), min.end(), 0); + fill(max, base_type::begin(), max.end(), -1); +# endif return true; } @@ -140,69 +130,63 @@ IndexRange::get_regular_range( if (is_regular_range == regular_false) return false; - base_type::const_iterator iter=base_type::begin(); + base_type::const_iterator iter = base_type::begin(); - BasicCoordinate lower_dim_min; - BasicCoordinate lower_dim_max; + BasicCoordinate lower_dim_min; + BasicCoordinate lower_dim_max; if (!iter->get_regular_range(lower_dim_min, lower_dim_max)) return false; - if (is_regular_range == regular_to_do) - { - // check if all lower dimensional ranges have same regular range - BasicCoordinate lower_dim_min_try; - BasicCoordinate lower_dim_max_try; - - for (++iter; iter != base_type::end(); ++iter) - { - if (!iter->get_regular_range(lower_dim_min_try, lower_dim_max_try)) - { - //is_regular_range = regular_false; - return false; + if (is_regular_range == regular_to_do) { + // check if all lower dimensional ranges have same regular range + BasicCoordinate lower_dim_min_try; + BasicCoordinate lower_dim_max_try; + + for (++iter; iter != base_type::end(); ++iter) { + if (!iter->get_regular_range(lower_dim_min_try, lower_dim_max_try)) { + // is_regular_range = regular_false; + return false; } -#ifndef STIR_NO_NAMESPACES +# ifndef STIR_NO_NAMESPACES if (!std::equal(lower_dim_min.begin(), lower_dim_min.end(), lower_dim_min_try.begin()) || - !std::equal(lower_dim_max.begin(), lower_dim_max.end(), lower_dim_max_try.begin())) -#else - if (!equal(lower_dim_min.begin(), lower_dim_min.end(), lower_dim_min_try.begin()) || - !equal(lower_dim_max.begin(), lower_dim_max.end(), lower_dim_max_try.begin())) -#endif + !std::equal(lower_dim_max.begin(), lower_dim_max.end(), lower_dim_max_try.begin())) +# else + if (!equal(lower_dim_min.begin(), lower_dim_min.end(), lower_dim_min_try.begin()) || + !equal(lower_dim_max.begin(), lower_dim_max.end(), lower_dim_max_try.begin())) +# endif { - //is_regular_range = regular_false; - return false; + // is_regular_range = regular_false; + return false; } } // yes, they do - //is_regular_range = regular_true; + // is_regular_range = regular_true; } -#if defined(_MSC_VER) && _MSC_VER<1200 +# if defined(_MSC_VER) && _MSC_VER < 1200 // bug in VC++ 5.0, needs explicit template args - min = join(base_type::get_min_index(), lower_dim_min); - max = join(base_type::get_max_index(), lower_dim_max); -#else + min = join(base_type::get_min_index(), lower_dim_min); + max = join(base_type::get_max_index(), lower_dim_max); +# else min = join(base_type::get_min_index(), lower_dim_min); max = join(base_type::get_max_index(), lower_dim_max); -#endif +# endif return true; } template bool -IndexRange::get_regular_range( - BasicCoordinate& min, - BasicCoordinate& max) -{ - // check if empty range - if (base_type::begin() == base_type::end()) - { -#ifndef STIR_NO_NAMESPACES +IndexRange::get_regular_range(BasicCoordinate& min, + BasicCoordinate& max) { + // check if empty range + if (base_type::begin() == base_type::end()) { +# ifndef STIR_NO_NAMESPACES std::fill(min.begin(), min.end(), 0); - std::fill(max.begin(), max.end(),-1); -#else + std::fill(max.begin(), max.end(), -1); +# else fill(min.begin(), min.end(), 0); - fill(max.begin(), max.end(),-1); -#endif + fill(max.begin(), max.end(), -1); +# endif return true; } @@ -210,56 +194,52 @@ IndexRange::get_regular_range( if (is_regular_range == regular_false) return false; - base_type::iterator iter=base_type::begin(); + base_type::iterator iter = base_type::begin(); - BasicCoordinate lower_dim_min; - BasicCoordinate lower_dim_max; + BasicCoordinate lower_dim_min; + BasicCoordinate lower_dim_max; if (!iter->get_regular_range(lower_dim_min, lower_dim_max)) return false; - if (is_regular_range == regular_to_do) - { - // check if all lower dimensional ranges have same regular range - BasicCoordinate lower_dim_min_try; - BasicCoordinate lower_dim_max_try; - - for (++iter; iter != base_type::end(); ++iter) - { - if (!iter->get_regular_range(lower_dim_min_try, lower_dim_max_try)) - { - is_regular_range = regular_false; - return false; + if (is_regular_range == regular_to_do) { + // check if all lower dimensional ranges have same regular range + BasicCoordinate lower_dim_min_try; + BasicCoordinate lower_dim_max_try; + + for (++iter; iter != base_type::end(); ++iter) { + if (!iter->get_regular_range(lower_dim_min_try, lower_dim_max_try)) { + is_regular_range = regular_false; + return false; } -#ifndef STIR_NO_NAMESPACES +# ifndef STIR_NO_NAMESPACES if (!std::equal(lower_dim_min.begin(), lower_dim_min.end(), lower_dim_min_try.begin()) || - !std::equal(lower_dim_max.begin(), lower_dim_max.end(), lower_dim_max_try.begin())) -#else - if (!equal(lower_dim_min.begin(), lower_dim_min.end(), lower_dim_min_try.begin()) || - !equal(lower_dim_max.begin(), lower_dim_max.end(), lower_dim_max_try.begin())) -#endif + !std::equal(lower_dim_max.begin(), lower_dim_max.end(), lower_dim_max_try.begin())) +# else + if (!equal(lower_dim_min.begin(), lower_dim_min.end(), lower_dim_min_try.begin()) || + !equal(lower_dim_max.begin(), lower_dim_max.end(), lower_dim_max_try.begin())) +# endif { - is_regular_range = regular_false; - return false; + is_regular_range = regular_false; + return false; } } // yes, they do is_regular_range = regular_true; } -#if defined(_MSC_VER) && _MSC_VER<1200 +# if defined(_MSC_VER) && _MSC_VER < 1200 // bug in VC++ 5.0, needs explicit template args - min = join(base_type::get_min_index(), lower_dim_min); - max = join(base_type::get_max_index(), lower_dim_max); -#else + min = join(base_type::get_min_index(), lower_dim_min); + max = join(base_type::get_max_index(), lower_dim_max); +# else min = join(base_type::get_min_index(), lower_dim_min); max = join(base_type::get_max_index(), lower_dim_max); -#endif +# endif return true; } #endif // STIR_NO_MUTABLE - /*************************************************** instantiations ***************************************************/ diff --git a/src/buildblock/KeyParser.cxx b/src/buildblock/KeyParser.cxx index f77e858f96..e5c211a461 100644 --- a/src/buildblock/KeyParser.cxx +++ b/src/buildblock/KeyParser.cxx @@ -39,14 +39,16 @@ #include #include #include -# ifdef BOOST_NO_STDC_NAMESPACE - namespace std { using ::getenv; } -# endif +#ifdef BOOST_NO_STDC_NAMESPACE +namespace std { +using ::getenv; +} +#endif #include #ifndef BOOST_NO_STRINGSTREAM -#include +# include #endif #ifndef STIR_NO_NAMESPACES @@ -59,7 +61,7 @@ using std::istrstream; using std::ostrstream; using std::vector; using std::string; -//using std::map; +// using std::map; using std::list; using std::pair; using std::istream; @@ -81,222 +83,162 @@ START_NAMESPACE_STIR This function should be moved somewhere else as it might be useful to someone else as well. (TODO) */ -static void read_line(istream& input, string& line, - const char continuation_char = '\\') -{ +static void +read_line(istream& input, string& line, const char continuation_char = '\\') { line.resize(0); if (!input) return; string thisline; - while(true) - { + while (true) { #ifndef _MSC_VER - std::getline(input, thisline); + std::getline(input, thisline); #else - /* VC 6.0 getline does not work properly when input==cin. - It only returns after a 2nd CR is entered. (The entered input is - used for the next getline, so that the input is indeed ok. Problem is - if that in a sequence - getline(cin,...); - cout << "prompt"; - getline(cin,...); - the user sees the 'prompt' only after the 2nd CR. - So, we replace getline(stream,string) with our own mess... - */ - { - const size_t buf_size=512; // arbitrary number here. we'll check if the line was too long below - char buf[buf_size]; - thisline.resize(0); - bool more_chars = false; - do - { - buf[0]='\0'; - input.getline(buf,buf_size); - thisline += buf; - if (input.fail() && !input.bad() && !input.eof()) - { - // either no characters (end-of-line somehow) or buf_size-1 or end-of-file - input.clear(); - more_chars = strlen(buf)==buf_size-1; - } - else - more_chars = false; - } - while (more_chars); - } + /* VC 6.0 getline does not work properly when input==cin. + It only returns after a 2nd CR is entered. (The entered input is + used for the next getline, so that the input is indeed ok. Problem is + if that in a sequence + getline(cin,...); + cout << "prompt"; + getline(cin,...); + the user sees the 'prompt' only after the 2nd CR. + So, we replace getline(stream,string) with our own mess... + */ + { + const size_t buf_size = 512; // arbitrary number here. we'll check if the line was too long below + char buf[buf_size]; + thisline.resize(0); + bool more_chars = false; + do { + buf[0] = '\0'; + input.getline(buf, buf_size); + thisline += buf; + if (input.fail() && !input.bad() && !input.eof()) { + // either no characters (end-of-line somehow) or buf_size-1 or end-of-file + input.clear(); + more_chars = strlen(buf) == buf_size - 1; + } else + more_chars = false; + } while (more_chars); + } #endif - // check if last character is \r, - // in case this is a DOS file, but not a DOS/Windows host - if (thisline.size() != 0) - { - string::size_type position_of_last_char = - thisline.size()-1; - if (thisline[position_of_last_char] == '\r') - thisline.erase(position_of_last_char, 1); - } - // TODO handle the case of a Mac file on a non-Mac host (EOL on Mac is \r) - - line += thisline; - - // check for continuation - if (line.size() != 0) - { - string::size_type position_of_last_char = - line.size()-1; - if (line[position_of_last_char] == continuation_char) - { - line.erase(position_of_last_char, 1); - // now the while loop will keep on reading - } - else - { - // exit the loop - break; - } - } - else - { - // exit the loop - break; - } + // check if last character is \r, + // in case this is a DOS file, but not a DOS/Windows host + if (thisline.size() != 0) { + string::size_type position_of_last_char = thisline.size() - 1; + if (thisline[position_of_last_char] == '\r') + thisline.erase(position_of_last_char, 1); } + // TODO handle the case of a Mac file on a non-Mac host (EOL on Mac is \r) + + line += thisline; + + // check for continuation + if (line.size() != 0) { + string::size_type position_of_last_char = line.size() - 1; + if (line[position_of_last_char] == continuation_char) { + line.erase(position_of_last_char, 1); + // now the while loop will keep on reading + } else { + // exit the loop + break; + } + } else { + // exit the loop + break; + } + } // replace ${text} with value of environment variable { string::size_type start_of_env_string; - while ((start_of_env_string = line.find("${")) != string::npos) - { - const string::size_type end_of_env_string = line.find('}',start_of_env_string+2); - if (end_of_env_string == string::npos) - break; - const string::size_type size_of_env_string = - end_of_env_string-start_of_env_string+1; - const string name_of_env_variable = line.substr(start_of_env_string+2, size_of_env_string-3); - const char * const value_of_env_variable = std::getenv(name_of_env_variable.c_str()); - if (value_of_env_variable == 0) - { - warning("KeyParser: environment variable '%s' not found. Replaced by empty string.\n" - "This happened while parsing the following line:\n%s", - name_of_env_variable.c_str(), - line.c_str()); - line.erase(start_of_env_string, size_of_env_string); - } - else - { - line.replace(start_of_env_string, - size_of_env_string, - value_of_env_variable); - } + while ((start_of_env_string = line.find("${")) != string::npos) { + const string::size_type end_of_env_string = line.find('}', start_of_env_string + 2); + if (end_of_env_string == string::npos) + break; + const string::size_type size_of_env_string = end_of_env_string - start_of_env_string + 1; + const string name_of_env_variable = line.substr(start_of_env_string + 2, size_of_env_string - 3); + const char* const value_of_env_variable = std::getenv(name_of_env_variable.c_str()); + if (value_of_env_variable == 0) { + warning("KeyParser: environment variable '%s' not found. Replaced by empty string.\n" + "This happened while parsing the following line:\n%s", + name_of_env_variable.c_str(), line.c_str()); + line.erase(start_of_env_string, size_of_env_string); + } else { + line.replace(start_of_env_string, size_of_env_string, value_of_env_variable); } + } } } // map_element implementation; map_element::map_element() - : - type(KeyArgument::NONE), - p_object_member(0), - p_object_variable(0), - vectorised_key_level(0), - p_object_list_of_values(0) + : type(KeyArgument::NONE), p_object_member(0), p_object_variable(0), vectorised_key_level(0), p_object_list_of_values(0) {} + +map_element::map_element(KeyArgument::type t, KeyParser::KeywordProcessor pom, void* pov, const int vectorised_key_level_v, + const ASCIIlist_type* list_of_values) + : type(t), p_object_member(pom), p_object_variable(pov), vectorised_key_level(vectorised_key_level_v), + p_object_list_of_values(list_of_values) {} + +map_element::map_element(void (KeyParser::*pom)(), RegisteredObjectBase** pov, Parser* parser) + : type(KeyArgument::PARSINGOBJECT), p_object_member(pom), p_object_variable(pov), vectorised_key_level(0), + p_object_list_of_values(0), parser(parser) // static_cast(parser)) {} -map_element::map_element(KeyArgument::type t, - KeyParser::KeywordProcessor pom, - void* pov, - const int vectorised_key_level_v, - const ASCIIlist_type *list_of_values) - : - type(t), - p_object_member(pom), - p_object_variable(pov), - vectorised_key_level(vectorised_key_level_v), - p_object_list_of_values(list_of_values) +map_element::map_element(void (KeyParser::*pom)(), shared_ptr* pov, Parser* parser) + : type(KeyArgument::SHARED_PARSINGOBJECT), p_object_member(pom), p_object_variable(pov), vectorised_key_level(0), + p_object_list_of_values(0), parser(parser) // static_cast(parser)) {} -map_element::map_element(void (KeyParser::*pom)(), - RegisteredObjectBase** pov, - Parser* parser) - : - type(KeyArgument::PARSINGOBJECT), - p_object_member(pom), - p_object_variable(pov), - vectorised_key_level(0), - p_object_list_of_values(0), - parser(parser)//static_cast(parser)) - {} - -map_element::map_element(void (KeyParser::*pom)(), - shared_ptr* pov, - Parser* parser) - : - type(KeyArgument::SHARED_PARSINGOBJECT), - p_object_member(pom), - p_object_variable(pov), - vectorised_key_level(0), - p_object_list_of_values(0), - parser(parser)//static_cast(parser)) - {} - -map_element::~map_element() -{ -} - - -map_element& map_element::operator=(const map_element& me) -{ - type=me.type; - p_object_member=me.p_object_member; - p_object_variable=me.p_object_variable; - vectorised_key_level=me.vectorised_key_level; - p_object_list_of_values=me.p_object_list_of_values; +map_element::~map_element() {} + +map_element& +map_element::operator=(const map_element& me) { + type = me.type; + p_object_member = me.p_object_member; + p_object_variable = me.p_object_variable; + vectorised_key_level = me.vectorised_key_level; + p_object_list_of_values = me.p_object_list_of_values; parser = me.parser; return *this; } // KeyParser implementation +KeyParser::KeyParser() { -KeyParser::KeyParser() -{ - - current_index=-1; - status=end_parsing; - current=0;//KTnew map_element(); + current_index = -1; + status = end_parsing; + current = 0; // KTnew map_element(); } -KeyParser::~KeyParser() -{ -} +KeyParser::~KeyParser() {} -bool KeyParser::parse(const char * const filename, const bool write_warning) -{ - ifstream hdr_stream(filename); - if (!hdr_stream) - { - warning("KeyParser::parse: couldn't open file %s\n", filename); - return false; - } - return parse(hdr_stream, write_warning); +bool +KeyParser::parse(const char* const filename, const bool write_warning) { + ifstream hdr_stream(filename); + if (!hdr_stream) { + warning("KeyParser::parse: couldn't open file %s\n", filename); + return false; + } + return parse(hdr_stream, write_warning); } -bool KeyParser::parse(istream& f, const bool write_warning) -{ +bool +KeyParser::parse(istream& f, const bool write_warning) { // print_keywords_to_stream(cerr); - input=&f; - return (parse_header(write_warning)==Succeeded::yes && post_processing()==false); + input = &f; + return (parse_header(write_warning) == Succeeded::yes && post_processing() == false); } - // KT 10/07/2000 new function /*! This follows Interfile 3.3 conventions:
    • The characters \c space, \c tab, \c underscore, \c ! are all - treated as white space and ignored. + treated as white space and ignored.
    • Case is ignored.
    Note: in this implementation 'ignoring' white space means 'trimming' @@ -304,389 +246,310 @@ bool KeyParser::parse(istream& f, const bool write_warning) with a single space. */ -string -KeyParser::standardise_keyword(const string& keyword) const -{ +string +KeyParser::standardise_keyword(const string& keyword) const { return standardise_interfile_keyword(keyword); } // KT 07/10/2002 moved here from Line -string -KeyParser::get_keyword(const string& line) const -{ - // keyword stops at either := or an index [] +string +KeyParser::get_keyword(const string& line) const { + // keyword stops at either := or an index [] // TODO should check that = follows : to allow keywords with colons in there - const string::size_type eok = line.find_first_of(":[",0); - return line.substr(0,eok); + const string::size_type eok = line.find_first_of(":[", 0); + return line.substr(0, eok); } -map_element* KeyParser::find_in_keymap(const string& keyword) -{ - for (Keymap::iterator iter = kmap.begin(); - iter != kmap.end(); - ++iter) - { - if (iter->first == keyword) - return &(iter->second); +map_element* +KeyParser::find_in_keymap(const string& keyword) { + for (Keymap::iterator iter = kmap.begin(); iter != kmap.end(); ++iter) { + if (iter->first == keyword) + return &(iter->second); } // it wasn't there return 0; } bool -KeyParser::remove_key(const string& keyword) -{ - for (Keymap::iterator iter = kmap.begin(); - iter != kmap.end(); - ++iter) - { - if (iter->first == keyword) - { - kmap.erase(iter); - return true; - } +KeyParser::remove_key(const string& keyword) { + for (Keymap::iterator iter = kmap.begin(); iter != kmap.end(); ++iter) { + if (iter->first == keyword) { + kmap.erase(iter); + return true; + } } // it wasn't there return false; } -void -KeyParser::add_in_keymap(const string& keyword, const map_element& new_element) -{ +void +KeyParser::add_in_keymap(const string& keyword, const map_element& new_element) { const string standardised_keyword = standardise_keyword(keyword); - map_element * elem_ptr = find_in_keymap(standardised_keyword); - if (elem_ptr != 0) - { + map_element* elem_ptr = find_in_keymap(standardised_keyword); + if (elem_ptr != 0) { warning(boost::format("KeyParser: keyword '%s' already registered for parsing, overwriting previous value") % keyword); *elem_ptr = new_element; - } - else - kmap.push_back(pair(standardised_keyword, new_element)); + } else + kmap.push_back(pair(standardised_keyword, new_element)); } void -KeyParser::add_key(const string& keyword, float * variable) - { - add_key(keyword, KeyArgument::FLOAT, variable); - } +KeyParser::add_key(const string& keyword, float* variable) { + add_key(keyword, KeyArgument::FLOAT, variable); +} void -KeyParser::add_vectorised_key(const string& keyword, vector * variable) - { - add_key(keyword, KeyArgument::FLOAT, variable, 1); - } +KeyParser::add_vectorised_key(const string& keyword, vector* variable) { + add_key(keyword, KeyArgument::FLOAT, variable, 1); +} void -KeyParser::add_key(const string& keyword, double * variable) - { - add_key(keyword, KeyArgument::DOUBLE, variable); - } +KeyParser::add_key(const string& keyword, double* variable) { + add_key(keyword, KeyArgument::DOUBLE, variable); +} void -KeyParser::add_vectorised_key(const string& keyword, vector * variable) - { - add_key(keyword, KeyArgument::DOUBLE, variable, 1); - } +KeyParser::add_vectorised_key(const string& keyword, vector* variable) { + add_key(keyword, KeyArgument::DOUBLE, variable, 1); +} void -KeyParser::add_vectorised_key(const string& keyword, vector > * variable) - { - add_key(keyword, KeyArgument::LIST_OF_DOUBLES, variable, 1); - } +KeyParser::add_vectorised_key(const string& keyword, vector>* variable) { + add_key(keyword, KeyArgument::LIST_OF_DOUBLES, variable, 1); +} void -KeyParser::add_key(const string& keyword, int * variable) - { - add_key(keyword, KeyArgument::INT, variable); - } +KeyParser::add_key(const string& keyword, int* variable) { + add_key(keyword, KeyArgument::INT, variable); +} void -KeyParser::add_key(const string& keyword, vector * variable) - { - add_key(keyword, KeyArgument::LIST_OF_INTS, variable); - } +KeyParser::add_key(const string& keyword, vector* variable) { + add_key(keyword, KeyArgument::LIST_OF_INTS, variable); +} void -KeyParser::add_vectorised_key(const string& keyword, vector * variable) - { - add_key(keyword, KeyArgument::INT, variable, 1); - } +KeyParser::add_vectorised_key(const string& keyword, vector* variable) { + add_key(keyword, KeyArgument::INT, variable, 1); +} void -KeyParser::add_vectorised_key(const string& keyword, vector > * variable) - { - add_key(keyword, KeyArgument::LIST_OF_INTS, variable, 1); - } +KeyParser::add_vectorised_key(const string& keyword, vector>* variable) { + add_key(keyword, KeyArgument::LIST_OF_INTS, variable, 1); +} void -KeyParser::add_key(const string& keyword, unsigned int * variable) - { - add_key(keyword, KeyArgument::UINT, variable); - } +KeyParser::add_key(const string& keyword, unsigned int* variable) { + add_key(keyword, KeyArgument::UINT, variable); +} void -KeyParser::add_vectorised_key(const string& keyword, vector * variable) - { - add_key(keyword, KeyArgument::UINT, variable, 1); - } +KeyParser::add_vectorised_key(const string& keyword, vector* variable) { + add_key(keyword, KeyArgument::UINT, variable, 1); +} void -KeyParser::add_key(const string& keyword, long int * variable) - { - add_key(keyword, KeyArgument::LONG, variable); - } +KeyParser::add_key(const string& keyword, long int* variable) { + add_key(keyword, KeyArgument::LONG, variable); +} void -KeyParser::add_key(const string& keyword, unsigned long * variable) - { - add_key(keyword, KeyArgument::ULONG, variable); - } +KeyParser::add_key(const string& keyword, unsigned long* variable) { + add_key(keyword, KeyArgument::ULONG, variable); +} void -KeyParser::add_vectorised_key(const string& keyword, vector * variable) - { - add_key(keyword, KeyArgument::ULONG, variable, 1); - } +KeyParser::add_vectorised_key(const string& keyword, vector* variable) { + add_key(keyword, KeyArgument::ULONG, variable, 1); +} void -KeyParser::add_key(const string& keyword, bool * variable) - { - add_key(keyword, KeyArgument::BOOL, variable); - } +KeyParser::add_key(const string& keyword, bool* variable) { + add_key(keyword, KeyArgument::BOOL, variable); +} void -KeyParser::add_key(const string& keyword, vector* variable) - { - add_key(keyword, KeyArgument::LIST_OF_DOUBLES, variable); - } +KeyParser::add_key(const string& keyword, vector* variable) { + add_key(keyword, KeyArgument::LIST_OF_DOUBLES, variable); +} void -KeyParser::add_key(const string& keyword, vector* variable) - { - add_key(keyword, KeyArgument::LIST_OF_ASCII, variable); - } +KeyParser::add_key(const string& keyword, vector* variable) { + add_key(keyword, KeyArgument::LIST_OF_ASCII, variable); +} void -KeyParser::add_key(const string& keyword, Array<2,float>* variable) - { - add_key(keyword, KeyArgument::ARRAY2D_OF_FLOATS, variable); - } +KeyParser::add_key(const string& keyword, Array<2, float>* variable) { + add_key(keyword, KeyArgument::ARRAY2D_OF_FLOATS, variable); +} void -KeyParser::add_key(const string& keyword, Array<3,float>* variable) - { - add_key(keyword, KeyArgument::ARRAY3D_OF_FLOATS, variable); - } +KeyParser::add_key(const string& keyword, Array<3, float>* variable) { + add_key(keyword, KeyArgument::ARRAY3D_OF_FLOATS, variable); +} void -KeyParser::add_key(const string& keyword, BasicCoordinate<3,float>* variable) - { - add_key(keyword, KeyArgument::BASICCOORDINATE3D_OF_FLOATS, variable); - } +KeyParser::add_key(const string& keyword, BasicCoordinate<3, float>* variable) { + add_key(keyword, KeyArgument::BASICCOORDINATE3D_OF_FLOATS, variable); +} void -KeyParser::add_key(const string& keyword, BasicCoordinate<3,Array<3,float> >* variable) - { - add_key(keyword, KeyArgument::BASICCOORDINATE3D_OF_ARRAY3D_OF_FLOATS, variable); - } +KeyParser::add_key(const string& keyword, BasicCoordinate<3, Array<3, float>>* variable) { + add_key(keyword, KeyArgument::BASICCOORDINATE3D_OF_ARRAY3D_OF_FLOATS, variable); +} void -KeyParser::add_key(const string& keyword, string * variable) - { - add_key(keyword, KeyArgument::ASCII, variable); - } - +KeyParser::add_key(const string& keyword, string* variable) { + add_key(keyword, KeyArgument::ASCII, variable); +} void -KeyParser::add_vectorised_key(const string& keyword, vector * variable) - { - add_key(keyword, KeyArgument::ASCII, variable, 1); - } +KeyParser::add_vectorised_key(const string& keyword, vector* variable) { + add_key(keyword, KeyArgument::ASCII, variable, 1); +} void -KeyParser::add_key(const string& keyword, int * variable, - const ASCIIlist_type * list_of_values_ptr) - { - add_key(keyword, KeyArgument::ASCIIlist, variable, list_of_values_ptr); - } - +KeyParser::add_key(const string& keyword, int* variable, const ASCIIlist_type* list_of_values_ptr) { + add_key(keyword, KeyArgument::ASCIIlist, variable, list_of_values_ptr); +} + void -KeyParser::ignore_key(const string& keyword) - { - add_key(keyword, KeyArgument::NONE, &KeyParser::do_nothing); - } +KeyParser::ignore_key(const string& keyword) { + add_key(keyword, KeyArgument::NONE, &KeyParser::do_nothing); +} void -KeyParser::add_start_key(const string& keyword) - { - add_key(keyword, KeyArgument::NONE, &KeyParser::start_parsing); - } +KeyParser::add_start_key(const string& keyword) { + add_key(keyword, KeyArgument::NONE, &KeyParser::start_parsing); +} void -KeyParser::add_stop_key(const string& keyword) - { - add_key(keyword, KeyArgument::NONE, &KeyParser::stop_parsing); - } - - - +KeyParser::add_stop_key(const string& keyword) { + add_key(keyword, KeyArgument::NONE, &KeyParser::stop_parsing); +} -void KeyParser::add_key(const string& keyword, - KeyArgument::type t, - KeywordProcessor function, - void* variable, - const ASCIIlist_type * const list_of_values) -{ +void +KeyParser::add_key(const string& keyword, KeyArgument::type t, KeywordProcessor function, void* variable, + const ASCIIlist_type* const list_of_values) { add_in_keymap(keyword, map_element(t, function, variable, 0, list_of_values)); } -void KeyParser::add_key(const string& keyword, - KeyArgument::type t, - KeywordProcessor function, - void* variable, - const int vectorised_key_level, - const ASCIIlist_type * const list_of_values) -{ +void +KeyParser::add_key(const string& keyword, KeyArgument::type t, KeywordProcessor function, void* variable, + const int vectorised_key_level, const ASCIIlist_type* const list_of_values) { add_in_keymap(keyword, map_element(t, function, variable, vectorised_key_level, list_of_values)); } -void KeyParser::add_key(const string& keyword, - KeyArgument::type t, - void* variable, - const ASCIIlist_type * const list_of_values) -{ +void +KeyParser::add_key(const string& keyword, KeyArgument::type t, void* variable, const ASCIIlist_type* const list_of_values) { add_in_keymap(keyword, map_element(t, &KeyParser::set_variable, variable, 0, list_of_values)); } -void KeyParser::add_key(const string& keyword, - KeyArgument::type t, - void* variable, - const int vectorised_key_level, - const ASCIIlist_type * const list_of_values) -{ +void +KeyParser::add_key(const string& keyword, KeyArgument::type t, void* variable, const int vectorised_key_level, + const ASCIIlist_type* const list_of_values) { add_in_keymap(keyword, map_element(t, &KeyParser::set_variable, variable, vectorised_key_level, list_of_values)); } - void -KeyParser::print_keywords_to_stream(ostream& out) const -{ - for (Keymap::const_iterator key = kmap.begin(); key != kmap.end(); ++key) - { +KeyParser::print_keywords_to_stream(ostream& out) const { + for (Keymap::const_iterator key = kmap.begin(); key != kmap.end(); ++key) { out << key->first << '\n'; } out << endl; } - -Succeeded KeyParser::parse_header(const bool write_warning) -{ - - if (read_and_parse_line(false) == Succeeded::yes) +Succeeded +KeyParser::parse_header(const bool write_warning) { + + if (read_and_parse_line(false) == Succeeded::yes) process_key(); - if (status != parsing) - { + if (status != parsing) { // something's wrong. We're finding data, but we're not supposed to be // parsing. We'll exit with an error, but first write a warning. // The warning will say that we miss the "start parsing" keyword (if we can find it in the map) // find starting keyword string start_keyword; - for (Keymap::const_iterator i=kmap.begin(); i!= kmap.end(); ++i) - { + for (Keymap::const_iterator i = kmap.begin(); i != kmap.end(); ++i) { if (i->second.p_object_member == &KeyParser::start_parsing) - start_keyword = i->first; - } - if (start_keyword.length()>0) - { - warning("KeyParser error: required first keyword \"%s\" not found\n", - start_keyword.c_str()); + start_keyword = i->first; } - else - { - // there doesn't seem to be a start_parsing keyword, so we cannot include it - // in the warning. (it could be a side-effect of another key, so we're + if (start_keyword.length() > 0) { + warning("KeyParser error: required first keyword \"%s\" not found\n", start_keyword.c_str()); + } else { + // there doesn't seem to be a start_parsing keyword, so we cannot include it + // in the warning. (it could be a side-effect of another key, so we're // not sure if the map is correct or not) warning("KeyParser error: data found, but KeyParser status is \"not parsing\". Keymap possibly incorrect"); } - return Succeeded::no; + return Succeeded::no; } - while(status==parsing) - { - if(read_and_parse_line(write_warning) == Succeeded::yes) - process_key(); - if (input->eof()) { - status = end_parsing; - } + while (status == parsing) { + if (read_and_parse_line(write_warning) == Succeeded::yes) + process_key(); + if (input->eof()) { + status = end_parsing; + } } - - + return Succeeded::yes; - -} +} -Succeeded KeyParser::read_and_parse_line(const bool write_warning) -{ - string line; +Succeeded +KeyParser::read_and_parse_line(const bool write_warning) { + string line; // we keep reading a line until it's either non-empty, or we're at the end of the input - while (true) - { - if (!input->good()) - { - warning("KeyParser warning: early EOF or bad file"); - stop_parsing(); - return Succeeded::no; - } - - read_line(*input, line); - // check if only white-space, if not, get out of the loop to continue - std::size_t pos = line.find_first_not_of(" \t"); - if ( pos != string::npos) - break; - // check if empty line - if (line.size() == 0) - break; + while (true) { + if (!input->good()) { + warning("KeyParser warning: early EOF or bad file"); + stop_parsing(); + return Succeeded::no; } + read_line(*input, line); + // check if only white-space, if not, get out of the loop to continue + std::size_t pos = line.find_first_not_of(" \t"); + if (pos != string::npos) + break; + // check if empty line + if (line.size() == 0) + break; + } + // gets keyword - keyword=standardise_keyword(get_keyword(line)); + keyword = standardise_keyword(get_keyword(line)); return parse_value_in_line(line, write_warning); } - // functions that get arbitrary type parameters from a string (after '=') // unfortunately, the string type needs special case as istream::operator>> stops a string at white space // we also do special things for vectors // they all return Succeeded::yes when there was a parameter template -static -Succeeded -get_param_from_string(T& param, const string& s) -{ - const string::size_type cp=s.find('=',0); - if(cp==string::npos) +static Succeeded +get_param_from_string(T& param, const string& s) { + const string::size_type cp = s.find('=', 0); + if (cp == string::npos) return Succeeded::no; - istrstream str(s.c_str()+cp+1); + istrstream str(s.c_str() + cp + 1); str >> param; return str.fail() ? Succeeded::no : Succeeded::yes; } template <> Succeeded -get_param_from_string(string& param, const string& s) -{ - const string::size_type cp = s.find('=',0); - if(cp!=string::npos) - { +get_param_from_string(string& param, const string& s) { + const string::size_type cp = s.find('=', 0); + if (cp != string::npos) { // skip starting white space - const string::size_type sok=s.find_first_not_of(" \t",cp+1); // KT 07/10/2002 now also skips tabs - if(sok!=string::npos) - { + const string::size_type sok = s.find_first_not_of(" \t", cp + 1); // KT 07/10/2002 now also skips tabs + if (sok != string::npos) { // strip trailing white space - const string::size_type eok=s.find_last_not_of(" \t",s.length()); - param=s.substr(sok,eok-sok+1); + const string::size_type eok = s.find_last_not_of(" \t", s.length()); + param = s.substr(sok, eok - sok + 1); return Succeeded::yes; } } @@ -697,23 +560,18 @@ get_param_from_string(string& param, const string& s) // this is currently only used for the matrix_size keywords in InterfileHeader. template -static -Succeeded -get_vparam_from_string(vector& param, const string& s) -{ - const string::size_type cp = s.find('=',0); - if(cp!=string::npos) - { +static Succeeded +get_vparam_from_string(vector& param, const string& s) { + const string::size_type cp = s.find('=', 0); + if (cp != string::npos) { // skip starting white space - const string::size_type start=s.find_first_not_of(" \t",cp+1); // KT 07/10/2002 now also skips tabs - if(start!=string::npos) - { - istrstream str(s.c_str()+start); - + const string::size_type start = s.find_first_not_of(" \t", cp + 1); // KT 07/10/2002 now also skips tabs + if (start != string::npos) { + istrstream str(s.c_str() + start); + if (s[start] == '{') str >> param; - else - { + else { param.resize(1); str >> param[0]; } @@ -724,25 +582,20 @@ get_vparam_from_string(vector& param, const string& s) } template -static -Succeeded -get_vparam_from_string(VectorWithOffset& param, const string& s) -{ - const string::size_type cp = s.find('=',0); - if(cp!=string::npos) - { +static Succeeded +get_vparam_from_string(VectorWithOffset& param, const string& s) { + const string::size_type cp = s.find('=', 0); + if (cp != string::npos) { // skip starting white space - const string::size_type start=s.find_first_not_of(" \t",cp+1); // KT 07/10/2002 now also skips tabs - if(start!=string::npos) - { - istrstream str(s.c_str()+start); - + const string::size_type start = s.find_first_not_of(" \t", cp + 1); // KT 07/10/2002 now also skips tabs + if (start != string::npos) { + istrstream str(s.c_str() + start); + if (s[start] == '{') str >> param; - else - { + else { param = VectorWithOffset(); // TODO will NOT work with multi-dimensional arrays - param.grow(0,0); + param.grow(0, 0); str >> param[0]; } return str.fail() ? Succeeded::no : Succeeded::yes; @@ -754,47 +607,36 @@ get_vparam_from_string(VectorWithOffset& param, const string& s) // vectors of strings are also special as we need to split the string up if there are commas template <> Succeeded -get_vparam_from_string(vector& param, const string& s) -{ - string::size_type cp = s.find('=',0); - if(cp!=string::npos) - { +get_vparam_from_string(vector& param, const string& s) { + string::size_type cp = s.find('=', 0); + if (cp != string::npos) { // skip starting white space - const string::size_type start=s.find_first_not_of(" \t",cp+1); // KT 07/10/2002 now also skips tabs - if(start!=string::npos) - { - if (s[start] == '{') - { - bool end=false; - cp = start+1; - while (!end) - { - cp=s.find_first_not_of("},",cp); - cp=s.find_first_not_of(" \t",cp); - - if(cp==string::npos) - { - end=true; - } - else - { - string::size_type eop=s.find_first_of(",}",cp); - if(eop==string::npos) - { - end=true; - eop=s.length(); + const string::size_type start = s.find_first_not_of(" \t", cp + 1); // KT 07/10/2002 now also skips tabs + if (start != string::npos) { + if (s[start] == '{') { + bool end = false; + cp = start + 1; + while (!end) { + cp = s.find_first_not_of("},", cp); + cp = s.find_first_not_of(" \t", cp); + + if (cp == string::npos) { + end = true; + } else { + string::size_type eop = s.find_first_of(",}", cp); + if (eop == string::npos) { + end = true; + eop = s.length(); } // trim ending white space - const string::size_type eop2 = s.find_last_not_of(" \t",eop); - param.push_back(s.substr(cp,eop2-cp)); - cp=eop+1; + const string::size_type eop2 = s.find_last_not_of(" \t", eop); + param.push_back(s.substr(cp, eop2 - cp)); + cp = eop + 1; } } - } - else - { - param.resize(1); - param[0] = s.substr(start, s.find_last_not_of(" \t",s.size())); + } else { + param.resize(1); + param[0] = s.substr(start, s.find_last_not_of(" \t", s.size())); } return Succeeded::yes; } @@ -803,26 +645,23 @@ get_vparam_from_string(vector& param, const string& s) } // function that finds the current_index. work to do here! -static int get_index(const string& line) -{ +static int +get_index(const string& line) { // we take 0 as a default value for the index - int in=0; + int in = 0; // make sure that the index is part of the key (i.e. before :=) - const string::size_type cp=line.find_first_of(":[",0); - if(cp!=string::npos && line[cp] == '[') - { - const string::size_type sok=cp+1; - const string::size_type eok=line.find_first_of(']',cp); + const string::size_type cp = line.find_first_of(":[", 0); + if (cp != string::npos && line[cp] == '[') { + const string::size_type sok = cp + 1; + const string::size_type eok = line.find_first_of(']', cp); // check if closing bracket really there - if (eok == string::npos) - { + if (eok == string::npos) { // TODO do something more graceful - warning("Interfile warning: invalid vectorised key in line \n'%s'.\n%s", - line.c_str(), - "Assuming this is not a vectorised key."); + warning("Interfile warning: invalid vectorised key in line \n'%s'.\n%s", line.c_str(), + "Assuming this is not a vectorised key."); return 0; } - in=atoi(line.substr(sok,eok-sok).c_str()); + in = atoi(line.substr(sok, eok - sok).c_str()); } return in; } @@ -831,113 +670,93 @@ static int get_index(const string& line) // this class comes from "Modern C++ Design" by Andrei Alexandrescu template -struct Type2Type -{ +struct Type2Type { typedef T type; }; template -static -Succeeded -get_any_param_from_string(boost::any& parameter, Type2Type, const string& s) -{ +static Succeeded +get_any_param_from_string(boost::any& parameter, Type2Type, const string& s) { parameter = T(); // note: don't use cast to reference as it might break VC 6.0 - return - get_param_from_string(*boost::any_cast(¶meter), s); + return get_param_from_string(*boost::any_cast(¶meter), s); } template -static -Succeeded -get_any_vparam_from_string(boost::any& parameter, Type2Type, const string& s) -{ +static Succeeded +get_any_vparam_from_string(boost::any& parameter, Type2Type, const string& s) { parameter = T(); // note: don't use cast to reference as it might break VC 6.0 - return - get_vparam_from_string(*boost::any_cast(¶meter), s); + return get_vparam_from_string(*boost::any_cast(¶meter), s); } -Succeeded KeyParser::parse_value_in_line(const string& line, const bool write_warning) -{ +Succeeded +KeyParser::parse_value_in_line(const string& line, const bool write_warning) { // KT 07/10/2002 use return value of get_param to detect if a value was present at all - current_index=get_index(line); - + current_index = get_index(line); + // maps keyword to appropriate map_element (sets current) - if(map_keyword(keyword)==Succeeded::yes) - { - switch(current->type) // depending on the par_type, gets the correct value from the line - { // and sets the right temporary variable - case KeyArgument::NONE : + if (map_keyword(keyword) == Succeeded::yes) { + switch (current->type) // depending on the par_type, gets the correct value from the line + { // and sets the right temporary variable + case KeyArgument::NONE: keyword_has_a_value = false; break; - case KeyArgument::ASCII : - case KeyArgument::ASCIIlist : + case KeyArgument::ASCII: + case KeyArgument::ASCIIlist: // KT 07/02/2001 new case KeyArgument::PARSINGOBJECT: case KeyArgument::SHARED_PARSINGOBJECT: - keyword_has_a_value = - get_any_param_from_string(this->parameter, Type2Type(), line) == Succeeded::yes; + keyword_has_a_value = get_any_param_from_string(this->parameter, Type2Type(), line) == Succeeded::yes; break; - case KeyArgument::INT : - case KeyArgument::BOOL : - keyword_has_a_value = - get_any_param_from_string(this->parameter, Type2Type(), line) == Succeeded::yes; + case KeyArgument::INT: + case KeyArgument::BOOL: + keyword_has_a_value = get_any_param_from_string(this->parameter, Type2Type(), line) == Succeeded::yes; break; - case KeyArgument::UINT : - keyword_has_a_value = - get_any_param_from_string(this->parameter, Type2Type(), line) == Succeeded::yes; + case KeyArgument::UINT: + keyword_has_a_value = get_any_param_from_string(this->parameter, Type2Type(), line) == Succeeded::yes; break; - case KeyArgument::ULONG : - keyword_has_a_value = - get_any_param_from_string(this->parameter, Type2Type(), line) == Succeeded::yes; - break; - case KeyArgument::LONG : - keyword_has_a_value = - get_any_param_from_string(this->parameter, Type2Type(), line) == Succeeded::yes; + case KeyArgument::ULONG: + keyword_has_a_value = get_any_param_from_string(this->parameter, Type2Type(), line) == Succeeded::yes; break; - case KeyArgument::DOUBLE : - keyword_has_a_value = - get_any_param_from_string(this->parameter, Type2Type(), line) == Succeeded::yes; + case KeyArgument::LONG: + keyword_has_a_value = get_any_param_from_string(this->parameter, Type2Type(), line) == Succeeded::yes; break; - case KeyArgument::FLOAT : - keyword_has_a_value = - get_any_param_from_string(this->parameter, Type2Type(), line) == Succeeded::yes; + case KeyArgument::DOUBLE: + keyword_has_a_value = get_any_param_from_string(this->parameter, Type2Type(), line) == Succeeded::yes; break; - case KeyArgument::LIST_OF_INTS : - keyword_has_a_value = - get_any_vparam_from_string(this->parameter, Type2Type >(), line) == Succeeded::yes; + case KeyArgument::FLOAT: + keyword_has_a_value = get_any_param_from_string(this->parameter, Type2Type(), line) == Succeeded::yes; break; - case KeyArgument::LIST_OF_DOUBLES : - keyword_has_a_value = - get_any_vparam_from_string(this->parameter, Type2Type >(), line) == Succeeded::yes; + case KeyArgument::LIST_OF_INTS: + keyword_has_a_value = get_any_vparam_from_string(this->parameter, Type2Type>(), line) == Succeeded::yes; break; - case KeyArgument::LIST_OF_ASCII : + case KeyArgument::LIST_OF_DOUBLES: + keyword_has_a_value = get_any_vparam_from_string(this->parameter, Type2Type>(), line) == Succeeded::yes; + break; + case KeyArgument::LIST_OF_ASCII: // TODO enforce {} by writing get_param_from_string for vector - keyword_has_a_value = - get_any_vparam_from_string(this->parameter, Type2Type >(), line) == Succeeded::yes; + keyword_has_a_value = + get_any_vparam_from_string(this->parameter, Type2Type>(), line) == Succeeded::yes; break; case KeyArgument::ARRAY2D_OF_FLOATS: - keyword_has_a_value = - get_any_param_from_string(this->parameter, Type2Type >(), line) == Succeeded::yes; + keyword_has_a_value = get_any_param_from_string(this->parameter, Type2Type>(), line) == Succeeded::yes; break; case KeyArgument::ARRAY3D_OF_FLOATS: - keyword_has_a_value = - get_any_param_from_string(this->parameter, Type2Type >(), line) == Succeeded::yes; + keyword_has_a_value = get_any_param_from_string(this->parameter, Type2Type>(), line) == Succeeded::yes; break; case KeyArgument::BASICCOORDINATE3D_OF_FLOATS: - keyword_has_a_value = - get_any_param_from_string(this->parameter, Type2Type >(), line) == Succeeded::yes; + keyword_has_a_value = + get_any_param_from_string(this->parameter, Type2Type>(), line) == Succeeded::yes; break; case KeyArgument::BASICCOORDINATE3D_OF_ARRAY3D_OF_FLOATS: - keyword_has_a_value = - get_any_param_from_string(this->parameter, Type2Type > >(), line) == Succeeded::yes; + keyword_has_a_value = + get_any_param_from_string(this->parameter, Type2Type>>(), line) == Succeeded::yes; break; - default : + default: // KT 07/10/2002 now exit with error - error ("KeyParser internal error: keyword '%s' has unsupported type of parameters\n", - keyword.c_str()); - return Succeeded::no; // just a line to avoid compiler warnings + error("KeyParser internal error: keyword '%s' has unsupported type of parameters\n", keyword.c_str()); + return Succeeded::no; // just a line to avoid compiler warnings } return Succeeded::yes; } @@ -950,443 +769,406 @@ Succeeded KeyParser::parse_value_in_line(const string& line, const bool write_wa return Succeeded::no; } -void KeyParser::start_parsing() -{ - status=parsing; +void +KeyParser::start_parsing() { + status = parsing; } -void KeyParser::stop_parsing() -{ - status=end_parsing; +void +KeyParser::stop_parsing() { + status = end_parsing; } // KT 07/02/2001 new -void KeyParser::set_parsing_object() -{ +void +KeyParser::set_parsing_object() { // KT 07/10/2002 new if (!keyword_has_a_value) return; // TODO this does not handle the vectorised key convention - + // current_index is set to 0 when there was no index - if(current_index!=0) + if (current_index != 0) error("KeyParser::PARSINGOBJECT can't handle vectorised keys yet\n"); const std::string& par_ascii = *boost::any_cast(&this->parameter); - *reinterpret_cast(current->p_object_variable) = - (*current->parser)(input, par_ascii); + *reinterpret_cast(current->p_object_variable) = (*current->parser)(input, par_ascii); } - // KT 20/08/2001 new -void KeyParser::set_shared_parsing_object() -{ +void +KeyParser::set_shared_parsing_object() { // KT 07/10/2002 new if (!keyword_has_a_value) return; - + // TODO this does not handle the vectorised key convention - + // current_index is set to 0 when there was no index - if(current_index!=0) + if (current_index != 0) error("KeyParser::SHARED_PARSINGOBJECT can't handle vectorised keys yet"); const std::string& par_ascii = *boost::any_cast(&this->parameter); - reinterpret_cast *>(current->p_object_variable)-> - reset((*current->parser)(input, par_ascii)); + reinterpret_cast*>(current->p_object_variable)->reset((*current->parser)(input, par_ascii)); } // local function to be used in set_variable below template -void static -assign_to_list(T1& mylist, const T2& value, const int current_index, - const string& keyword) -{ - if(mylist.size() < static_cast(current_index)) - { - error("KeyParser: the list corresponding to the keyword \"%s\" has to be resized " - "to size %d. This means you have a problem in the keyword values.", - keyword.c_str(), current_index); - // mylist.resize(current_index); - } - mylist[current_index-1] = value; +void static assign_to_list(T1& mylist, const T2& value, const int current_index, const string& keyword) { + if (mylist.size() < static_cast(current_index)) { + error("KeyParser: the list corresponding to the keyword \"%s\" has to be resized " + "to size %d. This means you have a problem in the keyword values.", + keyword.c_str(), current_index); + // mylist.resize(current_index); + } + mylist[current_index - 1] = value; } -void KeyParser::set_variable() -{ +void +KeyParser::set_variable() { if (!keyword_has_a_value) return; // TODO this does not handle the vectorised key convention - + // current_index is set to 0 when there was no index - if(!current_index) - { - if (current->vectorised_key_level>0) - error(boost::format("Error parsing: expected a vectorised key as in \"%1%[1]\", but no bracket found") % keyword); - - switch(current->type) - { -#define KP_case_assign(KeyArgumentValue, type) \ - case KeyArgumentValue : \ - *reinterpret_cast(current->p_object_variable) = \ - * boost::any_cast(&this->parameter); break - - case KeyArgument::BOOL : - { - const int par_int = * boost::any_cast(¶meter); - if (par_int !=0 && par_int != 1) - warning("KeyParser: keyword %s expects a bool value which should be 0 or 1\n" - " (actual value is %d). A non-zero value will be assumed to mean 'true'\n", - keyword.c_str(), par_int); - bool* p_bool=(bool*)current->p_object_variable; // performs the required casting - *p_bool=par_int != 0; - break; - } - KP_case_assign(KeyArgument::INT, int); - KP_case_assign(KeyArgument::UINT, unsigned int); - KP_case_assign(KeyArgument::ULONG,unsigned long); - KP_case_assign(KeyArgument::LONG,long); - KP_case_assign(KeyArgument::DOUBLE,double); - KP_case_assign(KeyArgument::FLOAT,float); - KP_case_assign(KeyArgument::ASCII, std::string); - - case KeyArgument::ASCIIlist : - { - const std::string& par_ascii = *boost::any_cast(&this->parameter); - const int index = - find_in_ASCIIlist(par_ascii, *(current->p_object_list_of_values)); - *((int *)current->p_object_variable) = index; - if (index == -1) - { - // it was not in the list - // TODO we should use warning() instead - cerr << "KeyParser warning : value of keyword \"" - << keyword << "\" is \"" - << par_ascii - << "\"\n\tshould have been one of:"; - for (unsigned int i=0; ip_object_list_of_values->size(); i++) - cerr << "\n\t" << (*current->p_object_list_of_values)[i]; - cerr << '\n' << endl; - } - break; - } - KP_case_assign(KeyArgument::LIST_OF_INTS, IntVect); - KP_case_assign(KeyArgument::LIST_OF_DOUBLES, DoubleVect); - // sigh... macro expansion fails of type contain commas.... - // Work-around: use typedefs. - typedef Array<2,float> KP_array2d; - typedef Array<3,float> KP_array3d; - typedef BasicCoordinate<3,float> KP_coord; - typedef BasicCoordinate<3,Array<3,float> > KP_coord_array3d; - KP_case_assign(KeyArgument::ARRAY2D_OF_FLOATS, KP_array2d); - KP_case_assign(KeyArgument::ARRAY3D_OF_FLOATS, KP_array3d); - KP_case_assign(KeyArgument::BASICCOORDINATE3D_OF_FLOATS, KP_coord); - KP_case_assign(KeyArgument::BASICCOORDINATE3D_OF_ARRAY3D_OF_FLOATS,KP_coord_array3d); - KP_case_assign(KeyArgument::LIST_OF_ASCII, std::vector); - default : - warning("KeyParser error: unknown type. Implementation error"); - break; - } -#undef KP_case_assign + if (!current_index) { + if (current->vectorised_key_level > 0) + error(boost::format("Error parsing: expected a vectorised key as in \"%1%[1]\", but no bracket found") % keyword); + + switch (current->type) { +#define KP_case_assign(KeyArgumentValue, type) \ + case KeyArgumentValue: \ + *reinterpret_cast(current->p_object_variable) = *boost::any_cast(&this->parameter); \ + break + + case KeyArgument::BOOL: { + const int par_int = *boost::any_cast(¶meter); + if (par_int != 0 && par_int != 1) + warning("KeyParser: keyword %s expects a bool value which should be 0 or 1\n" + " (actual value is %d). A non-zero value will be assumed to mean 'true'\n", + keyword.c_str(), par_int); + bool* p_bool = (bool*)current->p_object_variable; // performs the required casting + *p_bool = par_int != 0; + break; + } + KP_case_assign(KeyArgument::INT, int); + KP_case_assign(KeyArgument::UINT, unsigned int); + KP_case_assign(KeyArgument::ULONG, unsigned long); + KP_case_assign(KeyArgument::LONG, long); + KP_case_assign(KeyArgument::DOUBLE, double); + KP_case_assign(KeyArgument::FLOAT, float); + KP_case_assign(KeyArgument::ASCII, std::string); + + case KeyArgument::ASCIIlist: { + const std::string& par_ascii = *boost::any_cast(&this->parameter); + const int index = find_in_ASCIIlist(par_ascii, *(current->p_object_list_of_values)); + *((int*)current->p_object_variable) = index; + if (index == -1) { + // it was not in the list + // TODO we should use warning() instead + cerr << "KeyParser warning : value of keyword \"" << keyword << "\" is \"" << par_ascii + << "\"\n\tshould have been one of:"; + for (unsigned int i = 0; i < current->p_object_list_of_values->size(); i++) + cerr << "\n\t" << (*current->p_object_list_of_values)[i]; + cerr << '\n' << endl; + } + break; + } + KP_case_assign(KeyArgument::LIST_OF_INTS, IntVect); + KP_case_assign(KeyArgument::LIST_OF_DOUBLES, DoubleVect); + // sigh... macro expansion fails of type contain commas.... + // Work-around: use typedefs. + typedef Array<2, float> KP_array2d; + typedef Array<3, float> KP_array3d; + typedef BasicCoordinate<3, float> KP_coord; + typedef BasicCoordinate<3, Array<3, float>> KP_coord_array3d; + KP_case_assign(KeyArgument::ARRAY2D_OF_FLOATS, KP_array2d); + KP_case_assign(KeyArgument::ARRAY3D_OF_FLOATS, KP_array3d); + KP_case_assign(KeyArgument::BASICCOORDINATE3D_OF_FLOATS, KP_coord); + KP_case_assign(KeyArgument::BASICCOORDINATE3D_OF_ARRAY3D_OF_FLOATS, KP_coord_array3d); + KP_case_assign(KeyArgument::LIST_OF_ASCII, std::vector); + default: + warning("KeyParser error: unknown type. Implementation error"); + break; } - else // Sets vector elements using current_index - { - if (current->vectorised_key_level==0) - error(boost::format("Error parsing: encountered unexpected \"vectorisation\" of key: \"%1%[%2%]\"") % keyword % current_index); - - switch(current->type) - { -#define KP_case_assign(KeyArgumentValue, type) \ - case KeyArgumentValue : \ - assign_to_list(*reinterpret_cast *>(current->p_object_variable), \ - * boost::any_cast(&this->parameter), current_index, keyword); \ - break - - KP_case_assign(KeyArgument::INT, int); - KP_case_assign(KeyArgument::UINT,unsigned int); - KP_case_assign(KeyArgument::LONG,long); - KP_case_assign(KeyArgument::ULONG,unsigned long); - KP_case_assign(KeyArgument::DOUBLE,double); - KP_case_assign(KeyArgument::FLOAT,float); - KP_case_assign(KeyArgument::ASCII, std::string); - case KeyArgument::ASCIIlist : - { - const std::string& par_ascii = *boost::any_cast(&this->parameter); - const int index_in_asciilist = - find_in_ASCIIlist(par_ascii, *(current->p_object_list_of_values)); - assign_to_list(*(IntVect*)current->p_object_variable, - index_in_asciilist, current_index, keyword); - if (index_in_asciilist == -1) - { - // it was not in the list - // TODO we should use warning() instead - cerr << "KeyParser warning : value of keyword \"" - << keyword << "\" is \"" - << par_ascii - << "\"\n\tshould have been one of:"; - for (unsigned int i=0; ip_object_list_of_values->size(); i++) - cerr << "\n\t" << (*current->p_object_list_of_values)[i]; - } - break; - } - KP_case_assign(KeyArgument::LIST_OF_INTS, IntVect); - KP_case_assign(KeyArgument::LIST_OF_DOUBLES, DoubleVect); - default : - - warning("KeyParser error: unknown type. Implementation error"); - break; - } #undef KP_case_assign + } else // Sets vector elements using current_index + { + if (current->vectorised_key_level == 0) + error(boost::format("Error parsing: encountered unexpected \"vectorisation\" of key: \"%1%[%2%]\"") % keyword % + current_index); + + switch (current->type) { +#define KP_case_assign(KeyArgumentValue, type) \ + case KeyArgumentValue: \ + assign_to_list(*reinterpret_cast*>(current->p_object_variable), *boost::any_cast(&this->parameter), \ + current_index, keyword); \ + break + + KP_case_assign(KeyArgument::INT, int); + KP_case_assign(KeyArgument::UINT, unsigned int); + KP_case_assign(KeyArgument::LONG, long); + KP_case_assign(KeyArgument::ULONG, unsigned long); + KP_case_assign(KeyArgument::DOUBLE, double); + KP_case_assign(KeyArgument::FLOAT, float); + KP_case_assign(KeyArgument::ASCII, std::string); + case KeyArgument::ASCIIlist: { + const std::string& par_ascii = *boost::any_cast(&this->parameter); + const int index_in_asciilist = find_in_ASCIIlist(par_ascii, *(current->p_object_list_of_values)); + assign_to_list(*(IntVect*)current->p_object_variable, index_in_asciilist, current_index, keyword); + if (index_in_asciilist == -1) { + // it was not in the list + // TODO we should use warning() instead + cerr << "KeyParser warning : value of keyword \"" << keyword << "\" is \"" << par_ascii + << "\"\n\tshould have been one of:"; + for (unsigned int i = 0; i < current->p_object_list_of_values->size(); i++) + cerr << "\n\t" << (*current->p_object_list_of_values)[i]; + } + break; + } + KP_case_assign(KeyArgument::LIST_OF_INTS, IntVect); + KP_case_assign(KeyArgument::LIST_OF_DOUBLES, DoubleVect); + default: + + warning("KeyParser error: unknown type. Implementation error"); + break; } +#undef KP_case_assign + } } -int KeyParser::find_in_ASCIIlist(const string& par_ascii, const ASCIIlist_type& list_of_values) -{ +int +KeyParser::find_in_ASCIIlist(const string& par_ascii, const ASCIIlist_type& list_of_values) { { // TODO, once we know for sure type of ASCIIlist_type, we could use STL find() - // TODO it would be more efficient to call standardise_keyword on the + // TODO it would be more efficient to call standardise_keyword on the // list_of_values in add_key() - for (unsigned int i=0; ip_object_member!=0) - { - (this->*(current->p_object_member))(); //calls appropriate member function + if (current->p_object_member != 0) { + (this->*(current->p_object_member))(); // calls appropriate member function } } -namespace detail -{ - /* A local helper function, essentially equivalent to operator<<(ostream&, const T& var). - However, it will insert \ characters in front of end-of-line. - This is used for types for which operator<< can result in multi-line strings. - If this is not fixed, it would mean that the output of parameter_info() is - not immediatelly suitable for parsing back. - Example: suppose there's a keyword that needs a 2d array. If at parsing we have - my array:={{1,2},{3}} - then parameter_info would give - my_array:={{1,2} - , {3} - } - and this would not follow standard syntax. When using the following function - in parameter_info(), the output will be - my_array:={{1,2}\ - , {3}\ - } - */ - template - static void to_stream(ostream& s, const T& var, const char continuation_char = '\\') - { - // we will first write everything to a temporary stringstream - // and then read it back, inserting the backslash +namespace detail { +/* A local helper function, essentially equivalent to operator<<(ostream&, const T& var). + However, it will insert \ characters in front of end-of-line. + This is used for types for which operator<< can result in multi-line strings. + If this is not fixed, it would mean that the output of parameter_info() is + not immediatelly suitable for parsing back. + Example: suppose there's a keyword that needs a 2d array. If at parsing we have + my array:={{1,2},{3}} + then parameter_info would give + my_array:={{1,2} + , {3} + } + and this would not follow standard syntax. When using the following function + in parameter_info(), the output will be + my_array:={{1,2}\ + , {3}\ + } +*/ +template +static void +to_stream(ostream& s, const T& var, const char continuation_char = '\\') { + // we will first write everything to a temporary stringstream + // and then read it back, inserting the backslash #ifdef BOOST_NO_STRINGSTREAM - // dangerous for out-of-range, but 'old-style' ostrstream seems to need this - char str[100000]; - strstream stemp(str, 100000); + // dangerous for out-of-range, but 'old-style' ostrstream seems to need this + char str[100000]; + strstream stemp(str, 100000); #else - std::stringstream stemp; + std::stringstream stemp; #endif - // write to stemp - stemp << var; - - // now read it back, character by character - while (true) - { - char c; - stemp.get(c); - if (!stemp) - break; - if (c == '\n') - s << continuation_char; // insert continuation character - s << c; - } + // write to stemp + stemp << var; + + // now read it back, character by character + while (true) { + char c; + stemp.get(c); + if (!stemp) + break; + if (c == '\n') + s << continuation_char; // insert continuation character + s << c; } } +} // namespace detail -string KeyParser::parameter_info() const -{ +string +KeyParser::parameter_info() const { #ifdef BOOST_NO_STRINGSTREAM - // dangerous for out-of-range, but 'old-style' ostrstream seems to need this - char str[100000]; - ostrstream s(str, 100000); + // dangerous for out-of-range, but 'old-style' ostrstream seems to need this + char str[100000]; + ostrstream s(str, 100000); #else - std::ostringstream s; + std::ostringstream s; #endif - // first find start key - for (Keymap::const_iterator i=kmap.begin(); i!= kmap.end(); ++i) - { - if (i->second.p_object_member == &KeyParser::start_parsing) + // first find start key + for (Keymap::const_iterator i = kmap.begin(); i != kmap.end(); ++i) { + if (i->second.p_object_member == &KeyParser::start_parsing) s << i->first << " :=\n"; - } + } - for (Keymap::const_iterator i=kmap.begin(); i!= kmap.end(); ++i) - { - if (i->second.p_object_member == &KeyParser::start_parsing || - i->second.p_object_member == &KeyParser::stop_parsing) - continue; - - if (i->second.vectorised_key_level > 0) - { - warning("KeyParser: cannot handle vectorised key yet");//TODO - continue; - } - s << i->first << " := "; - switch(i->second.type) - { - // TODO will break with vectorised keys - case KeyArgument::DOUBLE: - s << *reinterpret_cast(i->second.p_object_variable); break; - case KeyArgument::FLOAT: - s << *reinterpret_cast(i->second.p_object_variable); break; - case KeyArgument::INT: - s << *reinterpret_cast(i->second.p_object_variable); break; - case KeyArgument::BOOL: - s << (*reinterpret_cast(i->second.p_object_variable) ? 1 : 0); break; - case KeyArgument::UINT: - s << *reinterpret_cast(i->second.p_object_variable); break; - case KeyArgument::ULONG: - s << *reinterpret_cast(i->second.p_object_variable); break; - case KeyArgument::LONG: - s << *reinterpret_cast(i->second.p_object_variable); break; - case KeyArgument::NONE: - break; - case KeyArgument::ASCII : - s << *reinterpret_cast(i->second.p_object_variable); break; - case KeyArgument::ASCIIlist : - { - const int index = *reinterpret_cast(i->second.p_object_variable); - s << (index == -1 ? - "UNALLOWED VALUE" : - (*i->second.p_object_list_of_values)[index]); - break; - } - case KeyArgument::PARSINGOBJECT: - { - RegisteredObjectBase* parsing_object_ptr = - *reinterpret_cast(i->second.p_object_variable); - if (parsing_object_ptr!=0) - { - s << parsing_object_ptr->get_registered_name() << endl; - s << parsing_object_ptr->parameter_info() << endl; - } - else - s << "None"; - break; - } - case KeyArgument::SHARED_PARSINGOBJECT: - { + for (Keymap::const_iterator i = kmap.begin(); i != kmap.end(); ++i) { + if (i->second.p_object_member == &KeyParser::start_parsing || i->second.p_object_member == &KeyParser::stop_parsing) + continue; + + if (i->second.vectorised_key_level > 0) { + warning("KeyParser: cannot handle vectorised key yet"); // TODO + continue; + } + s << i->first << " := "; + switch (i->second.type) { + // TODO will break with vectorised keys + case KeyArgument::DOUBLE: + s << *reinterpret_cast(i->second.p_object_variable); + break; + case KeyArgument::FLOAT: + s << *reinterpret_cast(i->second.p_object_variable); + break; + case KeyArgument::INT: + s << *reinterpret_cast(i->second.p_object_variable); + break; + case KeyArgument::BOOL: + s << (*reinterpret_cast(i->second.p_object_variable) ? 1 : 0); + break; + case KeyArgument::UINT: + s << *reinterpret_cast(i->second.p_object_variable); + break; + case KeyArgument::ULONG: + s << *reinterpret_cast(i->second.p_object_variable); + break; + case KeyArgument::LONG: + s << *reinterpret_cast(i->second.p_object_variable); + break; + case KeyArgument::NONE: + break; + case KeyArgument::ASCII: + s << *reinterpret_cast(i->second.p_object_variable); + break; + case KeyArgument::ASCIIlist: { + const int index = *reinterpret_cast(i->second.p_object_variable); + s << (index == -1 ? "UNALLOWED VALUE" : (*i->second.p_object_list_of_values)[index]); + break; + } + case KeyArgument::PARSINGOBJECT: { + RegisteredObjectBase* parsing_object_ptr = *reinterpret_cast(i->second.p_object_variable); + if (parsing_object_ptr != 0) { + s << parsing_object_ptr->get_registered_name() << endl; + s << parsing_object_ptr->parameter_info() << endl; + } else + s << "None"; + break; + } + case KeyArgument::SHARED_PARSINGOBJECT: { #if defined(__GNUC__) && __GNUC__ < 3 - s << "COMPILED WITH GNU C++ (prior to version 3.0), CANNOT INSERT VALUE"; + s << "COMPILED WITH GNU C++ (prior to version 3.0), CANNOT INSERT VALUE"; #else - shared_ptr parsing_object_ptr = - (*reinterpret_cast*>(i->second.p_object_variable)); - - if (!is_null_ptr(parsing_object_ptr)) - { - //std::cerr << "\nBefore *parsing_object_ptr" << endl; - //std::cerr << "\ntypename *parsing_object_ptr " << typeid(*parsing_object_ptr).name() <get_registered_name() << endl; - s << parsing_object_ptr->parameter_info(); - } - else - s << "None"; + shared_ptr parsing_object_ptr = + (*reinterpret_cast*>(i->second.p_object_variable)); + + if (!is_null_ptr(parsing_object_ptr)) { + // std::cerr << "\nBefore *parsing_object_ptr" << endl; + // std::cerr << "\ntypename *parsing_object_ptr " << typeid(*parsing_object_ptr).name() <get_registered_name() << endl; + s << parsing_object_ptr->parameter_info(); + } else + s << "None"; #endif - break; - } - - case KeyArgument::LIST_OF_DOUBLES: - s << *reinterpret_cast(i->second.p_object_variable); break; - case KeyArgument::LIST_OF_INTS: - s << *reinterpret_cast(i->second.p_object_variable); break; - case KeyArgument::ARRAY2D_OF_FLOATS: - detail::to_stream(s, *reinterpret_cast*>(i->second.p_object_variable)); break; - case KeyArgument::ARRAY3D_OF_FLOATS: - detail::to_stream(s, *reinterpret_cast*>(i->second.p_object_variable)); break; - case KeyArgument::BASICCOORDINATE3D_OF_FLOATS: - { - typedef BasicCoordinate<3,float> type; - s << *reinterpret_cast(i->second.p_object_variable); - break; - } - case KeyArgument::BASICCOORDINATE3D_OF_ARRAY3D_OF_FLOATS: - { - typedef BasicCoordinate<3,Array<3,float> > type; - detail::to_stream(s, *reinterpret_cast(i->second.p_object_variable)); - break; - } - case KeyArgument::LIST_OF_ASCII: - s << *reinterpret_cast*>(i->second.p_object_variable); break; - default : - warning("KeyParser error: unknown type. Implementation error\n"); - break; + break; + } - } - s << endl; + case KeyArgument::LIST_OF_DOUBLES: + s << *reinterpret_cast(i->second.p_object_variable); + break; + case KeyArgument::LIST_OF_INTS: + s << *reinterpret_cast(i->second.p_object_variable); + break; + case KeyArgument::ARRAY2D_OF_FLOATS: + detail::to_stream(s, *reinterpret_cast*>(i->second.p_object_variable)); + break; + case KeyArgument::ARRAY3D_OF_FLOATS: + detail::to_stream(s, *reinterpret_cast*>(i->second.p_object_variable)); + break; + case KeyArgument::BASICCOORDINATE3D_OF_FLOATS: { + typedef BasicCoordinate<3, float> type; + s << *reinterpret_cast(i->second.p_object_variable); + break; } - // finally, find stop key - for (Keymap::const_iterator i=kmap.begin(); i!= kmap.end(); ++i) - { - if (i->second.p_object_member == &KeyParser::stop_parsing) - s << i->first << " := \n"; + case KeyArgument::BASICCOORDINATE3D_OF_ARRAY3D_OF_FLOATS: { + typedef BasicCoordinate<3, Array<3, float>> type; + detail::to_stream(s, *reinterpret_cast(i->second.p_object_variable)); + break; } - - return s.str(); + case KeyArgument::LIST_OF_ASCII: + s << *reinterpret_cast*>(i->second.p_object_variable); + break; + default: + warning("KeyParser error: unknown type. Implementation error\n"); + break; + } + s << endl; + } + // finally, find stop key + for (Keymap::const_iterator i = kmap.begin(); i != kmap.end(); ++i) { + if (i->second.p_object_member == &KeyParser::stop_parsing) + s << i->first << " := \n"; } -void KeyParser::ask_parameters() -{ - // This is necessary for set_parsing_object. It will allow the + return s.str(); +} + +void +KeyParser::ask_parameters() { + // This is necessary for set_parsing_object. It will allow the // 'recursive' parser to see it's being called interactively. input = 0; - while(true) - { + while (true) { string line; - - for (Keymap::const_iterator i=kmap.begin(); i!= kmap.end(); ++i) - { - - if (i->second.p_object_member == &KeyParser::do_nothing || - i->second.p_object_member == &KeyParser::start_parsing || + + for (Keymap::const_iterator i = kmap.begin(); i != kmap.end(); ++i) { + + if (i->second.p_object_member == &KeyParser::do_nothing || i->second.p_object_member == &KeyParser::start_parsing || i->second.p_object_member == &KeyParser::stop_parsing) - continue; + continue; - if (i->second.vectorised_key_level > 0) - { - warning("KeyParser: cannot handle vectorised key yet");//TODO - continue; - } + if (i->second.vectorised_key_level > 0) { + warning("KeyParser: cannot handle vectorised key yet"); // TODO + continue; + } keyword = i->first; cout << keyword << " := "; { - read_line(cin, line); - // prepend ":=" such that parse_value_in_line can work properly - line.insert(0, ":= "); + read_line(cin, line); + // prepend ":=" such that parse_value_in_line can work properly + line.insert(0, ":= "); } if (parse_value_in_line(line, false) == Succeeded::yes) - process_key(); + process_key(); } - if (post_processing()) - cout << " Asking all questions again! (Sorry)\n"; + if (post_processing()) + cout << " Asking all questions again! (Sorry)\n"; else return; } diff --git a/src/buildblock/ML_norm.cxx b/src/buildblock/ML_norm.cxx index c22df577b7..ff22fc679f 100644 --- a/src/buildblock/ML_norm.cxx +++ b/src/buildblock/ML_norm.cxx @@ -38,287 +38,235 @@ using std::max; START_NAMESPACE_STIR -DetPairData::DetPairData() -{} +DetPairData::DetPairData() {} -DetPairData::DetPairData(const IndexRange<2>& range) -:base_type(range), num_detectors(range.get_length()) -{ -} +DetPairData::DetPairData(const IndexRange<2>& range) : base_type(range), num_detectors(range.get_length()) {} -DetPairData& -DetPairData::operator=(const DetPairData& other) -{ +DetPairData& +DetPairData::operator=(const DetPairData& other) { base_type::operator=(other); num_detectors = other.num_detectors; return *this; } -float & DetPairData::operator()(const int a, const int b) -{ - return (*this)[a][b=get_min_index(a)) - return b<=get_max_index(a); +bool +DetPairData::is_in_data(const int a, const int b) const { + if (b >= get_min_index(a)) + return b <= get_max_index(a); else - return b+num_detectors<=get_max_index(a); + return b + num_detectors <= get_max_index(a); } -void DetPairData::fill(const float d) -{ +void +DetPairData::fill(const float d) { base_type::fill(d); } -void DetPairData::grow(const IndexRange<2>& range) -{ +void +DetPairData::grow(const IndexRange<2>& range) { base_type::grow(range); - num_detectors=range.get_length(); + num_detectors = range.get_length(); } -int DetPairData::get_min_index() const -{ +int +DetPairData::get_min_index() const { return base_type::get_min_index(); } -int DetPairData::get_max_index() const -{ +int +DetPairData::get_max_index() const { return base_type::get_max_index(); } -int DetPairData::get_min_index(const int a) const -{ +int +DetPairData::get_min_index(const int a) const { return (*this)[a].get_min_index(); } -int DetPairData::get_max_index(const int a) const -{ +int +DetPairData::get_max_index(const int a) const { return (*this)[a].get_max_index(); } -float DetPairData::sum() const -{ +float +DetPairData::sum() const { return base_type::sum(); } -float DetPairData::sum(const int a) const -{ +float +DetPairData::sum(const int a) const { return (*this)[a].sum(); } -float DetPairData::find_max() const -{ +float +DetPairData::find_max() const { return base_type::find_max(); } -float DetPairData::find_min() const -{ +float +DetPairData::find_min() const { return base_type::find_min(); } -int DetPairData::get_num_detectors() const -{ +int +DetPairData::get_num_detectors() const { return num_detectors; } -void make_det_pair_data(DetPairData& det_pair_data, - const ProjDataInfo& proj_data_info_general_type, - const int segment_num, - const int ax_pos_num) -{ +void +make_det_pair_data(DetPairData& det_pair_data, const ProjDataInfo& proj_data_info_general_type, const int segment_num, + const int ax_pos_num) { const ProjDataInfoCylindricalNoArcCorr& proj_data_info = - dynamic_cast(proj_data_info_general_type); + dynamic_cast(proj_data_info_general_type); - const int num_detectors = - proj_data_info.get_scanner_ptr()->get_num_detectors_per_ring(); - const int fan_size = - 2*max(proj_data_info.get_max_tangential_pos_num(), - -proj_data_info.get_min_tangential_pos_num()) + 1; + const int num_detectors = proj_data_info.get_scanner_ptr()->get_num_detectors_per_ring(); + const int fan_size = 2 * max(proj_data_info.get_max_tangential_pos_num(), -proj_data_info.get_min_tangential_pos_num()) + 1; // fan will range from -half_fan_size to +half_fan_size (i.e. an odd number of elements) - const int half_fan_size = fan_size/2; + const int half_fan_size = fan_size / 2; IndexRange<2> fan_indices; - fan_indices.grow(0,num_detectors-1); - for (int a = 0; a < num_detectors; ++a) - { - fan_indices[a] = - IndexRange<1>(a+num_detectors/2-half_fan_size, - a+num_detectors/2+half_fan_size); + fan_indices.grow(0, num_detectors - 1); + for (int a = 0; a < num_detectors; ++a) { + fan_indices[a] = IndexRange<1>(a + num_detectors / 2 - half_fan_size, a + num_detectors / 2 + half_fan_size); } det_pair_data.grow(fan_indices); det_pair_data.fill(0); } -void make_det_pair_data(DetPairData& det_pair_data, - const ProjData& proj_data, - const int segment_num, - const int ax_pos_num) -{ - make_det_pair_data(det_pair_data, - *proj_data.get_proj_data_info_sptr(), - segment_num, - ax_pos_num); - const int num_detectors = - det_pair_data.get_num_detectors(); +void +make_det_pair_data(DetPairData& det_pair_data, const ProjData& proj_data, const int segment_num, const int ax_pos_num) { + make_det_pair_data(det_pair_data, *proj_data.get_proj_data_info_sptr(), segment_num, ax_pos_num); + const int num_detectors = det_pair_data.get_num_detectors(); const ProjDataInfoCylindricalNoArcCorr& proj_data_info = - dynamic_cast(*proj_data.get_proj_data_info_sptr()); + dynamic_cast(*proj_data.get_proj_data_info_sptr()); - shared_ptr > - pos_sino_ptr(new Sinogram(proj_data.get_sinogram(ax_pos_num,segment_num))); - shared_ptr > neg_sino_ptr; + shared_ptr> pos_sino_ptr(new Sinogram(proj_data.get_sinogram(ax_pos_num, segment_num))); + shared_ptr> neg_sino_ptr; if (segment_num == 0) neg_sino_ptr = pos_sino_ptr; else - neg_sino_ptr. - reset(new Sinogram(proj_data.get_sinogram(ax_pos_num,-segment_num))); - - - for (int view_num = 0; view_num < num_detectors/2; view_num++) - for (int tang_pos_num = proj_data.get_min_tangential_pos_num(); - tang_pos_num <= proj_data.get_max_tangential_pos_num(); - ++tang_pos_num) - { - int det_num_a = 0; - int det_num_b = 0; + neg_sino_ptr.reset(new Sinogram(proj_data.get_sinogram(ax_pos_num, -segment_num))); - proj_data_info.get_det_num_pair_for_view_tangential_pos_num(det_num_a, det_num_b, view_num, tang_pos_num); + for (int view_num = 0; view_num < num_detectors / 2; view_num++) + for (int tang_pos_num = proj_data.get_min_tangential_pos_num(); tang_pos_num <= proj_data.get_max_tangential_pos_num(); + ++tang_pos_num) { + int det_num_a = 0; + int det_num_b = 0; - det_pair_data(det_num_a,det_num_b) = - (*pos_sino_ptr)[view_num][tang_pos_num]; - det_pair_data(det_num_b,det_num_a) = - (*neg_sino_ptr)[view_num][tang_pos_num]; - } + proj_data_info.get_det_num_pair_for_view_tangential_pos_num(det_num_a, det_num_b, view_num, tang_pos_num); + + det_pair_data(det_num_a, det_num_b) = (*pos_sino_ptr)[view_num][tang_pos_num]; + det_pair_data(det_num_b, det_num_a) = (*neg_sino_ptr)[view_num][tang_pos_num]; + } } -void set_det_pair_data(ProjData& proj_data, - const DetPairData& det_pair_data, - const int segment_num, - const int ax_pos_num) -{ - const shared_ptr proj_data_info_sptr = - proj_data.get_proj_data_info_sptr(); +void +set_det_pair_data(ProjData& proj_data, const DetPairData& det_pair_data, const int segment_num, const int ax_pos_num) { + const shared_ptr proj_data_info_sptr = proj_data.get_proj_data_info_sptr(); const ProjDataInfoCylindricalNoArcCorr& proj_data_info = - dynamic_cast(*proj_data_info_sptr); + dynamic_cast(*proj_data_info_sptr); const int num_detectors = det_pair_data.get_num_detectors(); assert(proj_data_info.get_scanner_ptr()->get_num_detectors_per_ring() == num_detectors); - shared_ptr > - pos_sino_ptr(new Sinogram(proj_data.get_empty_sinogram(ax_pos_num,segment_num))); - shared_ptr > neg_sino_ptr; + shared_ptr> pos_sino_ptr(new Sinogram(proj_data.get_empty_sinogram(ax_pos_num, segment_num))); + shared_ptr> neg_sino_ptr; if (segment_num != 0) - neg_sino_ptr. - reset(new Sinogram(proj_data.get_empty_sinogram(ax_pos_num,-segment_num))); - - - for (int view_num = 0; view_num < num_detectors/2; view_num++) - for (int tang_pos_num = proj_data.get_min_tangential_pos_num(); - tang_pos_num <= proj_data.get_max_tangential_pos_num(); - ++tang_pos_num) - { - int det_num_a = 0; - int det_num_b = 0; + neg_sino_ptr.reset(new Sinogram(proj_data.get_empty_sinogram(ax_pos_num, -segment_num))); - proj_data_info.get_det_num_pair_for_view_tangential_pos_num(det_num_a, det_num_b, view_num, tang_pos_num); + for (int view_num = 0; view_num < num_detectors / 2; view_num++) + for (int tang_pos_num = proj_data.get_min_tangential_pos_num(); tang_pos_num <= proj_data.get_max_tangential_pos_num(); + ++tang_pos_num) { + int det_num_a = 0; + int det_num_b = 0; - (*pos_sino_ptr)[view_num][tang_pos_num] = - det_pair_data(det_num_a,det_num_b); - if (segment_num!=0) - (*neg_sino_ptr)[view_num][tang_pos_num] = - det_pair_data(det_num_b,det_num_a); - } + proj_data_info.get_det_num_pair_for_view_tangential_pos_num(det_num_a, det_num_b, view_num, tang_pos_num); + + (*pos_sino_ptr)[view_num][tang_pos_num] = det_pair_data(det_num_a, det_num_b); + if (segment_num != 0) + (*neg_sino_ptr)[view_num][tang_pos_num] = det_pair_data(det_num_b, det_num_a); + } proj_data.set_sinogram(*pos_sino_ptr); if (segment_num != 0) proj_data.set_sinogram(*neg_sino_ptr); } - -void apply_block_norm(DetPairData& det_pair_data, const BlockData& block_data, const bool apply) -{ +void +apply_block_norm(DetPairData& det_pair_data, const BlockData& block_data, const bool apply) { const int num_detectors = det_pair_data.get_num_detectors(); const int num_blocks = block_data.get_length(); - const int num_crystals_per_block = num_detectors/num_blocks; + const int num_crystals_per_block = num_detectors / num_blocks; assert(num_blocks * num_crystals_per_block == num_detectors); - + for (int a = det_pair_data.get_min_index(); a <= det_pair_data.get_max_index(); ++a) - for (int b = det_pair_data.get_min_index(a); b <= det_pair_data.get_max_index(a); ++b) - { - // note: add 2*num_detectors to newb to avoid using mod with negative numbers - if (det_pair_data(a,b) == 0) - continue; - if (apply) - det_pair_data(a,b) *= - block_data[a/num_crystals_per_block][(b/num_crystals_per_block)%num_blocks]; - else - det_pair_data(a,b) /= - block_data[a/num_crystals_per_block][(b/num_crystals_per_block)%num_blocks]; - } + for (int b = det_pair_data.get_min_index(a); b <= det_pair_data.get_max_index(a); ++b) { + // note: add 2*num_detectors to newb to avoid using mod with negative numbers + if (det_pair_data(a, b) == 0) + continue; + if (apply) + det_pair_data(a, b) *= block_data[a / num_crystals_per_block][(b / num_crystals_per_block) % num_blocks]; + else + det_pair_data(a, b) /= block_data[a / num_crystals_per_block][(b / num_crystals_per_block) % num_blocks]; + } } -void apply_geo_norm(DetPairData& det_pair_data, const GeoData& geo_data, const bool apply) -{ +void +apply_geo_norm(DetPairData& det_pair_data, const GeoData& geo_data, const bool apply) { const int num_detectors = det_pair_data.get_num_detectors(); - const int num_crystals_per_block = geo_data.get_length()*2; + const int num_crystals_per_block = geo_data.get_length() * 2; for (int a = det_pair_data.get_min_index(); a <= det_pair_data.get_max_index(); ++a) - for (int b = det_pair_data.get_min_index(a); b <= det_pair_data.get_max_index(a); ++b) - { - if (det_pair_data(a,b) == 0) - continue; - int newa = a % num_crystals_per_block; - int newb = b - (a - newa); - if (newa > num_crystals_per_block - 1 - newa) - { - newa = num_crystals_per_block - 1 - newa; - newb = - newb + num_crystals_per_block - 1; - } - // note: add 2*num_detectors to newb to avoid using mod with negative numbers - if (apply) - det_pair_data(a,b) *= - geo_data[newa][(2*num_detectors + newb)%num_detectors]; - else - det_pair_data(a,b) /= - geo_data[newa][(2*num_detectors + newb)%num_detectors]; + for (int b = det_pair_data.get_min_index(a); b <= det_pair_data.get_max_index(a); ++b) { + if (det_pair_data(a, b) == 0) + continue; + int newa = a % num_crystals_per_block; + int newb = b - (a - newa); + if (newa > num_crystals_per_block - 1 - newa) { + newa = num_crystals_per_block - 1 - newa; + newb = -newb + num_crystals_per_block - 1; } + // note: add 2*num_detectors to newb to avoid using mod with negative numbers + if (apply) + det_pair_data(a, b) *= geo_data[newa][(2 * num_detectors + newb) % num_detectors]; + else + det_pair_data(a, b) /= geo_data[newa][(2 * num_detectors + newb) % num_detectors]; + } } -void apply_efficiencies(DetPairData& det_pair_data, const Array<1,float>& efficiencies, const bool apply) -{ +void +apply_efficiencies(DetPairData& det_pair_data, const Array<1, float>& efficiencies, const bool apply) { const int num_detectors = det_pair_data.get_num_detectors(); for (int a = det_pair_data.get_min_index(); a <= det_pair_data.get_max_index(); ++a) - for (int b = det_pair_data.get_min_index(a); b <= det_pair_data.get_max_index(a); ++b) - { - if (det_pair_data(a,b) == 0) - continue; - if (apply) - det_pair_data(a,b) *= - efficiencies[a]*efficiencies[b%num_detectors]; - else - det_pair_data(a,b) /= - efficiencies[a]*efficiencies[b%num_detectors]; - } + for (int b = det_pair_data.get_min_index(a); b <= det_pair_data.get_max_index(a); ++b) { + if (det_pair_data(a, b) == 0) + continue; + if (apply) + det_pair_data(a, b) *= efficiencies[a] * efficiencies[b % num_detectors]; + else + det_pair_data(a, b) /= efficiencies[a] * efficiencies[b % num_detectors]; + } } - -void make_fan_sum_data(Array<1,float>& data_fan_sums, const DetPairData& det_pair_data) -{ +void +make_fan_sum_data(Array<1, float>& data_fan_sums, const DetPairData& det_pair_data) { for (int a = det_pair_data.get_min_index(); a <= det_pair_data.get_max_index(); ++a) data_fan_sums[a] = det_pair_data.sum(a); } -void make_geo_data(GeoData& geo_data, const DetPairData& det_pair_data) -{ +void +make_geo_data(GeoData& geo_data, const DetPairData& det_pair_data) { const int num_detectors = det_pair_data.get_num_detectors(); - const int num_crystals_per_block = geo_data.get_length()*2; + const int num_crystals_per_block = geo_data.get_length() * 2; const int num_blocks = num_detectors / num_crystals_per_block; assert(num_blocks * num_crystals_per_block == num_detectors); @@ -327,174 +275,145 @@ void make_geo_data(GeoData& geo_data, const DetPairData& det_pair_data) work.fill(0); for (int a = det_pair_data.get_min_index(); a <= det_pair_data.get_max_index(); ++a) - for (int b = det_pair_data.get_min_index(a); b <= det_pair_data.get_max_index(a); ++b) - { - // mirror symmetry - work(a,b) = - det_pair_data(a,b) + - det_pair_data(num_detectors-1-a,(2*num_detectors-1-b)%num_detectors); - } + for (int b = det_pair_data.get_min_index(a); b <= det_pair_data.get_max_index(a); ++b) { + // mirror symmetry + work(a, b) = det_pair_data(a, b) + det_pair_data(num_detectors - 1 - a, (2 * num_detectors - 1 - b) % num_detectors); + } geo_data.fill(0); - for (int crystal_num_a = 0; crystal_num_a < num_crystals_per_block/2; ++crystal_num_a) - for (int det_num_b = det_pair_data.get_min_index(crystal_num_a); det_num_b <= det_pair_data.get_max_index(crystal_num_a); ++det_num_b) - { - for (int block_num = 0; block_num& efficiencies, - const Array<1,float>& data_fan_sums, - const DetPairData& model) -{ + +void +iterate_efficiencies(Array<1, float>& efficiencies, const Array<1, float>& data_fan_sums, const DetPairData& model) { const int num_detectors = efficiencies.get_length(); - for (int a = 0; a < num_detectors; ++a) - { - if (data_fan_sums[a] == 0) - efficiencies[a] = 0; - else - { - //const float denominator = inner_product(efficiencies,model[a]); - float denominator = 0; - for (int b = model.get_min_index(a); b <= model.get_max_index(a); ++b) - denominator += efficiencies[b%num_detectors]*model(a,b); - efficiencies[a] = data_fan_sums[a] / denominator; - } + for (int a = 0; a < num_detectors; ++a) { + if (data_fan_sums[a] == 0) + efficiencies[a] = 0; + else { + // const float denominator = inner_product(efficiencies,model[a]); + float denominator = 0; + for (int b = model.get_min_index(a); b <= model.get_max_index(a); ++b) + denominator += efficiencies[b % num_detectors] * model(a, b); + efficiencies[a] = data_fan_sums[a] / denominator; } + } } -void iterate_geo_norm(GeoData& norm_geo_data, - const GeoData& measured_geo_data, - const DetPairData& model) -{ +void +iterate_geo_norm(GeoData& norm_geo_data, const GeoData& measured_geo_data, const DetPairData& model) { make_geo_data(norm_geo_data, model); - //norm_geo_data = measured_geo_data / norm_geo_data; + // norm_geo_data = measured_geo_data / norm_geo_data; const int num_detectors = model.get_num_detectors(); - const int num_crystals_per_block = measured_geo_data.get_length()*2; - const float threshold = measured_geo_data.find_max()/10000.F; - for (int a = 0; a < num_crystals_per_block/2; ++a) - for (int b = 0; b < num_detectors; ++b) - { - norm_geo_data[a][b] = - (measured_geo_data[a][b]>=threshold || - measured_geo_data[a][b] < 10000*norm_geo_data[a][b]) - ? measured_geo_data[a][b] / norm_geo_data[a][b] - : 0; - } + const int num_crystals_per_block = measured_geo_data.get_length() * 2; + const float threshold = measured_geo_data.find_max() / 10000.F; + for (int a = 0; a < num_crystals_per_block / 2; ++a) + for (int b = 0; b < num_detectors; ++b) { + norm_geo_data[a][b] = (measured_geo_data[a][b] >= threshold || measured_geo_data[a][b] < 10000 * norm_geo_data[a][b]) + ? measured_geo_data[a][b] / norm_geo_data[a][b] + : 0; + } } - -void iterate_block_norm(BlockData& norm_block_data, - const BlockData& measured_block_data, - const DetPairData& model) -{ + +void +iterate_block_norm(BlockData& norm_block_data, const BlockData& measured_block_data, const DetPairData& model) { make_block_data(norm_block_data, model); - //norm_block_data = measured_block_data / norm_block_data; + // norm_block_data = measured_block_data / norm_block_data; const int num_blocks = norm_block_data.get_length(); - const float threshold = measured_block_data.find_max()/10000.F; + const float threshold = measured_block_data.find_max() / 10000.F; for (int a = 0; a < num_blocks; ++a) - for (int b = 0; b < num_blocks; ++b) - { - norm_block_data[a][b] = - (measured_block_data[a][b]>=threshold || - measured_block_data[a][b] < 10000*norm_block_data[a][b]) - ? measured_block_data[a][b] / norm_block_data[a][b] - : 0; - } + for (int b = 0; b < num_blocks; ++b) { + norm_block_data[a][b] = + (measured_block_data[a][b] >= threshold || measured_block_data[a][b] < 10000 * norm_block_data[a][b]) + ? measured_block_data[a][b] / norm_block_data[a][b] + : 0; + } } -double KL(const DetPairData& d1, const DetPairData& d2, const double threshold) -{ - double sum=0; - for (int a = d1.get_min_index(); a <= d1.get_max_index(); ++a) - { - double bsum=0; - for (int b = d1.get_min_index(a); b <= d1.get_max_index(a); ++b) - bsum += KL(d1(a,b), d2(a,b), threshold); - sum += bsum; - } +double +KL(const DetPairData& d1, const DetPairData& d2, const double threshold) { + double sum = 0; + for (int a = d1.get_min_index(); a <= d1.get_max_index(); ++a) { + double bsum = 0; + for (int b = d1.get_min_index(a); b <= d1.get_max_index(a); ++b) + bsum += KL(d1(a, b), d2(a, b), threshold); + sum += bsum; + } return static_cast(sum); } - ////////// ******* GeoData3D ******** ////// -GeoData3D::GeoData3D() -{} +GeoData3D::GeoData3D() {} -GeoData3D::~GeoData3D() -{} +GeoData3D::~GeoData3D() {} +GeoData3D::GeoData3D(const int num_axial_crystals_per_block, const int half_num_transaxial_crystals_per_block, + const int num_rings, const int num_detectors_per_ring) + : num_axial_crystals_per_block(num_axial_crystals_per_block), + half_num_transaxial_crystals_per_block(half_num_transaxial_crystals_per_block), num_rings(num_rings), + num_detectors_per_ring(num_detectors_per_ring) { -GeoData3D:: -GeoData3D(const int num_axial_crystals_per_block, const int half_num_transaxial_crystals_per_block, const int num_rings, const int num_detectors_per_ring) -:num_axial_crystals_per_block(num_axial_crystals_per_block), half_num_transaxial_crystals_per_block(half_num_transaxial_crystals_per_block), -num_rings(num_rings), num_detectors_per_ring(num_detectors_per_ring) -{ - - // assert(max_ring_diff= (num_axial_crystals_per_block -1) + // assert(max_ring_diff= (num_axial_crystals_per_block -1) - - IndexRange<4> fan_indices; - fan_indices.grow(0,num_axial_crystals_per_block-1); - for (int ra = 0; ra < num_axial_crystals_per_block; ++ra) - { - const int min_rb = ra; - const int max_rb = num_rings-1; // I assumed max ring diff is num_ring_1 - fan_indices[ra].grow(0,half_num_transaxial_crystals_per_block-1); - for (int a = 0; a < half_num_transaxial_crystals_per_block; ++a) - { - // store only 1 half of data as ra,a,rb,b = rb,b,ra,a - fan_indices[ra][a].grow(min_rb, max_rb); - for (int rb = min_rb; rb <= max_rb; ++rb) - fan_indices[ra][a][rb] = - IndexRange<1>(a, - a+num_detectors_per_ring -1); // I assumed fan size is number of detector per ring - 2 - } + IndexRange<4> fan_indices; + fan_indices.grow(0, num_axial_crystals_per_block - 1); + for (int ra = 0; ra < num_axial_crystals_per_block; ++ra) { + const int min_rb = ra; + const int max_rb = num_rings - 1; // I assumed max ring diff is num_ring_1 + fan_indices[ra].grow(0, half_num_transaxial_crystals_per_block - 1); + for (int a = 0; a < half_num_transaxial_crystals_per_block; ++a) { + // store only 1 half of data as ra,a,rb,b = rb,b,ra,a + fan_indices[ra][a].grow(min_rb, max_rb); + for (int rb = min_rb; rb <= max_rb; ++rb) + fan_indices[ra][a][rb] = + IndexRange<1>(a, + a + num_detectors_per_ring - 1); // I assumed fan size is number of detector per ring - 2 } - grow(fan_indices); - fill(0); + } + grow(fan_indices); + fill(0); } GeoData3D& -GeoData3D::operator=(const GeoData3D& other) -{ - base_type::operator=(other); - num_axial_crystals_per_block = other.num_axial_crystals_per_block; - half_num_transaxial_crystals_per_block = other.half_num_transaxial_crystals_per_block; - num_rings = other.num_rings; - num_detectors_per_ring = other.num_detectors_per_ring; - return *this; +GeoData3D::operator=(const GeoData3D& other) { + base_type::operator=(other); + num_axial_crystals_per_block = other.num_axial_crystals_per_block; + half_num_transaxial_crystals_per_block = other.half_num_transaxial_crystals_per_block; + num_rings = other.num_rings; + num_detectors_per_ring = other.num_detectors_per_ring; + return *this; } /*float & GeoData3D::operator()(const int ra, const int a, const int rb, const int b) @@ -504,7 +423,8 @@ GeoData3D::operator=(const GeoData3D& other) return ra=0); - assert(b>=0); - return +float& +GeoData3D::operator()(const int ra, const int a, const int rb, const int b) { + assert(a >= 0); + assert(b >= 0); + return - (*this)[ra][a%num_detectors_per_ring][rb][b=0); - assert(b>=0); - return - - (*this)[ra][a%num_detectors_per_ring][rb][b= 0); + assert(b >= 0); + return -} + (*this)[ra][a % num_detectors_per_ring][rb][b < get_min_b(a) ? b + num_detectors_per_ring : b]; +} bool -GeoData3D:: -is_in_data(const int ra, const int a, const int rb, const int b) const -{ - assert(a>=0); - assert(b>=0); - if (rb<(*this)[ra][a].get_min_index() || rb >(*this)[ra][a].get_max_index()) - return false; - if (b>=get_min_b(a)) - return b<=get_max_b(a); - else - return b+num_detectors_per_ring<=get_max_b(a); -} -void GeoData3D::fill(const float d) -{ - base_type::fill(d); +GeoData3D::is_in_data(const int ra, const int a, const int rb, const int b) const { + assert(a >= 0); + assert(b >= 0); + if (rb < (*this)[ra][a].get_min_index() || rb > (*this)[ra][a].get_max_index()) + return false; + if (b >= get_min_b(a)) + return b <= get_max_b(a); + else + return b + num_detectors_per_ring <= get_max_b(a); } - - -int GeoData3D::get_min_ra() const -{ - return base_type::get_min_index(); +void +GeoData3D::fill(const float d) { + base_type::fill(d); } -int GeoData3D::get_max_ra() const -{ - return base_type::get_max_index(); +int +GeoData3D::get_min_ra() const { + return base_type::get_min_index(); } - -int GeoData3D::get_min_a() const -{ - return (*this)[get_min_index()].get_min_index(); +int +GeoData3D::get_max_ra() const { + return base_type::get_max_index(); } -int GeoData3D::get_max_a() const -{ - return (*this)[get_min_index()].get_max_index(); +int +GeoData3D::get_min_a() const { + return (*this)[get_min_index()].get_min_index(); } - -int GeoData3D::get_min_rb(const int ra) const -{ - return 0; - // next is no longer true because we store only half the data - //return (*this)[ra][(*this)[ra].get_min_index()].get_min_index(); +int +GeoData3D::get_max_a() const { + return (*this)[get_min_index()].get_max_index(); } - -int GeoData3D::get_max_rb(const int ra) const -{ - return (*this)[ra][(*this)[ra].get_min_index()].get_max_index(); +int +GeoData3D::get_min_rb(const int ra) const { + return 0; + // next is no longer true because we store only half the data + // return (*this)[ra][(*this)[ra].get_min_index()].get_min_index(); } -int GeoData3D::get_min_b(const int a) const -{ - return (*this)[get_min_index()][a][(*this)[get_min_index()][a].get_min_index()].get_min_index(); +int +GeoData3D::get_max_rb(const int ra) const { + return (*this)[ra][(*this)[ra].get_min_index()].get_max_index(); } -int GeoData3D::get_max_b(const int a) const -{ - return (*this)[get_min_index()][a][(*this)[get_min_index()][a].get_min_index()].get_max_index(); +int +GeoData3D::get_min_b(const int a) const { + return (*this)[get_min_index()][a][(*this)[get_min_index()][a].get_min_index()].get_min_index(); } - -float GeoData3D::sum() const -{ - //return base_type::sum(); - float sum = 0; - for (int ra=get_min_ra(); ra <= get_max_ra(); ++ra) - for (int a = get_min_a(); a <= get_max_a(); ++a) - sum += this->sum(ra,a); - return sum; +int +GeoData3D::get_max_b(const int a) const { + return (*this)[get_min_index()][a][(*this)[get_min_index()][a].get_min_index()].get_max_index(); } -float GeoData3D::sum(const int ra, const int a) const -{ - //return (*this)[ra][a].sum(); - float sum = 0; - for (int rb=get_min_rb(ra); rb <= get_max_rb(ra); ++rb) - for (int b = get_min_b(a); b <= get_max_b(a); ++b) - sum += (*this)(ra,a,rb,b%num_detectors_per_ring); - return sum; +float +GeoData3D::sum() const { + // return base_type::sum(); + float sum = 0; + for (int ra = get_min_ra(); ra <= get_max_ra(); ++ra) + for (int a = get_min_a(); a <= get_max_a(); ++a) + sum += this->sum(ra, a); + return sum; } -float GeoData3D::find_max() const -{ - return base_type::find_max(); +float +GeoData3D::sum(const int ra, const int a) const { + // return (*this)[ra][a].sum(); + float sum = 0; + for (int rb = get_min_rb(ra); rb <= get_max_rb(ra); ++rb) + for (int b = get_min_b(a); b <= get_max_b(a); ++b) + sum += (*this)(ra, a, rb, b % num_detectors_per_ring); + return sum; } -float GeoData3D::find_min() const -{ - return base_type::find_min(); +float +GeoData3D::find_max() const { + return base_type::find_max(); } -int GeoData3D::get_num_axial_crystals_per_block() const -{ - return num_axial_crystals_per_block; +float +GeoData3D::find_min() const { + return base_type::find_min(); } -int GeoData3D::get_half_num_transaxial_crystals_per_block() const -{ - return half_num_transaxial_crystals_per_block; +int +GeoData3D::get_num_axial_crystals_per_block() const { + return num_axial_crystals_per_block; } +int +GeoData3D::get_half_num_transaxial_crystals_per_block() const { + return half_num_transaxial_crystals_per_block; +} -std::ostream& operator<<(std::ostream& s, const GeoData3D& geo_data) -{ - return s << static_cast(geo_data); +std::ostream& +operator<<(std::ostream& s, const GeoData3D& geo_data) { + return s << static_cast(geo_data); } -std::istream& operator>>(std::istream& s, GeoData3D& geo_data) -{ - s >> static_cast(geo_data); - if (!s) - return s; - geo_data.half_num_transaxial_crystals_per_block = geo_data.get_max_a() - geo_data.get_min_a() + 1; - geo_data.num_axial_crystals_per_block = geo_data.get_max_ra() - geo_data.get_min_ra() + 1; - - - const int max_delta = geo_data[0][0].get_length()-1; - const int num_detectors_per_ring = - geo_data[0][0][0].get_length(); - geo_data.num_detectors_per_ring = num_detectors_per_ring; - geo_data.num_rings = max_delta+1; - - - - - for (int ra = 0; ra < geo_data.num_axial_crystals_per_block; ++ra) - +std::istream& +operator>>(std::istream& s, GeoData3D& geo_data) { + s >> static_cast(geo_data); + if (!s) + return s; + geo_data.half_num_transaxial_crystals_per_block = geo_data.get_max_a() - geo_data.get_min_a() + 1; + geo_data.num_axial_crystals_per_block = geo_data.get_max_ra() - geo_data.get_min_ra() + 1; - { - const int min_rb = ra; - const int max_rb = geo_data.num_rings-1; - for (int a = 0; a < geo_data.half_num_transaxial_crystals_per_block; ++a) - { + const int max_delta = geo_data[0][0].get_length() - 1; + const int num_detectors_per_ring = geo_data[0][0][0].get_length(); + geo_data.num_detectors_per_ring = num_detectors_per_ring; + geo_data.num_rings = max_delta + 1; - if (geo_data[ra][a].get_length() != max_rb - max(ra,min_rb) + 1) - { - warning("Reading GeoData3D: inconsistent length %d for rb at ra=%d, a=%d, " - "Expected length %d\n", - geo_data[ra][a].get_length(), ra, a, max_rb - max(ra,min_rb) + 1); - } - geo_data[ra][a].set_offset(max(ra,min_rb)); - for (int rb = geo_data[ra][a].get_min_index(); rb <= geo_data[ra][a].get_max_index(); ++rb) - { - if (geo_data[ra][a][rb].get_length() != num_detectors_per_ring) - { - warning("Reading GeoData3D: inconsistent length %d for b at ra=%d, a=%d, rb=%d\n" - "Expected length %d\n", - geo_data[ra][a][rb].get_length(), ra, a, rb, num_detectors_per_ring); - } - geo_data[ra][a][rb].set_offset(a); - } + for (int ra = 0; ra < geo_data.num_axial_crystals_per_block; ++ra) + + { + const int min_rb = ra; + const int max_rb = geo_data.num_rings - 1; + for (int a = 0; a < geo_data.half_num_transaxial_crystals_per_block; ++a) { + + if (geo_data[ra][a].get_length() != max_rb - max(ra, min_rb) + 1) { + warning("Reading GeoData3D: inconsistent length %d for rb at ra=%d, a=%d, " + "Expected length %d\n", + geo_data[ra][a].get_length(), ra, a, max_rb - max(ra, min_rb) + 1); + } + geo_data[ra][a].set_offset(max(ra, min_rb)); + for (int rb = geo_data[ra][a].get_min_index(); rb <= geo_data[ra][a].get_max_index(); ++rb) { + if (geo_data[ra][a][rb].get_length() != num_detectors_per_ring) { + warning("Reading GeoData3D: inconsistent length %d for b at ra=%d, a=%d, rb=%d\n" + "Expected length %d\n", + geo_data[ra][a][rb].get_length(), ra, a, rb, num_detectors_per_ring); } + geo_data[ra][a][rb].set_offset(a); + } } - - return s; -} + } + return s; +} //////////////////////////////////////////////////// +FanProjData::FanProjData() {} - - -FanProjData::FanProjData() -{} - -FanProjData::~FanProjData() -{} +FanProjData::~FanProjData() {} #if 0 FanProjData::FanProjData(const IndexRange<4>& range) @@ -718,40 +614,33 @@ FanProjData::FanProjData(const IndexRange<4>& range) } #endif -FanProjData:: -FanProjData(const int num_rings, const int num_detectors_per_ring, const int max_ring_diff, const int fan_size) -: num_rings(num_rings), num_detectors_per_ring(num_detectors_per_ring), - max_ring_diff(max_ring_diff), half_fan_size(fan_size/2) -{ - assert(num_detectors_per_ring%2 == 0); - assert(max_ring_diff fan_indices; - fan_indices.grow(0,num_rings-1); - for (int ra = 0; ra < num_rings; ++ra) - { - const int min_rb = max(ra-max_ring_diff, 0); - const int max_rb = min(ra+max_ring_diff, num_rings-1); - fan_indices[ra].grow(0,num_detectors_per_ring-1); - for (int a = 0; a < num_detectors_per_ring; ++a) - { + fan_indices.grow(0, num_rings - 1); + for (int ra = 0; ra < num_rings; ++ra) { + const int min_rb = max(ra - max_ring_diff, 0); + const int max_rb = min(ra + max_ring_diff, num_rings - 1); + fan_indices[ra].grow(0, num_detectors_per_ring - 1); + for (int a = 0; a < num_detectors_per_ring; ++a) { // store only 1 half of data as ra,a,rb,b = rb,b,ra,a - fan_indices[ra][a].grow(max(ra,min_rb), max_rb); - for (int rb = max(ra,min_rb); rb <= max_rb; ++rb) - fan_indices[ra][a][rb] = - IndexRange<1>(a+num_detectors_per_ring/2-half_fan_size, - a+num_detectors_per_ring/2+half_fan_size); + fan_indices[ra][a].grow(max(ra, min_rb), max_rb); + for (int rb = max(ra, min_rb); rb <= max_rb; ++rb) + fan_indices[ra][a][rb] = + IndexRange<1>(a + num_detectors_per_ring / 2 - half_fan_size, a + num_detectors_per_ring / 2 + half_fan_size); } } grow(fan_indices); fill(0); } -FanProjData& -FanProjData::operator=(const FanProjData& other) -{ +FanProjData& +FanProjData::operator=(const FanProjData& other) { base_type::operator=(other); num_detectors_per_ring = other.num_detectors_per_ring; num_rings = other.num_rings; @@ -760,42 +649,38 @@ FanProjData::operator=(const FanProjData& other) return *this; } -float & FanProjData::operator()(const int ra, const int a, const int rb, const int b) -{ - assert(a>=0); - assert(b>=0); - return - ra= 0); + assert(b >= 0); + return ra < rb ? (*this)[ra][a % num_detectors_per_ring][rb][b < get_min_b(a) ? b + num_detectors_per_ring : b] + : (*this)[rb][b % num_detectors_per_ring][ra] + [a < get_min_b(b % num_detectors_per_ring) ? a + num_detectors_per_ring : a]; } -float FanProjData::operator()(const int ra, const int a, const int rb, const int b) const -{ - assert(a>=0); - assert(b>=0); - return - ra= 0); + assert(b >= 0); + return ra < rb ? (*this)[ra][a % num_detectors_per_ring][rb][b < get_min_b(a) ? b + num_detectors_per_ring : b] + : (*this)[rb][b % num_detectors_per_ring][ra] + [a < get_min_b(b % num_detectors_per_ring) ? a + num_detectors_per_ring : a]; } -bool -FanProjData:: -is_in_data(const int ra, const int a, const int rb, const int b) const -{ - assert(a>=0); - assert(b>=0); - if (rb<(*this)[ra][a].get_min_index() || rb >(*this)[ra][a].get_max_index()) +bool +FanProjData::is_in_data(const int ra, const int a, const int rb, const int b) const { + assert(a >= 0); + assert(b >= 0); + if (rb < (*this)[ra][a].get_min_index() || rb > (*this)[ra][a].get_max_index()) return false; - if (b>=get_min_b(a)) - return b<=get_max_b(a); + if (b >= get_min_b(a)) + return b <= get_max_b(a); else - return b+num_detectors_per_ring<=get_max_b(a); + return b + num_detectors_per_ring <= get_max_b(a); } -void FanProjData::fill(const float d) -{ +void +FanProjData::fill(const float d) { base_type::fill(d); } @@ -808,136 +693,126 @@ void FanProjData::grow(const IndexRange<4>& range) } #endif -int FanProjData::get_min_ra() const -{ +int +FanProjData::get_min_ra() const { return base_type::get_min_index(); } -int FanProjData::get_max_ra() const -{ +int +FanProjData::get_max_ra() const { return base_type::get_max_index(); } - -int FanProjData::get_min_a() const -{ +int +FanProjData::get_min_a() const { return (*this)[get_min_index()].get_min_index(); } -int FanProjData::get_max_a() const -{ +int +FanProjData::get_max_a() const { return (*this)[get_min_index()].get_max_index(); } - -int FanProjData::get_min_rb(const int ra) const -{ - return max(ra-max_ring_diff, 0); +int +FanProjData::get_min_rb(const int ra) const { + return max(ra - max_ring_diff, 0); // next is no longer true because we store only half the data - //return (*this)[ra][(*this)[ra].get_min_index()].get_min_index(); + // return (*this)[ra][(*this)[ra].get_min_index()].get_min_index(); } -int FanProjData::get_max_rb(const int ra) const -{ +int +FanProjData::get_max_rb(const int ra) const { return (*this)[ra][(*this)[ra].get_min_index()].get_max_index(); } -int FanProjData::get_min_b(const int a) const -{ +int +FanProjData::get_min_b(const int a) const { return (*this)[get_min_index()][a][(*this)[get_min_index()][a].get_min_index()].get_min_index(); } -int FanProjData::get_max_b(const int a) const -{ +int +FanProjData::get_max_b(const int a) const { return (*this)[get_min_index()][a][(*this)[get_min_index()][a].get_min_index()].get_max_index(); } - -float FanProjData::sum() const -{ - //return base_type::sum(); +float +FanProjData::sum() const { + // return base_type::sum(); float sum = 0; - for (int ra=get_min_ra(); ra <= get_max_ra(); ++ra) - for (int a = get_min_a(); a <= get_max_a(); ++a) - sum += this->sum(ra,a); + for (int ra = get_min_ra(); ra <= get_max_ra(); ++ra) + for (int a = get_min_a(); a <= get_max_a(); ++a) + sum += this->sum(ra, a); return sum; } -float FanProjData::sum(const int ra, const int a) const -{ - //return (*this)[ra][a].sum(); +float +FanProjData::sum(const int ra, const int a) const { + // return (*this)[ra][a].sum(); float sum = 0; - for (int rb=get_min_rb(ra); rb <= get_max_rb(ra); ++rb) - for (int b = get_min_b(a); b <= get_max_b(a); ++b) - sum += (*this)(ra,a,rb,b%num_detectors_per_ring); + for (int rb = get_min_rb(ra); rb <= get_max_rb(ra); ++rb) + for (int b = get_min_b(a); b <= get_max_b(a); ++b) + sum += (*this)(ra, a, rb, b % num_detectors_per_ring); return sum; } -float FanProjData::find_max() const -{ +float +FanProjData::find_max() const { return base_type::find_max(); } -float FanProjData::find_min() const -{ +float +FanProjData::find_min() const { return base_type::find_min(); } -int FanProjData::get_num_detectors_per_ring() const -{ +int +FanProjData::get_num_detectors_per_ring() const { return num_detectors_per_ring; } - -int FanProjData::get_num_rings() const -{ +int +FanProjData::get_num_rings() const { return num_rings; } -std::ostream& operator<<(std::ostream& s, const FanProjData& fan_data) -{ +std::ostream& +operator<<(std::ostream& s, const FanProjData& fan_data) { return s << static_cast(fan_data); } -std::istream& operator>>(std::istream& s, FanProjData& fan_data) -{ +std::istream& +operator>>(std::istream& s, FanProjData& fan_data) { s >> static_cast(fan_data); if (!s) return s; fan_data.num_detectors_per_ring = fan_data.get_max_a() - fan_data.get_min_a() + 1; fan_data.num_rings = fan_data.get_max_ra() - fan_data.get_min_ra() + 1; - - //int max_delta = 0; - //for (int ra = 0; ra < fan_data.num_rings; ++ra) + + // int max_delta = 0; + // for (int ra = 0; ra < fan_data.num_rings; ++ra) // max_delta = max(max_delta,fan_data[ra][0].get_length()-1); - const int max_delta = fan_data[0][0].get_length()-1; - const int half_fan_size = - fan_data[0][0][0].get_length()/2; + const int max_delta = fan_data[0][0].get_length() - 1; + const int half_fan_size = fan_data[0][0][0].get_length() / 2; fan_data.half_fan_size = half_fan_size; fan_data.max_ring_diff = max_delta; - for (int ra = 0; ra < fan_data.num_rings; ++ra) - { - const int min_rb = max(ra-max_delta, 0); - const int max_rb = min(ra+max_delta, fan_data.num_rings-1); - for (int a = 0; a < fan_data.num_detectors_per_ring; ++a) - { - if (fan_data[ra][a].get_length() != max_rb - max(ra,min_rb) + 1) - { + for (int ra = 0; ra < fan_data.num_rings; ++ra) { + const int min_rb = max(ra - max_delta, 0); + const int max_rb = min(ra + max_delta, fan_data.num_rings - 1); + for (int a = 0; a < fan_data.num_detectors_per_ring; ++a) { + if (fan_data[ra][a].get_length() != max_rb - max(ra, min_rb) + 1) { warning("Reading FanProjData: inconsistent length %d for rb at ra=%d, a=%d, " - "Expected length %d\n", - fan_data[ra][a].get_length(), ra, a, max_rb - max(ra,min_rb) + 1); + "Expected length %d\n", + fan_data[ra][a].get_length(), ra, a, max_rb - max(ra, min_rb) + 1); } - fan_data[ra][a].set_offset(max(ra,min_rb)); - for (int rb = fan_data[ra][a].get_min_index(); rb <= fan_data[ra][a].get_max_index(); ++rb) - { - if (fan_data[ra][a][rb].get_length() != 2*half_fan_size+1) - { + fan_data[ra][a].set_offset(max(ra, min_rb)); + for (int rb = fan_data[ra][a].get_min_index(); rb <= fan_data[ra][a].get_max_index(); ++rb) { + if (fan_data[ra][a][rb].get_length() != 2 * half_fan_size + 1) { warning("Reading FanProjData: inconsistent length %d for b at ra=%d, a=%d, rb=%d\n" - "Expected length %d\n", - fan_data[ra][a][rb].get_length(), ra, a, rb, 2*half_fan_size+1); + "Expected length %d\n", + fan_data[ra][a][rb].get_length(), ra, a, rb, 2 * half_fan_size + 1); } - fan_data[ra][a][rb].set_offset(a+fan_data.num_detectors_per_ring/2-half_fan_size); + fan_data[ra][a][rb].set_offset(a + fan_data.num_detectors_per_ring / 2 - half_fan_size); } } } @@ -946,328 +821,274 @@ std::istream& operator>>(std::istream& s, FanProjData& fan_data) } shared_ptr -get_fan_info(int& num_rings, int& num_detectors_per_ring, - int& max_ring_diff, int& fan_size, - const ProjDataInfo& proj_data_info) -{ - const ProjDataInfoCylindricalNoArcCorr * const proj_data_info_ptr = - dynamic_cast(&proj_data_info); - if (proj_data_info_ptr == 0) - { +get_fan_info(int& num_rings, int& num_detectors_per_ring, int& max_ring_diff, int& fan_size, const ProjDataInfo& proj_data_info) { + const ProjDataInfoCylindricalNoArcCorr* const proj_data_info_ptr = + dynamic_cast(&proj_data_info); + if (proj_data_info_ptr == 0) { error("Can only process not arc-corrected data\n"); } - if (proj_data_info_ptr->get_view_mashing_factor()>1) - { + if (proj_data_info_ptr->get_view_mashing_factor() > 1) { error("Can only process data without mashing of views\n"); } - if (proj_data_info_ptr->get_max_ring_difference(0)>0) - { + if (proj_data_info_ptr->get_max_ring_difference(0) > 0) { error("Can only process data without axial compression (i.e. span=1)\n"); } - num_rings = - proj_data_info.get_scanner_ptr()->get_num_rings(); - num_detectors_per_ring = - proj_data_info.get_scanner_ptr()->get_num_detectors_per_ring(); - const int half_fan_size = - min(proj_data_info.get_max_tangential_pos_num(), - -proj_data_info.get_min_tangential_pos_num()); - fan_size = 2*half_fan_size+1; + num_rings = proj_data_info.get_scanner_ptr()->get_num_rings(); + num_detectors_per_ring = proj_data_info.get_scanner_ptr()->get_num_detectors_per_ring(); + const int half_fan_size = min(proj_data_info.get_max_tangential_pos_num(), -proj_data_info.get_min_tangential_pos_num()); + fan_size = 2 * half_fan_size + 1; max_ring_diff = proj_data_info_ptr->get_max_segment_num(); - shared_ptr - ret_value(dynamic_cast(proj_data_info_ptr->clone())); + shared_ptr ret_value( + dynamic_cast(proj_data_info_ptr->clone())); return ret_value; - } -void make_fan_data(FanProjData& fan_data, - const ProjData& proj_data) -{ +void +make_fan_data(FanProjData& fan_data, const ProjData& proj_data) { int num_rings; 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."); + if (proj_data.get_proj_data_info_sptr()->is_tof_data()) + error("make_fan_data: Incompatible with TOF data. Abort."); shared_ptr proj_data_info_ptr = - get_fan_info(num_rings, num_detectors_per_ring, max_delta, fan_size, - *proj_data.get_proj_data_info_sptr()); + get_fan_info(num_rings, num_detectors_per_ring, max_delta, fan_size, *proj_data.get_proj_data_info_sptr()); - const int half_fan_size = fan_size/2; - fan_data = FanProjData(num_rings, num_detectors_per_ring, max_delta, 2*half_fan_size+1); + const int half_fan_size = fan_size / 2; + fan_data = FanProjData(num_rings, num_detectors_per_ring, max_delta, 2 * half_fan_size + 1); - shared_ptr > segment_ptr; + shared_ptr> segment_ptr; Bin bin; - for (bin.segment_num() = proj_data.get_min_segment_num(); bin.segment_num() <= proj_data.get_max_segment_num(); ++ 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.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; - - proj_data_info_ptr->get_det_pair_for_bin(a, ra, b, rb, bin); - - fan_data(ra, a, rb, b) = - fan_data(rb, b, ra, a) = - (*segment_ptr)[bin.axial_pos_num()][bin.view_num()][bin.tangential_pos_num()]; + for (bin.segment_num() = proj_data.get_min_segment_num(); bin.segment_num() <= proj_data.get_max_segment_num(); + ++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.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; + + proj_data_info_ptr->get_det_pair_for_bin(a, ra, b, rb, bin); + + fan_data(ra, a, rb, b) = fan_data(rb, b, ra, a) = + (*segment_ptr)[bin.axial_pos_num()][bin.view_num()][bin.tangential_pos_num()]; } - } + } } } /// **** This function make fan_data from projecion file while removing the intermodule gaps **** //// /// *** fan_data doesn't have gaps, proj_data has gaps *** /// -void make_fan_data_remove_gaps(FanProjData& fan_data, - const ProjData& proj_data) -{ - int num_rings; - int num_detectors_per_ring; - int fan_size; - int max_delta; - shared_ptr proj_data_info_ptr = - get_fan_info(num_rings, num_detectors_per_ring, max_delta, fan_size, - *proj_data.get_proj_data_info_sptr()); - - const int half_fan_size = fan_size/2; - - - // **** Added by me **** // - - - const int num_transaxial_blocks = - proj_data_info_ptr ->get_scanner_sptr()-> - get_num_transaxial_blocks(); - const int num_axial_blocks = - proj_data_info_ptr->get_scanner_sptr()-> - get_num_axial_blocks(); - const int num_transaxial_crystals_per_block = - proj_data_info_ptr->get_scanner_sptr()-> - get_num_transaxial_crystals_per_block(); - const int num_axial_crystals_per_block = - proj_data_info_ptr->get_scanner_sptr()-> - get_num_axial_crystals_per_block(); - - - - const int num_transaxial_blocks_in_fansize = fan_size/num_transaxial_crystals_per_block; - const int new_fan_size = fan_size - num_transaxial_blocks_in_fansize; - const int new_half_fan_size = new_fan_size/2; - const int num_axial_blocks_in_max_delta = max_delta/num_axial_crystals_per_block; - const int new_max_delta = max_delta - num_axial_blocks_in_max_delta - 1; - const int new_num_detectors_per_ring = num_detectors_per_ring - num_transaxial_blocks; - const int new_num_rings = num_rings - num_axial_blocks; - - // **** End **** // - - fan_data = FanProjData(new_num_rings, new_num_detectors_per_ring, new_max_delta, 2*new_half_fan_size+1); - - - shared_ptr > segment_ptr; - Bin bin; - - 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.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()) - { - int ra = 0, a = 0; - int rb = 0, b = 0; - - proj_data_info_ptr->get_det_pair_for_bin(a, ra, b, rb, bin); - int new_a = a - a/num_transaxial_crystals_per_block; - int new_b = b - b/num_transaxial_crystals_per_block; - int new_ra = ra - ra/ num_axial_crystals_per_block; - int new_rb = rb - rb/num_axial_crystals_per_block; - - if ((ra == num_rings -1) || (rb == num_rings -1) || (a == num_detectors_per_ring-1) || (b == num_detectors_per_ring-1)) continue; - - - fan_data(new_ra, new_a, new_rb, new_b) = - fan_data(new_rb, new_b, new_ra, new_a) = - (*segment_ptr)[bin.axial_pos_num()][bin.view_num()][bin.tangential_pos_num()]; - } - } -} +void +make_fan_data_remove_gaps(FanProjData& fan_data, const ProjData& proj_data) { + int num_rings; + int num_detectors_per_ring; + int fan_size; + int max_delta; + shared_ptr proj_data_info_ptr = + get_fan_info(num_rings, num_detectors_per_ring, max_delta, fan_size, *proj_data.get_proj_data_info_sptr()); + const int half_fan_size = fan_size / 2; -void set_fan_data(ProjData& proj_data, - const FanProjData& fan_data) -{ + // **** Added by me **** // + + const int num_transaxial_blocks = proj_data_info_ptr->get_scanner_sptr()->get_num_transaxial_blocks(); + const int num_axial_blocks = proj_data_info_ptr->get_scanner_sptr()->get_num_axial_blocks(); + const int num_transaxial_crystals_per_block = proj_data_info_ptr->get_scanner_sptr()->get_num_transaxial_crystals_per_block(); + const int num_axial_crystals_per_block = proj_data_info_ptr->get_scanner_sptr()->get_num_axial_crystals_per_block(); + + const int num_transaxial_blocks_in_fansize = fan_size / num_transaxial_crystals_per_block; + const int new_fan_size = fan_size - num_transaxial_blocks_in_fansize; + const int new_half_fan_size = new_fan_size / 2; + const int num_axial_blocks_in_max_delta = max_delta / num_axial_crystals_per_block; + const int new_max_delta = max_delta - num_axial_blocks_in_max_delta - 1; + const int new_num_detectors_per_ring = num_detectors_per_ring - num_transaxial_blocks; + const int new_num_rings = num_rings - num_axial_blocks; + + // **** End **** // + + fan_data = FanProjData(new_num_rings, new_num_detectors_per_ring, new_max_delta, 2 * new_half_fan_size + 1); + + shared_ptr> segment_ptr; + Bin bin; + + 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.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()) { + int ra = 0, a = 0; + int rb = 0, b = 0; + + proj_data_info_ptr->get_det_pair_for_bin(a, ra, b, rb, bin); + int new_a = a - a / num_transaxial_crystals_per_block; + int new_b = b - b / num_transaxial_crystals_per_block; + int new_ra = ra - ra / num_axial_crystals_per_block; + int new_rb = rb - rb / num_axial_crystals_per_block; + + if ((ra == num_rings - 1) || (rb == num_rings - 1) || (a == num_detectors_per_ring - 1) || + (b == num_detectors_per_ring - 1)) + continue; + + fan_data(new_ra, new_a, new_rb, new_b) = fan_data(new_rb, new_b, new_ra, new_a) = + (*segment_ptr)[bin.axial_pos_num()][bin.view_num()][bin.tangential_pos_num()]; + } + } +} + +void +set_fan_data(ProjData& proj_data, const FanProjData& fan_data) { int num_rings; 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."); + if (proj_data.get_proj_data_info_sptr()->is_tof_data()) + error("make_fan_data: Incompatible with TOF data. Abort."); shared_ptr proj_data_info_ptr = - get_fan_info(num_rings, num_detectors_per_ring, max_delta, fan_size, - *proj_data.get_proj_data_info_sptr()); + get_fan_info(num_rings, num_detectors_per_ring, max_delta, fan_size, *proj_data.get_proj_data_info_sptr()); - const int half_fan_size = fan_size/2; + const int half_fan_size = fan_size / 2; assert(num_rings == fan_data.get_num_rings()); assert(num_detectors_per_ring == fan_data.get_num_detectors_per_ring()); Bin bin; - shared_ptr > segment_ptr; - - for (bin.segment_num() = proj_data.get_min_segment_num(); bin.segment_num() <= proj_data.get_max_segment_num(); ++ 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_empty_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.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; - - proj_data_info_ptr->get_det_pair_for_bin(a, ra, b, rb, bin); - - (*segment_ptr)[bin.axial_pos_num()][bin.view_num()][bin.tangential_pos_num()] = - fan_data(ra, a, rb, b); - } - proj_data.set_segment(*segment_ptr); - } + shared_ptr> segment_ptr; + + for (bin.segment_num() = proj_data.get_min_segment_num(); bin.segment_num() <= proj_data.get_max_segment_num(); + ++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_empty_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.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; + + proj_data_info_ptr->get_det_pair_for_bin(a, ra, b, rb, bin); + + (*segment_ptr)[bin.axial_pos_num()][bin.view_num()][bin.tangential_pos_num()] = fan_data(ra, a, rb, b); + } + proj_data.set_segment(*segment_ptr); + } } } /// **** This function make proj_data from fan_data while adding the intermodule gaps **** //// /// *** fan_data doesn't have gaps, proj_data has gaps *** /// -void set_fan_data_add_gaps(ProjData& proj_data, - const FanProjData& fan_data) -{ - int num_rings; - int num_detectors_per_ring; - int fan_size; - int max_delta; - shared_ptr proj_data_info_ptr = - get_fan_info(num_rings, num_detectors_per_ring, max_delta, fan_size, - *proj_data.get_proj_data_info_sptr()); - - const int half_fan_size = fan_size/2; - //assert(num_rings == fan_data.get_num_rings()); - //assert(num_detectors_per_ring == fan_data.get_num_detectors_per_ring()); - - - // **** Added by Tahereh Nikjenad **** // - - const int num_transaxial_crystals_per_block = - proj_data_info_ptr->get_scanner_sptr()-> - get_num_transaxial_crystals_per_block(); - const int num_axial_crystals_per_block = - proj_data_info_ptr->get_scanner_sptr()-> - get_num_axial_crystals_per_block(); - - // const int num_axial_detectors = fan_data.get_num_rings(); // Number of ring in fan data (w/o gaps) - // const int num_transaxial_detectors = fan_data.get_num_detectors_per_ring(); // number of detector per ring in fan data w/o gaps - - // const int num_axial_crystals_per_block = num_axial_detectors/num_axial_blocks; // number of axial detector per block in fan_data w/o gaps - // const int num_transaxial_crystals_per_block = num_transaxial_detectors/num_transaxial_blocks; // number of transaxial detector per block in fan_data w/o gaps - - - // **** End **** // - - Bin bin; - shared_ptr > segment_ptr; - - 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_empty_segment_by_sinogram(bin.segment_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()) - { - int ra = 0, a = 0; - int rb = 0, b = 0; - - proj_data_info_ptr->get_det_pair_for_bin(a, ra, b, rb, bin); - - (*segment_ptr)[bin.axial_pos_num()][bin.view_num()][bin.tangential_pos_num()] = 1e20; - - - if ((ra+1)% num_axial_crystals_per_block == 0) continue; - if ((rb+1)% num_axial_crystals_per_block == 0) continue; - if ((a+1)% num_transaxial_crystals_per_block == 0) continue; - if ((b+1)% num_transaxial_crystals_per_block == 0) continue; - - int new_a = a - a/num_transaxial_crystals_per_block; - int new_b = b - b/num_transaxial_crystals_per_block; - int new_ra = ra - ra/ num_axial_crystals_per_block; - int new_rb = rb - rb/num_axial_crystals_per_block; - - - (*segment_ptr)[bin.axial_pos_num()][bin.view_num()][bin.tangential_pos_num()] = - fan_data(new_ra, new_a, new_rb, new_b); - } - proj_data.set_segment(*segment_ptr); - } -} +void +set_fan_data_add_gaps(ProjData& proj_data, const FanProjData& fan_data) { + int num_rings; + int num_detectors_per_ring; + int fan_size; + int max_delta; + shared_ptr proj_data_info_ptr = + get_fan_info(num_rings, num_detectors_per_ring, max_delta, fan_size, *proj_data.get_proj_data_info_sptr()); + const int half_fan_size = fan_size / 2; + // assert(num_rings == fan_data.get_num_rings()); + // assert(num_detectors_per_ring == fan_data.get_num_detectors_per_ring()); -void apply_block_norm(FanProjData& fan_data, const BlockData3D& block_data, const bool apply) -{ + // **** Added by Tahereh Nikjenad **** // + + const int num_transaxial_crystals_per_block = proj_data_info_ptr->get_scanner_sptr()->get_num_transaxial_crystals_per_block(); + const int num_axial_crystals_per_block = proj_data_info_ptr->get_scanner_sptr()->get_num_axial_crystals_per_block(); + + // const int num_axial_detectors = fan_data.get_num_rings(); // Number of ring in fan data (w/o gaps) + // const int num_transaxial_detectors = fan_data.get_num_detectors_per_ring(); // number of detector per ring in fan data w/o + // gaps + + // const int num_axial_crystals_per_block = num_axial_detectors/num_axial_blocks; // number of axial detector per block in + // fan_data w/o gaps const int num_transaxial_crystals_per_block = num_transaxial_detectors/num_transaxial_blocks; // number of + // transaxial detector per block in fan_data w/o gaps + + // **** End **** // + + Bin bin; + shared_ptr> segment_ptr; + + 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_empty_segment_by_sinogram(bin.segment_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()) { + int ra = 0, a = 0; + int rb = 0, b = 0; + + proj_data_info_ptr->get_det_pair_for_bin(a, ra, b, rb, bin); + + (*segment_ptr)[bin.axial_pos_num()][bin.view_num()][bin.tangential_pos_num()] = 1e20; + + if ((ra + 1) % num_axial_crystals_per_block == 0) + continue; + if ((rb + 1) % num_axial_crystals_per_block == 0) + continue; + if ((a + 1) % num_transaxial_crystals_per_block == 0) + continue; + if ((b + 1) % num_transaxial_crystals_per_block == 0) + continue; + + int new_a = a - a / num_transaxial_crystals_per_block; + int new_b = b - b / num_transaxial_crystals_per_block; + int new_ra = ra - ra / num_axial_crystals_per_block; + int new_rb = rb - rb / num_axial_crystals_per_block; + + (*segment_ptr)[bin.axial_pos_num()][bin.view_num()][bin.tangential_pos_num()] = fan_data(new_ra, new_a, new_rb, new_b); + } + proj_data.set_segment(*segment_ptr); + } +} + +void +apply_block_norm(FanProjData& fan_data, const BlockData3D& block_data, const bool apply) { const int num_axial_detectors = fan_data.get_num_rings(); const int num_tangential_detectors = fan_data.get_num_detectors_per_ring(); const int num_axial_blocks = block_data.get_num_rings(); const int num_tangential_blocks = block_data.get_num_detectors_per_ring(); - const int num_axial_crystals_per_block = num_axial_detectors/num_axial_blocks; + const int num_axial_crystals_per_block = num_axial_detectors / num_axial_blocks; assert(num_axial_blocks * num_axial_crystals_per_block == num_axial_detectors); - const int num_tangential_crystals_per_block = num_tangential_detectors/num_tangential_blocks; + const int num_tangential_crystals_per_block = num_tangential_detectors / num_tangential_blocks; assert(num_tangential_blocks * num_tangential_crystals_per_block == num_tangential_detectors); - + for (int ra = fan_data.get_min_ra(); ra <= fan_data.get_max_ra(); ++ra) for (int a = fan_data.get_min_a(); a <= fan_data.get_max_a(); ++a) // loop rb from ra to avoid double counting - for (int rb = max(ra,fan_data.get_min_rb(ra)); rb <= fan_data.get_max_rb(ra); ++rb) - for (int b = fan_data.get_min_b(a); b <= fan_data.get_max_b(a); ++b) - { - // note: add 2*num_detectors_per_ring to newb to avoid using mod with negative numbers - if (fan_data(ra,a,rb,b) == 0) - continue; - if (apply) - fan_data(ra,a,rb,b) *= - block_data(ra/num_axial_crystals_per_block,a/num_tangential_crystals_per_block, - rb/num_axial_crystals_per_block,b/num_tangential_crystals_per_block); - else - fan_data(ra,a,rb,b) /= - block_data(ra/num_axial_crystals_per_block,a/num_tangential_crystals_per_block, - rb/num_axial_crystals_per_block,b/num_tangential_crystals_per_block); - } + for (int rb = max(ra, fan_data.get_min_rb(ra)); rb <= fan_data.get_max_rb(ra); ++rb) + for (int b = fan_data.get_min_b(a); b <= fan_data.get_max_b(a); ++b) { + // note: add 2*num_detectors_per_ring to newb to avoid using mod with negative numbers + if (fan_data(ra, a, rb, b) == 0) + continue; + if (apply) + fan_data(ra, a, rb, b) *= block_data(ra / num_axial_crystals_per_block, a / num_tangential_crystals_per_block, + rb / num_axial_crystals_per_block, b / num_tangential_crystals_per_block); + else + fan_data(ra, a, rb, b) /= block_data(ra / num_axial_crystals_per_block, a / num_tangential_crystals_per_block, + rb / num_axial_crystals_per_block, b / num_tangential_crystals_per_block); + } } #if 0 @@ -1300,424 +1121,366 @@ void apply_geo_norm(FanProjData& fan_data, const GeoData& geo_data, const bool a } #endif -void apply_geo_norm(FanProjData& fan_data, const GeoData3D& geo_data, const bool apply) -{ - - const int num_axial_detectors = fan_data.get_num_rings(); - const int num_transaxial_detectors = fan_data.get_num_detectors_per_ring(); - const int num_axial_crystals_per_block = geo_data.get_num_axial_crystals_per_block(); - const int num_transaxial_crystals_per_block = geo_data.get_half_num_transaxial_crystals_per_block()*2; - - const int num_transaxial_blocks = num_transaxial_detectors/num_transaxial_crystals_per_block; - const int num_axial_blocks = num_axial_detectors/num_axial_crystals_per_block; - - FanProjData work = fan_data; - work.fill(0); - - - for (int ra = 0; ra < num_axial_crystals_per_block; ++ra) - for (int a = 0; a < num_transaxial_crystals_per_block /2; ++a) - // loop rb from ra to avoid double counting - for (int rb = max(ra,fan_data.get_min_rb(ra)); rb <= fan_data.get_max_rb(ra); ++rb) - for (int b = fan_data.get_min_b(a); b <= fan_data.get_max_b(a); ++b) - { - - - // rotation - - for (int axial_block_num = 0; axial_block_num& data_fan_sums, const FanProjData& fan_data) -{ +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) for (int a = fan_data.get_min_a(); a <= fan_data.get_max_a(); ++a) - data_fan_sums[ra][a] = fan_data.sum(ra,a); + data_fan_sums[ra][a] = fan_data.sum(ra, a); } -void make_fan_sum_data(Array<2,float>& data_fan_sums, - const ProjData& proj_data) -{ +void +make_fan_sum_data(Array<2, float>& data_fan_sums, const ProjData& proj_data) { int num_rings; 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."); + if (proj_data.get_proj_data_info_sptr()->is_tof_data()) + error("make_fan_data: Incompatible with TOF data. Abort."); shared_ptr proj_data_info_ptr = - get_fan_info(num_rings, num_detectors_per_ring, max_delta, fan_size, - *proj_data.get_proj_data_info_sptr()); - const int half_fan_size = fan_size/2; + get_fan_info(num_rings, num_detectors_per_ring, max_delta, fan_size, *proj_data.get_proj_data_info_sptr()); + const int half_fan_size = fan_size / 2; data_fan_sums.fill(0); - shared_ptr > segment_ptr; + shared_ptr> segment_ptr; Bin bin; - for (bin.segment_num() = proj_data.get_min_segment_num(); bin.segment_num() <= proj_data.get_max_segment_num(); ++ 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.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; - - proj_data_info_ptr->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; - } - } + for (bin.segment_num() = proj_data.get_min_segment_num(); bin.segment_num() <= proj_data.get_max_segment_num(); + ++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.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; + + proj_data_info_ptr->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; + } + } } } -void make_fan_sum_data(Array<2,float>& data_fan_sums, - const DetectorEfficiencies& efficiencies, - const int max_ring_diff, const int half_fan_size) -{ +void +make_fan_sum_data(Array<2, float>& data_fan_sums, const DetectorEfficiencies& efficiencies, const int max_ring_diff, + const int half_fan_size) { const int num_rings = data_fan_sums.get_length(); - assert(data_fan_sums.get_min_index()==0); - const int num_detectors_per_ring = - data_fan_sums[0].get_length(); + assert(data_fan_sums.get_min_index() == 0); + const int num_detectors_per_ring = data_fan_sums[0].get_length(); for (int ra = data_fan_sums.get_min_index(); ra <= data_fan_sums.get_max_index(); ++ra) - for (int a = data_fan_sums[ra].get_min_index(); a <= data_fan_sums[ra].get_max_index(); ++a) - { - float fan_sum = 0; - for (int rb = max(ra-max_ring_diff, 0); rb <= min(ra+max_ring_diff, num_rings-1); ++rb) - for (int b = a+num_detectors_per_ring/2-half_fan_size; b <= a+num_detectors_per_ring/2+half_fan_size; ++b) - fan_sum += efficiencies[rb][b%num_detectors_per_ring]; - data_fan_sums[ra][a] = efficiencies[ra][a]*fan_sum; - } + for (int a = data_fan_sums[ra].get_min_index(); a <= data_fan_sums[ra].get_max_index(); ++a) { + float fan_sum = 0; + for (int rb = max(ra - max_ring_diff, 0); rb <= min(ra + max_ring_diff, num_rings - 1); ++rb) + for (int b = a + num_detectors_per_ring / 2 - half_fan_size; b <= a + num_detectors_per_ring / 2 + half_fan_size; ++b) + fan_sum += efficiencies[rb][b % num_detectors_per_ring]; + data_fan_sums[ra][a] = efficiencies[ra][a] * fan_sum; + } } +void +make_geo_data(GeoData3D& geo_data, const FanProjData& fan_data) { -void make_geo_data(GeoData3D& geo_data, const FanProjData& fan_data) -{ - - const int num_axial_detectors = fan_data.get_num_rings(); - const int num_transaxial_detectors = fan_data.get_num_detectors_per_ring(); - const int num_axial_crystals_per_block = geo_data.get_num_axial_crystals_per_block(); - const int num_transaxial_crystals_per_block = geo_data.get_half_num_transaxial_crystals_per_block()*2; - const int num_transaxial_blocks = num_transaxial_detectors/num_transaxial_crystals_per_block; - const int num_axial_blocks = num_axial_detectors/num_axial_crystals_per_block; - - - // transaxial and axial mirroring - - FanProjData work = fan_data; - work.fill(0); - - for (int ra = fan_data.get_min_ra(); ra <= fan_data.get_max_ra(); ++ra) - for (int a = fan_data.get_min_a(); a <= fan_data.get_max_a(); ++a) - //1// for (int rb = fan_data.get_min_ra(); rb <= fan_data.get_max_ra(); ++rb) - for (int rb = max(ra,fan_data.get_min_rb(ra)); rb <= fan_data.get_max_rb(ra); ++rb) - for (int b = fan_data.get_min_b(a); b <= fan_data.get_max_b(a); ++b) - { - - const int ma = num_transaxial_detectors-1-a; - const int mb = (2*num_transaxial_detectors-1-b)%num_transaxial_detectors; - const int mra = num_axial_detectors-1-ra; - const int mrb = (num_axial_detectors-1-rb); - - - if (ra!=mra && rb !=mrb) - work(ra,a,rb,b)= fan_data(ra,a,rb,b) + fan_data(ra,ma,rb,mb) + fan_data(mra,a,mrb,b) + fan_data(mra,ma,mrb,mb); - else - work(ra,a,rb,b)= fan_data(ra,a,rb,b) + fan_data(ra,ma,rb,mb); - - } - - - geo_data.fill(0); - - - for (int ra = 0; ra < num_axial_crystals_per_block; ++ra) - // for (int a = 0; a <= num_transaxial_detectors/2; ++a) - for (int a = 0; a & data_fan_sums, - const FanProjData& model) -{ +void +iterate_efficiencies(DetectorEfficiencies& efficiencies, const Array<2, float>& data_fan_sums, const FanProjData& model) { const int num_detectors_per_ring = model.get_num_detectors_per_ring(); - + assert(model.get_min_ra() == data_fan_sums.get_min_index()); assert(model.get_max_ra() == data_fan_sums.get_max_index()); assert(model.get_min_a() == data_fan_sums[data_fan_sums.get_min_index()].get_min_index()); assert(model.get_max_a() == data_fan_sums[data_fan_sums.get_min_index()].get_max_index()); for (int ra = model.get_min_ra(); ra <= model.get_max_ra(); ++ra) - for (int a = model.get_min_a(); a <= model.get_max_a(); ++a) - { + for (int a = model.get_min_a(); a <= model.get_max_a(); ++a) { if (data_fan_sums[ra][a] == 0) - efficiencies[ra][a] = 0; - else - { - float denominator = 0; - for (int rb = model.get_min_rb(ra); rb <= model.get_max_rb(ra); ++rb) - for (int b = model.get_min_b(a); b <= model.get_max_b(a); ++b) - denominator += efficiencies[rb][b%num_detectors_per_ring]*model(ra,a,rb,b); - efficiencies[ra][a] = data_fan_sums[ra][a] / denominator; - } + efficiencies[ra][a] = 0; + else { + float denominator = 0; + for (int rb = model.get_min_rb(ra); rb <= model.get_max_rb(ra); ++rb) + for (int b = model.get_min_b(a); b <= model.get_max_b(a); ++b) + denominator += efficiencies[rb][b % num_detectors_per_ring] * model(ra, a, rb, b); + efficiencies[ra][a] = data_fan_sums[ra][a] / denominator; + } } } // version without model -void iterate_efficiencies(DetectorEfficiencies& efficiencies, - const Array<2,float>& data_fan_sums, - const int max_ring_diff, const int half_fan_size) -{ +void +iterate_efficiencies(DetectorEfficiencies& efficiencies, const Array<2, float>& data_fan_sums, const int max_ring_diff, + const int half_fan_size) { const int num_rings = data_fan_sums.get_length(); const int num_detectors_per_ring = data_fan_sums[data_fan_sums.get_min_index()].get_length(); #ifdef WRITE_ALL static int sub_iter_num = 0; #endif for (int ra = data_fan_sums.get_min_index(); ra <= data_fan_sums.get_max_index(); ++ra) - for (int a = data_fan_sums[ra].get_min_index(); a <= data_fan_sums[ra].get_max_index(); ++a) - { + for (int a = data_fan_sums[ra].get_min_index(); a <= data_fan_sums[ra].get_max_index(); ++a) { if (data_fan_sums[ra][a] == 0) - efficiencies[ra][a] = 0; - else - { - float denominator = 0; - for (int rb = max(ra-max_ring_diff, 0); rb <= min(ra+max_ring_diff, num_rings-1); ++rb) - for (int b = a+num_detectors_per_ring/2-half_fan_size; b <= a+num_detectors_per_ring/2+half_fan_size; ++b) - denominator += efficiencies[rb][b%num_detectors_per_ring]; - efficiencies[ra][a] = data_fan_sums[ra][a] / denominator; - } + efficiencies[ra][a] = 0; + else { + float denominator = 0; + for (int rb = max(ra - max_ring_diff, 0); rb <= min(ra + max_ring_diff, num_rings - 1); ++rb) + for (int b = a + num_detectors_per_ring / 2 - half_fan_size; b <= a + num_detectors_per_ring / 2 + half_fan_size; ++b) + denominator += efficiencies[rb][b % num_detectors_per_ring]; + efficiencies[ra][a] = data_fan_sums[ra][a] / denominator; + } #ifdef WRITE_ALL - { - char out_filename[100]; - sprintf(out_filename, "MLresult_subiter_eff_1_%d.out", - sub_iter_num++); - ofstream out(out_filename); - if (!out) - { - warning("Error opening output file %s\n", out_filename); - exit(EXIT_FAILURE); - } - out << efficiencies; - if (!out) - { - warning("Error writing data to output file %s\n", out_filename); - exit(EXIT_FAILURE); - } - } + { + char out_filename[100]; + sprintf(out_filename, "MLresult_subiter_eff_1_%d.out", sub_iter_num++); + ofstream out(out_filename); + if (!out) { + warning("Error opening output file %s\n", out_filename); + exit(EXIT_FAILURE); + } + out << efficiencies; + if (!out) { + warning("Error writing data to output file %s\n", out_filename); + exit(EXIT_FAILURE); + } + } #endif } } -void iterate_geo_norm(GeoData3D& norm_geo_data, - const GeoData3D& measured_geo_data, - const FanProjData& model) -{ - make_geo_data(norm_geo_data, model); - //norm_geo_data = measured_geo_data / norm_geo_data; - - const int num_axial_crystals_per_block = measured_geo_data.get_num_axial_crystals_per_block(); - const int num_transaxial_crystals_per_block = measured_geo_data.get_half_num_transaxial_crystals_per_block()*2; - - const float threshold = measured_geo_data.find_max()/10000.F; - - for (int ra = 0; ra < num_axial_crystals_per_block; ++ra) - for (int a = 0; a =threshold || measured_geo_data(ra,a,rb,b) < 10000*norm_geo_data(ra,a,rb,b))? measured_geo_data(ra,a,rb,b) / norm_geo_data(ra,a,rb,b) - : 0; - } +void +iterate_geo_norm(GeoData3D& norm_geo_data, const GeoData3D& measured_geo_data, const FanProjData& model) { + make_geo_data(norm_geo_data, model); + // norm_geo_data = measured_geo_data / norm_geo_data; + + const int num_axial_crystals_per_block = measured_geo_data.get_num_axial_crystals_per_block(); + const int num_transaxial_crystals_per_block = measured_geo_data.get_half_num_transaxial_crystals_per_block() * 2; + + const float threshold = measured_geo_data.find_max() / 10000.F; + + for (int ra = 0; ra < num_axial_crystals_per_block; ++ra) + for (int a = 0; a < num_transaxial_crystals_per_block / 2; ++a) + // loop rb from ra to avoid double counting + // for (int rb = model.get_min_ra(); rb <= model.get_max_ra(); ++rb) + for (int rb = max(ra, model.get_min_rb(ra)); rb <= model.get_max_rb(ra); ++rb) + for (int b = model.get_min_b(a); b <= model.get_max_b(a); ++b) { + + norm_geo_data(ra, a, rb, b) = (measured_geo_data(ra, a, rb, b) >= threshold || + measured_geo_data(ra, a, rb, b) < 10000 * norm_geo_data(ra, a, rb, b)) + ? measured_geo_data(ra, a, rb, b) / norm_geo_data(ra, a, rb, b) + : 0; + } } -void iterate_block_norm(BlockData3D& norm_block_data, - const BlockData3D& measured_block_data, - const FanProjData& model) -{ +void +iterate_block_norm(BlockData3D& norm_block_data, const BlockData3D& measured_block_data, const FanProjData& model) { make_block_data(norm_block_data, model); - //norm_block_data = measured_block_data / norm_block_data; - const float threshold = measured_block_data.find_max()/10000.F; + // norm_block_data = measured_block_data / norm_block_data; + const float threshold = measured_block_data.find_max() / 10000.F; for (int ra = norm_block_data.get_min_ra(); ra <= norm_block_data.get_max_ra(); ++ra) for (int a = norm_block_data.get_min_a(); a <= norm_block_data.get_max_a(); ++a) // loop rb from ra to avoid double counting - for (int rb = max(ra,norm_block_data.get_min_rb(ra)); rb <= norm_block_data.get_max_rb(ra); ++rb) - for (int b = norm_block_data.get_min_b(a); b <= norm_block_data.get_max_b(a); ++b) - { - norm_block_data(ra,a,rb,b) = - (measured_block_data(ra,a,rb,b)>=threshold || - measured_block_data(ra,a,rb,b) < 10000*norm_block_data(ra,a,rb,b)) - ? measured_block_data(ra,a,rb,b) / norm_block_data(ra,a,rb,b) - : 0; - } + for (int rb = max(ra, norm_block_data.get_min_rb(ra)); rb <= norm_block_data.get_max_rb(ra); ++rb) + for (int b = norm_block_data.get_min_b(a); b <= norm_block_data.get_max_b(a); ++b) { + norm_block_data(ra, a, rb, b) = (measured_block_data(ra, a, rb, b) >= threshold || + measured_block_data(ra, a, rb, b) < 10000 * norm_block_data(ra, a, rb, b)) + ? measured_block_data(ra, a, rb, b) / norm_block_data(ra, a, rb, b) + : 0; + } } - -double KL(const FanProjData& d1, const FanProjData& d2, const double threshold) -{ - double sum=0; - for (int ra = d1.get_min_ra(); ra <= d1.get_max_ra(); ++ra) - { - double asum=0; - for (int a = d1.get_min_a(); a <= d1.get_max_a(); ++a) - { - double rbsum=0; - for (int rb = max(ra,d1.get_min_rb(ra)); rb <= d1.get_max_rb(ra); ++rb) - { - double bsum=0; - for (int b = d1.get_min_b(a); b <= d1.get_max_b(a); ++b) - bsum += static_cast(KL(d1(ra,a,rb,b), d2(ra,a,rb,b), threshold)); - rbsum += bsum; - } - asum += rbsum; - } - sum += asum; +double +KL(const FanProjData& d1, const FanProjData& d2, const double threshold) { + double sum = 0; + for (int ra = d1.get_min_ra(); ra <= d1.get_max_ra(); ++ra) { + double asum = 0; + for (int a = d1.get_min_a(); a <= d1.get_max_a(); ++a) { + double rbsum = 0; + for (int rb = max(ra, d1.get_min_rb(ra)); rb <= d1.get_max_rb(ra); ++rb) { + double bsum = 0; + for (int b = d1.get_min_b(a); b <= d1.get_max_b(a); ++b) + bsum += static_cast(KL(d1(ra, a, rb, b), d2(ra, a, rb, b), threshold)); + rbsum += bsum; + } + asum += rbsum; } + sum += asum; + } return static_cast(sum); } @@ -1729,19 +1492,19 @@ double KL(const FanProjData& d1, const FanProjData& d2, const double threshold) const int num_transaxial_crystals_per_block = d1.get_half_num_transaxial_crystals_per_block()*2; double sum=0; - + for (int ra = 0; ra < num_axial_crystals_per_block; ++ra) - { - double asum=0; - for (int a = 0; a (KL(d1(ra,a,rb,b), d2(ra,a,rb,b), threshold)); rbsum += bsum; } @@ -1752,6 +1515,5 @@ double KL(const FanProjData& d1, const FanProjData& d2, const double threshold) return static_cast(sum); } */ - END_NAMESPACE_STIR diff --git a/src/buildblock/MaximalArrayFilter3D.cxx b/src/buildblock/MaximalArrayFilter3D.cxx index 8dad72e64a..f5addc5a59 100644 --- a/src/buildblock/MaximalArrayFilter3D.cxx +++ b/src/buildblock/MaximalArrayFilter3D.cxx @@ -2,17 +2,17 @@ Copyright (C) 2006 - 2007, Hammersmith Imanet Ltd Copyright (C) 2010 - 2013, King's College London This file is part of STIR. - + This file is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.3 of the License, or (at your option) any later version. - + This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - + See STIR/LICENSE.txt for details */ /*! @@ -32,16 +32,14 @@ START_NAMESPACE_STIR template -MaximalArrayFilter3D::MaximalArrayFilter3D(const Coordinate3D& mask_radius) -{ +MaximalArrayFilter3D::MaximalArrayFilter3D(const Coordinate3D& mask_radius) { this->mask_radius_x = mask_radius[3]; this->mask_radius_y = mask_radius[2]; this->mask_radius_z = mask_radius[1]; } template -MaximalArrayFilter3D::MaximalArrayFilter3D() -{ +MaximalArrayFilter3D::MaximalArrayFilter3D() { this->mask_radius_x = 0; this->mask_radius_y = 0; this->mask_radius_z = 0; @@ -49,61 +47,50 @@ MaximalArrayFilter3D::MaximalArrayFilter3D() template int -MaximalArrayFilter3D:: -extract_neighbours(Array<1,elemT>& neigbours,const Array<3,elemT>& in_array,const Coordinate3D& c_pixel) const -{ - int index=0; - - for (int zi =-mask_radius_z;zi<= mask_radius_z;++zi) - { - const int z = c_pixel[1]+zi; - if (z in_array.get_max_index()) - continue; - for (int yi =-mask_radius_y;yi<= mask_radius_y;++yi) - { - const int y = c_pixel[2]+yi; - if (y in_array[z].get_max_index()) - continue; - for( int xi=-mask_radius_x;xi<= mask_radius_x;++xi) - { - const int x = c_pixel[3]+xi; - if (x in_array[z][y].get_max_index()) - continue; - neigbours[index++] = in_array[z][y][x]; - } - } +MaximalArrayFilter3D::extract_neighbours(Array<1, elemT>& neigbours, const Array<3, elemT>& in_array, + const Coordinate3D& c_pixel) const { + int index = 0; + + for (int zi = -mask_radius_z; zi <= mask_radius_z; ++zi) { + const int z = c_pixel[1] + zi; + if (z < in_array.get_min_index() || z > in_array.get_max_index()) + continue; + for (int yi = -mask_radius_y; yi <= mask_radius_y; ++yi) { + const int y = c_pixel[2] + yi; + if (y < in_array[z].get_min_index() || y > in_array[z].get_max_index()) + continue; + for (int xi = -mask_radius_x; xi <= mask_radius_x; ++xi) { + const int x = c_pixel[3] + xi; + if (x < in_array[z][y].get_min_index() || x > in_array[z][y].get_max_index()) + continue; + neigbours[index++] = in_array[z][y][x]; + } } + } return index; } template void -MaximalArrayFilter3D:: -do_it(Array<3,elemT>& out_array, const Array<3,elemT>& in_array) const -{ +MaximalArrayFilter3D::do_it(Array<3, elemT>& out_array, const Array<3, elemT>& in_array) const { assert(out_array.get_index_range() == in_array.get_index_range()); - Array<1,elemT> neighbours (0,(2*mask_radius_x+1)*(2*mask_radius_y+1)*(2*mask_radius_z+1)-1); - - for (int z=out_array.get_min_index();z<= out_array.get_max_index();++z) - for (int y=out_array[z].get_min_index();y <= out_array[z].get_max_index();++y) - for (int x=out_array[z][y].get_min_index();x <= out_array[z][y].get_max_index();++x) - { - const int num_neighbours = - extract_neighbours(neighbours,in_array,Coordinate3D(z,y,x)); - if (num_neighbours==0) - continue; - out_array[z][y][x] = - *std::max_element(neighbours.begin(), neighbours.begin() + num_neighbours); - } + Array<1, elemT> neighbours(0, (2 * mask_radius_x + 1) * (2 * mask_radius_y + 1) * (2 * mask_radius_z + 1) - 1); + + for (int z = out_array.get_min_index(); z <= out_array.get_max_index(); ++z) + for (int y = out_array[z].get_min_index(); y <= out_array[z].get_max_index(); ++y) + for (int x = out_array[z][y].get_min_index(); x <= out_array[z][y].get_max_index(); ++x) { + const int num_neighbours = extract_neighbours(neighbours, in_array, Coordinate3D(z, y, x)); + if (num_neighbours == 0) + continue; + out_array[z][y][x] = *std::max_element(neighbours.begin(), neighbours.begin() + num_neighbours); + } } template bool -MaximalArrayFilter3D:: -is_trivial() const -{ - if (mask_radius_x!=1 &&mask_radius_y !=1 &&mask_radius_z!=1) +MaximalArrayFilter3D::is_trivial() const { + if (mask_radius_x != 1 && mask_radius_y != 1 && mask_radius_z != 1) return true; else return false; diff --git a/src/buildblock/MaximalImageFilter3D.cxx b/src/buildblock/MaximalImageFilter3D.cxx index 3513efe420..fc2b16fe45 100644 --- a/src/buildblock/MaximalImageFilter3D.cxx +++ b/src/buildblock/MaximalImageFilter3D.cxx @@ -2,24 +2,24 @@ Copyright (C) 2006 - 2007, Hammersmith Imanet Ltd Copyright (C) 2010 - 2013, King's College London This file is part of STIR. - + This file is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.3 of the License, or (at your option) any later version. - + This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - + See STIR/LICENSE.txt for details */ /*! \file \ingroup ImageProcessor \brief Implementations for class stir::MaximalImageFilter3D - + \author Charalampos Tsoumpas \author Kris Thielemans @@ -29,56 +29,48 @@ #include "stir/CartesianCoordinate3D.h" #include "stir/DiscretisedDensity.h" - START_NAMESPACE_STIR template -MaximalImageFilter3D:: MaximalImageFilter3D(const CartesianCoordinate3D& mask_radius) -{ +MaximalImageFilter3D::MaximalImageFilter3D(const CartesianCoordinate3D& mask_radius) { mask_radius_x = mask_radius.x(); mask_radius_y = mask_radius.y(); mask_radius_z = mask_radius.z(); } template -MaximalImageFilter3D:: MaximalImageFilter3D() -{ +MaximalImageFilter3D::MaximalImageFilter3D() { set_defaults(); } template Succeeded -MaximalImageFilter3D::virtual_set_up (const DiscretisedDensity<3,elemT>& density) -{ +MaximalImageFilter3D::virtual_set_up(const DiscretisedDensity<3, elemT>& density) { /* if (consistency_check(density) == Succeeded::no) return Succeeded::no;*/ - maximal_filter = - MaximalArrayFilter3D(Coordinate3D - (mask_radius_z, mask_radius_y, mask_radius_x)); + maximal_filter = MaximalArrayFilter3D(Coordinate3D(mask_radius_z, mask_radius_y, mask_radius_x)); return Succeeded::yes; } template void -MaximalImageFilter3D::virtual_apply(DiscretisedDensity<3, elemT>& density) const -{ - //assert(consistency_check(density) == Succeeded::yes); - maximal_filter(density); +MaximalImageFilter3D::virtual_apply(DiscretisedDensity<3, elemT>& density) const { + // assert(consistency_check(density) == Succeeded::yes); + maximal_filter(density); } template void -MaximalImageFilter3D::virtual_apply(DiscretisedDensity<3, elemT>& out_density, const DiscretisedDensity<3, elemT>& in_density) const -{ - //assert(consistency_check(in_density) == Succeeded::yes); - maximal_filter(out_density,in_density); +MaximalImageFilter3D::virtual_apply(DiscretisedDensity<3, elemT>& out_density, + const DiscretisedDensity<3, elemT>& in_density) const { + // assert(consistency_check(in_density) == Succeeded::yes); + maximal_filter(out_density, in_density); } template void -MaximalImageFilter3D::set_defaults() -{ +MaximalImageFilter3D::set_defaults() { base_type::set_defaults(); mask_radius_x = 0; @@ -87,9 +79,8 @@ MaximalImageFilter3D::set_defaults() } template -void -MaximalImageFilter3D::initialise_keymap() -{ +void +MaximalImageFilter3D::initialise_keymap() { base_type::initialise_keymap(); this->parser.add_start_key("Maximal Filter Parameters"); this->parser.add_key("mask radius x", &mask_radius_x); @@ -99,16 +90,13 @@ MaximalImageFilter3D::initialise_keymap() } template <> -const char * const -MaximalImageFilter3D::registered_name = - "Maximal"; - +const char* const MaximalImageFilter3D::registered_name = "Maximal"; -# ifdef _MSC_VER -// prevent warning message on reinstantiation, +#ifdef _MSC_VER +// prevent warning message on reinstantiation, // note that we get a linking error if we don't have the explicit instantiation below -# pragma warning(disable:4660) -# endif +# pragma warning(disable : 4660) +#endif template class MaximalImageFilter3D; diff --git a/src/buildblock/MedianArrayFilter3D.cxx b/src/buildblock/MedianArrayFilter3D.cxx index e7aba4a3c6..599ce6a995 100644 --- a/src/buildblock/MedianArrayFilter3D.cxx +++ b/src/buildblock/MedianArrayFilter3D.cxx @@ -28,7 +28,7 @@ */ /* History: SM first version - KT 19/03/2002 + KT 19/03/2002 change handling of edges (they were not filtered before) correct bug in case output and input array size were not the same */ @@ -43,102 +43,83 @@ using std::nth_element; START_NAMESPACE_STIR - template -MedianArrayFilter3D::MedianArrayFilter3D(const Coordinate3D& mask_radius) -{ +MedianArrayFilter3D::MedianArrayFilter3D(const Coordinate3D& mask_radius) { this->mask_radius_x = mask_radius[3]; this->mask_radius_y = mask_radius[2]; this->mask_radius_z = mask_radius[1]; - /* assert(mask_radius_x>0); - assert(mask_radius_x%2 == 1); - assert(mask_radius_y>0); - assert(mask_radius_y%2 == 1); - assert(mask_radius_z>0); - assert(mask_radius_z%2 == 1);*/ + /* assert(mask_radius_x>0); + assert(mask_radius_x%2 == 1); + assert(mask_radius_y>0); + assert(mask_radius_y%2 == 1); + assert(mask_radius_z>0); + assert(mask_radius_z%2 == 1);*/ } template -MedianArrayFilter3D::MedianArrayFilter3D() -{ +MedianArrayFilter3D::MedianArrayFilter3D() { this->mask_radius_x = 0; this->mask_radius_y = 0; this->mask_radius_z = 0; } - template int -MedianArrayFilter3D:: -extract_neighbours(Array<1,elemT>& neigbours,const Array<3,elemT>& in_array,const Coordinate3D& c_pixel) const -{ - int index=0; - - for (int zi =-mask_radius_z;zi<= mask_radius_z;++zi) - { - const int z = c_pixel[1]+zi; - if (z in_array.get_max_index()) - continue; - for (int yi =-mask_radius_y;yi<= mask_radius_y;++yi) - { - const int y = c_pixel[2]+yi; - if (y in_array[z].get_max_index()) - continue; - for( int xi=-mask_radius_x;xi<= mask_radius_x;++xi) - { - const int x = c_pixel[3]+xi; - if (x in_array[z][y].get_max_index()) - continue; - neigbours[index++] = in_array[z][y][x]; - } - } +MedianArrayFilter3D::extract_neighbours(Array<1, elemT>& neigbours, const Array<3, elemT>& in_array, + const Coordinate3D& c_pixel) const { + int index = 0; + + for (int zi = -mask_radius_z; zi <= mask_radius_z; ++zi) { + const int z = c_pixel[1] + zi; + if (z < in_array.get_min_index() || z > in_array.get_max_index()) + continue; + for (int yi = -mask_radius_y; yi <= mask_radius_y; ++yi) { + const int y = c_pixel[2] + yi; + if (y < in_array[z].get_min_index() || y > in_array[z].get_max_index()) + continue; + for (int xi = -mask_radius_x; xi <= mask_radius_x; ++xi) { + const int x = c_pixel[3] + xi; + if (x < in_array[z][y].get_min_index() || x > in_array[z][y].get_max_index()) + continue; + neigbours[index++] = in_array[z][y][x]; + } } + } return index; } template void -MedianArrayFilter3D:: -do_it(Array<3,elemT>& out_array, const Array<3,elemT>& in_array) const -{ +MedianArrayFilter3D::do_it(Array<3, elemT>& out_array, const Array<3, elemT>& in_array) const { assert(out_array.get_index_range() == in_array.get_index_range()); - Array<1,elemT> neighbours (0,(2*mask_radius_x+1)*(2*mask_radius_y+1)*(2*mask_radius_z+1)-1); - - for (int z=out_array.get_min_index();z<= out_array.get_max_index();++z) - for (int y=out_array[z].get_min_index();y <= out_array[z].get_max_index();++y) - for (int x=out_array[z][y].get_min_index();x <= out_array[z][y].get_max_index();++x) - { - const int num_neighbours = - extract_neighbours(neighbours,in_array,Coordinate3D(z,y,x)); - if (num_neighbours==0) - continue; - nth_element(neighbours.begin(), neighbours.begin()+num_neighbours/2, neighbours.end()); - if (num_neighbours%2==1) - out_array[z][y][x] = neighbours[num_neighbours/2]; - else - out_array[z][y][x] = (neighbours[num_neighbours/2]+ - neighbours[num_neighbours/2 - 1])/2; - } + Array<1, elemT> neighbours(0, (2 * mask_radius_x + 1) * (2 * mask_radius_y + 1) * (2 * mask_radius_z + 1) - 1); + + for (int z = out_array.get_min_index(); z <= out_array.get_max_index(); ++z) + for (int y = out_array[z].get_min_index(); y <= out_array[z].get_max_index(); ++y) + for (int x = out_array[z][y].get_min_index(); x <= out_array[z][y].get_max_index(); ++x) { + const int num_neighbours = extract_neighbours(neighbours, in_array, Coordinate3D(z, y, x)); + if (num_neighbours == 0) + continue; + nth_element(neighbours.begin(), neighbours.begin() + num_neighbours / 2, neighbours.end()); + if (num_neighbours % 2 == 1) + out_array[z][y][x] = neighbours[num_neighbours / 2]; + else + out_array[z][y][x] = (neighbours[num_neighbours / 2] + neighbours[num_neighbours / 2 - 1]) / 2; + } } - template bool -MedianArrayFilter3D:: -is_trivial() const -{ - if (mask_radius_x!=1 &&mask_radius_y !=1 &&mask_radius_z!=1) +MedianArrayFilter3D::is_trivial() const { + if (mask_radius_x != 1 && mask_radius_y != 1 && mask_radius_z != 1) return true; else return false; - } - // instantiation template class MedianArrayFilter3D; END_NAMESPACE_STIR - diff --git a/src/buildblock/MedianImageFilter3D.cxx b/src/buildblock/MedianImageFilter3D.cxx index 7ae3a7a07e..045e7f7602 100644 --- a/src/buildblock/MedianImageFilter3D.cxx +++ b/src/buildblock/MedianImageFilter3D.cxx @@ -30,57 +30,49 @@ #include "stir/CartesianCoordinate3D.h" #include "stir/DiscretisedDensity.h" - START_NAMESPACE_STIR template -MedianImageFilter3D:: MedianImageFilter3D(const CartesianCoordinate3D& mask_radius) -{ +MedianImageFilter3D::MedianImageFilter3D(const CartesianCoordinate3D& mask_radius) { mask_radius_x = mask_radius.x(); mask_radius_y = mask_radius.y(); mask_radius_z = mask_radius.z(); } template -MedianImageFilter3D:: MedianImageFilter3D() -{ +MedianImageFilter3D::MedianImageFilter3D() { set_defaults(); } template Succeeded -MedianImageFilter3D::virtual_set_up (const DiscretisedDensity<3,elemT>& density) -{ +MedianImageFilter3D::virtual_set_up(const DiscretisedDensity<3, elemT>& density) { -/* if (consistency_check(density) == Succeeded::no) - return Succeeded::no;*/ - median_filter = - MedianArrayFilter3D(Coordinate3D - (mask_radius_z, mask_radius_y, mask_radius_x)); + /* if (consistency_check(density) == Succeeded::no) + return Succeeded::no;*/ + median_filter = MedianArrayFilter3D(Coordinate3D(mask_radius_z, mask_radius_y, mask_radius_x)); - return Succeeded::yes; + return Succeeded::yes; } template void -MedianImageFilter3D::virtual_apply(DiscretisedDensity<3, elemT>& density) const -{ - //assert(consistency_check(density) == Succeeded::yes); - median_filter(density); +MedianImageFilter3D::virtual_apply(DiscretisedDensity<3, elemT>& density) const { + // assert(consistency_check(density) == Succeeded::yes); + median_filter(density); } template void -MedianImageFilter3D::virtual_apply(DiscretisedDensity<3, elemT>& out_density, const DiscretisedDensity<3, elemT>& in_density) const -{ - //assert(consistency_check(in_density) == Succeeded::yes); - median_filter(out_density,in_density); +MedianImageFilter3D::virtual_apply(DiscretisedDensity<3, elemT>& out_density, + const DiscretisedDensity<3, elemT>& in_density) const { + // assert(consistency_check(in_density) == Succeeded::yes); + median_filter(out_density, in_density); } template void -MedianImageFilter3D::set_defaults() -{ +MedianImageFilter3D::set_defaults() { base_type::set_defaults(); mask_radius_x = 0; @@ -89,9 +81,8 @@ MedianImageFilter3D::set_defaults() } template -void -MedianImageFilter3D::initialise_keymap() -{ +void +MedianImageFilter3D::initialise_keymap() { base_type::initialise_keymap(); this->parser.add_start_key("Median Filter Parameters"); this->parser.add_key("mask radius x", &mask_radius_x); @@ -101,20 +92,16 @@ MedianImageFilter3D::initialise_keymap() } template <> -const char * const -MedianImageFilter3D::registered_name = - "Median"; +const char* const MedianImageFilter3D::registered_name = "Median"; - -# ifdef _MSC_VER -// prevent warning message on reinstantiation, +#ifdef _MSC_VER +// prevent warning message on reinstantiation, // note that we get a linking error if we don't have the explicit instantiation below -# pragma warning(disable:4660) -# endif - +# pragma warning(disable : 4660) +#endif // Register this class in the ImageProcessor registry -//static MedianImageFilter3D::RegisterIt dummy; +// static MedianImageFilter3D::RegisterIt dummy; template class MedianImageFilter3D; diff --git a/src/buildblock/MinimalArrayFilter3D.cxx b/src/buildblock/MinimalArrayFilter3D.cxx index 656454320f..1d40db4efa 100644 --- a/src/buildblock/MinimalArrayFilter3D.cxx +++ b/src/buildblock/MinimalArrayFilter3D.cxx @@ -33,16 +33,14 @@ START_NAMESPACE_STIR template -MinimalArrayFilter3D::MinimalArrayFilter3D(const Coordinate3D& mask_radius) -{ +MinimalArrayFilter3D::MinimalArrayFilter3D(const Coordinate3D& mask_radius) { this->mask_radius_x = mask_radius[3]; this->mask_radius_y = mask_radius[2]; this->mask_radius_z = mask_radius[1]; } template -MinimalArrayFilter3D::MinimalArrayFilter3D() -{ +MinimalArrayFilter3D::MinimalArrayFilter3D() { this->mask_radius_x = 0; this->mask_radius_y = 0; this->mask_radius_z = 0; @@ -50,65 +48,53 @@ MinimalArrayFilter3D::MinimalArrayFilter3D() template int -MinimalArrayFilter3D:: -extract_neighbours(Array<1,elemT>& neigbours,const Array<3,elemT>& in_array,const Coordinate3D& c_pixel) const -{ - int index=0; - - for (int zi =-mask_radius_z;zi<= mask_radius_z;++zi) - { - const int z = c_pixel[1]+zi; - if (z in_array.get_max_index()) - continue; - for (int yi =-mask_radius_y;yi<= mask_radius_y;++yi) - { - const int y = c_pixel[2]+yi; - if (y in_array[z].get_max_index()) - continue; - for( int xi=-mask_radius_x;xi<= mask_radius_x;++xi) - { - const int x = c_pixel[3]+xi; - if (x in_array[z][y].get_max_index()) - continue; - neigbours[index++] = in_array[z][y][x]; - } - } +MinimalArrayFilter3D::extract_neighbours(Array<1, elemT>& neigbours, const Array<3, elemT>& in_array, + const Coordinate3D& c_pixel) const { + int index = 0; + + for (int zi = -mask_radius_z; zi <= mask_radius_z; ++zi) { + const int z = c_pixel[1] + zi; + if (z < in_array.get_min_index() || z > in_array.get_max_index()) + continue; + for (int yi = -mask_radius_y; yi <= mask_radius_y; ++yi) { + const int y = c_pixel[2] + yi; + if (y < in_array[z].get_min_index() || y > in_array[z].get_max_index()) + continue; + for (int xi = -mask_radius_x; xi <= mask_radius_x; ++xi) { + const int x = c_pixel[3] + xi; + if (x < in_array[z][y].get_min_index() || x > in_array[z][y].get_max_index()) + continue; + neigbours[index++] = in_array[z][y][x]; + } } + } return index; } template void -MinimalArrayFilter3D:: -do_it(Array<3,elemT>& out_array, const Array<3,elemT>& in_array) const -{ +MinimalArrayFilter3D::do_it(Array<3, elemT>& out_array, const Array<3, elemT>& in_array) const { assert(out_array.get_index_range() == in_array.get_index_range()); - Array<1,elemT> neighbours (0,(2*mask_radius_x+1)*(2*mask_radius_y+1)*(2*mask_radius_z+1)-1); - - for (int z=out_array.get_min_index();z<= out_array.get_max_index();++z) - for (int y=out_array[z].get_min_index();y <= out_array[z].get_max_index();++y) - for (int x=out_array[z][y].get_min_index();x <= out_array[z][y].get_max_index();++x) - { - const int num_neighbours = - extract_neighbours(neighbours,in_array,Coordinate3D(z,y,x)); - if (num_neighbours==0) - continue; - out_array[z][y][x] = - *std::min_element(neighbours.begin(), neighbours.begin() + num_neighbours); - } + Array<1, elemT> neighbours(0, (2 * mask_radius_x + 1) * (2 * mask_radius_y + 1) * (2 * mask_radius_z + 1) - 1); + + for (int z = out_array.get_min_index(); z <= out_array.get_max_index(); ++z) + for (int y = out_array[z].get_min_index(); y <= out_array[z].get_max_index(); ++y) + for (int x = out_array[z][y].get_min_index(); x <= out_array[z][y].get_max_index(); ++x) { + const int num_neighbours = extract_neighbours(neighbours, in_array, Coordinate3D(z, y, x)); + if (num_neighbours == 0) + continue; + out_array[z][y][x] = *std::min_element(neighbours.begin(), neighbours.begin() + num_neighbours); + } } template bool -MinimalArrayFilter3D:: -is_trivial() const -{ - if (mask_radius_x!=1 &&mask_radius_y !=1 &&mask_radius_z!=1) +MinimalArrayFilter3D::is_trivial() const { + if (mask_radius_x != 1 && mask_radius_y != 1 && mask_radius_z != 1) return true; else return false; - } // instantiation diff --git a/src/buildblock/MinimalImageFilter3D.cxx b/src/buildblock/MinimalImageFilter3D.cxx index 5c0ecff61e..443399c64e 100644 --- a/src/buildblock/MinimalImageFilter3D.cxx +++ b/src/buildblock/MinimalImageFilter3D.cxx @@ -30,57 +30,49 @@ #include "stir/CartesianCoordinate3D.h" #include "stir/DiscretisedDensity.h" - START_NAMESPACE_STIR template -MinimalImageFilter3D:: MinimalImageFilter3D(const CartesianCoordinate3D& mask_radius) -{ +MinimalImageFilter3D::MinimalImageFilter3D(const CartesianCoordinate3D& mask_radius) { mask_radius_x = mask_radius.x(); mask_radius_y = mask_radius.y(); mask_radius_z = mask_radius.z(); } template -MinimalImageFilter3D:: MinimalImageFilter3D() -{ +MinimalImageFilter3D::MinimalImageFilter3D() { set_defaults(); } template Succeeded -MinimalImageFilter3D::virtual_set_up (const DiscretisedDensity<3,elemT>& density) -{ +MinimalImageFilter3D::virtual_set_up(const DiscretisedDensity<3, elemT>& density) { -/* if (consistency_check(density) == Succeeded::no) - return Succeeded::no;*/ - minimal_filter = - MinimalArrayFilter3D(Coordinate3D - (mask_radius_z, mask_radius_y, mask_radius_x)); + /* if (consistency_check(density) == Succeeded::no) + return Succeeded::no;*/ + minimal_filter = MinimalArrayFilter3D(Coordinate3D(mask_radius_z, mask_radius_y, mask_radius_x)); - return Succeeded::yes; + return Succeeded::yes; } template void -MinimalImageFilter3D::virtual_apply(DiscretisedDensity<3, elemT>& density) const -{ - //assert(consistency_check(density) == Succeeded::yes); - minimal_filter(density); +MinimalImageFilter3D::virtual_apply(DiscretisedDensity<3, elemT>& density) const { + // assert(consistency_check(density) == Succeeded::yes); + minimal_filter(density); } template void -MinimalImageFilter3D::virtual_apply(DiscretisedDensity<3, elemT>& out_density, const DiscretisedDensity<3, elemT>& in_density) const -{ - //assert(consistency_check(in_density) == Succeeded::yes); - minimal_filter(out_density,in_density); +MinimalImageFilter3D::virtual_apply(DiscretisedDensity<3, elemT>& out_density, + const DiscretisedDensity<3, elemT>& in_density) const { + // assert(consistency_check(in_density) == Succeeded::yes); + minimal_filter(out_density, in_density); } template void -MinimalImageFilter3D::set_defaults() -{ +MinimalImageFilter3D::set_defaults() { base_type::set_defaults(); mask_radius_x = 0; @@ -89,9 +81,8 @@ MinimalImageFilter3D::set_defaults() } template -void -MinimalImageFilter3D::initialise_keymap() -{ +void +MinimalImageFilter3D::initialise_keymap() { base_type::initialise_keymap(); this->parser.add_start_key("Minimal Filter Parameters"); this->parser.add_key("mask radius x", &mask_radius_x); @@ -101,16 +92,13 @@ MinimalImageFilter3D::initialise_keymap() } template <> -const char * const -MinimalImageFilter3D::registered_name = - "Minimal"; - +const char* const MinimalImageFilter3D::registered_name = "Minimal"; -# ifdef _MSC_VER -// prevent warning message on reinstantiation, +#ifdef _MSC_VER +// prevent warning message on reinstantiation, // note that we get a linking error if we don't have the explicit instantiation below -# pragma warning(disable:4660) -# endif +# pragma warning(disable : 4660) +#endif template class MinimalImageFilter3D; diff --git a/src/buildblock/MultipleDataSetHeader.cxx b/src/buildblock/MultipleDataSetHeader.cxx index cc8dfcce93..d3fba8e40b 100644 --- a/src/buildblock/MultipleDataSetHeader.cxx +++ b/src/buildblock/MultipleDataSetHeader.cxx @@ -22,7 +22,7 @@ \ingroup data_buildblock \brief Implementation of class stir::MultipleDataSetHeader \author Richard Brown - + */ #include "stir/MultipleDataSetHeader.h" @@ -30,54 +30,45 @@ START_NAMESPACE_STIR -const char * const MultipleDataSetHeader:: -registered_name = "MultipleDataSetHeader"; +const char* const MultipleDataSetHeader::registered_name = "MultipleDataSetHeader"; -MultipleDataSetHeader:: -MultipleDataSetHeader() -{ - this->set_defaults(); - this->initialise_keymap(); +MultipleDataSetHeader::MultipleDataSetHeader() { + this->set_defaults(); + this->initialise_keymap(); } -void MultipleDataSetHeader:: -set_defaults() -{ - _num_data_sets = 0; +void +MultipleDataSetHeader::set_defaults() { + _num_data_sets = 0; } -void MultipleDataSetHeader:: -initialise_keymap() -{ - this->add_start_key("Multi"); - this->add_stop_key("End"); +void +MultipleDataSetHeader::initialise_keymap() { + this->add_start_key("Multi"); + this->add_stop_key("End"); - this->add_key("total number of data sets", - KeyArgument::INT, - static_cast(&MultipleDataSetHeader::read_num_data_sets), - &_num_data_sets); - this->add_vectorised_key("data set", &_filenames); + this->add_key("total number of data sets", KeyArgument::INT, + static_cast(&MultipleDataSetHeader::read_num_data_sets), &_num_data_sets); + this->add_vectorised_key("data set", &_filenames); } -bool MultipleDataSetHeader:: -post_processing() -{ - bool empty_filenames = false; - for (int i=0; i<_num_data_sets; ++i) { - if (_filenames[i].size() == unsigned(0)) { - warning(boost::format("MultipleDataSetHeader: Data set[%1%] is empty.") % i); - empty_filenames = true; - } +bool +MultipleDataSetHeader::post_processing() { + bool empty_filenames = false; + for (int i = 0; i < _num_data_sets; ++i) { + if (_filenames[i].size() == unsigned(0)) { + warning(boost::format("MultipleDataSetHeader: Data set[%1%] is empty.") % i); + empty_filenames = true; } + } - return empty_filenames; + return empty_filenames; } -void MultipleDataSetHeader:: -read_num_data_sets() -{ - set_variable(); - _filenames.resize(_num_data_sets); +void +MultipleDataSetHeader::read_num_data_sets() { + set_variable(); + _filenames.resize(_num_data_sets); } END_NAMESPACE_STIR diff --git a/src/buildblock/MultipleProjData.cxx b/src/buildblock/MultipleProjData.cxx index e29afa06dc..c23462c706 100644 --- a/src/buildblock/MultipleProjData.cxx +++ b/src/buildblock/MultipleProjData.cxx @@ -23,7 +23,7 @@ \brief Implementation of class stir::MultipleProjData \author Kris Thielemans \author Richard Brown - + */ #include "stir/MultipleProjData.h" @@ -37,66 +37,56 @@ START_NAMESPACE_STIR const shared_ptr -MultipleProjData:: -get_proj_data_info_sptr() const -{ +MultipleProjData::get_proj_data_info_sptr() const { if (get_num_gates() == 0) return shared_ptr(); else return _proj_datas[0]->get_proj_data_info_sptr(); } -void -MultipleProjData:: -set_proj_data_sptr(const shared_ptr& proj_data_sptr, - const unsigned int gate_num) -{ - this->_proj_datas[gate_num-1]=proj_data_sptr; -} +void +MultipleProjData::set_proj_data_sptr(const shared_ptr& proj_data_sptr, const unsigned int gate_num) { + this->_proj_datas[gate_num - 1] = proj_data_sptr; +} -MultipleProjData:: -MultipleProjData(const shared_ptr& exam_info_sptr, - const int num_gates) : - ExamData(exam_info_sptr) -{ - this->_proj_datas.resize(num_gates); +MultipleProjData::MultipleProjData(const shared_ptr& exam_info_sptr, const int num_gates) + : ExamData(exam_info_sptr) { + this->_proj_datas.resize(num_gates); } unique_ptr -MultipleProjData:: -read_from_file(const std::string ¶meter_file) -{ - MultipleDataSetHeader header; - - if (header.parse(parameter_file.c_str()) == false) - error(boost::format("MultipleProjData::read_from_file: Error parsing %1%") % parameter_file); - - const int num_data_sets = header.get_num_data_sets(); - - if (num_data_sets == 0) - error("MultipleProjData::read_from_file: no data sets provided"); - - // Create the multiple proj data - unique_ptr multiple_proj_data( new MultipleProjData ); - - // Read the projdata - for (int i=0; i_proj_datas.push_back(ProjData::read_from_file(header.get_filename(i))); - } - - // Get the exam info (from the first ProjData) - ExamInfo exam_info = multiple_proj_data->_proj_datas.at(0)->get_exam_info(); - - // Update the time definitions based on each individual frame - exam_info.time_frame_definitions.set_num_time_frames(num_data_sets); - for (int i=0; i_proj_datas[i]->get_exam_info().time_frame_definitions; - exam_info.time_frame_definitions.set_time_frame(i+1, tdef.get_start_time(1), tdef.get_end_time(1)); - } - multiple_proj_data->set_exam_info(exam_info); - return multiple_proj_data; +MultipleProjData::read_from_file(const std::string& parameter_file) { + MultipleDataSetHeader header; + + if (header.parse(parameter_file.c_str()) == false) + error(boost::format("MultipleProjData::read_from_file: Error parsing %1%") % parameter_file); + + const int num_data_sets = header.get_num_data_sets(); + + if (num_data_sets == 0) + error("MultipleProjData::read_from_file: no data sets provided"); + + // Create the multiple proj data + unique_ptr multiple_proj_data(new MultipleProjData); + + // Read the projdata + for (int i = 0; i < num_data_sets; ++i) { + info(boost::format("MultipleProjData::read_from_file: Reading %1%") % header.get_filename(i)); + // Create each of the individual proj datas + multiple_proj_data->_proj_datas.push_back(ProjData::read_from_file(header.get_filename(i))); + } + + // Get the exam info (from the first ProjData) + ExamInfo exam_info = multiple_proj_data->_proj_datas.at(0)->get_exam_info(); + + // Update the time definitions based on each individual frame + exam_info.time_frame_definitions.set_num_time_frames(num_data_sets); + for (int i = 0; i < num_data_sets; ++i) { + const TimeFrameDefinitions& tdef = multiple_proj_data->_proj_datas[i]->get_exam_info().time_frame_definitions; + exam_info.time_frame_definitions.set_time_frame(i + 1, tdef.get_start_time(1), tdef.get_end_time(1)); + } + multiple_proj_data->set_exam_info(exam_info); + return multiple_proj_data; } #if 0 diff --git a/src/buildblock/NonseparableConvolutionUsingRealDFTImageFilter.cxx b/src/buildblock/NonseparableConvolutionUsingRealDFTImageFilter.cxx index e8657890aa..8ce30e1c00 100644 --- a/src/buildblock/NonseparableConvolutionUsingRealDFTImageFilter.cxx +++ b/src/buildblock/NonseparableConvolutionUsingRealDFTImageFilter.cxx @@ -20,7 +20,7 @@ \file \ingroup ImageProcessor \brief Implementations for class stir::NonseparableConvolutionUsingRealDFTImageFilter - + \author Sanida Mustafovic \author Kris Thielemans */ @@ -32,137 +32,117 @@ #include "stir/Array_complex_numbers.h" #include "stir/IO/read_from_file.h" - START_NAMESPACE_STIR // fixed to 3D at present static const int num_dimensions = 3; template -NonseparableConvolutionUsingRealDFTImageFilter:: -NonseparableConvolutionUsingRealDFTImageFilter() -{ +NonseparableConvolutionUsingRealDFTImageFilter::NonseparableConvolutionUsingRealDFTImageFilter() { this->set_defaults(); } template -NonseparableConvolutionUsingRealDFTImageFilter:: -NonseparableConvolutionUsingRealDFTImageFilter( const Array<3,elemT>& filter_coefficients ) -{ - this->_filter_coefficients = filter_coefficients; +NonseparableConvolutionUsingRealDFTImageFilter::NonseparableConvolutionUsingRealDFTImageFilter( + const Array<3, elemT>& filter_coefficients) { + this->_filter_coefficients = filter_coefficients; } template -Succeeded -NonseparableConvolutionUsingRealDFTImageFilter:: -virtual_set_up(const DiscretisedDensity<3,elemT>& density) -{ +Succeeded +NonseparableConvolutionUsingRealDFTImageFilter::virtual_set_up(const DiscretisedDensity<3, elemT>& density) { BasicCoordinate min_indices, max_indices; if (!density.get_regular_range(min_indices, max_indices)) return Succeeded::no; - BasicCoordinate padded_sizes = max_indices - min_indices +1; + BasicCoordinate padded_sizes = max_indices - min_indices + 1; if (!this->_filter_coefficients.get_regular_range(min_indices, max_indices)) return Succeeded::no; - padded_sizes += max_indices - min_indices +1; + padded_sizes += max_indices - min_indices + 1; // remove 1 to be accurate - padded_sizes -= 1; + padded_sizes -= 1; // need to make it a power of 2 for the DFT implementation - for (int d=1; d<= num_dimensions; ++d) - { - padded_sizes[d] = - static_cast(round(pow(2., ceil(log(static_cast(padded_sizes[d])) / log(2.))))); - } + for (int d = 1; d <= num_dimensions; ++d) { + padded_sizes[d] = static_cast(round(pow(2., ceil(log(static_cast(padded_sizes[d])) / log(2.))))); + } IndexRange padding_range(padded_sizes); Array padded_filter_coefficients(padding_range); transform_array_to_periodic_indices(padded_filter_coefficients, this->_filter_coefficients); - this->_array_filter_sptr.reset( - new ArrayFilterUsingRealDFTWithPadding(padded_filter_coefficients)); - return Succeeded::yes; + this->_array_filter_sptr.reset(new ArrayFilterUsingRealDFTWithPadding(padded_filter_coefficients)); + return Succeeded::yes; } template void -NonseparableConvolutionUsingRealDFTImageFilter:: -virtual_apply(DiscretisedDensity<3,elemT>& out_density, const DiscretisedDensity<3,elemT>& in_density) const -{ +NonseparableConvolutionUsingRealDFTImageFilter::virtual_apply(DiscretisedDensity<3, elemT>& out_density, + const DiscretisedDensity<3, elemT>& in_density) const { (*this->_array_filter_sptr)(out_density, in_density); } template void -NonseparableConvolutionUsingRealDFTImageFilter:: -virtual_apply(DiscretisedDensity<3,elemT>& density) const -{ +NonseparableConvolutionUsingRealDFTImageFilter::virtual_apply(DiscretisedDensity<3, elemT>& density) const { // should use scoped_ptr but don't have it yet - shared_ptr > tmp_density_sptr(density.clone()); + shared_ptr> tmp_density_sptr(density.clone()); this->virtual_apply(density, *tmp_density_sptr); } - template void -NonseparableConvolutionUsingRealDFTImageFilter::set_defaults() -{ - this->_kernel_filename=""; +NonseparableConvolutionUsingRealDFTImageFilter::set_defaults() { + this->_kernel_filename = ""; this->_filter_coefficients.fill(0.F); } - + template void -NonseparableConvolutionUsingRealDFTImageFilter:: initialise_keymap() -{ +NonseparableConvolutionUsingRealDFTImageFilter::initialise_keymap() { this->parser.add_start_key("Nonseparable Convolution Using Real DFT Image Filter"); this->parser.add_key("filter kernel", &this->_kernel_filename); this->parser.add_stop_key("END Nonseparable Convolution Using Real DFT Image Filter"); } - + template -bool -NonseparableConvolutionUsingRealDFTImageFilter:: -post_processing() -{ - if (this->_kernel_filename.length() == 0) - { warning("You need to specify a kernel file"); return true; } - else - this->_kernel_sptr = read_from_file >(this->_kernel_filename); - const DiscretisedDensity<3,elemT>& kernel = *this->_kernel_sptr; +bool +NonseparableConvolutionUsingRealDFTImageFilter::post_processing() { + if (this->_kernel_filename.length() == 0) { + warning("You need to specify a kernel file"); + return true; + } else + this->_kernel_sptr = read_from_file>(this->_kernel_filename); + const DiscretisedDensity<3, elemT>& kernel = *this->_kernel_sptr; BasicCoordinate min_indices, max_indices; if (!kernel.get_regular_range(min_indices, max_indices)) return true; const BasicCoordinate sizes = max_indices - min_indices + 1; - - if (sizes[1]%2==0 || sizes[2]%2==0 || sizes[3]%2==0) - warning("Parsing Nonseparable Convolution Using Real DFT Image Filter\n" - "Even number of filter coefficients for at least one of the dimensions." - "I'll (effectively) append a 0 at the end.\n"); - const BasicCoordinate new_min_indices = (sizes/2)*(-1); + if (sizes[1] % 2 == 0 || sizes[2] % 2 == 0 || sizes[3] % 2 == 0) + warning("Parsing Nonseparable Convolution Using Real DFT Image Filter\n" + "Even number of filter coefficients for at least one of the dimensions." + "I'll (effectively) append a 0 at the end.\n"); + + const BasicCoordinate new_min_indices = (sizes / 2) * (-1); + + this->_filter_coefficients.grow(IndexRange(new_min_indices, new_min_indices + sizes - 1)); - this->_filter_coefficients.grow(IndexRange(new_min_indices, new_min_indices + sizes - 1 )); - BasicCoordinate index = get_min_indices(this->_filter_coefficients); - do - { - this->_filter_coefficients[index] = kernel[index - new_min_indices + min_indices]; - } - while(next(index, this->_filter_coefficients)); + do { + this->_filter_coefficients[index] = kernel[index - new_min_indices + min_indices]; + } while (next(index, this->_filter_coefficients)); return false; } -template<> -const char * const -NonseparableConvolutionUsingRealDFTImageFilter::registered_name = -"Nonseparable Convolution Using Real DFT Image Filter"; - -# ifdef _MSC_VER - // prevent warning message on reinstantiation, - // note that we get a linking error if we don't have the explicit instantiation below -# pragma warning(disable:4660) -# endif +template <> +const char* const NonseparableConvolutionUsingRealDFTImageFilter::registered_name = + "Nonseparable Convolution Using Real DFT Image Filter"; + +#ifdef _MSC_VER +// prevent warning message on reinstantiation, +// note that we get a linking error if we don't have the explicit instantiation below +# pragma warning(disable : 4660) +#endif template class NonseparableConvolutionUsingRealDFTImageFilter; - + END_NAMESPACE_STIR - diff --git a/src/buildblock/NumericType.cxx b/src/buildblock/NumericType.cxx index bf73b62c5d..1e8319a6c9 100644 --- a/src/buildblock/NumericType.cxx +++ b/src/buildblock/NumericType.cxx @@ -1,6 +1,6 @@ /*! - \file - + \file + \brief implementations for the stir::NumericType class \author Kris Thielemans @@ -23,7 +23,7 @@ See STIR/LICENSE.txt for details */ -/* +/* History: - first version Kris Thielemans @@ -39,178 +39,161 @@ using std::string; START_NAMESPACE_STIR -NumericType::NumericType(const string& number_format, const size_t size_in_bytes) -{ +NumericType::NumericType(const string& number_format, const size_t size_in_bytes) { bool it_is_signed; bool it_is_integral; - - if (number_format == "signed integer") - { + + if (number_format == "signed integer") { it_is_signed = true; it_is_integral = true; - } - else if (number_format == "unsigned integer") - { + } else if (number_format == "unsigned integer") { it_is_signed = false; it_is_integral = true; - } - else if (number_format == "float") - { + } else if (number_format == "float") { it_is_signed = true; it_is_integral = false; } - - else if (number_format == "bit") - { + + else if (number_format == "bit") { id = BIT; return; - } - else - { + } else { // set to some values which are guaranteed to break later on it_is_signed = false; it_is_integral = false; } - + // set up default value id = UNKNOWN_TYPE; - + // rely on enum<->int conversions - for (int t=1; t < (int)UNKNOWN_TYPE; t++) - { + for (int t = 1; t < (int)UNKNOWN_TYPE; t++) { const NumericType type = (Type)t; - if (it_is_signed == type.signed_type() && - it_is_integral == type.integer_type() && - size_in_bytes == type.size_in_bytes()) - { + if (it_is_signed == type.signed_type() && it_is_integral == type.integer_type() && size_in_bytes == type.size_in_bytes()) { id = (Type)t; return; } } - -} - +} -void -NumericType::get_Interfile_info(string& number_format, - size_t& size_in_bytes_v) const -{ +void +NumericType::get_Interfile_info(string& number_format, size_t& size_in_bytes_v) const { size_in_bytes_v = size_in_bytes(); - - switch(id) - { + + switch (id) { case BIT: - number_format = "bit"; break; - case SCHAR: - case SHORT: - case INT: - case LONG: - number_format = "signed integer"; break; - case UCHAR: - case USHORT: - case UINT: - case ULONG: - number_format = "unsigned integer"; break; - case FLOAT: - case DOUBLE: - number_format = "float"; break; + number_format = "bit"; + break; + case SCHAR: + case SHORT: + case INT: + case LONG: + number_format = "signed integer"; + break; + case UCHAR: + case USHORT: + case UINT: + case ULONG: + number_format = "unsigned integer"; + break; + case FLOAT: + case DOUBLE: + number_format = "float"; + break; case UNKNOWN_TYPE: - number_format = "unknown"; break; + number_format = "unknown"; + break; } } - - -size_t NumericType::size_in_bytes() const - { +size_t +NumericType::size_in_bytes() const { // KT TODO pretty awful way of doings things, but I'm not sure how to handle it - switch(id) - { - case BIT: - // TODO sensible value ? - return 0; -#define CASE(NUMERICTYPE) \ - case NUMERICTYPE : \ - return NumericInfo::type>().size_in_bytes(); - - // now list cases that we want - CASE(NumericType::SCHAR); - CASE(NumericType::UCHAR); - CASE(NumericType::SHORT); - CASE(NumericType::USHORT); - CASE(NumericType::INT); - CASE(NumericType::UINT); - CASE(NumericType::LONG); - CASE(NumericType::ULONG); - CASE(NumericType::FLOAT); - CASE(NumericType::DOUBLE); + switch (id) { + case BIT: + // TODO sensible value ? + return 0; +#define CASE(NUMERICTYPE) \ + case NUMERICTYPE: \ + return NumericInfo::type>().size_in_bytes(); + + // now list cases that we want + CASE(NumericType::SCHAR); + CASE(NumericType::UCHAR); + CASE(NumericType::SHORT); + CASE(NumericType::USHORT); + CASE(NumericType::INT); + CASE(NumericType::UINT); + CASE(NumericType::LONG); + CASE(NumericType::ULONG); + CASE(NumericType::FLOAT); + CASE(NumericType::DOUBLE); #undef CASE - case UNKNOWN_TYPE: - return 0; - } - // we never get here, but VC++ wants a return nevertheless - return 0; - } + case UNKNOWN_TYPE: + return 0; + } + // we never get here, but VC++ wants a return nevertheless + return 0; +} -size_t NumericType::size_in_bits() const - { return CHAR_BIT * size_in_bytes(); } - -bool NumericType::signed_type() const - { - switch(id) - { - case BIT: - return false; -#define CASE(NUMERICTYPE) \ - case NUMERICTYPE : \ - return NumericInfo::type>().signed_type(); - - // now list cases that we want - CASE(NumericType::SCHAR); - CASE(NumericType::UCHAR); - CASE(NumericType::SHORT); - CASE(NumericType::USHORT); - CASE(NumericType::INT); - CASE(NumericType::UINT); - CASE(NumericType::LONG); - CASE(NumericType::ULONG); - CASE(NumericType::FLOAT); - CASE(NumericType::DOUBLE); -#undef CASE - case UNKNOWN_TYPE: - return false; - } - // we never get here, but VC++ wants a return nevertheless - return false; - } +size_t +NumericType::size_in_bits() const { + return CHAR_BIT * size_in_bytes(); +} -bool NumericType::integer_type() const - { - switch(id) - { - case BIT: - return true; -#define CASE(NUMERICTYPE) \ - case NUMERICTYPE : \ - return NumericInfo::type>().integer_type(); - - // now list cases that we want - CASE(NumericType::SCHAR); - CASE(NumericType::UCHAR); - CASE(NumericType::SHORT); - CASE(NumericType::USHORT); - CASE(NumericType::INT); - CASE(NumericType::UINT); - CASE(NumericType::LONG); - CASE(NumericType::ULONG); - CASE(NumericType::FLOAT); - CASE(NumericType::DOUBLE); +bool +NumericType::signed_type() const { + switch (id) { + case BIT: + return false; +#define CASE(NUMERICTYPE) \ + case NUMERICTYPE: \ + return NumericInfo::type>().signed_type(); + + // now list cases that we want + CASE(NumericType::SCHAR); + CASE(NumericType::UCHAR); + CASE(NumericType::SHORT); + CASE(NumericType::USHORT); + CASE(NumericType::INT); + CASE(NumericType::UINT); + CASE(NumericType::LONG); + CASE(NumericType::ULONG); + CASE(NumericType::FLOAT); + CASE(NumericType::DOUBLE); #undef CASE - case UNKNOWN_TYPE: - return false; + case UNKNOWN_TYPE: + return false; + } + // we never get here, but VC++ wants a return nevertheless + return false; +} - } - // we never get here, but VC++ wants a return nevertheless - return false; - } +bool +NumericType::integer_type() const { + switch (id) { + case BIT: + return true; +#define CASE(NUMERICTYPE) \ + case NUMERICTYPE: \ + return NumericInfo::type>().integer_type(); + + // now list cases that we want + CASE(NumericType::SCHAR); + CASE(NumericType::UCHAR); + CASE(NumericType::SHORT); + CASE(NumericType::USHORT); + CASE(NumericType::INT); + CASE(NumericType::UINT); + CASE(NumericType::LONG); + CASE(NumericType::ULONG); + CASE(NumericType::FLOAT); + CASE(NumericType::DOUBLE); +#undef CASE + case UNKNOWN_TYPE: + return false; + } + // we never get here, but VC++ wants a return nevertheless + return false; +} END_NAMESPACE_STIR diff --git a/src/buildblock/ParseDiscretisedDensityParameters.cxx b/src/buildblock/ParseDiscretisedDensityParameters.cxx index e779bd5797..4f2f7be73c 100644 --- a/src/buildblock/ParseDiscretisedDensityParameters.cxx +++ b/src/buildblock/ParseDiscretisedDensityParameters.cxx @@ -1,32 +1,32 @@ /* Copyright (C) 2000 PARAPET partners - Copyright (C) 2000- 2007, Hammersmith Imanet Ltd + Copyright (C) 2000- 2007, Hammersmith Imanet Ltd Copyright (C) 2019, University College London - This file is part of STIR. - - This file is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - This file is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - + This file is part of STIR. + + This file is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + See STIR/LICENSE.txt for details */ /*! \file - \ingroup densitydata - + \ingroup densitydata + \brief Implementation of the stir::ParseDiscretisedDensityParameters class - + \author Kris Thielemans \author Matthew Jacobson \author Claire Labbe \author PARAPET project - + */ #include "stir/KeyParser.h" #include "stir/ParseDiscretisedDensityParameters.h" @@ -35,29 +35,25 @@ START_NAMESPACE_STIR -void -ParseDiscretisedDensityParameters:: -set_defaults() -{ - //base_type::set_defaults(); - output_image_size_xy=-1; - output_image_size_z=-1; - zoom_xy=1.F; - zoom_z=1.F; +void +ParseDiscretisedDensityParameters::set_defaults() { + // base_type::set_defaults(); + output_image_size_xy = -1; + output_image_size_z = -1; + zoom_xy = 1.F; + zoom_z = 1.F; offset.fill(0.F); } void -ParseDiscretisedDensityParameters:: -add_to_keymap(KeyParser& parser) -{ - //base_type::initialise_keymap(); +ParseDiscretisedDensityParameters::add_to_keymap(KeyParser& parser) { + // base_type::initialise_keymap(); parser.add_key("zoom", &zoom_xy); parser.add_key("Z zoom", &zoom_z); - parser.add_key("XY output image size (in pixels)",&output_image_size_xy); - parser.add_key("Z output image size (in pixels)",&output_image_size_z); - //parser.add_key("X offset (in mm)", &offset.x()); // KT 10122001 added spaces - //parser.add_key("Y offset (in mm)", &offset.y()); + parser.add_key("XY output image size (in pixels)", &output_image_size_xy); + parser.add_key("Z output image size (in pixels)", &output_image_size_z); + // parser.add_key("X offset (in mm)", &offset.x()); // KT 10122001 added spaces + // parser.add_key("Y offset (in mm)", &offset.y()); parser.add_key("Z offset (in mm)", &offset.z()); } @@ -75,8 +71,8 @@ ask_parameters() -1, 4*static_cast(proj_data_ptr->get_num_tangential_poss()*zoom), -1); - -#if 0 + +# if 0 // This section enables you to position a reconstructed image // along x (horizontal), y (vertical) and/or z (transverse) axes // The default values is in the center of the FOV, @@ -88,75 +84,78 @@ ask_parameters() cout << endl << " Enter offset Xoff, Yoff (in pixels) :"; Xoffset = ask_num(" X offset ",-old_size/2, old_size/2, 0); Yoffset = ask_num(" Y offset ",-old_size/2, old_size/2, 0); -#endif +# endif } #endif // ask_parameters disabled - void -ParseDiscretisedDensityParameters:: -check_values() const -{ - if (zoom_xy <= 0) - { error("zoom should be positive"); } - if (zoom_z <= 0) - { error("z zoom should be positive"); } - - if (output_image_size_xy!=-1 && output_image_size_xy<1) // KT 10122001 appended_xy - { error("output image size xy must be positive (or -1 as default)"); } - if (output_image_size_z!=-1 && output_image_size_z<1) // KT 10122001 new - { error("output image size z must be positive (or -1 as default)"); } +ParseDiscretisedDensityParameters::check_values() const { + if (zoom_xy <= 0) { + error("zoom should be positive"); + } + if (zoom_z <= 0) { + error("z zoom should be positive"); + } + + if (output_image_size_xy != -1 && output_image_size_xy < 1) // KT 10122001 appended_xy + { + error("output image size xy must be positive (or -1 as default)"); + } + if (output_image_size_z != -1 && output_image_size_z < 1) // KT 10122001 new + { + error("output image size z must be positive (or -1 as default)"); + } } int -ParseDiscretisedDensityParameters:: -get_output_image_size_xy() const -{ return this->output_image_size_xy; } +ParseDiscretisedDensityParameters::get_output_image_size_xy() const { + return this->output_image_size_xy; +} void -ParseDiscretisedDensityParameters:: -set_output_image_size_xy(int v) -{ this->output_image_size_xy = v; } +ParseDiscretisedDensityParameters::set_output_image_size_xy(int v) { + this->output_image_size_xy = v; +} int -ParseDiscretisedDensityParameters:: -get_output_image_size_z() const -{ return this->output_image_size_z; } +ParseDiscretisedDensityParameters::get_output_image_size_z() const { + return this->output_image_size_z; +} void -ParseDiscretisedDensityParameters:: -set_output_image_size_z(int v) -{ this->output_image_size_z = v; } +ParseDiscretisedDensityParameters::set_output_image_size_z(int v) { + this->output_image_size_z = v; +} float -ParseDiscretisedDensityParameters:: -get_zoom_xy() const -{ return this->zoom_xy; } +ParseDiscretisedDensityParameters::get_zoom_xy() const { + return this->zoom_xy; +} void -ParseDiscretisedDensityParameters:: -set_zoom_xy(float v) -{ this->zoom_xy = v; } +ParseDiscretisedDensityParameters::set_zoom_xy(float v) { + this->zoom_xy = v; +} float -ParseDiscretisedDensityParameters:: -get_zoom_z() const -{ return this->zoom_z; } +ParseDiscretisedDensityParameters::get_zoom_z() const { + return this->zoom_z; +} void -ParseDiscretisedDensityParameters:: -set_zoom_z(float v) -{ this->zoom_z = v; } +ParseDiscretisedDensityParameters::set_zoom_z(float v) { + this->zoom_z = v; +} const CartesianCoordinate3D& -ParseDiscretisedDensityParameters:: -get_offset() const -{ return this->offset; } +ParseDiscretisedDensityParameters::get_offset() const { + return this->offset; +} void -ParseDiscretisedDensityParameters:: -set_offset(const CartesianCoordinate3D& v) -{ this->offset = v; } +ParseDiscretisedDensityParameters::set_offset(const CartesianCoordinate3D& v) { + this->offset = v; +} END_NAMESPACE_STIR diff --git a/src/buildblock/ParsingObject.cxx b/src/buildblock/ParsingObject.cxx index 55abf892fa..bc8d669614 100644 --- a/src/buildblock/ParsingObject.cxx +++ b/src/buildblock/ParsingObject.cxx @@ -35,81 +35,56 @@ using std::ifstream; START_NAMESPACE_STIR -ParsingObject::ParsingObject() -: - keymap_is_initialised(false) -{} - - - - -ParsingObject::ParsingObject(const ParsingObject& par) -: - keymap_is_initialised(false) - {} +ParsingObject::ParsingObject() : keymap_is_initialised(false) {} +ParsingObject::ParsingObject(const ParsingObject& par) : keymap_is_initialised(false) {} ParsingObject& -ParsingObject::operator =(const ParsingObject& par) -{ - if (&par == this) return *this; +ParsingObject::operator=(const ParsingObject& par) { + if (&par == this) + return *this; keymap_is_initialised = false; return *this; } -void -ParsingObject:: -set_defaults() -{} +void +ParsingObject::set_defaults() {} void -ParsingObject:: -initialise_keymap() -{} +ParsingObject::initialise_keymap() {} -bool -ParsingObject:: -post_processing() -{ return false; } +bool +ParsingObject::post_processing() { + return false; +} -void -ParsingObject:: -set_key_values() -{} +void +ParsingObject::set_key_values() {} -//void +// void bool -ParsingObject:: parse(std::istream& in) -{ +ParsingObject::parse(std::istream& in) { // potentially remove the if() and always call initialise_keymap - if (!keymap_is_initialised) - { - initialise_keymap(); + if (!keymap_is_initialised) { + initialise_keymap(); keymap_is_initialised = true; } set_key_values(); - if (!parser.parse(in)) - { - warning("Error parsing.\n"); + if (!parser.parse(in)) { + warning("Error parsing.\n"); return false; - } - else if (post_processing()==true) - { - warning("Error post processing keyword values.\n"); - return false; - } - else + } else if (post_processing() == true) { + warning("Error post processing keyword values.\n"); + return false; + } else return true; } - -//void +// void bool -ParsingObject::parse(const char * const filename) -{ +ParsingObject::parse(const char* const filename) { ifstream hdr_stream(filename); - if (!hdr_stream) - { + if (!hdr_stream) { error("ParsingObject::parse: couldn't open file %s\n", filename); return false; } @@ -117,41 +92,34 @@ ParsingObject::parse(const char * const filename) } void -ParsingObject::ask_parameters() -{ +ParsingObject::ask_parameters() { // potentially remove the if() and always call initialise_keymap - if (!keymap_is_initialised) - { - initialise_keymap(); + if (!keymap_is_initialised) { + initialise_keymap(); keymap_is_initialised = true; } // TODO drop next line set_defaults(); set_key_values(); - while(true) - { + while (true) { parser.ask_parameters(); - if (post_processing()==true) - { - warning("\nError post processing keyword values. Doing it all over again...\n"); - } - else + if (post_processing() == true) { + warning("\nError post processing keyword values. Doing it all over again...\n"); + } else return; } } std::string -ParsingObject::parameter_info() -{ - if (!keymap_is_initialised) - { - initialise_keymap(); +ParsingObject::parameter_info() { + if (!keymap_is_initialised) { + initialise_keymap(); keymap_is_initialised = true; } set_key_values(); - return parser.parameter_info(); + return parser.parameter_info(); } END_NAMESPACE_STIR diff --git a/src/buildblock/PatientPosition.cxx b/src/buildblock/PatientPosition.cxx index a95431302e..03354146df 100644 --- a/src/buildblock/PatientPosition.cxx +++ b/src/buildblock/PatientPosition.cxx @@ -28,63 +28,80 @@ START_NAMESPACE_STIR -PatientPosition::PatientPosition(PatientPosition::PositionValue position) -{ - switch(position) - { - case FFP: - orientation=feet_in; rotation=prone; break; - case HFP: - orientation=head_in; rotation=prone; break; - case FFS: - orientation=feet_in; rotation=supine; break; - case HFS: - orientation=head_in; rotation=supine; break; - case FFDR: - orientation=feet_in; rotation=right; break; - case HFDR: - orientation=head_in; rotation=right; break; - case FFDL: - orientation=feet_in; rotation=left; break; - case HFDL: - orientation=head_in; rotation=left; break; - case unknown_position: - orientation=unknown_orientation; rotation=unknown_rotation; break; - } +PatientPosition::PatientPosition(PatientPosition::PositionValue position) { + switch (position) { + case FFP: + orientation = feet_in; + rotation = prone; + break; + case HFP: + orientation = head_in; + rotation = prone; + break; + case FFS: + orientation = feet_in; + rotation = supine; + break; + case HFS: + orientation = head_in; + rotation = supine; + break; + case FFDR: + orientation = feet_in; + rotation = right; + break; + case HFDR: + orientation = head_in; + rotation = right; + break; + case FFDL: + orientation = feet_in; + rotation = left; + break; + case HFDL: + orientation = head_in; + rotation = left; + break; + case unknown_position: + orientation = unknown_orientation; + rotation = unknown_rotation; + break; + } } PatientPosition::PositionValue -PatientPosition::get_position() const -{ +PatientPosition::get_position() const { // make use of order of enum's - if (orientation<=feet_in && rotation<=left) - { - return static_cast(orientation*4 + rotation); - } - else - { - return unknown_position; - } + if (orientation <= feet_in && rotation <= left) { + return static_cast(orientation * 4 + rotation); + } else { + return unknown_position; + } } -const char * const -PatientPosition:: -get_position_as_string() const -{ - switch (this->get_position()) - { - case HFP: return "HFP"; - case HFS: return "HFS"; - case HFDR: return "HFDR"; - case HFDL: return "HFDL"; - case FFDR: return "FFDR"; - case FFDL: return "FFDL"; - case FFP: return "FFP"; - case FFS: return "FFS"; - case unknown_position: - default: - return "unknown"; - } +const char* const +PatientPosition::get_position_as_string() const { + switch (this->get_position()) { + case HFP: + return "HFP"; + case HFS: + return "HFS"; + case HFDR: + return "HFDR"; + case HFDL: + return "HFDL"; + case FFDR: + return "FFDR"; + case FFDL: + return "FFDL"; + case FFP: + return "FFP"; + case FFS: + return "FFS"; + case unknown_position: + default: + return "unknown"; + } } END_NAMESPACE_STIR diff --git a/src/buildblock/ProjData.cxx b/src/buildblock/ProjData.cxx index 833de66354..3ad44cec99 100644 --- a/src/buildblock/ProjData.cxx +++ b/src/buildblock/ProjData.cxx @@ -20,7 +20,7 @@ */ /*! \file - \ingroup projdata + \ingroup projdata \brief Implementations for non-inline functions of class stir::ProjData @@ -43,20 +43,20 @@ #include "stir/ProjDataFromStream.h" // needed for converting ProjDataFromStream* to ProjData* #ifndef STIR_USE_GE_IO -#include "stir/ProjDataGEAdvance.h" +# include "stir/ProjDataGEAdvance.h" #else -#include "stir_experimental/IO/GE/ProjDataVOLPET.h" -#ifdef HAVE_RDF -#include "stir_experimental/IO/GE/stir_RDF.h" -#include "stir_experimental/IO/GE/ProjDataRDF.h" -#endif +# include "stir_experimental/IO/GE/ProjDataVOLPET.h" +# ifdef HAVE_RDF +# include "stir_experimental/IO/GE/stir_RDF.h" +# include "stir_experimental/IO/GE/ProjDataRDF.h" +# endif #endif // STIR_USE_GE_IO #ifdef HAVE_IE -#include "stir_experimental/IO/GE/ProjDataIE.h" +# include "stir_experimental/IO/GE/ProjDataIE.h" #endif #ifdef HAVE_HDF5 -#include "stir/ProjDataGEHDF5.h" -#include "stir/IO/GEHDF5Wrapper.h" +# include "stir/ProjDataGEHDF5.h" +# include "stir/IO/GEHDF5Wrapper.h" #endif #include "stir/IO/stir_ecat7.h" #include "stir/ViewSegmentNumbers.h" @@ -75,9 +75,9 @@ using std::vector; START_NAMESPACE_STIR -/*! +/*! This function will attempt to determine the type of projection data in the file, - construct an object of the appropriate type, and return a pointer to + construct an object of the appropriate type, and return a pointer to the object. The return value is a shared_ptr, to make sure that the caller will @@ -92,102 +92,86 @@ START_NAMESPACE_STIR
    • GE VOLPET data (via class ProjDataVOLPET)
    • Interfile (using read_interfile_PDFS()) -
    • ECAT 7 3D sinograms and attenuation files +
    • ECAT 7 3D sinograms and attenuation files
    Developer's note: ideally the return value would be an stir::unique_ptr. */ -shared_ptr -ProjData:: -read_from_file(const string& filename, - const std::ios::openmode openmode) -{ +shared_ptr +ProjData::read_from_file(const string& filename, const std::ios::openmode openmode) { std::string actual_filename = filename; // parse filename to see if it's like filename,options { const std::size_t comma_pos = filename.find(','); - if (comma_pos != std::string::npos) - { - actual_filename.resize(comma_pos); - } + if (comma_pos != std::string::npos) { + actual_filename.resize(comma_pos); + } } - fstream * input = new fstream(actual_filename.c_str(), openmode | ios::binary); - if (! *input) + fstream* input = new fstream(actual_filename.c_str(), openmode | ios::binary); + if (!*input) error("ProjData::read_from_file: error opening file %s", actual_filename.c_str()); const FileSignature file_signature(actual_filename); - const char * signature = file_signature.get_signature(); + const char* signature = file_signature.get_signature(); // GE Advance - if (strncmp(signature, "2D3D", 4) == 0) - { + if (strncmp(signature, "2D3D", 4) == 0) { // if (ask("Read with old code (Y) or new (N)?",false)) -#ifndef STIR_USE_GE_IO - { -#ifndef NDEBUG - warning("ProjData::read_from_file trying to read %s as GE Advance file", - filename.c_str()); -#endif - return shared_ptr( new ProjDataGEAdvance(input) ); - } - //else +#ifndef STIR_USE_GE_IO + { +# ifndef NDEBUG + warning("ProjData::read_from_file trying to read %s as GE Advance file", filename.c_str()); +# endif + return shared_ptr(new ProjDataGEAdvance(input)); + } + // else #else // use VOLPET - { -#ifndef NDEBUG - warning("ProjData::read_from_file trying to read %s as GE VOLPET file", - filename.c_str()); -#endif - delete input;// TODO no longer use pointer after getting rid of ProjDataGEAdvance - return shared_ptr( new GE_IO::ProjDataVOLPET(filename, openmode) ); - } + { +# ifndef NDEBUG + warning("ProjData::read_from_file trying to read %s as GE VOLPET file", filename.c_str()); +# endif + delete input; // TODO no longer use pointer after getting rid of ProjDataGEAdvance + return shared_ptr(new GE_IO::ProjDataVOLPET(filename, openmode)); + } #endif // STIR_USE_GE_IO to differentiate between Advance and VOLPET code } - delete input;// TODO no longer use pointer after getting rid of ProjDataGEAdvance + delete input; // TODO no longer use pointer after getting rid of ProjDataGEAdvance #ifdef HAVE_IE - // GE IE file format - if (GE_IO::is_IE_signature(signature)) - { -#ifndef NDEBUG - warning("ProjData::read_from_file trying to read %s as GE IE file", - filename.c_str()); -#endif - return shared_ptr( new GE_IO::ProjDataIE(filename) ); - } + // GE IE file format + if (GE_IO::is_IE_signature(signature)) { +# ifndef NDEBUG + warning("ProjData::read_from_file trying to read %s as GE IE file", filename.c_str()); +# endif + return shared_ptr(new GE_IO::ProjDataIE(filename)); + } #endif // HAVE_IE - #ifdef HAVE_LLN_MATRIX // ECAT 7 - if (strncmp(signature, "MATRIX", 6) == 0) - { -#ifndef NDEBUG + if (strncmp(signature, "MATRIX", 6) == 0) { +# ifndef NDEBUG warning("ProjData::read_from_file trying to read %s as ECAT7", filename.c_str()); -#endif +# endif USING_NAMESPACE_ECAT; USING_NAMESPACE_ECAT7; - if (is_ECAT7_emission_file(actual_filename) || is_ECAT7_attenuation_file(actual_filename)) - { - warning("\nReading frame 1, gate 1, data 0, bed 0 from file %s", - actual_filename.c_str()); - shared_ptr proj_data_sptr(ECAT7_to_PDFS(filename, /*frame_num, gate_num, data_num, bed_num*/1,1,0,0)); + if (is_ECAT7_emission_file(actual_filename) || is_ECAT7_attenuation_file(actual_filename)) { + warning("\nReading frame 1, gate 1, data 0, bed 0 from file %s", actual_filename.c_str()); + shared_ptr proj_data_sptr(ECAT7_to_PDFS(filename, /*frame_num, gate_num, data_num, bed_num*/ 1, 1, 0, 0)); return proj_data_sptr; - } - else - { + } else { if (is_ECAT7_file(actual_filename)) - warning("ProjData::read_from_file ECAT7 file %s is of unsupported file type", actual_filename.c_str()); + warning("ProjData::read_from_file ECAT7 file %s is of unsupported file type", actual_filename.c_str()); } } #endif // HAVE_LLN_MATRIX // Interfile - if (is_interfile_signature(signature)) - { + if (is_interfile_signature(signature)) { #ifndef NDEBUG warning("ProjData::read_from_file trying to read %s as Interfile", filename.c_str()); #endif @@ -196,124 +180,96 @@ read_from_file(const string& filename, return ptr; } - #if defined(STIR_USE_GE_IO) && defined(HAVE_RDF) - if (GE_IO::is_RDF_file(actual_filename)) - { -#ifndef NDEBUG - warning("ProjData::read_from_file trying to read %s as RDF", filename.c_str()); -#endif - shared_ptr ptr(new GE_IO::ProjDataRDF(filename)); - if (!is_null_ptr(ptr)) - return ptr; + if (GE_IO::is_RDF_file(actual_filename)) { +# ifndef NDEBUG + warning("ProjData::read_from_file trying to read %s as RDF", filename.c_str()); +# endif + shared_ptr ptr(new GE_IO::ProjDataRDF(filename)); + if (!is_null_ptr(ptr)) + return ptr; } #endif // RDF - + #ifdef HAVE_HDF5 - if (GE::RDF_HDF5::GEHDF5Wrapper::check_GE_signature(actual_filename)) - { -#ifndef NDEBUG - warning("ProjData::read_from_file trying to read %s as GE HDF5", filename.c_str()); -#endif - shared_ptr ptr(new GE::RDF_HDF5::ProjDataGEHDF5(filename)); - if (!is_null_ptr(ptr)) - return ptr; + if (GE::RDF_HDF5::GEHDF5Wrapper::check_GE_signature(actual_filename)) { +# ifndef NDEBUG + warning("ProjData::read_from_file trying to read %s as GE HDF5", filename.c_str()); +# endif + shared_ptr ptr(new GE::RDF_HDF5::ProjDataGEHDF5(filename)); + if (!is_null_ptr(ptr)) + return ptr; } #endif // GE HDF5 error("\nProjData::read_from_file could not read projection data %s.\n" - "Unsupported file format? Aborting.", - filename.c_str()); + "Unsupported file format? Aborting.", + filename.c_str()); // need to return something to satisfy the compiler, but we never get here shared_ptr null_ptr; return null_ptr; } -//void -//ProjData::set_exam_info(ExamInfo const& new_exam_info) +// void +// ProjData::set_exam_info(ExamInfo const& new_exam_info) //{ // this->exam_info_sptr.reset(new ExamInfo(new_exam_info)); //} - -Viewgram -ProjData::get_empty_viewgram(const int view_num, const int segment_num, - 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, timing_pos); +Viewgram +ProjData::get_empty_viewgram(const int view_num, const int segment_num, 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, timing_pos); } Sinogram -ProjData::get_empty_sinogram(const int ax_pos_num, const int segment_num, - 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, timing_pos); +ProjData::get_empty_sinogram(const int ax_pos_num, const int segment_num, 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, timing_pos); } - SegmentBySinogram -ProjData::get_empty_segment_by_sinogram(const int segment_num, - 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, timing_pos); -} - +ProjData::get_empty_segment_by_sinogram(const int segment_num, 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, timing_pos); +} SegmentByView -ProjData::get_empty_segment_by_view(const int segment_num, - 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, timing_pos); +ProjData::get_empty_segment_by_view(const int segment_num, 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, timing_pos); } -RelatedViewgrams +RelatedViewgrams ProjData::get_empty_related_viewgrams(const ViewSegmentNumbers& view_segmnet_num, - //const int view_num, const int segment_num, - 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, timing_pos); + // const int view_num, const int segment_num, + 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, + timing_pos); } - -RelatedViewgrams +RelatedViewgrams ProjData::get_related_viewgrams(const ViewSegmentNumbers& view_segment_num, - //const int view_num, const int segment_num, - const shared_ptr& symmetries_used, - const bool make_num_bins_odd, - const int timing_pos) const -{ + // const int view_num, const int segment_num, + const shared_ptr& symmetries_used, + const bool make_num_bins_odd, const int timing_pos) const { vector pairs; symmetries_used->get_related_view_segment_numbers( - pairs, - ViewSegmentNumbers(view_segment_num.view_num(),view_segment_num.segment_num()) - ); + pairs, ViewSegmentNumbers(view_segment_num.view_num(), view_segment_num.segment_num())); - vector > viewgrams; + vector> viewgrams; viewgrams.reserve(pairs.size()); - for (unsigned int i=0; i(viewgrams, symmetries_used); } -//std::vector -//ProjData::get_related_bin_values(const std::vector& r_bins) const +// std::vector +// ProjData::get_related_bin_values(const std::vector& r_bins) const //{ // std::vector values; @@ -328,15 +284,12 @@ ProjData::get_related_viewgrams(const ViewSegmentNumbers& view_segment_num, // return values; //} - -Succeeded -ProjData::set_related_viewgrams( const RelatedViewgrams& viewgrams) -{ +Succeeded +ProjData::set_related_viewgrams(const RelatedViewgrams& viewgrams) { RelatedViewgrams::const_iterator r_viewgrams_iter = viewgrams.begin(); - while( r_viewgrams_iter!=viewgrams.end()) - { - if (set_viewgram(*r_viewgrams_iter)== Succeeded::no) + while (r_viewgrams_iter != viewgrams.end()) { + if (set_viewgram(*r_viewgrams_iter) == Succeeded::no) return Succeeded::no; ++r_viewgrams_iter; } @@ -353,195 +306,147 @@ ProjData::set_related_viewgrams( const RelatedViewgrams& viewgrams) } #endif -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,timing_pos); +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, 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, timing_pos)); + 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 int timing_pos) const -{ - SegmentByView segment = - proj_data_info_sptr->get_empty_segment_by_view(segment_num,false,timing_pos); +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, 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, timing_pos)); + segment.set_viewgram(get_viewgram(view_num, segment_num, false, timing_pos)); return segment; } -Succeeded -ProjData::set_segment(const SegmentBySinogram& segment) -{ - for (int view_num = get_min_view_num(); view_num <= get_max_view_num(); ++view_num) - { - if(set_viewgram(segment.get_viewgram(view_num)) - == Succeeded::no) - return Succeeded::no; +Succeeded +ProjData::set_segment(const SegmentBySinogram& segment) { + for (int view_num = get_min_view_num(); view_num <= get_max_view_num(); ++view_num) { + if (set_viewgram(segment.get_viewgram(view_num)) == Succeeded::no) + return Succeeded::no; } return Succeeded::yes; } -Succeeded -ProjData::set_segment(const SegmentByView& segment) -{ - for (int view_num = get_min_view_num(); view_num <= get_max_view_num(); ++view_num) - { - if(set_viewgram(segment.get_viewgram(view_num)) - == Succeeded::no) - return Succeeded::no; +Succeeded +ProjData::set_segment(const SegmentByView& segment) { + for (int view_num = get_min_view_num(); view_num <= get_max_view_num(); ++view_num) { + if (set_viewgram(segment.get_viewgram(view_num)) == Succeeded::no) + return Succeeded::no; } return Succeeded::yes; } - -void -ProjData::fill(const float value) -{ - 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 = 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"); - } +void +ProjData::fill(const float value) { + 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 = 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"); + } } } -void -ProjData::fill(const ProjData& proj_data) -{ +void +ProjData::fill(const ProjData& proj_data) { shared_ptr source_proj_data_info_sptr = proj_data.get_proj_data_info_sptr()->create_shared_clone(); source_proj_data_info_sptr->reduce_segment_range(std::max(this->get_min_segment_num(), proj_data.get_min_segment_num()), std::min(this->get_max_segment_num(), proj_data.get_max_segment_num())); if ((*this->get_proj_data_info_sptr()) != (*source_proj_data_info_sptr)) - error("Filling projection data from incompatible source"); + error("Filling projection data from incompatible source"); - 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) - { - if(this->set_segment(proj_data.get_segment_by_view(segment_num, timing_pos_num)) - == 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) { + 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"); + } } } -ProjData:: ProjData() - :ExamData() -{} +ProjData::ProjData() : ExamData() {} -ProjData::ProjData(const shared_ptr& exam_info_sptr, - const shared_ptr& proj_data_info_sptr) - :ExamData(exam_info_sptr), proj_data_info_sptr(proj_data_info_sptr) -{} +ProjData::ProjData(const shared_ptr& exam_info_sptr, const shared_ptr& proj_data_info_sptr) + : ExamData(exam_info_sptr), proj_data_info_sptr(proj_data_info_sptr) {} Succeeded -ProjData:: -write_to_file(const string& output_filename) const -{ +ProjData::write_to_file(const string& output_filename) const { - ProjDataInterfile out_projdata(get_exam_info_sptr(), - this->proj_data_info_sptr, output_filename, ios::out); + 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) + 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; - } void -ProjData:: -axpby( const float a, const ProjData& x, - const float b, const ProjData& y) -{ - xapyb(x,a,y,b); +ProjData::axpby(const float a, const ProjData& x, const float b, const ProjData& y) { + xapyb(x, a, y, b); } void -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"); - - const int n_min = get_min_segment_num(); - const int n_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); - seg.xapyb(sx, a, sy, b); - set_segment(seg); - } +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"); + + const int n_min = get_min_segment_num(); + const int n_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); + seg.xapyb(sx, a, sy, b); + set_segment(seg); + } } void -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"); - - const int n_min = get_min_segment_num(); - const int n_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); - - seg.xapyb(sx, sa, sy, sb); - set_segment(seg); - } +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"); + + const int n_min = get_min_segment_num(); + const int n_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); + + seg.xapyb(sx, sa, sy, sb); + set_segment(seg); + } } void -ProjData:: -sapyb(const float a, const ProjData& y, const float b) -{ - this->xapyb(*this,a,y,b); +ProjData::sapyb(const float a, const ProjData& y, const float b) { + this->xapyb(*this, a, y, b); } void -ProjData:: -sapyb(const ProjData& a, const ProjData& y,const ProjData& b) -{ - this->xapyb(*this,a,y,b); +ProjData::sapyb(const ProjData& a, const ProjData& y, const ProjData& b) { + this->xapyb(*this, a, y, b); } - std::vector -ProjData:: -standard_segment_sequence(const ProjDataInfo& pdi) -{ +ProjData::standard_segment_sequence(const ProjDataInfo& pdi) { std::vector segment_sequence(pdi.get_num_segments()); - if (pdi.get_num_segments()==0) + if (pdi.get_num_segments() == 0) return segment_sequence; const int max_segment_num = pdi.get_max_segment_num(); @@ -549,11 +454,10 @@ standard_segment_sequence(const ProjDataInfo& pdi) segment_sequence[0] = 0; unsigned idx = 1; int segment_num = 1; - while (idx < segment_sequence.size()) - { - if (segment_num<=max_segment_num) + while (idx < segment_sequence.size()) { + if (segment_num <= max_segment_num) segment_sequence[idx++] = segment_num; - if (-segment_num>=min_segment_num) + if (-segment_num >= min_segment_num) segment_sequence[idx++] = -segment_num; ++segment_num; } diff --git a/src/buildblock/ProjDataFromStream.cxx b/src/buildblock/ProjDataFromStream.cxx index 03dc061f29..9088e290ef 100644 --- a/src/buildblock/ProjDataFromStream.cxx +++ b/src/buildblock/ProjDataFromStream.cxx @@ -63,9 +63,9 @@ using std::vector; #ifdef _MSC_VER // work-around for compiler bug: VC messes up std namespace -#define FIND std::find +# define FIND std::find #else -#define FIND find +# define FIND find #endif START_NAMESPACE_STIR @@ -74,411 +74,315 @@ START_NAMESPACE_STIR //--------------------------------------------------------- ProjDataFromStream::ProjDataFromStream(shared_ptr const& exam_info_sptr, - shared_ptr const& proj_data_info_sptr, - shared_ptr const& s, const streamoff offs, - const vector& segment_sequence_in_stream_v, - StorageOrder o, - NumericType data_type, - ByteOrder byte_order, - float scale_factor) + shared_ptr const& proj_data_info_sptr, shared_ptr const& s, + const streamoff offs, const vector& segment_sequence_in_stream_v, StorageOrder o, + NumericType data_type, ByteOrder byte_order, float scale_factor) - : - ProjData(exam_info_sptr, proj_data_info_sptr), - sino_stream(s), offset(offs), - segment_sequence(segment_sequence_in_stream_v), - storage_order(o), - on_disk_data_type(data_type), - on_disk_byte_order(byte_order), - scale_factor(scale_factor) -{ + : ProjData(exam_info_sptr, proj_data_info_sptr), sino_stream(s), offset(offs), segment_sequence(segment_sequence_in_stream_v), + storage_order(o), on_disk_data_type(data_type), on_disk_byte_order(byte_order), scale_factor(scale_factor) { assert(storage_order != Unsupported); assert(!(data_type == NumericType::UNKNOWN_TYPE)); if (proj_data_info_sptr->get_num_tof_poss() > 1) - activate_TOF(); - + activate_TOF(); } ProjDataFromStream::ProjDataFromStream(shared_ptr const& exam_info_sptr, - shared_ptr const& proj_data_info_sptr, - shared_ptr const& s, const streamoff offs, - StorageOrder o, - NumericType data_type, - ByteOrder byte_order, + 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) - : - ProjData(exam_info_sptr, proj_data_info_sptr), - sino_stream(s), offset(offs), - storage_order(o), - on_disk_data_type(data_type), - on_disk_byte_order(byte_order), - scale_factor(scale_factor) -{ + : ProjData(exam_info_sptr, proj_data_info_sptr), sino_stream(s), offset(offs), storage_order(o), on_disk_data_type(data_type), + on_disk_byte_order(byte_order), scale_factor(scale_factor) { assert(storage_order != Unsupported); assert(!(data_type == NumericType::UNKNOWN_TYPE)); 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. + // 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_sptr->get_min_segment_num(); - segment_num<=proj_data_info_sptr->get_max_segment_num(); - ++i, ++segment_num) - { - segment_sequence[i] =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()); +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(); + } - // Now, lets initialise a TOF stream - Similarly to segments - storage_order = Timing_Segment_View_AxialPos_TangPos; + offset_3d_data = static_cast(sum * on_disk_data_type.size_in_bytes()); - timing_poss_sequence.resize(proj_data_info_sptr->get_num_tof_poss()); + // Now, lets initialise a TOF stream - Similarly to segments + storage_order = Timing_Segment_View_AxialPos_TangPos; - 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; - } + 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; + } } Viewgram -ProjDataFromStream::get_viewgram(const int view_num, const int segment_num, - const bool make_num_tangential_poss_odd, const int timing_pos) const -{ - if (is_null_ptr(sino_stream)) - { +ProjDataFromStream::get_viewgram(const int view_num, const int segment_num, const bool make_num_tangential_poss_odd, + const int timing_pos) const { + if (is_null_ptr(sino_stream)) { error("ProjDataFromStream::get_viewgram: stream ptr is 0\n"); } - if (! *sino_stream) - { + if (!*sino_stream) { error("ProjDataFromStream::get_viewgram: error in stream state before reading\n"); } - vector offsets = get_offsets(view_num,segment_num, timing_pos); + vector offsets = get_offsets(view_num, segment_num, timing_pos); 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, timing_pos); float scale = float(1); Succeeded succeeded = Succeeded::yes; - + #ifdef STIR_OPENMP -#pragma omp critical(PROJDATAFROMSTREAMIO) +# pragma omp critical(PROJDATAFROMSTREAMIO) #endif { - sino_stream->seekg(segment_offset, ios::beg); // start of segment + 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; + + if (!*sino_stream) { + warning("ProjDataFromStream::get_viewgram: error after seekg"); + succeeded = Succeeded::no; + } else if (get_storage_order() == + Segment_AxialPos_View_TangPos) //|| get_storage_order() == Timing_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; + 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 (ax_pos_num != get_max_axial_pos_num(segment_num)) + sino_stream->seekg(intra_views_offset, ios::cur); } - else if (get_storage_order() == Segment_AxialPos_View_TangPos) //|| get_storage_order() == Timing_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; - 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(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 || get_storage_order() == Timing_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; - } + } else if (get_storage_order() == Segment_View_AxialPos_TangPos || + get_storage_order() == Timing_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 if (succeeded == Succeeded::no) error("ProjDataFromStream: error reading data"); viewgram *= scale_factor; - if (make_num_tangential_poss_odd &&(get_num_tangential_poss()%2==0)) + if (make_num_tangential_poss_odd && (get_num_tangential_poss() % 2 == 0)) - { - const int new_max_tangential_pos = get_max_tangential_pos_num() + 1; - - viewgram.grow( - IndexRange2D(get_min_axial_pos_num(segment_num), - get_max_axial_pos_num(segment_num), - - get_min_tangential_pos_num(), - new_max_tangential_pos)); - } - return viewgram; + { + const int new_max_tangential_pos = get_max_tangential_pos_num() + 1; + + viewgram.grow(IndexRange2D(get_min_axial_pos_num(segment_num), get_max_axial_pos_num(segment_num), + + get_min_tangential_pos_num(), new_max_tangential_pos)); + } + return viewgram; } float -ProjDataFromStream::get_bin_value(const Bin& this_bin) const -{ - if (is_null_ptr(sino_stream)) - { - error("ProjDataFromStream::get_bin_value: stream ptr is 0\n"); - } - if (! *sino_stream) - { - error("ProjDataFromStream::get_bin_value: error in stream state before reading\n"); - } +ProjDataFromStream::get_bin_value(const Bin& this_bin) const { + if (is_null_ptr(sino_stream)) { + error("ProjDataFromStream::get_bin_value: stream ptr is 0\n"); + } + if (!*sino_stream) { + error("ProjDataFromStream::get_bin_value: error in stream state before reading\n"); + } - vector offsets = get_offsets_bin(this_bin); + vector offsets = get_offsets_bin(this_bin); - const streamoff total_offset = offsets[0]; + 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 + 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."); - } + if (!*sino_stream) { + error("ProjDataFromStream::get_bin_value: error after seekg."); + } - Array< 1, float> value(1); - float scale = float(1); + 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"); - if(scale != 1.f) - error("ProjDataFromStream: error reading data: scale factor returned by read_data should be 1\n"); + // 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"); + if (scale != 1.f) + error("ProjDataFromStream: error reading data: scale factor returned by read_data should be 1\n"); - value *= scale_factor; + value *= scale_factor; - return value[0]; + return value[0]; } void -ProjDataFromStream::set_bin_value(const Bin& this_bin) -{ - if (is_null_ptr(sino_stream)) - { - error("ProjDataFromStream::set_bin_value: stream ptr is 0\n"); - } - if (! *sino_stream) - { - error("ProjDataFromStream::set_bin_value: error in stream state before writing"); - } +ProjDataFromStream::set_bin_value(const Bin& this_bin) { + if (is_null_ptr(sino_stream)) { + error("ProjDataFromStream::set_bin_value: stream ptr is 0\n"); + } + if (!*sino_stream) { + error("ProjDataFromStream::set_bin_value: error in stream state before writing"); + } - vector offsets = get_offsets_bin(this_bin); + vector offsets = get_offsets_bin(this_bin); - const streamoff total_offset = offsets[0]; + 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 + 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."); - } + if (!*sino_stream) { + error("ProjDataFromStream::set_bin_value: error after seekp."); + } - Array< 1, float> value(1); - value[0]=this_bin.get_bin_value(); - float scale = float(1); - // Now the storage order is not more important. Just read. - if (write_data(*sino_stream, value, on_disk_data_type, scale, on_disk_byte_order) - == Succeeded::no) - error("ProjDataFromStream: error writing data\n"); - if(scale != 1.f) - error("ProjDataFromStream: error writing data: scale factor returned by write_data should be 1\n"); + Array<1, float> value(1); + value[0] = this_bin.get_bin_value(); + float scale = float(1); + // Now the storage order is not more important. Just read. + if (write_data(*sino_stream, value, on_disk_data_type, scale, on_disk_byte_order) == Succeeded::no) + error("ProjDataFromStream: error writing data\n"); + if (scale != 1.f) + 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 int timing_num) const +ProjDataFromStream::get_offsets(const int view_num, const int segment_num, const int timing_num) const { - if (!(segment_num >= get_min_segment_num() && - segment_num <= get_max_segment_num())) + 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); + 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()); + 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) - { + for (int i = 0; i < index; i++) + num_axial_pos_offset += get_num_axial_poss(segment_sequence[i]); + + streamoff segment_offset = offset + static_cast(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(); + (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(); + 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(); + } 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; + } else if (get_storage_order() == Timing_Segment_View_AxialPos_TangPos) { + // The timing offset will be added to the segment offset. This approach we minimise the + // changes + if (!(timing_num >= get_min_tof_pos_num() && timing_num <= get_max_tof_pos_num())) + error("ProjDataFromStream::get_offsets: timing_num out of range : %d", timing_num); + + const int timing_index = static_cast(FIND(timing_poss_sequence.begin(), timing_poss_sequence.end(), timing_num) - + timing_poss_sequence.begin()); + assert(offset_3d_data > 0); + segment_offset += static_cast(timing_index) * offset_3d_data; + + 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[0] = segment_offset; + temp[1] = beg_view_offset; temp[2] = 0; return temp; - } - else if (get_storage_order() == Timing_Segment_View_AxialPos_TangPos) - { - // The timing offset will be added to the segment offset. This approach we minimise the - // changes - if (!(timing_num >= get_min_tof_pos_num() && - timing_num <= get_max_tof_pos_num())) - error("ProjDataFromStream::get_offsets: timing_num out of range : %d", timing_num); - - const int timing_index = static_cast(FIND(timing_poss_sequence.begin(), timing_poss_sequence.end(), timing_num) - - timing_poss_sequence.begin()); - - assert(offset_3d_data > 0); - segment_offset += static_cast(timing_index) * offset_3d_data; - - 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; - } - else - { + } else { error("ProjDataFromStream::get_offsets: unsupported storage_order"); return vector(); // return something to avoid compiler warning } } Succeeded -ProjDataFromStream::set_viewgram(const Viewgram& v) -{ - if (is_null_ptr(sino_stream)) - { +ProjDataFromStream::set_viewgram(const Viewgram& v) { + if (is_null_ptr(sino_stream)) { warning("ProjDataFromStream::set_viewgram: stream ptr is 0\n"); return Succeeded::no; } - if (! *sino_stream) - { + if (!*sino_stream) { warning("ProjDataFromStream::set_viewgram: error in stream state before writing\n"); return Succeeded::no; } // KT 03/07/2001 modified handling of scale_factor etc. - if (on_disk_data_type.id != NumericType::FLOAT) - { + if (on_disk_data_type.id != NumericType::FLOAT) { warning("ProjDataFromStream::set_viewgram: non-float output uses original " "scale factor %g which might not be appropriate for the current data\n", scale_factor); } - if (get_num_tangential_poss() != v.get_proj_data_info_sptr()->get_num_tangential_poss()) - { + 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"); return Succeeded::no; } - if (get_num_axial_poss(v.get_segment_num()) != v.get_num_axial_poss()) - { + 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"); return Succeeded::no; } - - if (*get_proj_data_info_sptr() != *(v.get_proj_data_info_sptr())) - { + if (*get_proj_data_info_sptr() != *(v.get_proj_data_info_sptr())) { warning("ProjDataFromStream::set_viewgram: viewgram has incompatible ProjDataInfo member\n" "Original ProjDataInfo: %s\n" "ProjDataInfo From viewgram: %s", - this->get_proj_data_info_sptr()->parameter_info().c_str(), - v.get_proj_data_info_sptr()->parameter_info().c_str() - ); + this->get_proj_data_info_sptr()->parameter_info().c_str(), v.get_proj_data_info_sptr()->parameter_info().c_str()); - return Succeeded::no; + return Succeeded::no; } int segment_num = v.get_segment_num(); int view_num = v.get_view_num(); int timing_pos = v.get_timing_pos_num(); - - vector offsets = get_offsets(view_num,segment_num, timing_pos); + vector offsets = get_offsets(view_num, segment_num, timing_pos); const streamoff segment_offset = offsets[0]; const streamoff beg_view_offset = offsets[1]; const streamoff intra_views_offset = offsets[2]; @@ -486,242 +390,174 @@ ProjDataFromStream::set_viewgram(const Viewgram& v) Succeeded succeeded = Succeeded::yes; #ifdef STIR_OPENMP -#pragma omp critical(PROJDATAFROMSTREAMIO) +# pragma omp critical(PROJDATAFROMSTREAMIO) #endif { - sino_stream->seekp(segment_offset, ios::beg); // start of segment + 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); - } - // 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(); + + 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); } - else if (get_storage_order() == Timing_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(); + // 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; } - else - { - warning("ProjDataFromStream::set_viewgram: unsupported storage order\n"); + // flush the stream, see the class documentation + sino_stream->flush(); + } else if (get_storage_order() == Timing_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(); + } 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 -{ - - 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()); +ProjDataFromStream::get_offsets_bin(const Bin this_bin) const { - 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()); + 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()); + 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()); - const int index = - static_cast(FIND(segment_sequence.begin(), segment_sequence.end(), this_bin.segment_num()) - - segment_sequence.begin()); - - streamoff num_axial_pos_offset = 0; - - for (int i=0; i(FIND(segment_sequence.begin(), segment_sequence.end(), this_bin.segment_num()) - segment_sequence.begin()); - streamoff segment_offset = - offset + - static_cast(num_axial_pos_offset* - get_num_tangential_poss() * - get_num_views() * - on_disk_data_type.size_in_bytes()); + streamoff num_axial_pos_offset = 0; - // Now we are just in front of the correct segment - if (get_storage_order() == Segment_AxialPos_View_TangPos) - { - // skip axial positions - const streamoff ax_pos_offset = - (this_bin.axial_pos_num() - get_min_axial_pos_num(this_bin.segment_num()))* - get_num_views() * - get_num_tangential_poss()* - on_disk_data_type.size_in_bytes(); + for (int i = 0; i < index; i++) + num_axial_pos_offset += get_num_axial_poss(segment_sequence[i]); - // sinogram location + streamoff segment_offset = offset + static_cast(num_axial_pos_offset * get_num_tangential_poss() * get_num_views() * + on_disk_data_type.size_in_bytes()); - //find view - const streamoff view_offset = - (this_bin.view_num() - get_min_view_num()) - * get_num_tangential_poss() - * 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) { + // skip axial positions + const streamoff ax_pos_offset = (this_bin.axial_pos_num() - get_min_axial_pos_num(this_bin.segment_num())) * get_num_views() * + get_num_tangential_poss() * on_disk_data_type.size_in_bytes(); - // find tang pos - const streamoff tang_offset = - (this_bin.tangential_pos_num() - get_min_tangential_pos_num()) * on_disk_data_type.size_in_bytes(); + // sinogram location - vector temp(1); - temp[0] = segment_offset + ax_pos_offset + view_offset +tang_offset; + // find view + const streamoff view_offset = + (this_bin.view_num() - get_min_view_num()) * get_num_tangential_poss() * on_disk_data_type.size_in_bytes(); - return temp; - } - else if (get_storage_order() == Segment_View_AxialPos_TangPos) - { + // find tang pos + const streamoff tang_offset = + (this_bin.tangential_pos_num() - get_min_tangential_pos_num()) * on_disk_data_type.size_in_bytes(); - // Skip views - const streamoff view_offset = - (this_bin.view_num() - get_min_view_num())* - get_num_axial_poss(this_bin.segment_num()) * - get_num_tangential_poss()* - on_disk_data_type.size_in_bytes(); + vector temp(1); + temp[0] = segment_offset + ax_pos_offset + view_offset + tang_offset; + return temp; + } else if (get_storage_order() == Segment_View_AxialPos_TangPos) { - // find axial pos - const streamoff ax_pos_offset = - (this_bin.axial_pos_num() - get_min_axial_pos_num(this_bin.segment_num())) * - get_num_tangential_poss()* - on_disk_data_type.size_in_bytes(); + // Skip views + const streamoff view_offset = (this_bin.view_num() - get_min_view_num()) * get_num_axial_poss(this_bin.segment_num()) * + get_num_tangential_poss() * on_disk_data_type.size_in_bytes(); - // find tang pos - const streamoff tang_offset = - (this_bin.tangential_pos_num() - get_min_tangential_pos_num()) * on_disk_data_type.size_in_bytes(); + // find axial pos + const streamoff ax_pos_offset = (this_bin.axial_pos_num() - get_min_axial_pos_num(this_bin.segment_num())) * + get_num_tangential_poss() * on_disk_data_type.size_in_bytes(); - vector temp(1); - temp[0] = segment_offset + ax_pos_offset +view_offset + tang_offset; + // find tang pos + const streamoff tang_offset = + (this_bin.tangential_pos_num() - get_min_tangential_pos_num()) * on_disk_data_type.size_in_bytes(); - return temp; - } - else if (get_storage_order() == Timing_Segment_View_AxialPos_TangPos) - { - // The timing offset will be added to the segment offset. This approach we minimise the - // changes - if (!(this_bin.timing_pos_num() >= get_min_tof_pos_num() && - this_bin.timing_pos_num() <= get_max_tof_pos_num())) - error("ProjDataFromStream::get_offsets_bin: timing_num out of range : %d", this_bin.timing_pos_num()); + vector temp(1); + temp[0] = segment_offset + ax_pos_offset + view_offset + tang_offset; - const int timing_index = static_cast(FIND(timing_poss_sequence.begin(), timing_poss_sequence.end(), this_bin.timing_pos_num()) - - timing_poss_sequence.begin()); + return temp; + } else if (get_storage_order() == Timing_Segment_View_AxialPos_TangPos) { + // The timing offset will be added to the segment offset. This approach we minimise the + // changes + if (!(this_bin.timing_pos_num() >= get_min_tof_pos_num() && this_bin.timing_pos_num() <= get_max_tof_pos_num())) + error("ProjDataFromStream::get_offsets_bin: timing_num out of range : %d", this_bin.timing_pos_num()); - assert(offset_3d_data > 0); - segment_offset += static_cast(timing_index) * offset_3d_data; + const int timing_index = static_cast( + FIND(timing_poss_sequence.begin(), timing_poss_sequence.end(), this_bin.timing_pos_num()) - timing_poss_sequence.begin()); - // Skip views - const streamoff view_offset = - (this_bin.view_num() - get_min_view_num())* - get_num_axial_poss(this_bin.segment_num()) * - get_num_tangential_poss()* - on_disk_data_type.size_in_bytes(); + 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()) * get_num_axial_poss(this_bin.segment_num()) * + get_num_tangential_poss() * on_disk_data_type.size_in_bytes(); - // find axial pos - const streamoff ax_pos_offset = - (this_bin.axial_pos_num() - get_min_axial_pos_num(this_bin.segment_num())) * - get_num_tangential_poss()* - on_disk_data_type.size_in_bytes(); + // find axial pos + const streamoff ax_pos_offset = (this_bin.axial_pos_num() - get_min_axial_pos_num(this_bin.segment_num())) * + get_num_tangential_poss() * on_disk_data_type.size_in_bytes(); - // find tang pos - const streamoff tang_offset = - (this_bin.tangential_pos_num() - get_min_tangential_pos_num()) * on_disk_data_type.size_in_bytes(); + // find tang pos + 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; + vector temp(1); + temp[0] = segment_offset + ax_pos_offset + view_offset + tang_offset; - return temp; - } - else - { - error("ProjDataFromStream::get_offsets_bin: unsupported storage order"); - return vector(); // return something to avoid compiler warning - } + return temp; + } else { + error("ProjDataFromStream::get_offsets_bin: unsupported storage order"); + return vector(); // 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 int timing_num) const -{ - if (!(segment_num >= get_min_segment_num() && - segment_num <= get_max_segment_num())) +ProjDataFromStream::get_offsets_sino(const int ax_pos_num, const int segment_num, const int timing_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))) + 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()); - + 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()); - streamoff segment_offset = - offset + - static_cast(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) { - 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(); + 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; @@ -729,74 +565,61 @@ ProjDataFromStream::get_offsets_sino(const int ax_pos_num, const int segment_num temp[2] = 0; return temp; - } - else if (get_storage_order() == Segment_View_AxialPos_TangPos) - { - + } 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(); + (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(); - + (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; + temp[0] = segment_offset; + temp[1] = beg_ax_pos_offset; + temp[2] = intra_ax_pos_offset; return temp; - } - else if (get_storage_order() == Timing_Segment_View_AxialPos_TangPos) - { - // The timing offset will be added to the segment offset. This approach we minimise the - // changes - if (!(timing_num >= get_min_tof_pos_num() && - timing_num <= get_max_tof_pos_num())) - error("ProjDataFromStream::get_offsets: timing_num out of range : %d", timing_num); - - const int timing_index = static_cast(FIND(timing_poss_sequence.begin(), timing_poss_sequence.end(), timing_num) - - timing_poss_sequence.begin()); + } else if (get_storage_order() == Timing_Segment_View_AxialPos_TangPos) { + // The timing offset will be added to the segment offset. This approach we minimise the + // changes + if (!(timing_num >= get_min_tof_pos_num() && timing_num <= get_max_tof_pos_num())) + error("ProjDataFromStream::get_offsets: timing_num out of range : %d", timing_num); - assert(offset_3d_data > 0); - segment_offset += static_cast(timing_index) * offset_3d_data; + const int timing_index = static_cast(FIND(timing_poss_sequence.begin(), timing_poss_sequence.end(), timing_num) - + timing_poss_sequence.begin()); - 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(); + assert(offset_3d_data > 0); + segment_offset += static_cast(timing_index) * offset_3d_data; - const streamoff intra_ax_pos_offset = - (get_num_axial_poss(segment_num) -1) *get_num_tangential_poss() * on_disk_data_type.size_in_bytes(); + 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; - } - else - { - error("ProjDataFromStream::get_offsets_sino: unsupported storage order"); - return vector(); // return something to avoid compiler warning + vector temp(3); + temp[0] = segment_offset; + temp[1] = beg_ax_pos_offset; + temp[2] = intra_ax_pos_offset; + return temp; + } else { + error("ProjDataFromStream::get_offsets_sino: unsupported storage order"); + return vector(); // return something to avoid compiler warning } } Sinogram -ProjDataFromStream::get_sinogram(const int ax_pos_num, const int segment_num, - const bool make_num_tangential_poss_odd, const int timing_pos) const -{ - if (is_null_ptr(sino_stream)) - { +ProjDataFromStream::get_sinogram(const int ax_pos_num, const int segment_num, 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"); } - if (! *sino_stream) - { + if (!*sino_stream) { error("ProjDataFromStream::get_sinogram: error in stream state before reading\n"); } // 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, timing_pos); + vector offsets = get_offsets_sino(ax_pos_num, segment_num, timing_pos); const streamoff segment_offset = offsets[0]; const streamoff beg_ax_pos_offset = offsets[1]; @@ -806,109 +629,87 @@ ProjDataFromStream::get_sinogram(const int ax_pos_num, const int segment_num, float scale = float(1); Succeeded succeeded = Succeeded::yes; - if (get_storage_order() == Segment_AxialPos_View_TangPos) - { + if (get_storage_order() == Segment_AxialPos_View_TangPos) { #ifdef STIR_OPENMP -#pragma omp critical(PROJDATAFROMSTREAMIO) +# 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 || get_storage_order() == Timing_Segment_View_AxialPos_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; + } 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 || + get_storage_order() == Timing_Segment_View_AxialPos_TangPos) { #ifdef STIR_OPENMP -#pragma omp critical(PROJDATAFROMSTREAMIO) +# 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; - } - for (int view = get_min_view_num(); view <= get_max_view_num(); view++) - { - 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); - } - } // end of critical section + { + 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++) { + 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); + } + } // end of critical section + } if (succeeded == Succeeded::no) error("ProjDataFromStream: error reading data"); sinogram *= scale_factor; - if (make_num_tangential_poss_odd&&(get_num_tangential_poss()%2==0)) - { - int new_max_tangential_pos = get_max_tangential_pos_num() + 1; + if (make_num_tangential_poss_odd && (get_num_tangential_poss() % 2 == 0)) { + int new_max_tangential_pos = get_max_tangential_pos_num() + 1; - sinogram.grow(IndexRange2D(get_min_view_num(), - get_max_view_num(), - get_min_tangential_pos_num(), - new_max_tangential_pos)); - } + sinogram.grow(IndexRange2D(get_min_view_num(), get_max_view_num(), get_min_tangential_pos_num(), new_max_tangential_pos)); + } return sinogram; } Succeeded -ProjDataFromStream::set_sinogram(const Sinogram& s) -{ - if (is_null_ptr(sino_stream)) - { +ProjDataFromStream::set_sinogram(const Sinogram& s) { + if (is_null_ptr(sino_stream)) { warning("ProjDataFromStream::set_sinogram: stream ptr is 0\n"); return Succeeded::no; } - if (! *sino_stream) - { + if (!*sino_stream) { warning("ProjDataFromStream::set_sinogram: error in stream state before writing\n"); return Succeeded::no; } // KT 03/07/2001 modified handling of scale_factor etc. - if (on_disk_data_type.id != NumericType::FLOAT) - { + if (on_disk_data_type.id != NumericType::FLOAT) { warning("ProjDataFromStream::set_sinogram: non-float output uses original " "scale factor %g which might not be appropriate for the current data\n", scale_factor); } - if (*get_proj_data_info_sptr() != *(s.get_proj_data_info_sptr())) - { + if (*get_proj_data_info_sptr() != *(s.get_proj_data_info_sptr())) { warning("ProjDataFromStream::set_sinogram: Sinogram has incompatible ProjDataInfo member.\n" "Original ProjDataInfo: %s\n" "ProjDataInfo from sinogram: %s", - this->get_proj_data_info_sptr()->parameter_info().c_str(), - s.get_proj_data_info_sptr()->parameter_info().c_str() - ); + this->get_proj_data_info_sptr()->parameter_info().c_str(), s.get_proj_data_info_sptr()->parameter_info().c_str()); return Succeeded::no; } @@ -916,8 +717,7 @@ ProjDataFromStream::set_sinogram(const Sinogram& s) int ax_pos_num = s.get_axial_pos_num(); int timing_pos = s.get_timing_pos_num(); - - vector offsets = get_offsets_sino(ax_pos_num,segment_num, timing_pos); + vector offsets = get_offsets_sino(ax_pos_num, segment_num, timing_pos); const streamoff segment_offset = offsets[0]; const streamoff beg_ax_pos_offset = offsets[1]; const streamoff intra_ax_pos_offset = offsets[2]; @@ -925,239 +725,191 @@ ProjDataFromStream::set_sinogram(const Sinogram& s) Succeeded succeeded = Succeeded::yes; #ifdef STIR_OPENMP -#pragma omp critical(PROJDATAFROMSTREAMIO) +# pragma omp critical(PROJDATAFROMSTREAMIO) #endif { - sino_stream->seekp(segment_offset, ios::beg); // start of segment + 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"); + + 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; - } - - 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 || get_storage_order() == Timing_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; + // flush the stream, see the class documentation + sino_stream->flush(); + } + + else if (get_storage_order() == Segment_View_AxialPos_TangPos || + get_storage_order() == Timing_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 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()); +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(FIND(segment_sequence.begin(), segment_sequence.end(), segment_num) - segment_sequence.begin()); + + streamoff num_axial_pos_offset = 0; + for (int i = 0; i < index; i++) + num_axial_pos_offset += get_num_axial_poss(segment_sequence[i]); + const streamoff segment_offset = + offset + num_axial_pos_offset * get_num_tangential_poss() * get_num_views() * on_disk_data_type.size_in_bytes(); + return segment_offset; + } } std::streamoff -ProjDataFromStream::get_offset_timing(const int timing_num) const -{ +ProjDataFromStream::get_offset_timing(const int timing_num) const { - const int index = - static_cast(FIND(timing_poss_sequence.begin(), timing_poss_sequence.end(), timing_num) - - timing_poss_sequence.begin()); + const int index = + static_cast(FIND(timing_poss_sequence.begin(), timing_poss_sequence.end(), timing_num) - timing_poss_sequence.begin()); - assert (timing_num >= get_min_tof_pos_num() && - timing_num <= get_max_tof_pos_num()); + assert(timing_num >= get_min_tof_pos_num() && timing_num <= get_max_tof_pos_num()); + { + if (offset_3d_data < 0) // Calculate the full 3D sinogram size - Very slow { - if(offset_3d_data < 0 ) // Calculate the full 3D sinogram size - Very slow - { - long long 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(); - } - return static_cast (sum * index * on_disk_data_type.size_in_bytes()); - } - else - return static_cast(offset_3d_data * index); - } -} + long long 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(); + } + return static_cast(sum * index * on_disk_data_type.size_in_bytes()); + } else + return static_cast(offset_3d_data * index); + } +} // TODO the segment version could be written in terms of the above. // -> No need for get_offset_segment SegmentBySinogram -ProjDataFromStream::get_segment_by_sinogram(const int segment_num, const int timing_num) const -{ - if(is_null_ptr(sino_stream)) - { +ProjDataFromStream::get_segment_by_sinogram(const int segment_num, const int timing_num) const { + if (is_null_ptr(sino_stream)) { error("ProjDataFromStream::get_segment_by_sinogram: stream ptr is 0\n"); } - if (! *sino_stream) - { + if (!*sino_stream) { error("ProjDataFromStream::get_segment_by_sinogram: error in stream state before reading\n"); } - - streamoff segment_offset = get_offset_segment(segment_num); + + streamoff segment_offset = get_offset_segment(segment_num); // Go to the right timing full 3D sinogram - segment_offset += get_offset_timing(timing_num) ; + segment_offset += get_offset_timing(timing_num); - if (get_storage_order() == Segment_AxialPos_View_TangPos) - { - SegmentBySinogram segment(proj_data_info_sptr,segment_num, timing_num); - float scale = float(1); - Succeeded succeeded = Succeeded::yes; + if (get_storage_order() == Segment_AxialPos_View_TangPos) { + SegmentBySinogram segment(proj_data_info_sptr, segment_num, timing_num); + float scale = float(1); + Succeeded succeeded = Succeeded::yes; #ifdef STIR_OPENMP -#pragma omp critical(PROJDATAFROMSTREAMIO) +# 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 + { + 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 if (succeeded == Succeeded::no) error("ProjDataFromStream: error reading data\n"); - if(scale != 1) + if (scale != 1) error("ProjDataFromStream: error reading data: scale factor returned by read_data should be 1\n"); - segment *= scale_factor; - return segment; - } - else - { - // TODO rewrite in terms of get_viewgram - return SegmentBySinogram (get_segment_by_view(segment_num, timing_num)); - } + segment *= scale_factor; + return segment; + } else { + // TODO rewrite in terms of get_viewgram + return SegmentBySinogram(get_segment_by_view(segment_num, timing_num)); + } } SegmentByView -ProjDataFromStream::get_segment_by_view(const int segment_num, const int timing_pos) const -{ - if(is_null_ptr(sino_stream)) - { +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"); } - if (! *sino_stream) - { + if (!*sino_stream) { error("ProjDataFromStream::get_segment_by_view: error in stream state before reading\n"); } - if (get_storage_order() == Segment_View_AxialPos_TangPos || get_storage_order() == Timing_Segment_View_AxialPos_TangPos) - { - SegmentByView segment(proj_data_info_sptr,segment_num, timing_pos); + if (get_storage_order() == Segment_View_AxialPos_TangPos || get_storage_order() == Timing_Segment_View_AxialPos_TangPos) { + SegmentByView segment(proj_data_info_sptr, segment_num, timing_pos); streamoff segment_offset = get_offset_segment(segment_num); - // Go to the right timing full 3D sinogram - segment_offset += get_offset_timing(timing_pos) ; + // Go to the right timing full 3D sinogram + segment_offset += get_offset_timing(timing_pos); float scale = float(1); - Succeeded succeeded = Succeeded::yes; + Succeeded succeeded = Succeeded::yes; #ifdef STIR_OPENMP -#pragma omp critical(PROJDATAFROMSTREAMIO) +# 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); - } + 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 if (succeeded == Succeeded::no) error("ProjDataFromStream: error reading data"); - if(scale != 1) + if (scale != 1) error("ProjDataFromStream: error reading data: scale factor returned by read_data should be 1\n"); 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, timing_pos)); + return SegmentByView(get_segment_by_sinogram(segment_num, timing_pos)); } Succeeded -ProjDataFromStream::set_segment(const SegmentBySinogram& segmentbysinogram_v) -{ - if(is_null_ptr(sino_stream)) - { +ProjDataFromStream::set_segment(const SegmentBySinogram& segmentbysinogram_v) { + if (is_null_ptr(sino_stream)) { error("ProjDataFromStream::set_segment: stream ptr is 0\n"); } - if (! *sino_stream) - { + if (!*sino_stream) { error("ProjDataFromStream::set_segment: error in stream state before writing\n"); } - if (get_num_tangential_poss() != segmentbysinogram_v.get_num_tangential_poss()) - { + if (get_num_tangential_poss() != segmentbysinogram_v.get_num_tangential_poss()) { warning("ProjDataFromStream::set_segmen: num_bins is not correct\n"); return Succeeded::no; } - if (get_num_views() != segmentbysinogram_v.get_num_views()) - { + if (get_num_views() != segmentbysinogram_v.get_num_views()) { warning("ProjDataFromStream::set_segment: num_views is not correct\n"); return Succeeded::no; } @@ -1165,74 +917,58 @@ ProjDataFromStream::set_segment(const SegmentBySinogram& segmentbysinogra int segment_num = segmentbysinogram_v.get_segment_num(); streamoff segment_offset = get_offset_segment(segment_num); // Go to the right timing full 3D sinogram - segment_offset += get_offset_timing(segmentbysinogram_v.get_timing_pos_num()) ; - - if (get_storage_order() == Segment_AxialPos_View_TangPos) - { - // KT 03/07/2001 handle scale_factor appropriately - if (on_disk_data_type.id != NumericType::FLOAT) - { - warning("ProjDataFromStream::set_segment: non-float output uses original " - "scale factor %g which might not be appropriate for the current data\n", - scale_factor); - } - float scale = scale_factor; - Succeeded succeeded = Succeeded::yes; + segment_offset += get_offset_timing(segmentbysinogram_v.get_timing_pos_num()); + + if (get_storage_order() == Segment_AxialPos_View_TangPos) { + // KT 03/07/2001 handle scale_factor appropriately + if (on_disk_data_type.id != NumericType::FLOAT) { + warning("ProjDataFromStream::set_segment: non-float output uses original " + "scale factor %g which might not be appropriate for the current data\n", + scale_factor); + } + float scale = scale_factor; + Succeeded succeeded = Succeeded::yes; #ifdef STIR_OPENMP -#pragma omp critical(PROJDATAFROMSTREAMIO) +# 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) 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(); - } // end of critical section - return succeeded; - } - else { - // TODO rewrite in terms of set_viewgram - const SegmentByView segmentbyview= - SegmentByView(segmentbysinogram_v); + 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) 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(); + } // end of critical section + return succeeded; + } else { + // TODO rewrite in terms of set_viewgram + const SegmentByView segmentbyview = SegmentByView(segmentbysinogram_v); - return set_segment(segmentbyview); - } + return set_segment(segmentbyview); + } } - Succeeded -ProjDataFromStream::set_segment(const SegmentByView& segmentbyview_v) -{ - if(is_null_ptr(sino_stream)) - { +ProjDataFromStream::set_segment(const SegmentByView& segmentbyview_v) { + if (is_null_ptr(sino_stream)) { error("ProjDataFromStream::set_segment: stream ptr is 0\n"); } - if (! *sino_stream) - { + 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()) - { + if (get_num_tangential_poss() != segmentbyview_v.get_num_tangential_poss()) { warning("ProjDataFromStream::set_segment: num_bins is not correct\n"); return Succeeded::no; } - if (get_num_views() != segmentbyview_v.get_num_views()) - { + if (get_num_views() != segmentbyview_v.get_num_views()) { warning("ProjDataFromStream::set_segment: num_views is not correct\n"); return Succeeded::no; } @@ -1240,53 +976,43 @@ ProjDataFromStream::set_segment(const SegmentByView& segmentbyview_v) int segment_num = segmentbyview_v.get_segment_num(); streamoff segment_offset = get_offset_segment(segment_num); // Go to the right timing full 3D sinogram - segment_offset += get_offset_timing(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) - { - warning("ProjDataFromStream::set_segment: non-float output uses original " - "scale factor %g which might not be appropriate for the current data\n", - scale_factor); - } - float scale = scale_factor; - Succeeded succeeded = Succeeded::yes; + segment_offset += get_offset_timing(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) { + warning("ProjDataFromStream::set_segment: non-float output uses original " + "scale factor %g which might not be appropriate for the current data\n", + scale_factor); + } + float scale = scale_factor; + Succeeded succeeded = Succeeded::yes; #ifdef STIR_OPENMP -#pragma omp critical(PROJDATAFROMSTREAMIO) +# 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) 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(); - } // end of critical section - return succeeded; - } - else { - // TODO rewrite in terms of set_sinogram - const SegmentBySinogram segmentbysinogram = - SegmentBySinogram(segmentbyview_v); - return set_segment(segmentbysinogram); - } + 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) 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(); + } // end of critical section + return succeeded; + } else { + // TODO rewrite in terms of set_sinogram + const SegmentBySinogram segmentbysinogram = SegmentBySinogram(segmentbyview_v); + return set_segment(segmentbysinogram); + } } - #if 0 ProjDataFromStream* ProjDataFromStream::ask_parameters(const bool on_disk) { @@ -1332,7 +1058,7 @@ ProjDataFromStream* ProjDataFromStream::ask_parameters(const bool on_disk) memory = (char *)read_stream_in_memory(input, file_size); } -#ifdef BOOST_NO_STRINGSTREAM +# 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); @@ -1342,14 +1068,14 @@ ProjDataFromStream* ProjDataFromStream::ask_parameters(const bool on_disk) strstreambuf * buffer = new strstreambuf(memory, file_size, memory+file_size); p_in_stream.reset(new iostream(buffer)); -#else +# 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)), open_mode | ios::binary)); delete[] memory; -#endif +# endif } // else 'on_disk' @@ -1454,18 +1180,8 @@ ProjDataFromStream* ProjDataFromStream::ask_parameters(const bool on_disk) #endif float -ProjDataFromStream::get_scale_factor() const -{ - return scale_factor;} - - +ProjDataFromStream::get_scale_factor() const { + return scale_factor; +} END_NAMESPACE_STIR - - - - - - - - diff --git a/src/buildblock/ProjDataGEAdvance.cxx b/src/buildblock/ProjDataGEAdvance.cxx index 9c3dafd1ad..9d63f4ee9d 100644 --- a/src/buildblock/ProjDataGEAdvance.cxx +++ b/src/buildblock/ProjDataGEAdvance.cxx @@ -28,8 +28,6 @@ See STIR/LICENSE.txt for details */ - - #include "stir/ProjDataGEAdvance.h" #include "stir/Succeeded.h" #include "stir/Viewgram.h" @@ -55,272 +53,224 @@ using std::streamoff; START_NAMESPACE_STIR ProjDataGEAdvance::ProjDataGEAdvance(iostream* s) - : - sino_stream(s), - offset(0), - on_disk_data_type(NumericType::SHORT), - on_disk_byte_order(ByteOrder::big_endian) -{ + : sino_stream(s), offset(0), on_disk_data_type(NumericType::SHORT), on_disk_byte_order(ByteOrder::big_endian) { // TODO find from file const int max_delta = 11; shared_ptr scanner_sptr(new Scanner(Scanner::Advance)); - proj_data_info_sptr.reset( - ProjDataInfo::ProjDataInfoGE(scanner_sptr, max_delta, - scanner_sptr->get_max_num_views(), - scanner_sptr->get_default_num_arccorrected_bins())); - - + proj_data_info_sptr.reset(ProjDataInfo::ProjDataInfoGE(scanner_sptr, max_delta, scanner_sptr->get_max_num_views(), + scanner_sptr->get_default_num_arccorrected_bins())); + const int min_view = proj_data_info_sptr->get_min_view_num(); const int max_view = proj_data_info_sptr->get_max_view_num(); view_scaling_factor.grow(min_view, max_view); - + const int offset_begin = 256; - sino_stream->seekg(offset, ios::beg); // overall offset + sino_stream->seekg(offset, ios::beg); // overall offset sino_stream->seekg(offset_begin, ios::cur); // "breakfile" offset - + // reads the views_scaling_factors { float scale = float(1); // KT 18/10/99 added on_disk_byte_order - if (read_data(*sino_stream, view_scaling_factor, - NumericType::FLOAT, - scale, - on_disk_byte_order) == Succeeded::no - || scale != 1) + if (read_data(*sino_stream, view_scaling_factor, NumericType::FLOAT, scale, on_disk_byte_order) == Succeeded::no || + scale != 1) error("ProjDataGEAdvance: error reading scale factors from header\n"); - + // for ( int k=min_view ; k <= max_view ; k++) // { - // cout << "view_scaling_factor[" << k << "] = " + // cout << "view_scaling_factor[" << k << "] = " // << view_scaling_factor[k] << endl; // } } - + //============================================================= // parameters as in the unsorted case (help to access the file) //============================================================= //------------------------------------------------------------- // WARNING: this construction works only with odd max_delta //------------------------------------------------------------- - - int num_segments_orig = proj_data_info_sptr->get_max_segment_num()+1; - //(int) get_max_average_ring_difference(); - + + int num_segments_orig = proj_data_info_sptr->get_max_segment_num() + 1; + //(int) get_max_average_ring_difference(); + num_rings_orig.resize(num_segments_orig); segment_sequence_orig.resize(num_segments_orig); - - + const int num_rings = proj_data_info_sptr->get_scanner_ptr()->get_num_rings(); - num_rings_orig[0]=(2*num_rings-1); - + num_rings_orig[0] = (2 * num_rings - 1); + { - for (unsigned int i=1; i<= num_rings_orig.size()/2; i++) - { - num_rings_orig[2*i-1]=(2*num_rings-1) - 4*i; - num_rings_orig[2*i]=(2*num_rings-1) - 4*i; + for (unsigned int i = 1; i <= num_rings_orig.size() / 2; i++) { + num_rings_orig[2 * i - 1] = (2 * num_rings - 1) - 4 * i; + num_rings_orig[2 * i] = (2 * num_rings - 1) - 4 * i; // cout << "num_rings_orig[" << 2*i-1 << "] = " << num_rings_orig[2*i-1] << endl; // cout << "num_rings_orig[" << 2*i << "] = " << num_rings_orig[2*i] << endl; } } - - - + // segment sequence in the unsorted data format - - segment_sequence_orig[0]=0; - + + segment_sequence_orig[0] = 0; + { - for (unsigned int i=1; i<= segment_sequence_orig.size()/2; i++) - { - segment_sequence_orig[2*i-1] = static_cast(i); - segment_sequence_orig[2*i] = -static_cast(i); + for (unsigned int i = 1; i <= segment_sequence_orig.size() / 2; i++) { + segment_sequence_orig[2 * i - 1] = static_cast(i); + segment_sequence_orig[2 * i] = -static_cast(i); // cout << "segment_sequence_orig[" << 2*i-1 << "] = " << segment_sequence_orig[2*i-1] << endl; // cout << "segment_sequence_orig[" << 2*i << "] = " << segment_sequence_orig[2*i] << endl; } } - } - + Viewgram -ProjDataGEAdvance:: -get_viewgram(const int view_num, const int segment_num, - const bool make_num_tangential_poss_odd, const int timing_pos) const - { - // -------------------------------------------------------- - // -------------------------------------------------------- - // Advance GE format reader with sort of the segments - // -------------------------------------------------------- - // -------------------------------------------------------- - // - // segment_num 0 +1 -1 +2 -2 +3 -3 ....... - // ave_ring_diff 0 +2 -2 +3 -3 +4 -4 ....... - // max_ring_diff +1 +2 -2 +3 -3 +4 -4 ....... - // min_ring_diff -1 +2 -2 +3 -3 +4 -4 ....... - - - streamoff odd_ring_segment_offset; - streamoff num_rings_offset; - streamoff segment_offset; - streamoff jump_size; - streamoff jump_ring; - - - //---------------------------------------------------------------- - // data with delta ring = +1 and -1 are contained in segment 0: - // for this reason there is not a segment +1 and a segment -1 - //---------------------------------------------------------------- - - // segment number in the unsorted data format - // (no problem for segments +1 and -1) - - const int sign_segment_num = (segment_num == 0) ? 0 : ( (segment_num > 0) ? 1 : -1 ); - const int segment_num_orig = (int) ((segment_num+sign_segment_num) / 2); - - // cout << "segment_num_orig " << segment_num_orig << endl; - - // segment index in the unsorted data format - - const int index_orig = - static_cast(find(segment_sequence_orig.begin(), - segment_sequence_orig.end(), segment_num_orig) - - segment_sequence_orig.begin()); - - // cout << "index_orig " << index_orig << endl; - - - //--------------------------------------------------------------- - // OFFSETS - //--------------------------------------------------------------- - - const streamoff offset_begin = 256; - - // offset in rings for the odd sorted segments (value 0 or 1) - - odd_ring_segment_offset = abs((segment_num+sign_segment_num) % 2); - - // cout << "odd_ring_segment_offset " << odd_ring_segment_offset << endl; - - // offset in rings in the unsorted data format - - num_rings_offset = - accumulate(num_rings_orig.begin(), - num_rings_orig.begin() + index_orig, 0) + - odd_ring_segment_offset; - - // cout << "num_rings_offset " << num_rings_offset << endl; - - // offset to the first data in the first view_num of the segment chosen - - segment_offset = - num_rings_offset * get_num_tangential_poss() * on_disk_data_type.size_in_bytes(); - - // cout << "segment_offset " << segment_offset << endl; - - // size of the jump to the first record of the following view_num - if (segment_num == 0) - { - jump_size = - ( - accumulate(num_rings_orig.begin(), num_rings_orig.end(), 0) - - num_rings_orig[index_orig] + - + 2 * odd_ring_segment_offset - ) * get_num_tangential_poss() * on_disk_data_type.size_in_bytes(); - } - else - { - jump_size = - ( - accumulate(num_rings_orig.begin(), num_rings_orig.end(), 0) - - num_rings_orig[index_orig] - - 1 // offset to balance the jump_ring in the cycle - + 2 * odd_ring_segment_offset - ) * get_num_tangential_poss() * on_disk_data_type.size_in_bytes(); - } - // cout << "jump_size " << jump_size << endl; - - // size of the jump between two rings of the same view_num and of the same - // segment - if (segment_num == 0) - jump_ring = 0; - else - jump_ring = get_num_tangential_poss() * on_disk_data_type.size_in_bytes(); - - streamoff ring_offset = get_num_tangential_poss() * on_disk_data_type.size_in_bytes(); - - // jumps the initial offset - - sino_stream->seekg(offset, ios::beg); // overall offset - sino_stream->seekg(offset_begin, ios::cur); // "breakfile" offset - sino_stream->seekg(get_num_views() * sizeof(float), ios::cur); // skip view scaling factors - - // jumps at the beginning of the chosen segment - - sino_stream->seekg(segment_offset, ios::cur); - - // jump to the right Viewgram - - streamoff jump_ini = (view_num - get_min_view_num())* - (ring_offset*get_num_axial_poss(segment_num) - + jump_ring*get_num_axial_poss(segment_num) - + jump_size); - - // cout << "ring_offset " << ring_offset << endl; - // cout << "jump_ring " << jump_ring << endl; - // cout << "jump_size " << jump_size << endl; - // cout << " jump_ini " << jump_ini << endl; - - sino_stream->seekg(jump_ini, ios::cur); - - Viewgram data = get_empty_viewgram(view_num, segment_num,false); - - for (int ring =get_min_axial_pos_num(segment_num) ; ring <= get_max_axial_pos_num(segment_num); ring++) +ProjDataGEAdvance::get_viewgram(const int view_num, const int segment_num, const bool make_num_tangential_poss_odd, + const int timing_pos) const { + // -------------------------------------------------------- + // -------------------------------------------------------- + // Advance GE format reader with sort of the segments + // -------------------------------------------------------- + // -------------------------------------------------------- + // + // segment_num 0 +1 -1 +2 -2 +3 -3 ....... + // ave_ring_diff 0 +2 -2 +3 -3 +4 -4 ....... + // max_ring_diff +1 +2 -2 +3 -3 +4 -4 ....... + // min_ring_diff -1 +2 -2 +3 -3 +4 -4 ....... + + streamoff odd_ring_segment_offset; + streamoff num_rings_offset; + streamoff segment_offset; + streamoff jump_size; + streamoff jump_ring; + + //---------------------------------------------------------------- + // data with delta ring = +1 and -1 are contained in segment 0: + // for this reason there is not a segment +1 and a segment -1 + //---------------------------------------------------------------- + + // segment number in the unsorted data format + // (no problem for segments +1 and -1) + + const int sign_segment_num = (segment_num == 0) ? 0 : ((segment_num > 0) ? 1 : -1); + const int segment_num_orig = (int)((segment_num + sign_segment_num) / 2); + + // cout << "segment_num_orig " << segment_num_orig << endl; + + // segment index in the unsorted data format + + const int index_orig = static_cast(find(segment_sequence_orig.begin(), segment_sequence_orig.end(), segment_num_orig) - + segment_sequence_orig.begin()); + + // cout << "index_orig " << index_orig << endl; + + //--------------------------------------------------------------- + // OFFSETS + //--------------------------------------------------------------- + + const streamoff offset_begin = 256; + + // offset in rings for the odd sorted segments (value 0 or 1) + + odd_ring_segment_offset = abs((segment_num + sign_segment_num) % 2); + + // cout << "odd_ring_segment_offset " << odd_ring_segment_offset << endl; + + // offset in rings in the unsorted data format + + num_rings_offset = accumulate(num_rings_orig.begin(), num_rings_orig.begin() + index_orig, 0) + odd_ring_segment_offset; + + // cout << "num_rings_offset " << num_rings_offset << endl; + + // offset to the first data in the first view_num of the segment chosen + + segment_offset = num_rings_offset * get_num_tangential_poss() * on_disk_data_type.size_in_bytes(); + + // cout << "segment_offset " << segment_offset << endl; + + // size of the jump to the first record of the following view_num + if (segment_num == 0) { + jump_size = (accumulate(num_rings_orig.begin(), num_rings_orig.end(), 0) - num_rings_orig[index_orig] + + +2 * odd_ring_segment_offset) * + get_num_tangential_poss() * on_disk_data_type.size_in_bytes(); + } else { + jump_size = (accumulate(num_rings_orig.begin(), num_rings_orig.end(), 0) - num_rings_orig[index_orig] - + 1 // offset to balance the jump_ring in the cycle + + 2 * odd_ring_segment_offset) * + get_num_tangential_poss() * on_disk_data_type.size_in_bytes(); + } + // cout << "jump_size " << jump_size << endl; + + // size of the jump between two rings of the same view_num and of the same + // segment + if (segment_num == 0) + jump_ring = 0; + else + jump_ring = get_num_tangential_poss() * on_disk_data_type.size_in_bytes(); + + streamoff ring_offset = get_num_tangential_poss() * on_disk_data_type.size_in_bytes(); + + // jumps the initial offset + + sino_stream->seekg(offset, ios::beg); // overall offset + sino_stream->seekg(offset_begin, ios::cur); // "breakfile" offset + sino_stream->seekg(get_num_views() * sizeof(float), ios::cur); // skip view scaling factors + + // jumps at the beginning of the chosen segment + + sino_stream->seekg(segment_offset, ios::cur); + + // jump to the right Viewgram + + streamoff jump_ini = (view_num - get_min_view_num()) * + (ring_offset * get_num_axial_poss(segment_num) + jump_ring * get_num_axial_poss(segment_num) + jump_size); + + // cout << "ring_offset " << ring_offset << endl; + // cout << "jump_ring " << jump_ring << endl; + // cout << "jump_size " << jump_size << endl; + // cout << " jump_ini " << jump_ini << endl; + + sino_stream->seekg(jump_ini, ios::cur); + + Viewgram data = get_empty_viewgram(view_num, segment_num, false); + + for (int ring = get_min_axial_pos_num(segment_num); ring <= get_max_axial_pos_num(segment_num); ring++) { { - { - float scale = float(1); - if (read_data(*sino_stream, data[ring], - on_disk_data_type, - scale, - on_disk_byte_order) == Succeeded::no - || scale != 1) - error("ProjDataGEAdvance: error reading data\n"); - } - sino_stream->seekg(jump_ring, ios::cur); + float scale = float(1); + if (read_data(*sino_stream, data[ring], on_disk_data_type, scale, on_disk_byte_order) == Succeeded::no || scale != 1) + error("ProjDataGEAdvance: error reading data\n"); } - - // scales the Viewgram - data *= view_scaling_factor[view_num]; - - if (make_num_tangential_poss_odd && get_num_tangential_poss()%2==0) - { - const int new_max_tangential_pos = get_max_tangential_pos_num() + 1; - data.grow( - IndexRange2D(get_min_axial_pos_num(segment_num), - get_max_axial_pos_num(segment_num), - get_min_tangential_pos_num(), - new_max_tangential_pos)); - } - - return data; -} + sino_stream->seekg(jump_ring, ios::cur); + } + // scales the Viewgram + data *= view_scaling_factor[view_num]; + + if (make_num_tangential_poss_odd && get_num_tangential_poss() % 2 == 0) { + const int new_max_tangential_pos = get_max_tangential_pos_num() + 1; + data.grow(IndexRange2D(get_min_axial_pos_num(segment_num), get_max_axial_pos_num(segment_num), get_min_tangential_pos_num(), + new_max_tangential_pos)); + } -Succeeded ProjDataGEAdvance::set_viewgram(const Viewgram& v) -{ + return data; +} + +Succeeded +ProjDataGEAdvance::set_viewgram(const Viewgram& v) { // TODO // but this is difficult: how to adjust the scale factors when writing only 1 viewgram ? warning("ProjDataGEAdvance::set_viewgram not implemented yet\n"); return Succeeded::no; } -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 -{ +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"); - return get_empty_sinogram(ax_pos_num, segment_num);} + error("ProjDataGEAdvance::get_sinogram not implemented yet\n"); + return get_empty_sinogram(ax_pos_num, segment_num); +} -Succeeded ProjDataGEAdvance::set_sinogram(const Sinogram& s) -{ +Succeeded +ProjDataGEAdvance::set_sinogram(const Sinogram& s) { // TODO warning("ProjDataGEAdvance::set_sinogram not implemented yet\n"); return Succeeded::no; diff --git a/src/buildblock/ProjDataGEHDF5.cxx b/src/buildblock/ProjDataGEHDF5.cxx index 5c046773a5..95d3d9398f 100644 --- a/src/buildblock/ProjDataGEHDF5.cxx +++ b/src/buildblock/ProjDataGEHDF5.cxx @@ -28,8 +28,6 @@ See STIR/LICENSE.txt for details */ - - #include "stir/ProjDataGEHDF5.h" #include "stir/IndexRange.h" #include "stir/IndexRange3D.h" @@ -49,116 +47,101 @@ START_NAMESPACE_STIR namespace GE { namespace RDF_HDF5 { -ProjDataGEHDF5::ProjDataGEHDF5(const std::string& input_filename) : - ProjData() -{ - this->m_input_hdf5_sptr.reset(new GEHDF5Wrapper(input_filename)); - this->initialise_from_wrapper(); +ProjDataGEHDF5::ProjDataGEHDF5(const std::string& input_filename) : ProjData() { + this->m_input_hdf5_sptr.reset(new GEHDF5Wrapper(input_filename)); + this->initialise_from_wrapper(); } -ProjDataGEHDF5::ProjDataGEHDF5(shared_ptr input_hdf5_sptr) : - ProjData(), - m_input_hdf5_sptr(input_hdf5_sptr) -{ - this->initialise_from_wrapper(); +ProjDataGEHDF5::ProjDataGEHDF5(shared_ptr input_hdf5_sptr) : ProjData(), m_input_hdf5_sptr(input_hdf5_sptr) { + this->initialise_from_wrapper(); } -void ProjDataGEHDF5::initialise_from_wrapper() -{ - this->exam_info_sptr = this->m_input_hdf5_sptr->get_exam_info_sptr(); - this->proj_data_info_sptr = this->m_input_hdf5_sptr->get_proj_data_info_sptr()->create_shared_clone(); - this->initialise_segment_sequence(); - this->initialise_ax_pos_offset(); - this->initialise_viewgram_buffer(); +void +ProjDataGEHDF5::initialise_from_wrapper() { + this->exam_info_sptr = this->m_input_hdf5_sptr->get_exam_info_sptr(); + this->proj_data_info_sptr = this->m_input_hdf5_sptr->get_proj_data_info_sptr()->create_shared_clone(); + this->initialise_segment_sequence(); + this->initialise_ax_pos_offset(); + this->initialise_viewgram_buffer(); } -void ProjDataGEHDF5::initialise_viewgram_buffer() -{ +void +ProjDataGEHDF5::initialise_viewgram_buffer() { if (!this->tof_data.empty()) error("there is already data loaded. Aborting"); tof_data.reserve(get_num_views()); - Array<3,unsigned char> buffer; + Array<3, unsigned char> buffer; - for (int view_num = get_min_view_num(); view_num <= get_max_view_num(); view_num++) - { - // view numbering for initialise_proj_data starts from 1 - m_input_hdf5_sptr->initialise_proj_data(view_num - get_min_view_num() + 1); + for (int view_num = get_min_view_num(); view_num <= get_max_view_num(); view_num++) { + // view numbering for initialise_proj_data starts from 1 + m_input_hdf5_sptr->initialise_proj_data(view_num - get_min_view_num() + 1); - m_input_hdf5_sptr->read_sinogram(buffer); - this->tof_data.push_back(buffer); + m_input_hdf5_sptr->read_sinogram(buffer); + this->tof_data.push_back(buffer); } - } -void ProjDataGEHDF5::initialise_segment_sequence() -{ - segment_sequence.resize(2*get_max_segment_num()+1); - segment_sequence[0] = 0; -// PW Flipped the segments, segment sequence is now as: 0,1,-1 and so on. - for (int segment_num = 1; segment_num<=get_max_segment_num(); ++segment_num) - { - segment_sequence[2*segment_num-1] = segment_num; - segment_sequence[2*segment_num] = -segment_num; - } +void +ProjDataGEHDF5::initialise_segment_sequence() { + segment_sequence.resize(2 * get_max_segment_num() + 1); + segment_sequence[0] = 0; + // PW Flipped the segments, segment sequence is now as: 0,1,-1 and so on. + for (int segment_num = 1; segment_num <= get_max_segment_num(); ++segment_num) { + segment_sequence[2 * segment_num - 1] = segment_num; + segment_sequence[2 * segment_num] = -segment_num; + } } -void ProjDataGEHDF5::initialise_ax_pos_offset() -{ +void +ProjDataGEHDF5::initialise_ax_pos_offset() { seg_ax_offset.resize(get_num_segments()); seg_ax_offset[0] = 0; unsigned int previous_value = 0; - for (int i_seg = 1; i_seg < get_num_segments(); ++i_seg) - { - const int segment_num = segment_sequence[i_seg-1]; + for (int i_seg = 1; i_seg < get_num_segments(); ++i_seg) { + const int segment_num = segment_sequence[i_seg - 1]; - seg_ax_offset[i_seg] = static_cast(get_num_axial_poss(segment_num)) + - previous_value; - previous_value = seg_ax_offset[i_seg]; + seg_ax_offset[i_seg] = static_cast(get_num_axial_poss(segment_num)) + previous_value; + previous_value = seg_ax_offset[i_seg]; } } Viewgram -ProjDataGEHDF5:: -get_viewgram(const int view_num, const int segment_num, - 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"); - Viewgram ret_viewgram = get_empty_viewgram(view_num, segment_num); - // not necessary - // ret_viewgram.fill(0.0); - - // find num TOF bins - BasicCoordinate<3, int> min_index, max_index; - tof_data[0].get_regular_range(min_index,max_index); - const int num_tof_poss = max_index[2] - min_index[2] + 1; - if (num_tof_poss <= 0) - error("ProjDataGEHDF5: internal error on TOF data dimension"); - if (proj_data_info_sptr->get_scanner_ptr()->get_type() == Scanner::PETMR_Signa) - if (num_tof_poss != 27) +ProjDataGEHDF5::get_viewgram(const int view_num, const int segment_num, 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"); + Viewgram ret_viewgram = get_empty_viewgram(view_num, segment_num); + // not necessary + // ret_viewgram.fill(0.0); + + // find num TOF bins + BasicCoordinate<3, int> min_index, max_index; + tof_data[0].get_regular_range(min_index, max_index); + const int num_tof_poss = max_index[2] - min_index[2] + 1; + if (num_tof_poss <= 0) + error("ProjDataGEHDF5: internal error on TOF data dimension"); + if (proj_data_info_sptr->get_scanner_ptr()->get_type() == Scanner::PETMR_Signa) + if (num_tof_poss != 27) error("ProjDataGEHDF5: internal error on TOF data dimension for GE Signa"); - - // PW flip the tangential and view numbers. The TOF bins are added below to return non TOF viewgram. - if (get_min_view_num() != 0) - error("ProjDataGEHDF5: internal error on views"); - if (get_max_tangential_pos_num()+get_min_tangential_pos_num() != 0) - error("ProjDataGEHDF5: internal error on tangential positions"); - for (int tang_pos = ret_viewgram.get_min_tangential_pos_num(), i_tang = 0; - tang_pos <= ret_viewgram.get_max_tangential_pos_num(); - ++tang_pos, ++i_tang) - for(int i_axial=get_min_axial_pos_num(segment_num), axial_pos = seg_ax_offset[find_segment_index_in_sequence(segment_num)]; - i_axial<=get_max_axial_pos_num(segment_num); - i_axial++, axial_pos++) - for (int tof_poss = 0; tof_poss <= num_tof_poss-1; tof_poss++) - { - ret_viewgram[i_axial][-tang_pos] += static_cast (tof_data[get_max_view_num()-view_num][i_tang][tof_poss][axial_pos]); - } + + // PW flip the tangential and view numbers. The TOF bins are added below to return non TOF viewgram. + if (get_min_view_num() != 0) + error("ProjDataGEHDF5: internal error on views"); + if (get_max_tangential_pos_num() + get_min_tangential_pos_num() != 0) + error("ProjDataGEHDF5: internal error on tangential positions"); + for (int tang_pos = ret_viewgram.get_min_tangential_pos_num(), i_tang = 0; + tang_pos <= ret_viewgram.get_max_tangential_pos_num(); ++tang_pos, ++i_tang) + for (int i_axial = get_min_axial_pos_num(segment_num), axial_pos = seg_ax_offset[find_segment_index_in_sequence(segment_num)]; + i_axial <= get_max_axial_pos_num(segment_num); i_axial++, axial_pos++) + for (int tof_poss = 0; tof_poss <= num_tof_poss - 1; tof_poss++) { + ret_viewgram[i_axial][-tang_pos] += + static_cast(tof_data[get_max_view_num() - view_num][i_tang][tof_poss][axial_pos]); + } #if 0 ofstream write_tof_data; @@ -184,52 +167,48 @@ get_viewgram(const int view_num, const int segment_num, write_ret_viewgram_data << std::endl; } #endif - return ret_viewgram; + return ret_viewgram; } - -Succeeded ProjDataGEHDF5::set_viewgram(const Viewgram& v) -{ - // but this is difficult: how to adjust the scale factors when writing only 1 viewgram ? - error("ProjDataFromGEHDF5::set_viewgram not implemented yet"); - return Succeeded::no; +Succeeded +ProjDataGEHDF5::set_viewgram(const Viewgram& v) { + // but this is difficult: how to adjust the scale factors when writing only 1 viewgram ? + error("ProjDataFromGEHDF5::set_viewgram not implemented yet"); + return Succeeded::no; } -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"); - return get_empty_sinogram(ax_pos_num, segment_num); +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"); + return get_empty_sinogram(ax_pos_num, segment_num); } -Succeeded ProjDataGEHDF5::set_sinogram(const Sinogram& s) -{ - error("ProjDataGEHDF5::set_sinogram not implemented yet"); - return Succeeded::no; +Succeeded +ProjDataGEHDF5::set_sinogram(const Sinogram& s) { + error("ProjDataGEHDF5::set_sinogram not implemented yet"); + return Succeeded::no; } unsigned int -ProjDataGEHDF5::find_segment_index_in_sequence(const int segment_num) const -{ +ProjDataGEHDF5::find_segment_index_in_sequence(const int segment_num) const { #ifndef STIR_NO_NAMESPACES - std::vector::const_iterator iter = - std::find(segment_sequence.begin(), segment_sequence.end(), segment_num); + std::vector::const_iterator iter = std::find(segment_sequence.begin(), segment_sequence.end(), segment_num); #else - vector::const_iterator iter = - find(segment_sequence.begin(), segment_sequence.end(), segment_num); + vector::const_iterator iter = find(segment_sequence.begin(), segment_sequence.end(), segment_num); #endif // TODO do some proper error handling here - assert(iter != segment_sequence.end()); + assert(iter != segment_sequence.end()); return static_cast(iter - segment_sequence.begin()); } std::vector -ProjDataGEHDF5::get_segment_sequence_in_hdf5() const -{ return segment_sequence; } - - -} // namespace +ProjDataGEHDF5::get_segment_sequence_in_hdf5() const { + return segment_sequence; } + +} // namespace RDF_HDF5 +} // namespace GE END_NAMESPACE_STIR diff --git a/src/buildblock/ProjDataInMemory.cxx b/src/buildblock/ProjDataInMemory.cxx index 417653c5cb..757acbb5ed 100644 --- a/src/buildblock/ProjDataInMemory.cxx +++ b/src/buildblock/ProjDataInMemory.cxx @@ -27,7 +27,6 @@ See STIR/LICENSE.txt for details */ - #include "stir/ProjDataInMemory.h" #include "stir/shared_ptr.h" #include "stir/Succeeded.h" @@ -38,71 +37,63 @@ #include #ifdef STIR_USE_OLD_STRSTREAM -#include +# include #else -#include +# include #endif #ifndef STIR_NO_NAMESPACES using std::fstream; using std::iostream; using std::ios; -#ifdef STIR_USE_OLD_STRSTREAM +# ifdef STIR_USE_OLD_STRSTREAM using std::strstream; -#endif +# endif using std::string; #endif START_NAMESPACE_STIR -ProjDataInMemory:: -~ProjDataInMemory() -{ +ProjDataInMemory::~ProjDataInMemory() { // release such that it can deallocate safely this->buffer.release_data_ptr(); } -ProjDataInMemory:: -ProjDataInMemory(shared_ptr const& exam_info_sptr, - shared_ptr const& proj_data_info_ptr, const bool initialise_with_0) - : - ProjDataFromStream(exam_info_sptr, proj_data_info_ptr, shared_ptr(), // trick: first initialise sino_stream_ptr to 0 - std::streamoff(0), - ProjData::standard_segment_sequence(*proj_data_info_ptr), - Segment_AxialPos_View_TangPos) -{ +ProjDataInMemory::ProjDataInMemory(shared_ptr const& exam_info_sptr, + shared_ptr const& proj_data_info_ptr, const bool initialise_with_0) + : ProjDataFromStream( + exam_info_sptr, proj_data_info_ptr, shared_ptr(), // trick: first initialise sino_stream_ptr to 0 + std::streamoff(0), ProjData::standard_segment_sequence(*proj_data_info_ptr), Segment_AxialPos_View_TangPos) { this->create_buffer(initialise_with_0); this->create_stream(); } void -ProjDataInMemory:: -create_buffer(const bool initialise_with_0) -{ +ProjDataInMemory::create_buffer(const bool initialise_with_0) { #if 0 float *b = new float[this->get_size_of_buffer_in_bytes()/sizeof(float)]; if (initialise_with_0) memset(b, 0, this->get_size_of_buffer_in_bytes()); return b; #else - this->buffer = Array<1,float>(0, static_cast(this->get_size_of_buffer_in_bytes()/sizeof(float))-1); + this->buffer = Array<1, float>(0, static_cast(this->get_size_of_buffer_in_bytes() / sizeof(float)) - 1); #endif } void -ProjDataInMemory:: -create_stream() -{ +ProjDataInMemory::create_stream() { shared_ptr output_stream_sptr; #ifdef STIR_USE_OLD_STRSTREAM - output_stream_sptr.reset(new strstream(buffer.get_data_ptr(), this->get_size_of_buffer_in_bytes(), ios::in | ios::out | ios::binary)); + output_stream_sptr.reset( + new strstream(buffer.get_data_ptr(), this->get_size_of_buffer_in_bytes(), ios::in | ios::out | ios::binary)); #else // Use boost::bufferstream to avoid reallocate. // For std::stringstream, the only way to do this is by passing a string of the appropriate size // However, if basic_string doesn't do reference counting, we would have // temporarily 2 strings of a (potentially large) size in memory. - output_stream_sptr.reset - (new boost::interprocess::bufferstream(reinterpret_cast(this->buffer.get_data_ptr()), this->get_size_of_buffer_in_bytes(), ios::in | ios::out | ios::binary)); + output_stream_sptr.reset(new boost::interprocess::bufferstream(reinterpret_cast(this->buffer.get_data_ptr()), + this->get_size_of_buffer_in_bytes(), + ios::in | ios::out | ios::binary)); #endif if (!*output_stream_sptr) @@ -111,36 +102,26 @@ create_stream() this->sino_stream = output_stream_sptr; } - void -ProjDataInMemory::fill(const float value) -{ +ProjDataInMemory::fill(const float value) { std::fill(begin_all(), end_all(), value); } void -ProjDataInMemory::fill(const ProjData& proj_data) -{ - auto pdm_ptr = dynamic_cast(&proj_data); - if (!is_null_ptr(pdm_ptr) && - (*this->get_proj_data_info_sptr()) == (*proj_data.get_proj_data_info_sptr())) - { - std::copy(pdm_ptr->begin_all(), pdm_ptr->end_all(), begin_all()); - } - else - { - return ProjData::fill(proj_data); - } -} - -ProjDataInMemory:: -ProjDataInMemory(const ProjData& proj_data) - : ProjDataFromStream(proj_data.get_exam_info_sptr(), - proj_data.get_proj_data_info_sptr()->create_shared_clone(), shared_ptr(), - std::streamoff(0), - ProjData::standard_segment_sequence(*proj_data.get_proj_data_info_sptr()), - Segment_AxialPos_View_TangPos) -{ +ProjDataInMemory::fill(const ProjData& proj_data) { + auto pdm_ptr = dynamic_cast(&proj_data); + if (!is_null_ptr(pdm_ptr) && (*this->get_proj_data_info_sptr()) == (*proj_data.get_proj_data_info_sptr())) { + std::copy(pdm_ptr->begin_all(), pdm_ptr->end_all(), begin_all()); + } else { + return ProjData::fill(proj_data); + } +} + +ProjDataInMemory::ProjDataInMemory(const ProjData& proj_data) + : ProjDataFromStream(proj_data.get_exam_info_sptr(), proj_data.get_proj_data_info_sptr()->create_shared_clone(), + shared_ptr(), std::streamoff(0), + ProjData::standard_segment_sequence(*proj_data.get_proj_data_info_sptr()), + Segment_AxialPos_View_TangPos) { this->create_buffer(); this->create_stream(); @@ -148,78 +129,64 @@ ProjDataInMemory(const ProjData& proj_data) this->fill(proj_data); } -ProjDataInMemory:: -ProjDataInMemory (const ProjDataInMemory& proj_data) - : ProjDataFromStream(proj_data.get_exam_info_sptr(), - proj_data.get_proj_data_info_sptr()->create_shared_clone(), shared_ptr(), - std::streamoff(0), +ProjDataInMemory::ProjDataInMemory(const ProjDataInMemory& proj_data) + : ProjDataFromStream(proj_data.get_exam_info_sptr(), proj_data.get_proj_data_info_sptr()->create_shared_clone(), + shared_ptr(), std::streamoff(0), ProjData::standard_segment_sequence(*proj_data.get_proj_data_info_sptr()), - Segment_AxialPos_View_TangPos) -{ + Segment_AxialPos_View_TangPos) { this->create_buffer(); this->create_stream(); // copy data std::copy(proj_data.begin_all(), proj_data.end_all(), begin_all()); - //this->fill(proj_data); + // this->fill(proj_data); } size_t -ProjDataInMemory:: -get_size_of_buffer_in_bytes() const -{ +ProjDataInMemory::get_size_of_buffer_in_bytes() const { return size_all() * sizeof(float); } -float -ProjDataInMemory::get_bin_value(Bin& bin) -{ -// Viewgram viewgram = get_viewgram(bin.view_num(),bin.segment_num()); -// return viewgram[bin.axial_pos_num()][bin.tangential_pos_num()]; - return ProjDataFromStream::get_bin_value(bin); +float +ProjDataInMemory::get_bin_value(Bin& bin) { + // Viewgram viewgram = get_viewgram(bin.view_num(),bin.segment_num()); + // return viewgram[bin.axial_pos_num()][bin.tangential_pos_num()]; + return ProjDataFromStream::get_bin_value(bin); } -void -ProjDataInMemory::set_bin_value(const Bin& bin) -{ +void +ProjDataInMemory::set_bin_value(const Bin& bin) { // first find offset in the stream std::vector offsets = get_offsets_bin(bin); const std::streamoff total_offset = offsets[0]; // now convert to index in the buffer - const int index = static_cast(total_offset/sizeof(float)); - - buffer[index]=bin.get_bin_value(); + const int index = static_cast(total_offset / sizeof(float)); + + buffer[index] = bin.get_bin_value(); } void -ProjDataInMemory:: -axpby( const float a, const ProjData& x, - const float b, const ProjData& y) -{ - xapyb(x,a,y,b); +ProjDataInMemory::axpby(const float a, const ProjData& x, const float b, const ProjData& y) { + xapyb(x, a, y, b); } void -ProjDataInMemory:: -xapyb(const ProjData& x, const float a, - const ProjData& y, const float b) -{ - // To use this method, we require that all three proj data be ProjDataInMemory - // So cast them. If any null pointers, fall back to default functionality - const ProjDataInMemory *x_pdm = dynamic_cast(&x); - const ProjDataInMemory *y_pdm = dynamic_cast(&y); - // At least one is not ProjDataInMemory, fall back to default - if (is_null_ptr(x_pdm) || is_null_ptr(y_pdm)) { - ProjData::xapyb(x,a,y,b); - return; - } - - // Else, all are ProjDataInMemory - - // First check that info 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("ProjDataInMemory::xapyb: ProjDataInfo don't match"); +ProjDataInMemory::xapyb(const ProjData& x, const float a, const ProjData& y, const float b) { + // To use this method, we require that all three proj data be ProjDataInMemory + // So cast them. If any null pointers, fall back to default functionality + const ProjDataInMemory* x_pdm = dynamic_cast(&x); + const ProjDataInMemory* y_pdm = dynamic_cast(&y); + // At least one is not ProjDataInMemory, fall back to default + if (is_null_ptr(x_pdm) || is_null_ptr(y_pdm)) { + ProjData::xapyb(x, a, y, b); + return; + } + + // Else, all are ProjDataInMemory + + // First check that info 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("ProjDataInMemory::xapyb: ProjDataInfo don't match"); #if 0 // Get number of elements @@ -232,38 +199,31 @@ xapyb(const ProjData& x, const float a, for (unsigned i=0; ibuffer.xapyb(x_pdm->buffer, a, y_pdm->buffer, b); + this->buffer.xapyb(x_pdm->buffer, a, y_pdm->buffer, b); #endif } - + void -ProjDataInMemory:: -xapyb(const ProjData& x, const ProjData& a, - const ProjData& y, const ProjData& b) -{ - // To use this method, we require that all three proj data be ProjDataInMemory - // So cast them. If any null pointers, fall back to default functionality - const ProjDataInMemory *x_pdm = dynamic_cast(&x); - const ProjDataInMemory *y_pdm = dynamic_cast(&y); - const ProjDataInMemory *a_pdm = dynamic_cast(&a); - const ProjDataInMemory *b_pdm = dynamic_cast(&b); - - - // At least one is not ProjDataInMemory, fall back to default - if (is_null_ptr(x_pdm) || is_null_ptr(y_pdm) || - is_null_ptr(a_pdm) || is_null_ptr(b_pdm)) { - ProjData::xapyb(x,a,y,b); - return; - } - - // Else, all are ProjDataInMemory - - // First check that info 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("ProjDataInMemory::xapyb: ProjDataInfo don't match"); +ProjDataInMemory::xapyb(const ProjData& x, const ProjData& a, const ProjData& y, const ProjData& b) { + // To use this method, we require that all three proj data be ProjDataInMemory + // So cast them. If any null pointers, fall back to default functionality + const ProjDataInMemory* x_pdm = dynamic_cast(&x); + const ProjDataInMemory* y_pdm = dynamic_cast(&y); + const ProjDataInMemory* a_pdm = dynamic_cast(&a); + const ProjDataInMemory* b_pdm = dynamic_cast(&b); + + // At least one is not ProjDataInMemory, fall back to default + if (is_null_ptr(x_pdm) || is_null_ptr(y_pdm) || is_null_ptr(a_pdm) || is_null_ptr(b_pdm)) { + ProjData::xapyb(x, a, y, b); + return; + } + + // Else, all are ProjDataInMemory + + // First check that info 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("ProjDataInMemory::xapyb: ProjDataInfo don't match"); #if 0 // Get number of elements @@ -278,22 +238,18 @@ xapyb(const ProjData& x, const ProjData& a, for (unsigned i=0; ibuffer.xapyb(x_pdm->buffer, a_pdm->buffer, y_pdm->buffer, b_pdm->buffer); + this->buffer.xapyb(x_pdm->buffer, a_pdm->buffer, y_pdm->buffer, b_pdm->buffer); #endif } void -ProjDataInMemory:: -sapyb(const float a, const ProjData& y, const float b) -{ - this->xapyb(*this,a,y,b); +ProjDataInMemory::sapyb(const float a, const ProjData& y, const float b) { + this->xapyb(*this, a, y, b); } void -ProjDataInMemory:: -sapyb(const ProjData& a, const ProjData& y,const ProjData& b) -{ - this->xapyb(*this,a,y,b); +ProjDataInMemory::sapyb(const ProjData& a, const ProjData& y, const ProjData& b) { + this->xapyb(*this, a, y, b); } END_NAMESPACE_STIR diff --git a/src/buildblock/ProjDataInfo.cxx b/src/buildblock/ProjDataInfo.cxx index 658c1678b7..0f497a8904 100644 --- a/src/buildblock/ProjDataInfo.cxx +++ b/src/buildblock/ProjDataInfo.cxx @@ -55,9 +55,9 @@ #include #include #ifdef BOOST_NO_STRINGSTREAM -#include +# include #else -#include +# include #endif #include "stir/info.h" #include "boost/foreach.hpp" @@ -75,215 +75,180 @@ 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); +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 -{ +ProjDataInfo::get_tof_delta_time(const Bin& bin) const { return mm_to_tof_delta_time(get_k(bin)); // get_k gives "left" edge N.E: corrected returns the center. } 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; +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 -{ - return - (get_t(Bin(bin.segment_num(), bin.view_num(), bin.axial_pos_num()+1,bin.tangential_pos_num())) - - get_t(Bin(bin.segment_num(), bin.view_num(), bin.axial_pos_num()-1,bin.tangential_pos_num())) - )/2; +float +ProjDataInfo::get_sampling_in_t(const Bin& bin) const { + return (get_t(Bin(bin.segment_num(), bin.view_num(), bin.axial_pos_num() + 1, bin.tangential_pos_num())) - + get_t(Bin(bin.segment_num(), bin.view_num(), bin.axial_pos_num() - 1, bin.tangential_pos_num()))) / + 2; } -float -ProjDataInfo::get_sampling_in_m(const Bin& bin) const -{ - return - (get_m(Bin(bin.segment_num(), bin.view_num(), bin.axial_pos_num()+1,bin.tangential_pos_num())) - - get_m(Bin(bin.segment_num(), bin.view_num(), bin.axial_pos_num()-1,bin.tangential_pos_num())) - )/2; +float +ProjDataInfo::get_sampling_in_m(const Bin& bin) const { + return (get_m(Bin(bin.segment_num(), bin.view_num(), bin.axial_pos_num() + 1, bin.tangential_pos_num())) - + get_m(Bin(bin.segment_num(), bin.view_num(), bin.axial_pos_num() - 1, bin.tangential_pos_num()))) / + 2; } - -float -ProjDataInfo::get_sampling_in_s(const Bin& bin) const -{ - return - (get_s(Bin(bin.segment_num(), bin.view_num(), bin.axial_pos_num(),bin.tangential_pos_num()+1)) - - get_s(Bin(bin.segment_num(), bin.view_num(), bin.axial_pos_num(),bin.tangential_pos_num()-1)) - )/2; +float +ProjDataInfo::get_sampling_in_s(const Bin& bin) const { + return (get_s(Bin(bin.segment_num(), bin.view_num(), bin.axial_pos_num(), bin.tangential_pos_num() + 1)) - + get_s(Bin(bin.segment_num(), bin.view_num(), bin.axial_pos_num(), bin.tangential_pos_num() - 1))) / + 2; } -void -ProjDataInfo::set_num_views(const int num_views) -{ +void +ProjDataInfo::set_num_views(const int num_views) { min_view_num = 0; - max_view_num = num_views-1; + max_view_num = num_views - 1; } -void -ProjDataInfo::set_num_tangential_poss(const int num_tang_poss) -{ +void +ProjDataInfo::set_num_tangential_poss(const int num_tang_poss) { - min_tangential_pos_num = -(num_tang_poss/2); - max_tangential_pos_num = min_tangential_pos_num + num_tang_poss-1; + min_tangential_pos_num = -(num_tang_poss / 2); + max_tangential_pos_num = min_tangential_pos_num + num_tang_poss - 1; } /*! Sets min_axial_pos_per_seg to 0 */ -void -ProjDataInfo::set_num_axial_poss_per_segment(const VectorWithOffset& num_axial_poss_per_segment) -{ - // first do assignments to make the members the correct size +void +ProjDataInfo::set_num_axial_poss_per_segment(const VectorWithOffset& num_axial_poss_per_segment) { + // first do assignments to make the members the correct size // (data will be overwritten) - min_axial_pos_per_seg= num_axial_poss_per_segment; - max_axial_pos_per_seg= num_axial_poss_per_segment; - - for (int i=num_axial_poss_per_segment.get_min_index(); - i<=num_axial_poss_per_segment.get_max_index(); - i++) - { - min_axial_pos_per_seg[i]=0; - max_axial_pos_per_seg[i]=num_axial_poss_per_segment[i]-1; + min_axial_pos_per_seg = num_axial_poss_per_segment; + max_axial_pos_per_seg = num_axial_poss_per_segment; + + for (int i = num_axial_poss_per_segment.get_min_index(); i <= num_axial_poss_per_segment.get_max_index(); i++) { + min_axial_pos_per_seg[i] = 0; + max_axial_pos_per_seg[i] = num_axial_poss_per_segment[i] - 1; } - } /*! No checks are done on validity of the min_ax_pos_num argument */ -void -ProjDataInfo::set_min_axial_pos_num(const int min_ax_pos_num, const int segment_num) -{ +void +ProjDataInfo::set_min_axial_pos_num(const int min_ax_pos_num, const int segment_num) { min_axial_pos_per_seg[segment_num] = min_ax_pos_num; } /*! No checks are done on validity of the max_ax_pos_num argument */ -void -ProjDataInfo::set_max_axial_pos_num(const int max_ax_pos_num, const int segment_num) -{ +void +ProjDataInfo::set_max_axial_pos_num(const int max_ax_pos_num, const int segment_num) { max_axial_pos_per_seg[segment_num] = max_ax_pos_num; } - void -ProjDataInfo::set_min_tangential_pos_num(const int min_tang_poss) -{ +ProjDataInfo::set_min_tangential_pos_num(const int min_tang_poss) { min_tangential_pos_num = min_tang_poss; } void -ProjDataInfo::set_max_tangential_pos_num(const int max_tang_poss) -{ +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 new_num) -{ - if (scanner_ptr->is_tof_ready() && new_num > 0 ) - { - if(tof_mash_factor < 0 || tof_mash_factor > scanner_ptr->get_max_num_timing_poss()) - error("ProjDataInfo: TOF mashing factor must be positive and smaller or equal than" - "the scanner's number of max timing bins. Abort."); - tof_mash_factor = new_num; - - 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; - - 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)); - - } +ProjDataInfo::set_tof_mash_factor(const int new_num) { + if (scanner_ptr->is_tof_ready() && new_num > 0) { + if (tof_mash_factor < 0 || tof_mash_factor > scanner_ptr->get_max_num_timing_poss()) + error("ProjDataInfo: TOF mashing factor must be positive and smaller or equal than" + "the scanner's number of max timing bins. Abort."); + tof_mash_factor = new_num; + + 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; + + 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)); + } - // 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()); + // 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()); - 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; + 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; - min_unmashed_tof_pos_num = - (scanner_ptr->get_max_num_timing_poss())/2; - max_unmashed_tof_pos_num = min_tof_pos_num + (scanner_ptr->get_max_num_timing_poss()) -1; + min_unmashed_tof_pos_num = -(scanner_ptr->get_max_num_timing_poss()) / 2; + max_unmashed_tof_pos_num = min_tof_pos_num + (scanner_ptr->get_max_num_timing_poss()) - 1; - num_tof_bins = max_tof_pos_num - min_tof_pos_num +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."); + // 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); + // 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); + 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; + 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; + 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 + 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 + } } +ProjDataInfo::ProjDataInfo() : bed_position_horizontal(0.F), bed_position_vertical(0.F) {} -ProjDataInfo::ProjDataInfo() - : bed_position_horizontal(0.F), bed_position_vertical(0.F) -{} - - - - -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) - : scanner_ptr(scanner_ptr_v), bed_position_horizontal(0.F), bed_position_vertical(0.F) -{ +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) + : scanner_ptr(scanner_ptr_v), bed_position_horizontal(0.F), bed_position_vertical(0.F) { 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); @@ -296,12 +261,9 @@ ProjDataInfo::ProjDataInfo(const shared_ptr& scanner_ptr_v, } // 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) +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); @@ -311,8 +273,7 @@ ProjDataInfo::ProjDataInfo(const shared_ptr& scanner_ptr_v, } string -ProjDataInfo::parameter_info() const -{ +ProjDataInfo::parameter_info() const { #ifdef BOOST_NO_STRINGSTREAM // dangerous for out-of-range, but 'old-style' ostrstream seems to need this @@ -323,286 +284,219 @@ ProjDataInfo::parameter_info() const #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 << "start vertical bed position (mm) := " << get_bed_position_vertical() << endl; + s << "start horizontal bed position (mm) := " << get_bed_position_horizontal() << endl; s << "\nNumber of TOF positions in data: " << get_num_tof_poss() << '\n'; - s << "\nSegment_num range: (" - << get_min_segment_num() - << ", " << get_max_segment_num() << ")\n"; + s << "\nSegment_num range: (" << get_min_segment_num() << ", " << get_max_segment_num() << ")\n"; s << "Number of Views: " << get_num_views() << endl; s << "Number of axial positions per seg: {"; - for (int seg_num=get_min_segment_num(); seg_num <= get_max_segment_num(); ++seg_num) + for (int seg_num = get_min_segment_num(); seg_num <= get_max_segment_num(); ++seg_num) s << get_num_axial_poss(seg_num) << " "; s << "}\n"; s << "Number of tangential positions: " << get_num_tangential_poss() << endl; return s.str(); - } - void -ProjDataInfo:: -reduce_segment_range(const int min_segment_num, const int max_segment_num) -{ +ProjDataInfo::reduce_segment_range(const int min_segment_num, const int max_segment_num) { assert(min_segment_num >= get_min_segment_num()); assert(max_segment_num <= get_max_segment_num()); - VectorWithOffset new_min_axial_pos_per_seg(min_segment_num, max_segment_num); + VectorWithOffset new_min_axial_pos_per_seg(min_segment_num, max_segment_num); VectorWithOffset new_max_axial_pos_per_seg(min_segment_num, max_segment_num); - for (int segment_num = min_segment_num; segment_num<= max_segment_num; ++segment_num) - { - new_min_axial_pos_per_seg[segment_num] = - min_axial_pos_per_seg[segment_num]; - new_max_axial_pos_per_seg[segment_num] = - max_axial_pos_per_seg[segment_num]; + for (int segment_num = min_segment_num; segment_num <= max_segment_num; ++segment_num) { + new_min_axial_pos_per_seg[segment_num] = min_axial_pos_per_seg[segment_num]; + new_max_axial_pos_per_seg[segment_num] = max_axial_pos_per_seg[segment_num]; } min_axial_pos_per_seg = new_min_axial_pos_per_seg; max_axial_pos_per_seg = new_max_axial_pos_per_seg; - } - -Viewgram -ProjDataInfo::get_empty_viewgram(const int view_num, - const int segment_num, - const bool make_num_tangential_poss_odd, - const int timing_pos_num) const -{ +Viewgram +ProjDataInfo::get_empty_viewgram(const int view_num, const int segment_num, 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()); - - if (make_num_tangential_poss_odd && (get_num_tangential_poss()%2==0)) + shared_ptr proj_data_info_sptr(this->clone()); + + 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, timing_pos_num); - + return v; } - Sinogram -ProjDataInfo::get_empty_sinogram(const int axial_pos_num, const int segment_num, - const bool make_num_tangential_poss_odd, - const int timing_pos_num) const -{ +ProjDataInfo::get_empty_sinogram(const int axial_pos_num, const int segment_num, 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()); - - if (make_num_tangential_poss_odd && (get_num_tangential_poss()%2==0)) + shared_ptr proj_data_info_sptr(this->clone()); + + 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, timing_pos_num); - + return s; } SegmentBySinogram -ProjDataInfo::get_empty_segment_by_sinogram(const int segment_num, - const bool make_num_tangential_poss_odd, - const int timing_pos_num) const -{ +ProjDataInfo::get_empty_segment_by_sinogram(const int segment_num, 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()); - + // we can't access the shared ptr, so we have to clone 'this'. - shared_ptr proj_data_info_sptr(this->clone()); - - if (make_num_tangential_poss_odd && (get_num_tangential_poss()%2==0)) + shared_ptr proj_data_info_sptr(this->clone()); + + 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, timing_pos_num); return s; -} - +} SegmentByView -ProjDataInfo::get_empty_segment_by_view(const int segment_num, - const bool make_num_tangential_poss_odd, - const int timing_pos_num) const -{ +ProjDataInfo::get_empty_segment_by_view(const int segment_num, 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()); - // we can't access the shared ptr, so we have to clone 'this'. - shared_ptr proj_data_info_sptr(this->clone()); - - if (make_num_tangential_poss_odd && (get_num_tangential_poss()%2==0)) + // we can't access the shared ptr, so we have to clone 'this'. + shared_ptr proj_data_info_sptr(this->clone()); + + 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, timing_pos_num); return s; } -RelatedViewgrams +RelatedViewgrams ProjDataInfo::get_empty_related_viewgrams(const ViewSegmentNumbers& view_segment_num, - //const int view_num, const int segment_num, - const shared_ptr& symmetries_used, - const bool make_num_tangential_poss_odd, - const int timing_pos_num) const -{ + // const int view_num, const int segment_num, + const shared_ptr& symmetries_used, + const bool make_num_tangential_poss_odd, const int timing_pos_num) const { vector pairs; symmetries_used->get_related_view_segment_numbers( - pairs, - ViewSegmentNumbers(view_segment_num.view_num(),view_segment_num.segment_num()) - ); + pairs, ViewSegmentNumbers(view_segment_num.view_num(), view_segment_num.segment_num())); - vector > viewgrams; + vector> viewgrams; viewgrams.reserve(pairs.size()); - for (unsigned int i=0; i(viewgrams, symmetries_used); } - /****************** static members **********************/ ProjDataInfo* -ProjDataInfo::ProjDataInfoCTI(const shared_ptr& scanner, - 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) -{ +ProjDataInfo::ProjDataInfoCTI(const shared_ptr& scanner, 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) - error(boost::format("construct_proj_data_info: max_ring_difference %d is too large, number of rings is %d") - % max_delta % num_ring); - if (span < 1) + error(boost::format("construct_proj_data_info: max_ring_difference %d is too large, number of rings is %d") % max_delta % + num_ring); + if (span < 1) error(boost::format("construct_proj_data_info: span %d has to be larger than 0") % span); if (span > 2 * num_ring - 1) - error(boost::format("construct_proj_data_info: span %d is too large for a scanner with %d rings") - % span % num_ring); - if (max_delta<(span-1)/2) - error(boost::format("construct_proj_data_info: max_ring_difference %d has to be at least (span-1)/2, span is %d") - % max_delta % span); - - // Construct first a temporary list of min and max ring diff per segment (0,1,2,3...) - vector RDmintmp(num_ring); - vector RDmaxtmp(num_ring); - - if (span%2 == 1) - { - RDmintmp[0] = -((span-1)/2); - RDmaxtmp[0] = RDmintmp[0] + span - 1; - } - else - { - RDmintmp[0] = -(span/2); - RDmaxtmp[0] = RDmintmp[0] + span; - } + error(boost::format("construct_proj_data_info: span %d is too large for a scanner with %d rings") % span % num_ring); + if (max_delta < (span - 1) / 2) + error(boost::format("construct_proj_data_info: max_ring_difference %d has to be at least (span-1)/2, span is %d") % + max_delta % span); + + // Construct first a temporary list of min and max ring diff per segment (0,1,2,3...) + vector RDmintmp(num_ring); + vector RDmaxtmp(num_ring); + + if (span % 2 == 1) { + RDmintmp[0] = -((span - 1) / 2); + RDmaxtmp[0] = RDmintmp[0] + span - 1; + } else { + RDmintmp[0] = -(span / 2); + RDmaxtmp[0] = RDmintmp[0] + span; + } - int seg_num =0; - while (RDmaxtmp[seg_num] < max_delta) - { + int seg_num = 0; + while (RDmaxtmp[seg_num] < max_delta) { seg_num++; - RDmintmp[seg_num] = RDmaxtmp[seg_num-1] + 1; + RDmintmp[seg_num] = RDmaxtmp[seg_num - 1] + 1; RDmaxtmp[seg_num] = RDmintmp[seg_num] + span - 1; } // check if we went one too far - if (RDmaxtmp[seg_num] > max_delta) - { - if (max_delta < num_ring-1) - warning(boost::format("Creation of ProjDataInfo with span=%1% and max_delta=%2% leads to a 'smaller' last segment than the others (did you mean to set max_delta=%3%?).\n" - "This is fine, but note that in previous versions of STIR this last segment was dropped.") - % span % max_delta % RDmaxtmp[seg_num]); + if (RDmaxtmp[seg_num] > max_delta) { + if (max_delta < num_ring - 1) + warning(boost::format("Creation of ProjDataInfo with span=%1% and max_delta=%2% leads to a 'smaller' last segment than the " + "others (did you mean to set max_delta=%3%?).\n" + "This is fine, but note that in previous versions of STIR this last segment was dropped.") % + span % max_delta % RDmaxtmp[seg_num]); // Palak Wadwha changed this to max_delta to accomodate GE scanners, it is more general anyway. RDmaxtmp[seg_num] = max_delta; } const int max_seg_num = seg_num; - - VectorWithOffset num_axial_pos_per_segment(-max_seg_num,max_seg_num); - VectorWithOffset min_ring_difference(-max_seg_num,max_seg_num); - VectorWithOffset max_ring_difference(-max_seg_num,max_seg_num); - + + VectorWithOffset num_axial_pos_per_segment(-max_seg_num, max_seg_num); + VectorWithOffset min_ring_difference(-max_seg_num, max_seg_num); + VectorWithOffset max_ring_difference(-max_seg_num, max_seg_num); + min_ring_difference[0] = RDmintmp[0]; - max_ring_difference[0]= RDmaxtmp[0]; - - for(int i=1; i<=max_seg_num; i++) - { + max_ring_difference[0] = RDmaxtmp[0]; + + for (int i = 1; i <= max_seg_num; i++) { // KT 28/06/2001 make sure max_ring_diff>min_ring_diff for negative segments - max_ring_difference[-i]= -RDmintmp[i]; - max_ring_difference[i]= RDmaxtmp[i]; - min_ring_difference[-i]= -RDmaxtmp[i]; - min_ring_difference[i]= RDmintmp[i]; + max_ring_difference[-i] = -RDmintmp[i]; + max_ring_difference[i] = RDmaxtmp[i]; + min_ring_difference[-i] = -RDmaxtmp[i]; + min_ring_difference[i] = RDmintmp[i]; } - - if (span==1) - { + + if (span == 1) { num_axial_pos_per_segment[0] = num_ring; - for(int i=1; i<=max_seg_num; i++) - { - num_axial_pos_per_segment[i]= - num_axial_pos_per_segment[-i] = num_ring -i; + for (int i = 1; i <= max_seg_num; i++) { + num_axial_pos_per_segment[i] = num_axial_pos_per_segment[-i] = num_ring - i; + } + } else { + num_axial_pos_per_segment[0] = 2 * num_ring - 1; + for (int i = 1; i <= max_seg_num; i++) { + num_axial_pos_per_segment[i] = num_axial_pos_per_segment[-i] = (2 * num_ring - 1 - 2 * RDmintmp[i]); } } - else - { - num_axial_pos_per_segment[0] = 2*num_ring -1 ; - for( int i=1; i<=max_seg_num; i++) - { - num_axial_pos_per_segment[i] = - num_axial_pos_per_segment[-i] = (2*num_ring -1 - 2*RDmintmp[i]); - } - } - + const float bin_size = scanner->get_default_bin_size(); - - + if (arc_corrected) - return - new ProjDataInfoCylindricalArcCorr(scanner,bin_size, - num_axial_pos_per_segment, - min_ring_difference, - max_ring_difference, - num_views,num_tangential_poss, - tof_mash_factor); + return new ProjDataInfoCylindricalArcCorr(scanner, bin_size, num_axial_pos_per_segment, min_ring_difference, + max_ring_difference, 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, - tof_mash_factor); - + return new ProjDataInfoCylindricalNoArcCorr(scanner, 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 int tof_mash_factor) -{ - unique_ptr pdi(ProjDataInfoCTI(scanner_sptr, span, max_delta, num_views, num_tangential_poss, arc_corrected, tof_mash_factor)); +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 int tof_mash_factor) { + 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 tof_mash_factor) - - +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 tof_mash_factor) + { /* mixed span case: segment 0 has ring diff -1,0,1, @@ -610,147 +504,101 @@ ProjDataInfo::ProjDataInfoGE(const shared_ptr& scanner, */ const int num_rings = scanner->get_num_rings(); const float bin_size = scanner->get_default_bin_size(); - - if(max_delta<1) + + if (max_delta < 1) error("ProjDataInfo::ProjDataInfoGE: can only handle max_delta>=1\n"); - const int max_segment_num = - max_delta==0? 0 : max_delta-1; - - VectorWithOffset num_axial_pos_per_segment(-max_segment_num,max_segment_num); - - VectorWithOffset min_ring_difference(-max_segment_num,max_segment_num); - VectorWithOffset max_ring_difference(-max_segment_num,max_segment_num); - - num_axial_pos_per_segment[0] = 2*num_rings-1; - min_ring_difference[0] = -1; + const int max_segment_num = max_delta == 0 ? 0 : max_delta - 1; + + VectorWithOffset num_axial_pos_per_segment(-max_segment_num, max_segment_num); + + VectorWithOffset min_ring_difference(-max_segment_num, max_segment_num); + VectorWithOffset max_ring_difference(-max_segment_num, max_segment_num); + + num_axial_pos_per_segment[0] = 2 * num_rings - 1; + min_ring_difference[0] = -1; max_ring_difference[0] = 1; - - for (int i=1; i<=max_segment_num; i++) - - { - num_axial_pos_per_segment[i]= - num_axial_pos_per_segment[-i]=num_rings-i-1; - - max_ring_difference[i]= - min_ring_difference[i]= i+1; - max_ring_difference[-i]= - min_ring_difference[-i]= -i-1; - + + for (int i = 1; i <= max_segment_num; i++) + + { + num_axial_pos_per_segment[i] = num_axial_pos_per_segment[-i] = num_rings - i - 1; + + max_ring_difference[i] = min_ring_difference[i] = i + 1; + max_ring_difference[-i] = min_ring_difference[-i] = -i - 1; } - - - + if (arc_corrected) - return - new ProjDataInfoCylindricalArcCorr(scanner,bin_size, - num_axial_pos_per_segment, - min_ring_difference, - max_ring_difference, - num_views,num_tangential_poss, - tof_mash_factor); + return new ProjDataInfoCylindricalArcCorr(scanner, bin_size, num_axial_pos_per_segment, min_ring_difference, + max_ring_difference, 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, - tof_mash_factor); + return new ProjDataInfoCylindricalNoArcCorr(scanner, num_axial_pos_per_segment, min_ring_difference, max_ring_difference, + num_views, num_tangential_poss, tof_mash_factor); } - -ProjDataInfo* ProjDataInfo::ask_parameters() -{ +ProjDataInfo* +ProjDataInfo::ask_parameters() { shared_ptr 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; - - const bool arc_corrected = - ask("Is the data arc-corrected?",true); - - 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; - span = - ask_num("Span value (use 0 for mixed-span case of the Advance) : ", 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); - - - 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); - - cout << pdi_ptr->parameter_info() <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; + + const bool arc_corrected = ask("Is the data arc-corrected?", true); + + 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; + span = ask_num("Span value (use 0 for mixed-span case of the Advance) : ", 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); + + 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); + + cout << pdi_ptr->parameter_info() << endl; return pdi_ptr; - } /*! Default implementation checks common variables. Needs to be overloaded. */ bool -ProjDataInfo:: -blindly_equals(const root_type * const that) const -{ - const root_type & proj = *that; - - return - (get_min_segment_num()==proj.get_min_segment_num()) && - (get_max_segment_num()==proj.get_max_segment_num()) && - (get_min_view_num()==proj.get_min_view_num()) && - (get_max_view_num()==proj.get_max_view_num()) && - (get_min_tangential_pos_num() ==proj.get_min_tangential_pos_num())&& - (get_max_tangential_pos_num() ==proj.get_max_tangential_pos_num())&& - (proj.is_tof_data() && is_tof_data() ? (get_min_tof_pos_num() == proj.get_min_tof_pos_num()) && - (get_max_tof_pos_num() == proj.get_max_tof_pos_num()) : true) && - equal(min_axial_pos_per_seg.begin(), min_axial_pos_per_seg.end(), proj.min_axial_pos_per_seg.begin())&& - equal(max_axial_pos_per_seg.begin(), max_axial_pos_per_seg.end(), proj.max_axial_pos_per_seg.begin())&& - (*get_scanner_ptr()== *(proj.get_scanner_ptr()))&& - (get_bed_position_horizontal()==proj.get_bed_position_horizontal())&& - (get_bed_position_vertical()==proj.get_bed_position_vertical()); +ProjDataInfo::blindly_equals(const root_type* const that) const { + const root_type& proj = *that; + + return (get_min_segment_num() == proj.get_min_segment_num()) && (get_max_segment_num() == proj.get_max_segment_num()) && + (get_min_view_num() == proj.get_min_view_num()) && (get_max_view_num() == proj.get_max_view_num()) && + (get_min_tangential_pos_num() == proj.get_min_tangential_pos_num()) && + (get_max_tangential_pos_num() == proj.get_max_tangential_pos_num()) && + (proj.is_tof_data() && is_tof_data() + ? (get_min_tof_pos_num() == proj.get_min_tof_pos_num()) && (get_max_tof_pos_num() == proj.get_max_tof_pos_num()) + : true) && + equal(min_axial_pos_per_seg.begin(), min_axial_pos_per_seg.end(), proj.min_axial_pos_per_seg.begin()) && + equal(max_axial_pos_per_seg.begin(), max_axial_pos_per_seg.end(), proj.max_axial_pos_per_seg.begin()) && + (*get_scanner_ptr() == *(proj.get_scanner_ptr())) && + (get_bed_position_horizontal() == proj.get_bed_position_horizontal()) && + (get_bed_position_vertical() == proj.get_bed_position_vertical()); } bool -ProjDataInfo:: -operator ==(const root_type& that) const -{ - return - typeid(*this) == typeid(that) && - (this == &that || - this->blindly_equals(&that) - ); +ProjDataInfo::operator==(const root_type& that) const { + return typeid(*this) == typeid(that) && (this == &that || this->blindly_equals(&that)); } bool -ProjDataInfo:: -operator !=(const root_type& that) const -{ +ProjDataInfo::operator!=(const root_type& that) const { return !((*this) == that); } @@ -762,9 +610,7 @@ operator !=(const root_type& that) const \warning Currently view ranges have to be identical. */ bool -ProjDataInfo:: -operator>=(const ProjDataInfo& proj_data_info) const -{ +ProjDataInfo::operator>=(const ProjDataInfo& proj_data_info) const { if (typeid(*this) != typeid(proj_data_info)) return false; @@ -777,41 +623,36 @@ operator>=(const ProjDataInfo& proj_data_info) const proj_data_info.get_min_segment_num() < larger_proj_data_info.get_min_segment_num() || proj_data_info.get_max_tangential_pos_num() > larger_proj_data_info.get_max_tangential_pos_num() || proj_data_info.get_min_tangential_pos_num() < larger_proj_data_info.get_min_tangential_pos_num() || - ((proj_data_info.get_min_tof_pos_num() < larger_proj_data_info.get_min_tof_pos_num() || - proj_data_info.get_max_tof_pos_num() > larger_proj_data_info.get_max_tof_pos_num()) && - (proj_data_info.is_tof_data() && larger_proj_data_info.is_tof_data()))) + ((proj_data_info.get_min_tof_pos_num() < larger_proj_data_info.get_min_tof_pos_num() || + proj_data_info.get_max_tof_pos_num() > larger_proj_data_info.get_max_tof_pos_num()) && + (proj_data_info.is_tof_data() && larger_proj_data_info.is_tof_data()))) return false; - for (int segment_num=proj_data_info.get_min_segment_num(); - segment_num<=proj_data_info.get_max_segment_num(); - ++segment_num) - { - if (proj_data_info.get_max_axial_pos_num(segment_num) > larger_proj_data_info.get_max_axial_pos_num(segment_num) || - proj_data_info.get_min_axial_pos_num(segment_num) < larger_proj_data_info.get_min_axial_pos_num(segment_num)) - return false; - } + for (int segment_num = proj_data_info.get_min_segment_num(); segment_num <= proj_data_info.get_max_segment_num(); + ++segment_num) { + if (proj_data_info.get_max_axial_pos_num(segment_num) > larger_proj_data_info.get_max_axial_pos_num(segment_num) || + proj_data_info.get_min_axial_pos_num(segment_num) < larger_proj_data_info.get_min_axial_pos_num(segment_num)) + return false; + } // now check all the rest. That's a bit hard, so what we'll do is reduce the sizes of the larger one // to the ones from proj_data_info (which we can safely do as we've checked that they're smaller) // and then check for equality. // This will check stuff like scanners etc etc... shared_ptr smaller_proj_data_info_sptr(larger_proj_data_info.clone()); - smaller_proj_data_info_sptr->reduce_segment_range(proj_data_info.get_min_segment_num(), proj_data_info.get_max_segment_num()); + smaller_proj_data_info_sptr->reduce_segment_range(proj_data_info.get_min_segment_num(), proj_data_info.get_max_segment_num()); smaller_proj_data_info_sptr->set_min_tangential_pos_num(proj_data_info.get_min_tangential_pos_num()); smaller_proj_data_info_sptr->set_max_tangential_pos_num(proj_data_info.get_max_tangential_pos_num()); // smaller_proj_data_info_sptr->set_min_tof_pos_num(proj_data_info.get_min_tof_pos_num()); // smaller_proj_data_info_sptr->set_max_tof_pos_num(proj_data_info.get_max_tof_pos_num()); - for (int segment_num=proj_data_info.get_min_segment_num(); - segment_num<=proj_data_info.get_max_segment_num(); - ++segment_num) - { - smaller_proj_data_info_sptr->set_min_axial_pos_num(proj_data_info.get_min_axial_pos_num(segment_num), segment_num); - smaller_proj_data_info_sptr->set_max_axial_pos_num(proj_data_info.get_max_axial_pos_num(segment_num), segment_num); - } + for (int segment_num = proj_data_info.get_min_segment_num(); segment_num <= proj_data_info.get_max_segment_num(); + ++segment_num) { + smaller_proj_data_info_sptr->set_min_axial_pos_num(proj_data_info.get_min_axial_pos_num(segment_num), segment_num); + smaller_proj_data_info_sptr->set_max_axial_pos_num(proj_data_info.get_max_axial_pos_num(segment_num), segment_num); + } return (proj_data_info == *smaller_proj_data_info_sptr); } END_NAMESPACE_STIR - diff --git a/src/buildblock/ProjDataInfoCylindrical.cxx b/src/buildblock/ProjDataInfoCylindrical.cxx index 9b103ae1cc..0d3ecf5e9e 100644 --- a/src/buildblock/ProjDataInfoCylindrical.cxx +++ b/src/buildblock/ProjDataInfoCylindrical.cxx @@ -37,9 +37,9 @@ #include "stir/Array.h" #include #ifdef BOOST_NO_STRINGSTREAM -#include +# include #else -#include +# include #endif #include "stir/round.h" @@ -60,92 +60,73 @@ using std::vector; START_NAMESPACE_STIR -ProjDataInfoCylindrical:: -ProjDataInfoCylindrical() -{} - - -ProjDataInfoCylindrical:: -ProjDataInfoCylindrical(const shared_ptr& scanner_ptr, - const VectorWithOffset& num_axial_pos_per_segment, - const VectorWithOffset& min_ring_diff_v, - const VectorWithOffset& max_ring_diff_v, - const int num_views,const int num_tangential_poss) - :ProjDataInfo(scanner_ptr,num_axial_pos_per_segment, - num_views,num_tangential_poss), - min_ring_diff(min_ring_diff_v), - max_ring_diff(max_ring_diff_v) -{ - - azimuthal_angle_sampling = static_cast(_PI/num_views); - ring_radius.resize(0,0); +ProjDataInfoCylindrical::ProjDataInfoCylindrical() {} + +ProjDataInfoCylindrical::ProjDataInfoCylindrical(const shared_ptr& scanner_ptr, + const VectorWithOffset& num_axial_pos_per_segment, + const VectorWithOffset& min_ring_diff_v, + const VectorWithOffset& max_ring_diff_v, const int num_views, + const int num_tangential_poss) + : ProjDataInfo(scanner_ptr, num_axial_pos_per_segment, num_views, num_tangential_poss), min_ring_diff(min_ring_diff_v), + max_ring_diff(max_ring_diff_v) { + + azimuthal_angle_sampling = static_cast(_PI / num_views); + ring_radius.resize(0, 0); ring_radius[0] = get_scanner_ptr()->get_effective_ring_radius(); - ring_spacing= get_scanner_ptr()->get_ring_spacing() ; + ring_spacing = get_scanner_ptr()->get_ring_spacing(); // TODO this info should probably be provided via the constructor, or at // least by Scanner. - sampling_corresponds_to_physical_rings = - scanner_ptr->get_type() != Scanner::HiDAC; - + sampling_corresponds_to_physical_rings = scanner_ptr->get_type() != Scanner::HiDAC; assert(min_ring_diff.get_length() == max_ring_diff.get_length()); assert(min_ring_diff.get_length() == num_axial_pos_per_segment.get_length()); // check min,max ring diff { - for (int segment_num=get_min_segment_num(); segment_num<=get_max_segment_num(); ++segment_num) - if (min_ring_diff[segment_num]> max_ring_diff[segment_num]) - { + for (int segment_num = get_min_segment_num(); segment_num <= get_max_segment_num(); ++segment_num) + if (min_ring_diff[segment_num] > max_ring_diff[segment_num]) { warning("ProjDataInfoCylindrical: min_ring_difference %d is larger than max_ring_difference %d for segment %d. " - "Swapping them around", - min_ring_diff[segment_num], max_ring_diff[segment_num], segment_num); + "Swapping them around", + min_ring_diff[segment_num], max_ring_diff[segment_num], segment_num); swap(min_ring_diff[segment_num], max_ring_diff[segment_num]); } } - initialise_ring_diff_arrays(); + initialise_ring_diff_arrays(); } void -ProjDataInfoCylindrical:: -initialise_ring_diff_arrays() const -{ +ProjDataInfoCylindrical::initialise_ring_diff_arrays() const { // check min,max ring diff { // check is necessary here again because of set_min_ring_difference() // we do not swap here because that would require the min/max_ring_diff arrays to be mutable as well - for (int segment_num=get_min_segment_num(); segment_num<=get_max_segment_num(); ++segment_num) - if (min_ring_diff[segment_num]> max_ring_diff[segment_num]) - { + for (int segment_num = get_min_segment_num(); segment_num <= get_max_segment_num(); ++segment_num) + if (min_ring_diff[segment_num] > max_ring_diff[segment_num]) { error("ProjDataInfoCylindrical: min_ring_difference %d is larger than " - "max_ring_difference %d for segment %d.", - min_ring_diff[segment_num], max_ring_diff[segment_num], segment_num); + "max_ring_difference %d for segment %d.", + min_ring_diff[segment_num], max_ring_diff[segment_num], segment_num); } } - // initialise m_offset - { - m_offset = - VectorWithOffset(get_min_segment_num(),get_max_segment_num()); - + // initialise m_offset + { + m_offset = VectorWithOffset(get_min_segment_num(), get_max_segment_num()); + /* m_offsets are found by requiring get_m(..., min_axial_pos_num,...) == - get_m(..., max_axial_pos_num,...) */ - for (int segment_num=get_min_segment_num(); segment_num<=get_max_segment_num(); ++segment_num) - { + for (int segment_num = get_min_segment_num(); segment_num <= get_max_segment_num(); ++segment_num) { m_offset[segment_num] = - ((get_max_axial_pos_num(segment_num) + get_min_axial_pos_num(segment_num)) - *get_axial_sampling(segment_num) - )/2; + ((get_max_axial_pos_num(segment_num) + get_min_axial_pos_num(segment_num)) * get_axial_sampling(segment_num)) / 2; } } - // initialise ax_pos_num_offset - if (sampling_corresponds_to_physical_rings) - { + // initialise ax_pos_num_offset + if (sampling_corresponds_to_physical_rings) { const int num_rings = get_scanner_ptr()->get_num_rings(); - ax_pos_num_offset = - VectorWithOffset(get_min_segment_num(),get_max_segment_num()); - + ax_pos_num_offset = VectorWithOffset(get_min_segment_num(), get_max_segment_num()); + /* ax_pos_num will be determined by looking at ring1+ring2. This also works for axially compressed data (i.e. span) as ring1+ring2 is constant for all ring-pairs combined into 1 @@ -157,90 +138,75 @@ initialise_ring_diff_arrays() const ring1 = get_m(bin)/ring_spacing - ring_diff/2 + (num_rings-1)/2 This follows from the fact that get_m() returns the z position in millimeter of the middle of the LOR w.r.t. the middle of the scanner. - The (num_rings-1)/2 shifts the origin such that the first ring has + The (num_rings-1)/2 shifts the origin such that the first ring has ring_num==0. From the above, it follows that ring1+ring2=2*get_m(bin)/ring_spacing + (num_rings-1) Finally, we use the formula for get_m to obtain ring1+ring2=2*ax_pos_num/get_num_axial_poss_per_ring_inc(segment_num) - -2*m_offset[segment_num]/ring_spacing + (num_rings-1) + -2*m_offset[segment_num]/ring_spacing + (num_rings-1) Solving this for ax_pos_num: ax_pos_num = (ring1+ring2-(num_rings-1) + 2*m_offset[segment_num]/ring_spacing - ) * get_num_axial_poss_per_ring_inc(segment_num)/2 + ) * get_num_axial_poss_per_ring_inc(segment_num)/2 We could plug m_offset in to obtain ax_pos_num = (ring1+ring2-(num_rings-1) - ) * get_num_axial_poss_per_ring_inc(segment_num)/2. + ) * get_num_axial_poss_per_ring_inc(segment_num)/2. + - (get_max_axial_pos_num(segment_num) - + get_min_axial_pos_num(segment_num) )/2. + (get_max_axial_pos_num(segment_num) + + get_min_axial_pos_num(segment_num) )/2. this formula is easy to understand, but we don't use it as at some point somebody might change m_offset - and forget to change this code... + and forget to change this code... (also, the form above would need float division and then rounding) */ - for (int segment_num=get_min_segment_num(); segment_num<=get_max_segment_num(); ++segment_num) - { - ax_pos_num_offset[segment_num] = - round((num_rings-1) - 2*m_offset[segment_num]/ring_spacing); + for (int segment_num = get_min_segment_num(); segment_num <= get_max_segment_num(); ++segment_num) { + ax_pos_num_offset[segment_num] = round((num_rings - 1) - 2 * m_offset[segment_num] / ring_spacing); // check that it was integer - if (fabs(ax_pos_num_offset[segment_num] - - ((num_rings-1) - 2*m_offset[segment_num]/ring_spacing)) > 1E-4) - { - error("ProjDataInfoCylindrical: in segment %d, the axial positions\n" - "do not correspond to the usual locations between physical rings.\n" - "This is suspicious and can make things go wrong in STIR, so I abort.\n" - "Check the number of axial positions in this segment.", - segment_num); - } - - if (get_num_axial_poss_per_ring_inc(segment_num)==1) - { - // check that we'll get an integer ax_pos_num, i.e. - // (ring1+ring2 - ax_pos_num_offset) has to be even, for any - // ring1,ring2 in the segment, i.e ring1-ring2 = ring_diff, so - // ring1+ring2 = 2*ring2 + ring_diff - assert(get_min_ring_difference(segment_num) == - get_max_ring_difference(segment_num)); - if ((get_max_ring_difference(segment_num) - - ax_pos_num_offset[segment_num]) % 2 != 0) - warning("ProjDataInfoCylindrical: the number of axial positions in " - "segment %d is such that current conventions will place " - "the LORs shifted with respect to the physical rings.", - segment_num); + if (fabs(ax_pos_num_offset[segment_num] - ((num_rings - 1) - 2 * m_offset[segment_num] / ring_spacing)) > 1E-4) { + error("ProjDataInfoCylindrical: in segment %d, the axial positions\n" + "do not correspond to the usual locations between physical rings.\n" + "This is suspicious and can make things go wrong in STIR, so I abort.\n" + "Check the number of axial positions in this segment.", + segment_num); + } + + if (get_num_axial_poss_per_ring_inc(segment_num) == 1) { + // check that we'll get an integer ax_pos_num, i.e. + // (ring1+ring2 - ax_pos_num_offset) has to be even, for any + // ring1,ring2 in the segment, i.e ring1-ring2 = ring_diff, so + // ring1+ring2 = 2*ring2 + ring_diff + assert(get_min_ring_difference(segment_num) == get_max_ring_difference(segment_num)); + if ((get_max_ring_difference(segment_num) - ax_pos_num_offset[segment_num]) % 2 != 0) + warning("ProjDataInfoCylindrical: the number of axial positions in " + "segment %d is such that current conventions will place " + "the LORs shifted with respect to the physical rings.", + segment_num); } } } // initialise ring_diff_to_segment_num - if (sampling_corresponds_to_physical_rings) - { - const int min_ring_difference = - *min_element(min_ring_diff.begin(), min_ring_diff.end()); - const int max_ring_difference = - *max_element(max_ring_diff.begin(), max_ring_diff.end()); + if (sampling_corresponds_to_physical_rings) { + const int min_ring_difference = *min_element(min_ring_diff.begin(), min_ring_diff.end()); + const int max_ring_difference = *max_element(max_ring_diff.begin(), max_ring_diff.end()); // set ring_diff_to_segment_num to appropriate size - // in principle, the max ring difference would be scanner.num_rings-1, but - // in case someone is up to strange things, we take the max of this value + // in principle, the max ring difference would be scanner.num_rings-1, but + // in case someone is up to strange things, we take the max of this value // with the max_ring_difference as given in the file - ring_diff_to_segment_num = - VectorWithOffset(min(min_ring_difference, -(get_scanner_ptr()->get_num_rings()-1)), - max(max_ring_difference, get_scanner_ptr()->get_num_rings()-1)); + ring_diff_to_segment_num = VectorWithOffset(min(min_ring_difference, -(get_scanner_ptr()->get_num_rings() - 1)), + max(max_ring_difference, get_scanner_ptr()->get_num_rings() - 1)); // first set all to impossible value // warning: get_segment_num_for_ring_difference relies on the fact that this value // is larger than get_max_segment_num() - ring_diff_to_segment_num.fill(get_max_segment_num()+1); + ring_diff_to_segment_num.fill(get_max_segment_num() + 1); - for(int ring_diff=min_ring_difference; ring_diff <= max_ring_difference; ++ring_diff) - { + for (int ring_diff = min_ring_difference; ring_diff <= max_ring_difference; ++ring_diff) { int segment_num; - for (segment_num=get_min_segment_num(); segment_num<=get_max_segment_num(); ++segment_num) - { - if (ring_diff >= min_ring_diff[segment_num] && - ring_diff <= max_ring_diff[segment_num]) - { + for (segment_num = get_min_segment_num(); segment_num <= get_max_segment_num(); ++segment_num) { + if (ring_diff >= min_ring_diff[segment_num] && ring_diff <= max_ring_diff[segment_num]) { #if 0 std::cerr << "ring diff " << ring_diff << " stored in s:" << segment_num << std::endl; #endif @@ -248,33 +214,25 @@ initialise_ring_diff_arrays() const break; } } - if (segment_num>get_max_segment_num()) - { - warning("ProjDataInfoCylindrical: ring difference %d does not belong to a segment", - ring_diff); + if (segment_num > get_max_segment_num()) { + warning("ProjDataInfoCylindrical: ring difference %d does not belong to a segment", ring_diff); } } } // initialise segment_axial_pos_to_ring1_plus_ring2 - if (sampling_corresponds_to_physical_rings) - { - segment_axial_pos_to_ring1_plus_ring2 = - VectorWithOffset >(get_min_segment_num(), get_max_segment_num()); - for (int s_num=get_min_segment_num(); s_num<=get_max_segment_num(); ++s_num) - { + if (sampling_corresponds_to_physical_rings) { + segment_axial_pos_to_ring1_plus_ring2 = VectorWithOffset>(get_min_segment_num(), get_max_segment_num()); + for (int s_num = get_min_segment_num(); s_num <= get_max_segment_num(); ++s_num) { const int min_ax_pos_num = get_min_axial_pos_num(s_num); const int max_ax_pos_num = get_max_axial_pos_num(s_num); segment_axial_pos_to_ring1_plus_ring2[s_num].grow(min_ax_pos_num, max_ax_pos_num); - for (int ax_pos_num=min_ax_pos_num; ax_pos_num<=max_ax_pos_num; ++ax_pos_num) - { - // see documentation above for formulas - const float ring1_plus_ring2_float = - 2*ax_pos_num/get_num_axial_poss_per_ring_inc(s_num) - -2*m_offset[s_num]/ring_spacing + (get_scanner_ptr()->get_num_rings()-1); - const int ring1_plus_ring2 = - round(ring1_plus_ring2_float); + for (int ax_pos_num = min_ax_pos_num; ax_pos_num <= max_ax_pos_num; ++ax_pos_num) { + // see documentation above for formulas + const float ring1_plus_ring2_float = 2 * ax_pos_num / get_num_axial_poss_per_ring_inc(s_num) - + 2 * m_offset[s_num] / ring_spacing + (get_scanner_ptr()->get_num_rings() - 1); + const int ring1_plus_ring2 = round(ring1_plus_ring2_float); // check that it was integer - assert(fabs(ring1_plus_ring2 - ring1_plus_ring2_float) < 1E-4) ; + assert(fabs(ring1_plus_ring2 - ring1_plus_ring2_float) < 1E-4); segment_axial_pos_to_ring1_plus_ring2[s_num][ax_pos_num] = ring1_plus_ring2; } } @@ -286,33 +244,24 @@ initialise_ring_diff_arrays() const ring_diff_arrays_computed = true; } -/*! Default implementation checks common variables. Needs to be overloaded. +/*! Default implementation checks common variables. Needs to be overloaded. */ bool -ProjDataInfoCylindrical:: -blindly_equals(const root_type * const that) const -{ +ProjDataInfoCylindrical::blindly_equals(const root_type* const that) const { if (!base_type::blindly_equals(that)) return false; const self_type& proj_data_info = static_cast(*that); - const Array<1,float> tmp(this->ring_radius - proj_data_info.ring_radius); - return - fabs(this->azimuthal_angle_sampling - proj_data_info.azimuthal_angle_sampling) < 0.05F && - norm(tmp) < 0.05F && - this->sampling_corresponds_to_physical_rings == proj_data_info.sampling_corresponds_to_physical_rings && - fabs(this->ring_spacing - proj_data_info.ring_spacing) < 0.05F && - this->min_ring_diff == proj_data_info.min_ring_diff && - this->max_ring_diff == proj_data_info.max_ring_diff; + const Array<1, float> tmp(this->ring_radius - proj_data_info.ring_radius); + return fabs(this->azimuthal_angle_sampling - proj_data_info.azimuthal_angle_sampling) < 0.05F && norm(tmp) < 0.05F && + this->sampling_corresponds_to_physical_rings == proj_data_info.sampling_corresponds_to_physical_rings && + fabs(this->ring_spacing - proj_data_info.ring_spacing) < 0.05F && this->min_ring_diff == proj_data_info.min_ring_diff && + this->max_ring_diff == proj_data_info.max_ring_diff; } void -ProjDataInfoCylindrical:: -get_ring_pair_for_segment_axial_pos_num(int& ring1, - int& ring2, - const int segment_num, - const int axial_pos_num) const -{ +ProjDataInfoCylindrical::get_ring_pair_for_segment_axial_pos_num(int& ring1, int& ring2, const int segment_num, + const int axial_pos_num) const { if (!sampling_corresponds_to_physical_rings) error("ProjDataInfoCylindrical::get_ring_pair_for_segment_axial_pos_num does not work for this type of sampled data"); // can do only span=1 at the moment @@ -322,106 +271,81 @@ get_ring_pair_for_segment_axial_pos_num(int& ring1, this->initialise_ring_diff_arrays_if_not_done_yet(); const int ring_diff = get_max_ring_difference(segment_num); - const int ring1_plus_ring2= segment_axial_pos_to_ring1_plus_ring2[segment_num][axial_pos_num]; + const int ring1_plus_ring2 = segment_axial_pos_to_ring1_plus_ring2[segment_num][axial_pos_num]; // KT 01/08/2002 swapped rings - ring1 = (ring1_plus_ring2 - ring_diff)/2; - ring2 = (ring1_plus_ring2 + ring_diff)/2; - assert((ring1_plus_ring2 + ring_diff)%2 == 0); - assert((ring1_plus_ring2 - ring_diff)%2 == 0); + ring1 = (ring1_plus_ring2 - ring_diff) / 2; + ring2 = (ring1_plus_ring2 + ring_diff) / 2; + assert((ring1_plus_ring2 + ring_diff) % 2 == 0); + assert((ring1_plus_ring2 - ring_diff) % 2 == 0); } - void -ProjDataInfoCylindrical:: -set_azimuthal_angle_sampling(const float angle_v) -{ - azimuthal_angle_sampling = angle_v; +ProjDataInfoCylindrical::set_azimuthal_angle_sampling(const float angle_v) { + azimuthal_angle_sampling = angle_v; } -//void -//ProjDataInfoCylindrical:: -//set_axial_sampling(const float samp_v, int segment_num) +// void +// ProjDataInfoCylindrical:: +// set_axial_sampling(const float samp_v, int segment_num) //{axial_sampling = samp_v;} - void -ProjDataInfoCylindrical:: -set_num_views(const int new_num_views) -{ - const float old_azimuthal_angle_range = - this->get_azimuthal_angle_sampling() * this->get_num_views(); +ProjDataInfoCylindrical::set_num_views(const int new_num_views) { + const float old_azimuthal_angle_range = this->get_azimuthal_angle_sampling() * this->get_num_views(); base_type::set_num_views(new_num_views); - this->azimuthal_angle_sampling = old_azimuthal_angle_range/this->get_num_views(); + this->azimuthal_angle_sampling = old_azimuthal_angle_range / this->get_num_views(); } void -ProjDataInfoCylindrical:: -set_min_ring_difference( int min_ring_diff_v, int segment_num) -{ +ProjDataInfoCylindrical::set_min_ring_difference(int min_ring_diff_v, int segment_num) { ring_diff_arrays_computed = false; min_ring_diff[segment_num] = min_ring_diff_v; } void -ProjDataInfoCylindrical:: -set_max_ring_difference( int max_ring_diff_v, int segment_num) -{ +ProjDataInfoCylindrical::set_max_ring_difference(int max_ring_diff_v, int segment_num) { ring_diff_arrays_computed = false; max_ring_diff[segment_num] = max_ring_diff_v; } void -ProjDataInfoCylindrical:: -set_ring_spacing(float ring_spacing_v) -{ +ProjDataInfoCylindrical::set_ring_spacing(float ring_spacing_v) { ring_diff_arrays_computed = false; ring_spacing = ring_spacing_v; } void -ProjDataInfoCylindrical:: -allocate_segment_axial_pos_to_ring_pair() const -{ - segment_axial_pos_to_ring_pair = - VectorWithOffset > > - (get_min_segment_num(), get_max_segment_num()); - - for (int segment_num = get_min_segment_num(); - segment_num <= get_max_segment_num(); - ++segment_num) - { - segment_axial_pos_to_ring_pair[segment_num].grow(get_min_axial_pos_num(segment_num), - get_max_axial_pos_num(segment_num)); - } +ProjDataInfoCylindrical::allocate_segment_axial_pos_to_ring_pair() const { + segment_axial_pos_to_ring_pair = + VectorWithOffset>>(get_min_segment_num(), get_max_segment_num()); + + for (int segment_num = get_min_segment_num(); segment_num <= get_max_segment_num(); ++segment_num) { + segment_axial_pos_to_ring_pair[segment_num].grow(get_min_axial_pos_num(segment_num), get_max_axial_pos_num(segment_num)); + } } void -ProjDataInfoCylindrical:: -compute_segment_axial_pos_to_ring_pair(const int segment_num, const int axial_pos_num) const -{ +ProjDataInfoCylindrical::compute_segment_axial_pos_to_ring_pair(const int segment_num, const int axial_pos_num) const { shared_ptr new_el(new RingNumPairs); segment_axial_pos_to_ring_pair[segment_num][axial_pos_num] = new_el; - - RingNumPairs& table = - *segment_axial_pos_to_ring_pair[segment_num][axial_pos_num]; - table.reserve(get_max_ring_difference(segment_num) - - get_min_ring_difference(segment_num) + 1); + + RingNumPairs& table = *segment_axial_pos_to_ring_pair[segment_num][axial_pos_num]; + table.reserve(get_max_ring_difference(segment_num) - get_min_ring_difference(segment_num) + 1); /* We compute the lookup-table in a fancy way. - We could just as well have a simple loop over all ring pairs and check - if it belongs to this segment/axial_pos. + We could just as well have a simple loop over all ring pairs and check + if it belongs to this segment/axial_pos. The current way is a lot faster though. */ const int min_ring_diff = get_min_ring_difference(segment_num); const int max_ring_diff = get_max_ring_difference(segment_num); const int num_rings = get_scanner_ptr()->get_num_rings(); - /* ring1_plus_ring2 is the same for any ring pair that contributes to + /* ring1_plus_ring2 is the same for any ring pair that contributes to this particular segment_num, axial_pos_num. */ - const int ring1_plus_ring2= - segment_axial_pos_to_ring1_plus_ring2[segment_num][axial_pos_num]; + const int ring1_plus_ring2 = segment_axial_pos_to_ring1_plus_ring2[segment_num][axial_pos_num]; /* The ring_difference increments with 2 as the other ring differences do @@ -432,202 +356,164 @@ compute_segment_axial_pos_to_ring_pair(const int segment_num, const int axial_po is satisfied. You can check it by noting that the start_ring_diff%2 == (min_ring_diff + (min_ring_diff+ring1_plus_ring2)%2)%2 - == (2*min_ring_diff+ring1_plus_ring2)%2 - == ring1_plus_ring2%2 + == (2*min_ring_diff+ring1_plus_ring2)%2 + == ring1_plus_ring2%2 */ - for(int ring_diff = min_ring_diff + (min_ring_diff+ring1_plus_ring2)%2; - ring_diff <= max_ring_diff; - ring_diff+=2 ) - { - const int ring1 = (ring1_plus_ring2 - ring_diff)/2; - const int ring2 = (ring1_plus_ring2 + ring_diff)/2; - if (ring1<0 || ring2 < 0 || ring1>=num_rings || ring2 >= num_rings) - continue; - assert((ring1_plus_ring2 + ring_diff)%2 == 0); - assert((ring1_plus_ring2 - ring_diff)%2 == 0); - table.push_back(pair(ring1, ring2)); + for (int ring_diff = min_ring_diff + (min_ring_diff + ring1_plus_ring2) % 2; ring_diff <= max_ring_diff; ring_diff += 2) { + const int ring1 = (ring1_plus_ring2 - ring_diff) / 2; + const int ring2 = (ring1_plus_ring2 + ring_diff) / 2; + if (ring1 < 0 || ring2 < 0 || ring1 >= num_rings || ring2 >= num_rings) + continue; + assert((ring1_plus_ring2 + ring_diff) % 2 == 0); + assert((ring1_plus_ring2 - ring_diff) % 2 == 0); + table.push_back(pair(ring1, ring2)); #ifndef NDEBUG - int check_segment_num = 0, check_axial_pos_num = 0; - assert(get_segment_axial_pos_num_for_ring_pair(check_segment_num, - check_axial_pos_num, - ring1, - ring2) == - Succeeded::yes); - assert(check_segment_num == segment_num); - assert(check_axial_pos_num == axial_pos_num); + int check_segment_num = 0, check_axial_pos_num = 0; + assert(get_segment_axial_pos_num_for_ring_pair(check_segment_num, check_axial_pos_num, ring1, ring2) == Succeeded::yes); + assert(check_segment_num == segment_num); + assert(check_axial_pos_num == axial_pos_num); #endif - } + } } void -ProjDataInfoCylindrical:: -set_tof_mash_factor(const int new_num) -{ - base_type::set_tof_mash_factor(new_num); - //! \todo N.E. Would be nice to have all the points of the scanner in cache. - //initialise_uncompressed_lor_as_point1point2(); +ProjDataInfoCylindrical::set_tof_mash_factor(const int new_num) { + base_type::set_tof_mash_factor(new_num); + //! \todo N.E. Would be nice to have all the points of the scanner in cache. + // initialise_uncompressed_lor_as_point1point2(); } -void -ProjDataInfoCylindrical:: -set_num_axial_poss_per_segment(const VectorWithOffset& num_axial_poss_per_segment) -{ +void +ProjDataInfoCylindrical::set_num_axial_poss_per_segment(const VectorWithOffset& num_axial_poss_per_segment) { ProjDataInfo::set_num_axial_poss_per_segment(num_axial_poss_per_segment); ring_diff_arrays_computed = false; } -void -ProjDataInfoCylindrical:: -set_min_axial_pos_num(const int min_ax_pos_num, const int segment_num) -{ +void +ProjDataInfoCylindrical::set_min_axial_pos_num(const int min_ax_pos_num, const int segment_num) { ProjDataInfo::set_min_axial_pos_num(min_ax_pos_num, segment_num); ring_diff_arrays_computed = false; } - -void ProjDataInfoCylindrical:: -set_max_axial_pos_num(const int max_ax_pos_num, const int segment_num) -{ +void +ProjDataInfoCylindrical::set_max_axial_pos_num(const int max_ax_pos_num, const int segment_num) { ProjDataInfo::set_max_axial_pos_num(max_ax_pos_num, segment_num); ring_diff_arrays_computed = false; } void -ProjDataInfoCylindrical:: -reduce_segment_range(const int min_segment_num, const int max_segment_num) -{ +ProjDataInfoCylindrical::reduce_segment_range(const int min_segment_num, const int max_segment_num) { ProjDataInfo::reduce_segment_range(min_segment_num, max_segment_num); // reduce ring_diff arrays to new valid size - VectorWithOffset new_min_ring_diff(min_segment_num, max_segment_num); + VectorWithOffset new_min_ring_diff(min_segment_num, max_segment_num); VectorWithOffset new_max_ring_diff(min_segment_num, max_segment_num); - for (int segment_num = min_segment_num; segment_num<= max_segment_num; ++segment_num) - { + for (int segment_num = min_segment_num; segment_num <= max_segment_num; ++segment_num) { new_min_ring_diff[segment_num] = this->min_ring_diff[segment_num]; new_max_ring_diff[segment_num] = this->max_ring_diff[segment_num]; } this->min_ring_diff = new_min_ring_diff; this->max_ring_diff = new_max_ring_diff; - + // make sure other arrays will be updated if/when necessary this->ring_diff_arrays_computed = false; } - void -ProjDataInfoCylindrical:: -get_LOR(LORInAxialAndNoArcCorrSinogramCoordinates& lor, - const Bin& bin) const -{ +ProjDataInfoCylindrical::get_LOR(LORInAxialAndNoArcCorrSinogramCoordinates& lor, const Bin& bin) const { const float s_in_mm = get_s(bin); const float m_in_mm = get_m(bin); const float tantheta = get_tantheta(bin); const float phi = get_phi(bin); /* parametrisation of LOR is - X= s*cphi + a*sphi, - Y= s*sphi - a*cphi, + X= s*cphi + a*sphi, + Y= s*sphi - a*cphi, Z= m - a*tantheta find now min_a, max_a such that end-points intersect the ring - */ + */ assert(fabs(s_in_mm) < get_ring_radius()); - // a has to be such that X^2+Y^2 == R^2 - const float max_a = sqrt(square(get_ring_radius()) - square(s_in_mm)); - const float min_a = -max_a; - + // a has to be such that X^2+Y^2 == R^2 + const float max_a = sqrt(square(get_ring_radius()) - square(s_in_mm)); + const float min_a = -max_a; + /* start_point.x() = (s_in_mm*cphi + max_a*sphi); - start_point.y() = (s_in_mm*sphi - max_a*cphi); - start_point.z() = (m_in_mm - max_a*tantheta); - stop_point.x() = (s_in_mm*cphi + min_a*sphi); - stop_point.y() = (s_in_mm*sphi - min_a*cphi); - stop_point.z() = (m_in_mm - min_a*tantheta); + start_point.y() = (s_in_mm*sphi - max_a*cphi); + start_point.z() = (m_in_mm - max_a*tantheta); + stop_point.x() = (s_in_mm*cphi + min_a*sphi); + stop_point.y() = (s_in_mm*sphi - min_a*cphi); + stop_point.z() = (m_in_mm - min_a*tantheta); */ - const float z1 = (m_in_mm - max_a*tantheta); - const float z2 = (m_in_mm - min_a*tantheta); - - - lor = - LORInAxialAndNoArcCorrSinogramCoordinates(z1, z2, - phi, - asin(s_in_mm/get_ring_radius()), - get_ring_radius(), - false);// needs to set "swapped" to false given above code -} + const float z1 = (m_in_mm - max_a * tantheta); + const float z2 = (m_in_mm - min_a * tantheta); + + lor = LORInAxialAndNoArcCorrSinogramCoordinates(z1, z2, phi, asin(s_in_mm / get_ring_radius()), get_ring_radius(), + false); // needs to set "swapped" to false given above code +} void -ProjDataInfoCylindrical:: -get_LOR_as_two_points(CartesianCoordinate3D& coord_1, - CartesianCoordinate3D& coord_2, - const Bin& bin) const -{ - const float s_in_mm = get_s(bin); - const float m_in_mm = get_m(bin); - const float tantheta = get_tantheta(bin); - const float phi = get_phi(bin); - /* parametrisation of LOR is - X= s*cphi + a*sphi, - Y= s*sphi - a*cphi, - Z= m - a*tantheta - find now min_a, max_a such that end-points intersect the ring - */ - assert(fabs(s_in_mm) < get_ring_radius()); - // a has to be such that X^2+Y^2 == R^2 - const float max_a = sqrt(square(get_ring_radius()) - square(s_in_mm)); - const float min_a = -max_a; +ProjDataInfoCylindrical::get_LOR_as_two_points(CartesianCoordinate3D& coord_1, CartesianCoordinate3D& coord_2, + const Bin& bin) const { + const float s_in_mm = get_s(bin); + const float m_in_mm = get_m(bin); + const float tantheta = get_tantheta(bin); + const float phi = get_phi(bin); + /* parametrisation of LOR is + X= s*cphi + a*sphi, + Y= s*sphi - a*cphi, + Z= m - a*tantheta + find now min_a, max_a such that end-points intersect the ring +*/ + assert(fabs(s_in_mm) < get_ring_radius()); + // a has to be such that X^2+Y^2 == R^2 + const float max_a = sqrt(square(get_ring_radius()) - square(s_in_mm)); + const float min_a = -max_a; - coord_1.x() = s_in_mm*cos(phi) + min_a*sin(phi); - coord_1.y() = s_in_mm*sin(phi) - max_a*cos(phi); - coord_1.z() = m_in_mm - max_a*tantheta; + coord_1.x() = s_in_mm * cos(phi) + min_a * sin(phi); + coord_1.y() = s_in_mm * sin(phi) - max_a * cos(phi); + coord_1.z() = m_in_mm - max_a * tantheta; - coord_2.x() = s_in_mm*cos(phi) + max_a*sin(phi); - coord_2.y() = s_in_mm*sin(phi) - min_a*cos(phi); - coord_2.z() = m_in_mm - min_a*tantheta; + coord_2.x() = s_in_mm * cos(phi) + max_a * sin(phi); + coord_2.y() = s_in_mm * sin(phi) - min_a * cos(phi); + coord_2.z() = m_in_mm - min_a * tantheta; - if (bin.timing_pos_num()<0) - std::swap(coord_1, coord_2); + if (bin.timing_pos_num() < 0) + std::swap(coord_1, coord_2); } void -ProjDataInfoCylindrical:: -get_LOR_as_two_points_alt(CartesianCoordinate3D& coord_1, - CartesianCoordinate3D& coord_2, - const int det1, - const int det2, - const int ring1, - const int ring2, - const int timing_pos) const -{ - const int num_detectors_per_ring = - get_scanner_ptr()->get_num_detectors_per_ring(); - - float h_scanner_height = ( (get_scanner_ptr()->get_ring_spacing() -1) * get_scanner_ptr()->get_num_rings())/2.F; - - // although code maybe doesn't really need the following, - // asserts in the LOR code will break if these conditions are not satisfied. - assert(0<=det1); - assert(det1 cyl_coords(get_scanner_ptr()->get_inner_ring_radius()); - - cyl_coords.p1().psi() = static_cast((2.*_PI/num_detectors_per_ring)*(det1)); - cyl_coords.p2().psi() = static_cast((2.*_PI/num_detectors_per_ring)*(det2)); - - cyl_coords.p1().z() = ring1*get_scanner_ptr()->get_ring_spacing() - h_scanner_height; - cyl_coords.p2().z() = ring2*get_scanner_ptr()->get_ring_spacing() - h_scanner_height; - - LORAs2Points lor(cyl_coords); - coord_1 = lor.p1(); - coord_2 = lor.p2(); - - if (timing_pos<0) - std::swap(coord_1, coord_2); +ProjDataInfoCylindrical::get_LOR_as_two_points_alt(CartesianCoordinate3D& coord_1, CartesianCoordinate3D& coord_2, + const int det1, const int det2, const int ring1, const int ring2, + const int timing_pos) const { + const int num_detectors_per_ring = get_scanner_ptr()->get_num_detectors_per_ring(); + + float h_scanner_height = ((get_scanner_ptr()->get_ring_spacing() - 1) * get_scanner_ptr()->get_num_rings()) / 2.F; + + // although code maybe doesn't really need the following, + // asserts in the LOR code will break if these conditions are not satisfied. + assert(0 <= det1); + assert(det1 < num_detectors_per_ring); + assert(0 <= det2); + assert(det2 < num_detectors_per_ring); + + LORInCylinderCoordinates cyl_coords(get_scanner_ptr()->get_inner_ring_radius()); + + cyl_coords.p1().psi() = static_cast((2. * _PI / num_detectors_per_ring) * (det1)); + cyl_coords.p2().psi() = static_cast((2. * _PI / num_detectors_per_ring) * (det2)); + + cyl_coords.p1().z() = ring1 * get_scanner_ptr()->get_ring_spacing() - h_scanner_height; + cyl_coords.p2().z() = ring2 * get_scanner_ptr()->get_ring_spacing() - h_scanner_height; + + LORAs2Points lor(cyl_coords); + coord_1 = lor.p1(); + coord_2 = lor.p2(); + + if (timing_pos < 0) + std::swap(coord_1, coord_2); } string -ProjDataInfoCylindrical::parameter_info() const -{ +ProjDataInfoCylindrical::parameter_info() const { #ifdef BOOST_NO_STRINGSTREAM // dangerous for out-of-range, but 'old-style' ostrstream seems to need this @@ -635,15 +521,14 @@ ProjDataInfoCylindrical::parameter_info() const ostrstream s(str, 30000); #else std::ostringstream s; -#endif +#endif s << ProjDataInfo::parameter_info(); - s << "Azimuthal angle increment (deg): " << get_azimuthal_angle_sampling()*180/_PI << '\n'; - s << "Azimuthal angle extent (deg): " << fabs(get_azimuthal_angle_sampling())*get_num_views()*180/_PI << '\n'; + s << "Azimuthal angle increment (deg): " << get_azimuthal_angle_sampling() * 180 / _PI << '\n'; + s << "Azimuthal angle extent (deg): " << fabs(get_azimuthal_angle_sampling()) * get_num_views() * 180 / _PI << '\n'; s << "ring differences per segment: \n"; - for (int segment_num=get_min_segment_num(); segment_num<=get_max_segment_num(); ++segment_num) - { - s << '(' << min_ring_diff[segment_num] << ',' << max_ring_diff[segment_num] <<')'; + for (int segment_num = get_min_segment_num(); segment_num <= get_max_segment_num(); ++segment_num) { + s << '(' << min_ring_diff[segment_num] << ',' << max_ring_diff[segment_num] << ')'; } s << std::endl; return s.str(); diff --git a/src/buildblock/ProjDataInfoCylindricalArcCorr.cxx b/src/buildblock/ProjDataInfoCylindricalArcCorr.cxx index 4e2a44d4d3..75d19792da 100644 --- a/src/buildblock/ProjDataInfoCylindricalArcCorr.cxx +++ b/src/buildblock/ProjDataInfoCylindricalArcCorr.cxx @@ -23,7 +23,7 @@ \file \ingroup projdata - \brief Implementation of non-inline functions of class + \brief Implementation of non-inline functions of class stir::ProjDataInfoCylindricalArcCorr \author Sanida Mustafovic @@ -38,9 +38,9 @@ #include "stir/round.h" #include "stir/LORCoordinates.h" #ifdef BOOST_NO_STRINGSTREAM -#include +# include #else -#include +# include #endif #ifndef STIR_NO_NAMESPACES @@ -49,64 +49,48 @@ using std::ends; using std::string; #endif - START_NAMESPACE_STIR -ProjDataInfoCylindricalArcCorr:: ProjDataInfoCylindricalArcCorr() -{} +ProjDataInfoCylindricalArcCorr::ProjDataInfoCylindricalArcCorr() {} + +ProjDataInfoCylindricalArcCorr::ProjDataInfoCylindricalArcCorr(const shared_ptr scanner_ptr, float bin_size_v, + const VectorWithOffset& num_axial_pos_per_segment, + const VectorWithOffset& min_ring_diff_v, + const VectorWithOffset& max_ring_diff_v, const int num_views, + const int num_tangential_poss, const int tof_mash_factor) + : ProjDataInfoCylindrical(scanner_ptr, num_axial_pos_per_segment, min_ring_diff_v, max_ring_diff_v, num_views, + num_tangential_poss), + bin_size(bin_size_v) -ProjDataInfoCylindricalArcCorr:: ProjDataInfoCylindricalArcCorr(const shared_ptr scanner_ptr,float bin_size_v, - const VectorWithOffset& num_axial_pos_per_segment, - const VectorWithOffset& min_ring_diff_v, - const VectorWithOffset& max_ring_diff_v, - const int num_views,const int num_tangential_poss, - const int tof_mash_factor) - :ProjDataInfoCylindrical(scanner_ptr, - num_axial_pos_per_segment, - min_ring_diff_v, max_ring_diff_v, - num_views, num_tangential_poss), - bin_size(bin_size_v) - { - if (scanner_ptr->is_tof_ready()) - set_tof_mash_factor(tof_mash_factor); + if (scanner_ptr->is_tof_ready()) + set_tof_mash_factor(tof_mash_factor); } - void -ProjDataInfoCylindricalArcCorr::set_tangential_sampling(const float new_tangential_sampling) -{bin_size = new_tangential_sampling;} - - +ProjDataInfoCylindricalArcCorr::set_tangential_sampling(const float new_tangential_sampling) { + bin_size = new_tangential_sampling; +} ProjDataInfo* -ProjDataInfoCylindricalArcCorr::clone() const -{ +ProjDataInfoCylindricalArcCorr::clone() const { return static_cast(new ProjDataInfoCylindricalArcCorr(*this)); } - bool -ProjDataInfoCylindricalArcCorr:: -operator==(const self_type& that) const -{ +ProjDataInfoCylindricalArcCorr::operator==(const self_type& that) const { if (!base_type::blindly_equals(&that)) return false; - return - fabs(this->bin_size - that.bin_size) < 0.05F; + return fabs(this->bin_size - that.bin_size) < 0.05F; } bool -ProjDataInfoCylindricalArcCorr:: -blindly_equals(const root_type * const that_ptr) const -{ - assert(dynamic_cast(that_ptr) != 0); - return - this->operator==(static_cast(*that_ptr)); +ProjDataInfoCylindricalArcCorr::blindly_equals(const root_type* const that_ptr) const { + assert(dynamic_cast(that_ptr) != 0); + return this->operator==(static_cast(*that_ptr)); } string -ProjDataInfoCylindricalArcCorr::parameter_info() const -{ +ProjDataInfoCylindricalArcCorr::parameter_info() const { #ifdef BOOST_NO_STRINGSTREAM // dangerous for out-of-range, but 'old-style' ostrstream seems to need this @@ -114,7 +98,7 @@ ProjDataInfoCylindricalArcCorr::parameter_info() const ostrstream s(str, 50000); #else std::ostringstream s; -#endif +#endif s << "ProjDataInfoCylindricalArcCorr := \n"; s << ProjDataInfoCylindrical::parameter_info(); s << "tangential sampling := " << get_tangential_sampling() << endl; @@ -122,46 +106,39 @@ ProjDataInfoCylindricalArcCorr::parameter_info() const return s.str(); } - Bin -ProjDataInfoCylindricalArcCorr:: -get_bin(const LOR& lor,const double delta_time) const +ProjDataInfoCylindricalArcCorr::get_bin(const LOR& lor, const double delta_time) const { - if (delta_time != 0) - { - error("TODO NO TOF YET"); - } + if (delta_time != 0) { + error("TODO NO TOF YET"); + } Bin bin; LORInAxialAndSinogramCoordinates lor_coords; - if (lor.change_representation(lor_coords, get_ring_radius()) == Succeeded::no) - { - bin.set_bin_value(-1); - return bin; - } + if (lor.change_representation(lor_coords, get_ring_radius()) == Succeeded::no) { + bin.set_bin_value(-1); + return bin; + } - // first find view + // first find view // unfortunately, phi ranges from [0,Pi[, but the rounding can // map this to a view which corresponds to Pi anyway. bin.view_num() = round(lor_coords.phi() / get_azimuthal_angle_sampling()); - assert(bin.view_num()>=0); - assert(bin.view_num()<=get_num_views()); - const bool swap_direction = - bin.view_num() > get_max_view_num(); + assert(bin.view_num() >= 0); + assert(bin.view_num() <= get_num_views()); + const bool swap_direction = bin.view_num() > get_max_view_num(); if (swap_direction) - bin.view_num()-=get_num_views(); + bin.view_num() -= get_num_views(); bin.tangential_pos_num() = round(lor_coords.s() / get_tangential_sampling()); if (swap_direction) bin.tangential_pos_num() *= -1; - if (bin.tangential_pos_num() < get_min_tangential_pos_num() || - bin.tangential_pos_num() > get_max_tangential_pos_num()) - { - bin.set_bin_value(-1); - return bin; - } + if (bin.tangential_pos_num() < get_min_tangential_pos_num() || bin.tangential_pos_num() > get_max_tangential_pos_num()) { + bin.set_bin_value(-1); + return bin; + } #if 0 const int num_rings = @@ -194,63 +171,51 @@ get_bin(const LOR& lor,const double delta_time) const // find nearest segment { const float delta = - (swap_direction - ? lor_coords.z1()-lor_coords.z2() - : lor_coords.z2()-lor_coords.z1() - )/get_ring_spacing(); + (swap_direction ? lor_coords.z1() - lor_coords.z2() : lor_coords.z2() - lor_coords.z1()) / get_ring_spacing(); // check if out of acquired range // note the +1 or -1, which takes the size of the rings into account - if (delta>get_max_ring_difference(get_max_segment_num())+1 || - delta=0) - { - for (bin.segment_num()=0; bin.segment_num() get_max_ring_difference(get_max_segment_num()) + 1 || + delta < get_min_ring_difference(get_min_segment_num()) - 1) { + bin.set_bin_value(-1); + return bin; + } + if (delta >= 0) { + for (bin.segment_num() = 0; bin.segment_num() < get_max_segment_num(); ++bin.segment_num()) { + if (delta < get_max_ring_difference(bin.segment_num()) + .5) + break; } - else - { - // delta<0 - for (bin.segment_num()=0; bin.segment_num()>get_min_segment_num(); --bin.segment_num()) - { - if (delta > get_min_ring_difference(bin.segment_num())-.5) - break; - } + } else { + // delta<0 + for (bin.segment_num() = 0; bin.segment_num() > get_min_segment_num(); --bin.segment_num()) { + if (delta > get_min_ring_difference(bin.segment_num()) - .5) + break; } + } } // now find nearest axial position { - const float m = (lor_coords.z2()+lor_coords.z1())/2; -#if 0 + const float m = (lor_coords.z2() + lor_coords.z1()) / 2; +# if 0 // this uses private member of ProjDataInfoCylindrical // enable when moved initialise_ring_diff_arrays_if_not_done_yet(); -#ifndef NDEBUG +# ifndef NDEBUG bin.axial_pos_num()=0; assert(get_m(bin)==- m_offset[bin.segment_num()]); -#endif +# endif bin.axial_pos_num() = round((m + m_offset[bin.segment_num()])/ get_axial_sampling(bin.segment_num())); -#else - bin.axial_pos_num()=0; - bin.axial_pos_num() = - round((m - get_m(bin))/ - get_axial_sampling(bin.segment_num())); -#endif +# else + bin.axial_pos_num() = 0; + bin.axial_pos_num() = round((m - get_m(bin)) / get_axial_sampling(bin.segment_num())); +# endif if (bin.axial_pos_num() < get_min_axial_pos_num(bin.segment_num()) || - bin.axial_pos_num() > get_max_axial_pos_num(bin.segment_num())) - { - bin.set_bin_value(-1); - return bin; - } + bin.axial_pos_num() > get_max_axial_pos_num(bin.segment_num())) { + bin.set_bin_value(-1); + return bin; + } } #endif @@ -258,4 +223,3 @@ get_bin(const LOR& lor,const double delta_time) const return bin; } END_NAMESPACE_STIR - diff --git a/src/buildblock/ProjDataInfoCylindricalNoArcCorr.cxx b/src/buildblock/ProjDataInfoCylindricalNoArcCorr.cxx index 90fa8ee019..294c10ae63 100644 --- a/src/buildblock/ProjDataInfoCylindricalNoArcCorr.cxx +++ b/src/buildblock/ProjDataInfoCylindricalNoArcCorr.cxx @@ -23,7 +23,7 @@ \file \ingroup projdata - \brief Implementation of non-inline functions of class + \brief Implementation of non-inline functions of class stir::ProjDataInfoCylindricalNoArcCorr \author Nikos Efthimiou @@ -38,9 +38,9 @@ #include "stir/round.h" #include #ifdef BOOST_NO_STRINGSTREAM -#include +# include #else -#include +# include #endif #include @@ -54,90 +54,63 @@ using std::vector; #endif START_NAMESPACE_STIR -ProjDataInfoCylindricalNoArcCorr:: -ProjDataInfoCylindricalNoArcCorr() -{} - -ProjDataInfoCylindricalNoArcCorr:: -ProjDataInfoCylindricalNoArcCorr(const shared_ptr scanner_ptr, - const float ring_radius_v, const float angular_increment_v, - const VectorWithOffset& num_axial_pos_per_segment, - const VectorWithOffset& min_ring_diff_v, - const VectorWithOffset& max_ring_diff_v, - const int num_views,const int num_tangential_poss, - const int tof_mash_factor) -: ProjDataInfoCylindrical(scanner_ptr, - num_axial_pos_per_segment, - min_ring_diff_v, max_ring_diff_v, - num_views, num_tangential_poss), - ring_radius(ring_radius_v), - angular_increment(angular_increment_v) -{ +ProjDataInfoCylindricalNoArcCorr::ProjDataInfoCylindricalNoArcCorr() {} + +ProjDataInfoCylindricalNoArcCorr::ProjDataInfoCylindricalNoArcCorr( + const shared_ptr scanner_ptr, const float ring_radius_v, const float angular_increment_v, + const VectorWithOffset& num_axial_pos_per_segment, const VectorWithOffset& min_ring_diff_v, + const VectorWithOffset& max_ring_diff_v, const int num_views, const int num_tangential_poss, const int tof_mash_factor) + : ProjDataInfoCylindrical(scanner_ptr, num_axial_pos_per_segment, min_ring_diff_v, max_ring_diff_v, num_views, + num_tangential_poss), + ring_radius(ring_radius_v), angular_increment(angular_increment_v) { uncompressed_view_tangpos_to_det1det2_initialised = false; det1det2_to_uncompressed_view_tangpos_initialised = false; - if(scanner_ptr->is_tof_ready()) + if (scanner_ptr->is_tof_ready()) set_tof_mash_factor(tof_mash_factor); - //this->initialise_uncompressed_view_tangpos_to_det1det2(); - //this->initialise_det1det2_to_uncompressed_view_tangpos(); + // this->initialise_uncompressed_view_tangpos_to_det1det2(); + // this->initialise_det1det2_to_uncompressed_view_tangpos(); } -ProjDataInfoCylindricalNoArcCorr:: -ProjDataInfoCylindricalNoArcCorr(const shared_ptr scanner_ptr, - const VectorWithOffset& num_axial_pos_per_segment, - const VectorWithOffset& min_ring_diff_v, - const VectorWithOffset& max_ring_diff_v, - const int num_views, const int num_tangential_poss, - const int tof_mash_factor) -: ProjDataInfoCylindrical(scanner_ptr, - num_axial_pos_per_segment, - min_ring_diff_v, max_ring_diff_v, - num_views, num_tangential_poss) -{ +ProjDataInfoCylindricalNoArcCorr::ProjDataInfoCylindricalNoArcCorr(const shared_ptr scanner_ptr, + const VectorWithOffset& num_axial_pos_per_segment, + const VectorWithOffset& min_ring_diff_v, + const VectorWithOffset& max_ring_diff_v, + const int num_views, const int num_tangential_poss, + const int tof_mash_factor) + : ProjDataInfoCylindrical(scanner_ptr, num_axial_pos_per_segment, min_ring_diff_v, max_ring_diff_v, num_views, + num_tangential_poss) { assert(!is_null_ptr(scanner_ptr)); ring_radius = scanner_ptr->get_effective_ring_radius(); - angular_increment = static_cast(_PI/scanner_ptr->get_num_detectors_per_ring()); + angular_increment = static_cast(_PI / scanner_ptr->get_num_detectors_per_ring()); uncompressed_view_tangpos_to_det1det2_initialised = false; det1det2_to_uncompressed_view_tangpos_initialised = false; - if(scanner_ptr->is_tof_ready()) + if (scanner_ptr->is_tof_ready()) set_tof_mash_factor(tof_mash_factor); - //this->initialise_uncompressed_view_tangpos_to_det1det2(); - //this->initialise_det1det2_to_uncompressed_view_tangpos(); + // this->initialise_uncompressed_view_tangpos_to_det1det2(); + // this->initialise_det1det2_to_uncompressed_view_tangpos(); } - - - ProjDataInfo* -ProjDataInfoCylindricalNoArcCorr::clone() const -{ +ProjDataInfoCylindricalNoArcCorr::clone() const { return static_cast(new ProjDataInfoCylindricalNoArcCorr(*this)); } bool -ProjDataInfoCylindricalNoArcCorr:: -operator==(const self_type& that) const -{ +ProjDataInfoCylindricalNoArcCorr::operator==(const self_type& that) const { if (!base_type::blindly_equals(&that)) return false; - return - fabs(this->ring_radius - that.ring_radius) < 0.05F && - fabs(this->angular_increment - that.angular_increment) < 0.05F; + return fabs(this->ring_radius - that.ring_radius) < 0.05F && fabs(this->angular_increment - that.angular_increment) < 0.05F; } bool -ProjDataInfoCylindricalNoArcCorr:: -blindly_equals(const root_type * const that_ptr) const -{ - assert(dynamic_cast(that_ptr) != 0); - return - this->operator==(static_cast(*that_ptr)); +ProjDataInfoCylindricalNoArcCorr::blindly_equals(const root_type* const that_ptr) const { + assert(dynamic_cast(that_ptr) != 0); + return this->operator==(static_cast(*that_ptr)); } - string -ProjDataInfoCylindricalNoArcCorr::parameter_info() const -{ +ProjDataInfoCylindricalNoArcCorr::parameter_info() const { #ifdef BOOST_NO_STRINGSTREAM // dangerous for out-of-range, but 'old-style' ostrstream seems to need this @@ -145,7 +118,7 @@ ProjDataInfoCylindricalNoArcCorr::parameter_info() const ostrstream s(str, 50000); #else std::ostringstream s; -#endif +#endif s << "ProjDataInfoCylindricalNoArcCorr := \n"; s << ProjDataInfoCylindrical::parameter_info(); s << "End :=\n"; @@ -181,87 +154,71 @@ ProjDataInfoCylindricalNoArcCorr::parameter_info() const - angles have to be defined modulo 2 Pi (so num_detectors) - interleaving */ -void -ProjDataInfoCylindricalNoArcCorr:: -initialise_uncompressed_view_tangpos_to_det1det2() const -{ +void +ProjDataInfoCylindricalNoArcCorr::initialise_uncompressed_view_tangpos_to_det1det2() const { BOOST_STATIC_ASSERT(-1 >> 1 == -1); BOOST_STATIC_ASSERT(-2 >> 1 == -1); - const int num_detectors = - get_scanner_ptr()->get_num_detectors_per_ring(); + const int num_detectors = get_scanner_ptr()->get_num_detectors_per_ring(); - assert(num_detectors%2 == 0); + assert(num_detectors % 2 == 0); // check views range from 0 to Pi - assert(fabs(get_phi(Bin(0,0,0,0))) < 1.E-4); - assert(fabs(get_phi(Bin(0,get_num_views(),0,0)) - _PI) < 1.E-4); - const int min_tang_pos_num = -(num_detectors/2)+1; - const int max_tang_pos_num = -(num_detectors/2)+num_detectors; - - if (this->get_min_tangential_pos_num() < min_tang_pos_num || - this->get_max_tangential_pos_num() > max_tang_pos_num) - { - error("The tangential_pos range (%d to %d) for this projection data is too large.\n" - "Maximum supported range is from %d to %d", - this->get_min_tangential_pos_num(), this->get_max_tangential_pos_num(), - min_tang_pos_num, max_tang_pos_num); - } + assert(fabs(get_phi(Bin(0, 0, 0, 0))) < 1.E-4); + assert(fabs(get_phi(Bin(0, get_num_views(), 0, 0)) - _PI) < 1.E-4); + const int min_tang_pos_num = -(num_detectors / 2) + 1; + const int max_tang_pos_num = -(num_detectors / 2) + num_detectors; + + if (this->get_min_tangential_pos_num() < min_tang_pos_num || this->get_max_tangential_pos_num() > max_tang_pos_num) { + error("The tangential_pos range (%d to %d) for this projection data is too large.\n" + "Maximum supported range is from %d to %d", + this->get_min_tangential_pos_num(), this->get_max_tangential_pos_num(), min_tang_pos_num, max_tang_pos_num); + } - uncompressed_view_tangpos_to_det1det2.grow(0,num_detectors/2-1); - for (int v_num=0; v_num<=num_detectors/2-1; ++v_num) - { + uncompressed_view_tangpos_to_det1det2.grow(0, num_detectors / 2 - 1); + for (int v_num = 0; v_num <= num_detectors / 2 - 1; ++v_num) { uncompressed_view_tangpos_to_det1det2[v_num].grow(min_tang_pos_num, max_tang_pos_num); - for (int tp_num=min_tang_pos_num; tp_num<=max_tang_pos_num; ++tp_num) - { + for (int tp_num = min_tang_pos_num; tp_num <= max_tang_pos_num; ++tp_num) { /* adapted from CTI code Note for implementation: avoid using % with negative numbers so add num_detectors before doing modulo num_detectors) */ - uncompressed_view_tangpos_to_det1det2[v_num][tp_num].det1_num = - (v_num + (tp_num >> 1) + num_detectors) % num_detectors; - uncompressed_view_tangpos_to_det1det2[v_num][tp_num].det2_num = - (v_num - ( (tp_num + 1) >> 1 ) + num_detectors/2) % num_detectors; + uncompressed_view_tangpos_to_det1det2[v_num][tp_num].det1_num = (v_num + (tp_num >> 1) + num_detectors) % num_detectors; + uncompressed_view_tangpos_to_det1det2[v_num][tp_num].det2_num = + (v_num - ((tp_num + 1) >> 1) + num_detectors / 2) % num_detectors; } } uncompressed_view_tangpos_to_det1det2_initialised = true; } -void -ProjDataInfoCylindricalNoArcCorr:: -initialise_det1det2_to_uncompressed_view_tangpos() const -{ +void +ProjDataInfoCylindricalNoArcCorr::initialise_det1det2_to_uncompressed_view_tangpos() const { BOOST_STATIC_ASSERT(-1 >> 1 == -1); BOOST_STATIC_ASSERT(-2 >> 1 == -1); - const int num_detectors = - get_scanner_ptr()->get_num_detectors_per_ring(); + const int num_detectors = get_scanner_ptr()->get_num_detectors_per_ring(); - if (num_detectors%2 != 0) - { - error("Number of detectors per ring should be even but is %d", num_detectors); - } - if (this->get_min_view_num() != 0) - { - error("Minimum view number should currently be zero to be able to use get_view_tangential_pos_num_for_det_num_pair()"); - } + if (num_detectors % 2 != 0) { + error("Number of detectors per ring should be even but is %d", num_detectors); + } + if (this->get_min_view_num() != 0) { + error("Minimum view number should currently be zero to be able to use get_view_tangential_pos_num_for_det_num_pair()"); + } // check views range from 0 to Pi - assert(fabs(get_phi(Bin(0,0,0,0))) < 1.E-4); - assert(fabs(get_phi(Bin(0,get_max_view_num()+1,0,0)) - _PI) < 1.E-4); - //const int min_tang_pos_num = -(num_detectors/2); - //const int max_tang_pos_num = -(num_detectors/2)+num_detectors; - const int max_num_views = num_detectors/2; - - det1det2_to_uncompressed_view_tangpos.grow(0,num_detectors-1); - for (int det1_num=0; det1_num> 1) + num_detectors) % num_detectors; - + int tang_pos_num = (det1_num - det2_num + 3 * num_detectors / 2) % num_detectors; + int view_num = (det1_num - (tang_pos_num >> 1) + num_detectors) % num_detectors; + /* Now adjust ranges for view_num, tang_pos_num. The next lines go only wrong in the singular (and irrelevant) case det_num1 == det_num2 (when tang_pos_num == num_detectors - tang_pos_num) - + We use the combinations of the following 'symmetries' of (tang_pos_num, view_num) == (tang_pos_num+2*num_views, view_num + num_views) == (-tang_pos_num, view_num + num_views) @@ -288,116 +245,86 @@ initialise_det1det2_to_uncompressed_view_tangpos() const as well. So, we keep track of this in swap_detectors, and return its final value. */ - if (view_num < max_num_views) - { - if (tang_pos_num >= max_num_views) - { + if (view_num < max_num_views) { + if (tang_pos_num >= max_num_views) { tang_pos_num = num_detectors - tang_pos_num; swap_detectors = 1; - } - else - { + } else { swap_detectors = 0; } - } - else - { + } else { view_num -= max_num_views; - if (tang_pos_num >= max_num_views) - { + if (tang_pos_num >= max_num_views) { tang_pos_num -= num_detectors; swap_detectors = 0; - } - else - { + } else { tang_pos_num *= -1; swap_detectors = 1; } } - + det1det2_to_uncompressed_view_tangpos[det1_num][det2_num].view_num = view_num; det1det2_to_uncompressed_view_tangpos[det1_num][det2_num].tang_pos_num = tang_pos_num; - det1det2_to_uncompressed_view_tangpos[det1_num][det2_num].swap_detectors = swap_detectors==0; + det1det2_to_uncompressed_view_tangpos[det1_num][det2_num].swap_detectors = swap_detectors == 0; } } det1det2_to_uncompressed_view_tangpos_initialised = true; } unsigned int -ProjDataInfoCylindricalNoArcCorr:: -get_num_det_pos_pairs_for_bin(const Bin& bin) const -{ - return - get_num_ring_pairs_for_segment_axial_pos_num(bin.segment_num(), - bin.axial_pos_num())* - get_view_mashing_factor()* - std::max(1,get_tof_mash_factor()); +ProjDataInfoCylindricalNoArcCorr::get_num_det_pos_pairs_for_bin(const Bin& bin) const { + return get_num_ring_pairs_for_segment_axial_pos_num(bin.segment_num(), bin.axial_pos_num()) * get_view_mashing_factor() * + std::max(1, get_tof_mash_factor()); } void -ProjDataInfoCylindricalNoArcCorr:: -get_all_det_pos_pairs_for_bin(vector >& dps, - const Bin& bin) const -{ +ProjDataInfoCylindricalNoArcCorr::get_all_det_pos_pairs_for_bin(vector>& dps, const Bin& bin) const { this->initialise_uncompressed_view_tangpos_to_det1det2_if_not_done_yet(); dps.resize(get_num_det_pos_pairs_for_bin(bin)); const ProjDataInfoCylindrical::RingNumPairs& ring_pairs = - get_all_ring_pairs_for_segment_axial_pos_num(bin.segment_num(), - bin.axial_pos_num()); + get_all_ring_pairs_for_segment_axial_pos_num(bin.segment_num(), bin.axial_pos_num()); // not sure how to handle mashing with non-zero view offset... - assert(get_min_view_num()==0); + assert(get_min_view_num() == 0); // not sure how to handle even tof mashing assert(!is_tof_data() || (get_tof_mash_factor() % 2 == 1)); - unsigned int current_dp_num=0; - for (int uncompressed_view_num=bin.view_num()*get_view_mashing_factor(); - uncompressed_view_num<(bin.view_num()+1)*get_view_mashing_factor(); - ++uncompressed_view_num) - { - const int det1_num = - uncompressed_view_tangpos_to_det1det2[uncompressed_view_num][bin.tangential_pos_num()].det1_num; - const int det2_num = - uncompressed_view_tangpos_to_det1det2[uncompressed_view_num][bin.tangential_pos_num()].det2_num; - for (ProjDataInfoCylindrical::RingNumPairs::const_iterator rings_iter = ring_pairs.begin(); - rings_iter != ring_pairs.end(); - ++rings_iter) - { - for (int uncompressed_timing_pos_num = bin.timing_pos_num()*get_tof_mash_factor() - (get_tof_mash_factor() / 2); - uncompressed_timing_pos_num <= bin.timing_pos_num()*get_tof_mash_factor() + (get_tof_mash_factor() / 2); - ++uncompressed_timing_pos_num) - { - assert(current_dp_num < get_num_det_pos_pairs_for_bin(bin)); - dps[current_dp_num].pos1().tangential_coord() = det1_num; - dps[current_dp_num].pos1().axial_coord() = rings_iter->first; - dps[current_dp_num].pos2().tangential_coord() = det2_num; - dps[current_dp_num].pos2().axial_coord() = rings_iter->second; - // need to keep dp.timing_pos positive - if (uncompressed_timing_pos_num > 0) - { - dps[current_dp_num].timing_pos() = static_cast(uncompressed_timing_pos_num); - } - else - { - std::swap(dps[current_dp_num].pos1(), dps[current_dp_num].pos2()); - dps[current_dp_num].timing_pos() = static_cast(-uncompressed_timing_pos_num); - } - ++current_dp_num; - } + unsigned int current_dp_num = 0; + for (int uncompressed_view_num = bin.view_num() * get_view_mashing_factor(); + uncompressed_view_num < (bin.view_num() + 1) * get_view_mashing_factor(); ++uncompressed_view_num) { + const int det1_num = uncompressed_view_tangpos_to_det1det2[uncompressed_view_num][bin.tangential_pos_num()].det1_num; + const int det2_num = uncompressed_view_tangpos_to_det1det2[uncompressed_view_num][bin.tangential_pos_num()].det2_num; + for (ProjDataInfoCylindrical::RingNumPairs::const_iterator rings_iter = ring_pairs.begin(); rings_iter != ring_pairs.end(); + ++rings_iter) { + for (int uncompressed_timing_pos_num = bin.timing_pos_num() * get_tof_mash_factor() - (get_tof_mash_factor() / 2); + uncompressed_timing_pos_num <= bin.timing_pos_num() * get_tof_mash_factor() + (get_tof_mash_factor() / 2); + ++uncompressed_timing_pos_num) { + assert(current_dp_num < get_num_det_pos_pairs_for_bin(bin)); + dps[current_dp_num].pos1().tangential_coord() = det1_num; + dps[current_dp_num].pos1().axial_coord() = rings_iter->first; + dps[current_dp_num].pos2().tangential_coord() = det2_num; + dps[current_dp_num].pos2().axial_coord() = rings_iter->second; + // need to keep dp.timing_pos positive + if (uncompressed_timing_pos_num > 0) { + dps[current_dp_num].timing_pos() = static_cast(uncompressed_timing_pos_num); + } else { + std::swap(dps[current_dp_num].pos1(), dps[current_dp_num].pos2()); + dps[current_dp_num].timing_pos() = static_cast(-uncompressed_timing_pos_num); } + ++current_dp_num; + } } + } assert(current_dp_num == get_num_det_pos_pairs_for_bin(bin)); } Succeeded -ProjDataInfoCylindricalNoArcCorr:: -find_scanner_coordinates_given_cartesian_coordinates(int& det1, int& det2, int& ring1, int& ring2, - const CartesianCoordinate3D& c1, - const CartesianCoordinate3D& c2) const -{ - const int num_detectors=get_scanner_ptr()->get_num_detectors_per_ring(); - const float ring_spacing=get_scanner_ptr()->get_ring_spacing(); - const float ring_radius=get_scanner_ptr()->get_effective_ring_radius(); +ProjDataInfoCylindricalNoArcCorr::find_scanner_coordinates_given_cartesian_coordinates( + int& det1, int& det2, int& ring1, int& ring2, const CartesianCoordinate3D& c1, + const CartesianCoordinate3D& c2) const { + const int num_detectors = get_scanner_ptr()->get_num_detectors_per_ring(); + const float ring_spacing = get_scanner_ptr()->get_ring_spacing(); + const float ring_radius = get_scanner_ptr()->get_effective_ring_radius(); #if 0 const CartesianCoordinate3D d = c2 - c1; @@ -433,77 +360,60 @@ find_scanner_coordinates_given_cartesian_coordinates(int& det1, int& det2, int& ring2 = round(coord_det2.z()/ring_spacing); #else LORInCylinderCoordinates cyl_coords; - if (find_LOR_intersections_with_cylinder(cyl_coords, - LORAs2Points(c1, c2), - ring_radius) - == Succeeded::no) + if (find_LOR_intersections_with_cylinder(cyl_coords, LORAs2Points(c1, c2), ring_radius) == Succeeded::no) return Succeeded::no; - det1 = modulo(round(cyl_coords.p1().psi()/(2.*_PI/num_detectors)), num_detectors); - det2 = modulo(round(cyl_coords.p2().psi()/(2.*_PI/num_detectors)), num_detectors); - ring1 = round(cyl_coords.p1().z()/ring_spacing); - ring2 = round(cyl_coords.p2().z()/ring_spacing); + det1 = modulo(round(cyl_coords.p1().psi() / (2. * _PI / num_detectors)), num_detectors); + det2 = modulo(round(cyl_coords.p2().psi() / (2. * _PI / num_detectors)), num_detectors); + ring1 = round(cyl_coords.p1().z() / ring_spacing); + ring2 = round(cyl_coords.p2().z() / ring_spacing); #endif - assert(det1 >=0 && det1get_num_detectors_per_ring()); - assert(det2 >=0 && det2get_num_detectors_per_ring()); + assert(det1 >= 0 && det1 < get_scanner_ptr()->get_num_detectors_per_ring()); + assert(det2 >= 0 && det2 < get_scanner_ptr()->get_num_detectors_per_ring()); - return - (ring1 >=0 && ring1get_num_rings() && - ring2 >=0 && ring2get_num_rings()) - ? Succeeded::yes : Succeeded::no; + return (ring1 >= 0 && ring1 < get_scanner_ptr()->get_num_rings() && ring2 >= 0 && ring2 < get_scanner_ptr()->get_num_rings()) + ? Succeeded::yes + : Succeeded::no; } - -void -ProjDataInfoCylindricalNoArcCorr:: -find_cartesian_coordinates_of_detection( - CartesianCoordinate3D& coord_1, - CartesianCoordinate3D& coord_2, - const Bin& bin) const -{ - // find detectors +void +ProjDataInfoCylindricalNoArcCorr::find_cartesian_coordinates_of_detection(CartesianCoordinate3D& coord_1, + CartesianCoordinate3D& coord_2, + const Bin& bin) const { + // find detectors int det_num_a; int det_num_b; int ring_a; int ring_b; - get_det_pair_for_bin(det_num_a, ring_a, - det_num_b, ring_b, bin); - + get_det_pair_for_bin(det_num_a, ring_a, det_num_b, ring_b, bin); + // find corresponding cartesian coordinates - find_cartesian_coordinates_given_scanner_coordinates(coord_1,coord_2, - ring_a,ring_b,det_num_a,det_num_b); + find_cartesian_coordinates_given_scanner_coordinates(coord_1, coord_2, ring_a, ring_b, det_num_a, det_num_b); } - void -ProjDataInfoCylindricalNoArcCorr:: -find_cartesian_coordinates_given_scanner_coordinates (CartesianCoordinate3D& coord_1, - CartesianCoordinate3D& coord_2, - const int Ring_A,const int Ring_B, - const int det1, const int det2) const -{ - const int num_detectors_per_ring = - get_scanner_ptr()->get_num_detectors_per_ring(); +ProjDataInfoCylindricalNoArcCorr::find_cartesian_coordinates_given_scanner_coordinates(CartesianCoordinate3D& coord_1, + CartesianCoordinate3D& coord_2, + const int Ring_A, const int Ring_B, + const int det1, const int det2) const { + const int num_detectors_per_ring = get_scanner_ptr()->get_num_detectors_per_ring(); int d1, d2, r1, r2; this->initialise_det1det2_to_uncompressed_view_tangpos_if_not_done_yet(); - if (!det1det2_to_uncompressed_view_tangpos[det1][det2].swap_detectors) - { - d1 = det2; - d2 = det1; - r1 = Ring_B; - r2 = Ring_A; - } - else - { - d1 = det1; - d2 = det2; - r1 = Ring_A; - r2 = Ring_B; + if (!det1det2_to_uncompressed_view_tangpos[det1][det2].swap_detectors) { + d1 = det2; + d2 = det1; + r1 = Ring_B; + r2 = Ring_A; + } else { + d1 = det1; + d2 = det2; + r1 = Ring_A; + r2 = Ring_B; } #if 0 @@ -522,175 +432,141 @@ find_cartesian_coordinates_given_scanner_coordinates (CartesianCoordinate3D cyl_coords(get_scanner_ptr()->get_effective_ring_radius()); - cyl_coords.p1().psi() = static_cast((2.*_PI/num_detectors_per_ring)*(d1)); - cyl_coords.p2().psi() = static_cast((2.*_PI/num_detectors_per_ring)*(d2)); - cyl_coords.p1().z() = r1*get_scanner_ptr()->get_ring_spacing(); - cyl_coords.p2().z() = r2*get_scanner_ptr()->get_ring_spacing(); - LORAs2Points lor(cyl_coords); + cyl_coords.p1().psi() = static_cast((2. * _PI / num_detectors_per_ring) * (d1)); + cyl_coords.p2().psi() = static_cast((2. * _PI / num_detectors_per_ring) * (d2)); + cyl_coords.p1().z() = r1 * get_scanner_ptr()->get_ring_spacing(); + cyl_coords.p2().z() = r2 * get_scanner_ptr()->get_ring_spacing(); + LORAs2Points lor(cyl_coords); coord_1 = lor.p1(); coord_2 = lor.p2(); - + #endif } - //! \obsolete I don't see any reason why to keep having this function. void -ProjDataInfoCylindricalNoArcCorr:: -find_cartesian_coordinates_given_scanner_coordinates_of_the_front_surface ( - CartesianCoordinate3D& coord_1, - CartesianCoordinate3D& coord_2, - const int Ring_A,const int Ring_B, - const int det1, const int det2) const -{ - const int num_detectors_per_ring = - get_scanner_ptr()->get_num_detectors_per_ring(); - -// float h_scanner_height = ( (get_scanner_ptr()->get_ring_spacing() -1) * get_scanner_ptr()->get_num_rings())/2.F; - - // although code maybe doesn't really need the following, - // asserts in the LOR code will break if these conditions are not satisfied. - assert(0<=det1); - assert(det1 cyl_coords(get_scanner_ptr()->get_inner_ring_radius()); - - cyl_coords.p1().psi() = static_cast((2.*_PI/num_detectors_per_ring)*(det1)); - cyl_coords.p2().psi() = static_cast((2.*_PI/num_detectors_per_ring)*(det2)); - -// cyl_coords.p1().z() = Ring_A*get_scanner_ptr()->get_ring_spacing() - h_scanner_height; -// cyl_coords.p2().z() = Ring_B*get_scanner_ptr()->get_ring_spacing() - h_scanner_height; - - LORAs2Points lor(cyl_coords); - coord_1 = lor.p1(); - coord_2 = lor.p2(); -} +ProjDataInfoCylindricalNoArcCorr::find_cartesian_coordinates_given_scanner_coordinates_of_the_front_surface( + CartesianCoordinate3D& coord_1, CartesianCoordinate3D& coord_2, const int Ring_A, const int Ring_B, + const int det1, const int det2) const { + const int num_detectors_per_ring = get_scanner_ptr()->get_num_detectors_per_ring(); + // float h_scanner_height = ( (get_scanner_ptr()->get_ring_spacing() -1) * get_scanner_ptr()->get_num_rings())/2.F; -void -ProjDataInfoCylindricalNoArcCorr:: -find_bin_given_cartesian_coordinates_of_detection(Bin& bin, - const CartesianCoordinate3D& coord_1, - const CartesianCoordinate3D& coord_2) const -{ + // although code maybe doesn't really need the following, + // asserts in the LOR code will break if these conditions are not satisfied. + assert(0 <= det1); + assert(det1 < num_detectors_per_ring); + assert(0 <= det2); + assert(det2 < num_detectors_per_ring); + + LORInCylinderCoordinates cyl_coords(get_scanner_ptr()->get_inner_ring_radius()); + + cyl_coords.p1().psi() = static_cast((2. * _PI / num_detectors_per_ring) * (det1)); + cyl_coords.p2().psi() = static_cast((2. * _PI / num_detectors_per_ring) * (det2)); + + // cyl_coords.p1().z() = Ring_A*get_scanner_ptr()->get_ring_spacing() - h_scanner_height; + // cyl_coords.p2().z() = Ring_B*get_scanner_ptr()->get_ring_spacing() - h_scanner_height; + + LORAs2Points lor(cyl_coords); + coord_1 = lor.p1(); + coord_2 = lor.p2(); +} + +void +ProjDataInfoCylindricalNoArcCorr::find_bin_given_cartesian_coordinates_of_detection( + Bin& bin, const CartesianCoordinate3D& coord_1, const CartesianCoordinate3D& coord_2) const { int det_num_a; int det_num_b; int ring_a; int ring_b; - - // given two CartesianCoordinates find the intersection - if (find_scanner_coordinates_given_cartesian_coordinates(det_num_a,det_num_b, - ring_a, ring_b, - coord_1, - coord_2) == - Succeeded::no) - { + + // given two CartesianCoordinates find the intersection + if (find_scanner_coordinates_given_cartesian_coordinates(det_num_a, det_num_b, ring_a, ring_b, coord_1, coord_2) == + Succeeded::no) { bin.set_bin_value(-1); return; } // check rings are in valid range // this should have been done by find_scanner_coordinates_given_cartesian_coordinates - assert(!(ring_a<0 || - ring_a>=get_scanner_ptr()->get_num_rings() || - ring_b<0 || - ring_b>=get_scanner_ptr()->get_num_rings())); - - if (get_bin_for_det_pair(bin, - det_num_a, ring_a, - det_num_b, ring_b) == Succeeded::no || - bin.tangential_pos_num() < get_min_tangential_pos_num() || - bin.tangential_pos_num() > get_max_tangential_pos_num()) + assert(!(ring_a < 0 || ring_a >= get_scanner_ptr()->get_num_rings() || ring_b < 0 || + ring_b >= get_scanner_ptr()->get_num_rings())); + + if (get_bin_for_det_pair(bin, det_num_a, ring_a, det_num_b, ring_b) == Succeeded::no || + bin.tangential_pos_num() < get_min_tangential_pos_num() || bin.tangential_pos_num() > get_max_tangential_pos_num()) bin.set_bin_value(-1); } Bin -ProjDataInfoCylindricalNoArcCorr:: -get_bin(const LOR& lor,const double delta_time) const -{ +ProjDataInfoCylindricalNoArcCorr::get_bin(const LOR& lor, const double delta_time) const { Bin bin; #ifndef STIR_DEVEL // find nearest bin by going to nearest detectors first LORInCylinderCoordinates cyl_coords; - if (lor.change_representation(cyl_coords, get_ring_radius()) == Succeeded::no) - { - bin.set_bin_value(-1); - return bin; - } - const int num_detectors_per_ring = - get_scanner_ptr()->get_num_detectors_per_ring(); - const int num_rings = - get_scanner_ptr()->get_num_rings(); + if (lor.change_representation(cyl_coords, get_ring_radius()) == Succeeded::no) { + bin.set_bin_value(-1); + return bin; + } + const int num_detectors_per_ring = get_scanner_ptr()->get_num_detectors_per_ring(); + const int num_rings = get_scanner_ptr()->get_num_rings(); - const int det1 = modulo(round(cyl_coords.p1().psi()/(2.*_PI/num_detectors_per_ring)),num_detectors_per_ring); - const int det2 = modulo(round(cyl_coords.p2().psi()/(2.*_PI/num_detectors_per_ring)),num_detectors_per_ring); + const int det1 = modulo(round(cyl_coords.p1().psi() / (2. * _PI / num_detectors_per_ring)), num_detectors_per_ring); + const int det2 = modulo(round(cyl_coords.p2().psi() / (2. * _PI / num_detectors_per_ring)), num_detectors_per_ring); // TODO WARNING LOR coordinates are w.r.t. centre of scanner, but the rings are numbered with the first ring at 0 - const int ring1 = round(cyl_coords.p1().z()/get_ring_spacing() + (num_rings-1)/2.F); - const int ring2 = round(cyl_coords.p2().z()/get_ring_spacing() + (num_rings-1)/2.F); - - assert(det1 >=0 && det1=0 && det2=0 && ring1=0 && ring2= get_min_tangential_pos_num() && - bin.tangential_pos_num() <= get_max_tangential_pos_num()) - { - bin.set_bin_value(1); - return bin; - } - else - { - bin.set_bin_value(-1); - return bin; - } - + const int ring1 = round(cyl_coords.p1().z() / get_ring_spacing() + (num_rings - 1) / 2.F); + const int ring2 = round(cyl_coords.p2().z() / get_ring_spacing() + (num_rings - 1) / 2.F); + + assert(det1 >= 0 && det1 < num_detectors_per_ring); + assert(det2 >= 0 && det2 < num_detectors_per_ring); + + if (ring1 >= 0 && ring1 < num_rings && ring2 >= 0 && ring2 < num_rings && + get_bin_for_det_pair(bin, det1, ring1, det2, ring2, (cyl_coords.is_swapped() ? -1 : 1) * get_tof_bin(delta_time)) == + Succeeded::yes && + bin.tangential_pos_num() >= get_min_tangential_pos_num() && bin.tangential_pos_num() <= get_max_tangential_pos_num()) { + bin.set_bin_value(1); + return bin; + } else { + bin.set_bin_value(-1); + return bin; + } #else LORInAxialAndNoArcCorrSinogramCoordinates lor_coords; - if (lor.change_representation(lor_coords, get_ring_radius()) == Succeeded::no) - { - bin.set_bin_value(-1); - return bin; - } + if (lor.change_representation(lor_coords, get_ring_radius()) == Succeeded::no) { + bin.set_bin_value(-1); + return bin; + } - // first find view + // first find view // unfortunately, phi ranges from [0,Pi[, but the rounding can // map this to a view which corresponds to Pi anyway. bin.view_num() = round(lor_coords.phi() / get_azimuthal_angle_sampling()); - assert(bin.view_num()>=0); - assert(bin.view_num()<=get_num_views()); - const bool swap_direction = - bin.view_num() > get_max_view_num(); + assert(bin.view_num() >= 0); + assert(bin.view_num() <= get_num_views()); + const bool swap_direction = bin.view_num() > get_max_view_num(); if (swap_direction) - bin.view_num()-=get_num_views(); + bin.view_num() -= get_num_views(); bin.tangential_pos_num() = round(lor_coords.beta() / angular_increment); if (swap_direction) bin.tangential_pos_num() *= -1; - if (bin.tangential_pos_num() < get_min_tangential_pos_num() || - bin.tangential_pos_num() > get_max_tangential_pos_num()) - { - bin.set_bin_value(-1); - return bin; - } + if (bin.tangential_pos_num() < get_min_tangential_pos_num() || bin.tangential_pos_num() > get_max_tangential_pos_num()) { + bin.set_bin_value(-1); + return bin; + } -#if 0 +# if 0 const int num_rings = get_scanner_ptr()->get_num_rings(); // TODO WARNING LOR coordinates are w.r.t. centre of scanner, but the rings are numbered with the first ring at 0 @@ -717,79 +593,64 @@ get_bin(const LOR& lor,const double delta_time) const bin.set_bin_value(-1); return bin; } -#else +# else // find nearest segment { - if (delta_time!=0) - { - error("TODO TOF"); - } + if (delta_time != 0) { + error("TODO TOF"); + } const float delta = - (swap_direction - ? lor_coords.z1()-lor_coords.z2() - : lor_coords.z2()-lor_coords.z1() - )/get_ring_spacing(); + (swap_direction ? lor_coords.z1() - lor_coords.z2() : lor_coords.z2() - lor_coords.z1()) / get_ring_spacing(); // check if out of acquired range // note the +1 or -1, which takes the size of the rings into account - if (delta>get_max_ring_difference(get_max_segment_num())+1 || - delta=0) - { - for (bin.segment_num()=0; bin.segment_num() get_max_ring_difference(get_max_segment_num()) + 1 || + delta < get_min_ring_difference(get_min_segment_num()) - 1) { + bin.set_bin_value(-1); + return bin; + } + if (delta >= 0) { + for (bin.segment_num() = 0; bin.segment_num() < get_max_segment_num(); ++bin.segment_num()) { + if (delta < get_max_ring_difference(bin.segment_num()) + .5) + break; } - else - { - // delta<0 - for (bin.segment_num()=0; bin.segment_num()>get_min_segment_num(); --bin.segment_num()) - { - if (delta > get_min_ring_difference(bin.segment_num())-.5) - break; - } + } else { + // delta<0 + for (bin.segment_num() = 0; bin.segment_num() > get_min_segment_num(); --bin.segment_num()) { + if (delta > get_min_ring_difference(bin.segment_num()) - .5) + break; } + } } // now find nearest axial position { - const float m = (lor_coords.z2()+lor_coords.z1())/2; -#if 0 + const float m = (lor_coords.z2() + lor_coords.z1()) / 2; +# if 0 // this uses private member of ProjDataInfoCylindrical // enable when moved initialise_ring_diff_arrays_if_not_done_yet(); -#ifndef NDEBUG +# ifndef NDEBUG bin.axial_pos_num()=0; assert(get_m(bin)==- m_offset[bin.segment_num()]); -#endif +# endif bin.axial_pos_num() = round((m + m_offset[bin.segment_num()])/ get_axial_sampling(bin.segment_num())); -#else - bin.axial_pos_num()=0; - bin.axial_pos_num() = - round((m - get_m(bin))/ - get_axial_sampling(bin.segment_num())); -#endif +# else + bin.axial_pos_num() = 0; + bin.axial_pos_num() = round((m - get_m(bin)) / get_axial_sampling(bin.segment_num())); +# endif if (bin.axial_pos_num() < get_min_axial_pos_num(bin.segment_num()) || - bin.axial_pos_num() > get_max_axial_pos_num(bin.segment_num())) - { - bin.set_bin_value(-1); - return bin; - } + bin.axial_pos_num() > get_max_axial_pos_num(bin.segment_num())) { + bin.set_bin_value(-1); + return bin; + } } -#endif +# endif bin.set_bin_value(1); return bin; #endif } - END_NAMESPACE_STIR - diff --git a/src/buildblock/ProjDataInterfile.cxx b/src/buildblock/ProjDataInterfile.cxx index 058ab9a1b1..d853f455a6 100644 --- a/src/buildblock/ProjDataInterfile.cxx +++ b/src/buildblock/ProjDataInterfile.cxx @@ -24,7 +24,7 @@ See STIR/LICENSE.txt for details */ -#include "stir/ProjDataInterfile.h" +#include "stir/ProjDataInterfile.h" #include "stir/utilities.h" #include "stir/IO/interfile.h" @@ -42,42 +42,36 @@ using std::ios; START_NAMESPACE_STIR - void -ProjDataInterfile :: -create_stream(const string& filename, const ios::openmode open_mode) -{ +ProjDataInterfile ::create_stream(const string& filename, const ios::openmode open_mode) { #if 1 - string data_name=filename; + string data_name = filename; { - string::size_type pos=find_pos_of_extension(filename); - if (pos!=string::npos && filename.substr(pos)==".hs") + string::size_type pos = find_pos_of_extension(filename); + if (pos != string::npos && filename.substr(pos) == ".hs") replace_extension(data_name, ".s"); else add_extension(data_name, ".s"); } - string header_name=filename; + string header_name = filename; #else - char * data_name = new char[filename.size() + 5]; + char* data_name = new char[filename.size() + 5]; { strcpy(data_name, filename.c_str()); - const char * const extension = strchr(find_filename(data_name),'.'); - if (extension!=NULL && strcmp(extension, ".hs")==0) + const char* const extension = strchr(find_filename(data_name), '.'); + if (extension != NULL && strcmp(extension, ".hs") == 0) replace_extension(data_name, ".s"); else add_extension(data_name, ".s"); } - char * header_name = new char[filename.size() + 5]; + char* header_name = new char[filename.size() + 5]; strcpy(header_name, data_name); #endif replace_extension(header_name, ".hs"); - write_basic_interfile_PDFS_header(header_name, data_name, - *this); + write_basic_interfile_PDFS_header(header_name, data_name, *this); - sino_stream.reset( - new fstream (data_name.c_str(), open_mode|ios::binary)); - if (!sino_stream->good()) - { + sino_stream.reset(new fstream(data_name.c_str(), open_mode | ios::binary)); + if (!sino_stream->good()) { error("ProjDataInterfile: error opening output file %s\n", data_name.c_str()); } #if 0 @@ -86,35 +80,21 @@ create_stream(const string& filename, const ios::openmode open_mode) #endif } -ProjDataInterfile :: -ProjDataInterfile (shared_ptr const& exam_info_sptr, - shared_ptr const& proj_data_info_ptr, - const string& filename, const ios::openmode open_mode, - const vector& segment_sequence_in_stream, - StorageOrder o, - NumericType data_type, - ByteOrder byte_order, - float scale_factor) - : ProjDataFromStream(exam_info_sptr, proj_data_info_ptr, shared_ptr(), 0, - segment_sequence_in_stream, o, data_type, byte_order, scale_factor) -{ +ProjDataInterfile ::ProjDataInterfile(shared_ptr const& exam_info_sptr, + shared_ptr const& proj_data_info_ptr, const string& filename, + const ios::openmode open_mode, const vector& segment_sequence_in_stream, + StorageOrder o, NumericType data_type, ByteOrder byte_order, float scale_factor) + : ProjDataFromStream(exam_info_sptr, proj_data_info_ptr, shared_ptr(), 0, segment_sequence_in_stream, o, data_type, + byte_order, scale_factor) { create_stream(filename, open_mode); } -ProjDataInterfile :: -ProjDataInterfile (shared_ptr const& exam_info_sptr, - shared_ptr const& proj_data_info_ptr, - const string& filename, const ios::openmode open_mode, - StorageOrder o, - NumericType data_type, - ByteOrder byte_order, - float scale_factor ) - : ProjDataFromStream(exam_info_sptr, proj_data_info_ptr, shared_ptr(), 0, - o, data_type, byte_order, scale_factor) -{ +ProjDataInterfile ::ProjDataInterfile(shared_ptr const& exam_info_sptr, + shared_ptr const& proj_data_info_ptr, const string& filename, + const ios::openmode open_mode, StorageOrder o, NumericType data_type, ByteOrder byte_order, + float scale_factor) + : ProjDataFromStream(exam_info_sptr, proj_data_info_ptr, shared_ptr(), 0, o, data_type, byte_order, scale_factor) { create_stream(filename, open_mode); } - - END_NAMESPACE_STIR diff --git a/src/buildblock/RegisteredObject.cxx b/src/buildblock/RegisteredObject.cxx index 147cd44177..f0130edf33 100644 --- a/src/buildblock/RegisteredObject.cxx +++ b/src/buildblock/RegisteredObject.cxx @@ -33,23 +33,22 @@ #include "stir/RegisteredObject.h" #ifdef __STIR_REGISTRY_NOT_INLINE -#pragma message("instantiating RegisteredObject > >") -#include "stir/DataProcessor.h" -#include "stir/DiscretisedDensity.h" +# pragma message("instantiating RegisteredObject > >") +# include "stir/DataProcessor.h" +# include "stir/DiscretisedDensity.h" // add here all roots of hierarchies based on RegisteredObject START_NAMESPACE_STIR template -RegisteredObject::RegistryType& -RegisteredObject::registry () -{ +RegisteredObject::RegistryType& +RegisteredObject::registry() { static RegistryType the_registry("None", 0); return the_registry; } -template RegisteredObject > >; +template RegisteredObject>>; // add here all roots of hierarchies based on RegisteredObject END_NAMESPACE_STIR diff --git a/src/buildblock/RelatedViewgrams.cxx b/src/buildblock/RelatedViewgrams.cxx index 745d3a02e3..15658fb573 100644 --- a/src/buildblock/RelatedViewgrams.cxx +++ b/src/buildblock/RelatedViewgrams.cxx @@ -35,7 +35,7 @@ #ifdef _MSC_VER // disable warning that constructor with PMessage is not implemented -#pragma warning(disable: 4661) +# pragma warning(disable : 4661) #endif // _MSC_VER using std::string; @@ -43,11 +43,10 @@ using std::vector; START_NAMESPACE_STIR - // a function which is called internally to see if the object is valid template -void RelatedViewgrams::debug_check_state() const -{ +void +RelatedViewgrams::debug_check_state() const { // KT 09/03/99 can't use any methods of RelatedViewgrams here, as // this causes an infinite recursion with check_state if (viewgrams.size() == 0) @@ -55,148 +54,99 @@ void RelatedViewgrams::debug_check_state() const vector pairs; symmetries_used->get_related_view_segment_numbers( - pairs, - ViewSegmentNumbers( - viewgrams[0].get_view_num(), - viewgrams[0].get_segment_num() - ) ); + pairs, ViewSegmentNumbers(viewgrams[0].get_view_num(), viewgrams[0].get_segment_num())); assert(pairs.size() == viewgrams.size()); - for (unsigned int i=0; i -RelatedViewgrams RelatedViewgrams::get_empty_copy() const -{ +RelatedViewgrams +RelatedViewgrams::get_empty_copy() const { check_state(); - vector > empty_viewgrams; + vector> empty_viewgrams; empty_viewgrams.reserve(viewgrams.size()); // TODO optimise to get shared proj_data_info_ptr - for (unsigned int i=0; i(empty_viewgrams, - symmetries_used); + return RelatedViewgrams(empty_viewgrams, symmetries_used); } -template +template bool -RelatedViewgrams:: -has_same_characteristics(self_type const& other, - string& explanation) const -{ +RelatedViewgrams::has_same_characteristics(self_type const& other, string& explanation) const { using boost::format; using boost::str; - if (*this->get_proj_data_info_sptr() != - *other.get_proj_data_info_sptr()) - { - explanation = - str(format("Differing projection data info:\n%1%\n-------- vs-------\n %2%") - % this->get_proj_data_info_sptr()->parameter_info() - % other.get_proj_data_info_sptr()->parameter_info() - ); - return false; - } - if (*this->get_symmetries_ptr() != - *other.get_symmetries_ptr()) - { - explanation = - str(format("Differing symmetries") - ); - return false; - } - if (this->get_basic_view_num() != - other.get_basic_view_num()) - { - explanation = - str(format("Differing basic view number: %1% vs %2%") - % this->get_basic_view_num() - % other.get_basic_view_num() - ); - return false; - } - if (this->get_basic_segment_num() != - other.get_basic_segment_num()) - { - explanation = - str(format("Differing basic segment number: %1% vs %2%") - % this->get_basic_segment_num() - % other.get_basic_segment_num() - ); - return false; - } - if (this->get_basic_timing_pos_num() != - other.get_basic_timing_pos_num()) - { - explanation = - str(format("Differing basic timing position index: %1% vs %2%") - % this->get_basic_timing_pos_num() - % other.get_basic_timing_pos_num() - ); - return false; - } + if (*this->get_proj_data_info_sptr() != *other.get_proj_data_info_sptr()) { + explanation = str(format("Differing projection data info:\n%1%\n-------- vs-------\n %2%") % + this->get_proj_data_info_sptr()->parameter_info() % other.get_proj_data_info_sptr()->parameter_info()); + return false; + } + if (*this->get_symmetries_ptr() != *other.get_symmetries_ptr()) { + explanation = str(format("Differing symmetries")); + return false; + } + if (this->get_basic_view_num() != other.get_basic_view_num()) { + explanation = + str(format("Differing basic view number: %1% vs %2%") % this->get_basic_view_num() % other.get_basic_view_num()); + return false; + } + if (this->get_basic_segment_num() != other.get_basic_segment_num()) { + explanation = + str(format("Differing basic segment number: %1% vs %2%") % this->get_basic_segment_num() % other.get_basic_segment_num()); + return false; + } + if (this->get_basic_timing_pos_num() != other.get_basic_timing_pos_num()) { + explanation = str(format("Differing basic timing position index: %1% vs %2%") % this->get_basic_timing_pos_num() % + other.get_basic_timing_pos_num()); + return false; + } return true; } -template +template bool -RelatedViewgrams:: -has_same_characteristics(self_type const& other) const -{ +RelatedViewgrams::has_same_characteristics(self_type const& other) const { std::string explanation; return this->has_same_characteristics(other, explanation); } -template -bool -RelatedViewgrams:: -operator ==(const self_type& that) const -{ - return - this->has_same_characteristics(that) && - std::equal(this->begin(), this->end(), that.begin()); +template +bool +RelatedViewgrams::operator==(const self_type& that) const { + return this->has_same_characteristics(that) && std::equal(this->begin(), this->end(), that.begin()); } - -template -bool -RelatedViewgrams:: -operator !=(const self_type& that) const -{ + +template +bool +RelatedViewgrams::operator!=(const self_type& that) const { return !((*this) == that); } /*! \warning: this uses multiplication according to elemT (careful for overflow for integer types!) */ template -RelatedViewgrams& -RelatedViewgrams:: -operator*= (const elemT f) -{ +RelatedViewgrams& +RelatedViewgrams::operator*=(const elemT f) { for (iterator iter = begin(); iter != end(); ++iter) *iter *= f; return *this; } /*! \warning: this uses division according to elemT (i.e. no rounding or so) */ -template +template RelatedViewgrams& -RelatedViewgrams:: -operator/= (const elemT f) -{ - assert(f!=0); +RelatedViewgrams::operator/=(const elemT f) { + assert(f != 0); for (iterator iter = begin(); iter != end(); ++iter) *iter /= f; @@ -205,10 +155,8 @@ operator/= (const elemT f) /*! \warning: this uses addition according to elemT (careful with overflow with integer types!) */ template -RelatedViewgrams& -RelatedViewgrams:: -operator+= (const elemT f) -{ +RelatedViewgrams& +RelatedViewgrams::operator+=(const elemT f) { for (iterator iter = begin(); iter != end(); ++iter) *iter += f; return *this; @@ -216,148 +164,124 @@ operator+= (const elemT f) /*! \warning: this uses subtraction according to elemT (careful with unsigned types!) */ template -RelatedViewgrams& -RelatedViewgrams:: -operator-= (const elemT f) -{ +RelatedViewgrams& +RelatedViewgrams::operator-=(const elemT f) { for (iterator iter = begin(); iter != end(); ++iter) *iter -= f; return *this; } - /*! \warning: this uses multiplication according to elemT (careful for overflow for integer types!) */ -template +template RelatedViewgrams& -RelatedViewgrams:: -operator*= (const RelatedViewgrams& arg) -{ +RelatedViewgrams::operator*=(const RelatedViewgrams& arg) { assert(get_num_viewgrams() == arg.get_num_viewgrams()); - iterator iter = begin(); + iterator iter = begin(); const_iterator arg_iter = arg.begin(); - for ( ; iter != end(); ++iter, ++arg_iter) + for (; iter != end(); ++iter, ++arg_iter) *iter *= *arg_iter; return *this; } /*! \warning: this uses division according to elemT (i.e. no rounding or so) */ -template +template RelatedViewgrams& -RelatedViewgrams:: -operator/= (const RelatedViewgrams& arg) -{ +RelatedViewgrams::operator/=(const RelatedViewgrams& arg) { assert(get_num_viewgrams() == arg.get_num_viewgrams()); - iterator iter = begin(); + iterator iter = begin(); const_iterator arg_iter = arg.begin(); - for ( ; iter != end(); ++iter, ++arg_iter) + for (; iter != end(); ++iter, ++arg_iter) *iter /= *arg_iter; return *this; } - /*! \warning: this uses addition according to elemT (careful with overflow with integer types!) */ -template +template RelatedViewgrams& -RelatedViewgrams:: -operator+= (const RelatedViewgrams& arg) -{ +RelatedViewgrams::operator+=(const RelatedViewgrams& arg) { assert(get_num_viewgrams() == arg.get_num_viewgrams()); - iterator iter = begin(); + iterator iter = begin(); const_iterator arg_iter = arg.begin(); - for ( ; iter != end(); ++iter, ++arg_iter) + for (; iter != end(); ++iter, ++arg_iter) *iter += *arg_iter; return *this; } /*! \warning: this uses subtraction according to elemT (careful with unsigned types!) */ -template +template RelatedViewgrams& -RelatedViewgrams:: -operator-= (const RelatedViewgrams& arg) -{ +RelatedViewgrams::operator-=(const RelatedViewgrams& arg) { assert(get_num_viewgrams() == arg.get_num_viewgrams()); - iterator iter = begin(); + iterator iter = begin(); const_iterator arg_iter = arg.begin(); - for ( ; iter != end(); ++iter, ++arg_iter) + for (; iter != end(); ++iter, ++arg_iter) *iter -= *arg_iter; return *this; } - - template -elemT -RelatedViewgrams:: -find_max() const -{ - Array<1,elemT> max_per_viewgram(get_num_viewgrams()); - typename Array<1,elemT>::iterator max_iter = max_per_viewgram.begin(); - const_iterator iter = begin(); - while (iter != end()) - { +elemT +RelatedViewgrams::find_max() const { + Array<1, elemT> max_per_viewgram(get_num_viewgrams()); + typename Array<1, elemT>::iterator max_iter = max_per_viewgram.begin(); + const_iterator iter = begin(); + while (iter != end()) { *max_iter = iter->find_max(); - ++iter; ++ max_iter; + ++iter; + ++max_iter; } return max_per_viewgram.find_max(); } template -elemT -RelatedViewgrams:: -find_min() const -{ - Array<1,elemT> min_per_viewgram(get_num_viewgrams()); - typename Array<1,elemT>::iterator min_iter = min_per_viewgram.begin(); - const_iterator iter = begin(); - while (iter != end()) - { +elemT +RelatedViewgrams::find_min() const { + Array<1, elemT> min_per_viewgram(get_num_viewgrams()); + typename Array<1, elemT>::iterator min_iter = min_per_viewgram.begin(); + const_iterator iter = begin(); + while (iter != end()) { *min_iter = iter->find_min(); - ++iter; ++ min_iter; + ++iter; + ++min_iter; } return min_per_viewgram.find_min(); } - template -void -RelatedViewgrams::fill(const elemT &n) -{ - for (iterator iter = begin(); iter != end(); ++iter) +void +RelatedViewgrams::fill(const elemT& n) { + for (iterator iter = begin(); iter != end(); ++iter) iter->fill(n); } -/*! +/*! This function is necessary because it modifies the size of - each viewgram sequentially. This is not allowed by an external + each viewgram sequentially. This is not allowed by an external function, and leads to different proj_data_info_ptrs anyway. So, it would be caught by an assert at some point. */ template -void RelatedViewgrams:: -grow(const IndexRange<2>& range) -{ +void +RelatedViewgrams::grow(const IndexRange<2>& range) { check_state(); - if (begin()==end()) + if (begin() == end()) return; if (range == begin()->get_index_range()) return; - assert(range.is_regular()==true); + assert(range.is_regular() == true); // first construct a new appropriate ProjDataInfo object const int ax_min = range.get_min_index(); const int ax_max = range.get_max_index(); - + shared_ptr pdi_ptr(get_proj_data_info_sptr()->clone()); // set axial_pos range for all segments - for (const_iterator iter= begin(); - iter != end(); - ++iter) - { + for (const_iterator iter = begin(); iter != end(); ++iter) { pdi_ptr->set_min_axial_pos_num(ax_min, iter->get_segment_num()); pdi_ptr->set_max_axial_pos_num(ax_max, iter->get_segment_num()); } @@ -365,21 +289,17 @@ grow(const IndexRange<2>& range) pdi_ptr->set_max_tangential_pos_num(range[ax_min].get_max_index()); shared_ptr pdi_shared_ptr = pdi_ptr; - // now resize each viewgram + // now resize each viewgram // this will not set their respective proj_data_info_ptr correctly though, // so, we have to construct new viewgrams for this - for (iterator iter= begin(); - iter != end(); - ++iter) - { + for (iterator iter = begin(); iter != end(); ++iter) { iter->grow(range); - *iter = Viewgram(*iter, pdi_shared_ptr, - iter->get_view_num(), iter->get_segment_num(),iter->get_timing_pos_num()); + *iter = Viewgram(*iter, pdi_shared_ptr, iter->get_view_num(), iter->get_segment_num(), iter->get_timing_pos_num()); } check_state(); } -/* +/* TODO #include "stir/zoom.h" @@ -399,13 +319,13 @@ void RelatedViewgrams::zoom(const float zoom, const float Xoffp, const fl */ /* template -void RelatedViewgrams::grow_num_bins(const int new_min_bin_num, - const int new_max_bin_num) +void RelatedViewgrams::grow_num_bins(const int new_min_bin_num, + const int new_max_bin_num) { for (vector::iterator iter= viewgrams.begin(); iter != viewgrams.end(); iter++) - (*iter).grow_width(new_min_bin_num, new_max_bin_num); + (*iter).grow_width(new_min_bin_num, new_max_bin_num); } */ diff --git a/src/buildblock/SSRB.cxx b/src/buildblock/SSRB.cxx index 7110443478..1c1447a7a1 100644 --- a/src/buildblock/SSRB.cxx +++ b/src/buildblock/SSRB.cxx @@ -46,296 +46,198 @@ START_NAMESPACE_STIR // TODO this function needs work to reliable handle segments with unequal 'num_segments_to_combine' (as GE Advance) // parts with 'num_segments_to_combine' should be revised, all the rest is fine -ProjDataInfo * -SSRB(const ProjDataInfo& in_proj_data_info, - const int num_segments_to_combine, - const int num_views_to_combine, - const int num_tang_poss_to_trim, - const int max_in_segment_num_to_process_argument, - const int num_tof_bins_to_combine - ) -{ - if (num_tof_bins_to_combine!=1) - error("SSRB: num_tof_bins_to_combine (%d) currently needs to be 1", - num_tof_bins_to_combine); - if (num_segments_to_combine%2==0) - error("SSRB: num_segments_to_combine (%d) needs to be odd\n", - num_segments_to_combine); - const int max_in_segment_num_to_process = - max_in_segment_num_to_process_argument >= 0 - ? max_in_segment_num_to_process_argument - : in_proj_data_info.get_max_segment_num(); +ProjDataInfo* +SSRB(const ProjDataInfo& in_proj_data_info, const int num_segments_to_combine, const int num_views_to_combine, + const int num_tang_poss_to_trim, const int max_in_segment_num_to_process_argument, const int num_tof_bins_to_combine) { + if (num_tof_bins_to_combine != 1) + error("SSRB: num_tof_bins_to_combine (%d) currently needs to be 1", num_tof_bins_to_combine); + if (num_segments_to_combine % 2 == 0) + error("SSRB: num_segments_to_combine (%d) needs to be odd\n", num_segments_to_combine); + const int max_in_segment_num_to_process = max_in_segment_num_to_process_argument >= 0 ? max_in_segment_num_to_process_argument + : in_proj_data_info.get_max_segment_num(); if (in_proj_data_info.get_max_segment_num() < max_in_segment_num_to_process) error("SSRB: max_in_segment_num_to_process (%d) is too large\n" - "Input data has maximum segment number %d.", - max_in_segment_num_to_process, - in_proj_data_info.get_max_segment_num()); - if (in_proj_data_info.get_num_tangential_poss() <= - num_tang_poss_to_trim) - error("SSRB: too large number of tangential positions to trim (%d)\n", - num_tang_poss_to_trim); - const ProjDataInfoCylindrical * const in_proj_data_info_sptr = - dynamic_cast - (&in_proj_data_info); - if (in_proj_data_info_sptr== NULL) - { - error("SSRB works only on segments with proj_data_info of " - "type ProjDataInfoCylindrical\n"); - } - ProjDataInfoCylindrical * out_proj_data_info_sptr = - dynamic_cast - (in_proj_data_info_sptr->clone()); - - out_proj_data_info_sptr-> - set_num_views( - in_proj_data_info.get_num_views()/ - num_views_to_combine); - out_proj_data_info_sptr-> - set_num_tangential_poss(in_proj_data_info.get_num_tangential_poss() - - num_tang_poss_to_trim); + "Input data has maximum segment number %d.", + max_in_segment_num_to_process, in_proj_data_info.get_max_segment_num()); + if (in_proj_data_info.get_num_tangential_poss() <= num_tang_poss_to_trim) + error("SSRB: too large number of tangential positions to trim (%d)\n", num_tang_poss_to_trim); + const ProjDataInfoCylindrical* const in_proj_data_info_sptr = dynamic_cast(&in_proj_data_info); + if (in_proj_data_info_sptr == NULL) { + error("SSRB works only on segments with proj_data_info of " + "type ProjDataInfoCylindrical\n"); + } + ProjDataInfoCylindrical* out_proj_data_info_sptr = dynamic_cast(in_proj_data_info_sptr->clone()); + + out_proj_data_info_sptr->set_num_views(in_proj_data_info.get_num_views() / num_views_to_combine); + out_proj_data_info_sptr->set_num_tangential_poss(in_proj_data_info.get_num_tangential_poss() - num_tang_poss_to_trim); // Find new maximum segment_num - // To understand this formula, check how the out_segment_num is related to + // To understand this formula, check how the out_segment_num is related to // the in_segment_num below - const int out_max_segment_num = - ((max_in_segment_num_to_process == -1 - ? in_proj_data_info.get_max_segment_num() - : max_in_segment_num_to_process - )-(num_segments_to_combine/2))/num_segments_to_combine; - if (out_max_segment_num <0) - error("SSRB: max_in_segment_num_to_process %d is too small. No output segments\n", - max_in_segment_num_to_process); + const int out_max_segment_num = + ((max_in_segment_num_to_process == -1 ? in_proj_data_info.get_max_segment_num() : max_in_segment_num_to_process) - + (num_segments_to_combine / 2)) / + num_segments_to_combine; + if (out_max_segment_num < 0) + error("SSRB: max_in_segment_num_to_process %d is too small. No output segments\n", max_in_segment_num_to_process); const int in_axial_compression = - in_proj_data_info_sptr->get_max_ring_difference(0) - - in_proj_data_info_sptr->get_min_ring_difference(0) - + 1; - - out_proj_data_info_sptr->reduce_segment_range(-out_max_segment_num,out_max_segment_num); - for (int out_segment_num = -out_max_segment_num; - out_segment_num <= out_max_segment_num; - ++out_segment_num) + in_proj_data_info_sptr->get_max_ring_difference(0) - in_proj_data_info_sptr->get_min_ring_difference(0) + 1; + + out_proj_data_info_sptr->reduce_segment_range(-out_max_segment_num, out_max_segment_num); + for (int out_segment_num = -out_max_segment_num; out_segment_num <= out_max_segment_num; ++out_segment_num) { + const int in_min_segment_num = out_segment_num * num_segments_to_combine - num_segments_to_combine / 2; + const int in_max_segment_num = out_segment_num * num_segments_to_combine + num_segments_to_combine / 2; + out_proj_data_info_sptr->set_min_ring_difference(in_proj_data_info_sptr->get_min_ring_difference(in_min_segment_num), + out_segment_num); + out_proj_data_info_sptr->set_max_ring_difference(in_proj_data_info_sptr->get_max_ring_difference(in_max_segment_num), + out_segment_num); + + out_proj_data_info_sptr->set_min_axial_pos_num(0, out_segment_num); + + // find number of axial_poss in out_segment + // get_m could be replaced by get_t { - const int in_min_segment_num = out_segment_num*num_segments_to_combine - num_segments_to_combine/2; - const int in_max_segment_num = out_segment_num*num_segments_to_combine + num_segments_to_combine/2; - out_proj_data_info_sptr-> - set_min_ring_difference(in_proj_data_info_sptr->get_min_ring_difference(in_min_segment_num), - out_segment_num); - out_proj_data_info_sptr-> - set_max_ring_difference(in_proj_data_info_sptr->get_max_ring_difference(in_max_segment_num), - out_segment_num); - - out_proj_data_info_sptr-> - set_min_axial_pos_num(0,out_segment_num); - - // find number of axial_poss in out_segment - // get_m could be replaced by get_t - { - float min_m =1.E37F; - float max_m =-1.E37F; - for (int in_segment_num = in_min_segment_num; - in_segment_num <= in_max_segment_num; - ++in_segment_num) - { - if (in_axial_compression != - (in_proj_data_info_sptr->get_max_ring_difference(in_segment_num) - - in_proj_data_info_sptr->get_min_ring_difference(in_segment_num) + - 1)) - warning("SSRB: in_proj_data_info with non-identical axial compression for all segments.\n" - "That's ok, but results might not be what you expect.\n"); - - min_m = - min(min_m, - in_proj_data_info_sptr-> - get_m(Bin(in_segment_num,0,in_proj_data_info_sptr->get_min_axial_pos_num(in_segment_num), 0))); - max_m = - max(max_m, - in_proj_data_info_sptr-> - get_m(Bin(in_segment_num,0,in_proj_data_info_sptr->get_max_axial_pos_num(in_segment_num), 0))); - } - const float number_of_ms = - (max_m - min_m)/out_proj_data_info_sptr->get_axial_sampling(out_segment_num)+1; - if (fabs(round(number_of_ms)-number_of_ms) > 1.E-3) - error("SSRB: number of axial positions to be found in out_segment %d is non-integer %g\n", - out_segment_num, number_of_ms); - out_proj_data_info_sptr-> - set_max_axial_pos_num(round(number_of_ms) - 1, - out_segment_num); + float min_m = 1.E37F; + float max_m = -1.E37F; + for (int in_segment_num = in_min_segment_num; in_segment_num <= in_max_segment_num; ++in_segment_num) { + if (in_axial_compression != (in_proj_data_info_sptr->get_max_ring_difference(in_segment_num) - + in_proj_data_info_sptr->get_min_ring_difference(in_segment_num) + 1)) + warning("SSRB: in_proj_data_info with non-identical axial compression for all segments.\n" + "That's ok, but results might not be what you expect.\n"); + + min_m = min(min_m, in_proj_data_info_sptr->get_m( + Bin(in_segment_num, 0, in_proj_data_info_sptr->get_min_axial_pos_num(in_segment_num), 0))); + max_m = max(max_m, in_proj_data_info_sptr->get_m( + Bin(in_segment_num, 0, in_proj_data_info_sptr->get_max_axial_pos_num(in_segment_num), 0))); } + const float number_of_ms = (max_m - min_m) / out_proj_data_info_sptr->get_axial_sampling(out_segment_num) + 1; + if (fabs(round(number_of_ms) - number_of_ms) > 1.E-3) + error("SSRB: number of axial positions to be found in out_segment %d is non-integer %g\n", out_segment_num, number_of_ms); + out_proj_data_info_sptr->set_max_axial_pos_num(round(number_of_ms) - 1, out_segment_num); } + } - if (num_tof_bins_to_combine!=1) - { - if (num_tof_bins_to_combine<1) - error("SSRB: num_tof_bins_to_combine needs to be at least 1"); - const int new_tof_mash_factor = in_proj_data_info_sptr->get_tof_mash_factor() * num_tof_bins_to_combine; - out_proj_data_info_sptr->set_tof_mash_factor(new_tof_mash_factor); - } + if (num_tof_bins_to_combine != 1) { + if (num_tof_bins_to_combine < 1) + error("SSRB: num_tof_bins_to_combine needs to be at least 1"); + const int new_tof_mash_factor = in_proj_data_info_sptr->get_tof_mash_factor() * num_tof_bins_to_combine; + out_proj_data_info_sptr->set_tof_mash_factor(new_tof_mash_factor); + } return out_proj_data_info_sptr; } -void -SSRB(const string& output_filename, - const ProjData& in_proj_data, - const int num_segments_to_combine, - const int num_views_to_combine, - const int num_tang_poss_to_trim, - const bool do_norm, - const int max_in_segment_num_to_process, - const int num_tof_bins_to_combine - ) -{ - shared_ptr out_proj_data_info_sptr( - SSRB(*in_proj_data.get_proj_data_info_sptr(), - num_segments_to_combine, - num_views_to_combine, - num_tang_poss_to_trim, - max_in_segment_num_to_process, - num_tof_bins_to_combine - )); - ProjDataInterfile out_proj_data(in_proj_data.get_exam_info_sptr(), - out_proj_data_info_sptr, output_filename, std::ios::out); +void +SSRB(const string& output_filename, const ProjData& in_proj_data, const int num_segments_to_combine, + const int num_views_to_combine, const int num_tang_poss_to_trim, const bool do_norm, const int max_in_segment_num_to_process, + const int num_tof_bins_to_combine) { + shared_ptr out_proj_data_info_sptr(SSRB(*in_proj_data.get_proj_data_info_sptr(), num_segments_to_combine, + num_views_to_combine, num_tang_poss_to_trim, + max_in_segment_num_to_process, num_tof_bins_to_combine)); + ProjDataInterfile out_proj_data(in_proj_data.get_exam_info_sptr(), out_proj_data_info_sptr, output_filename, std::ios::out); SSRB(out_proj_data, in_proj_data, do_norm); } -void -SSRB(ProjData& out_proj_data, - const ProjData& in_proj_data, - const bool do_norm - ) -{ - if (in_proj_data.get_proj_data_info_sptr()->is_tof_data()) - error("SSRB for TOF data is not currently implemented.\n"); +void +SSRB(ProjData& out_proj_data, const ProjData& in_proj_data, const bool do_norm) { + if (in_proj_data.get_proj_data_info_sptr()->is_tof_data()) + error("SSRB for TOF data is not currently implemented.\n"); const shared_ptr in_proj_data_info_sptr = - dynamic_pointer_cast - (in_proj_data.get_proj_data_info_sptr()); - if (is_null_ptr(in_proj_data_info_sptr)) - { + dynamic_pointer_cast(in_proj_data.get_proj_data_info_sptr()); + if (is_null_ptr(in_proj_data_info_sptr)) { error("SSRB works only on segments with proj_data_info of " - "type ProjDataInfoCylindrical\n"); + "type ProjDataInfoCylindrical\n"); } const shared_ptr out_proj_data_info_sptr = - dynamic_pointer_cast - (out_proj_data.get_proj_data_info_sptr()); - if (is_null_ptr(out_proj_data_info_sptr)) - { + dynamic_pointer_cast(out_proj_data.get_proj_data_info_sptr()); + if (is_null_ptr(out_proj_data_info_sptr)) { error("SSRB works only on segments with proj_data_info of " - "type ProjDataInfoCylindrical\n"); + "type ProjDataInfoCylindrical\n"); } - const int num_views_to_combine = - in_proj_data.get_num_views()/ out_proj_data.get_num_views(); - + const int num_views_to_combine = in_proj_data.get_num_views() / out_proj_data.get_num_views(); - if (in_proj_data.get_min_view_num()!=0 || out_proj_data.get_min_view_num()!=0) - error ("SSRB can only mash views when min_view_num==0\n"); + if (in_proj_data.get_min_view_num() != 0 || out_proj_data.get_min_view_num() != 0) + error("SSRB can only mash views when min_view_num==0\n"); if (in_proj_data.get_num_views() % out_proj_data.get_num_views()) - error ("SSRB can only mash views when out_num_views divides in_num_views\n"); - - for (int out_segment_num = out_proj_data.get_min_segment_num(); - out_segment_num <= out_proj_data.get_max_segment_num(); - ++out_segment_num) + error("SSRB can only mash views when out_num_views divides in_num_views\n"); + + for (int out_segment_num = out_proj_data.get_min_segment_num(); out_segment_num <= out_proj_data.get_max_segment_num(); + ++out_segment_num) { + // find range of input segments that fit in the current output segment + int in_min_segment_num = in_proj_data.get_max_segment_num(); + int in_max_segment_num = in_proj_data.get_min_segment_num(); { - // find range of input segments that fit in the current output segment - int in_min_segment_num = in_proj_data.get_max_segment_num(); - int in_max_segment_num = in_proj_data.get_min_segment_num(); - { - // this the only place where we need ProjDataInfoCylindrical - // Presumably for other types, there'd be something equivalent (say range of theta) - const int out_min_ring_diff = - out_proj_data_info_sptr->get_min_ring_difference(out_segment_num); - const int out_max_ring_diff = - out_proj_data_info_sptr->get_max_ring_difference(out_segment_num); - for (int in_segment_num = in_proj_data.get_min_segment_num(); - in_segment_num <= in_proj_data.get_max_segment_num(); - ++in_segment_num) - { - const int in_min_ring_diff = - in_proj_data_info_sptr->get_min_ring_difference(in_segment_num); - const int in_max_ring_diff = - in_proj_data_info_sptr->get_max_ring_difference(in_segment_num); - if (in_min_ring_diff >= out_min_ring_diff && - in_max_ring_diff <= out_max_ring_diff) - { - // it's a in_segment that should be rebinned in the out_segment - if (in_min_segment_num > in_segment_num) - in_min_segment_num = in_segment_num; - if (in_max_segment_num < in_segment_num) - in_max_segment_num = in_segment_num; - } - else if (in_min_ring_diff > out_max_ring_diff || - in_max_ring_diff < out_min_ring_diff) - { - // this one is outside the range of the out_segment - } - else - { - error("SSRB called with in and out ring difference ranges that overlap:\n" - "in_segment %d has ring diffs (%d,%d)\n" - "out_segment %d has ring diffs (%d,%d)\n", - in_segment_num, in_min_ring_diff, in_max_ring_diff, - in_segment_num, out_min_ring_diff, out_max_ring_diff); - } - } - - // keep sinograms out of the loop to avoid reallocations - // initialise to something because there's no default constructor - Sinogram out_sino = - out_proj_data.get_empty_sinogram(out_proj_data.get_min_axial_pos_num(out_segment_num),out_segment_num); - Sinogram in_sino = - in_proj_data.get_empty_sinogram(in_proj_data.get_min_axial_pos_num(out_segment_num),out_segment_num); - - for (int out_ax_pos_num = out_proj_data.get_min_axial_pos_num(out_segment_num); - out_ax_pos_num <= out_proj_data.get_max_axial_pos_num(out_segment_num); - ++out_ax_pos_num ) - { - out_sino= out_proj_data.get_empty_sinogram(out_ax_pos_num, out_segment_num); - // get_m could be replaced by get_t - const float out_m = out_proj_data_info_sptr->get_m(Bin(out_segment_num,0, out_ax_pos_num, 0)); - - unsigned int num_in_ax_pos = 0; - - for (int in_segment_num = in_min_segment_num; - in_segment_num <= in_max_segment_num; - ++in_segment_num) - for (int in_ax_pos_num = in_proj_data.get_min_axial_pos_num(in_segment_num); - in_ax_pos_num <= in_proj_data.get_max_axial_pos_num(in_segment_num); - ++in_ax_pos_num ) - { - const float in_m = in_proj_data_info_sptr->get_m(Bin(in_segment_num,0, in_ax_pos_num, 0)); - if (fabs(out_m - in_m) < 1E-4) - { - ++num_in_ax_pos; - - in_sino = in_proj_data.get_sinogram(in_ax_pos_num, in_segment_num); - for (int in_view_num=in_proj_data.get_min_view_num(); - in_view_num <= in_proj_data.get_max_view_num(); - ++in_view_num) - for (int tangential_pos_num= - max(in_proj_data.get_min_tangential_pos_num(), - out_proj_data.get_min_tangential_pos_num()); - tangential_pos_num <= - min(in_proj_data.get_max_tangential_pos_num(), - out_proj_data.get_max_tangential_pos_num()); - ++tangential_pos_num) - out_sino[in_view_num/num_views_to_combine][tangential_pos_num] += - in_sino[in_view_num][tangential_pos_num]; - - break; // out of loop over ax_pos as we found where to put it - } - } - if (do_norm && num_in_ax_pos!=0) - out_sino /= static_cast(num_in_ax_pos*num_views_to_combine); - if (num_in_ax_pos==0) - warning("SSRB: no sinograms contributing to output segment %d, ax_pos %d\n", - out_segment_num, out_ax_pos_num); - - out_proj_data.set_sinogram(out_sino); - } + // this the only place where we need ProjDataInfoCylindrical + // Presumably for other types, there'd be something equivalent (say range of theta) + const int out_min_ring_diff = out_proj_data_info_sptr->get_min_ring_difference(out_segment_num); + const int out_max_ring_diff = out_proj_data_info_sptr->get_max_ring_difference(out_segment_num); + for (int in_segment_num = in_proj_data.get_min_segment_num(); in_segment_num <= in_proj_data.get_max_segment_num(); + ++in_segment_num) { + const int in_min_ring_diff = in_proj_data_info_sptr->get_min_ring_difference(in_segment_num); + const int in_max_ring_diff = in_proj_data_info_sptr->get_max_ring_difference(in_segment_num); + if (in_min_ring_diff >= out_min_ring_diff && in_max_ring_diff <= out_max_ring_diff) { + // it's a in_segment that should be rebinned in the out_segment + if (in_min_segment_num > in_segment_num) + in_min_segment_num = in_segment_num; + if (in_max_segment_num < in_segment_num) + in_max_segment_num = in_segment_num; + } else if (in_min_ring_diff > out_max_ring_diff || in_max_ring_diff < out_min_ring_diff) { + // this one is outside the range of the out_segment + } else { + error("SSRB called with in and out ring difference ranges that overlap:\n" + "in_segment %d has ring diffs (%d,%d)\n" + "out_segment %d has ring diffs (%d,%d)\n", + in_segment_num, in_min_ring_diff, in_max_ring_diff, in_segment_num, out_min_ring_diff, out_max_ring_diff); + } + } + + // keep sinograms out of the loop to avoid reallocations + // initialise to something because there's no default constructor + Sinogram out_sino = + out_proj_data.get_empty_sinogram(out_proj_data.get_min_axial_pos_num(out_segment_num), out_segment_num); + Sinogram in_sino = + in_proj_data.get_empty_sinogram(in_proj_data.get_min_axial_pos_num(out_segment_num), out_segment_num); + + for (int out_ax_pos_num = out_proj_data.get_min_axial_pos_num(out_segment_num); + out_ax_pos_num <= out_proj_data.get_max_axial_pos_num(out_segment_num); ++out_ax_pos_num) { + out_sino = out_proj_data.get_empty_sinogram(out_ax_pos_num, out_segment_num); + // get_m could be replaced by get_t + const float out_m = out_proj_data_info_sptr->get_m(Bin(out_segment_num, 0, out_ax_pos_num, 0)); + + unsigned int num_in_ax_pos = 0; + + for (int in_segment_num = in_min_segment_num; in_segment_num <= in_max_segment_num; ++in_segment_num) + for (int in_ax_pos_num = in_proj_data.get_min_axial_pos_num(in_segment_num); + in_ax_pos_num <= in_proj_data.get_max_axial_pos_num(in_segment_num); ++in_ax_pos_num) { + const float in_m = in_proj_data_info_sptr->get_m(Bin(in_segment_num, 0, in_ax_pos_num, 0)); + if (fabs(out_m - in_m) < 1E-4) { + ++num_in_ax_pos; + + in_sino = in_proj_data.get_sinogram(in_ax_pos_num, in_segment_num); + for (int in_view_num = in_proj_data.get_min_view_num(); in_view_num <= in_proj_data.get_max_view_num(); + ++in_view_num) + for (int tangential_pos_num = + max(in_proj_data.get_min_tangential_pos_num(), out_proj_data.get_min_tangential_pos_num()); + tangential_pos_num <= + min(in_proj_data.get_max_tangential_pos_num(), out_proj_data.get_max_tangential_pos_num()); + ++tangential_pos_num) + out_sino[in_view_num / num_views_to_combine][tangential_pos_num] += in_sino[in_view_num][tangential_pos_num]; + + break; // out of loop over ax_pos as we found where to put it + } + } + if (do_norm && num_in_ax_pos != 0) + out_sino /= static_cast(num_in_ax_pos * num_views_to_combine); + if (num_in_ax_pos == 0) + warning("SSRB: no sinograms contributing to output segment %d, ax_pos %d\n", out_segment_num, out_ax_pos_num); + + out_proj_data.set_sinogram(out_sino); } } + } } END_NAMESPACE_STIR diff --git a/src/buildblock/Scanner.cxx b/src/buildblock/Scanner.cxx index 14989590e2..d66b32e224 100644 --- a/src/buildblock/Scanner.cxx +++ b/src/buildblock/Scanner.cxx @@ -45,12 +45,11 @@ #include #include #ifdef BOOST_NO_STRINGSTREAM -#include +# include #else -#include +# include #endif - #ifndef STIR_NO_NAMESPACES using std::cerr; using std::cout; @@ -63,22 +62,13 @@ using std::list; START_NAMESPACE_STIR // local convenience functions to make a list of strings -static list - string_list(const string&); -static list - string_list(const string&, const string&); -static list - string_list(const string&, const string&, const string&); -static list - string_list(const string&, const string&, const string&, const string&); -static list - string_list(const string&, const string&, const string&, const string&, const string&); +static list string_list(const string&); +static list string_list(const string&, const string&); +static list string_list(const string&, const string&, const string&); +static list string_list(const string&, const string&, const string&, const string&); +static list string_list(const string&, const string&, const string&, const string&, const string&); - - - -Scanner::Scanner(Type scanner_type) -{ +Scanner::Scanner(Type scanner_type) { // set_params parameters: // @@ -105,7 +95,6 @@ Scanner::Scanner(Type scanner_type) // int num_detector_layers_v // - /* for CTI scanners (at least upto 966): before arc-correction, central_bin_size ~= ring_radius* pi/num_detectors @@ -117,101 +106,63 @@ Scanner::Scanner(Type scanner_type) where x=1 except for the 966 where x=2 */ - - switch ( scanner_type ) { + switch (scanner_type) { case E931: // KT 25/01/2002 corrected ring_spacing - set_params(E931, string_list("ECAT 931"), - 8, 192, 192, 2*256, - 510.0F, 7.0F, 13.5F, 3.129F, 0.0F, - 2, 4, 4, 8, 4, 8 * 4, 1, - 0.37F, 511.F, - 0, 0.F, 0.F); + set_params(E931, string_list("ECAT 931"), 8, 192, 192, 2 * 256, 510.0F, 7.0F, 13.5F, 3.129F, 0.0F, 2, 4, 4, 8, 4, 8 * 4, 1, + 0.37F, 511.F, 0, 0.F, 0.F); // 16 BUCKETS per ring in TWO rings - i.e. 32 buckets in total break; case E951: - set_params(E951, string_list("ECAT 951"), - 16, 192, 192, 2*256, - 510.0F, 7.0F, 6.75F, 3.12932F, 0.0F, - 1, 4, 8, 8, 8, 8 * 4, 1, - 0.0F, 511.F, - 0, 0.F, 0.F); + set_params(E951, string_list("ECAT 951"), 16, 192, 192, 2 * 256, 510.0F, 7.0F, 6.75F, 3.12932F, 0.0F, 1, 4, 8, 8, 8, 8 * 4, 1, + 0.0F, 511.F, 0, 0.F, 0.F); break; case E953: - set_params(E953, string_list("ECAT 953"), - 16, 160, 160, 2*192, - 382.5F, 7.0F, 6.75F, 3.12932F, static_cast(15.*_PI/180), - 1, 4, 8, 8, 8, 8 * 4, 1, - 0.0F, 511.F, - 0, 0.F, 0.F); + set_params(E953, string_list("ECAT 953"), 16, 160, 160, 2 * 192, 382.5F, 7.0F, 6.75F, 3.12932F, + static_cast(15. * _PI / 180), 1, 4, 8, 8, 8, 8 * 4, 1, 0.0F, 511.F, 0, 0.F, 0.F); break; case E921: - set_params(E921, string_list("ECAT 921", "ECAT EXACT", "EXACT"), - 24, 192, 192, 2 * 192, - 412.5F, 7.0F, 6.75F, 3.375F, static_cast(15.*_PI/180), - 1, 4, 8, 8, 8, 8 * 4, 1, - 0.0F, 511.F, - 0, 0.F, 0.F); + set_params(E921, string_list("ECAT 921", "ECAT EXACT", "EXACT"), 24, 192, 192, 2 * 192, 412.5F, 7.0F, 6.75F, 3.375F, + static_cast(15. * _PI / 180), 1, 4, 8, 8, 8, 8 * 4, 1, 0.0F, 511.F, 0, 0.F, 0.F); break; case E925: - set_params(E925, string_list("ECAT 925", "ECAT ART"), - 24, 192, 192, 2* 192, - 412.5F, 7.0F, 6.75F, 3.375F, static_cast(15.*_PI/180), - 3, 4, 8, 8, 8, 8 * 4, 1, - 0.0F, 511.F, - 0, 0.F, 0.F); + set_params(E925, string_list("ECAT 925", "ECAT ART"), 24, 192, 192, 2 * 192, 412.5F, 7.0F, 6.75F, 3.375F, + static_cast(15. * _PI / 180), 3, 4, 8, 8, 8, 8 * 4, 1, 0.0F, 511.F, 0, 0.F, 0.F); break; - case E961: - set_params(E961,string_list("ECAT 961", "ECAT HR"), - 24, 336, 336, 2*392, - 412.0F, 7.0F, 6.25F, 1.650F, static_cast(13.*_PI/180), - 1, 8, 8, 7, 8, 7 * 8, 1, - 0.0F, 511.F, - 0, 0.F, 0.F); + set_params(E961, string_list("ECAT 961", "ECAT HR"), 24, 336, 336, 2 * 392, 412.0F, 7.0F, 6.25F, 1.650F, + static_cast(13. * _PI / 180), 1, 8, 8, 7, 8, 7 * 8, 1, 0.0F, 511.F, 0, 0.F, 0.F); break; case E962: - set_params(E962,string_list("ECAT 962","ECAT HR+"), - 32, 288, 288, 2*288, - 412.0F, 7.0F, 4.85F, 2.25F, 0.0F, - 4, 3, 8, 8, 8, 8 * 3, 1, - 0.0F, 511.F, - 0, 0.F, 0.F); + set_params(E962, string_list("ECAT 962", "ECAT HR+"), 32, 288, 288, 2 * 288, 412.0F, 7.0F, 4.85F, 2.25F, 0.0F, 4, 3, 8, 8, 8, + 8 * 3, 1, 0.0F, 511.F, 0, 0.F, 0.F); break; case E966: - set_params(E966, string_list("ECAT EXACT 3D", "EXACT 3D", "ECAT HR++","ECAT 966"), - 48, 288, 288, 2*288, - 412.0F, 7.0F, 4.850F, 2.250F, 0.0, - 6, 2, 8, 8, 2 * 8, 8 * 2, 1, - 0.0F, 511.F, - 0, 0.F, 0.F); + set_params(E966, string_list("ECAT EXACT 3D", "EXACT 3D", "ECAT HR++", "ECAT 966"), 48, 288, 288, 2 * 288, 412.0F, 7.0F, + 4.850F, 2.250F, 0.0, 6, 2, 8, 8, 2 * 8, 8 * 2, 1, 0.0F, 511.F, 0, 0.F, 0.F); break; case E1080: // data added by Robert Barnett, Westmead Hospital, Sydney - set_params(E1080, string_list("ECAT 1080", "Biograph 16", "1080"), - 41, 336, 336, 2*336, - 412.0F, 7.0F, 4.0F, 2.000F, 0.0F, - 1, 2, 13+1, 13+1, 0,0, 1, - 0.0F, 511.F, - 0, 0.F, 0.F); + set_params(E1080, string_list("ECAT 1080", "Biograph 16", "1080"), 41, 336, 336, 2 * 336, 412.0F, 7.0F, 4.0F, 2.000F, 0.0F, 1, + 2, 13 + 1, 13 + 1, 0, 0, 1, 0.0F, 511.F, 0, 0.F, 0.F); // Transaxial blocks have 13 physical crystals and a gap at the // 14th crystal where the counts are zero. // There are 39 rings with 13 axial crystals per block. @@ -221,48 +172,34 @@ Scanner::Scanner(Type scanner_type) // 8x8 blocks, 1 virtual "crystal", 56 blocks along the ring, 8 blocks in axial direction // Transaxial blocks have 8 physical crystals and a gap at the // 9th crystal where the counts are zero. - set_params(Siemens_mMR, string_list("Siemens mMR", "mMR", "2008"), - 64, 344, 344, 2*252, - 328.0F, 7.0F, 4.0625F, 2.08626F, 0.0F, - 2, 1, 8, 9, 16, 9, 1, - 0.145F, 511.F, - 0, 0.F, 0.F); // TODO bucket/singles info incorrect? 224 buckets in total, but not sure how distributed + set_params(Siemens_mMR, string_list("Siemens mMR", "mMR", "2008"), 64, 344, 344, 2 * 252, 328.0F, 7.0F, 4.0625F, 2.08626F, + 0.0F, 2, 1, 8, 9, 16, 9, 1, 0.145F, 511.F, 0, 0.F, + 0.F); // TODO bucket/singles info incorrect? 224 buckets in total, but not sure how distributed break; case test_scanner: // This is a relatively small scanner for test purposes. - set_params(test_scanner, string_list("test_scanner"), - 4, 344, 344,2*252, - 328.0F, 7.0F, 4.0625F, 2.08626F, 0.0F, - 1, 1, 4, 1, 4, 1, 1, - 0.0F, 511.F, - (short int)(410), - (float)(10.0F), - (float)(400.0F) ); + set_params(test_scanner, string_list("test_scanner"), 4, 344, 344, 2 * 252, 328.0F, 7.0F, 4.0625F, 2.08626F, 0.0F, 1, 1, 4, 1, + 4, 1, 1, 0.0F, 511.F, (short int)(410), (float)(10.0F), (float)(400.0F)); break; case Siemens_mCT: - // 13x13 blocks, 1 virtual "crystal" along axial and transaxial direction, 48 blocks along the ring, 4 blocks in axial direction - set_params(Siemens_mCT, string_list("Siemens mCT", "mCT", "2011", "1104" /* used in norm files */, "1094" /* used in attenuation files */), - 55, 400, 336, (13+1)*48, - 421.0F, 7.0F, 4.054F, 2.005F, 0.0F, - 4, 1, 13+1, 13+1, 0,0, 1, + // 13x13 blocks, 1 virtual "crystal" along axial and transaxial direction, 48 blocks along the ring, 4 blocks in axial + // direction + set_params(Siemens_mCT, + string_list("Siemens mCT", "mCT", "2011", "1104" /* used in norm files */, "1094" /* used in attenuation files */), + 55, 400, 336, (13 + 1) * 48, 421.0F, 7.0F, 4.054F, 2.005F, 0.0F, 4, 1, 13 + 1, 13 + 1, 0, 0, 1, // energy 0.F, 511.F, // TOF TODO: timing res - 13, 4.0625*1000/13, -1.F - ); // TODO singles info incorrect + 13, 4.0625 * 1000 / 13, -1.F); // TODO singles info incorrect // energy: 435-650 break; case RPT: - set_params(RPT, string_list("PRT-1", "RPT"), - 16, 128, 128, 2*192, - 380.0F - 7.0F, 7.0F, 6.75F, 3.1088F, 0.0F, - 1, 4, 8, 8, 8, 32, 1, - 0.0F, 511.F, - 0, 0.F, 0.F); + set_params(RPT, string_list("PRT-1", "RPT"), 16, 128, 128, 2 * 192, 380.0F - 7.0F, 7.0F, 6.75F, 3.1088F, 0.0F, 1, 4, 8, 8, 8, + 32, 1, 0.0F, 511.F, 0, 0.F, 0.F); // Default 7.0mm average interaction depth. // This 7mm taken off the inner ring radius so that the effective radius remains 380mm @@ -270,12 +207,8 @@ Scanner::Scanner(Type scanner_type) case RATPET: - set_params(RATPET, string_list("RATPET"), - 8, 56, 56, 2*56, - 115 / 2.F, 7.0F, 6.25F, 1.65F, 0.0F, - 1, 16, 8, 7, 8, 0, 1, - 0.0F, 511.F, - 0, 0.F, 0.F); // HR block, 4 buckets per ring + set_params(RATPET, string_list("RATPET"), 8, 56, 56, 2 * 56, 115 / 2.F, 7.0F, 6.25F, 1.65F, 0.0F, 1, 16, 8, 7, 8, 0, 1, 0.0F, + 511.F, 0, 0.F, 0.F); // HR block, 4 buckets per ring // Default 7.0mm average interaction depth. // 8 x 0 crystals per singles unit because not known @@ -284,285 +217,193 @@ Scanner::Scanner(Type scanner_type) case PANDA: - set_params(PANDA, string_list("PANDA"), - 1 /*NumRings*/, 512 /*MaxBinsNonArcCor*/, 512 /*MaxBinsArcCor*/, 2048 /*NumDetPerRing*/, - /*MeanInnerRadius*/ 75.5/2.F, /*AverageDoI*/ 10.F, /*Ring Spacing*/ 3.F, /*BinSize*/ 0.1F, /*IntrinsicTilt*/ 0.F, - 1, 1, 1, 1, 0, 0, 1, - 0.0F, 511.F, - 0, 0.F, 0.F); + set_params(PANDA, string_list("PANDA"), 1 /*NumRings*/, 512 /*MaxBinsNonArcCor*/, 512 /*MaxBinsArcCor*/, + 2048 /*NumDetPerRing*/, + /*MeanInnerRadius*/ 75.5 / 2.F, /*AverageDoI*/ 10.F, /*Ring Spacing*/ 3.F, /*BinSize*/ 0.1F, /*IntrinsicTilt*/ 0.F, + 1, 1, 1, 1, 0, 0, 1, 0.0F, 511.F, 0, 0.F, 0.F); break; case nanoPET: - set_params(nanoPET, string_list("nanoPET"), /*Modelling the gap with one fake crystal */ - 81, 39*3, /* We could also model gaps in the future as one detector so 39->39+1, while 1 (point source), 3 (mouse) or 5 (rats) */ - 39*3, /* Just put the same with NonArcCor for now*/ - 12 * 39, 174.F, 5.0F, 1.17F, 1.17F, /* Actual size is 1.12 and 0.05 is the thickness of the optical reflector */ 0.0F, /* not sure for this */ - 0,0,0,0,0,0, 1, - 0.0F, 511.F, - 0, 0.F, 0.F); - break; + set_params(nanoPET, string_list("nanoPET"), /*Modelling the gap with one fake crystal */ + 81, 39 * 3, /* We could also model gaps in the future as one detector so 39->39+1, while 1 (point source), 3 + (mouse) or 5 (rats) */ + 39 * 3, /* Just put the same with NonArcCor for now*/ + 12 * 39, 174.F, 5.0F, 1.17F, 1.17F, + /* Actual size is 1.12 and 0.05 is the thickness of the optical reflector */ 0.0F, /* not sure for this */ + 0, 0, 0, 0, 0, 0, 1, 0.0F, 511.F, 0, 0.F, 0.F); + break; case HYPERimage: - set_params(HYPERimage, string_list("HYPERimage"), /*Modelling the gap with one fake crystal */ - 22, 239, 245, - 490, 103.97F, 3.0F, 1.4F, 1.4F, /* Actual size is 1.3667 and assume 0.0333 is the thickness of the optical reflector */ 0.F, - 0,0,0,0,0,0,1, - 0.0F, 511.F, - 0, 0.F, 0.F); - break; - + set_params(HYPERimage, string_list("HYPERimage"), /*Modelling the gap with one fake crystal */ + 22, 239, 245, 490, 103.97F, 3.0F, 1.4F, 1.4F, + /* Actual size is 1.3667 and assume 0.0333 is the thickness of the optical reflector */ 0.F, 0, 0, 0, 0, 0, 0, 1, + 0.0F, 511.F, 0, 0.F, 0.F); + break; case Advance: // 283 bins (non-uniform sampling) // 281 bins (uniform sampling) /* crystal size 4x8x30*/ - set_params(Advance, string_list("GE Advance", "Advance"), - 18, 283, 281, 2 * 336, - 471.875F - 8.4F, 8.4F, 8.5F, 1.970177F, 0.0F, //TODO view offset shouldn't be zero - 3, 2, 6, 6, 1, 1, 1, - 0.0F, 511.F, - 0, 0.F, 0.F); + set_params(Advance, string_list("GE Advance", "Advance"), 18, 283, 281, 2 * 336, 471.875F - 8.4F, 8.4F, 8.5F, 1.970177F, + 0.0F, // TODO view offset shouldn't be zero + 3, 2, 6, 6, 1, 1, 1, 0.0F, 511.F, 0, 0.F, 0.F); break; case DiscoveryLS: // identical to Advance - set_params(DiscoveryLS, string_list("GE Discovery LS", "Discovery LS"), - 18, 283, 281, 2 * 336, - 471.875F - 8.4F, 8.4F, 8.5F, 1.970177F, 0.0F, //TODO view offset shouldn't be zero - 3, 2, 6, 6, 1, 1, 1, - 0.0F, 511.F, - 0, 0.F, 0.F); + set_params(DiscoveryLS, string_list("GE Discovery LS", "Discovery LS"), 18, 283, 281, 2 * 336, 471.875F - 8.4F, 8.4F, 8.5F, + 1.970177F, 0.0F, // TODO view offset shouldn't be zero + 3, 2, 6, 6, 1, 1, 1, 0.0F, 511.F, 0, 0.F, 0.F); break; case DiscoveryST: // 249 bins (non-uniform sampling) // 221 bins (uniform sampling) /* crystal size: 6.3 x 6.3 x 30 mm*/ - set_params(DiscoveryST, string_list("GE Discovery ST", "Discovery ST"), - 24, 249, 221, 2 * 210, - 886.2F/2.F, 8.4F, 6.54F, 3.195F, - static_cast(-4.54224*_PI/180),//sign? - 4, 2, 6, 6, 1, 1, 1, - 0.0F, 511.F, - 0, 0.F, 0.F);// TODO not sure about sign of view_offset + set_params(DiscoveryST, string_list("GE Discovery ST", "Discovery ST"), 24, 249, 221, 2 * 210, 886.2F / 2.F, 8.4F, 6.54F, + 3.195F, + static_cast(-4.54224 * _PI / 180), // sign? + 4, 2, 6, 6, 1, 1, 1, 0.0F, 511.F, 0, 0.F, 0.F); // TODO not sure about sign of view_offset break; - case DiscoverySTE: - - set_params(DiscoverySTE, string_list("GE Discovery STE", "Discovery STE"), - 24, 329, 293, 2 * 280, - 886.2F/2.F, 8.4F, 6.54F, 2.397F, - static_cast(-4.5490*_PI/180),//sign? - 4, 2, 6, 8, 1, 1, 1, - 0.0F, 511.F, - (short int)(410), - (float)(10.0F), - (float)(400.0F) );// TODO not sure about sign of view_offset + case DiscoverySTE: + + set_params(DiscoverySTE, string_list("GE Discovery STE", "Discovery STE"), 24, 329, 293, 2 * 280, 886.2F / 2.F, 8.4F, 6.54F, + 2.397F, + static_cast(-4.5490 * _PI / 180), // sign? + 4, 2, 6, 8, 1, 1, 1, 0.0F, 511.F, (short int)(410), (float)(10.0F), + (float)(400.0F)); // TODO not sure about sign of view_offset break; case DiscoverySTE_nonTOF: - set_params(DiscoverySTE_nonTOF, string_list("GE Discovery STE nonTOF", "Discovery STE nonTOF"), - 24, 329, 293, 2 * 280, - 886.2F/2.F, 8.4F, 6.54F, 2.397F, - static_cast(-4.5490*_PI/180),//sign? - 4, 2, 6, 8, 1, 1, 1, - 0.0F, 511.F, - (short int)(0.F), - (float)(0.F), - (float)(0.F) );// TODO not sure about sign of view_offset - break; + set_params(DiscoverySTE_nonTOF, string_list("GE Discovery STE nonTOF", "Discovery STE nonTOF"), 24, 329, 293, 2 * 280, + 886.2F / 2.F, 8.4F, 6.54F, 2.397F, + static_cast(-4.5490 * _PI / 180), // sign? + 4, 2, 6, 8, 1, 1, 1, 0.0F, 511.F, (short int)(0.F), (float)(0.F), + (float)(0.F)); // TODO not sure about sign of view_offset + break; case ntest_TOF_50: // dummy - // 8x8 blocks, 1 virtual "crystal", 56 blocks along the ring, 8 blocks in axial direction - // Transaxial blocks have 8 physical crystals and a gap at the - // 9th crystal where the counts are zero. - set_params(ntest_TOF_50, string_list("ntest_TOF_50"), - 24, 320, 320,666, - 424.5F, 7.0F, 4.16F, 2.0F, 0.0F, - 1, 1, 24, 1, 24, 1, 1, - 0.0f, 511.f, - (short int)(2999), - (float)(1.0F), - (float)(81.2) ); // TODO bucket/singles info incorrect? 224 buckets in total, but not sure how di$ - break; - - case DiscoveryRX: - - set_params(DiscoveryRX, string_list("GE Discovery RX", "Discovery RX"), - 24, - 367, - 331, - 2 * 315, - 886.2F/2.F, - 9.4F, - 6.54F, 2.13F, - static_cast(-4.5950*_PI/180),//sign? - 4, - 2, - 6, 9, 1, 1, 1, - 0.0F, 511.F, - 1, 1.F, 1.F);// TODO not sure about sign of view_offset + // 8x8 blocks, 1 virtual "crystal", 56 blocks along the ring, 8 blocks in axial direction + // Transaxial blocks have 8 physical crystals and a gap at the + // 9th crystal where the counts are zero. + set_params(ntest_TOF_50, string_list("ntest_TOF_50"), 24, 320, 320, 666, 424.5F, 7.0F, 4.16F, 2.0F, 0.0F, 1, 1, 24, 1, 24, 1, + 1, 0.0f, 511.f, (short int)(2999), (float)(1.0F), + (float)(81.2)); // TODO bucket/singles info incorrect? 224 buckets in total, but not sure how di$ break; - case Discovery600: - - set_params(Discovery600, string_list("GE Discovery 600", "Discovery 600"), - 24, - 339, - 293, // TODO - 2 * 256, - 826.70F/2.F - 8.4F, - 8.4F, - 6.54F, - 2.3974F, - static_cast(-4.5490*_PI/180),//sign? TODO value - 4, - 2, - 6, 8, 1, 1, 1, - 0.0F, 511.F, - 1, 1.F, 1.F); + case DiscoveryRX: + + set_params(DiscoveryRX, string_list("GE Discovery RX", "Discovery RX"), 24, 367, 331, 2 * 315, 886.2F / 2.F, 9.4F, 6.54F, + 2.13F, + static_cast(-4.5950 * _PI / 180), // sign? + 4, 2, 6, 9, 1, 1, 1, 0.0F, 511.F, 1, 1.F, 1.F); // TODO not sure about sign of view_offset break; -case PETMR_Signa: - set_params(PETMR_Signa, string_list("GE Signa PET/MR", "PET/MR Signa", "Signa PET/MR"), - 45, - 357, - 331, // TODO - 2 * 224, - 311.8F, - 8.5F, - 5.56F, - 2.01565F, // TO CHECK - static_cast(-5.23*_PI/180),//sign? TODO value - 5, - 4, - 9, 4, 1, 1, 1, - 0.105F, // energy resolution from Levin et al. TMI 2016 - 511.F, - (short int)(351), - (float)(89.0F/13.0F), //TODO - (float)(390.0F) ); + case Discovery600: + + set_params(Discovery600, string_list("GE Discovery 600", "Discovery 600"), 24, 339, + 293, // TODO + 2 * 256, 826.70F / 2.F - 8.4F, 8.4F, 6.54F, 2.3974F, + static_cast(-4.5490 * _PI / 180), // sign? TODO value + 4, 2, 6, 8, 1, 1, 1, 0.0F, 511.F, 1, 1.F, 1.F); + break; + + case PETMR_Signa: + set_params(PETMR_Signa, string_list("GE Signa PET/MR", "PET/MR Signa", "Signa PET/MR"), 45, 357, + 331, // TODO + 2 * 224, 311.8F, 8.5F, 5.56F, + 2.01565F, // TO CHECK + static_cast(-5.23 * _PI / 180), // sign? TODO value + 5, 4, 9, 4, 1, 1, 1, + 0.105F, // energy resolution from Levin et al. TMI 2016 + 511.F, (short int)(351), + (float)(89.0F / 13.0F), // TODO + (float)(390.0F)); break; case PETMR_Signa_nonTOF: - set_params(PETMR_Signa_nonTOF, string_list("GE PET/MR Signa nonTOF", "GE PET/MR Signa nonTOF"), - 45, - 357, - 331, // TODO - 2 * 224, - 317.0F, - 9.4F, - 5.55F, - 2.1306F, // TO CHECK - static_cast(-5.23*_PI/180),//sign? TODO value - 5, - 4, - 9, 4, 1, 1, 1, - 0.105F, // energy resolution from Levin et al. TMI 2016 - 511.F, - (short int)(0), - (float)(0), //TODO - (float)(0) ); - break; + set_params(PETMR_Signa_nonTOF, string_list("GE PET/MR Signa nonTOF", "GE PET/MR Signa nonTOF"), 45, 357, + 331, // TODO + 2 * 224, 317.0F, 9.4F, 5.55F, + 2.1306F, // TO CHECK + static_cast(-5.23 * _PI / 180), // sign? TODO value + 5, 4, 9, 4, 1, 1, 1, + 0.105F, // energy resolution from Levin et al. TMI 2016 + 511.F, (short int)(0), + (float)(0), // TODO + (float)(0)); + break; case Discovery690: // same as 710 - set_params(Discovery690, string_list("GE Discovery 690", "Discovery 690", - "GE Discovery 710", "Discovery 710"), - 24, - 381, + set_params(Discovery690, string_list("GE Discovery 690", "Discovery 690", "GE Discovery 710", "Discovery 710"), 24, 381, 331, // TODO - 2 * 288, - 405.1F, - 9.4F, - 6.54F, - 2.1306F, - static_cast(-5.021*_PI/180),//sign? TODO value - 4, - 2, - 6, 9, 1, 1, 1, - 0.0F, 511.F, - (short int)(55), - (float)(89.0F), - (float)(550.0F) -); + 2 * 288, 405.1F, 9.4F, 6.54F, 2.1306F, + static_cast(-5.021 * _PI / 180), // sign? TODO value + 4, 2, 6, 9, 1, 1, 1, 0.0F, 511.F, (short int)(55), (float)(89.0F), (float)(550.0F)); break; - case DiscoveryMI3ring: // This is the 3-ring DMI // Hsu et al. 2017 JNM // crystal size 3.95 x 5.3 x 25 - set_params(DiscoveryMI3ring, string_list("GE Discovery MI 3 rings", "Discovery MI3", "Discovery MI"), // needs to include last value as used by GE in RDF files - 27, - 415, - 401, // TODO should compute num_arccorrected_bins from effective_FOV/default_bin_size - 2 * 272, - 380.5F - 9.4F,//TODO inner_ring_radius and DOI, currently set such that effective ring-radius is correct - 9.4F,//TODO DOI - 5.52296F, // ring-spacing - 2.206F,//TODO currently using the central bin size default bin size. GE might be using something else - static_cast(-4.399*_PI/180), //TODO check sign - 3, 4, - 9, 4, - 1, 1, - 1, + set_params(DiscoveryMI3ring, + string_list("GE Discovery MI 3 rings", "Discovery MI3", + "Discovery MI"), // needs to include last value as used by GE in RDF files + 27, 415, + 401, // TODO should compute num_arccorrected_bins from effective_FOV/default_bin_size + 2 * 272, + 380.5F - 9.4F, // TODO inner_ring_radius and DOI, currently set such that effective ring-radius is correct + 9.4F, // TODO DOI + 5.52296F, // ring-spacing + 2.206F, // TODO currently using the central bin size default bin size. GE might be using something else + static_cast(-4.399 * _PI / 180), // TODO check sign + 3, 4, 9, 4, 1, 1, 1, 0.0944F, // energy resolution from Hsu et al. 2017 - 511.F, - (short int)(0), - (float)(0), //TODO - (float)(0) ); + 511.F, (short int)(0), + (float)(0), // TODO + (float)(0)); break; case DiscoveryMI4ring: // This is the 4-ring DMI // as above, but one extra block // Hsu et al. 2017 JNM // crystal size 3.95 x 5.3 x 25 - set_params(DiscoveryMI4ring, string_list("GE Discovery MI 4 rings", "Discovery MI4", "Discovery MI"), // needs to include last value as used by GE in RDF files - 36, - 415, - 401, // TODO should compute num_arccorrected_bins from effective_FOV/default_bin_size - 2 * 272, - 380.5F - 9.4F,//TODO inner_ring_radius and DOI, currently set such that effective ring-radius is correct - 9.4F,//TODO DOI - 5.52296F, // ring-spacing - 2.206F,//TODO currently using the central bin size default bin size. GE might be using something else - static_cast(-4.399*_PI/180), //TODO check sign - 4, 4, - 9, 4, - 1, 1, - 1, + set_params(DiscoveryMI4ring, + string_list("GE Discovery MI 4 rings", "Discovery MI4", + "Discovery MI"), // needs to include last value as used by GE in RDF files + 36, 415, + 401, // TODO should compute num_arccorrected_bins from effective_FOV/default_bin_size + 2 * 272, + 380.5F - 9.4F, // TODO inner_ring_radius and DOI, currently set such that effective ring-radius is correct + 9.4F, // TODO DOI + 5.52296F, // ring-spacing + 2.206F, // TODO currently using the central bin size default bin size. GE might be using something else + static_cast(-4.399 * _PI / 180), // TODO check sign + 4, 4, 9, 4, 1, 1, 1, 0.0944F, // energy resolution from Hsu et al. 2017 - 511.F, - (short int)(0), - (float)(0), //TODO - (float)(0) ); + 511.F, (short int)(0), + (float)(0), // TODO + (float)(0)); break; case HZLR: - set_params(HZLR, string_list("Positron HZL/R"), - 32, 256, 2 * 192, 2*192, - 780.0F, 7.0F, 5.1875F, 2.F, 0.0F, - 0, 0, 0, 0, 0,0, 1, - 0.0F, 511.F, - 0, 0.F, 0.F); + set_params(HZLR, string_list("Positron HZL/R"), 32, 256, 2 * 192, 2 * 192, 780.0F, 7.0F, 5.1875F, 2.F, 0.0F, 0, 0, 0, 0, 0, 0, + 1, 0.0F, 511.F, 0, 0.F, 0.F); // Default 7.0mm average interaction depth. // crystals per singles unit etc unknown break; case HRRT: - set_params(HRRT, string_list("HRRT"), - 104, 288, 2 * 288, 2*288, - 234.765F, 7.0F, 2.4375F, 1.21875F, 0.0F, - 0, 0, 0, 0, 0, 0, 2, - 0.0F, 511.F, - 0, 0.F, 0.F); // added by Dylan Togane + set_params(HRRT, string_list("HRRT"), 104, 288, 2 * 288, 2 * 288, 234.765F, 7.0F, 2.4375F, 1.21875F, 0.0F, 0, 0, 0, 0, 0, 0, + 2, 0.0F, 511.F, 0, 0.F, 0.F); // added by Dylan Togane // warning: used 7.0mm average interaction depth. // crystals per singles unit etc unknown break; @@ -590,446 +431,305 @@ case PETMR_Signa: Ralph Brinks things there is only one singles rate for the whole scanner. */ - set_params(Allegro,string_list("Allegro", "Philips Allegro"), - 29, 295, 28*23, 28*23, - 430.05F, 12.F, - 6.3F, 4.3F, 0.0F, - 1, 0, - 29, 0 /* 23* or 22*/, - 29, 0 /* all detectors in a ring? */, - 1, - 0.0F, 511.F, - 0, 0.F, 0.F); + set_params(Allegro, string_list("Allegro", "Philips Allegro"), 29, 295, 28 * 23, 28 * 23, 430.05F, 12.F, 6.3F, 4.3F, 0.0F, 1, + 0, 29, 0 /* 23* or 22*/, 29, 0 /* all detectors in a ring? */, 1, 0.0F, 511.F, 0, 0.F, 0.F); break; case GeminiTF: - set_params(GeminiTF,string_list("GeminiTF", "Philips GeminiTF"), - 44, 322, 287, // Based on GATE output - Normally it is 644 detectors at each of the 44 rings - 322*2, // Actual number of crystals is 644 + set_params(GeminiTF, string_list("GeminiTF", "Philips GeminiTF"), 44, 322, + 287, // Based on GATE output - Normally it is 644 detectors at each of the 44 rings + 322 * 2, // Actual number of crystals is 644 450.17F, 8.F, // DOI is from XXX et al 2008 MIC - 4.F, 4.F, 0.F, - 0, 0, - 0, 0, // Not considering any gap, but this is per module 28 flat modules in total, while 420 PMTs - 0, 0 /* Not sure about these, but shouldn't be important */, - 1, - 0.0F, 511.F, - 0, 0.F, 0.F); + 4.F, 4.F, 0.F, 0, 0, 0, + 0, // Not considering any gap, but this is per module 28 flat modules in total, while 420 PMTs + 0, 0 /* Not sure about these, but shouldn't be important */, 1, 0.0F, 511.F, 0, 0.F, 0.F); break; case HiDAC: // all of these don't make any sense for the HiDAC - set_params(HiDAC, string_list("HiDAC"), - 0, 0, 0,0, - 0.F, 0.F, 0.F, 0.F, 0.F, - 0, 0, 0, 0, 0, 0, 0, - 0.0F, 511.F, - 0, 0.F, 0.F); + set_params(HiDAC, string_list("HiDAC"), 0, 0, 0, 0, 0.F, 0.F, 0.F, 0.F, 0.F, 0, 0, 0, 0, 0, 0, 0, 0.0F, 511.F, 0, 0.F, 0.F); break; case User_defined_scanner: // zlong, 08-04-2004, Userdefined support - set_params(User_defined_scanner, string_list("Userdefined"), - 0, 0, 0,0, - 0.F, 0.F, 0.F, 0.F, 0.F, - 0, 0, 0, 0, 0, 0, 0, - 0.0F, 511.F, - 0, 0.F, 0.F); + set_params(User_defined_scanner, string_list("Userdefined"), 0, 0, 0, 0, 0.F, 0.F, 0.F, 0.F, 0.F, 0, 0, 0, 0, 0, 0, 0, 0.0F, + 511.F, 0, 0.F, 0.F); break; default: // warning("Unknown scanner type used for initialisation of Scanner\n"); - set_params(Unknown_scanner, string_list("Unknown"), - 0, 0, 0,0, - 0.F, 0.F, 0.F, 0.F, 0.F, - 0, 0, 0, 0, 0, 0, 0, - 0.0F, 511.F, - 0, 0.F, 0.F); + set_params(Unknown_scanner, string_list("Unknown"), 0, 0, 0, 0, 0.F, 0.F, 0.F, 0.F, 0.F, 0, 0, 0, 0, 0, 0, 0, 0.0F, 511.F, 0, + 0.F, 0.F); break; - } - } -Scanner::Scanner(Type type_v, const list& list_of_names_v, - int num_detectors_per_ring_v, int num_rings_v, - int max_num_non_arccorrected_bins_v, - int default_num_arccorrected_bins_v, - float inner_ring_radius_v, float average_depth_of_interaction_v, - float ring_spacing_v, float bin_size_v, float intrinsic_tilt_v, - int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, - int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, - int num_axial_crystals_per_singles_unit_v, - int num_transaxial_crystals_per_singles_unit_v, - int num_detector_layers_v, - float energy_resolution_v, - float reference_energy_v, - short int max_num_of_timing_poss_v, - float size_timing_pos_v, - float timing_resolution_v) -{ - set_params(type_v, list_of_names_v, num_rings_v, - max_num_non_arccorrected_bins_v, - default_num_arccorrected_bins_v, - num_detectors_per_ring_v, - inner_ring_radius_v, - average_depth_of_interaction_v, - ring_spacing_v, bin_size_v, intrinsic_tilt_v, - num_axial_blocks_per_bucket_v, num_transaxial_blocks_per_bucket_v, - num_axial_crystals_per_block_v, num_transaxial_crystals_per_block_v, - num_axial_crystals_per_singles_unit_v, - num_transaxial_crystals_per_singles_unit_v, - num_detector_layers_v, - energy_resolution_v, - reference_energy_v, - max_num_of_timing_poss_v, - size_timing_pos_v, - timing_resolution_v); +Scanner::Scanner(Type type_v, const list& list_of_names_v, int num_detectors_per_ring_v, int num_rings_v, + int max_num_non_arccorrected_bins_v, int default_num_arccorrected_bins_v, float inner_ring_radius_v, + float average_depth_of_interaction_v, float ring_spacing_v, float bin_size_v, float intrinsic_tilt_v, + int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, int num_axial_crystals_per_block_v, + int num_transaxial_crystals_per_block_v, int num_axial_crystals_per_singles_unit_v, + int num_transaxial_crystals_per_singles_unit_v, int num_detector_layers_v, float energy_resolution_v, + float reference_energy_v, short int max_num_of_timing_poss_v, float size_timing_pos_v, + float timing_resolution_v) { + set_params(type_v, list_of_names_v, num_rings_v, max_num_non_arccorrected_bins_v, default_num_arccorrected_bins_v, + num_detectors_per_ring_v, inner_ring_radius_v, average_depth_of_interaction_v, ring_spacing_v, bin_size_v, + intrinsic_tilt_v, num_axial_blocks_per_bucket_v, num_transaxial_blocks_per_bucket_v, num_axial_crystals_per_block_v, + num_transaxial_crystals_per_block_v, num_axial_crystals_per_singles_unit_v, + num_transaxial_crystals_per_singles_unit_v, num_detector_layers_v, energy_resolution_v, reference_energy_v, + max_num_of_timing_poss_v, size_timing_pos_v, timing_resolution_v); } -Scanner::Scanner(Type type_v, const string& name, - int num_detectors_per_ring_v, int num_rings_v, - int max_num_non_arccorrected_bins_v, - int default_num_arccorrected_bins_v, - float inner_ring_radius_v, float average_depth_of_interaction_v, - float ring_spacing_v, float bin_size_v, float intrinsic_tilt_v, - int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, - int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, - int num_axial_crystals_per_singles_unit_v, - int num_transaxial_crystals_per_singles_unit_v, - int num_detector_layers_v, - float energy_resolution_v, - float reference_energy_v, - short int max_num_of_timing_poss_v, - float size_timing_pos_v, - float timing_resolution_v) -{ - set_params(type_v, string_list(name), num_rings_v, - max_num_non_arccorrected_bins_v, - default_num_arccorrected_bins_v, - num_detectors_per_ring_v, - inner_ring_radius_v, - average_depth_of_interaction_v, - ring_spacing_v, bin_size_v, intrinsic_tilt_v, - num_axial_blocks_per_bucket_v, num_transaxial_blocks_per_bucket_v, - num_axial_crystals_per_block_v, num_transaxial_crystals_per_block_v, - num_axial_crystals_per_singles_unit_v, - num_transaxial_crystals_per_singles_unit_v, - num_detector_layers_v, - energy_resolution_v, - reference_energy_v, - max_num_of_timing_poss_v, - size_timing_pos_v, - timing_resolution_v); +Scanner::Scanner(Type type_v, const string& name, int num_detectors_per_ring_v, int num_rings_v, + int max_num_non_arccorrected_bins_v, int default_num_arccorrected_bins_v, float inner_ring_radius_v, + float average_depth_of_interaction_v, float ring_spacing_v, float bin_size_v, float intrinsic_tilt_v, + int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, int num_axial_crystals_per_block_v, + int num_transaxial_crystals_per_block_v, int num_axial_crystals_per_singles_unit_v, + int num_transaxial_crystals_per_singles_unit_v, int num_detector_layers_v, float energy_resolution_v, + float reference_energy_v, short int max_num_of_timing_poss_v, float size_timing_pos_v, + float timing_resolution_v) { + set_params(type_v, string_list(name), num_rings_v, max_num_non_arccorrected_bins_v, default_num_arccorrected_bins_v, + num_detectors_per_ring_v, inner_ring_radius_v, average_depth_of_interaction_v, ring_spacing_v, bin_size_v, + intrinsic_tilt_v, num_axial_blocks_per_bucket_v, num_transaxial_blocks_per_bucket_v, num_axial_crystals_per_block_v, + num_transaxial_crystals_per_block_v, num_axial_crystals_per_singles_unit_v, + num_transaxial_crystals_per_singles_unit_v, num_detector_layers_v, energy_resolution_v, reference_energy_v, + max_num_of_timing_poss_v, size_timing_pos_v, timing_resolution_v); } void -Scanner:: -set_params(Type type_v, const std::list& list_of_names_v, - int num_rings_v, - int max_num_non_arccorrected_bins_v, - int default_num_arccorrected_bins_v, - int num_detectors_per_ring_v, - float inner_ring_radius_v, - float average_depth_of_interaction_v, - float ring_spacing_v, - float bin_size_v, float intrinsic_tilt_v, - int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, - int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, - int num_axial_crystals_per_singles_unit_v, - int num_transaxial_crystals_per_singles_unit_v, - int num_detector_layers_v, - float energy_resolution_v, - float reference_energy_v, - short int max_num_of_timing_poss_v, - float size_timing_pos_v, - float timing_resolution_v) -{ +Scanner::set_params(Type type_v, const std::list& list_of_names_v, int num_rings_v, + int max_num_non_arccorrected_bins_v, int default_num_arccorrected_bins_v, int num_detectors_per_ring_v, + float inner_ring_radius_v, float average_depth_of_interaction_v, float ring_spacing_v, float bin_size_v, + float intrinsic_tilt_v, int num_axial_blocks_per_bucket_v, int num_transaxial_blocks_per_bucket_v, + int num_axial_crystals_per_block_v, int num_transaxial_crystals_per_block_v, + int num_axial_crystals_per_singles_unit_v, int num_transaxial_crystals_per_singles_unit_v, + int num_detector_layers_v, float energy_resolution_v, float reference_energy_v, + short int max_num_of_timing_poss_v, float size_timing_pos_v, float timing_resolution_v) { type = type_v; list_of_names = list_of_names_v; - num_rings = num_rings_v; + num_rings = num_rings_v; max_num_non_arccorrected_bins = max_num_non_arccorrected_bins_v; default_num_arccorrected_bins = default_num_arccorrected_bins_v; num_detectors_per_ring = num_detectors_per_ring_v; - inner_ring_radius = inner_ring_radius_v; + inner_ring_radius = inner_ring_radius_v; average_depth_of_interaction = average_depth_of_interaction_v; ring_spacing = ring_spacing_v; bin_size = bin_size_v; intrinsic_tilt = intrinsic_tilt_v; num_transaxial_blocks_per_bucket = num_transaxial_blocks_per_bucket_v; num_axial_blocks_per_bucket = num_axial_blocks_per_bucket_v; - num_axial_crystals_per_block= num_axial_crystals_per_block_v; - num_transaxial_crystals_per_block= num_transaxial_crystals_per_block_v; + num_axial_crystals_per_block = num_axial_crystals_per_block_v; + num_transaxial_crystals_per_block = num_transaxial_crystals_per_block_v; num_axial_crystals_per_singles_unit = num_axial_crystals_per_singles_unit_v; num_transaxial_crystals_per_singles_unit = num_transaxial_crystals_per_singles_unit_v; num_detector_layers = num_detector_layers_v; energy_resolution = energy_resolution_v; if (reference_energy_v <= 0) - reference_energy = 511.f; + reference_energy = 511.f; else - reference_energy = reference_energy_v; + reference_energy = reference_energy_v; max_num_of_timing_poss = max_num_of_timing_poss_v; size_timing_pos = size_timing_pos_v; timing_resolution = timing_resolution_v; - } /*! \todo The current list is bound to be incomplete. would be better to stick it in set_params(). */ int -Scanner:: -get_num_virtual_axial_crystals_per_block() const -{ - switch(get_type()) - { - case E1080: - case Siemens_mCT: - return 1; - default: - return 0; - } +Scanner::get_num_virtual_axial_crystals_per_block() const { + switch (get_type()) { + case E1080: + case Siemens_mCT: + return 1; + default: + return 0; + } } /*! \todo The current list is bound to be incomplete. would be better to stick it in set_params(). */ int -Scanner:: -get_num_virtual_transaxial_crystals_per_block() const -{ - switch(get_type()) - { - case E1080: - case Siemens_mCT: - case Siemens_mMR: - return 1; - default: - return 0; - } +Scanner::get_num_virtual_transaxial_crystals_per_block() const { + switch (get_type()) { + case E1080: + case Siemens_mCT: + case Siemens_mMR: + return 1; + default: + return 0; + } } /*! \todo Can currently only set to hard-wired values. Otherwise calls error() */ void -Scanner:: -set_num_virtual_axial_crystals_per_block(int val) -{ - //num_virtual_axial_crystals_per_block = val; +Scanner::set_num_virtual_axial_crystals_per_block(int val) { + // num_virtual_axial_crystals_per_block = val; if (this->get_num_virtual_axial_crystals_per_block() != val) error("Scanner::set_num_virtual_axial_crystals_per_block not really implemented yet"); } /*! \todo Can currently only set to hard-wired values. Otherwise calls error() */ void -Scanner:: -set_num_virtual_transaxial_crystals_per_block(int val) -{ - //num_virtual_transaxial_crystals_per_block = val; +Scanner::set_num_virtual_transaxial_crystals_per_block(int val) { + // num_virtual_transaxial_crystals_per_block = val; if (this->get_num_virtual_transaxial_crystals_per_block() != val) error("Scanner::set_num_virtual_transaxial_crystals_per_block not really implemented yet"); } - -Succeeded -Scanner:: -check_consistency() const -{ - if (intrinsic_tilt<-_PI || intrinsic_tilt>_PI) +Succeeded +Scanner::check_consistency() const { + if (intrinsic_tilt < -_PI || intrinsic_tilt > _PI) warning("Scanner %s: intrinsic_tilt is very large. maybe it's in degrees (but should be in radians)", - this->get_name().c_str()); + this->get_name().c_str()); { - if (get_num_transaxial_crystals_per_block() <= 0 || - get_num_transaxial_blocks() <= 0) - warning("Scanner %s: transaxial block info is not set (probably irrelevant unless you use a projector or normalisation that needs this block info)", - this->get_name().c_str()); - else - { - const int dets_per_ring = - get_num_transaxial_blocks() * - get_num_transaxial_crystals_per_block(); - if ( dets_per_ring != get_num_detectors_per_ring()) - { - warning("Scanner %s: inconsistent transaxial block info", - this->get_name().c_str()); - return Succeeded::no; - } + if (get_num_transaxial_crystals_per_block() <= 0 || get_num_transaxial_blocks() <= 0) + warning("Scanner %s: transaxial block info is not set (probably irrelevant unless you use a projector or normalisation " + "that needs this block info)", + this->get_name().c_str()); + else { + const int dets_per_ring = get_num_transaxial_blocks() * get_num_transaxial_crystals_per_block(); + if (dets_per_ring != get_num_detectors_per_ring()) { + warning("Scanner %s: inconsistent transaxial block info", this->get_name().c_str()); + return Succeeded::no; } + } } { - if (get_num_transaxial_blocks_per_bucket() <= 0 || - get_num_transaxial_buckets() <=0) - warning("Scanner %s: transaxial bucket info is not set (probably irrelevant unless you use dead-time correction that needs this info)", - this->get_name().c_str()); - else - { - const int blocks_per_ring = - get_num_transaxial_buckets() * - get_num_transaxial_blocks_per_bucket(); - if ( blocks_per_ring != get_num_transaxial_blocks()) - { - warning("Scanner %s: inconsistent transaxial block/bucket info", - this->get_name().c_str()); - return Succeeded::no; - } + if (get_num_transaxial_blocks_per_bucket() <= 0 || get_num_transaxial_buckets() <= 0) + warning("Scanner %s: transaxial bucket info is not set (probably irrelevant unless you use dead-time correction that needs " + "this info)", + this->get_name().c_str()); + else { + const int blocks_per_ring = get_num_transaxial_buckets() * get_num_transaxial_blocks_per_bucket(); + if (blocks_per_ring != get_num_transaxial_blocks()) { + warning("Scanner %s: inconsistent transaxial block/bucket info", this->get_name().c_str()); + return Succeeded::no; } + } } { - if (get_num_axial_crystals_per_block() <= 0 || - get_num_axial_blocks() <=0) - warning("Scanner %s: axial block info is not set (probably irrelevant unless you use a projector or normalisation that needs this block info)", - this->get_name().c_str()); - else - { - const int dets_axial = - get_num_axial_blocks() * - get_num_axial_crystals_per_block(); - if ( dets_axial != (get_num_rings() + get_num_virtual_axial_crystals_per_block())) - { - warning("Scanner %s: inconsistent axial block info: %d vs %d", - this->get_name().c_str(), - dets_axial, get_num_rings() + get_num_virtual_axial_crystals_per_block()); - return Succeeded::no; - } + if (get_num_axial_crystals_per_block() <= 0 || get_num_axial_blocks() <= 0) + warning("Scanner %s: axial block info is not set (probably irrelevant unless you use a projector or normalisation that " + "needs this block info)", + this->get_name().c_str()); + else { + const int dets_axial = get_num_axial_blocks() * get_num_axial_crystals_per_block(); + if (dets_axial != (get_num_rings() + get_num_virtual_axial_crystals_per_block())) { + warning("Scanner %s: inconsistent axial block info: %d vs %d", this->get_name().c_str(), dets_axial, + get_num_rings() + get_num_virtual_axial_crystals_per_block()); + return Succeeded::no; } + } } { - if (get_num_axial_blocks_per_bucket() <= 0 || - get_num_axial_buckets() <=0) - warning("Scanner %s: axial bucket info is not set (probably irrelevant unless you use dead-time correction that needs this info)", - this->get_name().c_str()); - else - { - const int blocks_axial = - get_num_axial_buckets() * - get_num_axial_blocks_per_bucket(); - if ( blocks_axial != get_num_axial_blocks()) - { - warning("Scanner %s: inconsistent axial block/bucket info", - this->get_name().c_str()); - return Succeeded::no; - } + if (get_num_axial_blocks_per_bucket() <= 0 || get_num_axial_buckets() <= 0) + warning("Scanner %s: axial bucket info is not set (probably irrelevant unless you use dead-time correction that needs this " + "info)", + this->get_name().c_str()); + else { + const int blocks_axial = get_num_axial_buckets() * get_num_axial_blocks_per_bucket(); + if (blocks_axial != get_num_axial_blocks()) { + warning("Scanner %s: inconsistent axial block/bucket info", this->get_name().c_str()); + return Succeeded::no; } + } } // checks on singles units { if (get_num_transaxial_crystals_per_singles_unit() <= 0) - warning("Scanner %s: transaxial singles_unit info is not set (probably irrelevant unless you use dead-time correction that needs this info)", - this->get_name().c_str()); - else - { - if ( get_num_detectors_per_ring() % get_num_transaxial_crystals_per_singles_unit() != 0) - { - warning("Scanner %s: inconsistent transaxial singles unit info:\n" - "\tnum_detectors_per_ring %d should be a multiple of num_transaxial_crystals_per_singles_unit %d", - this->get_name().c_str(), - get_num_detectors_per_ring(), get_num_transaxial_crystals_per_singles_unit()); - return Succeeded::no; - } - if ( get_num_transaxial_crystals_per_bucket() % get_num_transaxial_crystals_per_singles_unit() != 0) - { - warning("Scanner %s: inconsistent transaxial singles unit info:\n" - "\tnum_transaxial_crystals_per_bucket %d should be a multiple of num_transaxial_crystals_per_singles_unit %d", - this->get_name().c_str(), - get_num_transaxial_crystals_per_bucket(), get_num_transaxial_crystals_per_singles_unit()); - return Succeeded::no; - } + warning("Scanner %s: transaxial singles_unit info is not set (probably irrelevant unless you use dead-time correction that " + "needs this info)", + this->get_name().c_str()); + else { + if (get_num_detectors_per_ring() % get_num_transaxial_crystals_per_singles_unit() != 0) { + warning("Scanner %s: inconsistent transaxial singles unit info:\n" + "\tnum_detectors_per_ring %d should be a multiple of num_transaxial_crystals_per_singles_unit %d", + this->get_name().c_str(), get_num_detectors_per_ring(), get_num_transaxial_crystals_per_singles_unit()); + return Succeeded::no; + } + if (get_num_transaxial_crystals_per_bucket() % get_num_transaxial_crystals_per_singles_unit() != 0) { + warning("Scanner %s: inconsistent transaxial singles unit info:\n" + "\tnum_transaxial_crystals_per_bucket %d should be a multiple of num_transaxial_crystals_per_singles_unit %d", + this->get_name().c_str(), get_num_transaxial_crystals_per_bucket(), + get_num_transaxial_crystals_per_singles_unit()); + return Succeeded::no; } + } } { if (get_num_axial_crystals_per_singles_unit() <= 0) - warning("Scanner %s: axial singles_unit info is not set (probably irrelevant unless you use dead-time correction that needs this info)", - this->get_name().c_str()); - else - { - if ( get_num_rings() % get_num_axial_crystals_per_singles_unit() != 0) - { - warning("Scanner %s: inconsistent axial singles unit info:\n" - "\tnum_rings %d should be a multiple of num_axial_crystals_per_singles_unit %d", - this->get_name().c_str(), - get_num_rings(), get_num_axial_crystals_per_singles_unit()); - return Succeeded::no; - } - if ( get_num_axial_crystals_per_bucket() % get_num_axial_crystals_per_singles_unit() != 0) - { - warning("Scanner %s: inconsistent axial singles unit info:\n" - "\tnum_axial_crystals_per_bucket %d should be a multiple of num_axial_crystals_per_singles_unit %d", - this->get_name().c_str(), - get_num_axial_crystals_per_bucket(), get_num_axial_crystals_per_singles_unit()); - return Succeeded::no; - } + warning("Scanner %s: axial singles_unit info is not set (probably irrelevant unless you use dead-time correction that " + "needs this info)", + this->get_name().c_str()); + else { + if (get_num_rings() % get_num_axial_crystals_per_singles_unit() != 0) { + warning("Scanner %s: inconsistent axial singles unit info:\n" + "\tnum_rings %d should be a multiple of num_axial_crystals_per_singles_unit %d", + this->get_name().c_str(), get_num_rings(), get_num_axial_crystals_per_singles_unit()); + return Succeeded::no; + } + if (get_num_axial_crystals_per_bucket() % get_num_axial_crystals_per_singles_unit() != 0) { + warning("Scanner %s: inconsistent axial singles unit info:\n" + "\tnum_axial_crystals_per_bucket %d should be a multiple of num_axial_crystals_per_singles_unit %d", + this->get_name().c_str(), get_num_axial_crystals_per_bucket(), get_num_axial_crystals_per_singles_unit()); + return Succeeded::no; } + } } return Succeeded::yes; } - - - - - - // TODO replace by using boost::floating_point_comparison -bool static close_enough(const double a, const double b) -{ - return fabs(a-b) <= std::min(fabs(a), fabs(b)) * 10E-4; -} +bool static close_enough(const double a, const double b) { return fabs(a - b) <= std::min(fabs(a), fabs(b)) * 10E-4; } bool -Scanner::operator ==(const Scanner& scanner) const -{ -if (!close_enough(energy_resolution, scanner.energy_resolution) && - !close_enough(reference_energy, scanner.reference_energy)) +Scanner::operator==(const Scanner& scanner) const { + if (!close_enough(energy_resolution, scanner.energy_resolution) && !close_enough(reference_energy, scanner.reference_energy)) warning("The energy resolution of the two scanners is different. \n" " %f opposed to %f" - "This only affects scatter simulation. \n", energy_resolution, scanner.energy_resolution); - - bool ok = - (num_rings == scanner.num_rings) && - (max_num_non_arccorrected_bins == scanner.max_num_non_arccorrected_bins) && - (default_num_arccorrected_bins == scanner.default_num_arccorrected_bins) && - (num_detectors_per_ring == scanner.num_detectors_per_ring) && - close_enough(inner_ring_radius, scanner.inner_ring_radius) && - close_enough(average_depth_of_interaction, scanner.average_depth_of_interaction) && - close_enough(ring_spacing, scanner.ring_spacing) && - close_enough(bin_size,scanner.bin_size) && - close_enough(intrinsic_tilt,scanner.intrinsic_tilt) && - (num_transaxial_blocks_per_bucket == scanner.num_transaxial_blocks_per_bucket) && - (num_axial_blocks_per_bucket == scanner.num_axial_blocks_per_bucket) && - (num_axial_crystals_per_block == scanner.num_axial_crystals_per_block) && - (num_transaxial_crystals_per_block == scanner.num_transaxial_crystals_per_block) && - (num_detector_layers == scanner.num_detector_layers) && - (num_axial_crystals_per_singles_unit == scanner.num_axial_crystals_per_singles_unit) && - (num_transaxial_crystals_per_singles_unit == scanner.num_transaxial_crystals_per_singles_unit); - - if (this->is_tof_ready() && scanner.is_tof_ready()) - { - ok = (max_num_of_timing_poss == scanner.max_num_of_timing_poss) && - close_enough(size_timing_pos, scanner.size_timing_pos) && - close_enough(timing_resolution, scanner.timing_resolution); + "This only affects scatter simulation. \n", + energy_resolution, scanner.energy_resolution); + + bool ok = (num_rings == scanner.num_rings) && (max_num_non_arccorrected_bins == scanner.max_num_non_arccorrected_bins) && + (default_num_arccorrected_bins == scanner.default_num_arccorrected_bins) && + (num_detectors_per_ring == scanner.num_detectors_per_ring) && + close_enough(inner_ring_radius, scanner.inner_ring_radius) && + close_enough(average_depth_of_interaction, scanner.average_depth_of_interaction) && + close_enough(ring_spacing, scanner.ring_spacing) && close_enough(bin_size, scanner.bin_size) && + close_enough(intrinsic_tilt, scanner.intrinsic_tilt) && + (num_transaxial_blocks_per_bucket == scanner.num_transaxial_blocks_per_bucket) && + (num_axial_blocks_per_bucket == scanner.num_axial_blocks_per_bucket) && + (num_axial_crystals_per_block == scanner.num_axial_crystals_per_block) && + (num_transaxial_crystals_per_block == scanner.num_transaxial_crystals_per_block) && + (num_detector_layers == scanner.num_detector_layers) && + (num_axial_crystals_per_singles_unit == scanner.num_axial_crystals_per_singles_unit) && + (num_transaxial_crystals_per_singles_unit == scanner.num_transaxial_crystals_per_singles_unit); + + if (this->is_tof_ready() && scanner.is_tof_ready()) { + ok = (max_num_of_timing_poss == scanner.max_num_of_timing_poss) && close_enough(size_timing_pos, scanner.size_timing_pos) && + close_enough(timing_resolution, scanner.timing_resolution); } return ok; - } - const list& -Scanner::get_all_names() const -{return list_of_names;} - +Scanner::get_all_names() const { + return list_of_names; +} const string& -Scanner::get_name() const -{ - - return *(list_of_names.begin()); +Scanner::get_name() const { + return *(list_of_names.begin()); } string -Scanner::parameter_info() const -{ +Scanner::parameter_info() const { // warning: these should match the parsing keywords in InterfilePDFSHeader #ifdef BOOST_NO_STRINGSTREAM // dangerous for out-of-range, but 'old-style' ostrstream seems to need this @@ -1038,58 +738,48 @@ Scanner::parameter_info() const #else std::ostringstream s; #endif - s << "Scanner parameters:= "<<'\n'; + s << "Scanner parameters:= " << '\n'; - s << "Scanner type := " << get_name() <<'\n'; + s << "Scanner type := " << get_name() << '\n'; s << "Number of rings := " << num_rings << '\n'; s << "Number of detectors per ring := " << get_num_detectors_per_ring() << '\n'; - s << "Inner ring diameter (cm) := " << get_inner_ring_radius()*2./10 << '\n' + s << "Inner ring diameter (cm) := " << get_inner_ring_radius() * 2. / 10 << '\n' << "Average depth of interaction (cm) := " << get_average_depth_of_interaction() / 10 << '\n' - << "Distance between rings (cm) := " << get_ring_spacing()/10 << '\n' - << "Default bin size (cm) := " << get_default_bin_size()/10. << '\n' - << "View offset (degrees) := " << get_default_intrinsic_tilt()*180/_PI << '\n'; - s << "Maximum number of non-arc-corrected bins := " - << get_max_num_non_arccorrected_bins() << '\n' - << "Default number of arc-corrected bins := " - << get_default_num_arccorrected_bins() << '\n'; - if (get_energy_resolution() >= 0 && get_reference_energy() >= 0) - { + << "Distance between rings (cm) := " << get_ring_spacing() / 10 << '\n' + << "Default bin size (cm) := " << get_default_bin_size() / 10. << '\n' + << "View offset (degrees) := " << get_default_intrinsic_tilt() * 180 / _PI << '\n'; + s << "Maximum number of non-arc-corrected bins := " << get_max_num_non_arccorrected_bins() << '\n' + << "Default number of arc-corrected bins := " << get_default_num_arccorrected_bins() << '\n'; + if (get_energy_resolution() >= 0 && get_reference_energy() >= 0) { s << "Energy resolution := " << get_energy_resolution() << '\n'; s << "Reference energy (in keV) := " << get_reference_energy() << '\n'; } - if (is_tof_ready()) - { + if (is_tof_ready()) { s << "Number of TOF time bins :=" << get_max_num_timing_poss() << "\n"; s << "Size of timing bin (ps) :=" << get_size_of_timing_pos() << "\n"; s << "Timing resolution (ps) :=" << get_timing_resolution() << "\n"; } // block/bucket description - s << "Number of blocks per bucket in transaxial direction := " - << get_num_transaxial_blocks_per_bucket() << '\n' - << "Number of blocks per bucket in axial direction := " - << get_num_axial_blocks_per_bucket() << '\n' - << "Number of crystals per block in axial direction := " - << get_num_axial_crystals_per_block() << '\n' - << "Number of crystals per block in transaxial direction := " - << get_num_transaxial_crystals_per_block() << '\n' - << "Number of detector layers := " - << get_num_detector_layers() << '\n' - << "Number of crystals per singles unit in axial direction := " - << get_num_axial_crystals_per_singles_unit() << '\n' - << "Number of crystals per singles unit in transaxial direction := " - << get_num_transaxial_crystals_per_singles_unit() << '\n'; + s << "Number of blocks per bucket in transaxial direction := " << get_num_transaxial_blocks_per_bucket() << '\n' + << "Number of blocks per bucket in axial direction := " << get_num_axial_blocks_per_bucket() << '\n' + << "Number of crystals per block in axial direction := " << get_num_axial_crystals_per_block() << '\n' + << "Number of crystals per block in transaxial direction := " << get_num_transaxial_crystals_per_block() << '\n' + << "Number of detector layers := " << get_num_detector_layers() << '\n' + << "Number of crystals per singles unit in axial direction := " << get_num_axial_crystals_per_singles_unit() << '\n' + << "Number of crystals per singles unit in transaxial direction := " << get_num_transaxial_crystals_per_singles_unit() + << '\n'; s << "end scanner parameters:=\n"; return s.str(); } -string Scanner::list_names() const -{ +string +Scanner::list_names() const { #ifdef BOOST_NO_STRINGSTREAM // dangerous for out-of-range, but 'old-style' ostrstream seems to need this char str[3000]; @@ -1102,12 +792,11 @@ string Scanner::list_names() const // work-around VC bug std:: #endif - list::const_iterator iterator = list_of_names.begin(); + list::const_iterator iterator = list_of_names.begin(); s << *iterator; ++iterator; - while(iterator!=list_of_names.end()) - { - s << " , " << *iterator ; + while (iterator != list_of_names.end()) { + s << " , " << *iterator; ++iterator; } @@ -1117,136 +806,94 @@ string Scanner::list_names() const /************************************************ static members *************************************************/ -Scanner* Scanner::ask_parameters() -{ +Scanner* +Scanner::ask_parameters() { cerr << list_all_names(); - const string name=ask_string("Enter the name of the scanner"); + const string name = ask_string("Enter the name of the scanner"); - //get the type from the name itself - Scanner* scanner_ptr = - get_scanner_from_name(name); + // get the type from the name itself + Scanner* scanner_ptr = get_scanner_from_name(name); // N.E: New optional parameters have been added, namely // energy resolution and timing resolution, // lets give users the chance to set these parameters on // old scanners. This should stay here as a transitional step. - if (scanner_ptr->type != Unknown_scanner && scanner_ptr->type != User_defined_scanner) - { - info("Two new options are available: (a) Energy Resolution and (b) Reference energy (in keV)." - "They are used in Scatter Simulation. In case, you need them, please set them " - "manually in your file. More over, the creation of a Time-Of-Flight scanner with energy" - "information is not supported. You have to do it manually."); + if (scanner_ptr->type != Unknown_scanner && scanner_ptr->type != User_defined_scanner) { + info("Two new options are available: (a) Energy Resolution and (b) Reference energy (in keV)." + "They are used in Scatter Simulation. In case, you need them, please set them " + "manually in your file. More over, the creation of a Time-Of-Flight scanner with energy" + "information is not supported. You have to do it manually."); - return scanner_ptr; - } + return scanner_ptr; + } if (scanner_ptr->type == Unknown_scanner) cerr << "I didn't recognise the scanner you entered."; cerr << "I'll ask lots of questions\n"; - while (true) - { - int num_detectors_per_ring = - ask_num("Enter number of detectors per ring:",0,2000,128); - - int NoRings = - ask_num("Enter number of rings :",0,1000,16); - - int NoBins = - ask_num("Enter default number of tangential positions for this scanner: ",0,3000,128); - - float InnerRingRadius= - ask_num("Enter inner ring radius (in mm): ",0.F,600.F,256.F); - - float AverageDepthOfInteraction = - ask_num("Enter average depth of interaction (in mm): ", 0.F, 100.F, 0.F); - - float RingSpacing= - ask_num("Enter ring spacing (in mm): ",0.F,30.F,6.75F); - - float BinSize= - ask_num("Enter default (tangential) bin size after arc-correction (in mm):",0.F,60.F,3.75F); - float intrTilt= - ask_num("Enter intrinsic_tilt (in degrees):",-180.F,360.F,0.F); - int TransBlocksPerBucket = - ask_num("Enter number of transaxial blocks per bucket: ",0,10,2); - int AxialBlocksPerBucket = - ask_num("Enter number of axial blocks per bucket: ",0,10,6); - int AxialCrystalsPerBlock = - ask_num("Enter number of axial crystals per block: ",0,12,8); - int TransaxialCrystalsPerBlock = - ask_num("Enter number of transaxial crystals per block: ",0,12,8); - int AxialCrstalsPerSinglesUnit = - ask_num("Enter number of axial crystals per singles unit: ", 0, NoRings, 1); - int TransaxialCrystalsPerSinglesUnit = + while (true) { + int num_detectors_per_ring = ask_num("Enter number of detectors per ring:", 0, 2000, 128); + + int NoRings = ask_num("Enter number of rings :", 0, 1000, 16); + + int NoBins = ask_num("Enter default number of tangential positions for this scanner: ", 0, 3000, 128); + + float InnerRingRadius = ask_num("Enter inner ring radius (in mm): ", 0.F, 600.F, 256.F); + + float AverageDepthOfInteraction = ask_num("Enter average depth of interaction (in mm): ", 0.F, 100.F, 0.F); + + float RingSpacing = ask_num("Enter ring spacing (in mm): ", 0.F, 30.F, 6.75F); + + float BinSize = ask_num("Enter default (tangential) bin size after arc-correction (in mm):", 0.F, 60.F, 3.75F); + float intrTilt = ask_num("Enter intrinsic_tilt (in degrees):", -180.F, 360.F, 0.F); + int TransBlocksPerBucket = ask_num("Enter number of transaxial blocks per bucket: ", 0, 10, 2); + int AxialBlocksPerBucket = ask_num("Enter number of axial blocks per bucket: ", 0, 10, 6); + int AxialCrystalsPerBlock = ask_num("Enter number of axial crystals per block: ", 0, 12, 8); + int TransaxialCrystalsPerBlock = ask_num("Enter number of transaxial crystals per block: ", 0, 12, 8); + int AxialCrstalsPerSinglesUnit = ask_num("Enter number of axial crystals per singles unit: ", 0, NoRings, 1); + int TransaxialCrystalsPerSinglesUnit = ask_num("Enter number of transaxial crystals per singles unit: ", 0, num_detectors_per_ring, 1); - short int Num_TOF_bins = - ask_num("Number of TOF time bins :", 0, 800, 0); - float Size_TOF_bin = - ask_num("Size of timing bin (ps) :", 0.0f, 100.0f, 0.0f); - float TOF_resolution = - ask_num("Timing resolution (ps) :", 0.0f, 1000.0f, 0.0f); - - float EnergyResolution = - ask_num("Enter the energy resolution of the scanner : ", 0.0f, 1000.0f, -1.0f); - - float ReferenceEnergy = - ask_num("Enter the reference energy for the energy resolution (in keV):", 0.0f, 1000.0f, -1.0f); - - int num_detector_layers = - ask_num("Enter number of detector layers per block: ",1,100,1); - Type type = User_defined_scanner; - - scanner_ptr = - new Scanner(type, string_list(name), - num_detectors_per_ring, NoRings, - NoBins, NoBins, - InnerRingRadius, AverageDepthOfInteraction, - RingSpacing, BinSize,intrTilt*float(_PI)/180, - AxialBlocksPerBucket,TransBlocksPerBucket, - AxialCrystalsPerBlock,TransaxialCrystalsPerBlock, - AxialCrstalsPerSinglesUnit, TransaxialCrystalsPerSinglesUnit, - num_detector_layers, - EnergyResolution, - ReferenceEnergy, - Num_TOF_bins, - Size_TOF_bin, - TOF_resolution ); - - if (scanner_ptr->check_consistency()==Succeeded::yes || - !ask("Ask questions again?",true)) - return scanner_ptr; + short int Num_TOF_bins = ask_num("Number of TOF time bins :", 0, 800, 0); + float Size_TOF_bin = ask_num("Size of timing bin (ps) :", 0.0f, 100.0f, 0.0f); + float TOF_resolution = ask_num("Timing resolution (ps) :", 0.0f, 1000.0f, 0.0f); - delete scanner_ptr; - } // infinite loop -} + float EnergyResolution = ask_num("Enter the energy resolution of the scanner : ", 0.0f, 1000.0f, -1.0f); + float ReferenceEnergy = ask_num("Enter the reference energy for the energy resolution (in keV):", 0.0f, 1000.0f, -1.0f); + int num_detector_layers = ask_num("Enter number of detector layers per block: ", 1, 100, 1); + Type type = User_defined_scanner; -Scanner * -Scanner::get_scanner_from_name(const string& name) -{ - Scanner * scanner_ptr; + scanner_ptr = new Scanner(type, string_list(name), num_detectors_per_ring, NoRings, NoBins, NoBins, InnerRingRadius, + AverageDepthOfInteraction, RingSpacing, BinSize, intrTilt * float(_PI) / 180, AxialBlocksPerBucket, + TransBlocksPerBucket, AxialCrystalsPerBlock, TransaxialCrystalsPerBlock, AxialCrstalsPerSinglesUnit, + TransaxialCrystalsPerSinglesUnit, num_detector_layers, EnergyResolution, ReferenceEnergy, + Num_TOF_bins, Size_TOF_bin, TOF_resolution); - const string matching_name = - standardise_interfile_keyword(name); - Type type= E931; - while (type != Unknown_scanner) - { + if (scanner_ptr->check_consistency() == Succeeded::yes || !ask("Ask questions again?", true)) + return scanner_ptr; + + delete scanner_ptr; + } // infinite loop +} + +Scanner* +Scanner::get_scanner_from_name(const string& name) { + Scanner* scanner_ptr; + + const string matching_name = standardise_interfile_keyword(name); + Type type = E931; + while (type != Unknown_scanner) { scanner_ptr = new Scanner(type); const list& list_of_names = scanner_ptr->get_all_names(); - for (std::list::const_iterator iter =list_of_names.begin(); - iter!=list_of_names.end(); - ++iter) - { - const string matching_scanner_name = - standardise_interfile_keyword(*iter); - if (matching_scanner_name==matching_name) - return scanner_ptr; - } + for (std::list::const_iterator iter = list_of_names.begin(); iter != list_of_names.end(); ++iter) { + const string matching_scanner_name = standardise_interfile_keyword(*iter); + if (matching_scanner_name == matching_name) + return scanner_ptr; + } // we didn't find it yet delete scanner_ptr; @@ -1259,10 +906,9 @@ Scanner::get_scanner_from_name(const string& name) return new Scanner(Unknown_scanner); } - -string Scanner:: list_all_names() -{ - Scanner * scanner_ptr; +string +Scanner::list_all_names() { + Scanner* scanner_ptr; #ifdef BOOST_NO_STRINGSTREAM // dangerous for out-of-range, but 'old-style' ostrstream seems to need this char str[30000]; @@ -1271,9 +917,8 @@ string Scanner:: list_all_names() std::ostringstream s; #endif - Type type= E931; - while (type != Unknown_scanner) - { + Type type = E931; + while (type != Unknown_scanner) { scanner_ptr = new Scanner(type); s << scanner_ptr->list_names() << '\n'; @@ -1287,18 +932,15 @@ string Scanner:: list_all_names() return s.str(); } - static list -string_list(const string& s) -{ +string_list(const string& s) { list l; l.push_back(s); return l; } static list -string_list(const string& s1, const string& s2) -{ +string_list(const string& s1, const string& s2) { list l; l.push_back(s1); l.push_back(s2); @@ -1306,8 +948,7 @@ string_list(const string& s1, const string& s2) } static list -string_list(const string& s1, const string& s2, const string& s3) -{ +string_list(const string& s1, const string& s2, const string& s3) { list l; l.push_back(s1); l.push_back(s2); @@ -1316,8 +957,7 @@ string_list(const string& s1, const string& s2, const string& s3) } static list -string_list(const string& s1, const string& s2, const string& s3, const string& s4) -{ +string_list(const string& s1, const string& s2, const string& s3, const string& s4) { list l; l.push_back(s1); l.push_back(s2); @@ -1327,8 +967,7 @@ string_list(const string& s1, const string& s2, const string& s3, const string& } static list -string_list(const string& s1, const string& s2, const string& s3, const string& s4, const string& s5) -{ +string_list(const string& s1, const string& s2, const string& s3, const string& s4, const string& s5) { list l; l.push_back(s1); l.push_back(s2); diff --git a/src/buildblock/Segment.cxx b/src/buildblock/Segment.cxx index f63e686d94..f8935a540a 100644 --- a/src/buildblock/Segment.cxx +++ b/src/buildblock/Segment.cxx @@ -38,72 +38,43 @@ using std::string; START_NAMESPACE_STIR - -template +template bool -Segment:: -has_same_characteristics(self_type const& other, - string& explanation) const -{ +Segment::has_same_characteristics(self_type const& other, string& explanation) const { using boost::format; using boost::str; - if (typeid(*this) != typeid(other)) - { - explanation = - str(format("Differing data types:%1% vs %2%") - % typeid(*this).name() - % typeid(other).name() - ); - return false; - } - if (*this->get_proj_data_info_sptr() != - *other.get_proj_data_info_sptr()) - { - explanation = - str(format("Differing projection data info:\n%1%\n-------- vs-------\n %2%") - % this->get_proj_data_info_sptr()->parameter_info() - % other.get_proj_data_info_sptr()->parameter_info() - ); - return false; - } - if (this->get_segment_num() != - other.get_segment_num()) - { - explanation = - str(format("Differing segment number: %1% vs %2%") - % this->get_segment_num() - % other.get_segment_num() - ); - return false; - } - if (this->get_timing_pos_num() != - other.get_timing_pos_num()) - { - explanation = - str(format("Differing timing position index: %1% vs %2%") - % this->get_timing_pos_num() - % other.get_timing_pos_num() - ); - return false; - } + if (typeid(*this) != typeid(other)) { + explanation = str(format("Differing data types:%1% vs %2%") % typeid(*this).name() % typeid(other).name()); + return false; + } + if (*this->get_proj_data_info_sptr() != *other.get_proj_data_info_sptr()) { + explanation = str(format("Differing projection data info:\n%1%\n-------- vs-------\n %2%") % + this->get_proj_data_info_sptr()->parameter_info() % other.get_proj_data_info_sptr()->parameter_info()); + return false; + } + if (this->get_segment_num() != other.get_segment_num()) { + explanation = str(format("Differing segment number: %1% vs %2%") % this->get_segment_num() % other.get_segment_num()); + return false; + } + if (this->get_timing_pos_num() != other.get_timing_pos_num()) { + explanation = + str(format("Differing timing position index: %1% vs %2%") % this->get_timing_pos_num() % other.get_timing_pos_num()); + return false; + } return true; } -template +template bool -Segment:: -has_same_characteristics(self_type const& other) const -{ +Segment::has_same_characteristics(self_type const& other) const { std::string explanation; return this->has_same_characteristics(other, explanation); } -template -bool -Segment:: -operator !=(const self_type& that) const -{ +template +bool +Segment::operator!=(const self_type& that) const { return !((*this) == that); } diff --git a/src/buildblock/SegmentBySinogram.cxx b/src/buildblock/SegmentBySinogram.cxx index 4a136cce65..ebf049ca14 100644 --- a/src/buildblock/SegmentBySinogram.cxx +++ b/src/buildblock/SegmentBySinogram.cxx @@ -37,88 +37,62 @@ START_NAMESPACE_STIR - - template -SegmentBySinogram :: -SegmentBySinogram(const Array<3,elemT>& v, - const shared_ptr& pdi_ptr, - const int segment_num, - const int timing_pos_num) - : - Segment(pdi_ptr, segment_num, timing_pos_num), - Array<3,elemT>(v) -{ - assert( get_min_view_num() == pdi_ptr->get_min_view_num()); - assert( get_max_view_num() == pdi_ptr->get_max_view_num()); - assert( get_min_axial_pos_num() == pdi_ptr->get_min_axial_pos_num(segment_num)); - assert( get_max_axial_pos_num() == pdi_ptr->get_max_axial_pos_num(segment_num)); - assert( get_min_tangential_pos_num() == pdi_ptr->get_min_tangential_pos_num()); - assert( get_max_tangential_pos_num() == pdi_ptr->get_max_tangential_pos_num()); +SegmentBySinogram::SegmentBySinogram(const Array<3, elemT>& v, const shared_ptr& pdi_ptr, + const int segment_num, const int timing_pos_num) + : Segment(pdi_ptr, segment_num, timing_pos_num), Array<3, elemT>(v) { + assert(get_min_view_num() == pdi_ptr->get_min_view_num()); + assert(get_max_view_num() == pdi_ptr->get_max_view_num()); + assert(get_min_axial_pos_num() == pdi_ptr->get_min_axial_pos_num(segment_num)); + assert(get_max_axial_pos_num() == pdi_ptr->get_max_axial_pos_num(segment_num)); + assert(get_min_tangential_pos_num() == pdi_ptr->get_min_tangential_pos_num()); + assert(get_max_tangential_pos_num() == pdi_ptr->get_max_tangential_pos_num()); } -template -SegmentBySinogram :: -SegmentBySinogram(const shared_ptr &pdi_ptr, - const int segment_num, - const int timing_pos_num) - : - Segment(pdi_ptr, segment_num, timing_pos_num), - Array<3,elemT>(IndexRange3D(pdi_ptr->get_min_axial_pos_num(segment_num), - pdi_ptr->get_max_axial_pos_num(segment_num), - pdi_ptr->get_min_view_num(), - pdi_ptr->get_max_view_num(), - pdi_ptr->get_min_tangential_pos_num(), - pdi_ptr->get_max_tangential_pos_num())) -{} +template +SegmentBySinogram::SegmentBySinogram(const shared_ptr& pdi_ptr, const int segment_num, + const int timing_pos_num) + : Segment(pdi_ptr, segment_num, timing_pos_num), + Array<3, elemT>(IndexRange3D(pdi_ptr->get_min_axial_pos_num(segment_num), pdi_ptr->get_max_axial_pos_num(segment_num), + pdi_ptr->get_min_view_num(), pdi_ptr->get_max_view_num(), + pdi_ptr->get_min_tangential_pos_num(), pdi_ptr->get_max_tangential_pos_num())) {} template -SegmentBySinogram:: -SegmentBySinogram(const SegmentByView& s_v ) - - : Segment(s_v.get_proj_data_info_sptr()->create_shared_clone(), - s_v.get_segment_num(), s_v.get_timing_pos_num()), - Array<3,elemT> (IndexRange3D (s_v.get_min_axial_pos_num(), s_v.get_max_axial_pos_num(), - s_v.get_min_view_num(), s_v.get_max_view_num(), - s_v.get_min_tangential_pos_num(), s_v.get_max_tangential_pos_num())) -{ - - for (int r=get_min_axial_pos_num(); r<= get_max_axial_pos_num(); r++) +SegmentBySinogram::SegmentBySinogram(const SegmentByView& s_v) + + : Segment(s_v.get_proj_data_info_sptr()->create_shared_clone(), s_v.get_segment_num(), s_v.get_timing_pos_num()), + Array<3, elemT>(IndexRange3D(s_v.get_min_axial_pos_num(), s_v.get_max_axial_pos_num(), s_v.get_min_view_num(), + s_v.get_max_view_num(), s_v.get_min_tangential_pos_num(), s_v.get_max_tangential_pos_num())) { + + for (int r = get_min_axial_pos_num(); r <= get_max_axial_pos_num(); r++) set_sinogram(s_v.get_sinogram(r)); } -template -bool -SegmentBySinogram:: -operator ==(const Segment& that) const -{ - return - this->has_same_characteristics(that) && - Array<3,elemT>::operator==(static_cast(that)); +template +bool +SegmentBySinogram::operator==(const Segment& that) const { + return this->has_same_characteristics(that) && Array<3, elemT>::operator==(static_cast(that)); } - template -Viewgram -SegmentBySinogram::get_viewgram(int view_num) const -{ +Viewgram +SegmentBySinogram::get_viewgram(int view_num) const { // gcc 2.95.2 needs a this-> in front of get_min_ring for unclear reasons - Array<2,elemT> pre_view(IndexRange2D(this->get_min_axial_pos_num(), get_max_axial_pos_num(), - get_min_tangential_pos_num(),get_max_tangential_pos_num())); - for (int r=get_min_axial_pos_num(); r<= get_max_axial_pos_num(); r++) - pre_view[r] = Array<3,elemT>::operator[](r)[view_num]; - //KT 9/12 constructed a PETSinogram before... + Array<2, elemT> pre_view(IndexRange2D(this->get_min_axial_pos_num(), get_max_axial_pos_num(), get_min_tangential_pos_num(), + get_max_tangential_pos_num())); + for (int r = get_min_axial_pos_num(); r <= get_max_axial_pos_num(); r++) + pre_view[r] = Array<3, elemT>::operator[](r)[view_num]; + // KT 9/12 constructed a PETSinogram before... // CL&KT 15/12 added ring_difference stuff - return Viewgram(pre_view, this->proj_data_info_sptr->create_shared_clone(), view_num, - this->get_segment_num(), this->get_timing_pos_num()); + return Viewgram(pre_view, this->proj_data_info_sptr->create_shared_clone(), view_num, this->get_segment_num(), + this->get_timing_pos_num()); } template void -SegmentBySinogram::set_viewgram(const Viewgram& viewgram) -{ - for (int r=get_min_axial_pos_num(); r<= get_max_axial_pos_num(); r++) - Array<3,elemT>::operator[](r)[viewgram.get_view_num()] = viewgram[r]; +SegmentBySinogram::set_viewgram(const Viewgram& viewgram) { + for (int r = get_min_axial_pos_num(); r <= get_max_axial_pos_num(); r++) + Array<3, elemT>::operator[](r)[viewgram.get_view_num()] = viewgram[r]; } /*! @@ -126,14 +100,12 @@ SegmentBySinogram::set_viewgram(const Viewgram& viewgram) ProjDataInfo member. */ template -void -SegmentBySinogram:: -resize(const IndexRange<3>& range) -{ +void +SegmentBySinogram::resize(const IndexRange<3>& range) { if (range == this->get_index_range()) return; - assert(range.is_regular()==true); + assert(range.is_regular() == true); const int ax_min = range.get_min_index(); const int ax_max = range.get_max_index(); @@ -143,30 +115,26 @@ resize(const IndexRange<3>& range) assert(range[ax_min].get_min_index() == 0); shared_ptr pdi_sptr = this->proj_data_info_sptr->create_shared_clone(); - + pdi_sptr->set_min_axial_pos_num(ax_min, this->get_segment_num()); pdi_sptr->set_max_axial_pos_num(ax_max, this->get_segment_num()); - + pdi_sptr->set_num_views(range[ax_min].get_max_index() + 1); pdi_sptr->set_min_tangential_pos_num(range[ax_min][0].get_min_index()); pdi_sptr->set_max_tangential_pos_num(range[ax_min][0].get_max_index()); this->proj_data_info_sptr = pdi_sptr; - Array<3,elemT>::resize(range); - + Array<3, elemT>::resize(range); } - /*! This makes sure that the new Array dimensions are the same as those in the ProjDataInfo member. */ template -void -SegmentBySinogram:: -grow(const IndexRange<3>& range) -{ +void +SegmentBySinogram::grow(const IndexRange<3>& range) { resize(range); } diff --git a/src/buildblock/SegmentByView.cxx b/src/buildblock/SegmentByView.cxx index 6e36682c23..478b2d4216 100644 --- a/src/buildblock/SegmentByView.cxx +++ b/src/buildblock/SegmentByView.cxx @@ -36,104 +36,74 @@ START_NAMESPACE_STIR - template -SegmentByView:: -SegmentByView(const Array<3,elemT>& v, - const shared_ptr& pdi_ptr, - const int segment_num, - const int timing_pos_num) - : - Segment(pdi_ptr, segment_num, timing_pos_num), - Array<3,elemT>(v) -{ - assert( get_min_view_num() == pdi_ptr->get_min_view_num()); - assert( get_max_view_num() == pdi_ptr->get_max_view_num()); - assert( get_min_axial_pos_num() == pdi_ptr->get_min_axial_pos_num(segment_num)); - assert( get_max_axial_pos_num() == pdi_ptr->get_max_axial_pos_num(segment_num)); - assert( get_min_tangential_pos_num() == pdi_ptr->get_min_tangential_pos_num()); - assert( get_max_tangential_pos_num() == pdi_ptr->get_max_tangential_pos_num()); - +SegmentByView::SegmentByView(const Array<3, elemT>& v, const shared_ptr& pdi_ptr, + const int segment_num, const int timing_pos_num) + : Segment(pdi_ptr, segment_num, timing_pos_num), Array<3, elemT>(v) { + assert(get_min_view_num() == pdi_ptr->get_min_view_num()); + assert(get_max_view_num() == pdi_ptr->get_max_view_num()); + assert(get_min_axial_pos_num() == pdi_ptr->get_min_axial_pos_num(segment_num)); + assert(get_max_axial_pos_num() == pdi_ptr->get_max_axial_pos_num(segment_num)); + assert(get_min_tangential_pos_num() == pdi_ptr->get_min_tangential_pos_num()); + assert(get_max_tangential_pos_num() == pdi_ptr->get_max_tangential_pos_num()); } template -SegmentByView:: -SegmentByView(const shared_ptr& pdi_ptr, - const int segment_num, - const int timing_pos_num) - : - Segment(pdi_ptr, segment_num, timing_pos_num), - Array<3,elemT>(IndexRange3D(pdi_ptr->get_min_view_num(), - pdi_ptr->get_max_view_num(), - pdi_ptr->get_min_axial_pos_num(segment_num), - pdi_ptr->get_max_axial_pos_num(segment_num), - pdi_ptr->get_min_tangential_pos_num(), - pdi_ptr->get_max_tangential_pos_num())) -{} +SegmentByView::SegmentByView(const shared_ptr& pdi_ptr, const int segment_num, + const int timing_pos_num) + : Segment(pdi_ptr, segment_num, timing_pos_num), + Array<3, elemT>(IndexRange3D(pdi_ptr->get_min_view_num(), pdi_ptr->get_max_view_num(), + pdi_ptr->get_min_axial_pos_num(segment_num), pdi_ptr->get_max_axial_pos_num(segment_num), + pdi_ptr->get_min_tangential_pos_num(), pdi_ptr->get_max_tangential_pos_num())) {} template SegmentByView::SegmentByView(const SegmentBySinogram& s_s) - : Segment(s_s.get_proj_data_info_sptr()->create_shared_clone(), - s_s.get_segment_num(),s_s.get_timing_pos_num()), - - Array<3,elemT> (IndexRange3D(s_s.get_min_view_num(),s_s.get_max_view_num(), - s_s.get_min_axial_pos_num(),s_s.get_max_axial_pos_num(), - s_s.get_min_tangential_pos_num(), s_s.get_max_tangential_pos_num())) -{ - - for (int v=get_min_view_num(); v<= get_max_view_num(); v++) + : Segment(s_s.get_proj_data_info_sptr()->create_shared_clone(), s_s.get_segment_num(), s_s.get_timing_pos_num()), + + Array<3, elemT>(IndexRange3D(s_s.get_min_view_num(), s_s.get_max_view_num(), s_s.get_min_axial_pos_num(), + s_s.get_max_axial_pos_num(), s_s.get_min_tangential_pos_num(), + s_s.get_max_tangential_pos_num())) { + + for (int v = get_min_view_num(); v <= get_max_view_num(); v++) set_viewgram(s_s.get_viewgram(v)); } - -template -bool -SegmentByView:: -operator ==(const Segment& that) const -{ - return - this->has_same_characteristics(that) && - Array<3,elemT>::operator==(static_cast(that)); +template +bool +SegmentByView::operator==(const Segment& that) const { + return this->has_same_characteristics(that) && Array<3, elemT>::operator==(static_cast(that)); } template -Sinogram -SegmentByView::get_sinogram(int axial_pos_num) const -{ +Sinogram +SegmentByView::get_sinogram(int axial_pos_num) const { // gcc 2.95.2 needs a this-> in front of get_min_voew_num for unclear reasons - Array<2,elemT> pre_sino(IndexRange2D(this->get_min_view_num(), get_max_view_num(), - get_min_tangential_pos_num(),get_max_tangential_pos_num())); - for (int v=get_min_view_num(); v<= get_max_view_num(); v++) - pre_sino[v] = Array<3,elemT>::operator[](v)[axial_pos_num]; - - return Sinogram(pre_sino, this->proj_data_info_sptr, axial_pos_num, - this->get_segment_num(), this->get_timing_pos_num()); + Array<2, elemT> pre_sino( + IndexRange2D(this->get_min_view_num(), get_max_view_num(), get_min_tangential_pos_num(), get_max_tangential_pos_num())); + for (int v = get_min_view_num(); v <= get_max_view_num(); v++) + pre_sino[v] = Array<3, elemT>::operator[](v)[axial_pos_num]; + + return Sinogram(pre_sino, this->proj_data_info_sptr, axial_pos_num, this->get_segment_num(), this->get_timing_pos_num()); } template void -SegmentByView::set_sinogram(const Sinogram& sino, int axial_pos_num) -{ - for (int v=get_min_view_num(); v<= get_max_view_num(); v++) - Array<3,elemT>::operator[](v)[axial_pos_num] = sino[v]; +SegmentByView::set_sinogram(const Sinogram& sino, int axial_pos_num) { + for (int v = get_min_view_num(); v <= get_max_view_num(); v++) + Array<3, elemT>::operator[](v)[axial_pos_num] = sino[v]; } - - - /*! This makes sure that the new Array dimensions are the same as those in the ProjDataInfo member. */ template -void -SegmentByView:: -resize(const IndexRange<3>& range) -{ +void +SegmentByView::resize(const IndexRange<3>& range) { if (range == this->get_index_range()) return; - assert(range.is_regular()==true); + assert(range.is_regular() == true); // can only handle min_view==0 at the moment // TODO @@ -143,18 +113,17 @@ resize(const IndexRange<3>& range) const int ax_min = range[0].get_min_index(); const int ax_max = range[0].get_max_index(); - + pdi_ptr->set_min_axial_pos_num(ax_min, this->get_segment_num()); pdi_ptr->set_max_axial_pos_num(ax_max, this->get_segment_num()); - + pdi_ptr->set_num_views(range.get_max_index() + 1); pdi_ptr->set_min_tangential_pos_num(range[0][ax_min].get_min_index()); pdi_ptr->set_max_tangential_pos_num(range[0][ax_min].get_max_index()); this->proj_data_info_sptr.reset(pdi_ptr); - Array<3,elemT>::resize(range); - + Array<3, elemT>::resize(range); } /*! @@ -162,10 +131,8 @@ resize(const IndexRange<3>& range) ProjDataInfo member. */ template -void -SegmentByView:: -grow(const IndexRange<3>& range) -{ +void +SegmentByView::grow(const IndexRange<3>& range) { resize(range); } @@ -175,5 +142,4 @@ grow(const IndexRange<3>& range) template class SegmentByView; - END_NAMESPACE_STIR diff --git a/src/buildblock/SeparableArrayFunctionObject.cxx b/src/buildblock/SeparableArrayFunctionObject.cxx index 051ccc79f2..86f8cef91b 100644 --- a/src/buildblock/SeparableArrayFunctionObject.cxx +++ b/src/buildblock/SeparableArrayFunctionObject.cxx @@ -34,40 +34,29 @@ START_NAMESPACE_STIR template -SeparableArrayFunctionObject:: -SeparableArrayFunctionObject() -: all_1d_array_filters(VectorWithOffset< shared_ptr > >(num_dim)) -{} +SeparableArrayFunctionObject::SeparableArrayFunctionObject() + : all_1d_array_filters(VectorWithOffset>>(num_dim)) {} template -SeparableArrayFunctionObject:: -SeparableArrayFunctionObject(const VectorWithOffset< shared_ptr > >& array_filters) -: all_1d_array_filters(array_filters) -{ +SeparableArrayFunctionObject::SeparableArrayFunctionObject( + const VectorWithOffset>>& array_filters) + : all_1d_array_filters(array_filters) { assert(all_1d_array_filters.get_length() == num_dim); } - template -void -SeparableArrayFunctionObject:: -do_it(Array& array) const -{ - if (!is_trivial()) - { +void +SeparableArrayFunctionObject::do_it(Array& array) const { + if (!is_trivial()) { #ifndef NDEBUG - // currently in_place_apply_array_functions_on_each_index doesn't handle 0 - // pointers gracefully, so we check here that there aren't any - for (typename VectorWithOffset< shared_ptr > >::const_iterator - iter=all_1d_array_filters.begin(); - iter!=all_1d_array_filters.end(); - ++iter) - assert(!is_null_ptr(*iter)); + // currently in_place_apply_array_functions_on_each_index doesn't handle 0 + // pointers gracefully, so we check here that there aren't any + for (typename VectorWithOffset>>::const_iterator iter = all_1d_array_filters.begin(); + iter != all_1d_array_filters.end(); ++iter) + assert(!is_null_ptr(*iter)); #endif - in_place_apply_array_functions_on_each_index(array, - all_1d_array_filters.begin(), - all_1d_array_filters.end()); - } + in_place_apply_array_functions_on_each_index(array, all_1d_array_filters.begin(), all_1d_array_filters.end()); + } } #if 0 @@ -80,7 +69,7 @@ do_it(Array& out_array, const Array& //if (!is_trivial()) { -#ifndef NDEBUG +# ifndef NDEBUG // currently apply_array_functions_on_each_index doesn't handle 0 // pointers gracefully, so we check here that there aren't any for ( VectorWithOffset< shared_ptr > >::const_iterator @@ -88,7 +77,7 @@ do_it(Array& out_array, const Array& iter!=all_1d_array_filters.end(); ++iter) assert(!is_null_ptr(*iter)); -#endif +# endif apply_array_functions_on_each_index(out_array, in_array, all_1d_array_filters.begin(), all_1d_array_filters.end()); @@ -100,26 +89,17 @@ do_it(Array& out_array, const Array& #endif template -bool -SeparableArrayFunctionObject:: -is_trivial() const -{ - for (typename VectorWithOffset< shared_ptr > >::const_iterator - iter=all_1d_array_filters.begin(); - iter!=all_1d_array_filters.end(); - ++iter) - { - if (!is_null_ptr(*iter) && !(*iter)->is_trivial()) - return false; - } - return true; +bool +SeparableArrayFunctionObject::is_trivial() const { + for (typename VectorWithOffset>>::const_iterator iter = all_1d_array_filters.begin(); + iter != all_1d_array_filters.end(); ++iter) { + if (!is_null_ptr(*iter) && !(*iter)->is_trivial()) + return false; + } + return true; } - // instantiation template class SeparableArrayFunctionObject<3, float>; END_NAMESPACE_STIR - - - diff --git a/src/buildblock/SeparableCartesianMetzImageFilter.cxx b/src/buildblock/SeparableCartesianMetzImageFilter.cxx index 3240aa9bde..9694e6e042 100644 --- a/src/buildblock/SeparableCartesianMetzImageFilter.cxx +++ b/src/buildblock/SeparableCartesianMetzImageFilter.cxx @@ -29,52 +29,39 @@ #include "stir/SeparableCartesianMetzImageFilter.h" #include "stir/VoxelsOnCartesianGrid.h" - START_NAMESPACE_STIR - template Succeeded -SeparableCartesianMetzImageFilter:: -virtual_set_up(const DiscretisedDensity<3,elemT>& density) +SeparableCartesianMetzImageFilter::virtual_set_up(const DiscretisedDensity<3, elemT>& density) { -/* if (consistency_check(density) == Succeeded::no) - return Succeeded::no; - */ - const VoxelsOnCartesianGrid& image = - dynamic_cast&>(density); + /* if (consistency_check(density) == Succeeded::no) + return Succeeded::no; + */ + const VoxelsOnCartesianGrid& image = dynamic_cast&>(density); + + metz_filter = + SeparableMetzArrayFilter<3, elemT>(get_metz_fwhms(), get_metz_powers(), image.get_voxel_size(), get_max_kernel_sizes()); - metz_filter = - SeparableMetzArrayFilter<3,elemT>(get_metz_fwhms(), - get_metz_powers(), - image.get_voxel_size(), - get_max_kernel_sizes()); - return Succeeded::yes; - } - template void -SeparableCartesianMetzImageFilter:: -virtual_apply(DiscretisedDensity<3,elemT>& density) const +SeparableCartesianMetzImageFilter::virtual_apply(DiscretisedDensity<3, elemT>& density) const -{ - //assert(consistency_check(density) == Succeeded::yes); - metz_filter(density); +{ + // assert(consistency_check(density) == Succeeded::yes); + metz_filter(density); } - template void -SeparableCartesianMetzImageFilter:: -virtual_apply(DiscretisedDensity<3,elemT>& out_density, - const DiscretisedDensity<3,elemT>& in_density) const -{ - //assert(consistency_check(in_density) == Succeeded::yes); - metz_filter(out_density,in_density); +SeparableCartesianMetzImageFilter::virtual_apply(DiscretisedDensity<3, elemT>& out_density, + const DiscretisedDensity<3, elemT>& in_density) const { + // assert(consistency_check(in_density) == Succeeded::yes); + metz_filter(out_density, in_density); } #if 0 @@ -104,50 +91,42 @@ consistency_check( const DiscretisedDensity<3, elemT>& image) const #endif template -SeparableCartesianMetzImageFilter:: -SeparableCartesianMetzImageFilter() -: fwhms(VectorWithOffset(1,3)), - metz_powers(VectorWithOffset(1,3)), - max_kernel_sizes(VectorWithOffset(1,3)) -{ +SeparableCartesianMetzImageFilter::SeparableCartesianMetzImageFilter() + : fwhms(VectorWithOffset(1, 3)), metz_powers(VectorWithOffset(1, 3)), + max_kernel_sizes(VectorWithOffset(1, 3)) { set_defaults(); } template VectorWithOffset -SeparableCartesianMetzImageFilter:: -get_metz_fwhms() const -{ return fwhms;} +SeparableCartesianMetzImageFilter::get_metz_fwhms() const { + return fwhms; +} template -VectorWithOffset -SeparableCartesianMetzImageFilter:: -get_metz_powers() const -{ return metz_powers;} - +VectorWithOffset +SeparableCartesianMetzImageFilter::get_metz_powers() const { + return metz_powers; +} template -VectorWithOffset -SeparableCartesianMetzImageFilter:: -get_max_kernel_sizes() const -{ return max_kernel_sizes;} +VectorWithOffset +SeparableCartesianMetzImageFilter::get_max_kernel_sizes() const { + return max_kernel_sizes; +} template void -SeparableCartesianMetzImageFilter:: -set_defaults() -{ +SeparableCartesianMetzImageFilter::set_defaults() { base_type::set_defaults(); fwhms.fill(0); - metz_powers.fill(0); + metz_powers.fill(0); max_kernel_sizes.fill(-1); } template -void -SeparableCartesianMetzImageFilter:: -initialise_keymap() -{ +void +SeparableCartesianMetzImageFilter::initialise_keymap() { base_type::initialise_keymap(); this->parser.add_start_key("Separable Cartesian Metz Filter Parameters"); this->parser.add_key("x-dir filter FWHM (in mm)", &fwhms[3]); @@ -155,25 +134,21 @@ initialise_keymap() this->parser.add_key("z-dir filter FWHM (in mm)", &fwhms[1]); this->parser.add_key("x-dir filter Metz power", &metz_powers[3]); this->parser.add_key("y-dir filter Metz power", &metz_powers[2]); - this->parser.add_key("z-dir filter Metz power", &metz_powers[1]); + this->parser.add_key("z-dir filter Metz power", &metz_powers[1]); this->parser.add_key("x-dir maximum kernel size", &max_kernel_sizes[3]); this->parser.add_key("y-dir maximum kernel size", &max_kernel_sizes[2]); this->parser.add_key("z-dir maximum kernel size", &max_kernel_sizes[1]); this->parser.add_stop_key("END Separable Cartesian Metz Filter Parameters"); } - template <> -const char * const -SeparableCartesianMetzImageFilter::registered_name = - "Separable Cartesian Metz"; - +const char* const SeparableCartesianMetzImageFilter::registered_name = "Separable Cartesian Metz"; -# ifdef _MSC_VER -// prevent warning message on reinstantiation, +#ifdef _MSC_VER +// prevent warning message on reinstantiation, // note that we get a linking error if we don't have the explicit instantiation below -# pragma warning(disable:4660) -# endif +# pragma warning(disable : 4660) +#endif // Register this class in the ImageProcessor registry // static SeparableCartesianMetzImageFilter::RegisterIt dummy; @@ -182,6 +157,3 @@ SeparableCartesianMetzImageFilter::registered_name = template class SeparableCartesianMetzImageFilter; END_NAMESPACE_STIR - - - diff --git a/src/buildblock/SeparableConvolutionImageFilter.cxx b/src/buildblock/SeparableConvolutionImageFilter.cxx index 7023ddd387..5d143ede49 100644 --- a/src/buildblock/SeparableConvolutionImageFilter.cxx +++ b/src/buildblock/SeparableConvolutionImageFilter.cxx @@ -1,8 +1,8 @@ /*! \file - \ingroup ImageProcessor + \ingroup ImageProcessor \brief Implementation of class stir::SeparableConvolutionImageFilter - + \author Kris Thielemans \author Sanida Mustafovic */ @@ -35,125 +35,88 @@ using std::max; START_NAMESPACE_STIR -template<> -const char * const -SeparableConvolutionImageFilter::registered_name = - "Separable Convolution"; - +template <> +const char* const SeparableConvolutionImageFilter::registered_name = "Separable Convolution"; template -void -SeparableConvolutionImageFilter:: -initialise_keymap() -{ +void +SeparableConvolutionImageFilter::initialise_keymap() { base_type::initialise_keymap(); this->parser.add_start_key("Separable Convolution Filter Parameters"); - this->parser.add_key("x-dir filter coefficients", &(*(filter_coefficients_for_parsing.begin()+2))); - this->parser.add_key("y-dir filter coefficients", &(*(filter_coefficients_for_parsing.begin()+1))); + this->parser.add_key("x-dir filter coefficients", &(*(filter_coefficients_for_parsing.begin() + 2))); + this->parser.add_key("y-dir filter coefficients", &(*(filter_coefficients_for_parsing.begin() + 1))); this->parser.add_key("z-dir filter coefficients", &(*filter_coefficients_for_parsing.begin())); this->parser.add_stop_key("END Separable Convolution Filter Parameters"); - } template -bool -SeparableConvolutionImageFilter:: -post_processing() -{ +bool +SeparableConvolutionImageFilter::post_processing() { if (base_type::post_processing() != false) return true; - // copy filter_coefficients_for_parsing to filter_coefficients + // copy filter_coefficients_for_parsing to filter_coefficients // todo drop any 0s at the start or end - typename VectorWithOffset< VectorWithOffset >::iterator - coefficients_iter = filter_coefficients.begin(); + typename VectorWithOffset>::iterator coefficients_iter = filter_coefficients.begin(); #ifndef STIR_NO_NAMESPACES - std:: + std:: #endif - vector< vector >::const_iterator - parsing_iter = filter_coefficients_for_parsing.begin(); - for (; - parsing_iter != filter_coefficients_for_parsing.end(); - ++parsing_iter, ++coefficients_iter) - { - const unsigned int size = static_cast(parsing_iter->size()); - const int min_index = -static_cast((size/2)); - if (size%2==0) - warning("Parsing SeparableConvolutionImageFilter\n" - "Even number of filter coefficients for the %d-th dimension." - "I'll (effectively) append a 0 at the end.\n", - coefficients_iter - filter_coefficients.begin() + 1); - - *coefficients_iter = VectorWithOffset(min_index, min_index + size - 1); - - // can't use std::copy because of cast. sigh. - typename VectorWithOffset::iterator - coefficients_elem_iter = coefficients_iter->begin(); + vector>::const_iterator parsing_iter = filter_coefficients_for_parsing.begin(); + for (; parsing_iter != filter_coefficients_for_parsing.end(); ++parsing_iter, ++coefficients_iter) { + const unsigned int size = static_cast(parsing_iter->size()); + const int min_index = -static_cast((size / 2)); + if (size % 2 == 0) + warning("Parsing SeparableConvolutionImageFilter\n" + "Even number of filter coefficients for the %d-th dimension." + "I'll (effectively) append a 0 at the end.\n", + coefficients_iter - filter_coefficients.begin() + 1); + + *coefficients_iter = VectorWithOffset(min_index, min_index + size - 1); + + // can't use std::copy because of cast. sigh. + typename VectorWithOffset::iterator coefficients_elem_iter = coefficients_iter->begin(); #ifndef STIR_NO_NAMESPACES - std:: + std:: #endif - vector::const_iterator - parsing_elem_iter = parsing_iter->begin(); - for (; - parsing_elem_iter != parsing_iter->end(); - ++parsing_elem_iter, ++coefficients_elem_iter) - *coefficients_elem_iter = static_cast(*parsing_elem_iter); - } + vector::const_iterator parsing_elem_iter = parsing_iter->begin(); + for (; parsing_elem_iter != parsing_iter->end(); ++parsing_elem_iter, ++coefficients_elem_iter) + *coefficients_elem_iter = static_cast(*parsing_elem_iter); + } return false; } template -SeparableConvolutionImageFilter:: -SeparableConvolutionImageFilter() -: filter_coefficients_for_parsing(3) -{ - set_defaults(); +SeparableConvolutionImageFilter::SeparableConvolutionImageFilter() : filter_coefficients_for_parsing(3) { + set_defaults(); } - template -SeparableConvolutionImageFilter:: -SeparableConvolutionImageFilter( - const VectorWithOffset< VectorWithOffset >& - filter_coefficients) - : - filter_coefficients_for_parsing(filter_coefficients.get_length()), - filter_coefficients(filter_coefficients) -{ - assert(filter_coefficients.get_length()==3);// num_dimensions +SeparableConvolutionImageFilter::SeparableConvolutionImageFilter( + const VectorWithOffset>& filter_coefficients) + : filter_coefficients_for_parsing(filter_coefficients.get_length()), filter_coefficients(filter_coefficients) { + assert(filter_coefficients.get_length() == 3); // num_dimensions - // copy filter_coefficients to filter_coefficients_for_parsing such + // copy filter_coefficients to filter_coefficients_for_parsing such // that get_parameters() works properly - typename VectorWithOffset< VectorWithOffset >::const_iterator - coefficients_iter = filter_coefficients.begin(); + typename VectorWithOffset>::const_iterator coefficients_iter = filter_coefficients.begin(); #ifndef STIR_NO_NAMESPACES - std:: + std:: #endif - vector< vector >::iterator - parsing_iter = filter_coefficients_for_parsing.begin(); - for (; - parsing_iter != filter_coefficients_for_parsing.end(); - ++parsing_iter, ++coefficients_iter) - { - // make sure that there are 0s appended such that parsing will read it back - // in correct place - const unsigned parsing_size = - 2*(max(coefficients_iter->get_max_index(), - -coefficients_iter->get_min_index())) + 1; - // make it long enough, and initialise with 0 - *parsing_iter = vector(coefficients_iter->get_length(), 0); - - for (int i = coefficients_iter->get_min_index(); - i <= coefficients_iter->get_max_index(); - ++i) - (*parsing_iter)[static_cast((parsing_size/2) + i)] = - (*coefficients_iter)[i]; - } - + vector>::iterator parsing_iter = filter_coefficients_for_parsing.begin(); + for (; parsing_iter != filter_coefficients_for_parsing.end(); ++parsing_iter, ++coefficients_iter) { + // make sure that there are 0s appended such that parsing will read it back + // in correct place + const unsigned parsing_size = 2 * (max(coefficients_iter->get_max_index(), -coefficients_iter->get_min_index())) + 1; + // make it long enough, and initialise with 0 + *parsing_iter = vector(coefficients_iter->get_length(), 0); + + for (int i = coefficients_iter->get_min_index(); i <= coefficients_iter->get_max_index(); ++i) + (*parsing_iter)[static_cast((parsing_size / 2) + i)] = (*coefficients_iter)[i]; + } } #if 0 @@ -168,68 +131,48 @@ get_filter_coefficients() template Succeeded -SeparableConvolutionImageFilter:: -virtual_set_up(const DiscretisedDensity<3,elemT>& density) -{ - VectorWithOffset< shared_ptr > > - all_1d_filters(filter_coefficients.get_min_index(), - filter_coefficients.get_max_index()); - - typename VectorWithOffset< VectorWithOffset >::const_iterator - coefficients_iter = filter_coefficients.begin(); - typename VectorWithOffset< shared_ptr > >::iterator - filter_iter = all_1d_filters.begin(); - for (; - coefficients_iter != filter_coefficients.end(); - ++filter_iter, ++coefficients_iter) - { - - filter_iter->reset(new ArrayFilter1DUsingConvolution(*coefficients_iter)); - } - filter = SeparableArrayFunctionObject<3,elemT>(all_1d_filters); +SeparableConvolutionImageFilter::virtual_set_up(const DiscretisedDensity<3, elemT>& density) { + VectorWithOffset>> all_1d_filters(filter_coefficients.get_min_index(), + filter_coefficients.get_max_index()); + + typename VectorWithOffset>::const_iterator coefficients_iter = filter_coefficients.begin(); + typename VectorWithOffset>>::iterator filter_iter = all_1d_filters.begin(); + for (; coefficients_iter != filter_coefficients.end(); ++filter_iter, ++coefficients_iter) { + + filter_iter->reset(new ArrayFilter1DUsingConvolution(*coefficients_iter)); + } + filter = SeparableArrayFunctionObject<3, elemT>(all_1d_filters); return Succeeded::yes; - } - template void -SeparableConvolutionImageFilter:: -virtual_apply(DiscretisedDensity<3,elemT>& density) const - -{ - filter(density); +SeparableConvolutionImageFilter::virtual_apply(DiscretisedDensity<3, elemT>& density) const +{ + filter(density); } - template void -SeparableConvolutionImageFilter:: -virtual_apply(DiscretisedDensity<3,elemT>& out_density, - const DiscretisedDensity<3,elemT>& in_density) const -{ - filter(out_density,in_density); +SeparableConvolutionImageFilter::virtual_apply(DiscretisedDensity<3, elemT>& out_density, + const DiscretisedDensity<3, elemT>& in_density) const { + filter(out_density, in_density); } - template void -SeparableConvolutionImageFilter:: -set_defaults() -{ +SeparableConvolutionImageFilter::set_defaults() { base_type::set_defaults(); - filter_coefficients = - VectorWithOffset< VectorWithOffset >(3); + filter_coefficients = VectorWithOffset>(3); } - -# ifdef _MSC_VER -// prevent warning message on reinstantiation, +#ifdef _MSC_VER +// prevent warning message on reinstantiation, // note that we get a linking error if we don't have the explicit instantiation below -# pragma warning(disable:4660) -# endif +# pragma warning(disable : 4660) +#endif template class SeparableConvolutionImageFilter; diff --git a/src/buildblock/SeparableGaussianArrayFilter.cxx b/src/buildblock/SeparableGaussianArrayFilter.cxx index 5518cf2557..781d15a8af 100644 --- a/src/buildblock/SeparableGaussianArrayFilter.cxx +++ b/src/buildblock/SeparableGaussianArrayFilter.cxx @@ -53,130 +53,105 @@ using std::cerr; using std::endl; #endif - START_NAMESPACE_STIR template -SeparableGaussianArrayFilter:: -SeparableGaussianArrayFilter() -:fwhms(0),max_kernel_sizes(0) -{ - for (int i=1;i<=num_dimensions;i++) +SeparableGaussianArrayFilter::SeparableGaussianArrayFilter() : fwhms(0), max_kernel_sizes(0) { + for (int i = 1; i <= num_dimensions; i++) { - this->all_1d_array_filters[i-1]. - reset(new ArrayFilter1DUsingConvolution()); + this->all_1d_array_filters[i - 1].reset(new ArrayFilter1DUsingConvolution()); } } template -SeparableGaussianArrayFilter:: -SeparableGaussianArrayFilter(const float fwhms_v,const float max_kernel_sizes_v, bool normalise) -:fwhms(fwhms_v),max_kernel_sizes(max_kernel_sizes_v) - { - - //normalisation to 1 is optinal - construct_filter(normalise); - } +SeparableGaussianArrayFilter::SeparableGaussianArrayFilter(const float fwhms_v, + const float max_kernel_sizes_v, bool normalise) + : fwhms(fwhms_v), max_kernel_sizes(max_kernel_sizes_v) { + // normalisation to 1 is optinal + construct_filter(normalise); +} -template -SeparableGaussianArrayFilter:: -SeparableGaussianArrayFilter(const BasicCoordinate< num_dimensions,float>& fwhms_v, - const BasicCoordinate< num_dimensions,int>& max_kernel_sizes_v, bool normalise) +template +SeparableGaussianArrayFilter::SeparableGaussianArrayFilter( + const BasicCoordinate& fwhms_v, const BasicCoordinate& max_kernel_sizes_v, + bool normalise) -:fwhms(fwhms_v),max_kernel_sizes(max_kernel_sizes_v) -{ - construct_filter(normalise); + : fwhms(fwhms_v), max_kernel_sizes(max_kernel_sizes_v) { + construct_filter(normalise); } - template void -SeparableGaussianArrayFilter:: -construct_filter(bool normalise) -{ - for (int i = 1; i<=num_dimensions;i++) - { +SeparableGaussianArrayFilter::construct_filter(bool normalise) { + for (int i = 1; i <= num_dimensions; i++) { VectorWithOffset filter_coefficients; - calculate_coefficients(filter_coefficients, max_kernel_sizes[i], - fwhms[i],normalise); + calculate_coefficients(filter_coefficients, max_kernel_sizes[i], fwhms[i], normalise); { - std::stringstream ss; - ss << "Gaussian filter dim[" << i << "] =" << filter_coefficients; - info(ss.str(), 2); + std::stringstream ss; + ss << "Gaussian filter dim[" << i << "] =" << filter_coefficients; + info(ss.str(), 2); } - this->all_1d_array_filters[i-1]. - reset(new ArrayFilter1DUsingConvolution(filter_coefficients)); - - } + this->all_1d_array_filters[i - 1].reset(new ArrayFilter1DUsingConvolution(filter_coefficients)); + } } -template +template void -SeparableGaussianArrayFilter:: -calculate_coefficients(VectorWithOffset& filter_coefficients, const int max_kernel_sizes, - const float fwhms, bool normalise) +SeparableGaussianArrayFilter::calculate_coefficients(VectorWithOffset& filter_coefficients, + const int max_kernel_sizes, const float fwhms, + bool normalise) { - if (max_kernel_sizes==0) + if (max_kernel_sizes == 0) error("SeparableGaussianArrayFilter called with max_kernel_size==0 (use -1 for auto-length)"); - const double standard_deviation = sqrt(fwhms*fwhms/(8*log(2.))); + const double standard_deviation = sqrt(fwhms * fwhms / (8 * log(2.))); - if (standard_deviation==0) - { - filter_coefficients.recycle(); - return; + if (standard_deviation == 0) { + filter_coefficients.recycle(); + return; } - int kernel_length = max_kernel_sizes/2; - if (max_kernel_sizes<0) - { - const double ZERO_TOL= 0.000001; // consistent with other files - // find the value x where a normal distribution is ZERO_TOL/sqrt(2 pi) - const double normal_max_x = sqrt(-2*log(ZERO_TOL)); - // rescale to actual - const double max_x = normal_max_x * standard_deviation; - kernel_length = static_cast(ceil(max_x)); - } - filter_coefficients.grow(-kernel_length,kernel_length); - - filter_coefficients[0] = static_cast(1/sqrt(2*_PI)/standard_deviation); - - for (int i = 1; i<=kernel_length;i++) - { - filter_coefficients[i] = - filter_coefficients[-i]= static_cast( - exp(-square(i)/(2.*square(standard_deviation)))/ - sqrt(2*square(standard_deviation)*_PI)); + int kernel_length = max_kernel_sizes / 2; + if (max_kernel_sizes < 0) { + const double ZERO_TOL = 0.000001; // consistent with other files + // find the value x where a normal distribution is ZERO_TOL/sqrt(2 pi) + const double normal_max_x = sqrt(-2 * log(ZERO_TOL)); + // rescale to actual + const double max_x = normal_max_x * standard_deviation; + kernel_length = static_cast(ceil(max_x)); } + filter_coefficients.grow(-kernel_length, kernel_length); -// normalisation: rescaled to dc =1 + filter_coefficients[0] = static_cast(1 / sqrt(2 * _PI) / standard_deviation); -if (normalise) + for (int i = 1; i <= kernel_length; i++) { + filter_coefficients[i] = filter_coefficients[-i] = + static_cast(exp(-square(i) / (2. * square(standard_deviation))) / sqrt(2 * square(standard_deviation) * _PI)); + } - { + // normalisation: rescaled to dc =1 - double sum = 0.; - for (int i =filter_coefficients.get_min_index();i<=filter_coefficients.get_max_index();i++) - { - sum +=double (filter_coefficients[i]); - } + if (normalise) - for (int i =filter_coefficients.get_min_index();i<=filter_coefficients.get_max_index();i++) - { - filter_coefficients[i] /= sum; - } + { + double sum = 0.; + for (int i = filter_coefficients.get_min_index(); i <= filter_coefficients.get_max_index(); i++) { + sum += double(filter_coefficients[i]); } + for (int i = filter_coefficients.get_min_index(); i <= filter_coefficients.get_max_index(); i++) { + filter_coefficients[i] /= sum; + } + } } - -template class SeparableGaussianArrayFilter<3,float>; +template class SeparableGaussianArrayFilter<3, float>; END_NAMESPACE_STIR diff --git a/src/buildblock/SeparableGaussianImageFilter.cxx b/src/buildblock/SeparableGaussianImageFilter.cxx index fe98226c19..8a85325351 100644 --- a/src/buildblock/SeparableGaussianImageFilter.cxx +++ b/src/buildblock/SeparableGaussianImageFilter.cxx @@ -35,154 +35,116 @@ START_NAMESPACE_STIR -//TODO remove define +// TODO remove define #define num_dimensions 3 - template Succeeded -SeparableGaussianImageFilter:: -virtual_set_up(const DiscretisedDensity<3,elemT>& density) -{ - +SeparableGaussianImageFilter::virtual_set_up(const DiscretisedDensity<3, elemT>& density) { - if(dynamic_cast*>(&density)== 0) + if (dynamic_cast*>(&density) == 0) - { - warning("SeparableGaussianImageFilter can only handle images of type VoxelsOnCartesianGrid"); - return Succeeded::no; - } + { + warning("SeparableGaussianImageFilter can only handle images of type VoxelsOnCartesianGrid"); + return Succeeded::no; + } - - const BasicCoordinate< num_dimensions,float> rescale = dynamic_cast*>(&density)->get_grid_spacing(); - gaussian_filter = SeparableGaussianArrayFilter(fwhms/rescale,max_kernel_sizes,normalise); + const BasicCoordinate rescale = + dynamic_cast*>(&density)->get_grid_spacing(); + gaussian_filter = SeparableGaussianArrayFilter(fwhms / rescale, max_kernel_sizes, normalise); return Succeeded::yes; - } - template void -SeparableGaussianImageFilter:: -virtual_apply(DiscretisedDensity<3,elemT>& density) const +SeparableGaussianImageFilter::virtual_apply(DiscretisedDensity<3, elemT>& density) const { - gaussian_filter(density); - + gaussian_filter(density); } - template void -SeparableGaussianImageFilter:: -virtual_apply(DiscretisedDensity<3,elemT>& out_density, - const DiscretisedDensity<3,elemT>& in_density) const -{ +SeparableGaussianImageFilter::virtual_apply(DiscretisedDensity<3, elemT>& out_density, + const DiscretisedDensity<3, elemT>& in_density) const { - gaussian_filter(out_density,in_density); + gaussian_filter(out_density, in_density); } - template -SeparableGaussianImageFilter:: -SeparableGaussianImageFilter() -: fwhms(0),max_kernel_sizes(0) -{ +SeparableGaussianImageFilter::SeparableGaussianImageFilter() : fwhms(0), max_kernel_sizes(0) { set_defaults(); } template -BasicCoordinate< num_dimensions,float> -SeparableGaussianImageFilter:: -get_fwhms() -{ +BasicCoordinate +SeparableGaussianImageFilter::get_fwhms() { return fwhms; } template bool -SeparableGaussianImageFilter:: -get_normalised_filter() -{ - return normalise; +SeparableGaussianImageFilter::get_normalised_filter() { + return normalise; } - template -BasicCoordinate< num_dimensions,int> -SeparableGaussianImageFilter:: -get_max_kernel_sizes() -{ +BasicCoordinate +SeparableGaussianImageFilter::get_max_kernel_sizes() { return max_kernel_sizes; } - template void -SeparableGaussianImageFilter:: -set_defaults() -{ - base_type::set_defaults(); - fwhms.fill(0); - max_kernel_sizes.fill(-1); - normalise = true; - +SeparableGaussianImageFilter::set_defaults() { + base_type::set_defaults(); + fwhms.fill(0); + max_kernel_sizes.fill(-1); + normalise = true; } template void -SeparableGaussianImageFilter:: -initialise_keymap() -{ - base_type::initialise_keymap(); - this->parser.add_start_key("Separable Gaussian Filter Parameters"); - this->parser.add_key("x-dir filter FWHM (in mm)", &fwhms[3]); - this->parser.add_key("y-dir filter FWHM (in mm)", &fwhms[2]); - this->parser.add_key("z-dir filter FWHM (in mm)", &fwhms[1]); - this->parser.add_key("x-dir maximum kernel size", &max_kernel_sizes[3]); - this->parser.add_key("y-dir maximum kernel size", &max_kernel_sizes[2]); - this->parser.add_key("z-dir maximum kernel size", &max_kernel_sizes[1]); - this->parser.add_key("Normalise filter to 1", &normalise); - this->parser.add_stop_key("END Separable Gaussian Filter Parameters"); +SeparableGaussianImageFilter::initialise_keymap() { + base_type::initialise_keymap(); + this->parser.add_start_key("Separable Gaussian Filter Parameters"); + this->parser.add_key("x-dir filter FWHM (in mm)", &fwhms[3]); + this->parser.add_key("y-dir filter FWHM (in mm)", &fwhms[2]); + this->parser.add_key("z-dir filter FWHM (in mm)", &fwhms[1]); + this->parser.add_key("x-dir maximum kernel size", &max_kernel_sizes[3]); + this->parser.add_key("y-dir maximum kernel size", &max_kernel_sizes[2]); + this->parser.add_key("z-dir maximum kernel size", &max_kernel_sizes[1]); + this->parser.add_key("Normalise filter to 1", &normalise); + this->parser.add_stop_key("END Separable Gaussian Filter Parameters"); } template void -SeparableGaussianImageFilter:: -set_fwhms(const BasicCoordinate< num_dimensions,float>& arg) -{ - fwhms = BasicCoordinate(arg); +SeparableGaussianImageFilter::set_fwhms(const BasicCoordinate& arg) { + fwhms = BasicCoordinate(arg); } template void -SeparableGaussianImageFilter:: -set_max_kernel_sizes(const BasicCoordinate< num_dimensions,int>& arg) -{ - max_kernel_sizes = BasicCoordinate(arg); +SeparableGaussianImageFilter::set_max_kernel_sizes(const BasicCoordinate& arg) { + max_kernel_sizes = BasicCoordinate(arg); } template void -SeparableGaussianImageFilter:: -set_normalise(const bool arg) -{ +SeparableGaussianImageFilter::set_normalise(const bool arg) { normalise = arg; } +template <> +const char* const SeparableGaussianImageFilter::registered_name = "Separable Gaussian"; -template<> -const char * const -SeparableGaussianImageFilter::registered_name = - "Separable Gaussian"; - - -# ifdef _MSC_VER -// prevent warning message on reinstantiation, +#ifdef _MSC_VER +// prevent warning message on reinstantiation, // note that we get a linking error if we don't have the explicit instantiation below -# pragma warning(disable:4660) -# endif +# pragma warning(disable : 4660) +#endif // Register this class in the ImageProcessor registry // static SeparableGaussianImageFilter::RegisterIt dummy; diff --git a/src/buildblock/SeparableMetzArrayFilter.cxx b/src/buildblock/SeparableMetzArrayFilter.cxx index 2e9c659e5d..6c3cc23dc2 100644 --- a/src/buildblock/SeparableMetzArrayFilter.cxx +++ b/src/buildblock/SeparableMetzArrayFilter.cxx @@ -47,201 +47,172 @@ using std::endl; START_NAMESPACE_STIR +const float ZERO_TOL = 0.000001F; // MJ 12/05/98 Made consistent with other files +const double TPI = 6.28318530717958647692; -const float ZERO_TOL= 0.000001F; //MJ 12/05/98 Made consistent with other files -const double TPI=6.28318530717958647692; - -// build Gaussian kernel according to the full width half maximum +// build Gaussian kernel according to the full width half maximum template -static void build_gauss(VectorWithOffset&kernel, - int res,float s2, float sampling_interval); +static void build_gauss(VectorWithOffset& kernel, int res, float s2, float sampling_interval); template -static void build_metz(VectorWithOffset&kernel, - float N,float fwhm, float MmPerVoxel, int max_kernel_size); - - +static void build_metz(VectorWithOffset& kernel, float N, float fwhm, float MmPerVoxel, int max_kernel_size); template -SeparableMetzArrayFilter:: -SeparableMetzArrayFilter - (const VectorWithOffset& fwhms_v, - const VectorWithOffset& metz_powers_v, - const BasicCoordinate& sampling_distances_v, - const VectorWithOffset& max_kernel_sizes_v) - : fwhms(fwhms_v), - metz_powers(metz_powers_v), - sampling_distances(sampling_distances_v), - max_kernel_sizes(max_kernel_sizes_v) -{ +SeparableMetzArrayFilter::SeparableMetzArrayFilter( + const VectorWithOffset& fwhms_v, const VectorWithOffset& metz_powers_v, + const BasicCoordinate& sampling_distances_v, const VectorWithOffset& max_kernel_sizes_v) + : fwhms(fwhms_v), metz_powers(metz_powers_v), sampling_distances(sampling_distances_v), max_kernel_sizes(max_kernel_sizes_v) { assert(metz_powers.get_length() == num_dimensions); assert(fwhms.get_length() == num_dimensions); assert(metz_powers.get_min_index() == 1); assert(fwhms.get_min_index() == 1); assert(max_kernel_sizes.get_length() == num_dimensions); assert(max_kernel_sizes.get_min_index() == 1); - - for (int i=1; i<=num_dimensions; ++i) - { + + for (int i = 1; i <= num_dimensions; ++i) { VectorWithOffset kernel; - build_metz(kernel, metz_powers[i],fwhms[i],sampling_distances[i],max_kernel_sizes[i]); - - for (int j=0;j0.0) printf ("%d-dir Metz[%d]=%f\n",i,j,kernel[j]); - else printf ("%d-dir Gauss[%d]=%f\n",i,j,kernel[j]); - - - this->all_1d_array_filters[i-1].reset(new ArrayFilter1DUsingConvolutionSymmetricKernel(kernel)); - + build_metz(kernel, metz_powers[i], fwhms[i], sampling_distances[i], max_kernel_sizes[i]); + + for (int j = 0; j < kernel.get_length(); j++) + if (metz_powers[i] > 0.0) + printf("%d-dir Metz[%d]=%f\n", i, j, kernel[j]); + else + printf("%d-dir Gauss[%d]=%f\n", i, j, kernel[j]); + + this->all_1d_array_filters[i - 1].reset(new ArrayFilter1DUsingConvolutionSymmetricKernel(kernel)); } } template -void build_gauss(VectorWithOffset&kernel, int res,float s2, float sampling_interval) -{ - - +void +build_gauss(VectorWithOffset& kernel, int res, float s2, float sampling_interval) { + elemT sum; - int cutoff=0; - int j,hres; - - - - hres = res/2; - kernel[hres-1] = static_cast(1/sqrt(s2*TPI)); - sum = kernel[hres-1]; - kernel[res-1] = 0; - for (j=1;(j(kernel[hres-1]* exp(-0.5*(j*sampling_interval)*(j*sampling_interval)/s2)); - kernel[hres+j-1] = kernel [hres-j-1]; - sum += 2.0F * kernel[hres-j-1]; - if (kernel[hres-j-1] (1 / sqrt(s2 * TPI)); + sum = kernel[hres - 1]; + kernel[res - 1] = 0; + for (j = 1; (j < hres && !cutoff); j++) { + kernel[hres - j - 1] = + static_cast(kernel[hres - 1] * exp(-0.5 * (j * sampling_interval) * (j * sampling_interval) / s2)); + kernel[hres + j - 1] = kernel[hres - j - 1]; + sum += 2.0F * kernel[hres - j - 1]; + if (kernel[hres - j - 1] < kernel[hres - 1] * ZERO_TOL) + cutoff = 1; + } + /* Normalize the filter to 1 */ - for (j=0;j +void +build_metz(VectorWithOffset& kernel, float N, float fwhm, float MmPerVox, int max_kernel_size) { + int kernel_length = 0; + if (fwhm > 0.0F) { + // MJ 12/05/98 compute parameters relevant to DFT/IDFT + elemT s2 = fwhm * fwhm / (8 * log(2.F)); // variance in Mm + const int n = 7; // determines cut-off in both space and frequency domains + const elemT sinc_length = 10000.0F; + int samples_per_voxel = (int)(MmPerVox * 2 * sqrt(2 * log(10.F) * n / s2) / TPI + 1); + const elemT sampling_interval = MmPerVox / samples_per_voxel; + elemT stretch = (samples_per_voxel > 1) ? sinc_length : 0.0F; + + int Res = (int)(log((sqrt(8 * n * log(10.) * s2) + stretch) / sampling_interval) / log(2.) + 1); + Res = (int)pow(2.0, (double)Res); // MJ 12/05/98 made adaptive -//MJ 19/04/99 Used KT's solution to the shifted index problem. Also build_metz now allocates the kernel. -template -void build_metz(VectorWithOffset& kernel, - float N,float fwhm, float MmPerVox, int max_kernel_size) -{ - - int kernel_length = 0; - - if(fwhm>0.0F){ - - //MJ 12/05/98 compute parameters relevant to DFT/IDFT - - elemT s2 = fwhm*fwhm/(8*log(2.F)); //variance in Mm - - const int n=7; //determines cut-off in both space and frequency domains - const elemT sinc_length=10000.0F; - int samples_per_voxel=(int)(MmPerVox*2*sqrt(2*log(10.F)*n/s2)/TPI +1); - const elemT sampling_interval=MmPerVox/samples_per_voxel; - elemT stretch= (samples_per_voxel>1)?sinc_length:0.0F; - - int Res=(int)(log((sqrt(8*n*log(10.)*s2)+stretch)/sampling_interval)/log(2.)+1); - Res=(int) pow(2.0,(double) Res); //MJ 12/05/98 made adaptive - info(boost::format("Filter parameters:\n" - "Variance: %1%\n" - "Voxel dimension (in mm): %2%\n" - "Samples per voxel: %3%\n" - "Sampling interval (in mm): %4%\n" - "FFT vector length: %5%") - % s2 % MmPerVox % samples_per_voxel % sampling_interval % Res); - + "Variance: %1%\n" + "Voxel dimension (in mm): %2%\n" + "Samples per voxel: %3%\n" + "Sampling interval (in mm): %4%\n" + "FFT vector length: %5%") % + s2 % MmPerVox % samples_per_voxel % sampling_interval % Res); + /* allocate memory to metz arrays */ VectorWithOffset filter(Res); filter.fill(0.0); - //The former technique was illegal. - Array<1,std::complex > fftdata(0,Res-1); - fftdata.fill(0.0); - + // The former technique was illegal. + Array<1, std::complex> fftdata(0, Res - 1); + fftdata.fill(0.0); + /* build gaussian */ - build_gauss(filter,Res,s2,sampling_interval); + build_gauss(filter, Res, s2, sampling_interval); /* Build the fft array*/ - for (int i=0;i<=Res-(Res/2);i++) { - fftdata[i].real(filter[Res/2-1+i]); + for (int i = 0; i <= Res - (Res / 2); i++) { + fftdata[i].real(filter[Res / 2 - 1 + i]); } - - for (int i=1;i<(Res/2);i++) { - fftdata[Res-(Res/2)+i].real(filter[i-1]); + + for (int i = 1; i < (Res / 2); i++) { + fftdata[Res - (Res / 2) + i].real(filter[i - 1]); } /* FFT to frequency space */ fourier(fftdata); - /* Build Metz */ + /* Build Metz */ N++; - - int cutoff=(int) (sampling_interval*Res/(2*MmPerVox)); - //cerr<0.0){ - // cerr<cutoff && Res-i>cutoff) zabs2=0; - - } - - if (zabs2>1) zabs2=static_cast (1-ZERO_TOL); - if (zabs2>0) { - // if (zabs2>=1) cerr< 0.0) { + // cerr< cutoff && Res - i > cutoff) + zabs2 = 0; + } + + if (zabs2 > 1) + zabs2 = static_cast(1 - ZERO_TOL); + if (zabs2 > 0) { + // if (zabs2>=1) cerr<=0;i--){ - if (fabs((double) filter[i])>=(0.0001)*filter[0]) break; - else (kernel_length)--; - + kernel_length = Res; + + for (int i = Res - 1; i >= 0; i--) { + if (fabs((double)filter[i]) >= (0.0001) * filter[0]) + break; + else + (kernel_length)--; } - - + #if 0 // SM&KT 04/04/2001 removed this truncation of the kernel as we don't have the relevant parameter anymore if ((kernel_length)>length_of_row_to_filter/2){ @@ -249,36 +220,31 @@ void build_metz(VectorWithOffset& kernel, } #endif - if (max_kernel_size>0 && (kernel_length)>max_kernel_size/2){ - kernel_length=max_kernel_size/2; + if (max_kernel_size > 0 && (kernel_length) > max_kernel_size / 2) { + kernel_length = max_kernel_size / 2; } - - //VectorWithOffset kernel(kernel_length);//=new elemT[(kernel_length)]; - kernel.grow(0,kernel_length-1); - - for (int i=0;i<(kernel_length);i++) kernel[i]=filter[i]; - - //return kernel; + + // VectorWithOffset kernel(kernel_length);//=new elemT[(kernel_length)]; + kernel.grow(0, kernel_length - 1); + + for (int i = 0; i < (kernel_length); i++) + kernel[i] = filter[i]; + + // return kernel; } - - else{ - //VectorWithOffset kernel(1);//=new elemT[1]; - kernel.grow(0,0); + + else { + // VectorWithOffset kernel(1);//=new elemT[1]; + kernel.grow(0, 0); //*kernel=1.0F; - //kernel_length=1L; + // kernel_length=1L; kernel[0] = 1.F; - kernel_length=1; - - //return kernel; - } + kernel_length = 1; + // return kernel; + } } - - template class SeparableMetzArrayFilter<3, float>; END_NAMESPACE_STIR - - - diff --git a/src/buildblock/Sinogram.cxx b/src/buildblock/Sinogram.cxx index 4beccc7cf0..5b2dd1cb33 100644 --- a/src/buildblock/Sinogram.cxx +++ b/src/buildblock/Sinogram.cxx @@ -35,102 +35,67 @@ #ifdef _MSC_VER // disable warning that not all functions have been implemented when instantiating -#pragma warning(disable: 4661) +# pragma warning(disable : 4661) #endif // _MSC_VER using std::string; START_NAMESPACE_STIR -template +template bool -Sinogram:: -has_same_characteristics(self_type const& other, - string& explanation) const -{ +Sinogram::has_same_characteristics(self_type const& other, string& explanation) const { using boost::format; using boost::str; - if (*this->get_proj_data_info_sptr() != - *other.get_proj_data_info_sptr()) - { - explanation = - str(format("Differing projection data info:\n%1%\n-------- vs-------\n %2%") - % this->get_proj_data_info_sptr()->parameter_info() - % other.get_proj_data_info_sptr()->parameter_info() - ); - return false; - } - if (this->get_axial_pos_num() != - other.get_axial_pos_num()) - { - explanation = - str(format("Differing axial position number: %1% vs %2%") - % this->get_axial_pos_num() - % other.get_axial_pos_num() - ); - return false; - } - if (this->get_segment_num() != - other.get_segment_num()) - { - explanation = - str(format("Differing segment number: %1% vs %2%") - % this->get_segment_num() - % other.get_segment_num() - ); - return false; - } - if (this->get_timing_pos_num() != - other.get_timing_pos_num()) - { - explanation = - str(format("Differing timing position index: %1% vs %2%") - % this->get_timing_pos_num() - % other.get_timing_pos_num() - ); - return false; - } + if (*this->get_proj_data_info_sptr() != *other.get_proj_data_info_sptr()) { + explanation = str(format("Differing projection data info:\n%1%\n-------- vs-------\n %2%") % + this->get_proj_data_info_sptr()->parameter_info() % other.get_proj_data_info_sptr()->parameter_info()); + return false; + } + if (this->get_axial_pos_num() != other.get_axial_pos_num()) { + explanation = + str(format("Differing axial position number: %1% vs %2%") % this->get_axial_pos_num() % other.get_axial_pos_num()); + return false; + } + if (this->get_segment_num() != other.get_segment_num()) { + explanation = str(format("Differing segment number: %1% vs %2%") % this->get_segment_num() % other.get_segment_num()); + return false; + } + if (this->get_timing_pos_num() != other.get_timing_pos_num()) { + explanation = + str(format("Differing timing position index: %1% vs %2%") % this->get_timing_pos_num() % other.get_timing_pos_num()); + return false; + } return true; } -template +template bool -Sinogram:: -has_same_characteristics(self_type const& other) const -{ +Sinogram::has_same_characteristics(self_type const& other) const { std::string explanation; return this->has_same_characteristics(other, explanation); } -template -bool -Sinogram:: -operator ==(const self_type& that) const -{ - return - this->has_same_characteristics(that) && - base_type::operator==(that); +template +bool +Sinogram::operator==(const self_type& that) const { + return this->has_same_characteristics(that) && base_type::operator==(that); } - -template -bool -Sinogram:: -operator !=(const self_type& that) const -{ + +template +bool +Sinogram::operator!=(const self_type& that) const { return !((*this) == that); } - /*! This makes sure that the new Array dimensions are the same as those in the ProjDataInfo member. */ template -void -Sinogram:: -resize(const IndexRange<2>& range) -{ +void +Sinogram::resize(const IndexRange<2>& range) { if (range == this->get_index_range()) return; @@ -138,17 +103,16 @@ resize(const IndexRange<2>& range) // TODO assert(range.get_min_index() == 0); - - shared_ptr pdi_ptr (proj_data_info_ptr->clone()); - + + shared_ptr pdi_ptr(proj_data_info_ptr->clone()); + pdi_ptr->set_num_views(range.get_max_index() + 1); pdi_ptr->set_min_tangential_pos_num(range[0].get_min_index()); pdi_ptr->set_max_tangential_pos_num(range[0].get_max_index()); proj_data_info_ptr = pdi_ptr; - Array<2,elemT>::resize(range); - + Array<2, elemT>::resize(range); } /*! @@ -156,10 +120,8 @@ resize(const IndexRange<2>& range) ProjDataInfo member. */ template -void -Sinogram:: -grow(const IndexRange<2>& range) -{ +void +Sinogram::grow(const IndexRange<2>& range) { resize(range); } diff --git a/src/buildblock/TextWriter.cxx b/src/buildblock/TextWriter.cxx index db41f3e387..a18402d6c3 100644 --- a/src/buildblock/TextWriter.cxx +++ b/src/buildblock/TextWriter.cxx @@ -6,18 +6,19 @@ aTextWriter* TextWriterHandle::information_channel_; aTextWriter* TextWriterHandle::warning_channel_; aTextWriter* TextWriterHandle::error_channel_; -void writeText(const char* text, OUTPUT_CHANNEL channel) { - TextWriterHandle h; - switch (channel) { - case INFORMATION_CHANNEL: - h.print_information(text); - break; - case WARNING_CHANNEL: - h.print_warning(text); - break; - case ERROR_CHANNEL: - h.print_error(text); - } +void +writeText(const char* text, OUTPUT_CHANNEL channel) { + TextWriterHandle h; + switch (channel) { + case INFORMATION_CHANNEL: + h.print_information(text); + break; + case WARNING_CHANNEL: + h.print_warning(text); + break; + case ERROR_CHANNEL: + h.print_error(text); + } } END_NAMESPACE_STIR diff --git a/src/buildblock/ThresholdMinToSmallPositiveValueDataProcessor.cxx b/src/buildblock/ThresholdMinToSmallPositiveValueDataProcessor.cxx index e64e034f7a..23e4e32267 100644 --- a/src/buildblock/ThresholdMinToSmallPositiveValueDataProcessor.cxx +++ b/src/buildblock/ThresholdMinToSmallPositiveValueDataProcessor.cxx @@ -28,94 +28,70 @@ #include "stir/ThresholdMinToSmallPositiveValueDataProcessor.h" #include "stir/thresholding.h" #include "stir/DiscretisedDensity.h" -#include "stir/modelling/ParametricDiscretisedDensity.h" -#include "stir/modelling/KineticParameters.h" +#include "stir/modelling/ParametricDiscretisedDensity.h" +#include "stir/modelling/KineticParameters.h" START_NAMESPACE_STIR - template Succeeded -ThresholdMinToSmallPositiveValueDataProcessor:: -virtual_set_up(const DataT& density) +ThresholdMinToSmallPositiveValueDataProcessor::virtual_set_up(const DataT& density) { - return Succeeded::yes; + return Succeeded::yes; } - template void -ThresholdMinToSmallPositiveValueDataProcessor:: -virtual_apply(DataT& data) const +ThresholdMinToSmallPositiveValueDataProcessor::virtual_apply(DataT& data) const -{ +{ threshold_min_to_small_positive_value(data.begin_all(), data.end_all(), 0.000001F); - //threshold_min_to_small_positive_value_and_truncate_rim(data, 0); + // threshold_min_to_small_positive_value_and_truncate_rim(data, 0); } - template void -ThresholdMinToSmallPositiveValueDataProcessor:: -virtual_apply(DataT& out_data, - const DataT& in_data) const -{ +ThresholdMinToSmallPositiveValueDataProcessor::virtual_apply(DataT& out_data, const DataT& in_data) const { out_data = in_data; threshold_min_to_small_positive_value(out_data.begin_all(), out_data.end_all(), 0.000001F); } template -ThresholdMinToSmallPositiveValueDataProcessor:: -ThresholdMinToSmallPositiveValueDataProcessor() -{ +ThresholdMinToSmallPositiveValueDataProcessor::ThresholdMinToSmallPositiveValueDataProcessor() { set_defaults(); } template void -ThresholdMinToSmallPositiveValueDataProcessor:: -set_defaults() -{ +ThresholdMinToSmallPositiveValueDataProcessor::set_defaults() { base_type::set_defaults(); } template -void -ThresholdMinToSmallPositiveValueDataProcessor:: -initialise_keymap() -{ +void +ThresholdMinToSmallPositiveValueDataProcessor::initialise_keymap() { base_type::initialise_keymap(); this->parser.add_start_key("Threshold Min To Small Positive Value Parameters"); this->parser.add_stop_key("END Threshold Min To Small Positive Value Parameters"); } +template +const char* const ThresholdMinToSmallPositiveValueDataProcessor::registered_name = "Threshold Min To Small Positive Value"; - -template -const char * const -ThresholdMinToSmallPositiveValueDataProcessor:: - registered_name = - "Threshold Min To Small Positive Value"; - - -# ifdef _MSC_VER -// prevent warning message on reinstantiation, +#ifdef _MSC_VER +// prevent warning message on reinstantiation, // note that we get a linking error if we don't have the explicit instantiation below -# pragma warning(disable:4660) -# endif +# pragma warning(disable : 4660) +#endif // Register this class in the DataProcessor registry // static ThresholdMinToSmallPositiveValueDataProcessor::RegisterIt dummy; // have the above variable in a separate file, which you need to pass at link time -template class ThresholdMinToSmallPositiveValueDataProcessor >; -template class ThresholdMinToSmallPositiveValueDataProcessor< ParametricVoxelsOnCartesianGrid >; -//template class ThresholdMinToSmallPositiveValueDataProcessor< VoxelsOnCartesianGrid > >; -//template class ThresholdMinToSmallPositiveValueDataProcessor< VoxelsOnCartesianGrid > >; -//template class ThresholdMinToSmallPositiveValueDataProcessor< VoxelsOnCartesianGrid > >; +template class ThresholdMinToSmallPositiveValueDataProcessor>; +template class ThresholdMinToSmallPositiveValueDataProcessor; +// template class ThresholdMinToSmallPositiveValueDataProcessor< VoxelsOnCartesianGrid > >; +// template class ThresholdMinToSmallPositiveValueDataProcessor< VoxelsOnCartesianGrid > >; +// template class ThresholdMinToSmallPositiveValueDataProcessor< VoxelsOnCartesianGrid > >; END_NAMESPACE_STIR - - - - diff --git a/src/buildblock/TimeFrameDefinitions.cxx b/src/buildblock/TimeFrameDefinitions.cxx index 5bf60661b4..3c06dd662f 100644 --- a/src/buildblock/TimeFrameDefinitions.cxx +++ b/src/buildblock/TimeFrameDefinitions.cxx @@ -17,11 +17,11 @@ See STIR/LICENSE.txt for details */ /*! - \file + \file \ingroup buildblock \brief Implementation of class stir::TimeFrameDefinitions - + \author Kris Thielemans */ @@ -29,8 +29,8 @@ #include "stir/ExamInfo.h" #include "stir/IO/FileSignature.h" #ifdef HAVE_LLN_MATRIX -#include "stir/IO/stir_ecat6.h" -#include "stir/IO/stir_ecat7.h" +# include "stir/IO/stir_ecat6.h" +# include "stir/IO/stir_ecat7.h" #endif #include "stir/IO/InterfileHeader.h" #include "stir/IO/interfile.h" @@ -54,108 +54,80 @@ using std::ifstream; START_NAMESPACE_STIR - double -TimeFrameDefinitions:: -get_start_time(unsigned int frame_num) const -{ - return frame_times.at(frame_num-1).first; +TimeFrameDefinitions::get_start_time(unsigned int frame_num) const { + return frame_times.at(frame_num - 1).first; } double -TimeFrameDefinitions:: -get_end_time(unsigned int frame_num) const -{ - return frame_times.at(frame_num-1).second; +TimeFrameDefinitions::get_end_time(unsigned int frame_num) const { + return frame_times.at(frame_num - 1).second; } double -TimeFrameDefinitions:: -get_duration(unsigned int frame_num) const -{ +TimeFrameDefinitions::get_duration(unsigned int frame_num) const { return get_end_time(frame_num) - get_start_time(frame_num); } double -TimeFrameDefinitions:: -get_start_time() const -{ +TimeFrameDefinitions::get_start_time() const { return get_start_time(1); } double -TimeFrameDefinitions:: -get_end_time() const -{ +TimeFrameDefinitions::get_end_time() const { return get_end_time(get_num_frames()); } unsigned int -TimeFrameDefinitions:: -get_num_frames() const -{ +TimeFrameDefinitions::get_num_frames() const { return static_cast(frame_times.size()); } unsigned int -TimeFrameDefinitions:: -get_num_time_frames() const -{ +TimeFrameDefinitions::get_num_time_frames() const { return static_cast(frame_times.size()); } -TimeFrameDefinitions:: -TimeFrameDefinitions() -{} +TimeFrameDefinitions::TimeFrameDefinitions() {} -unsigned int -TimeFrameDefinitions:: -get_time_frame_num(const double start_time, const double end_time) const -{ - assert(end_time >=start_time); - for (unsigned int i = 1; i <=this->get_num_frames(); i++) - { - const double start = this->get_start_time(i); - const double end = this->get_end_time(i); - if (std::fabs(start-start_time)<.01 && std::fabs(end-end_time)<.01) - { - return i; - } +unsigned int +TimeFrameDefinitions::get_time_frame_num(const double start_time, const double end_time) const { + assert(end_time >= start_time); + for (unsigned int i = 1; i <= this->get_num_frames(); i++) { + const double start = this->get_start_time(i); + const double end = this->get_end_time(i); + if (std::fabs(start - start_time) < .01 && std::fabs(end - end_time) < .01) { + return i; } + } // not found return 0; } -TimeFrameDefinitions:: -TimeFrameDefinitions(const string& filename) -{ +TimeFrameDefinitions::TimeFrameDefinitions(const string& filename) { const FileSignature file_signature(filename); - const char * signature = file_signature.get_signature(); + const char* signature = file_signature.get_signature(); #ifdef HAVE_LLN_MATRIX - if (ecat::ecat6::is_ECAT6_file(filename) || - ecat::ecat7::is_ECAT7_file(filename)) - { - shared_ptr exam_info_sptr(ecat::ecat7::read_ECAT7_exam_info(filename)); - *this = exam_info_sptr->time_frame_definitions; - } - else + if (ecat::ecat6::is_ECAT6_file(filename) || ecat::ecat7::is_ECAT7_file(filename)) { + shared_ptr exam_info_sptr(ecat::ecat7::read_ECAT7_exam_info(filename)); + *this = exam_info_sptr->time_frame_definitions; + } else #endif - // Interfile - if (is_interfile_signature(signature)) - { + // Interfile + if (is_interfile_signature(signature)) { #ifndef NDEBUG info(boost::format("TimeFrameDefinitions: trying to read '%s' as Interfile") % filename); #endif - InterfileHeader hdr; + InterfileHeader hdr; if (!hdr.parse(filename.c_str(), false)) // silent parsing - { - error(boost::format("Parsing of Interfile header failed for file '%s'") % filename); - } + { + error(boost::format("Parsing of Interfile header failed for file '%s'") % filename); + } *this = hdr.get_exam_info().time_frame_definitions; - } - else + } else read_fdef_file(filename); #if 0 @@ -171,119 +143,98 @@ TimeFrameDefinitions(const string& filename) cerr << '}' << endl; #endif } - + void -TimeFrameDefinitions:: -read_fdef_file(const string& fdef_filename) -{ +TimeFrameDefinitions::read_fdef_file(const string& fdef_filename) { ifstream in(fdef_filename.c_str()); if (!in) error("TimeFrameDefinitions: Error reading \"%s\"\n", fdef_filename.c_str()); - double previous_end_time = 0; - while (true) - { + while (true) { int num; double duration; in >> num >> duration; if (!in) break; // check if input is ok - // note: allow negative 'duration' if num==0 to be able to skip in negative direction + // note: allow negative 'duration' if num==0 to be able to skip in negative direction // (useful for starting the first frame at negative time) - if (num<0 || (num>0 && duration<=0)) - error("TimeFrameDefinitions: Reading frame_def file \"%s\":\n" - "encountered negative numbers (%d, %g)\n", - fdef_filename.c_str(), num, duration); - - if (num==0) - { - // special case to allow us to skip a time period without storing it - previous_end_time+=duration; - } - while (num--) - { - frame_times.push_back(make_pair(previous_end_time, previous_end_time+duration)); - previous_end_time+=duration; + if (num < 0 || (num > 0 && duration <= 0)) + error("TimeFrameDefinitions: Reading frame_def file \"%s\":\n" + "encountered negative numbers (%d, %g)\n", + fdef_filename.c_str(), num, duration); + + if (num == 0) { + // special case to allow us to skip a time period without storing it + previous_end_time += duration; + } + while (num--) { + frame_times.push_back(make_pair(previous_end_time, previous_end_time + duration)); + previous_end_time += duration; } } - if (this->get_num_frames()==0) + if (this->get_num_frames() == 0) error("TimeFrameDefinitions: Reading frame definitions file \"%s\":\n" - "I didn't discover any frames. Wrong file format?\n" - "Should be an ECAT6, ECAT7 file or a text file with something like\n\n" - "3 50.5\n1 10\n0 3\n1 9\n\n" - "for 3 frames of 50.5 secs, 1 frame of 10 secs, a gap of 3 secs, 1 frame of 9 secs.", - fdef_filename.c_str()); + "I didn't discover any frames. Wrong file format?\n" + "Should be an ECAT6, ECAT7 file or a text file with something like\n\n" + "3 50.5\n1 10\n0 3\n1 9\n\n" + "for 3 frames of 50.5 secs, 1 frame of 10 secs, a gap of 3 secs, 1 frame of 9 secs.", + fdef_filename.c_str()); } -TimeFrameDefinitions:: -TimeFrameDefinitions(const vector >& frame_times) - : frame_times(frame_times) -{ - if (get_num_frames()==0) +TimeFrameDefinitions::TimeFrameDefinitions(const vector>& frame_times) : frame_times(frame_times) { + if (get_num_frames() == 0) return; // check times are in sequence double current_time = get_start_time(1); - for (unsigned int current_frame = 1; current_frame <= get_num_frames(); ++ current_frame) - { - if (current_time > get_start_time(current_frame) + .001) // add .001 to avoid numerical errors - error("TimeFrameDefinitions: frame number %d start_time (%g) is smaller than " - "previous end_time (%g)\n", - current_frame, get_start_time(current_frame), current_time); - if (get_start_time(current_frame) > get_end_time(current_frame) + .01) // add .01 to avoid numerical errors - error("TimeFrameDefinitions: frame number %d start_time (%g) is larger than " - "end_time (%g)\n", - current_frame, get_start_time(current_frame), get_end_time(current_frame)); - current_time = get_end_time(current_frame); - } + for (unsigned int current_frame = 1; current_frame <= get_num_frames(); ++current_frame) { + if (current_time > get_start_time(current_frame) + .001) // add .001 to avoid numerical errors + error("TimeFrameDefinitions: frame number %d start_time (%g) is smaller than " + "previous end_time (%g)\n", + current_frame, get_start_time(current_frame), current_time); + if (get_start_time(current_frame) > get_end_time(current_frame) + .01) // add .01 to avoid numerical errors + error("TimeFrameDefinitions: frame number %d start_time (%g) is larger than " + "end_time (%g)\n", + current_frame, get_start_time(current_frame), get_end_time(current_frame)); + current_time = get_end_time(current_frame); + } } -TimeFrameDefinitions:: -TimeFrameDefinitions(const vector& start_times, - const vector& durations) -{ +TimeFrameDefinitions::TimeFrameDefinitions(const vector& start_times, const vector& durations) { if (start_times.size() != durations.size()) error("TimeFrameDefinitions: constructed with start_times " - "and durations of different length"); + "and durations of different length"); this->frame_times.resize(start_times.size()); - for (unsigned int current_frame = 1; - current_frame <= this->get_num_frames(); - ++ current_frame) - { - frame_times[current_frame-1].first = - start_times[current_frame-1]; - frame_times[current_frame-1].second = - start_times[current_frame-1] + durations[current_frame-1]; - } + for (unsigned int current_frame = 1; current_frame <= this->get_num_frames(); ++current_frame) { + frame_times[current_frame - 1].first = start_times[current_frame - 1]; + frame_times[current_frame - 1].second = start_times[current_frame - 1] + durations[current_frame - 1]; + } } -TimeFrameDefinitions:: -TimeFrameDefinitions(const TimeFrameDefinitions& org_frame_defs, unsigned int frame_num) -{ +TimeFrameDefinitions::TimeFrameDefinitions(const TimeFrameDefinitions& org_frame_defs, unsigned int frame_num) { this->frame_times.push_back(make_pair(org_frame_defs.get_start_time(frame_num), org_frame_defs.get_end_time(frame_num))); } void -TimeFrameDefinitions:: -set_time_frame(const int frame_num, const double start, const double end) -{ - frame_times.at(frame_num-1).first = start; - frame_times.at(frame_num-1).second = end; +TimeFrameDefinitions::set_time_frame(const int frame_num, const double start, const double end) { + frame_times.at(frame_num - 1).first = start; + frame_times.at(frame_num - 1).second = end; } -bool TimeFrameDefinitions::operator == (const TimeFrameDefinitions &t) const { - for (int frame=0;frame_gate_sequence[num-1].second; +TimeGateDefinitions::get_gate_duration(unsigned int num) const { + return this->_gate_sequence[num - 1].second; } unsigned int -TimeGateDefinitions:: -get_gate_num(unsigned int num) const -{ - return this->_gate_sequence[num-1].first; +TimeGateDefinitions::get_gate_num(unsigned int num) const { + return this->_gate_sequence[num - 1].first; } unsigned int -TimeGateDefinitions:: -get_num_gates() const -{ +TimeGateDefinitions::get_num_gates() const { return static_cast(this->_gate_sequence.size()); } unsigned int -TimeGateDefinitions:: -get_num_time_gates() const -{ +TimeGateDefinitions::get_num_time_gates() const { return static_cast(this->_gate_sequence.size()); } -TimeGateDefinitions:: -TimeGateDefinitions() -{} +TimeGateDefinitions::TimeGateDefinitions() {} -TimeGateDefinitions:: -TimeGateDefinitions(const string& gdef_filename) -{ - TimeGateDefinitions::read_gdef_file(gdef_filename); -} +TimeGateDefinitions::TimeGateDefinitions(const string& gdef_filename) { TimeGateDefinitions::read_gdef_file(gdef_filename); } - void -TimeGateDefinitions:: -read_gdef_file(const string& gdef_filename) -{ +TimeGateDefinitions::read_gdef_file(const string& gdef_filename) { ifstream in(gdef_filename.c_str()); - if (!in) - { - const string gdef_newfilename=gdef_filename+".gdef"; - warning("TimeGateDefinitions: Warning failed reading \"%s\"\n Trying with .gdef extension...", gdef_filename.c_str()); - ifstream innew(gdef_filename.c_str()); - if (!innew) - error("TimeGateDefinitions: Error reading \"%s\"\n", gdef_newfilename.c_str()); - } - while (true) - { - int gate_num; - double duration; - in >> gate_num >> duration; - if (!in) - break; - if (gate_num<0 || (gate_num>0 && duration<0)) - error("TimeGateDefinitions: Reading gate_def file \"%s\":\n" - "encountered negative numbers (%d, %g)\n", - gdef_filename.c_str(), gate_num, duration); - this->_gate_sequence.push_back(make_pair(gate_num, duration)); - } - if (this->get_num_gates()==0) + if (!in) { + const string gdef_newfilename = gdef_filename + ".gdef"; + warning("TimeGateDefinitions: Warning failed reading \"%s\"\n Trying with .gdef extension...", gdef_filename.c_str()); + ifstream innew(gdef_filename.c_str()); + if (!innew) + error("TimeGateDefinitions: Error reading \"%s\"\n", gdef_newfilename.c_str()); + } + while (true) { + int gate_num; + double duration; + in >> gate_num >> duration; + if (!in) + break; + if (gate_num < 0 || (gate_num > 0 && duration < 0)) + error("TimeGateDefinitions: Reading gate_def file \"%s\":\n" + "encountered negative numbers (%d, %g)\n", + gdef_filename.c_str(), gate_num, duration); + this->_gate_sequence.push_back(make_pair(gate_num, duration)); + } + if (this->get_num_gates() == 0) error("TimeGateDefinitions: Reading gate definitions file \"%s\":\n" - "I didn't discover any gates. Wrong file format?\n" - "A text file with something like\n\n" - "3 50.5\n1 10\n10 7\n\n" - "for 3rd gate of 50.5 secs, 1st gate of 10 secs, 10th gate of 7 secs.", - gdef_filename.c_str()); + "I didn't discover any gates. Wrong file format?\n" + "A text file with something like\n\n" + "3 50.5\n1 10\n10 7\n\n" + "for 3rd gate of 50.5 secs, 1st gate of 10 secs, 10th gate of 7 secs.", + gdef_filename.c_str()); } -TimeGateDefinitions:: -TimeGateDefinitions(const vector >& gate_sequence) - : _gate_sequence(gate_sequence) -{ +TimeGateDefinitions::TimeGateDefinitions(const vector>& gate_sequence) + : _gate_sequence(gate_sequence) { if (gate_sequence.size() == 0) error("TimeGateDefinitions: constructed with gate_sequence of no gates"); return; - + this->_gate_sequence.resize(gate_sequence.size()); - for (unsigned int current_gate = 1; - current_gate <= this->_gate_sequence.size(); - ++current_gate) - { - this->_gate_sequence[current_gate-1].first = - gate_sequence[current_gate-1].first; - this->_gate_sequence[current_gate-1].second = - gate_sequence[current_gate-1].second; - } + for (unsigned int current_gate = 1; current_gate <= this->_gate_sequence.size(); ++current_gate) { + this->_gate_sequence[current_gate - 1].first = gate_sequence[current_gate - 1].first; + this->_gate_sequence[current_gate - 1].second = gate_sequence[current_gate - 1].second; + } } -TimeGateDefinitions:: -TimeGateDefinitions(const vector& gate_num_vector, - const vector& duration_vector) -{ +TimeGateDefinitions::TimeGateDefinitions(const vector& gate_num_vector, const vector& duration_vector) { if (gate_num_vector.size() != duration_vector.size()) error("TimeGateDefinitions: constructed with gate_sequence " "and durations of different length"); this->_gate_sequence.resize(gate_num_vector.size()); - for (unsigned int current_gate = 1; - current_gate <= gate_num_vector.size(); - ++current_gate) - { - this->_gate_sequence[current_gate-1].first = - gate_num_vector[current_gate-1]; - this->_gate_sequence[current_gate-1].second = - duration_vector[current_gate-1]; - } + for (unsigned int current_gate = 1; current_gate <= gate_num_vector.size(); ++current_gate) { + this->_gate_sequence[current_gate - 1].first = gate_num_vector[current_gate - 1]; + this->_gate_sequence[current_gate - 1].second = duration_vector[current_gate - 1]; + } } END_NAMESPACE_STIR diff --git a/src/buildblock/TruncateToCylindricalFOVImageProcessor.cxx b/src/buildblock/TruncateToCylindricalFOVImageProcessor.cxx index fa6f5bd16b..c596a2e54f 100644 --- a/src/buildblock/TruncateToCylindricalFOVImageProcessor.cxx +++ b/src/buildblock/TruncateToCylindricalFOVImageProcessor.cxx @@ -31,84 +31,61 @@ START_NAMESPACE_STIR template <> -const char * const -TruncateToCylindricalFOVImageProcessor::registered_name = - "Truncate To Cylindrical FOV"; - +const char* const TruncateToCylindricalFOVImageProcessor::registered_name = "Truncate To Cylindrical FOV"; template -void -TruncateToCylindricalFOVImageProcessor:: -initialise_keymap() -{ +void +TruncateToCylindricalFOVImageProcessor::initialise_keymap() { base_type::initialise_keymap(); this->parser.add_start_key("Truncate To Cylindrical FOV Parameters"); this->parser.add_key("strictly_less_than_radius", &_strictly_less_than_radius); this->parser.add_stop_key("END Truncate To Cylindrical FOV Parameters"); } - template -void -TruncateToCylindricalFOVImageProcessor:: -set_defaults() -{ +void +TruncateToCylindricalFOVImageProcessor::set_defaults() { base_type::set_defaults(); this->_strictly_less_than_radius = true; } - - template Succeeded -TruncateToCylindricalFOVImageProcessor:: -virtual_set_up(const DiscretisedDensity<3,elemT>& density) +TruncateToCylindricalFOVImageProcessor::virtual_set_up(const DiscretisedDensity<3, elemT>& density) { - if (dynamic_cast *>(&density) == 0) + if (dynamic_cast*>(&density) == 0) return Succeeded::no; else return Succeeded::yes; - } - template void -TruncateToCylindricalFOVImageProcessor:: -virtual_apply(DiscretisedDensity<3,elemT>& density) const +TruncateToCylindricalFOVImageProcessor::virtual_apply(DiscretisedDensity<3, elemT>& density) const -{ - truncate_rim(density, 0, this->_strictly_less_than_radius); +{ + truncate_rim(density, 0, this->_strictly_less_than_radius); } - template void -TruncateToCylindricalFOVImageProcessor:: -virtual_apply(DiscretisedDensity<3,elemT>& out_density, - const DiscretisedDensity<3,elemT>& in_density) const -{ +TruncateToCylindricalFOVImageProcessor::virtual_apply(DiscretisedDensity<3, elemT>& out_density, + const DiscretisedDensity<3, elemT>& in_density) const { out_density = in_density; this->virtual_apply(out_density); } - template -TruncateToCylindricalFOVImageProcessor:: -TruncateToCylindricalFOVImageProcessor() -{ +TruncateToCylindricalFOVImageProcessor::TruncateToCylindricalFOVImageProcessor() { this->set_defaults(); } - - - -# ifdef _MSC_VER -// prevent warning message on reinstantiation, +#ifdef _MSC_VER +// prevent warning message on reinstantiation, // note that we get a linking error if we don't have the explicit instantiation below -# pragma warning(disable:4660) -# endif +# pragma warning(disable : 4660) +#endif // Register this class in the ImageProcessor registry // static TruncateToCylindricalFOVImageProcessor::RegisterIt dummy; @@ -117,6 +94,3 @@ TruncateToCylindricalFOVImageProcessor() template class TruncateToCylindricalFOVImageProcessor; END_NAMESPACE_STIR - - - diff --git a/src/buildblock/Verbosity.cxx b/src/buildblock/Verbosity.cxx index 5bb42a716d..1bb4dd2e42 100644 --- a/src/buildblock/Verbosity.cxx +++ b/src/buildblock/Verbosity.cxx @@ -31,23 +31,21 @@ START_NAMESPACE_STIR // Global static pointer used to ensure a single instance of the class. -Verbosity* Verbosity::_instance = NULL; +Verbosity* Verbosity::_instance = NULL; -Verbosity::Verbosity(){ - _verbosity_level = 2; -}; +Verbosity::Verbosity() { _verbosity_level = 2; }; -int Verbosity::get() -{ - if (!_instance) // Only allow one instance of class to be generated. +int +Verbosity::get() { + if (!_instance) // Only allow one instance of class to be generated. _instance = new Verbosity; return _instance->_verbosity_level; } -void Verbosity::set(int level) -{ - if (!_instance) // Only allow one instance of class to be generated. +void +Verbosity::set(int level) { + if (!_instance) // Only allow one instance of class to be generated. _instance = new Verbosity; _instance->_verbosity_level = level; diff --git a/src/buildblock/Viewgram.cxx b/src/buildblock/Viewgram.cxx index 6cd8e4eaf1..16fc4f4597 100644 --- a/src/buildblock/Viewgram.cxx +++ b/src/buildblock/Viewgram.cxx @@ -35,89 +35,56 @@ #ifdef _MSC_VER // disable warning that not all functions have been implemented when instantiating -#pragma warning(disable: 4661) +# pragma warning(disable : 4661) #endif // _MSC_VER using std::string; START_NAMESPACE_STIR -template +template bool -Viewgram:: -has_same_characteristics(self_type const& other, - string& explanation) const -{ +Viewgram::has_same_characteristics(self_type const& other, string& explanation) const { using boost::format; using boost::str; - if (*this->get_proj_data_info_sptr() != - *other.get_proj_data_info_sptr()) - { - explanation = - str(format("Differing projection data info:\n%1%\n-------- vs-------\n %2%") - % this->get_proj_data_info_sptr()->parameter_info() - % other.get_proj_data_info_sptr()->parameter_info() - ); - return false; - } - if (this->get_view_num() != - other.get_view_num()) - { - explanation = - str(format("Differing view number: %1% vs %2%") - % this->get_view_num() - % other.get_view_num() - ); - return false; - } - if (this->get_segment_num() != - other.get_segment_num()) - { - explanation = - str(format("Differing segment number: %1% vs %2%") - % this->get_segment_num() - % other.get_segment_num() - ); - return false; - } - if (this->get_timing_pos_num() != - other.get_timing_pos_num()) - { - explanation = - str(format("Differing timing position index: %1% vs %2%") - % this->get_timing_pos_num() - % other.get_timing_pos_num() - ); - return false; - } + if (*this->get_proj_data_info_sptr() != *other.get_proj_data_info_sptr()) { + explanation = str(format("Differing projection data info:\n%1%\n-------- vs-------\n %2%") % + this->get_proj_data_info_sptr()->parameter_info() % other.get_proj_data_info_sptr()->parameter_info()); + return false; + } + if (this->get_view_num() != other.get_view_num()) { + explanation = str(format("Differing view number: %1% vs %2%") % this->get_view_num() % other.get_view_num()); + return false; + } + if (this->get_segment_num() != other.get_segment_num()) { + explanation = str(format("Differing segment number: %1% vs %2%") % this->get_segment_num() % other.get_segment_num()); + return false; + } + if (this->get_timing_pos_num() != other.get_timing_pos_num()) { + explanation = + str(format("Differing timing position index: %1% vs %2%") % this->get_timing_pos_num() % other.get_timing_pos_num()); + return false; + } return true; } -template +template bool -Viewgram:: -has_same_characteristics(self_type const& other) const -{ +Viewgram::has_same_characteristics(self_type const& other) const { std::string explanation; return this->has_same_characteristics(other, explanation); } -template -bool -Viewgram:: -operator ==(const self_type& that) const -{ - return - this->has_same_characteristics(that) && - base_type::operator==(that); +template +bool +Viewgram::operator==(const self_type& that) const { + return this->has_same_characteristics(that) && base_type::operator==(that); } - -template -bool -Viewgram:: -operator !=(const self_type& that) const -{ + +template +bool +Viewgram::operator!=(const self_type& that) const { return !((*this) == that); } @@ -126,18 +93,16 @@ operator !=(const self_type& that) const ProjDataInfo member. */ template -void -Viewgram:: -resize(const IndexRange<2>& range) -{ +void +Viewgram::resize(const IndexRange<2>& range) { if (range == this->get_index_range()) return; - assert(range.is_regular()==true); + assert(range.is_regular() == true); const int ax_min = range.get_min_index(); const int ax_max = range.get_max_index(); - + shared_ptr pdi_sptr(proj_data_info_sptr->clone()); pdi_sptr->set_min_axial_pos_num(ax_min, get_segment_num()); @@ -147,24 +112,19 @@ resize(const IndexRange<2>& range) proj_data_info_sptr = pdi_sptr; - Array<2,elemT>::resize(range); - + Array<2, elemT>::resize(range); } - /*! This makes sure that the new Array dimensions are the same as those in the ProjDataInfo member. */ template -void -Viewgram:: -grow(const IndexRange<2>& range) -{ +void +Viewgram::grow(const IndexRange<2>& range) { resize(range); } - /****************************** instantiations ****************************/ diff --git a/src/buildblock/VoxelsOnCartesianGrid.cxx b/src/buildblock/VoxelsOnCartesianGrid.cxx index fce96131b0..5b3d692575 100644 --- a/src/buildblock/VoxelsOnCartesianGrid.cxx +++ b/src/buildblock/VoxelsOnCartesianGrid.cxx @@ -18,11 +18,11 @@ See STIR/LICENSE.txt for details */ /*! - \file - \ingroup densitydata - \brief Implementations of stir::VoxelsOnCartesianGrid + \file + \ingroup densitydata + \brief Implementations of stir::VoxelsOnCartesianGrid - \author Sanida Mustafovic + \author Sanida Mustafovic \author Kris Thielemans (with help from Alexey Zverovich) \author PARAPET project @@ -55,24 +55,18 @@ START_NAMESPACE_STIR // a local help function to find appropriate sizes etc. -static void find_sampling_and_z_size( - float& z_sampling, - float& s_sampling, - int& z_size, - const ProjDataInfo* proj_data_info_ptr) -{ +static void +find_sampling_and_z_size(float& z_sampling, float& s_sampling, int& z_size, const ProjDataInfo* proj_data_info_ptr) { // first z- things - if (const ProjDataInfoCylindrical* - proj_data_info_cyl_ptr = - dynamic_cast(proj_data_info_ptr)) + if (const ProjDataInfoCylindrical* proj_data_info_cyl_ptr = dynamic_cast(proj_data_info_ptr)) - { + { // the case of cylindrical data - z_sampling = proj_data_info_cyl_ptr->get_ring_spacing()/2; - + z_sampling = proj_data_info_cyl_ptr->get_ring_spacing() / 2; + // for 'span>1' case, we take z_size = number of sinograms in segment 0 // for 'span==1' case, we take 2*num_rings-1 @@ -80,152 +74,104 @@ static void find_sampling_and_z_size( assert(proj_data_info_cyl_ptr->get_min_segment_num() <= 0); assert(proj_data_info_cyl_ptr->get_max_segment_num() >= 0); - if (z_size<0) - z_size = - proj_data_info_cyl_ptr->get_max_ring_difference(0) > - proj_data_info_cyl_ptr->get_min_ring_difference(0) - ? proj_data_info_cyl_ptr->get_num_axial_poss(0) - : 2*proj_data_info_cyl_ptr->get_num_axial_poss(0) - 1; - } - else - { + if (z_size < 0) + z_size = proj_data_info_cyl_ptr->get_max_ring_difference(0) > proj_data_info_cyl_ptr->get_min_ring_difference(0) + ? proj_data_info_cyl_ptr->get_num_axial_poss(0) + : 2 * proj_data_info_cyl_ptr->get_num_axial_poss(0) - 1; + } else { // this is any other weird projection data. We just check sampling of segment 0 - + // first check if we have segment 0 assert(proj_data_info_cyl_ptr->get_min_segment_num() <= 0); assert(proj_data_info_cyl_ptr->get_max_segment_num() >= 0); // TODO make this independent on segment etc. - z_sampling = - proj_data_info_ptr->get_sampling_in_t(Bin(0,0,1,0)); + z_sampling = proj_data_info_ptr->get_sampling_in_t(Bin(0, 0, 1, 0)); - if (z_size<0) + if (z_size < 0) z_size = proj_data_info_ptr->get_num_axial_poss(0); } // now do s_sampling { - s_sampling = - proj_data_info_ptr->get_scanner_ptr()->get_default_bin_size(); - if (s_sampling <= 0) - { - s_sampling = - proj_data_info_ptr->get_sampling_in_s(Bin(0,0,0,0)); - info(boost::format("Determining voxel size from default_bin_size failed as it is not set.\n" - "Using sampling_in_s for central bin %1%.") % - s_sampling); - } - else - { - info(boost::format("Determined voxel size by dividing default_bin_size (%1%) by zoom") % - s_sampling); - } - + s_sampling = proj_data_info_ptr->get_scanner_ptr()->get_default_bin_size(); + if (s_sampling <= 0) { + s_sampling = proj_data_info_ptr->get_sampling_in_s(Bin(0, 0, 0, 0)); + info(boost::format("Determining voxel size from default_bin_size failed as it is not set.\n" + "Using sampling_in_s for central bin %1%.") % + s_sampling); + } else { + info(boost::format("Determined voxel size by dividing default_bin_size (%1%) by zoom") % s_sampling); + } } - } +template +VoxelsOnCartesianGrid::VoxelsOnCartesianGrid() : DiscretisedDensityOnCartesianGrid<3, elemT>() {} -template -VoxelsOnCartesianGrid ::VoxelsOnCartesianGrid() - : DiscretisedDensityOnCartesianGrid<3,elemT>() -{} - -template -VoxelsOnCartesianGrid::VoxelsOnCartesianGrid - (const Array<3,elemT>& v, - const CartesianCoordinate3D& origin, - const BasicCoordinate<3,float>& grid_spacing) - :DiscretisedDensityOnCartesianGrid<3,elemT> - (v.get_index_range(),origin,grid_spacing) -{ - Array<3,elemT>::operator=(v); +template +VoxelsOnCartesianGrid::VoxelsOnCartesianGrid(const Array<3, elemT>& v, const CartesianCoordinate3D& origin, + const BasicCoordinate<3, float>& grid_spacing) + : DiscretisedDensityOnCartesianGrid<3, elemT>(v.get_index_range(), origin, grid_spacing) { + Array<3, elemT>::operator=(v); } +template +VoxelsOnCartesianGrid::VoxelsOnCartesianGrid(const IndexRange<3>& range, const CartesianCoordinate3D& origin, + const BasicCoordinate<3, float>& grid_spacing) + : DiscretisedDensityOnCartesianGrid<3, elemT>(range, origin, grid_spacing) {} -template -VoxelsOnCartesianGrid::VoxelsOnCartesianGrid - (const IndexRange<3>& range, - const CartesianCoordinate3D& origin, - const BasicCoordinate<3,float>& grid_spacing) - :DiscretisedDensityOnCartesianGrid<3,elemT> - (range,origin,grid_spacing) -{} - -template -VoxelsOnCartesianGrid::VoxelsOnCartesianGrid - (const shared_ptr < const ExamInfo > & exam_info_sptr, - const Array<3,elemT>& v, - const CartesianCoordinate3D& origin, - const BasicCoordinate<3,float>& grid_spacing) - :DiscretisedDensityOnCartesianGrid<3,elemT> - (exam_info_sptr,v.get_index_range(),origin,grid_spacing) -{ - Array<3,elemT>::operator=(v); +template +VoxelsOnCartesianGrid::VoxelsOnCartesianGrid(const shared_ptr& exam_info_sptr, const Array<3, elemT>& v, + const CartesianCoordinate3D& origin, + const BasicCoordinate<3, float>& grid_spacing) + : DiscretisedDensityOnCartesianGrid<3, elemT>(exam_info_sptr, v.get_index_range(), origin, grid_spacing) { + Array<3, elemT>::operator=(v); } - -template -VoxelsOnCartesianGrid::VoxelsOnCartesianGrid - (const shared_ptr < const ExamInfo > & exam_info_sptr, - const IndexRange<3>& range, - const CartesianCoordinate3D& origin, - const BasicCoordinate<3,float>& grid_spacing) - :DiscretisedDensityOnCartesianGrid<3,elemT> - (exam_info_sptr,range,origin,grid_spacing) -{} +template +VoxelsOnCartesianGrid::VoxelsOnCartesianGrid(const shared_ptr& exam_info_sptr, const IndexRange<3>& range, + const CartesianCoordinate3D& origin, + const BasicCoordinate<3, float>& grid_spacing) + : DiscretisedDensityOnCartesianGrid<3, elemT>(exam_info_sptr, range, origin, grid_spacing) {} // KT 10/12/2001 use new format of args for the constructor, and remove the make_xy_size_odd constructor -template -VoxelsOnCartesianGrid::VoxelsOnCartesianGrid(const ProjDataInfo& proj_data_info, - const float zoom, +template +VoxelsOnCartesianGrid::VoxelsOnCartesianGrid(const ProjDataInfo& proj_data_info, const float zoom, const CartesianCoordinate3D& origin, const CartesianCoordinate3D& sizes) - + { shared_ptr exam_info_sptr_v(new ExamInfo); - this->construct_from_projdata_info(exam_info_sptr_v,proj_data_info, - CartesianCoordinate3D(1.F, zoom, zoom), - origin, + this->construct_from_projdata_info(exam_info_sptr_v, proj_data_info, CartesianCoordinate3D(1.F, zoom, zoom), origin, sizes); } -template -VoxelsOnCartesianGrid::VoxelsOnCartesianGrid(const shared_ptr < const ExamInfo > & exam_info_sptr_v, - const ProjDataInfo& proj_data_info, - const float zoom, +template +VoxelsOnCartesianGrid::VoxelsOnCartesianGrid(const shared_ptr& exam_info_sptr_v, + const ProjDataInfo& proj_data_info, const float zoom, const CartesianCoordinate3D& origin, - const CartesianCoordinate3D& sizes) -{ - this->construct_from_projdata_info(exam_info_sptr_v,proj_data_info, - CartesianCoordinate3D(1.F, zoom, zoom), - origin, + const CartesianCoordinate3D& sizes) { + this->construct_from_projdata_info(exam_info_sptr_v, proj_data_info, CartesianCoordinate3D(1.F, zoom, zoom), origin, sizes); } -template -VoxelsOnCartesianGrid::VoxelsOnCartesianGrid(const shared_ptr < const ExamInfo > & exam_info_sptr_v, - const ProjDataInfo& proj_data_info, - const CartesianCoordinate3D& zooms, +template +VoxelsOnCartesianGrid::VoxelsOnCartesianGrid(const shared_ptr& exam_info_sptr_v, + const ProjDataInfo& proj_data_info, const CartesianCoordinate3D& zooms, const CartesianCoordinate3D& origin, - const CartesianCoordinate3D& sizes) -{ - this->construct_from_projdata_info(exam_info_sptr_v,proj_data_info, - zooms, - origin, - sizes); + const CartesianCoordinate3D& sizes) { + this->construct_from_projdata_info(exam_info_sptr_v, proj_data_info, zooms, origin, sizes); } -template +template void -VoxelsOnCartesianGrid:: -construct_from_projdata_info(const shared_ptr < const ExamInfo > & exam_info_sptr_v, - const ProjDataInfo& proj_data_info, - const CartesianCoordinate3D& zooms, - const CartesianCoordinate3D& origin, - const CartesianCoordinate3D& sizes) -{ +VoxelsOnCartesianGrid::construct_from_projdata_info(const shared_ptr& exam_info_sptr_v, + const ProjDataInfo& proj_data_info, + const CartesianCoordinate3D& zooms, + const CartesianCoordinate3D& origin, + const CartesianCoordinate3D& sizes) { this->exam_info_sptr = exam_info_sptr_v; // sadly, this code is a complete copy of the above // probably avoidable in C++11 @@ -233,112 +179,93 @@ construct_from_projdata_info(const shared_ptr < const ExamInfo > & exam_info_spt int z_size = sizes.z(); // initialise to 0 to prevent compiler warnings - //int z_size = 0; + // int z_size = 0; float z_sampling = 0; float s_sampling = 0; find_sampling_and_z_size(z_sampling, s_sampling, z_size, &proj_data_info); - - this->set_grid_spacing( - CartesianCoordinate3D(z_sampling, s_sampling, s_sampling) / zooms - ); + + this->set_grid_spacing(CartesianCoordinate3D(z_sampling, s_sampling, s_sampling) / zooms); int x_size_used = sizes.x(); int y_size_used = sizes.y(); - if (sizes.x()==-1 || sizes.y()==-1) - { - // default it to cover full FOV by taking image_size>=2*FOVradius_in_pixs+1 - const float FOVradius_in_mm = - max(proj_data_info.get_s(Bin(0,0,0,proj_data_info.get_max_tangential_pos_num())), - -proj_data_info.get_s(Bin(0,0,0,proj_data_info.get_min_tangential_pos_num()))); - if (sizes.x()==-1) - x_size_used = 2*static_cast(ceil(FOVradius_in_mm / get_voxel_size().x())) + 1; - if (sizes.y()==-1) - y_size_used = 2*static_cast(ceil(FOVradius_in_mm / get_voxel_size().y())) + 1; - } - if (x_size_used<0) - error("VoxelsOnCartesianGrid: attempt to construct image with negative x_size %d\n", - x_size_used); - if (x_size_used==0) + if (sizes.x() == -1 || sizes.y() == -1) { + // default it to cover full FOV by taking image_size>=2*FOVradius_in_pixs+1 + const float FOVradius_in_mm = max(proj_data_info.get_s(Bin(0, 0, 0, proj_data_info.get_max_tangential_pos_num())), + -proj_data_info.get_s(Bin(0, 0, 0, proj_data_info.get_min_tangential_pos_num()))); + if (sizes.x() == -1) + x_size_used = 2 * static_cast(ceil(FOVradius_in_mm / get_voxel_size().x())) + 1; + if (sizes.y() == -1) + y_size_used = 2 * static_cast(ceil(FOVradius_in_mm / get_voxel_size().y())) + 1; + } + if (x_size_used < 0) + error("VoxelsOnCartesianGrid: attempt to construct image with negative x_size %d\n", x_size_used); + if (x_size_used == 0) warning("VoxelsOnCartesianGrid: constructed image with x_size 0\n"); - if (y_size_used<0) - error("VoxelsOnCartesianGrid: attempt to construct image with negative y_size %d\n", - y_size_used); - if (y_size_used==0) + if (y_size_used < 0) + error("VoxelsOnCartesianGrid: attempt to construct image with negative y_size %d\n", y_size_used); + if (y_size_used == 0) warning("VoxelsOnCartesianGrid: constructed image with y_size 0\n"); - IndexRange3D range (0, z_size-1, - -(y_size_used/2), -(y_size_used/2) + y_size_used-1, - -(x_size_used/2), -(x_size_used/2) + x_size_used-1); - + IndexRange3D range(0, z_size - 1, -(y_size_used / 2), -(y_size_used / 2) + y_size_used - 1, -(x_size_used / 2), + -(x_size_used / 2) + x_size_used - 1); this->grow(range); } /*! This member function will be unnecessary when all compilers can handle - 'covariant' return types. + 'covariant' return types. It is a non-virtual counterpart of get_empty_voxels_on_cartesian_grid. */ -template +template VoxelsOnCartesianGrid* VoxelsOnCartesianGrid::get_empty_voxels_on_cartesian_grid() const { - return new VoxelsOnCartesianGrid(this->get_exam_info().create_shared_clone(), - this->get_index_range(), - this->get_origin(), + return new VoxelsOnCartesianGrid(this->get_exam_info().create_shared_clone(), this->get_index_range(), this->get_origin(), this->get_grid_spacing()); } - -template +template #ifdef STIR_NO_COVARIANT_RETURN_TYPES -DiscretisedDensity<3,elemT>* +DiscretisedDensity<3, elemT>* #else VoxelsOnCartesianGrid* #endif -VoxelsOnCartesianGrid::get_empty_copy() const -{ +VoxelsOnCartesianGrid::get_empty_copy() const { return get_empty_voxels_on_cartesian_grid(); } -template +template #ifdef STIR_NO_COVARIANT_RETURN_TYPES -DiscretisedDensity<3,elemT>* +DiscretisedDensity<3, elemT>* #else VoxelsOnCartesianGrid* #endif -VoxelsOnCartesianGrid::clone() const -{ - VoxelsOnCartesianGrid *temp = new VoxelsOnCartesianGrid(*this); +VoxelsOnCartesianGrid::clone() const { + VoxelsOnCartesianGrid* temp = new VoxelsOnCartesianGrid(*this); temp->set_exam_info(temp->get_exam_info()); return temp; } -template -void -VoxelsOnCartesianGrid::set_voxel_size(const BasicCoordinate<3,float>& c) -{ +template +void +VoxelsOnCartesianGrid::set_voxel_size(const BasicCoordinate<3, float>& c) { this->set_grid_spacing(c); } -template -PixelsOnCartesianGrid -VoxelsOnCartesianGrid::get_plane(const int z) const -{ - PixelsOnCartesianGrid - plane(this->operator[](z), - this->get_origin(), - Coordinate2D(get_voxel_size().y(), get_voxel_size().x()) - ); +template +PixelsOnCartesianGrid +VoxelsOnCartesianGrid::get_plane(const int z) const { + PixelsOnCartesianGrid plane(this->operator[](z), this->get_origin(), + Coordinate2D(get_voxel_size().y(), get_voxel_size().x())); return plane; } /*! This function requires that the dimensions, origin and grid_spacings match. */ -template -void -VoxelsOnCartesianGrid::set_plane(const PixelsOnCartesianGrid& plane, const int z) -{ +template +void +VoxelsOnCartesianGrid::set_plane(const PixelsOnCartesianGrid& plane, const int z) { assert(this->get_min_x() == plane.get_min_x()); assert(this->get_max_x() == plane.get_max_x()); assert(this->get_min_y() == plane.get_min_y()); @@ -346,18 +273,17 @@ VoxelsOnCartesianGrid::set_plane(const PixelsOnCartesianGrid& plan assert(this->get_origin() == plane.get_origin()); assert(this->get_voxel_size().x() == plane.get_pixel_size().x()); assert(this->get_voxel_size().y() == plane.get_pixel_size().y()); - - this->operator[](z) = plane; + + this->operator[](z) = plane; } -template -void -VoxelsOnCartesianGrid::grow_z_range(const int min_z, const int max_z) -{ +template +void +VoxelsOnCartesianGrid::grow_z_range(const int min_z, const int max_z) { /* This is somewhat complicated as Array is not very good with regular ranges. - It works by - - getting the regular range, - - 'grow' this by hand, + It works by + - getting the regular range, + - 'grow' this by hand, - make a general IndexRange from this - call Array::grow with the general range */ @@ -372,30 +298,24 @@ VoxelsOnCartesianGrid::grow_z_range(const int min_z, const int max_z) this->grow(IndexRange<3>(min_indices, max_indices)); } -template -BasicCoordinate<3,int> -VoxelsOnCartesianGrid:: -get_lengths() const -{ +template +BasicCoordinate<3, int> +VoxelsOnCartesianGrid::get_lengths() const { return make_coordinate(this->get_z_size(), this->get_y_size(), this->get_x_size()); } -template -BasicCoordinate<3,int> -VoxelsOnCartesianGrid:: -get_min_indices() const -{ +template +BasicCoordinate<3, int> +VoxelsOnCartesianGrid::get_min_indices() const { CartesianCoordinate3D min_indices; CartesianCoordinate3D max_indices; this->get_regular_range(min_indices, max_indices); return min_indices; } -template -BasicCoordinate<3,int> -VoxelsOnCartesianGrid:: -get_max_indices() const -{ +template +BasicCoordinate<3, int> +VoxelsOnCartesianGrid::get_max_indices() const { CartesianCoordinate3D min_indices; CartesianCoordinate3D max_indices; this->get_regular_range(min_indices, max_indices); @@ -493,13 +413,13 @@ VoxelsOnCartesianGrid VoxelsOnCartesianGrid::ask_parameters() instantiations **********************************************/ template class VoxelsOnCartesianGrid; -template class VoxelsOnCartesianGrid >; +template class VoxelsOnCartesianGrid>; END_NAMESPACE_STIR #include "stir/modelling/KineticParameters.h" namespace stir { - template class VoxelsOnCartesianGrid >; - template class VoxelsOnCartesianGrid >; - template class VoxelsOnCartesianGrid >; -} +template class VoxelsOnCartesianGrid>; +template class VoxelsOnCartesianGrid>; +template class VoxelsOnCartesianGrid>; +} // namespace stir diff --git a/src/buildblock/buildblock_registries.cxx b/src/buildblock/buildblock_registries.cxx index c4e66fd977..13261f4311 100644 --- a/src/buildblock/buildblock_registries.cxx +++ b/src/buildblock/buildblock_registries.cxx @@ -25,7 +25,7 @@ \brief File that registers all stir::RegisterObject children in buildblock \author Kris Thielemans - + */ #include "stir/SeparableCartesianMetzImageFilter.h" @@ -38,7 +38,7 @@ #include "stir/NonseparableConvolutionUsingRealDFTImageFilter.h" #include "stir/TruncateToCylindricalFOVImageProcessor.h" #ifdef HAVE_JSON -#include "stir/HUToMuImageProcessor.h" +# include "stir/HUToMuImageProcessor.h" #endif START_NAMESPACE_STIR @@ -48,9 +48,9 @@ static SeparableCartesianMetzImageFilter::RegisterIt dummy2; static SeparableGaussianImageFilter::RegisterIt dummySGF; static SeparableConvolutionImageFilter::RegisterIt dummy5; static NonseparableConvolutionUsingRealDFTImageFilter::RegisterIt dummy7; -static TruncateToCylindricalFOVImageProcessor ::RegisterIt dummy6; -static ChainedDataProcessor >::RegisterIt dummy3; -static ThresholdMinToSmallPositiveValueDataProcessor >::RegisterIt dummy4; +static TruncateToCylindricalFOVImageProcessor::RegisterIt dummy6; +static ChainedDataProcessor>::RegisterIt dummy3; +static ThresholdMinToSmallPositiveValueDataProcessor>::RegisterIt dummy4; #ifdef HAVE_JSON static HUToMuImageProcessor>::RegisterIt dummyHUToMu; diff --git a/src/buildblock/centre_of_gravity.cxx b/src/buildblock/centre_of_gravity.cxx index 112bd76c56..a0c5c74913 100644 --- a/src/buildblock/centre_of_gravity.cxx +++ b/src/buildblock/centre_of_gravity.cxx @@ -17,15 +17,14 @@ See STIR/LICENSE.txt for details */ /*! - \file + \file \ingroup Array - - \brief Implementations of centre_of_gravity.h - \warning Only 1, 2 and 3 dimensional versions with floats are instantiated. + + \brief Implementations of centre_of_gravity.h + \warning Only 1, 2 and 3 dimensional versions with floats are instantiated. \author Kris Thielemans */ - #include "stir/VoxelsOnCartesianGrid.h" #include "stir/CartesianCoordinate3D.h" #include "stir/centre_of_gravity.h" @@ -37,106 +36,86 @@ using std::min; using std::max; #endif - START_NAMESPACE_STIR template T -find_unweighted_centre_of_gravity_1d(const VectorWithOffset& row) -{ +find_unweighted_centre_of_gravity_1d(const VectorWithOffset& row) { T CoG; assign(CoG, 0); - for (int x=row.get_min_index(); x<=row.get_max_index(); x++) - CoG += row[x]*x; + for (int x = row.get_min_index(); x <= row.get_max_index(); x++) + CoG += row[x] * x; return CoG; } #ifdef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION -#define T float +# define T float #else template #endif T -find_unweighted_centre_of_gravity(const Array<1,T>& row) -{ +find_unweighted_centre_of_gravity(const Array<1, T>& row) { return find_unweighted_centre_of_gravity_1d(row); } template -BasicCoordinate -find_unweighted_centre_of_gravity(const Array& array) -{ +BasicCoordinate +find_unweighted_centre_of_gravity(const Array& array) { if (array.size() == 0) - return BasicCoordinate(0); + return BasicCoordinate(0); /* Use recursion to lower dimensional case, based on the following sum_ijk {i,j,k} a_ijk - = sum_i {i,0,0} sum_jk a_ijk + + = sum_i {i,0,0} sum_jk a_ijk + sum_i sum_jk {0,j,k} a_ijk The first term can be computed as a 1D CoG calculation, the last term is a sum of num_dimensions-1 CoG's. */ // last term - BasicCoordinate lower_dimension_CoG(0); - for (int i=array.get_min_index(); i<=array.get_max_index(); ++i) - { - lower_dimension_CoG += find_unweighted_centre_of_gravity(array[i]); - } + BasicCoordinate lower_dimension_CoG(0); + for (int i = array.get_min_index(); i <= array.get_max_index(); ++i) { + lower_dimension_CoG += find_unweighted_centre_of_gravity(array[i]); + } // first term - Array<1,T> - first_dim_sums(array.get_min_index(), array.get_max_index()); - for (int i=array.get_min_index(); i<=array.get_max_index(); ++i) - { - first_dim_sums[i] = array[i].sum(); - } - const T first_dim_CoG = - find_unweighted_centre_of_gravity(first_dim_sums); + Array<1, T> first_dim_sums(array.get_min_index(), array.get_max_index()); + for (int i = array.get_min_index(); i <= array.get_max_index(); ++i) { + first_dim_sums[i] = array[i].sum(); + } + const T first_dim_CoG = find_unweighted_centre_of_gravity(first_dim_sums); // put them into 1 coordinate and return return join(first_dim_CoG, lower_dimension_CoG); } - template -BasicCoordinate -find_centre_of_gravity(const Array& array) -{ +BasicCoordinate +find_centre_of_gravity(const Array& array) { const T sum = array.sum(); if (sum == 0) error("Warning: find_centre_of_gravity cannot properly normalise, as data sum to 0\n"); - return - find_unweighted_centre_of_gravity(array) / sum; + return find_unweighted_centre_of_gravity(array) / sum; } - template void -find_centre_of_gravity_in_mm_per_plane( VectorWithOffset< CartesianCoordinate3D >& allCoG, - VectorWithOffset& weights, - const VoxelsOnCartesianGrid& image) -{ - - allCoG = - VectorWithOffset< CartesianCoordinate3D > - (image.get_min_index(), image.get_max_index()); - weights = - VectorWithOffset - (image.get_min_index(), image.get_max_index()); - - for (int z=image.get_min_index(); z<=image.get_max_index(); z++) - { +find_centre_of_gravity_in_mm_per_plane(VectorWithOffset>& allCoG, VectorWithOffset& weights, + const VoxelsOnCartesianGrid& image) { + + allCoG = VectorWithOffset>(image.get_min_index(), image.get_max_index()); + weights = VectorWithOffset(image.get_min_index(), image.get_max_index()); + + for (int z = image.get_min_index(); z <= image.get_max_index(); z++) { weights[z] = max(image[z].sum(), 0.F); - if (weights[z]==0) - allCoG[z] = CartesianCoordinate3D(0.F,0.F,0.F); - else - { - const BasicCoordinate<2,T> CoG = find_centre_of_gravity(image[z]); - allCoG[z].y() = CoG[1]; - allCoG[z].x() = CoG[2]; - } + if (weights[z] == 0) + allCoG[z] = CartesianCoordinate3D(0.F, 0.F, 0.F); + else { + const BasicCoordinate<2, T> CoG = find_centre_of_gravity(image[z]); + allCoG[z].y() = CoG[1]; + allCoG[z].x() = CoG[2]; + } allCoG[z].z() = static_cast(z); allCoG[z] = image.get_physical_coordinates_for_indices(allCoG[z]); } @@ -144,22 +123,16 @@ find_centre_of_gravity_in_mm_per_plane( VectorWithOffset< CartesianCoordinate3D template CartesianCoordinate3D -find_centre_of_gravity_in_mm(const VoxelsOnCartesianGrid& image) -{ - const BasicCoordinate<3,T> CoG = find_centre_of_gravity(image); - return image.get_physical_coordinates_for_indices(CoG); +find_centre_of_gravity_in_mm(const VoxelsOnCartesianGrid& image) { + const BasicCoordinate<3, T> CoG = find_centre_of_gravity(image); + return image.get_physical_coordinates_for_indices(CoG); } - - //******* INSTANTIATIONS // next instantiations already does 1 and 2 dimensional versions of the other functions -template -void -find_centre_of_gravity_in_mm_per_plane( VectorWithOffset< CartesianCoordinate3D >& allCoG, - VectorWithOffset& weights, - const VoxelsOnCartesianGrid& image); +template void find_centre_of_gravity_in_mm_per_plane(VectorWithOffset>& allCoG, + VectorWithOffset& weights, const VoxelsOnCartesianGrid& image); /* template @@ -167,8 +140,6 @@ BasicCoordinate<3,float> find_centre_of_gravity(const Array<3,float>&); */ // this instantiates 3D versions -template -CartesianCoordinate3D -find_centre_of_gravity_in_mm(const VoxelsOnCartesianGrid& image); +template CartesianCoordinate3D find_centre_of_gravity_in_mm(const VoxelsOnCartesianGrid& image); END_NAMESPACE_STIR diff --git a/src/buildblock/date_time_functions.cxx b/src/buildblock/date_time_functions.cxx index 2b43f8fd9b..73422e66ff 100644 --- a/src/buildblock/date_time_functions.cxx +++ b/src/buildblock/date_time_functions.cxx @@ -16,9 +16,9 @@ */ /*! - \file + \file \ingroup date_time - + \brief Functions for date-time conversions \author Kris Thielemans @@ -34,33 +34,32 @@ START_NAMESPACE_STIR -int time_zone_offset_in_secs() -{ +int +time_zone_offset_in_secs() { static bool first_run = true; static int tz_offset; - if (first_run) - { - first_run = false; - - time_t current_time = time(0); - //struct tm * local_time = localtime(¤t_time); - //std::cerr << "Local: " << local_time->tm_hour << ',' << local_time->tm_isdst; - struct tm * gmt = gmtime(¤t_time); - //std::cerr << ", GMT: " << gmt->tm_hour << ',' << gmt->tm_isdst << "\n"; - time_t gm_time = mktime(gmt); - // std::cerr << " diff Local-GMT: " << difftime(current_time, gm_time)/3600. << "\n"; - tz_offset =round(difftime(current_time, gm_time)); - } + if (first_run) { + first_run = false; + + time_t current_time = time(0); + // struct tm * local_time = localtime(¤t_time); + // std::cerr << "Local: " << local_time->tm_hour << ',' << local_time->tm_isdst; + struct tm* gmt = gmtime(¤t_time); + // std::cerr << ", GMT: " << gmt->tm_hour << ',' << gmt->tm_isdst << "\n"; + time_t gm_time = mktime(gmt); + // std::cerr << " diff Local-GMT: " << difftime(current_time, gm_time)/3600. << "\n"; + tz_offset = round(difftime(current_time, gm_time)); + } return tz_offset; } -int current_time_zone_and_DST_offset_in_secs() -{ +int +current_time_zone_and_DST_offset_in_secs() { time_t current_time = time(0); - struct tm * local_time = localtime(¤t_time); + struct tm* local_time = localtime(¤t_time); const int isdst = local_time->tm_isdst; - return time_zone_offset_in_secs() + isdst*3600; + return time_zone_offset_in_secs() + isdst * 3600; } /* internal function to find the time_t for the Unix epoch @@ -70,75 +69,64 @@ int current_time_zone_and_DST_offset_in_secs() in the GB/Portugal time_zone. This is of course weird. However, as long as we handle this internally consistently, it shouldn't matter. */ -static -time_t unix_epoch_time_t() -{ +static time_t +unix_epoch_time_t() { static bool first_run = true; static time_t epoch_offset; - if (first_run) - { - first_run = false; - struct tm time_info_start; // 1 JAN 1970 00:00 in local time (without DST) - time_info_start.tm_year = 1970 - 1900; - time_info_start.tm_mon = 0; - time_info_start.tm_mday = 1; - time_info_start.tm_sec = 0; - time_info_start.tm_min = 0; - time_info_start.tm_hour = 0; - time_info_start.tm_isdst = 0; - time_t loc_time_start = mktime(&time_info_start); - epoch_offset = loc_time_start + time_zone_offset_in_secs(); - if (epoch_offset != 0) - info(boost::format("Using Unix epoch (1Jan1970) offset of %1%") % epoch_offset, 3); - } + if (first_run) { + first_run = false; + struct tm time_info_start; // 1 JAN 1970 00:00 in local time (without DST) + time_info_start.tm_year = 1970 - 1900; + time_info_start.tm_mon = 0; + time_info_start.tm_mday = 1; + time_info_start.tm_sec = 0; + time_info_start.tm_min = 0; + time_info_start.tm_hour = 0; + time_info_start.tm_isdst = 0; + time_t loc_time_start = mktime(&time_info_start); + epoch_offset = loc_time_start + time_zone_offset_in_secs(); + if (epoch_offset != 0) + info(boost::format("Using Unix epoch (1Jan1970) offset of %1%") % epoch_offset, 3); + } return epoch_offset; } -std::string DICOM_date_time_to_DT(const std::string& date_org, const std::string& time_org, const std::string& TZ_org) -{ +std::string +DICOM_date_time_to_DT(const std::string& date_org, const std::string& time_org, const std::string& TZ_org) { // get rid of white spaces, just in case const std::string date = standardise_interfile_keyword(date_org); const std::string time = standardise_interfile_keyword(time_org); const std::string TZ = standardise_interfile_keyword(TZ_org); - if ((date.size()!=8) || (time.size()<6 || (time.size()>6 && time[6]!='.')) - || (!TZ.empty() && TZ.size()!=5)) + if ((date.size() != 8) || (time.size() < 6 || (time.size() > 6 && time[6] != '.')) || (!TZ.empty() && TZ.size() != 5)) error(boost::format("DICOM_date_time_to_DT: ill-formed input: date=%s, time=%s, TZ info=%s") % date % time % TZ); - return date+time+TZ; + return date + time + TZ; } static double -parse_DICOM_TZ(const std::string& tz, const bool silent) -{ +parse_DICOM_TZ(const std::string& tz, const bool silent) { double tz_offset; - if (tz.empty()) + if (tz.empty()) { { - { - tz_offset = time_zone_offset_in_secs(); - if (!silent) - warning(boost::format("No Time_Zone info in DICOM DT. Using local time-zone without DST (%+.0f secs)") % tz_offset); - } + tz_offset = time_zone_offset_in_secs(); + if (!silent) + warning(boost::format("No Time_Zone info in DICOM DT. Using local time-zone without DST (%+.0f secs)") % tz_offset); } - else - { - if (tz.size() != 5) - error("Time_Zone info '" + tz + "' does not fit DICOM standard"); - else - { - tz_offset = - (boost::lexical_cast(tz.substr(0,3))*60 + - boost::lexical_cast(tz.substr(3)))*60; - //info(boost::format("Found time zone difference in DICOM DT '%s' of %g secs") - // % str % tz_offset, 2); - } + } else { + if (tz.size() != 5) + error("Time_Zone info '" + tz + "' does not fit DICOM standard"); + else { + tz_offset = (boost::lexical_cast(tz.substr(0, 3)) * 60 + boost::lexical_cast(tz.substr(3))) * 60; + // info(boost::format("Found time zone difference in DICOM DT '%s' of %g secs") + // % str % tz_offset, 2); } + } return tz_offset; } static void -parse_DICOM_fraction_and_TZ(std::string& fraction, std::string& tz_string, const std::string& str) -{ +parse_DICOM_fraction_and_TZ(std::string& fraction, std::string& tz_string, const std::string& str) { const std::string rest = str.substr(14); std::size_t tz_pos = rest.find('+'); @@ -152,21 +140,23 @@ parse_DICOM_fraction_and_TZ(std::string& fraction, std::string& tz_string, const fraction = rest; } -double DICOM_datetime_to_secs_since_Unix_epoch(const std::string& str_org, bool silent) -{ +double +DICOM_datetime_to_secs_since_Unix_epoch(const std::string& str_org, bool silent) { // get rid of white spaces, just in case const std::string str = standardise_interfile_keyword(str_org); - if (str.size()<14) + if (str.size() < 14) error("DICOM DT '" + str + "' is ill-formed"); struct tm time_info; - time_info.tm_year = boost::lexical_cast(str.substr(0,4)) - 1900; - time_info.tm_mon = boost::lexical_cast(str.substr(4,2)) - 1; - time_info.tm_mday = boost::lexical_cast(str.substr(6,2)); - time_info.tm_hour = boost::lexical_cast(str.substr(8,2));; - time_info.tm_min = boost::lexical_cast(str.substr(10,2));; - time_info.tm_sec = boost::lexical_cast(str.substr(12,2)); + time_info.tm_year = boost::lexical_cast(str.substr(0, 4)) - 1900; + time_info.tm_mon = boost::lexical_cast(str.substr(4, 2)) - 1; + time_info.tm_mday = boost::lexical_cast(str.substr(6, 2)); + time_info.tm_hour = boost::lexical_cast(str.substr(8, 2)); + ; + time_info.tm_min = boost::lexical_cast(str.substr(10, 2)); + ; + time_info.tm_sec = boost::lexical_cast(str.substr(12, 2)); time_info.tm_isdst = 0; // no DST // find the time as if the above is specified in the local time_zone double time_diff = difftime(mktime(&time_info), unix_epoch_time_t()); @@ -182,91 +172,72 @@ double DICOM_datetime_to_secs_since_Unix_epoch(const std::string& str_org, bool time_diff -= tz_offset - time_zone_offset_in_secs(); // handle fraction of seconds - if (fraction.size()>0) - { - if (fraction[0] != '.') - error("DICOM DT '" + str + "' is ill-formed for the fractional seconds"); - try - { - const double frac_secs = boost::lexical_cast(fraction); - time_diff += frac_secs; - } - catch (...) - { - error("DICOM DT '" + str + "' is ill-formed for the fractional seconds"); - } + if (fraction.size() > 0) { + if (fraction[0] != '.') + error("DICOM DT '" + str + "' is ill-formed for the fractional seconds"); + try { + const double frac_secs = boost::lexical_cast(fraction); + time_diff += frac_secs; + } catch (...) { + error("DICOM DT '" + str + "' is ill-formed for the fractional seconds"); } + } } - info(boost::format("DICOM DT '%s' = %.2fs since unix epoch (1970)")% str % time_diff, 3); + info(boost::format("DICOM DT '%s' = %.2fs since unix epoch (1970)") % str % time_diff, 3); return time_diff; } -std::string secs_since_Unix_epoch_to_DICOM_datetime(double secs, int time_zone_offset_in_secs) -{ - const int tz_in_mins = time_zone_offset_in_secs/60; +std::string +secs_since_Unix_epoch_to_DICOM_datetime(double secs, int time_zone_offset_in_secs) { + const int tz_in_mins = time_zone_offset_in_secs / 60; // check it's in minutes (as expected, but also imposed by DICOM) { - if (round(tz_in_mins*60 - secs)>1) - error(boost::format("secs_since_Unix_epoch_to_DICOM_datetime: can only handle time_zone offsets that are a multiple of 60, argument was %d") % time_zone_offset_in_secs); + if (round(tz_in_mins * 60 - secs) > 1) + error(boost::format("secs_since_Unix_epoch_to_DICOM_datetime: can only handle time_zone offsets that are a multiple of 60, " + "argument was %d") % + time_zone_offset_in_secs); } time_t time = round(floor(secs) + unix_epoch_time_t() + time_zone_offset_in_secs); - struct tm * time_info = gmtime(&time); - return (boost::format("%04d%02d%02d%02d%02d%02d.%02d%+03d%02d") % - (time_info->tm_year + 1900)% - (time_info->tm_mon+1) % - time_info->tm_mday % - time_info->tm_hour % - time_info->tm_min % - time_info->tm_sec % - round((secs - floor(secs))*100) % - (tz_in_mins/60) % - (tz_in_mins%60)).str(); + struct tm* time_info = gmtime(&time); + return (boost::format("%04d%02d%02d%02d%02d%02d.%02d%+03d%02d") % (time_info->tm_year + 1900) % (time_info->tm_mon + 1) % + time_info->tm_mday % time_info->tm_hour % time_info->tm_min % time_info->tm_sec % round((secs - floor(secs)) * 100) % + (tz_in_mins / 60) % (tz_in_mins % 60)) + .str(); } - DateTimeStrings -DICOM_datetime_to_Interfile(const std::string& str) -{ +DICOM_datetime_to_Interfile(const std::string& str) { // just do a conversion to check on format (will throw if there's an error) DICOM_datetime_to_secs_since_Unix_epoch(str); DateTimeStrings dt; - dt.date = str.substr(0,4) + ':' + str.substr(4,2) + ':' + str.substr(6,2); - dt.time = str.substr(8,2) + ':' + str.substr(10,2) + ':' + str.substr(12); + dt.date = str.substr(0, 4) + ':' + str.substr(4, 2) + ':' + str.substr(6, 2); + dt.time = str.substr(8, 2) + ':' + str.substr(10, 2) + ':' + str.substr(12); return dt; } std::string -Interfile_datetime_to_DICOM(const DateTimeStrings& dt) -{ +Interfile_datetime_to_DICOM(const DateTimeStrings& dt) { // get rid of white spaces, just in case const std::string date = standardise_interfile_keyword(dt.date); const std::string time = standardise_interfile_keyword(dt.time); - if ((date.size()!=10) || - (date[4] != ':') || - (date[7] != ':')) + if ((date.size() != 10) || (date[4] != ':') || (date[7] != ':')) error("Interfile_datetime_to_DICOM: ill-formed date: " + date); - if ((time.size()<8) || - (time[2] != ':') || - (time[5] != ':')) + if ((time.size() < 8) || (time[2] != ':') || (time[5] != ':')) error("Interfile_datetime_to_DICOM: ill-formed time: " + time); - return - date.substr(0,4) + date.substr(5,2) + date.substr(8,2) + - time.substr(0,2) + time.substr(3,2) + time.substr(6); + return date.substr(0, 4) + date.substr(5, 2) + date.substr(8, 2) + time.substr(0, 2) + time.substr(3, 2) + time.substr(6); } -double Interfile_datetime_to_secs_since_Unix_epoch(const DateTimeStrings& intf, bool silent) -{ - return - DICOM_datetime_to_secs_since_Unix_epoch(Interfile_datetime_to_DICOM(intf), silent); +double +Interfile_datetime_to_secs_since_Unix_epoch(const DateTimeStrings& intf, bool silent) { + return DICOM_datetime_to_secs_since_Unix_epoch(Interfile_datetime_to_DICOM(intf), silent); } -DateTimeStrings secs_since_Unix_epoch_to_Interfile_datetime(double secs, int time_zone_offset_in_secs) -{ - return - DICOM_datetime_to_Interfile(secs_since_Unix_epoch_to_DICOM_datetime(secs, time_zone_offset_in_secs)); +DateTimeStrings +secs_since_Unix_epoch_to_Interfile_datetime(double secs, int time_zone_offset_in_secs) { + return DICOM_datetime_to_Interfile(secs_since_Unix_epoch_to_DICOM_datetime(secs, time_zone_offset_in_secs)); } END_NAMESPACE_STIR diff --git a/src/buildblock/error.cxx b/src/buildblock/error.cxx index e693e79702..faff01b7e3 100644 --- a/src/buildblock/error.cxx +++ b/src/buildblock/error.cxx @@ -1,8 +1,8 @@ // // /*! - \file - + \file + \brief defines the stir::error() function \author Kris Thielemans @@ -40,29 +40,27 @@ Visual Studio can be accomodated with the following work-around */ #ifdef BOOST_MSVC -#define vsnprintf _vsnprintf +# define vsnprintf _vsnprintf #endif START_NAMESPACE_STIR -void error(const char *const s, ...) -{ +void +error(const char* const s, ...) { va_list ap; va_start(ap, s); - const unsigned size=10000; + const unsigned size = 10000; char tmp[size]; - const int returned_size= vsnprintf(tmp,size, s, ap); + const int returned_size = vsnprintf(tmp, size, s, ap); std::stringstream ss; va_end(ap); - if (returned_size<0) - ss << "\nERROR: but error formatting error message" << std::endl; - else - { - ss << "\nERROR: " << tmp << std::endl; - if (static_cast(returned_size)>=size) - ss << "\nWARNING: previous error message truncated as it exceeds " - << size << "bytes" << std::endl; + if (returned_size < 0) + ss << "\nERROR: but error formatting error message" << std::endl; + else { + ss << "\nERROR: " << tmp << std::endl; + if (static_cast(returned_size) >= size) + ss << "\nWARNING: previous error message truncated as it exceeds " << size << "bytes" << std::endl; } writeText(ss.str().c_str(), ERROR_CHANNEL); std::string msg = tmp; diff --git a/src/buildblock/extend_projdata.cxx b/src/buildblock/extend_projdata.cxx index 455b993594..04b8a1be38 100644 --- a/src/buildblock/extend_projdata.cxx +++ b/src/buildblock/extend_projdata.cxx @@ -8,22 +8,22 @@ it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. - + This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - + See STIR/LICENSE.txt for details */ /*! -\file +\file \ingroup projdata \brief Implementation of functions to extension of direct sinograms in view direction \author Kris Thielemans \author Charalampos Tsoumpas - + */ #include "stir/Array.h" #include "stir/SegmentBySinogram.h" @@ -35,136 +35,105 @@ START_NAMESPACE_STIR -namespace detail -{ - /* This function takes symmetries in the sinogram space into account - to find data in the negative segment if necessary. - However, it needs testing if it would work for non-direct sinograms. - */ - inline static - Array<2,float> - extend_sinogram_in_views(const Array<2,float>& sino_positive_segment, - const Array<2,float>& sino_negative_segment, - const ProjDataInfo& proj_data_info, - const int min_view_extension, const int max_view_extension) - { - //* Check if projdata are from 0 to pi-phi - bool min_is_extended=false; - bool max_is_extended=false; - BasicCoordinate<2,int> min_in, max_in; - if (!sino_positive_segment.get_regular_range(min_in, max_in)) - { - warning("input segment 0 should have a regular range"); - } - - const int org_min_view_num=min_in[1]; - const int org_max_view_num=max_in[1]; - - const float min_phi = proj_data_info.get_phi(Bin(0,0,0,0)); - const float max_phi = proj_data_info.get_phi(Bin(0,max_in[1],0,0)); - - const float sampling_phi = - proj_data_info.get_phi(Bin(0,1,0,0)) - min_phi; - const int num_views_for_180 = round(_PI/sampling_phi); - - if (fabs(min_phi)< .01) - { - min_in[1]-=min_view_extension; - min_is_extended=true; - } - if (fabs(max_phi-(_PI-sampling_phi))<.01) - { - max_in[1]+=max_view_extension; - max_is_extended=true; - } - - - IndexRange<2> extended_range(min_in, max_in); - Array<2,float> input_extended_view(extended_range); - - if (!min_is_extended) - warning("Minimum view of the original projdata is not 0"); - if (!max_is_extended) - warning("Maximum view of the original projdata is not 180-sampling_phi"); - - for (int view_num=min_in[1]; view_num<=max_in[1]; ++view_num) - { - bool use_extension=false; - int symmetric_view_num=0; - if (view_numorg_max_view_num && max_is_extended==true) - { - use_extension=true; - symmetric_view_num = view_num - num_views_for_180; - } - - if (!use_extension) - input_extended_view[view_num]= - sino_positive_segment[view_num]; - else - { - const int symmetric_min = std::max(min_in[2], -max_in[2]); - const int symmetric_max = std::min(-min_in[2], max_in[2]); - for (int tang_num=symmetric_min; tang_num<=symmetric_max; ++tang_num) - input_extended_view[view_num][tang_num]= - sino_negative_segment[symmetric_view_num][-tang_num]; - // now do extrapolation where we don't have data - for (int tang_num=min_in[2]; tang_num +extend_sinogram_in_views(const Array<2, float>& sino_positive_segment, const Array<2, float>& sino_negative_segment, + const ProjDataInfo& proj_data_info, const int min_view_extension, const int max_view_extension) { + //* Check if projdata are from 0 to pi-phi + bool min_is_extended = false; + bool max_is_extended = false; + BasicCoordinate<2, int> min_in, max_in; + if (!sino_positive_segment.get_regular_range(min_in, max_in)) { + warning("input segment 0 should have a regular range"); + } + + const int org_min_view_num = min_in[1]; + const int org_max_view_num = max_in[1]; + + const float min_phi = proj_data_info.get_phi(Bin(0, 0, 0, 0)); + const float max_phi = proj_data_info.get_phi(Bin(0, max_in[1], 0, 0)); + + const float sampling_phi = proj_data_info.get_phi(Bin(0, 1, 0, 0)) - min_phi; + const int num_views_for_180 = round(_PI / sampling_phi); + + if (fabs(min_phi) < .01) { + min_in[1] -= min_view_extension; + min_is_extended = true; } + if (fabs(max_phi - (_PI - sampling_phi)) < .01) { + max_in[1] += max_view_extension; + max_is_extended = true; + } + + IndexRange<2> extended_range(min_in, max_in); + Array<2, float> input_extended_view(extended_range); + + if (!min_is_extended) + warning("Minimum view of the original projdata is not 0"); + if (!max_is_extended) + warning("Maximum view of the original projdata is not 180-sampling_phi"); + + for (int view_num = min_in[1]; view_num <= max_in[1]; ++view_num) { + bool use_extension = false; + int symmetric_view_num = 0; + if (view_num < org_min_view_num && min_is_extended == true) { + use_extension = true; + symmetric_view_num = view_num + num_views_for_180; + } else if (view_num > org_max_view_num && max_is_extended == true) { + use_extension = true; + symmetric_view_num = view_num - num_views_for_180; + } + + if (!use_extension) + input_extended_view[view_num] = sino_positive_segment[view_num]; + else { + const int symmetric_min = std::max(min_in[2], -max_in[2]); + const int symmetric_max = std::min(-min_in[2], max_in[2]); + for (int tang_num = symmetric_min; tang_num <= symmetric_max; ++tang_num) + input_extended_view[view_num][tang_num] = sino_negative_segment[symmetric_view_num][-tang_num]; + // now do extrapolation where we don't have data + for (int tang_num = min_in[2]; tang_num < symmetric_min; ++tang_num) + input_extended_view[view_num][tang_num] = input_extended_view[view_num][symmetric_min]; + for (int tang_num = symmetric_max + 1; tang_num <= max_in[2]; ++tang_num) + input_extended_view[view_num][tang_num] = input_extended_view[view_num][symmetric_max]; + } + } // loop over views + return input_extended_view; +} } // end of namespace detail -Array<3,float> -extend_segment_in_views(const SegmentBySinogram& sino, - const int min_view_extension, const int max_view_extension) -{ - if (sino.get_segment_num()!=0) +Array<3, float> +extend_segment_in_views(const SegmentBySinogram& sino, const int min_view_extension, const int max_view_extension) { + if (sino.get_segment_num() != 0) error("extend_segment with single segment works only for segment 0"); - BasicCoordinate<3,int> min, max; - - min[1]=sino.get_min_axial_pos_num(); - max[1]=sino.get_max_axial_pos_num(); - min[2]=sino.get_min_view_num(); - max[2]=sino.get_max_view_num(); - min[3]=sino.get_min_tangential_pos_num(); - max[3]=sino.get_max_tangential_pos_num(); - const IndexRange<3> out_range(min,max); - Array<3,float> out(out_range); - for (int ax_pos_num=min[1]; ax_pos_num <=max[1] ; ++ax_pos_num) - { - out[ax_pos_num] = - detail:: - extend_sinogram_in_views(sino[ax_pos_num],sino[ax_pos_num], - *(sino.get_proj_data_info_sptr()), - min_view_extension, max_view_extension); - } + BasicCoordinate<3, int> min, max; + + min[1] = sino.get_min_axial_pos_num(); + max[1] = sino.get_max_axial_pos_num(); + min[2] = sino.get_min_view_num(); + max[2] = sino.get_max_view_num(); + min[3] = sino.get_min_tangential_pos_num(); + max[3] = sino.get_max_tangential_pos_num(); + const IndexRange<3> out_range(min, max); + Array<3, float> out(out_range); + for (int ax_pos_num = min[1]; ax_pos_num <= max[1]; ++ax_pos_num) { + out[ax_pos_num] = detail::extend_sinogram_in_views(sino[ax_pos_num], sino[ax_pos_num], *(sino.get_proj_data_info_sptr()), + min_view_extension, max_view_extension); + } return out; } -Array<2,float> -extend_sinogram_in_views(const Sinogram& sino, - const int min_view_extension, const int max_view_extension) -{ - if (sino.get_segment_num()!=0) +Array<2, float> +extend_sinogram_in_views(const Sinogram& sino, const int min_view_extension, const int max_view_extension) { + if (sino.get_segment_num() != 0) error("extend_segment with single segment works only for segment 0"); - return - detail:: - extend_sinogram_in_views(sino, sino, - *(sino.get_proj_data_info_sptr()), - min_view_extension, max_view_extension); + return detail::extend_sinogram_in_views(sino, sino, *(sino.get_proj_data_info_sptr()), min_view_extension, max_view_extension); } END_NAMESPACE_STIR diff --git a/src/buildblock/find_fwhm_in_image.cxx b/src/buildblock/find_fwhm_in_image.cxx index ba1ce904a4..fa77d2af3c 100644 --- a/src/buildblock/find_fwhm_in_image.cxx +++ b/src/buildblock/find_fwhm_in_image.cxx @@ -29,7 +29,7 @@ #include "stir/round.h" #include "stir/assign_to_subregion.h" #include "stir/extract_line.h" -#include +#include #include using namespace std; @@ -39,274 +39,241 @@ START_NAMESPACE_STIR /* 2 functions that calculate the maximum point (x0,y0) of a parabola that passes through 3 points. - As input it takes the Begin and End Iterators of a sequence of numbers (e.g. vector). - The three points are the maximum (x2,y2) of this sequence and the two neighbour points - (x1,y1) and (x3,y3). + As input it takes the Begin and End Iterators of a sequence of numbers (e.g. vector). + The three points are the maximum (x2,y2) of this sequence and the two neighbour points + (x1,y1) and (x3,y3). The first function returns the maximum point value y0, the secodn returns x0 -*/ +*/ template -static float parabolic_3points_fit(const RandomAccessIterType& begin_iter, - const RandomAccessIterType& end_iter); +static float parabolic_3points_fit(const RandomAccessIterType& begin_iter, const RandomAccessIterType& end_iter); template -static float parabolic_3points_fit_x0(const RandomAccessIterType& begin_iter, - const RandomAccessIterType& end_iter) ; - +static float parabolic_3points_fit_x0(const RandomAccessIterType& begin_iter, const RandomAccessIterType& end_iter); + template -static float find_NEMA_level(const Array<1,elemT>& column, const float level) -{ - const float real_maximum_value = - parabolic_3points_fit(column.begin(),column.end()); - return find_level_width(column.begin(),column.end(),real_maximum_value/level) ; +static float +find_NEMA_level(const Array<1, elemT>& column, const float level) { + const float real_maximum_value = parabolic_3points_fit(column.begin(), column.end()); + return find_level_width(column.begin(), column.end(), real_maximum_value / level); } template -std::list > -find_fwhm_in_image(DiscretisedDensity<3,elemT> & input_image, - const unsigned int num_maxima, - const float level, - const int dimension, - const bool nema) -{ - ResolutionIndex<3,float> res_index; - std::list > list_res_index; - - const DiscretisedDensityOnCartesianGrid <3,float>* input_image_cartesian_ptr = - dynamic_cast< DiscretisedDensityOnCartesianGrid<3,float>*> (&input_image); - if (input_image_cartesian_ptr == 0) - { - error("find_fwhm_in_image currently only works with DiscretisedDensityOnCartesianGrid images"); - } - BasicCoordinate<3,int> min_index, max_index; +std::list> +find_fwhm_in_image(DiscretisedDensity<3, elemT>& input_image, const unsigned int num_maxima, const float level, + const int dimension, const bool nema) { + ResolutionIndex<3, float> res_index; + std::list> list_res_index; + + const DiscretisedDensityOnCartesianGrid<3, float>* input_image_cartesian_ptr = + dynamic_cast*>(&input_image); + if (input_image_cartesian_ptr == 0) { + error("find_fwhm_in_image currently only works with DiscretisedDensityOnCartesianGrid images"); + } + BasicCoordinate<3, int> min_index, max_index; if (!input_image.get_regular_range(min_index, max_index)) error("find_fwhm_in_image works only on regular ranges\n"); - - CartesianCoordinate3D - grid_spacing=input_image_cartesian_ptr->get_grid_spacing(); - - for (unsigned int maximum_num=0; maximum_num!=num_maxima; ++ maximum_num) - { - float current_maximum ; - BasicCoordinate<3,int> max_location ; - Coordinate3D do_direction(true,true,true); - if(dimension!=0)//Divide into [maximum_num] slices and returns a max per slice - { - const float step=float( max_index[dimension]- min_index[dimension])/float(num_maxima-1); - const int slice= round(min_index[dimension] + maximum_num*step); - max_location = maximum_location_per_slice(input_image,slice,dimension); - current_maximum = input_image[max_location]; - do_direction[dimension]=false; - } - else // Searches through out the image to find the point sources - { - max_location = indices_at_maximum(input_image); - current_maximum = input_image[max_location]; - } - res_index.voxel_location = max_location; - res_index.voxel_value = current_maximum ; - for(int i=1;i<=3;++i) - res_index.resolution[i] = grid_spacing[i]* - (do_direction[i] ? (nema?find_NEMA_level(extract_line(input_image,max_location, - i), level) : - find_NEMA_level(interpolate_line(input_image,max_location, - do_direction, - i), level)): 0); - list_res_index.push_back(res_index); - if (maximum_num+1!= num_maxima && dimension==0) - assign_to_subregion(input_image,max_location,round(res_index.resolution/grid_spacing/level*2), input_image.find_min()); + + CartesianCoordinate3D grid_spacing = input_image_cartesian_ptr->get_grid_spacing(); + + for (unsigned int maximum_num = 0; maximum_num != num_maxima; ++maximum_num) { + float current_maximum; + BasicCoordinate<3, int> max_location; + Coordinate3D do_direction(true, true, true); + if (dimension != 0) // Divide into [maximum_num] slices and returns a max per slice + { + const float step = float(max_index[dimension] - min_index[dimension]) / float(num_maxima - 1); + const int slice = round(min_index[dimension] + maximum_num * step); + max_location = maximum_location_per_slice(input_image, slice, dimension); + current_maximum = input_image[max_location]; + do_direction[dimension] = false; + } else // Searches through out the image to find the point sources + { + max_location = indices_at_maximum(input_image); + current_maximum = input_image[max_location]; } - return list_res_index ; -} + res_index.voxel_location = max_location; + res_index.voxel_value = current_maximum; + for (int i = 1; i <= 3; ++i) + res_index.resolution[i] = + grid_spacing[i] * (do_direction[i] + ? (nema ? find_NEMA_level(extract_line(input_image, max_location, i), level) + : find_NEMA_level(interpolate_line(input_image, max_location, do_direction, i), level)) + : 0); + list_res_index.push_back(res_index); + if (maximum_num + 1 != num_maxima && dimension == 0) + assign_to_subregion(input_image, max_location, round(res_index.resolution / grid_spacing / level * 2), + input_image.find_min()); + } + return list_res_index; +} -template -BasicCoordinate<3,int> -maximum_location_per_slice(const Array<3,elemT>& input_array, - const int slice, const int dimension) -{ - BasicCoordinate<3,int> min_index, max_index; +template +BasicCoordinate<3, int> +maximum_location_per_slice(const Array<3, elemT>& input_array, const int slice, const int dimension) { + BasicCoordinate<3, int> min_index, max_index; if (!input_array.get_regular_range(min_index, max_index)) error("maximum_location_per_slice works only on regular ranges\n"); - BasicCoordinate<3,int> min_slice_index=min_index, - max_slice_index=max_index; - min_slice_index[dimension] = slice ; - max_slice_index[dimension] = slice ; - const IndexRange<3> slice_range(min_slice_index,max_slice_index); - Array<3,elemT> slice_array(slice_range); - BasicCoordinate<3,int> counter; - for (counter[1]=min_slice_index[1]; counter[1]<= max_slice_index[1] ; ++counter[1]) - for (counter[2]=min_slice_index[2]; counter[2]<= max_slice_index[2] ; ++counter[2]) - for (counter[3]=min_slice_index[3]; counter[3]<= max_slice_index[3] ; ++counter[3]) - slice_array[counter] = input_array[counter]; - return indices_at_maximum(slice_array); -} - + BasicCoordinate<3, int> min_slice_index = min_index, max_slice_index = max_index; + min_slice_index[dimension] = slice; + max_slice_index[dimension] = slice; + const IndexRange<3> slice_range(min_slice_index, max_slice_index); + Array<3, elemT> slice_array(slice_range); + BasicCoordinate<3, int> counter; + for (counter[1] = min_slice_index[1]; counter[1] <= max_slice_index[1]; ++counter[1]) + for (counter[2] = min_slice_index[2]; counter[2] <= max_slice_index[2]; ++counter[2]) + for (counter[3] = min_slice_index[3]; counter[3] <= max_slice_index[3]; ++counter[3]) + slice_array[counter] = input_array[counter]; + return indices_at_maximum(slice_array); +} + template -Array<1,elemT> -interpolate_line(const Array<3,elemT>& input_array, - const BasicCoordinate<3,int>& max_location, - const BasicCoordinate<3,bool>& do_direction, - const int dimension) +Array<1, elemT> +interpolate_line(const Array<3, elemT>& input_array, const BasicCoordinate<3, int>& max_location, + const BasicCoordinate<3, bool>& do_direction, const int dimension) /* - This function interpolates a column from the given array, that includes the particular voxel and returns + This function interpolates a column from the given array, that includes the particular voxel and returns a column in Array<1,elemT> type, at the wanted dimension (z=1, y=2, x=3). //Assume same index at each direction // -*/ +*/ { - BasicCoordinate<3,int> min_index, max_index; + BasicCoordinate<3, int> min_index, max_index; if (!input_array.get_regular_range(min_index, max_index)) error("interpolate_line works only on regular ranges\n"); - Array<1,elemT> line(min_index[dimension],max_index[dimension]); + Array<1, elemT> line(min_index[dimension], max_index[dimension]); { - Array<1,elemT> line_z(min_index[1],max_index[1]), - line_y(min_index[2],max_index[2]), - line_x(min_index[3],max_index[3]), - line_000(min_index[dimension],max_index[dimension]), - line_001(min_index[dimension],max_index[dimension]), - line_010(min_index[dimension],max_index[dimension]), - line_100(min_index[dimension],max_index[dimension]), - line_011(min_index[dimension],max_index[dimension]), - line_101(min_index[dimension],max_index[dimension]), - line_110(min_index[dimension],max_index[dimension]), - line_111(min_index[dimension],max_index[dimension]); - line_z = extract_line(input_array,max_location,1); - line_y = extract_line(input_array,max_location,2); - line_x = extract_line(input_array,max_location,3); - float z0=do_direction[1] ? parabolic_3points_fit_x0(line_z.begin(),line_z.end()) : 0; - float y0=do_direction[2] ? parabolic_3points_fit_x0(line_y.begin(),line_y.end()) : 0; - float x0=do_direction[3] ? parabolic_3points_fit_x0(line_x.begin(),line_x.end()) : 0; - - BasicCoordinate<3,int> location_000,location_001,location_010,location_100, - location_011,location_101,location_110,location_111; - location_000 = max_location; - - location_001[1] = max_location[1]; - location_001[2] = max_location[2]; - location_001[3] = x0>0 ? max_location[3]+1 : (x0<0 ? max_location[3]-1 : max_location[3]) ; - - location_010[1] = max_location[1]; - location_010[2] = y0>0 ? max_location[2]+1 : (y0<0 ? max_location[2]-1 : max_location[2]) ; + Array<1, elemT> line_z(min_index[1], max_index[1]), line_y(min_index[2], max_index[2]), line_x(min_index[3], max_index[3]), + line_000(min_index[dimension], max_index[dimension]), line_001(min_index[dimension], max_index[dimension]), + line_010(min_index[dimension], max_index[dimension]), line_100(min_index[dimension], max_index[dimension]), + line_011(min_index[dimension], max_index[dimension]), line_101(min_index[dimension], max_index[dimension]), + line_110(min_index[dimension], max_index[dimension]), line_111(min_index[dimension], max_index[dimension]); + line_z = extract_line(input_array, max_location, 1); + line_y = extract_line(input_array, max_location, 2); + line_x = extract_line(input_array, max_location, 3); + float z0 = do_direction[1] ? parabolic_3points_fit_x0(line_z.begin(), line_z.end()) : 0; + float y0 = do_direction[2] ? parabolic_3points_fit_x0(line_y.begin(), line_y.end()) : 0; + float x0 = do_direction[3] ? parabolic_3points_fit_x0(line_x.begin(), line_x.end()) : 0; + + BasicCoordinate<3, int> location_000, location_001, location_010, location_100, location_011, location_101, location_110, + location_111; + location_000 = max_location; + + location_001[1] = max_location[1]; + location_001[2] = max_location[2]; + location_001[3] = x0 > 0 ? max_location[3] + 1 : (x0 < 0 ? max_location[3] - 1 : max_location[3]); + + location_010[1] = max_location[1]; + location_010[2] = y0 > 0 ? max_location[2] + 1 : (y0 < 0 ? max_location[2] - 1 : max_location[2]); location_010[3] = max_location[3]; - - location_100[1] = z0>0 ? max_location[1]+1 : (z0<0 ? max_location[1]-1 : max_location[1]) ; - location_100[2] = max_location[2]; + location_100[1] = z0 > 0 ? max_location[1] + 1 : (z0 < 0 ? max_location[1] - 1 : max_location[1]); + location_100[2] = max_location[2]; location_100[3] = max_location[3]; - - - location_011[1] = max_location[1]; - location_011[2] = y0>0 ? max_location[2]+1 : (y0<0 ? max_location[2]-1 : max_location[2]) ; - location_011[3] = x0>0 ? max_location[3]+1 : (x0<0 ? max_location[3]-1 : max_location[3]) ; - - location_101[1] = z0>0 ? max_location[1]+1 : (z0<0 ? max_location[1]-1 : max_location[1]) ; - location_101[2] = max_location[2]; - location_101[3] = x0>0 ? max_location[3]+1 : (x0<0 ? max_location[3]-1 : max_location[3]) ; - - location_110[1] = z0>0 ? max_location[1]+1 : (z0<0 ? max_location[1]-1 : max_location[1]) ; - location_110[2] = y0>0 ? max_location[2]+1 : (y0<0 ? max_location[2]-1 : max_location[2]) ; - location_110[3] = max_location[3] ; - - location_111[1] = z0>0 ? max_location[1]+1 : (z0<0 ? max_location[1]-1 : max_location[1]) ; - location_111[2] = y0>0 ? max_location[2]+1 : (y0<0 ? max_location[2]-1 : max_location[2]) ; - location_111[3] = x0>0 ? max_location[3]+1 : (x0<0 ? max_location[3]-1 : max_location[3]) ; - - line_000 = extract_line(input_array,location_000,dimension); - line_001 = extract_line(input_array,location_001,dimension); - line_010 = extract_line(input_array,location_010,dimension); - line_100 = extract_line(input_array,location_100,dimension); - line_011 = extract_line(input_array,location_011,dimension); - line_101 = extract_line(input_array,location_101,dimension); - - line_110 = extract_line(input_array,location_110,dimension); - line_111 = extract_line(input_array,location_111,dimension); - line = line_000*(1-abs(z0))*(1-abs(y0))*(1-abs(x0)) ; - line+= line_001*(1-abs(z0))*(1-abs(y0))*abs(x0) ; - line+= line_010*(1-abs(z0))*abs(y0)*(1-abs(x0)) ; - line+= line_100*abs(z0)*(1-abs(y0))*(1-abs(x0)) ; - line+= line_011*(1-abs(z0))*abs(y0)*abs(x0) ; - line+= line_101*abs(z0)*(1-abs(y0))*abs(x0) ; - line+= line_110*abs(z0)*abs(y0)*(1-abs(x0)) ; - line+= line_111*abs(z0)*abs(y0)*abs(x0) ; + + location_011[1] = max_location[1]; + location_011[2] = y0 > 0 ? max_location[2] + 1 : (y0 < 0 ? max_location[2] - 1 : max_location[2]); + location_011[3] = x0 > 0 ? max_location[3] + 1 : (x0 < 0 ? max_location[3] - 1 : max_location[3]); + + location_101[1] = z0 > 0 ? max_location[1] + 1 : (z0 < 0 ? max_location[1] - 1 : max_location[1]); + location_101[2] = max_location[2]; + location_101[3] = x0 > 0 ? max_location[3] + 1 : (x0 < 0 ? max_location[3] - 1 : max_location[3]); + + location_110[1] = z0 > 0 ? max_location[1] + 1 : (z0 < 0 ? max_location[1] - 1 : max_location[1]); + location_110[2] = y0 > 0 ? max_location[2] + 1 : (y0 < 0 ? max_location[2] - 1 : max_location[2]); + location_110[3] = max_location[3]; + + location_111[1] = z0 > 0 ? max_location[1] + 1 : (z0 < 0 ? max_location[1] - 1 : max_location[1]); + location_111[2] = y0 > 0 ? max_location[2] + 1 : (y0 < 0 ? max_location[2] - 1 : max_location[2]); + location_111[3] = x0 > 0 ? max_location[3] + 1 : (x0 < 0 ? max_location[3] - 1 : max_location[3]); + + line_000 = extract_line(input_array, location_000, dimension); + line_001 = extract_line(input_array, location_001, dimension); + line_010 = extract_line(input_array, location_010, dimension); + line_100 = extract_line(input_array, location_100, dimension); + line_011 = extract_line(input_array, location_011, dimension); + line_101 = extract_line(input_array, location_101, dimension); + + line_110 = extract_line(input_array, location_110, dimension); + line_111 = extract_line(input_array, location_111, dimension); + line = line_000 * (1 - abs(z0)) * (1 - abs(y0)) * (1 - abs(x0)); + line += line_001 * (1 - abs(z0)) * (1 - abs(y0)) * abs(x0); + line += line_010 * (1 - abs(z0)) * abs(y0) * (1 - abs(x0)); + line += line_100 * abs(z0) * (1 - abs(y0)) * (1 - abs(x0)); + line += line_011 * (1 - abs(z0)) * abs(y0) * abs(x0); + line += line_101 * abs(z0) * (1 - abs(y0)) * abs(x0); + line += line_110 * abs(z0) * abs(y0) * (1 - abs(x0)); + line += line_111 * abs(z0) * abs(y0) * abs(x0); } - return line ; -} + return line; +} template -float parabolic_3points_fit(const RandomAccessIterType& begin_iter, - const RandomAccessIterType& end_iter) -{ - float real_max_value; - const RandomAccessIterType max_iter = std::max_element(begin_iter,end_iter); - if (max_iter==end_iter-1 || max_iter==begin_iter) - return *max_iter; // In case maximum is at the borders of the image - { - const float y1 = *(max_iter-1); - const float y2 = *max_iter; - const float y3 = *(max_iter+1); +float +parabolic_3points_fit(const RandomAccessIterType& begin_iter, const RandomAccessIterType& end_iter) { + float real_max_value; + const RandomAccessIterType max_iter = std::max_element(begin_iter, end_iter); + if (max_iter == end_iter - 1 || max_iter == begin_iter) + return *max_iter; // In case maximum is at the borders of the image + { + const float y1 = *(max_iter - 1); + const float y2 = *max_iter; + const float y3 = *(max_iter + 1); const float x1 = -1.; - const float x2 = 0.; // Giving the axis zero point at x2. - const float x3 = 1.; - const float a1 = (x1-x2)*(x1-x3); - const float a2 = (x2-x1)*(x2-x3); - const float a3 = (x3-x2)*(x3-x1); - /* + const float x2 = 0.; // Giving the axis zero point at x2. + const float x3 = 1.; + const float a1 = (x1 - x2) * (x1 - x3); + const float a2 = (x2 - x1) * (x2 - x3); + const float a3 = (x3 - x2) * (x3 - x1); + /* Now find parameters for parabola that fits these 3 points. Using Langrange's classical formula, equation will be: y(x)=((x - x2)*(x - x3)*y1/a1)+ ((x - x1)*(x - x3)*y2/a2) + ((x - x1)*(x - x2)*y3/a3) - y'(x0) = 0 => x0 = 0.5*(x1*a1*(y2*a3+y3*a2)+x2*a2*(y1*a3+y3*a1)+x3*a3*(y1*a2+y2*a1))/(y1*a2*a3+y2*a1*a3+y3*a1*a2) - */ - const float x0 = 0.5F*(x1*a1*(y2*a3+y3*a2)+x2*a2*(y1*a3+y3*a1)+x3*a3*(y1*a2+y2*a1)) - /(y1*a2*a3+y2*a1*a3+y3*a1*a2) ; - real_max_value = ((x0 - x2)*(x0 - x3)*y1/a1) + - ((x0 - x1)*(x0 - x3)*y2/a2) + - ((x0 - x1)*(x0 - x2)*y3/a3) ; - - } - return real_max_value ; -} + y'(x0) = 0 => x0 = 0.5*(x1*a1*(y2*a3+y3*a2)+x2*a2*(y1*a3+y3*a1)+x3*a3*(y1*a2+y2*a1))/(y1*a2*a3+y2*a1*a3+y3*a1*a2) + */ + const float x0 = 0.5F * (x1 * a1 * (y2 * a3 + y3 * a2) + x2 * a2 * (y1 * a3 + y3 * a1) + x3 * a3 * (y1 * a2 + y2 * a1)) / + (y1 * a2 * a3 + y2 * a1 * a3 + y3 * a1 * a2); + real_max_value = ((x0 - x2) * (x0 - x3) * y1 / a1) + ((x0 - x1) * (x0 - x3) * y2 / a2) + ((x0 - x1) * (x0 - x2) * y3 / a3); + } + return real_max_value; +} template -float parabolic_3points_fit_x0(const RandomAccessIterType& begin_iter, - const RandomAccessIterType& end_iter) -{ - // float real_max_value; - const RandomAccessIterType max_iter = std::max_element(begin_iter,end_iter); - if (max_iter==end_iter-1) return 0; // In case maximum is at the borders of the image - if (max_iter==begin_iter) return 0; - //else - - const float y1 = *(max_iter-1); - const float y2 = *max_iter; - const float y3 = *(max_iter+1); +float +parabolic_3points_fit_x0(const RandomAccessIterType& begin_iter, const RandomAccessIterType& end_iter) { + // float real_max_value; + const RandomAccessIterType max_iter = std::max_element(begin_iter, end_iter); + if (max_iter == end_iter - 1) + return 0; // In case maximum is at the borders of the image + if (max_iter == begin_iter) + return 0; + // else + + const float y1 = *(max_iter - 1); + const float y2 = *max_iter; + const float y3 = *(max_iter + 1); const float x1 = -1.; - const float x2 = 0.; // Giving the axis zero point at x2. - const float x3 = 1.; - const float a1 = (x1-x2)*(x1-x3); - const float a2 = (x2-x1)*(x2-x3); - const float a3 = (x3-x2)*(x3-x1); - /* + const float x2 = 0.; // Giving the axis zero point at x2. + const float x3 = 1.; + const float a1 = (x1 - x2) * (x1 - x3); + const float a2 = (x2 - x1) * (x2 - x3); + const float a3 = (x3 - x2) * (x3 - x1); + /* Now find parameters for parabola that fits these 3 points. Using Langrange's classical formula, equation will be: y(x)=((x - x2)*(x - x3)*y1/a1)+ ((x - x1)*(x - x3)*y2/a2) + ((x - x1)*(x - x2)*y3/a3) - y'(x0) = 0 => x0 = 0.5*(x1*a1*(y2*a3+y3*a2)+x2*a2*(y1*a3+y3*a1)+x3*a3*(y1*a2+y2*a1))/(y1*a2*a3+y2*a1*a3+y3*a1*a2) - */ - const float x0 = 0.5F*(x1*a1*(y2*a3+y3*a2)+x2*a2*(y1*a3+y3*a1)+x3*a3*(y1*a2+y2*a1)) - /(y1*a2*a3+y2*a1*a3+y3*a1*a2) ; - return x0 ; -} - + y'(x0) = 0 => x0 = 0.5*(x1*a1*(y2*a3+y3*a2)+x2*a2*(y1*a3+y3*a1)+x3*a3*(y1*a2+y2*a1))/(y1*a2*a3+y2*a1*a3+y3*a1*a2) + */ + const float x0 = 0.5F * (x1 * a1 * (y2 * a3 + y3 * a2) + x2 * a2 * (y1 * a3 + y3 * a1) + x3 * a3 * (y1 * a2 + y2 * a1)) / + (y1 * a2 * a3 + y2 * a1 * a3 + y3 * a1 * a2); + return x0; +} /*************************************************** instantiations ***************************************************/ -template -std::list > -find_fwhm_in_image<>(DiscretisedDensity<3,float> & input_image, - const unsigned int num_maxima, - const float level, - const int dimension, - const bool nema); - - +template std::list> find_fwhm_in_image<>(DiscretisedDensity<3, float>& input_image, + const unsigned int num_maxima, const float level, + const int dimension, const bool nema); + END_NAMESPACE_STIR diff --git a/src/buildblock/getopt.c b/src/buildblock/getopt.c index fff1f5164c..6b5bd344e7 100644 --- a/src/buildblock/getopt.c +++ b/src/buildblock/getopt.c @@ -25,21 +25,19 @@ Ditto for AIX 3.2 and . */ #ifndef HAVE_SYSTEM_GETOPT +# ifndef _NO_PROTO +# define _NO_PROTO +# endif -#ifndef _NO_PROTO -# define _NO_PROTO -#endif - - -#if !defined __STDC__ || !__STDC__ +# if !defined __STDC__ || !__STDC__ /* This is a separate conditional since some stdc systems reject `defined (const)'. */ -# ifndef const -# define const -# endif -#endif +# ifndef const +# define const +# endif +# endif -#include +# include /* Comment out all this code if we are using the GNU C Library, and are not actually compiling the library itself. This code is part of the GNU C @@ -49,47 +47,46 @@ program understand `configure --with-gnu-libc' and omit the object files, it is simpler to just do this in the source for each such file. */ -#define GETOPT_INTERFACE_VERSION 2 -#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 -# include -# if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION -# define ELIDE_CODE -# endif -#endif - -#ifndef ELIDE_CODE +# define GETOPT_INTERFACE_VERSION 2 +# if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 +# include +# if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +# define ELIDE_CODE +# endif +# endif +# ifndef ELIDE_CODE /* This needs to come after some library #include to get __GNU_LIBRARY__ defined. */ -#ifdef __GNU_LIBRARY__ +# ifdef __GNU_LIBRARY__ /* Don't include stdlib.h for non-GNU C libraries because some of them contain conflicting prototypes for getopt. */ -# include -# include -#endif /* GNU C library. */ - -#ifdef VMS -# include -# if HAVE_STRING_H - 0 -# include -# endif -#endif - -#ifndef _ +# include +# include +# endif /* GNU C library. */ + +# ifdef VMS +# include +# if HAVE_STRING_H - 0 +# include +# endif +# endif + +# ifndef _ /* This is for other GNU distributions with internationalized messages. */ -# if (HAVE_LIBINTL_H && ENABLE_NLS) || defined _LIBC -# include -# ifndef _ -# define _(msgid) gettext (msgid) -# endif -# else -# define _(msgid) (msgid) -# endif -# if defined _LIBC && defined USE_IN_LIBIO -# include -# endif -#endif +# if (HAVE_LIBINTL_H && ENABLE_NLS) || defined _LIBC +# include +# ifndef _ +# define _(msgid) gettext(msgid) +# endif +# else +# define _(msgid) (msgid) +# endif +# if defined _LIBC && defined USE_IN_LIBIO +# include +# endif +# endif /* This version of `getopt' appears to the caller like standard Unix `getopt' but it behaves differently for the user, since it allows the user @@ -105,7 +102,7 @@ GNU application programs can use a third alternative mode in which they can distinguish the relative order of options and other arguments. */ -#include "stir/getopt.h" +# include "stir/getopt.h" /* For communication from `getopt' to the caller. When `getopt' finds an option that takes an argument, @@ -113,7 +110,7 @@ Also, when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ -char *optarg; +char* optarg; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller @@ -143,7 +140,7 @@ int __getopt_initialized; If this is zero, or a null string, it means resume the scan by advancing to the next ARGV-element. */ -static char *nextchar; +static char* nextchar; /* Callers store zero here to inhibit the error message for unrecognized options. */ @@ -185,63 +182,57 @@ int optopt = '?'; of the value of `ordering'. In the case of RETURN_IN_ORDER, only `--' can cause `getopt' to return -1 with `optind' != ARGC. */ -static enum -{ - REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER -} ordering; +static enum { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER } ordering; /* Value of POSIXLY_CORRECT environment variable. */ -static char *posixly_correct; +static char* posixly_correct; -#ifdef __GNU_LIBRARY__ +# ifdef __GNU_LIBRARY__ /* We want to avoid inclusion of string.h with non-GNU libraries because there are many ways it can cause trouble. On some systems, it contains special magic macros that don't work in GCC. */ -# include -# define my_index strchr -#else +# include +# define my_index strchr +# else -# if HAVE_STRING_H || WIN32 /* Pete Wilson mod 7/28/02 */ -# include -# else -# include -# endif +# if HAVE_STRING_H || WIN32 /* Pete Wilson mod 7/28/02 */ +# include +# else +# include +# endif /* Avoid depending on library functions or files whose names are inconsistent. */ -#ifndef getenv -extern char *getenv (); -#endif +# ifndef getenv +extern char* getenv(); +# endif -static char * -my_index (str, chr) - const char *str; - int chr; +static char* my_index(str, chr) const char* str; +int chr; { - while (*str) - { - if (*str == chr) - return (char *) str; - str++; - } + while (*str) { + if (*str == chr) + return (char*)str; + str++; + } return 0; } /* If using GCC, we can safely declare strlen this way. If not using GCC, it is ok not to declare it. */ -#ifdef __GNUC__ +# ifdef __GNUC__ /* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. That was relevant to code that was here before. */ -# if (!defined __STDC__ || !__STDC__) && !defined strlen +# if (!defined __STDC__ || !__STDC__) && !defined strlen /* gcc with -traditional declares the built-in strlen to return int, and has done so at least since version 2.4.5. -- rms. */ -extern int strlen (const char *); -# endif /* not __STDC__ */ -#endif /* __GNUC__ */ +extern int strlen(const char*); +# endif /* not __STDC__ */ +# endif /* __GNUC__ */ -#endif /* not __GNU_LIBRARY__ */ +# endif /* not __GNU_LIBRARY__ */ /* Handle permutation of arguments. */ @@ -252,38 +243,37 @@ extern int strlen (const char *); static int first_nonopt; static int last_nonopt; -#ifdef _LIBC +# ifdef _LIBC /* Stored original parameters. XXX This is no good solution. We should rather copy the args so that we can compare them later. But we must not use malloc(3). */ extern int __libc_argc; -extern char **__libc_argv; +extern char** __libc_argv; /* Bash 2.0 gives us an environment variable containing flags indicating ARGV elements that should not be considered arguments. */ -# ifdef USE_NONOPTION_FLAGS +# ifdef USE_NONOPTION_FLAGS /* Defined in getopt_init.c */ -extern char *__getopt_nonoption_flags; +extern char* __getopt_nonoption_flags; static int nonoption_flags_max_len; static int nonoption_flags_len; -# endif - -# ifdef USE_NONOPTION_FLAGS -# define SWAP_FLAGS(ch1, ch2) \ - if (nonoption_flags_len > 0) \ - { \ - char __tmp = __getopt_nonoption_flags[ch1]; \ - __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ - __getopt_nonoption_flags[ch2] = __tmp; \ - } -# else -# define SWAP_FLAGS(ch1, ch2) -# endif -#else /* !_LIBC */ -# define SWAP_FLAGS(ch1, ch2) -#endif /* _LIBC */ +# endif + +# ifdef USE_NONOPTION_FLAGS +# define SWAP_FLAGS(ch1, ch2) \ + if (nonoption_flags_len > 0) { \ + char __tmp = __getopt_nonoption_flags[ch1]; \ + __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ + __getopt_nonoption_flags[ch2] = __tmp; \ + } +# else +# define SWAP_FLAGS(ch1, ch2) +# endif +# else /* !_LIBC */ +# define SWAP_FLAGS(ch1, ch2) +# endif /* _LIBC */ /* Exchange two adjacent subsequences of ARGV. One subsequence is elements [first_nonopt,last_nonopt) @@ -294,83 +284,71 @@ static int nonoption_flags_len; `first_nonopt' and `last_nonopt' are relocated so that they describe the new indices of the non-options in ARGV after they are moved. */ -#if defined __STDC__ && __STDC__ -static void exchange (char **); -#endif +# if defined __STDC__ && __STDC__ +static void exchange(char**); +# endif -static void -exchange (argv) - char **argv; +static void exchange(argv) char** argv; { int bottom = first_nonopt; int middle = last_nonopt; int top = optind; - char *tem; + char* tem; /* Exchange the shorter segment with the far end of the longer segment. That puts the shorter segment into the right place. It leaves the longer segment in the right place overall, but it consists of two parts that need to be swapped next. */ -#if defined _LIBC && defined USE_NONOPTION_FLAGS +# if defined _LIBC && defined USE_NONOPTION_FLAGS /* First make sure the handling of the `__getopt_nonoption_flags' string can work normally. Our top argument must be in the range of the string. */ - if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) - { - /* We must extend the array. The user plays games with us and - presents new arguments. */ - char *new_str = malloc (top + 1); - if (new_str == NULL) - nonoption_flags_len = nonoption_flags_max_len = 0; - else - { - memset (__mempcpy (new_str, __getopt_nonoption_flags, - nonoption_flags_max_len), - '\0', top + 1 - nonoption_flags_max_len); - nonoption_flags_max_len = top + 1; - __getopt_nonoption_flags = new_str; - } + if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) { + /* We must extend the array. The user plays games with us and + presents new arguments. */ + char* new_str = malloc(top + 1); + if (new_str == NULL) + nonoption_flags_len = nonoption_flags_max_len = 0; + else { + memset(__mempcpy(new_str, __getopt_nonoption_flags, nonoption_flags_max_len), '\0', top + 1 - nonoption_flags_max_len); + nonoption_flags_max_len = top + 1; + __getopt_nonoption_flags = new_str; } -#endif - - while (top > middle && middle > bottom) - { - if (top - middle > middle - bottom) - { - /* Bottom segment is the short one. */ - int len = middle - bottom; - register int i; - - /* Swap it with the top part of the top segment. */ - for (i = 0; i < len; i++) - { - tem = argv[bottom + i]; - argv[bottom + i] = argv[top - (middle - bottom) + i]; - argv[top - (middle - bottom) + i] = tem; - SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); - } - /* Exclude the moved bottom segment from further swapping. */ - top -= len; - } - else - { - /* Top segment is the short one. */ - int len = top - middle; - register int i; - - /* Swap it with the bottom part of the bottom segment. */ - for (i = 0; i < len; i++) - { - tem = argv[bottom + i]; - argv[bottom + i] = argv[middle + i]; - argv[middle + i] = tem; - SWAP_FLAGS (bottom + i, middle + i); - } - /* Exclude the moved top segment from further swapping. */ - bottom += len; - } + } +# endif + + while (top > middle && middle > bottom) { + if (top - middle > middle - bottom) { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + SWAP_FLAGS(bottom + i, top - (middle - bottom) + i); + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } else { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + SWAP_FLAGS(bottom + i, middle + i); + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; } + } /* Update records for the slots the non-options now occupy. */ @@ -380,14 +358,12 @@ exchange (argv) /* Initialize the internal data when the first call is made. */ -#if defined __STDC__ && __STDC__ -static const char *_getopt_initialize (int, char *const *, const char *); -#endif -static const char * -_getopt_initialize (argc, argv, optstring) - int argc; - char *const *argv; - const char *optstring; +# if defined __STDC__ && __STDC__ +static const char* _getopt_initialize(int, char* const*, const char*); +# endif +static const char* _getopt_initialize(argc, argv, optstring) int argc; +char* const* argv; +const char* optstring; { /* Start processing options with ARGV-element 1 (since ARGV-element 0 is the program name); the sequence of previously skipped @@ -397,54 +373,42 @@ _getopt_initialize (argc, argv, optstring) nextchar = NULL; - posixly_correct = getenv ("POSIXLY_CORRECT"); + posixly_correct = getenv("POSIXLY_CORRECT"); /* Determine how to handle the ordering of options and nonoptions. */ - if (optstring[0] == '-') - { - ordering = RETURN_IN_ORDER; - ++optstring; - } - else if (optstring[0] == '+') - { - ordering = REQUIRE_ORDER; - ++optstring; - } - else if (posixly_correct != NULL) + if (optstring[0] == '-') { + ordering = RETURN_IN_ORDER; + ++optstring; + } else if (optstring[0] == '+') { + ordering = REQUIRE_ORDER; + ++optstring; + } else if (posixly_correct != NULL) ordering = REQUIRE_ORDER; else ordering = PERMUTE; -#if defined _LIBC && defined USE_NONOPTION_FLAGS - if (posixly_correct == NULL - && argc == __libc_argc && argv == __libc_argv) - { - if (nonoption_flags_max_len == 0) - { - if (__getopt_nonoption_flags == NULL - || __getopt_nonoption_flags[0] == '\0') - nonoption_flags_max_len = -1; - else - { - const char *orig_str = __getopt_nonoption_flags; - int len = nonoption_flags_max_len = strlen (orig_str); - if (nonoption_flags_max_len < argc) - nonoption_flags_max_len = argc; - __getopt_nonoption_flags = - (char *) malloc (nonoption_flags_max_len); - if (__getopt_nonoption_flags == NULL) - nonoption_flags_max_len = -1; - else - memset (__mempcpy (__getopt_nonoption_flags, orig_str, len), - '\0', nonoption_flags_max_len - len); - } - } - nonoption_flags_len = nonoption_flags_max_len; +# if defined _LIBC && defined USE_NONOPTION_FLAGS + if (posixly_correct == NULL && argc == __libc_argc && argv == __libc_argv) { + if (nonoption_flags_max_len == 0) { + if (__getopt_nonoption_flags == NULL || __getopt_nonoption_flags[0] == '\0') + nonoption_flags_max_len = -1; + else { + const char* orig_str = __getopt_nonoption_flags; + int len = nonoption_flags_max_len = strlen(orig_str); + if (nonoption_flags_max_len < argc) + nonoption_flags_max_len = argc; + __getopt_nonoption_flags = (char*)malloc(nonoption_flags_max_len); + if (__getopt_nonoption_flags == NULL) + nonoption_flags_max_len = -1; + else + memset(__mempcpy(__getopt_nonoption_flags, orig_str, len), '\0', nonoption_flags_max_len - len); + } } - else + nonoption_flags_len = nonoption_flags_max_len; + } else nonoption_flags_len = 0; -#endif +# endif return optstring; } @@ -505,14 +469,12 @@ _getopt_initialize (argc, argv, optstring) If LONG_ONLY is nonzero, '-' as well as '--' can introduce long-named options. */ -int -_getopt_internal (argc, argv, optstring, longopts, longind, long_only) - int argc; - char *const *argv; - const char *optstring; - const struct option *longopts; - int *longind; - int long_only; +int _getopt_internal(argc, argv, optstring, longopts, longind, long_only) int argc; +char* const* argv; +const char* optstring; +const struct option* longopts; +int* longind; +int long_only; { int print_errors = opterr; if (optstring[0] == ':') @@ -523,102 +485,95 @@ _getopt_internal (argc, argv, optstring, longopts, longind, long_only) optarg = NULL; - if (optind == 0 || !__getopt_initialized) - { - if (optind == 0) - optind = 1; /* Don't scan ARGV[0], the program name. */ - optstring = _getopt_initialize (argc, argv, optstring); - __getopt_initialized = 1; - } + if (optind == 0 || !__getopt_initialized) { + if (optind == 0) + optind = 1; /* Don't scan ARGV[0], the program name. */ + optstring = _getopt_initialize(argc, argv, optstring); + __getopt_initialized = 1; + } /* Test whether ARGV[optind] points to a non-option argument. Either it does not have option syntax, or there is an environment flag from the shell indicating it is not an option. The later information is only used when the used in the GNU libc. */ -#if defined _LIBC && defined USE_NONOPTION_FLAGS -# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ - || (optind < nonoption_flags_len \ - && __getopt_nonoption_flags[optind] == '1')) -#else -# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') -#endif - - if (nextchar == NULL || *nextchar == '\0') - { - /* Advance to the next ARGV-element. */ - - /* Give FIRST_NONOPT and LAST_NONOPT rational values if OPTIND has been - moved back by the user (who may also have changed the arguments). */ - if (last_nonopt > optind) - last_nonopt = optind; - if (first_nonopt > optind) +# if defined _LIBC && defined USE_NONOPTION_FLAGS +# define NONOPTION_P \ + (argv[optind][0] != '-' || argv[optind][1] == '\0' || \ + (optind < nonoption_flags_len && __getopt_nonoption_flags[optind] == '1')) +# else +# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') +# endif + + if (nextchar == NULL || *nextchar == '\0') { + /* Advance to the next ARGV-element. */ + + /* Give FIRST_NONOPT and LAST_NONOPT rational values if OPTIND has been + moved back by the user (who may also have changed the arguments). */ + if (last_nonopt > optind) + last_nonopt = optind; + if (first_nonopt > optind) + first_nonopt = optind; + + if (ordering == PERMUTE) { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange((char**)argv); + else if (last_nonopt != optind) first_nonopt = optind; - if (ordering == PERMUTE) - { - /* If we have just processed some options following some non-options, - exchange them so that the options come first. */ - - if (first_nonopt != last_nonopt && last_nonopt != optind) - exchange ((char **) argv); - else if (last_nonopt != optind) - first_nonopt = optind; + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ - /* Skip any additional non-options - and extend the range of non-options previously skipped. */ - - while (optind < argc && NONOPTION_P) - optind++; - last_nonopt = optind; - } + while (optind < argc && NONOPTION_P) + optind++; + last_nonopt = optind; + } - /* The special ARGV-element `--' means premature end of options. - Skip it like a null option, - then exchange with previous non-options as if it were an option, - then skip everything else like a non-option. */ + /* The special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ - if (optind != argc && !strcmp (argv[optind], "--")) - { - optind++; + if (optind != argc && !strcmp(argv[optind], "--")) { + optind++; - if (first_nonopt != last_nonopt && last_nonopt != optind) - exchange ((char **) argv); - else if (first_nonopt == last_nonopt) - first_nonopt = optind; - last_nonopt = argc; + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange((char**)argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; - optind = argc; - } + optind = argc; + } - /* If we have done all the ARGV-elements, stop the scan - and back over any non-options that we skipped and permuted. */ + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ - if (optind == argc) - { - /* Set the next-arg-index to point at the non-options - that we previously skipped, so the caller will digest them. */ - if (first_nonopt != last_nonopt) - optind = first_nonopt; - return -1; - } + if (optind == argc) { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return -1; + } - /* If we have come to a non-option and did not permute it, - either stop the scan or describe it to the caller and pass it by. */ + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ - if (NONOPTION_P) - { - if (ordering == REQUIRE_ORDER) - return -1; - optarg = argv[optind++]; - return 1; - } + if (NONOPTION_P) { + if (ordering == REQUIRE_ORDER) + return -1; + optarg = argv[optind++]; + return 1; + } - /* We have found another option-ARGV-element. - Skip the initial punctuation. */ + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ - nextchar = (argv[optind] + 1 - + (longopts != NULL && argv[optind][1] == '-')); - } + nextchar = (argv[optind] + 1 + (longopts != NULL && argv[optind][1] == '-')); + } /* Decode the current option-ARGV-element. */ @@ -635,626 +590,520 @@ _getopt_internal (argc, argv, optstring, longopts, longind, long_only) This distinction seems to be the most useful approach. */ - if (longopts != NULL - && (argv[optind][1] == '-' - || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) - { - char *nameend; - const struct option *p; - const struct option *pfound = NULL; - int exact = 0; - int ambig = 0; - int indfound = -1; - int option_index; - - for (nameend = nextchar; *nameend && *nameend != '='; nameend++) - /* Do nothing. */ ; - - /* Test all long options for either exact match - or abbreviated matches. */ - for (p = longopts, option_index = 0; p->name; p++, option_index++) - if (!strncmp (p->name, nextchar, nameend - nextchar)) - { - if ((unsigned int) (nameend - nextchar) - == (unsigned int) strlen (p->name)) - { - /* Exact match found. */ - pfound = p; - indfound = option_index; - exact = 1; - break; - } - else if (pfound == NULL) - { - /* First nonexact match found. */ - pfound = p; - indfound = option_index; - } - else if (long_only - || pfound->has_arg != p->has_arg - || pfound->flag != p->flag - || pfound->val != p->val) - /* Second or later nonexact match found. */ - ambig = 1; - } + if (longopts != NULL && (argv[optind][1] == '-' || (long_only && (argv[optind][2] || !my_index(optstring, argv[optind][1]))))) { + char* nameend; + const struct option* p; + const struct option* pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = -1; + int option_index; + + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp(p->name, nextchar, nameend - nextchar)) { + if ((unsigned int)(nameend - nextchar) == (unsigned int)strlen(p->name)) { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } else if (pfound == NULL) { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } else if (long_only || pfound->has_arg != p->has_arg || pfound->flag != p->flag || pfound->val != p->val) + /* Second or later nonexact match found. */ + ambig = 1; + } - if (ambig && !exact) - { - if (print_errors) - { -#if defined _LIBC && defined USE_IN_LIBIO - char *buf; + if (ambig && !exact) { + if (print_errors) { +# if defined _LIBC && defined USE_IN_LIBIO + char* buf; - __asprintf (&buf, _("%s: option `%s' is ambiguous\n"), - argv[0], argv[optind]); + __asprintf(&buf, _("%s: option `%s' is ambiguous\n"), argv[0], argv[optind]); - if (_IO_fwide (stderr, 0) > 0) - __fwprintf (stderr, L"%s", buf); - else - fputs (buf, stderr); + if (_IO_fwide(stderr, 0) > 0) + __fwprintf(stderr, L"%s", buf); + else + fputs(buf, stderr); - free (buf); -#else - fprintf (stderr, _("%s: option `%s' is ambiguous\n"), - argv[0], argv[optind]); -#endif - } - nextchar += strlen (nextchar); - optind++; - optopt = 0; - return '?'; - } + free(buf); +# else + fprintf(stderr, _("%s: option `%s' is ambiguous\n"), argv[0], argv[optind]); +# endif + } + nextchar += strlen(nextchar); + optind++; + optopt = 0; + return '?'; + } - if (pfound != NULL) - { - option_index = indfound; - optind++; - if (*nameend) - { - /* Don't test has_arg with >, because some C compilers don't - allow it to be used on enums. */ - if (pfound->has_arg) - optarg = nameend + 1; - else - { - if (print_errors) - { -#if defined _LIBC && defined USE_IN_LIBIO - char *buf; -#endif - - if (argv[optind - 1][1] == '-') - { - /* --option */ -#if defined _LIBC && defined USE_IN_LIBIO - __asprintf (&buf, _("\ + if (pfound != NULL) { + option_index = indfound; + optind++; + if (*nameend) { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else { + if (print_errors) { +# if defined _LIBC && defined USE_IN_LIBIO + char* buf; +# endif + + if (argv[optind - 1][1] == '-') { + /* --option */ +# if defined _LIBC && defined USE_IN_LIBIO + __asprintf(&buf, _("\ %s: option `--%s' doesn't allow an argument\n"), - argv[0], pfound->name); -#else - fprintf (stderr, _("\ + argv[0], pfound->name); +# else + fprintf(stderr, _("\ %s: option `--%s' doesn't allow an argument\n"), - argv[0], pfound->name); -#endif - } - else - { - /* +option or -option */ -#if defined _LIBC && defined USE_IN_LIBIO - __asprintf (&buf, _("\ + argv[0], pfound->name); +# endif + } else { + /* +option or -option */ +# if defined _LIBC && defined USE_IN_LIBIO + __asprintf(&buf, _("\ %s: option `%c%s' doesn't allow an argument\n"), - argv[0], argv[optind - 1][0], - pfound->name); -#else - fprintf (stderr, _("\ + argv[0], argv[optind - 1][0], pfound->name); +# else + fprintf(stderr, _("\ %s: option `%c%s' doesn't allow an argument\n"), - argv[0], argv[optind - 1][0], pfound->name); -#endif - } + argv[0], argv[optind - 1][0], pfound->name); +# endif + } -#if defined _LIBC && defined USE_IN_LIBIO - if (_IO_fwide (stderr, 0) > 0) - __fwprintf (stderr, L"%s", buf); - else - fputs (buf, stderr); +# if defined _LIBC && defined USE_IN_LIBIO + if (_IO_fwide(stderr, 0) > 0) + __fwprintf(stderr, L"%s", buf); + else + fputs(buf, stderr); - free (buf); -#endif - } + free(buf); +# endif + } - nextchar += strlen (nextchar); + nextchar += strlen(nextchar); - optopt = pfound->val; - return '?'; - } - } - else if (pfound->has_arg == 1) - { - if (optind < argc) - optarg = argv[optind++]; - else - { - if (print_errors) - { -#if defined _LIBC && defined USE_IN_LIBIO - char *buf; - - __asprintf (&buf, - _("%s: option `%s' requires an argument\n"), - argv[0], argv[optind - 1]); - - if (_IO_fwide (stderr, 0) > 0) - __fwprintf (stderr, L"%s", buf); - else - fputs (buf, stderr); - - free (buf); -#else - fprintf (stderr, - _("%s: option `%s' requires an argument\n"), - argv[0], argv[optind - 1]); -#endif - } - nextchar += strlen (nextchar); - optopt = pfound->val; - return optstring[0] == ':' ? ':' : '?'; - } - } - nextchar += strlen (nextchar); - if (longind != NULL) - *longind = option_index; - if (pfound->flag) - { - *(pfound->flag) = pfound->val; - return 0; - } - return pfound->val; + optopt = pfound->val; + return '?'; } + } else if (pfound->has_arg == 1) { + if (optind < argc) + optarg = argv[optind++]; + else { + if (print_errors) { +# if defined _LIBC && defined USE_IN_LIBIO + char* buf; - /* Can't find it as a long option. If this is not getopt_long_only, - or the option starts with '--' or is not a valid short - option, then it's an error. - Otherwise interpret it as a short option. */ - if (!long_only || argv[optind][1] == '-' - || my_index (optstring, *nextchar) == NULL) - { - if (print_errors) - { -#if defined _LIBC && defined USE_IN_LIBIO - char *buf; -#endif - - if (argv[optind][1] == '-') - { - /* --option */ -#if defined _LIBC && defined USE_IN_LIBIO - __asprintf (&buf, _("%s: unrecognized option `--%s'\n"), - argv[0], nextchar); -#else - fprintf (stderr, _("%s: unrecognized option `--%s'\n"), - argv[0], nextchar); -#endif - } - else - { - /* +option or -option */ -#if defined _LIBC && defined USE_IN_LIBIO - __asprintf (&buf, _("%s: unrecognized option `%c%s'\n"), - argv[0], argv[optind][0], nextchar); -#else - fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), - argv[0], argv[optind][0], nextchar); -#endif - } - -#if defined _LIBC && defined USE_IN_LIBIO - if (_IO_fwide (stderr, 0) > 0) - __fwprintf (stderr, L"%s", buf); - else - fputs (buf, stderr); + __asprintf(&buf, _("%s: option `%s' requires an argument\n"), argv[0], argv[optind - 1]); - free (buf); -#endif - } - nextchar = (char *) ""; - optind++; - optopt = 0; - return '?'; + if (_IO_fwide(stderr, 0) > 0) + __fwprintf(stderr, L"%s", buf); + else + fputs(buf, stderr); + + free(buf); +# else + fprintf(stderr, _("%s: option `%s' requires an argument\n"), argv[0], argv[optind - 1]); +# endif + } + nextchar += strlen(nextchar); + optopt = pfound->val; + return optstring[0] == ':' ? ':' : '?'; } + } + nextchar += strlen(nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; } + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' || my_index(optstring, *nextchar) == NULL) { + if (print_errors) { +# if defined _LIBC && defined USE_IN_LIBIO + char* buf; +# endif + + if (argv[optind][1] == '-') { + /* --option */ +# if defined _LIBC && defined USE_IN_LIBIO + __asprintf(&buf, _("%s: unrecognized option `--%s'\n"), argv[0], nextchar); +# else + fprintf(stderr, _("%s: unrecognized option `--%s'\n"), argv[0], nextchar); +# endif + } else { + /* +option or -option */ +# if defined _LIBC && defined USE_IN_LIBIO + __asprintf(&buf, _("%s: unrecognized option `%c%s'\n"), argv[0], argv[optind][0], nextchar); +# else + fprintf(stderr, _("%s: unrecognized option `%c%s'\n"), argv[0], argv[optind][0], nextchar); +# endif + } + +# if defined _LIBC && defined USE_IN_LIBIO + if (_IO_fwide(stderr, 0) > 0) + __fwprintf(stderr, L"%s", buf); + else + fputs(buf, stderr); + + free(buf); +# endif + } + nextchar = (char*)""; + optind++; + optopt = 0; + return '?'; + } + } + /* Look at and handle the next short option-character. */ { char c = *nextchar++; - char *temp = my_index (optstring, c); + char* temp = my_index(optstring, c); /* Increment `optind' when we start to process its last character. */ if (*nextchar == '\0') ++optind; - if (temp == NULL || c == ':') - { - if (print_errors) - { -#if defined _LIBC && defined USE_IN_LIBIO - char *buf; -#endif - - if (posixly_correct) - { - /* 1003.2 specifies the format of this message. */ -#if defined _LIBC && defined USE_IN_LIBIO - __asprintf (&buf, _("%s: illegal option -- %c\n"), - argv[0], c); -#else - fprintf (stderr, _("%s: illegal option -- %c\n"), argv[0], c); -#endif - } - else - { -#if defined _LIBC && defined USE_IN_LIBIO - __asprintf (&buf, _("%s: invalid option -- %c\n"), - argv[0], c); -#else - fprintf (stderr, _("%s: invalid option -- %c\n"), argv[0], c); -#endif - } - -#if defined _LIBC && defined USE_IN_LIBIO - if (_IO_fwide (stderr, 0) > 0) - __fwprintf (stderr, L"%s", buf); - else - fputs (buf, stderr); + if (temp == NULL || c == ':') { + if (print_errors) { +# if defined _LIBC && defined USE_IN_LIBIO + char* buf; +# endif + + if (posixly_correct) { + /* 1003.2 specifies the format of this message. */ +# if defined _LIBC && defined USE_IN_LIBIO + __asprintf(&buf, _("%s: illegal option -- %c\n"), argv[0], c); +# else + fprintf(stderr, _("%s: illegal option -- %c\n"), argv[0], c); +# endif + } else { +# if defined _LIBC && defined USE_IN_LIBIO + __asprintf(&buf, _("%s: invalid option -- %c\n"), argv[0], c); +# else + fprintf(stderr, _("%s: invalid option -- %c\n"), argv[0], c); +# endif + } - free (buf); -#endif - } - optopt = c; - return '?'; +# if defined _LIBC && defined USE_IN_LIBIO + if (_IO_fwide(stderr, 0) > 0) + __fwprintf(stderr, L"%s", buf); + else + fputs(buf, stderr); + + free(buf); +# endif } + optopt = c; + return '?'; + } /* Convenience. Treat POSIX -W foo same as long option --foo */ - if (temp[0] == 'W' && temp[1] == ';') - { - char *nameend; - const struct option *p; - const struct option *pfound = NULL; - int exact = 0; - int ambig = 0; - int indfound = 0; - int option_index; + if (temp[0] == 'W' && temp[1] == ';') { + char* nameend; + const struct option* p; + const struct option* pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = 0; + int option_index; - /* This is an option that requires an argument. */ - if (*nextchar != '\0') - { - optarg = nextchar; - /* If we end this ARGV-element by taking the rest as an arg, - we must advance to the next element now. */ - optind++; - } - else if (optind == argc) - { - if (print_errors) - { - /* 1003.2 specifies the format of this message. */ -#if defined _LIBC && defined USE_IN_LIBIO - char *buf; - - __asprintf (&buf, _("%s: option requires an argument -- %c\n"), - argv[0], c); - - if (_IO_fwide (stderr, 0) > 0) - __fwprintf (stderr, L"%s", buf); - else - fputs (buf, stderr); - - free (buf); -#else - fprintf (stderr, _("%s: option requires an argument -- %c\n"), - argv[0], c); -#endif - } - optopt = c; - if (optstring[0] == ':') - c = ':'; - else - c = '?'; - return c; - } + /* This is an option that requires an argument. */ + if (*nextchar != '\0') { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } else if (optind == argc) { + if (print_errors) { + /* 1003.2 specifies the format of this message. */ +# if defined _LIBC && defined USE_IN_LIBIO + char* buf; + + __asprintf(&buf, _("%s: option requires an argument -- %c\n"), argv[0], c); + + if (_IO_fwide(stderr, 0) > 0) + __fwprintf(stderr, L"%s", buf); + else + fputs(buf, stderr); + + free(buf); +# else + fprintf(stderr, _("%s: option requires an argument -- %c\n"), argv[0], c); +# endif + } + optopt = c; + if (optstring[0] == ':') + c = ':'; else - /* We already incremented `optind' once; - increment it again when taking next ARGV-elt as argument. */ - optarg = argv[optind++]; + c = '?'; + return c; + } else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + + /* optarg is now the argument, see if it's in the + table of longopts. */ + + for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) + /* Do nothing. */; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp(p->name, nextchar, nameend - nextchar)) { + if ((unsigned int)(nameend - nextchar) == strlen(p->name)) { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } else if (pfound == NULL) { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } else + /* Second or later nonexact match found. */ + ambig = 1; + } + if (ambig && !exact) { + if (print_errors) { +# if defined _LIBC && defined USE_IN_LIBIO + char* buf; + + __asprintf(&buf, _("%s: option `-W %s' is ambiguous\n"), argv[0], argv[optind]); + + if (_IO_fwide(stderr, 0) > 0) + __fwprintf(stderr, L"%s", buf); + else + fputs(buf, stderr); + + free(buf); +# else + fprintf(stderr, _("%s: option `-W %s' is ambiguous\n"), argv[0], argv[optind]); +# endif + } + nextchar += strlen(nextchar); + optind++; + return '?'; + } + if (pfound != NULL) { + option_index = indfound; + if (*nameend) { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else { + if (print_errors) { +# if defined _LIBC && defined USE_IN_LIBIO + char* buf; + + __asprintf(&buf, _("\ +%s: option `-W %s' doesn't allow an argument\n"), + argv[0], pfound->name); - /* optarg is now the argument, see if it's in the - table of longopts. */ - - for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) - /* Do nothing. */ ; - - /* Test all long options for either exact match - or abbreviated matches. */ - for (p = longopts, option_index = 0; p->name; p++, option_index++) - if (!strncmp (p->name, nextchar, nameend - nextchar)) - { - if ((unsigned int) (nameend - nextchar) == strlen (p->name)) - { - /* Exact match found. */ - pfound = p; - indfound = option_index; - exact = 1; - break; - } - else if (pfound == NULL) - { - /* First nonexact match found. */ - pfound = p; - indfound = option_index; - } + if (_IO_fwide(stderr, 0) > 0) + __fwprintf(stderr, L"%s", buf); else - /* Second or later nonexact match found. */ - ambig = 1; + fputs(buf, stderr); + + free(buf); +# else + fprintf(stderr, _("\ +%s: option `-W %s' doesn't allow an argument\n"), + argv[0], pfound->name); +# endif } - if (ambig && !exact) - { - if (print_errors) - { -#if defined _LIBC && defined USE_IN_LIBIO - char *buf; - - __asprintf (&buf, _("%s: option `-W %s' is ambiguous\n"), - argv[0], argv[optind]); - - if (_IO_fwide (stderr, 0) > 0) - __fwprintf (stderr, L"%s", buf); - else - fputs (buf, stderr); - - free (buf); -#else - fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), - argv[0], argv[optind]); -#endif - } - nextchar += strlen (nextchar); - optind++; + + nextchar += strlen(nextchar); return '?'; } - if (pfound != NULL) - { - option_index = indfound; - if (*nameend) - { - /* Don't test has_arg with >, because some C compilers don't - allow it to be used on enums. */ - if (pfound->has_arg) - optarg = nameend + 1; - else - { - if (print_errors) - { -#if defined _LIBC && defined USE_IN_LIBIO - char *buf; - - __asprintf (&buf, _("\ -%s: option `-W %s' doesn't allow an argument\n"), - argv[0], pfound->name); + } else if (pfound->has_arg == 1) { + if (optind < argc) + optarg = argv[optind++]; + else { + if (print_errors) { +# if defined _LIBC && defined USE_IN_LIBIO + char* buf; + + __asprintf(&buf, _("\ +%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); - if (_IO_fwide (stderr, 0) > 0) - __fwprintf (stderr, L"%s", buf); - else - fputs (buf, stderr); + if (_IO_fwide(stderr, 0) > 0) + __fwprintf(stderr, L"%s", buf); + else + fputs(buf, stderr); - free (buf); -#else - fprintf (stderr, _("\ -%s: option `-W %s' doesn't allow an argument\n"), - argv[0], pfound->name); -#endif - } - - nextchar += strlen (nextchar); - return '?'; - } - } - else if (pfound->has_arg == 1) - { - if (optind < argc) - optarg = argv[optind++]; - else - { - if (print_errors) - { -#if defined _LIBC && defined USE_IN_LIBIO - char *buf; - - __asprintf (&buf, _("\ -%s: option `%s' requires an argument\n"), - argv[0], argv[optind - 1]); - - if (_IO_fwide (stderr, 0) > 0) - __fwprintf (stderr, L"%s", buf); - else - fputs (buf, stderr); - - free (buf); -#else - fprintf (stderr, - _("%s: option `%s' requires an argument\n"), - argv[0], argv[optind - 1]); -#endif - } - nextchar += strlen (nextchar); - return optstring[0] == ':' ? ':' : '?'; - } - } - nextchar += strlen (nextchar); - if (longind != NULL) - *longind = option_index; - if (pfound->flag) - { - *(pfound->flag) = pfound->val; - return 0; - } - return pfound->val; + free(buf); +# else + fprintf(stderr, _("%s: option `%s' requires an argument\n"), argv[0], argv[optind - 1]); +# endif + } + nextchar += strlen(nextchar); + return optstring[0] == ':' ? ':' : '?'; } - nextchar = NULL; - return 'W'; /* Let the application handle it. */ + } + nextchar += strlen(nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; } - if (temp[1] == ':') - { - if (temp[2] == ':') - { - /* This is an option that accepts an argument optionally. */ - if (*nextchar != '\0') - { - optarg = nextchar; - optind++; - } - else - optarg = NULL; - nextchar = NULL; - } - else - { - /* This is an option that requires an argument. */ - if (*nextchar != '\0') - { - optarg = nextchar; - /* If we end this ARGV-element by taking the rest as an arg, - we must advance to the next element now. */ - optind++; - } - else if (optind == argc) - { - if (print_errors) - { - /* 1003.2 specifies the format of this message. */ -#if defined _LIBC && defined USE_IN_LIBIO - char *buf; - - __asprintf (&buf, - _("%s: option requires an argument -- %c\n"), - argv[0], c); - - if (_IO_fwide (stderr, 0) > 0) - __fwprintf (stderr, L"%s", buf); - else - fputs (buf, stderr); - - free (buf); -#else - fprintf (stderr, - _("%s: option requires an argument -- %c\n"), - argv[0], c); -#endif - } - optopt = c; - if (optstring[0] == ':') - c = ':'; - else - c = '?'; - } + nextchar = NULL; + return 'W'; /* Let the application handle it. */ + } + if (temp[1] == ':') { + if (temp[2] == ':') { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') { + optarg = nextchar; + optind++; + } else + optarg = NULL; + nextchar = NULL; + } else { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } else if (optind == argc) { + if (print_errors) { + /* 1003.2 specifies the format of this message. */ +# if defined _LIBC && defined USE_IN_LIBIO + char* buf; + + __asprintf(&buf, _("%s: option requires an argument -- %c\n"), argv[0], c); + + if (_IO_fwide(stderr, 0) > 0) + __fwprintf(stderr, L"%s", buf); else - /* We already incremented `optind' once; - increment it again when taking next ARGV-elt as argument. */ - optarg = argv[optind++]; - nextchar = NULL; + fputs(buf, stderr); + + free(buf); +# else + fprintf(stderr, _("%s: option requires an argument -- %c\n"), argv[0], c); +# endif } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; } + } return c; } } -int -getopt (argc, argv, optstring) - int argc; - char *const *argv; - const char *optstring; -{ - return _getopt_internal (argc, argv, optstring, - (const struct option *) 0, - (int *) 0, - 0); -} - -#endif /* Not ELIDE_CODE. */ +int getopt(argc, argv, optstring) int argc; +char* const* argv; +const char* optstring; +{ return _getopt_internal(argc, argv, optstring, (const struct option*)0, (int*)0, 0); } +# endif /* Not ELIDE_CODE. */ /* Compile with -DTEST to make an executable for use in testing the above definition of `getopt'. */ -/* #define TEST */ /* Pete Wilson mod 7/28/02 */ -#ifdef TEST +/* #define TEST */ /* Pete Wilson mod 7/28/02 */ +# ifdef TEST -#ifndef exit /* Pete Wilson mod 7/28/02 */ - int exit(int); /* Pete Wilson mod 7/28/02 */ -#endif /* Pete Wilson mod 7/28/02 */ +# ifndef exit /* Pete Wilson mod 7/28/02 */ +int exit(int); /* Pete Wilson mod 7/28/02 */ +# endif /* Pete Wilson mod 7/28/02 */ -int -main (argc, argv) - int argc; - char **argv; +int main(argc, argv) int argc; +char** argv; { int c; int digit_optind = 0; - while (1) - { - int this_option_optind = optind ? optind : 1; - - c = getopt (argc, argv, "abc:d:0123456789"); - if (c == -1) - break; - - switch (c) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - if (digit_optind != 0 && digit_optind != this_option_optind) - printf ("digits occur in two different argv-elements.\n"); - digit_optind = this_option_optind; - printf ("option %c\n", c); - break; - - case 'a': - printf ("option a\n"); - break; - - case 'b': - printf ("option b\n"); - break; - - case 'c': - printf ("option c with value `%s'\n", optarg); - break; - - case '?': - break; - - default: - printf ("?? getopt returned character code 0%o ??\n", c); - } + while (1) { + int this_option_optind = optind ? optind : 1; + + c = getopt(argc, argv, "abc:d:0123456789"); + if (c == -1) + break; + + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf("option %c\n", c); + break; + + case 'a': + printf("option a\n"); + break; + + case 'b': + printf("option b\n"); + break; + + case 'c': + printf("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf("?? getopt returned character code 0%o ??\n", c); } + } - if (optind < argc) - { - printf ("non-option ARGV-elements: "); - while (optind < argc) - printf ("%s ", argv[optind++]); - printf ("\n"); - } + if (optind < argc) { + printf("non-option ARGV-elements: "); + while (optind < argc) + printf("%s ", argv[optind++]); + printf("\n"); + } - exit (0); + exit(0); } -#endif /* TEST */ +# endif /* TEST */ #endif /* HAVE_SYSTEM_GETOPT */ diff --git a/src/buildblock/interfile_keyword_functions.cxx b/src/buildblock/interfile_keyword_functions.cxx index 0bd6d48354..e74cc3ac1e 100644 --- a/src/buildblock/interfile_keyword_functions.cxx +++ b/src/buildblock/interfile_keyword_functions.cxx @@ -32,38 +32,32 @@ using namespace std; START_NAMESPACE_STIR -string -standardise_interfile_keyword(const string& keyword) -{ - string::size_type cp =0; //current index - char const * const white_space = " \t_!"; - +string +standardise_interfile_keyword(const string& keyword) { + string::size_type cp = 0; // current index + char const* const white_space = " \t_!"; + // skip white space - cp=keyword.find_first_not_of(white_space,0); - - if(cp==string::npos) + cp = keyword.find_first_not_of(white_space, 0); + + if (cp == string::npos) return string(); - + // remove trailing white spaces - const string::size_type eok=keyword.find_last_not_of(white_space); - + const string::size_type eok = keyword.find_last_not_of(white_space); + string kw; - kw.reserve(eok-cp+1); + kw.reserve(eok - cp + 1); bool previous_was_white_space = false; - while (cp <= eok) - { - if (isspace(static_cast(keyword[cp])) || keyword[cp]=='_' || keyword[cp]=='!') - { - if (!previous_was_white_space) - { - kw.append(1,' '); + while (cp <= eok) { + if (isspace(static_cast(keyword[cp])) || keyword[cp] == '_' || keyword[cp] == '!') { + if (!previous_was_white_space) { + kw.append(1, ' '); previous_was_white_space = true; } // else: skip this white space character - } - else - { - kw.append(1,static_cast(tolower(static_cast(keyword[cp])))); + } else { + kw.append(1, static_cast(tolower(static_cast(keyword[cp])))); previous_was_white_space = false; } ++cp; @@ -71,5 +65,4 @@ standardise_interfile_keyword(const string& keyword) return kw; } - END_NAMESPACE_STIR diff --git a/src/buildblock/interpolate_projdata.cxx b/src/buildblock/interpolate_projdata.cxx index 7610bb9dfe..1ab7966934 100644 --- a/src/buildblock/interpolate_projdata.cxx +++ b/src/buildblock/interpolate_projdata.cxx @@ -9,12 +9,12 @@ it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. - + This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - + See STIR/LICENSE.txt for details */ /*! @@ -24,7 +24,7 @@ \author Charalampos Tsoumpas \author Kris Thielemans - + */ #include "stir/ProjData.h" //#include "stir/display.h" @@ -44,87 +44,69 @@ START_NAMESPACE_STIR -namespace detail_interpolate_projdata -{ - /* Collection of functions to remove interleaving in non-arccorrected data. +namespace detail_interpolate_projdata { +/* Collection of functions to remove interleaving in non-arccorrected data. - It does this by doubling the number of views, and filling in the new - tangential positions by averaging the 4 neighbouring bins. +It does this by doubling the number of views, and filling in the new +tangential positions by averaging the 4 neighbouring bins. - WARNING: most of STIR will get confused by the resulting sinograms, - so only use them here for the interpolate_projdata implementation. - */ - - static shared_ptr - make_non_interleaved_proj_data_info(const ProjDataInfo& proj_data_info) - { +WARNING: most of STIR will get confused by the resulting sinograms, +so only use them here for the interpolate_projdata implementation. +*/ - if (dynamic_cast(&proj_data_info) == NULL) - error("make_non_interleaved_proj_data is only appropriate for non-arccorrected data"); +static shared_ptr +make_non_interleaved_proj_data_info(const ProjDataInfo& proj_data_info) { - shared_ptr new_proj_data_info_sptr( - proj_data_info.clone()); - new_proj_data_info_sptr-> - set_num_views(proj_data_info.get_num_views()*2); - return new_proj_data_info_sptr; - } + if (dynamic_cast(&proj_data_info) == NULL) + error("make_non_interleaved_proj_data is only appropriate for non-arccorrected data"); - // access Sinogram element with wrap-around and boundary conditions - static float sino_element(const Sinogram& sinogram, const int view_num, const int tangential_pos_num) - { - assert(sinogram.get_min_view_num() == 0); - const int num_views = sinogram.get_num_views(); - const int tang_pos_num = (view_num>=num_views? -1: 1)*tangential_pos_num; - if (tang_pos_num < sinogram.get_min_tangential_pos_num() || - tang_pos_num > sinogram.get_max_tangential_pos_num()) - return 0.F; - else - return sinogram[view_num%num_views][tang_pos_num]; - } + shared_ptr new_proj_data_info_sptr(proj_data_info.clone()); + new_proj_data_info_sptr->set_num_views(proj_data_info.get_num_views() * 2); + return new_proj_data_info_sptr; +} - static void - make_non_interleaved_sinogram(Sinogram& out_sinogram, - const Sinogram& in_sinogram) - { - if (is_null_ptr(dynamic_pointer_cast(in_sinogram.get_proj_data_info_sptr()))) - error("make_non_interleaved_proj_data is only appropriate for non-arccorrected data"); - - assert(out_sinogram.get_min_view_num() == 0); - assert(in_sinogram.get_min_view_num() == 0); - assert(out_sinogram.get_num_views() == in_sinogram.get_num_views()*2); - assert(in_sinogram.get_segment_num() == 0); - assert(out_sinogram.get_segment_num() == 0); - - for (int view_num = out_sinogram.get_min_view_num(); - view_num <= out_sinogram.get_max_view_num(); - ++view_num) - { - for (int tangential_pos_num = out_sinogram.get_min_tangential_pos_num()+1; - tangential_pos_num <= out_sinogram.get_max_tangential_pos_num()-1; - ++tangential_pos_num) - { - if ((view_num+tangential_pos_num)%2 == 0) - { - const int in_view_num = - view_num%2==0 ? view_num/2 : (view_num+1)/2; - out_sinogram[view_num][tangential_pos_num] = - sino_element(in_sinogram, in_view_num, tangential_pos_num); - } - else - { - const int next_in_view = view_num/2+1; - const int other_in_view = (view_num+1)/2; - - out_sinogram[view_num][tangential_pos_num] = - (sino_element(in_sinogram, view_num/2, tangential_pos_num) + - sino_element(in_sinogram, next_in_view, tangential_pos_num) + - sino_element(in_sinogram, other_in_view, tangential_pos_num-1) + - sino_element(in_sinogram, other_in_view, tangential_pos_num+1) - )/4; - } - } +// access Sinogram element with wrap-around and boundary conditions +static float +sino_element(const Sinogram& sinogram, const int view_num, const int tangential_pos_num) { + assert(sinogram.get_min_view_num() == 0); + const int num_views = sinogram.get_num_views(); + const int tang_pos_num = (view_num >= num_views ? -1 : 1) * tangential_pos_num; + if (tang_pos_num < sinogram.get_min_tangential_pos_num() || tang_pos_num > sinogram.get_max_tangential_pos_num()) + return 0.F; + else + return sinogram[view_num % num_views][tang_pos_num]; +} + +static void +make_non_interleaved_sinogram(Sinogram& out_sinogram, const Sinogram& in_sinogram) { + if (is_null_ptr(dynamic_pointer_cast(in_sinogram.get_proj_data_info_sptr()))) + error("make_non_interleaved_proj_data is only appropriate for non-arccorrected data"); + + assert(out_sinogram.get_min_view_num() == 0); + assert(in_sinogram.get_min_view_num() == 0); + assert(out_sinogram.get_num_views() == in_sinogram.get_num_views() * 2); + assert(in_sinogram.get_segment_num() == 0); + assert(out_sinogram.get_segment_num() == 0); + + for (int view_num = out_sinogram.get_min_view_num(); view_num <= out_sinogram.get_max_view_num(); ++view_num) { + for (int tangential_pos_num = out_sinogram.get_min_tangential_pos_num() + 1; + tangential_pos_num <= out_sinogram.get_max_tangential_pos_num() - 1; ++tangential_pos_num) { + if ((view_num + tangential_pos_num) % 2 == 0) { + const int in_view_num = view_num % 2 == 0 ? view_num / 2 : (view_num + 1) / 2; + out_sinogram[view_num][tangential_pos_num] = sino_element(in_sinogram, in_view_num, tangential_pos_num); + } else { + const int next_in_view = view_num / 2 + 1; + const int other_in_view = (view_num + 1) / 2; + + out_sinogram[view_num][tangential_pos_num] = (sino_element(in_sinogram, view_num / 2, tangential_pos_num) + + sino_element(in_sinogram, next_in_view, tangential_pos_num) + + sino_element(in_sinogram, other_in_view, tangential_pos_num - 1) + + sino_element(in_sinogram, other_in_view, tangential_pos_num + 1)) / + 4; } + } } +} #if 0 // not needed for now @@ -138,180 +120,134 @@ namespace detail_interpolate_projdata make_non_interleaved_sinogram(out_sinogram, in_sinogram); return out_sinogram; - } + } #endif - static void - make_non_interleaved_segment(SegmentBySinogram& out_segment, - const SegmentBySinogram& in_segment) - { - if (is_null_ptr(dynamic_pointer_cast(in_segment.get_proj_data_info_sptr()))) - error("make_non_interleaved_proj_data is only appropriate for non-arccorrected data"); - - for (int axial_pos_num = out_segment.get_min_axial_pos_num(); - axial_pos_num <= out_segment.get_max_axial_pos_num(); - ++axial_pos_num) - { - Sinogram out_sinogram = out_segment.get_sinogram(axial_pos_num); - make_non_interleaved_sinogram(out_sinogram, - in_segment.get_sinogram(axial_pos_num)); - out_segment.set_sinogram(out_sinogram); - } +static void +make_non_interleaved_segment(SegmentBySinogram& out_segment, const SegmentBySinogram& in_segment) { + if (is_null_ptr(dynamic_pointer_cast(in_segment.get_proj_data_info_sptr()))) + error("make_non_interleaved_proj_data is only appropriate for non-arccorrected data"); + + for (int axial_pos_num = out_segment.get_min_axial_pos_num(); axial_pos_num <= out_segment.get_max_axial_pos_num(); + ++axial_pos_num) { + Sinogram out_sinogram = out_segment.get_sinogram(axial_pos_num); + make_non_interleaved_sinogram(out_sinogram, in_segment.get_sinogram(axial_pos_num)); + out_segment.set_sinogram(out_sinogram); } +} - static SegmentBySinogram - make_non_interleaved_segment(const ProjDataInfo& non_interleaved_proj_data_info, - const SegmentBySinogram& in_segment) - { - SegmentBySinogram out_segment = - non_interleaved_proj_data_info.get_empty_segment_by_sinogram(in_segment.get_segment_num(), - in_segment.get_timing_pos_num()); +static SegmentBySinogram +make_non_interleaved_segment(const ProjDataInfo& non_interleaved_proj_data_info, const SegmentBySinogram& in_segment) { + SegmentBySinogram out_segment = + non_interleaved_proj_data_info.get_empty_segment_by_sinogram(in_segment.get_segment_num(), in_segment.get_timing_pos_num()); - make_non_interleaved_segment(out_segment, in_segment); - return out_segment; - } + make_non_interleaved_segment(out_segment, in_segment); + return out_segment; +} } // end namespace detail_interpolate_projdata - using namespace detail_interpolate_projdata; - -Succeeded -interpolate_projdata(ProjData& proj_data_out, - const ProjData& proj_data_in, const BSpline::BSplineType these_types, - const bool remove_interleaving, - const bool use_view_offset) -{ - BasicCoordinate<3, BSpline::BSplineType> these_types_3; - these_types_3[1]=these_types_3[2]=these_types_3[3]=these_types; - interpolate_projdata(proj_data_out,proj_data_in,these_types_3, remove_interleaving, use_view_offset); + +Succeeded +interpolate_projdata(ProjData& proj_data_out, const ProjData& proj_data_in, const BSpline::BSplineType these_types, + const bool remove_interleaving, const bool use_view_offset) { + BasicCoordinate<3, BSpline::BSplineType> these_types_3; + these_types_3[1] = these_types_3[2] = these_types_3[3] = these_types; + interpolate_projdata(proj_data_out, proj_data_in, these_types_3, remove_interleaving, use_view_offset); return Succeeded::yes; } -Succeeded -interpolate_projdata(ProjData& proj_data_out, - const ProjData& proj_data_in, - const BasicCoordinate<3, BSpline::BSplineType> & these_types, - const bool remove_interleaving, - const bool use_view_offset) -{ +Succeeded +interpolate_projdata(ProjData& proj_data_out, const ProjData& proj_data_in, + const BasicCoordinate<3, BSpline::BSplineType>& these_types, const bool remove_interleaving, + const bool use_view_offset) { if (use_view_offset) warning("interpolate_projdata with use_view_offset is EXPERIMENTAL and NOT TESTED."); - const ProjDataInfo & proj_data_in_info = - *proj_data_in.get_proj_data_info_sptr(); - const ProjDataInfo & proj_data_out_info = - *proj_data_out.get_proj_data_info_sptr(); + const ProjDataInfo& proj_data_in_info = *proj_data_in.get_proj_data_info_sptr(); + const ProjDataInfo& proj_data_out_info = *proj_data_out.get_proj_data_info_sptr(); - if (typeid(proj_data_in_info) != typeid(proj_data_out_info)) - { - error("interpolate_projdata needs both projection data to be of the same type\n" - "(e.g. both arc-corrected or both not arc-corrected)"); - } + if (typeid(proj_data_in_info) != typeid(proj_data_out_info)) { + error("interpolate_projdata needs both projection data to be of the same type\n" + "(e.g. both arc-corrected or both not arc-corrected)"); + } // check for the same ring radius // This is strictly speaking only necessary for non-arccorrected data, but // we leave it in for all cases. if (fabs(proj_data_in_info.get_scanner_ptr()->get_inner_ring_radius() - - proj_data_out_info.get_scanner_ptr()->get_inner_ring_radius()) > 1) - { - error("interpolate_projdata needs both projection to be of a scanner with the same ring radius"); - } - - + proj_data_out_info.get_scanner_ptr()->get_inner_ring_radius()) > 1) { + error("interpolate_projdata needs both projection to be of a scanner with the same ring radius"); + } BSpline::BSplinesRegularGrid<3, float, float> proj_data_interpolator(these_types); - BasicCoordinate<3, double> offset, step ; - + BasicCoordinate<3, double> offset, step; + // find relation between out_index and in_index such that they correspond to the same physical position // out_index * m_zoom + m_offset = in_index - const float in_sampling_m = proj_data_in_info.get_sampling_in_m(Bin(0,0,0,0)); - const float out_sampling_m = proj_data_out_info.get_sampling_in_m(Bin(0,0,0,0)); + const float in_sampling_m = proj_data_in_info.get_sampling_in_m(Bin(0, 0, 0, 0)); + const float out_sampling_m = proj_data_out_info.get_sampling_in_m(Bin(0, 0, 0, 0)); // offset in 'in' index units - offset[1] = - (proj_data_in_info.get_m(Bin(0,0,0,0)) - - proj_data_out_info.get_m(Bin(0,0,0,0))) / in_sampling_m; - step[1]= - out_sampling_m/in_sampling_m; - - const float in_sampling_phi = - (proj_data_in_info.get_phi(Bin(0,1,0,0)) - proj_data_in_info.get_phi(Bin(0,0,0,0))) / - (remove_interleaving ? 2 : 1); - - const float out_sampling_phi = - proj_data_out_info.get_phi(Bin(0,1,0,0)) - proj_data_out_info.get_phi(Bin(0,0,0,0)); - - const float out_view_offset = - use_view_offset - ? proj_data_out_info.get_scanner_ptr()->get_default_intrinsic_tilt() - : 0.F; - const float in_view_offset = - use_view_offset - ? proj_data_in_info.get_scanner_ptr()->get_default_intrinsic_tilt() - : 0.F; - offset[2] = - (proj_data_in_info.get_phi(Bin(0,0,0,0)) + in_view_offset - proj_data_out_info.get_phi(Bin(0,0,0,0)) - out_view_offset) / in_sampling_phi; - step[2] = - out_sampling_phi/in_sampling_phi; - - const float in_sampling_s = proj_data_in_info.get_sampling_in_s(Bin(0,0,0,0)); - const float out_sampling_s = proj_data_out_info.get_sampling_in_s(Bin(0,0,0,0)); - offset[3] = - (proj_data_out_info.get_s(Bin(0,0,0,0)) - - proj_data_in_info.get_s(Bin(0,0,0,0))) / in_sampling_s; - step[3]= - out_sampling_s/in_sampling_s; - + offset[1] = (proj_data_in_info.get_m(Bin(0, 0, 0, 0)) - proj_data_out_info.get_m(Bin(0, 0, 0, 0))) / in_sampling_m; + step[1] = out_sampling_m / in_sampling_m; + + const float in_sampling_phi = + (proj_data_in_info.get_phi(Bin(0, 1, 0, 0)) - proj_data_in_info.get_phi(Bin(0, 0, 0, 0))) / (remove_interleaving ? 2 : 1); + + const float out_sampling_phi = proj_data_out_info.get_phi(Bin(0, 1, 0, 0)) - proj_data_out_info.get_phi(Bin(0, 0, 0, 0)); + + const float out_view_offset = use_view_offset ? proj_data_out_info.get_scanner_ptr()->get_default_intrinsic_tilt() : 0.F; + const float in_view_offset = use_view_offset ? proj_data_in_info.get_scanner_ptr()->get_default_intrinsic_tilt() : 0.F; + offset[2] = (proj_data_in_info.get_phi(Bin(0, 0, 0, 0)) + in_view_offset - proj_data_out_info.get_phi(Bin(0, 0, 0, 0)) - + out_view_offset) / + in_sampling_phi; + step[2] = out_sampling_phi / in_sampling_phi; + + const float in_sampling_s = proj_data_in_info.get_sampling_in_s(Bin(0, 0, 0, 0)); + const float out_sampling_s = proj_data_out_info.get_sampling_in_s(Bin(0, 0, 0, 0)); + offset[3] = (proj_data_out_info.get_s(Bin(0, 0, 0, 0)) - proj_data_in_info.get_s(Bin(0, 0, 0, 0))) / in_sampling_s; + step[3] = out_sampling_s / in_sampling_s; + // initialise interpolator - if (remove_interleaving) - { - shared_ptr non_interleaved_proj_data_info_sptr = - make_non_interleaved_proj_data_info(proj_data_in_info); + if (remove_interleaving) { + shared_ptr non_interleaved_proj_data_info_sptr = make_non_interleaved_proj_data_info(proj_data_in_info); const SegmentBySinogram non_interleaved_segment = - make_non_interleaved_segment(*non_interleaved_proj_data_info_sptr, - proj_data_in.get_segment_by_sinogram(0)); + make_non_interleaved_segment(*non_interleaved_proj_data_info_sptr, proj_data_in.get_segment_by_sinogram(0)); // display(non_interleaved_segment, non_interleaved_segment.find_max(),"non-inter"); - Array<3,float> extended = - extend_segment_in_views(non_interleaved_segment, 2, 2); - for (int z=extended.get_min_index(); z<= extended.get_max_index(); ++z) - { - for (int y=extended[z].get_min_index(); y<= extended[z].get_max_index(); ++y) - { - const int old_min = extended[z][y].get_min_index(); - const int old_max = extended[z][y].get_max_index(); - extended[z][y].grow(old_min-1, old_max+1); - extended[z][y][old_min-1] = extended[z][y][old_min]; - extended[z][y][old_max+1] = extended[z][y][old_max]; - } + Array<3, float> extended = extend_segment_in_views(non_interleaved_segment, 2, 2); + for (int z = extended.get_min_index(); z <= extended.get_max_index(); ++z) { + for (int y = extended[z].get_min_index(); y <= extended[z].get_max_index(); ++y) { + const int old_min = extended[z][y].get_min_index(); + const int old_max = extended[z][y].get_max_index(); + extended[z][y].grow(old_min - 1, old_max + 1); + extended[z][y][old_min - 1] = extended[z][y][old_min]; + extended[z][y][old_max + 1] = extended[z][y][old_max]; } + } proj_data_interpolator.set_coef(extended); - } - else - { - Array<3,float> extended = - extend_segment_in_views(proj_data_in.get_segment_by_sinogram(0), 2, 2); - for (int z=extended.get_min_index(); z<= extended.get_max_index(); ++z) - { - for (int y=extended[z].get_min_index(); y<= extended[z].get_max_index(); ++y) - { - const int old_min = extended[z][y].get_min_index(); - const int old_max = extended[z][y].get_max_index(); - extended[z][y].grow(old_min-1, old_max+1); - extended[z][y][old_min-1] = extended[z][y][old_min]; - extended[z][y][old_max+1] = extended[z][y][old_max]; - } + } else { + Array<3, float> extended = extend_segment_in_views(proj_data_in.get_segment_by_sinogram(0), 2, 2); + for (int z = extended.get_min_index(); z <= extended.get_max_index(); ++z) { + for (int y = extended[z].get_min_index(); y <= extended[z].get_max_index(); ++y) { + const int old_min = extended[z][y].get_min_index(); + const int old_max = extended[z][y].get_max_index(); + extended[z][y].grow(old_min - 1, old_max + 1); + extended[z][y][old_min - 1] = extended[z][y][old_min]; + extended[z][y][old_max + 1] = extended[z][y][old_max]; } + } proj_data_interpolator.set_coef(extended); } - - // now do interpolation - SegmentBySinogram sino_3D_out = proj_data_out.get_empty_segment_by_sinogram(0) ; + + // now do interpolation + SegmentBySinogram sino_3D_out = proj_data_out.get_empty_segment_by_sinogram(0); sample_function_on_regular_grid(sino_3D_out, proj_data_interpolator, offset, step); proj_data_out.set_segment(sino_3D_out); if (proj_data_out.set_segment(sino_3D_out) == Succeeded::no) - return Succeeded::no; + return Succeeded::no; return Succeeded::yes; } diff --git a/src/buildblock/inverse_SSRB.cxx b/src/buildblock/inverse_SSRB.cxx index da06c31a63..c3f59cdffe 100644 --- a/src/buildblock/inverse_SSRB.cxx +++ b/src/buildblock/inverse_SSRB.cxx @@ -8,12 +8,12 @@ it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. - + This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - + See STIR/LICENSE.txt for details */ /*! @@ -23,7 +23,7 @@ \author Charalampos Tsoumpas \author Kris Thielemans - + */ #include "stir/ProjData.h" #include "stir/ProjDataInfo.h" @@ -34,91 +34,66 @@ START_NAMESPACE_STIR -Succeeded -inverse_SSRB(ProjData& proj_data_4D, - const ProjData& proj_data_3D) -{ - const shared_ptr proj_data_3D_info_sptr = - dynamic_pointer_cast - (proj_data_3D.get_proj_data_info_sptr()); - const shared_ptr proj_data_4D_info_sptr = - dynamic_pointer_cast - (proj_data_4D.get_proj_data_info_sptr()); - if ((proj_data_3D_info_sptr->get_min_view_num() != - proj_data_4D_info_sptr->get_min_view_num()) || - (proj_data_3D_info_sptr->get_min_view_num() != - proj_data_4D_info_sptr->get_min_view_num())) - { - warning("inverse_SSRB: incompatible view-information"); - return Succeeded::no; - } - if ((proj_data_3D_info_sptr->get_min_tangential_pos_num() != - proj_data_4D_info_sptr->get_min_tangential_pos_num()) || - (proj_data_3D_info_sptr->get_min_tangential_pos_num() != - proj_data_4D_info_sptr->get_min_tangential_pos_num())) - { - warning("inverse_SSRB: incompatible tangential_pos-information"); - return Succeeded::no; - } +Succeeded +inverse_SSRB(ProjData& proj_data_4D, const ProjData& proj_data_3D) { + const shared_ptr proj_data_3D_info_sptr = + dynamic_pointer_cast(proj_data_3D.get_proj_data_info_sptr()); + const shared_ptr proj_data_4D_info_sptr = + dynamic_pointer_cast(proj_data_4D.get_proj_data_info_sptr()); + if ((proj_data_3D_info_sptr->get_min_view_num() != proj_data_4D_info_sptr->get_min_view_num()) || + (proj_data_3D_info_sptr->get_min_view_num() != proj_data_4D_info_sptr->get_min_view_num())) { + warning("inverse_SSRB: incompatible view-information"); + return Succeeded::no; + } + if ((proj_data_3D_info_sptr->get_min_tangential_pos_num() != proj_data_4D_info_sptr->get_min_tangential_pos_num()) || + (proj_data_3D_info_sptr->get_min_tangential_pos_num() != proj_data_4D_info_sptr->get_min_tangential_pos_num())) { + warning("inverse_SSRB: incompatible tangential_pos-information"); + return Succeeded::no; + } + + // keep sinograms out of the loop to avoid reallocations + // initialise to something because there's no default constructor + Sinogram sino_4D = proj_data_4D.get_empty_sinogram(proj_data_4D.get_min_axial_pos_num(0), 0); + Sinogram sino_3D = proj_data_3D.get_empty_sinogram(proj_data_3D.get_min_axial_pos_num(0), 0); + + for (int out_segment_num = proj_data_4D.get_min_segment_num(); out_segment_num <= proj_data_4D.get_max_segment_num(); + ++out_segment_num) { + for (int out_ax_pos_num = proj_data_4D.get_min_axial_pos_num(out_segment_num); + out_ax_pos_num <= proj_data_4D.get_max_axial_pos_num(out_segment_num); ++out_ax_pos_num) { + sino_4D = proj_data_4D.get_empty_sinogram(out_ax_pos_num, out_segment_num); + const float out_m = proj_data_4D_info_sptr->get_m(Bin(out_segment_num, 0, out_ax_pos_num, 0)); + int num_contributing_sinos = 0; - // keep sinograms out of the loop to avoid reallocations - // initialise to something because there's no default constructor - Sinogram sino_4D = - proj_data_4D. - get_empty_sinogram(proj_data_4D.get_min_axial_pos_num(0) , 0); - Sinogram sino_3D = - proj_data_3D. - get_empty_sinogram(proj_data_3D.get_min_axial_pos_num(0) , 0); - - for (int out_segment_num = proj_data_4D.get_min_segment_num(); - out_segment_num <= proj_data_4D.get_max_segment_num(); - ++out_segment_num) - { - for (int out_ax_pos_num = proj_data_4D.get_min_axial_pos_num(out_segment_num); - out_ax_pos_num <= proj_data_4D.get_max_axial_pos_num(out_segment_num); - ++out_ax_pos_num ) - { - sino_4D = proj_data_4D.get_empty_sinogram(out_ax_pos_num, out_segment_num); - const float out_m = - proj_data_4D_info_sptr-> - get_m(Bin(out_segment_num, 0, out_ax_pos_num, 0)); - int num_contributing_sinos = 0; - - for (int in_ax_pos_num = proj_data_3D.get_min_axial_pos_num(0); - in_ax_pos_num <= proj_data_3D.get_max_axial_pos_num(0); - ++in_ax_pos_num ) - { - sino_3D = proj_data_3D.get_sinogram(in_ax_pos_num,0); - const float in_m = - proj_data_3D_info_sptr->get_m(Bin(0, 0, in_ax_pos_num, 0)); + for (int in_ax_pos_num = proj_data_3D.get_min_axial_pos_num(0); in_ax_pos_num <= proj_data_3D.get_max_axial_pos_num(0); + ++in_ax_pos_num) { + sino_3D = proj_data_3D.get_sinogram(in_ax_pos_num, 0); + const float in_m = proj_data_3D_info_sptr->get_m(Bin(0, 0, in_ax_pos_num, 0)); - if (fabs(out_m - in_m) < 1E-2) - { - ++num_contributing_sinos; - sino_4D += sino_3D; - if (proj_data_4D.set_sinogram(sino_4D) == Succeeded::no) - return Succeeded::no; - break; - } - const float in_m_next = in_ax_pos_num == proj_data_3D.get_max_axial_pos_num(0) ? - -1000000.F : proj_data_3D_info_sptr->get_m(Bin(0, 0, in_ax_pos_num+1, 0)); + if (fabs(out_m - in_m) < 1E-2) { + ++num_contributing_sinos; + sino_4D += sino_3D; + if (proj_data_4D.set_sinogram(sino_4D) == Succeeded::no) + return Succeeded::no; + break; + } + const float in_m_next = in_ax_pos_num == proj_data_3D.get_max_axial_pos_num(0) + ? -1000000.F + : proj_data_3D_info_sptr->get_m(Bin(0, 0, in_ax_pos_num + 1, 0)); - if (fabs(out_m - .5F*(in_m + in_m_next)) < 1E-2) - { - ++num_contributing_sinos; - sino_4D += sino_3D; - sino_4D += proj_data_3D.get_sinogram(in_ax_pos_num+1,0); - sino_4D *= .5F; - if (proj_data_4D.set_sinogram(sino_4D) == Succeeded::no) - return Succeeded::no; - break; - } - } - if (num_contributing_sinos == 0) - warning("inverse_SSRB: no sinogram contributes to segment %d, axial_pos_num %d", - out_segment_num, out_ax_pos_num); - } - } - return Succeeded::yes; + if (fabs(out_m - .5F * (in_m + in_m_next)) < 1E-2) { + ++num_contributing_sinos; + sino_4D += sino_3D; + sino_4D += proj_data_3D.get_sinogram(in_ax_pos_num + 1, 0); + sino_4D *= .5F; + if (proj_data_4D.set_sinogram(sino_4D) == Succeeded::no) + return Succeeded::no; + break; + } + } + if (num_contributing_sinos == 0) + warning("inverse_SSRB: no sinogram contributes to segment %d, axial_pos_num %d", out_segment_num, out_ax_pos_num); + } + } + return Succeeded::yes; } END_NAMESPACE_STIR diff --git a/src/buildblock/line.cxx b/src/buildblock/line.cxx index 337fbb6bd9..28d6c7ceed 100644 --- a/src/buildblock/line.cxx +++ b/src/buildblock/line.cxx @@ -31,230 +31,204 @@ */ #include "stir/line.h" - START_NAMESPACE_STIR -const int LINE_ERROR =-1; -const int LINE_OK = 0; +const int LINE_ERROR = -1; +const int LINE_OK = 0; -string Line::get_keyword() -{ - // keyword stops at either := or an index [] +string +Line::get_keyword() { + // keyword stops at either := or an index [] // TODO should check that = follows : to allow keywords with colons in there - const size_type eok =find_first_of(":[",0); - return substr(0,eok); + const size_type eok = find_first_of(":[", 0); + return substr(0, eok); } -int Line::get_index() -{ - size_type cp,sok,eok; - // we take 0 as a default value for the index - int in=0; - string sin; - // make sure that the index is part of the key (i.e. before :=) - cp=find_first_of(":[",0); - if(cp!=string::npos && operator[](cp) == '[') - { - sok=cp+1; - eok=find_first_of(']',cp); - // check if closing bracket really there - if (eok == string::npos) - { - // TODO do something more graceful - warning("Interfile warning: invalid vectored key in line \n'%s'.\n%s", - this->c_str(), - "Assuming this is not a vectored key."); - return 0; - } - sin=substr(sok,eok-sok); - in=atoi(sin.c_str()); - } - return in; +int +Line::get_index() { + size_type cp, sok, eok; + // we take 0 as a default value for the index + int in = 0; + string sin; + // make sure that the index is part of the key (i.e. before :=) + cp = find_first_of(":[", 0); + if (cp != string::npos && operator[](cp) == '[') { + sok = cp + 1; + eok = find_first_of(']', cp); + // check if closing bracket really there + if (eok == string::npos) { + // TODO do something more graceful + warning("Interfile warning: invalid vectored key in line \n'%s'.\n%s", this->c_str(), + "Assuming this is not a vectored key."); + return 0; + } + sin = substr(sok, eok - sok); + in = atoi(sin.c_str()); + } + return in; } -int Line::get_param(string& s) -{ - size_type sok,eok; //start & end pos. of keyword - size_type cp =0; //current index - - cp=find('=',0); - - if(cp!=string::npos) - { - cp++; - sok=find_first_not_of(' ',cp); - if(sok!=string::npos) - { - cp=length(); - // strip trailing white space - eok=find_last_not_of(" \t",cp); - s=substr(sok,eok-sok+1); - return LINE_OK; - } - } - return LINE_ERROR; +int +Line::get_param(string& s) { + size_type sok, eok; // start & end pos. of keyword + size_type cp = 0; // current index + + cp = find('=', 0); + + if (cp != string::npos) { + cp++; + sok = find_first_not_of(' ', cp); + if (sok != string::npos) { + cp = length(); + // strip trailing white space + eok = find_last_not_of(" \t", cp); + s = substr(sok, eok - sok + 1); + return LINE_OK; + } + } + return LINE_ERROR; } -int Line::get_param(int& i) -{ - string s; - int r; - - r=get_param(s); - if(r==LINE_OK) - i=atoi(s.c_str()); - - return r; -} +int +Line::get_param(int& i) { + string s; + int r; + r = get_param(s); + if (r == LINE_OK) + i = atoi(s.c_str()); -int Line::get_param(unsigned long& i) -{ - string s; - int r; - - r=get_param(s); - if(r==LINE_OK) - // TODO not unsigned now - i=atol(s.c_str()); - - return r; + return r; } +int +Line::get_param(unsigned long& i) { + string s; + int r; -int Line::get_param(double& i) -{ - string s; - int r; + r = get_param(s); + if (r == LINE_OK) + // TODO not unsigned now + i = atol(s.c_str()); - r=get_param(s); - if(r==LINE_OK) - i=atof(s.c_str()); - - return r; + return r; } +int +Line::get_param(double& i) { + string s; + int r; + r = get_param(s); + if (r == LINE_OK) + i = atof(s.c_str()); -int Line::get_param(vector& v) -{ - string s; - size_type cp; - size_type eop; - bool end=false; - - cp=find_first_of('=',0)+1; - // skip white space - cp=find_first_not_of(" \t",cp); - // TODO? this does not check if brackets are balanced - while (!end) - { - cp=find_first_not_of("{},",cp); - - if(cp==string::npos) - { - end=true; - } - else - { - eop=find_first_of(",}",cp); - if(eop==string::npos) - { - end=true; - eop=length(); - } - s=substr(cp,eop-cp); - // TODO use strstream, would allow templates - v.push_back(atoi(s.c_str())); - cp=eop+1; - } - } - return LINE_OK; + return r; } +int +Line::get_param(vector& v) { + string s; + size_type cp; + size_type eop; + bool end = false; + + cp = find_first_of('=', 0) + 1; + // skip white space + cp = find_first_not_of(" \t", cp); + // TODO? this does not check if brackets are balanced + while (!end) { + cp = find_first_not_of("{},", cp); + + if (cp == string::npos) { + end = true; + } else { + eop = find_first_of(",}", cp); + if (eop == string::npos) { + end = true; + eop = length(); + } + s = substr(cp, eop - cp); + // TODO use strstream, would allow templates + v.push_back(atoi(s.c_str())); + cp = eop + 1; + } + } + return LINE_OK; +} -int Line::get_param(vector& v) -{ - string s; - // KT 02/11/98 don't use temporary variable anymore - //int r=LINE_OK; - size_type cp; - size_type eop; - bool end=false; - - cp=find_first_of('=',0)+1; - // skip white space - cp=find_first_not_of(" \t",cp); - // TODO? this does not check if brackets are balanced - while (!end) - { - cp=find_first_not_of("{},",cp); - - if(cp==string::npos) - { - end=true; - } - else - { - eop=find_first_of(",}",cp); - if(eop==string::npos) - { - end=true; - eop=length(); - } - s=substr(cp,eop-cp); - // TODO use strstream, would allow templates - v.push_back(atof(s.c_str())); - cp=eop+1; - } - } - return LINE_OK; +int +Line::get_param(vector& v) { + string s; + // KT 02/11/98 don't use temporary variable anymore + // int r=LINE_OK; + size_type cp; + size_type eop; + bool end = false; + + cp = find_first_of('=', 0) + 1; + // skip white space + cp = find_first_not_of(" \t", cp); + // TODO? this does not check if brackets are balanced + while (!end) { + cp = find_first_not_of("{},", cp); + + if (cp == string::npos) { + end = true; + } else { + eop = find_first_of(",}", cp); + if (eop == string::npos) { + end = true; + eop = length(); + } + s = substr(cp, eop - cp); + // TODO use strstream, would allow templates + v.push_back(atof(s.c_str())); + cp = eop + 1; + } + } + return LINE_OK; } -int Line::get_param(vector& v) -{ - string s; - - size_type cp; - size_type eop; - bool end=false; - - cp=find_first_of('=',0)+1; - // skip white space - cp=find_first_not_of(" \t",cp); - // TODO? this does not check if brackets are balanced - while (!end) - { - cp=find_first_not_of("{},",cp); - cp=find_first_not_of(" \t",cp); - - if(cp==string::npos) - { - end=true; - } - else - { - eop=find_first_of(",}",cp); - if(eop==string::npos) - { - end=true; - eop=length(); - } - // trim ending white space - size_type eop2 = find_last_not_of(" \t",eop); - s=substr(cp,eop2-cp); - - v.push_back(s); - cp=eop+1; - } - } - return LINE_OK; +int +Line::get_param(vector& v) { + string s; + + size_type cp; + size_type eop; + bool end = false; + + cp = find_first_of('=', 0) + 1; + // skip white space + cp = find_first_not_of(" \t", cp); + // TODO? this does not check if brackets are balanced + while (!end) { + cp = find_first_not_of("{},", cp); + cp = find_first_not_of(" \t", cp); + + if (cp == string::npos) { + end = true; + } else { + eop = find_first_of(",}", cp); + if (eop == string::npos) { + end = true; + eop = length(); + } + // trim ending white space + size_type eop2 = find_last_not_of(" \t", eop); + s = substr(cp, eop2 - cp); + + v.push_back(s); + cp = eop + 1; + } + } + return LINE_OK; } -Line& Line::operator=(const char* ch) -{ - string::operator=(ch); - return *this; +Line& +Line::operator=(const char* ch) { + string::operator=(ch); + return *this; } END_NAMESPACE_STIR diff --git a/src/buildblock/linear_regression.cxx b/src/buildblock/linear_regression.cxx index aefebfc8e0..4f9d994192 100644 --- a/src/buildblock/linear_regression.cxx +++ b/src/buildblock/linear_regression.cxx @@ -7,10 +7,10 @@ \brief Implementation for stir::linear_regression() function - \author Kris Thielemans + \author Kris Thielemans \author PARAPET project -*/ +*/ /* Copyright (C) 2000 PARAPET partners Copyright (C) 2000- 2011, Hammersmith Imanet Ltd @@ -35,34 +35,21 @@ START_NAMESPACE_STIR namespace detail { - // see linear_regression.inl for more info - +// see linear_regression.inl for more info template -void linear_regression_compute_fit_from_S(Value& constant, Value& scale, - Value& chi_square, - Value& variance_of_constant, - Value& variance_of_scale, - Value& covariance_of_constant_with_scale, - const double S, - const double Sx, - const double Sy, - const double Syy, - const double Stt, - const double Sty, - const std::size_t data_size, - const bool use_estimated_variance - ) -{ - - - +void +linear_regression_compute_fit_from_S(Value& constant, Value& scale, Value& chi_square, Value& variance_of_constant, + Value& variance_of_scale, Value& covariance_of_constant_with_scale, const double S, + const double Sx, const double Sy, const double Syy, const double Stt, const double Sty, + const std::size_t data_size, const bool use_estimated_variance) { + scale = static_cast(Sty / Stt); constant = static_cast((Sy - Sx * scale) / S); - variance_of_scale = static_cast(1/ Stt); - variance_of_constant = static_cast((1 + square(Sx)/(S*Stt))/S); - covariance_of_constant_with_scale = static_cast(- Sx / (S*Stt)); - + variance_of_scale = static_cast(1 / Stt); + variance_of_constant = static_cast((1 + square(Sx) / (S * Stt)) / S); + covariance_of_constant_with_scale = static_cast(-Sx / (S * Stt)); + // Compute chi_square, i.e. // sum_i weights[i]*(measured_data[i] - (constant+scale*coordinates[i]))^2 // It turns out this can be done using previously calculated constants, @@ -70,59 +57,36 @@ void linear_regression_compute_fit_from_S(Value& constant, Value& scale, // Proving the next identity is a tough algebraic calculation // (performed by KT in Mathematica) - chi_square = static_cast(Syy - square(scale)*Stt - square(Sy)/S); - - if (chi_square<0) - { - warning("linear_regression found negative chi_square %g.\n" - "This is probably just because of rounding errors and indicates " - "a small error compared to the data. I will set it to 0 to avoid " - "problems with sqrt(chi_square).", - chi_square); - chi_square=0; - } - if (use_estimated_variance==true) - { - const Value estimated_variance = - static_cast(chi_square/(data_size - 2)); - variance_of_scale *=estimated_variance; - variance_of_constant *=estimated_variance; - covariance_of_constant_with_scale *=estimated_variance; + chi_square = static_cast(Syy - square(scale) * Stt - square(Sy) / S); + + if (chi_square < 0) { + warning("linear_regression found negative chi_square %g.\n" + "This is probably just because of rounding errors and indicates " + "a small error compared to the data. I will set it to 0 to avoid " + "problems with sqrt(chi_square).", + chi_square); + chi_square = 0; + } + if (use_estimated_variance == true) { + const Value estimated_variance = static_cast(chi_square / (data_size - 2)); + variance_of_scale *= estimated_variance; + variance_of_constant *= estimated_variance; + covariance_of_constant_with_scale *= estimated_variance; } } // instantiations -template void -linear_regression_compute_fit_from_S<>(float& constant, float& scale, - float& chi_square, - float& variance_of_constant, - float& variance_of_scale, - float& covariance_of_constant_with_scale, - const double S, - const double Sx, - const double Sy, - const double Syy, - const double Stt, - const double Sty, - const std::size_t data_size, - const bool use_estimated_variance - ); - -template void -linear_regression_compute_fit_from_S<>(double& constant, double& scale, - double& chi_square, - double& variance_of_constant, - double& variance_of_scale, - double& covariance_of_constant_with_scale, - const double S, - const double Sx, - const double Sy, - const double Syy, - const double Stt, - const double Sty, - const std::size_t data_size, - const bool use_estimated_variance - ); +template void linear_regression_compute_fit_from_S<>(float& constant, float& scale, float& chi_square, + float& variance_of_constant, float& variance_of_scale, + float& covariance_of_constant_with_scale, const double S, const double Sx, + const double Sy, const double Syy, const double Stt, const double Sty, + const std::size_t data_size, const bool use_estimated_variance); + +template void linear_regression_compute_fit_from_S<>(double& constant, double& scale, double& chi_square, + double& variance_of_constant, double& variance_of_scale, + double& covariance_of_constant_with_scale, const double S, const double Sx, + const double Sy, const double Syy, const double Stt, const double Sty, + const std::size_t data_size, const bool use_estimated_variance); } // end namespace detail diff --git a/src/buildblock/num_threads.cxx b/src/buildblock/num_threads.cxx index 12bc2d0dab..e7762512e0 100644 --- a/src/buildblock/num_threads.cxx +++ b/src/buildblock/num_threads.cxx @@ -21,7 +21,7 @@ \brief Implementation of functions related to setting/getting the number of threads - \author Kris Thielemans + \author Kris Thielemans */ #include "stir/num_threads.h" @@ -32,13 +32,13 @@ #include #ifdef STIR_OPENMP -#include +# include #endif START_NAMESPACE_STIR -int get_max_num_threads() -{ +int +get_max_num_threads() { #ifdef STIR_OPENMP return omp_get_max_threads(); #else @@ -46,53 +46,47 @@ int get_max_num_threads() #endif } -void set_num_threads(const int num_threads) -{ +void +set_num_threads(const int num_threads) { static bool already_set_once = false; - if (num_threads==0) - { - if (!already_set_once) - { - set_default_num_threads(); - } + if (num_threads == 0) { + if (!already_set_once) { + set_default_num_threads(); } - else - { + } else { #ifdef STIR_OPENMP - omp_set_num_threads(num_threads); + omp_set_num_threads(num_threads); - if (omp_get_max_threads()==1) - warning("Using OpenMP with number of threads=1 produces parallel overhead. You should compile without OPENMP support"); + if (omp_get_max_threads() == 1) + warning("Using OpenMP with number of threads=1 produces parallel overhead. You should compile without OPENMP support"); #else - if (num_threads!=1) - warning("You have asked for more than 1 thread but this is ignored as STIR was not compiled with OpenMP support."); + if (num_threads != 1) + warning("You have asked for more than 1 thread but this is ignored as STIR was not compiled with OpenMP support."); #endif - } + } already_set_once = true; } -int get_default_num_threads() -{ +int +get_default_num_threads() { #ifdef STIR_OPENMP - int default_num_threads = std::max((int)floor(omp_get_num_procs()*.9), 2); - if (omp_get_num_procs()==1) - default_num_threads=1; + int default_num_threads = std::max((int)floor(omp_get_num_procs() * .9), 2); + if (omp_get_num_procs() == 1) + default_num_threads = 1; - if (getenv("OMP_NUM_THREADS")!=NULL) - { - default_num_threads=atoi(getenv("OMP_NUM_THREADS")); - } + if (getenv("OMP_NUM_THREADS") != NULL) { + default_num_threads = atoi(getenv("OMP_NUM_THREADS")); + } return default_num_threads; #else return 1; #endif } -void set_default_num_threads() -{ +void +set_default_num_threads() { set_num_threads(get_default_num_threads()); } - END_NAMESPACE_STIR diff --git a/src/buildblock/overlap_interpolate.cxx b/src/buildblock/overlap_interpolate.cxx index 349d4ae139..0c7d95d0b1 100644 --- a/src/buildblock/overlap_interpolate.cxx +++ b/src/buildblock/overlap_interpolate.cxx @@ -32,9 +32,9 @@ #include "stir/VectorWithOffset.h" START_NAMESPACE_STIR -/*! +/*! - This is an implementation of 'overlap' interpolation on + This is an implementation of 'overlap' interpolation on arbitrary data types (using templates). This type of interpolation considers the data as the samples of @@ -52,7 +52,7 @@ START_NAMESPACE_STIR range of indices). Note that a similar (but less general) effect to using 'offset' can be achieved by adjusting the min and max indices of the out_data. - + \param assign_rest_with_zeroes If \c false does not set values in \c out_data which do not overlap with \c in_data. @@ -64,7 +64,7 @@ START_NAMESPACE_STIR (The convention is used that the 'bins' are centered around the coordinate value.) - \warning when T involves integral types, there is no rounding + \warning when T involves integral types, there is no rounding but truncation. \par Examples: @@ -85,16 +85,16 @@ START_NAMESPACE_STIR offset = 0 out_data = {a/2, a/2+b+c/2, c/2} indices from -1 to 1 \endverbatim - + \par Implementation details: Because this implementation works for arbitrary (numeric) types T, it is slightly more complicated than would be necessary for (say) floats. - In particular,
    + In particular,
    - we do our best to avoid creating temporary objects of type T
    - we zero values by using multiplication with 0
    (actually we use T::operator*=(0)). This is to allow the case where - T::operator=(int) does not exist (in particular, in our higher + T::operator=(int) does not exist (in particular, in our higher dimensional arrays). @@ -107,136 +107,114 @@ START_NAMESPACE_STIR template void -overlap_interpolate(VectorWithOffset& out_data, - const VectorWithOffset& in_data, - const float zoom, - const float offset, - const bool assign_rest_with_zeroes) -{ - assert(zoom>0); - +overlap_interpolate(VectorWithOffset& out_data, const VectorWithOffset& in_data, const float zoom, const float offset, + const bool assign_rest_with_zeroes) { + assert(zoom > 0); + // First check trivial case - if (zoom==1.F && offset==0.F && - in_data.get_min_index()==out_data.get_min_index() && - in_data.get_max_index()==out_data.get_max_index()) - { + if (zoom == 1.F && offset == 0.F && in_data.get_min_index() == out_data.get_min_index() && + in_data.get_max_index() == out_data.get_max_index()) { out_data = in_data; return; } - - if(zoom>=1) - { - // Shrinking to a smaller bin size - + + if (zoom >= 1) { + // Shrinking to a smaller bin size + // start at the first 'in' bin that overlaps the 'out' data. // compute by checking by comparing its position with // the position of the left edge of the first 'out' bin: // left_edge = (out_data.get_min_index() - .5)/zoom + offset // x1 -.5 <= left_edge < x1+.5 int x2 = out_data.get_min_index(); - int x1 = (int)floor((x2 - .5)/zoom + offset + .5); - + int x1 = (int)floor((x2 - .5) / zoom + offset + .5); + // the next variable holds the difference between the coordinates // of the right edges of the 'in' bin and the 'out' bin, computed // in a coordinate system where the 'out' voxels are unit distance apart - double diff_between_right_edges = - zoom*(x1-offset+.5) - (x2 + .5); - - for(; - x2 <= out_data.get_max_index(); - x2++, diff_between_right_edges--) - { - - if(x1> in_data.get_max_index()) - { - // just fill out_data with 0, - // no need to check/update diff_between_right_edges - if (assign_rest_with_zeroes) - out_data[x2] *= 0; - - continue; + double diff_between_right_edges = zoom * (x1 - offset + .5) - (x2 + .5); + + for (; x2 <= out_data.get_max_index(); x2++, diff_between_right_edges--) { + + if (x1 > in_data.get_max_index()) { + // just fill out_data with 0, + // no need to check/update diff_between_right_edges + if (assign_rest_with_zeroes) + out_data[x2] *= 0; + + continue; } - - assert(diff_between_right_edges<= zoom/*+epsilon*/); - assert(diff_between_right_edges>= -1/*epsilon*/); - - if (diff_between_right_edges >= 0) - { - if(x1 >= in_data.get_min_index()) - { - out_data[x2] = in_data[x1]; + + assert(diff_between_right_edges <= zoom /*+epsilon*/); + assert(diff_between_right_edges >= -1 /*epsilon*/); + + if (diff_between_right_edges >= 0) { + if (x1 >= in_data.get_min_index()) { + out_data[x2] = in_data[x1]; #ifndef STIR_OVERLAP_NORMALISATION - out_data[x2] /= zoom; + out_data[x2] /= zoom; #endif - } - else - { - if (assign_rest_with_zeroes) - out_data[x2] *= 0; - } - } - else - { - /* + } else { + if (assign_rest_with_zeroes) + out_data[x2] *= 0; + } + } else { + /* Set out_data[x2] according to - + T V1; // bin value at x1 - T V2; // bin value at x1+1 + T V2; // bin value at x1+1 out_data[x2] = (V1+diff_between_right_edges*(V1-V2))/zoom; - + The lines below are more complicated because - - testing if x1, x1+1 are inside the range of in_data - - everything is done without creating temporary objects of type T + - testing if x1, x1+1 are inside the range of in_data + - everything is done without creating temporary objects of type T */ - if(x1 >= in_data.get_min_index()) - { - out_data[x2] = in_data[x1]; - out_data[x2] *= static_cast(1/diff_between_right_edges + 1); // note conversion to float to avoid compiler warnings in case that T is float (or a float array) - } - else - { - if (assign_rest_with_zeroes) - out_data[x2] *= 0; - } - if(x1+1 <= in_data.get_max_index() && x1+1>=in_data.get_min_index()) - { - out_data[x2] -= in_data[x1+1]; - } - + if (x1 >= in_data.get_min_index()) { + out_data[x2] = in_data[x1]; + out_data[x2] *= static_cast( + 1 / diff_between_right_edges + + 1); // note conversion to float to avoid compiler warnings in case that T is float (or a float array) + } else { + if (assign_rest_with_zeroes) + out_data[x2] *= 0; + } + if (x1 + 1 <= in_data.get_max_index() && x1 + 1 >= in_data.get_min_index()) { + out_data[x2] -= in_data[x1 + 1]; + } + #ifndef STIR_OVERLAP_NORMALISATION - out_data[x2] *= static_cast(diff_between_right_edges/zoom); + out_data[x2] *= static_cast(diff_between_right_edges / zoom); #else - out_data[x2] *= static_cast(diff_between_right_edges); + out_data[x2] *= static_cast(diff_between_right_edges); #endif - - // advance 'in' bin - x1++; - diff_between_right_edges += zoom; + + // advance 'in' bin + x1++; + diff_between_right_edges += zoom; } - }// End of for x2 - - } - else - { - // case zoom <1 : stretching the bin size + } // End of for x2 + + } else { + // case zoom <1 : stretching the bin size // start 1 before the first 'in' bin that overlaps the 'out' data. // compute by comparing its position with // the position of the left edge of the first 'out' bin: // left_edge = (out_data.get_min_index() - .5)/zoom + offset // x1-.5 <= left_edge < x1+.5 - - const float inverse_zoom = 1/zoom; - + + const float inverse_zoom = 1 / zoom; + // current coordinate in out_data int x2 = out_data.get_min_index(); // current coordinate in in_data - int x1 = (int)floor((x2 - .5)*inverse_zoom + offset + .5); - + int x1 = (int)floor((x2 - .5) * inverse_zoom + offset + .5); + // the next variable holds the difference between the coordinates // of the right edges of the 'in' bin and the 'out' bin, computed // in a coordinate system where the 'in' bins are unit distance apart - double diff_between_right_edges = (x1-offset+.5) - (x2 + .5)*inverse_zoom; - + double diff_between_right_edges = (x1 - offset + .5) - (x2 + .5) * inverse_zoom; + // we will loop over x1 to update out_data[x2] from in_data[x1] // however, we first check if the left edge of the first in_data is // to the left of the left edge of the first out_data. @@ -244,161 +222,102 @@ overlap_interpolate(VectorWithOffset& out_data, // lies outside the 'out' bin. In the loop later, this part of the // 'in' bin will be added again. { - const double diff_between_left_edges = - diff_between_right_edges-1+inverse_zoom; - if (diff_between_left_edges < 0 && - x1 >= in_data.get_min_index() && x1 <= in_data.get_max_index() ) - { - out_data[x2] = in_data[x1]; - out_data[x2] *= static_cast(diff_between_left_edges); - } - else - { - if (assign_rest_with_zeroes) - out_data[x2] *= 0; + const double diff_between_left_edges = diff_between_right_edges - 1 + inverse_zoom; + if (diff_between_left_edges < 0 && x1 >= in_data.get_min_index() && x1 <= in_data.get_max_index()) { + out_data[x2] = in_data[x1]; + out_data[x2] *= static_cast(diff_between_left_edges); + } else { + if (assign_rest_with_zeroes) + out_data[x2] *= 0; } } - for (; - x1 <= in_data.get_max_index(); - x1++, diff_between_right_edges++) - { - assert(diff_between_right_edges<= 1/*+epsilon*/); - assert(diff_between_right_edges>= -inverse_zoom/*-epsilon*/); - - if (diff_between_right_edges <= 0) - { - // 'in' bin fits entirely in the 'out' bin - if (x1 >= in_data.get_min_index()) - out_data[x2] += in_data[x1]; + for (; x1 <= in_data.get_max_index(); x1++, diff_between_right_edges++) { + assert(diff_between_right_edges <= 1 /*+epsilon*/); + assert(diff_between_right_edges >= -inverse_zoom /*-epsilon*/); + + if (diff_between_right_edges <= 0) { + // 'in' bin fits entirely in the 'out' bin + if (x1 >= in_data.get_min_index()) + out_data[x2] += in_data[x1]; + } else { + // dx = fraction of 'in' bin that lies within 'out' bin + const double dx = 1 - diff_between_right_edges; + // update current 'out' bin + if (x1 >= in_data.get_min_index()) { + // out_data[x2] += in_data[x1]*dx; + if (fabs(dx) > 1e-5) { + out_data[x2] /= static_cast(dx); + out_data[x2] += in_data[x1]; + out_data[x2] *= static_cast(dx); + } + } + // next bin + x2++; + diff_between_right_edges -= inverse_zoom; + // update this one with the rest + if (x2 <= out_data.get_max_index()) { + if (x1 >= in_data.get_min_index()) { + // out_data[x2] = in_data[x1]*(1-dx); + out_data[x2] = in_data[x1]; + out_data[x2] *= static_cast((1 - dx)); + } else if (assign_rest_with_zeroes) { + out_data[x2] *= 0; + } + } else { + // x2 goes out of range, so we can just as well stop + break; + } } - else - { - // dx = fraction of 'in' bin that lies within 'out' bin - const double dx= 1- diff_between_right_edges; - // update current 'out' bin - if (x1 >= in_data.get_min_index()) - { - // out_data[x2] += in_data[x1]*dx; - if (fabs(dx) > 1e-5) - { - out_data[x2] /= static_cast(dx); - out_data[x2] += in_data[x1]; - out_data[x2] *= static_cast(dx); - } - } - // next bin - x2++; - diff_between_right_edges -= inverse_zoom; - // update this one with the rest - if (x2<= out_data.get_max_index()) - { - if (x1 >= in_data.get_min_index()) - { - // out_data[x2] = in_data[x1]*(1-dx); - out_data[x2] = in_data[x1]; - out_data[x2] *= static_cast((1-dx)); - } - else if (assign_rest_with_zeroes) - { - out_data[x2] *= 0; - } - } - else - { - // x2 goes out of range, so we can just as well stop - break; - } - } - - }// End of for x1 - - if (assign_rest_with_zeroes) - { + + } // End of for x1 + + if (assign_rest_with_zeroes) { // set rest of out_data to 0 - for (x2++; - x2 <= out_data.get_max_index(); - x2++) - out_data[x2] *= 0; + for (x2++; x2 <= out_data.get_max_index(); x2++) + out_data[x2] *= 0; } #ifdef STIR_OVERLAP_NORMALISATION - for (x2 = out_data.get_min_index(); - x2 <= out_data.get_max_index(); - x2++) - out_data[x2] *= static_cast(zoom); + for (x2 = out_data.get_min_index(); x2 <= out_data.get_max_index(); x2++) + out_data[x2] *= static_cast(zoom); #endif - - - }// End of if(zoom>1) - -} - + } // End of if(zoom>1) +} /************************************************ Instantiations ************************************************/ -template -void -overlap_interpolate<>(VectorWithOffset& out_data, - const VectorWithOffset& in_data, - const float zoom, - const float offset, - const bool assign_rest_with_zeroes); - -template -void -overlap_interpolate<>(VectorWithOffset& out_data, - const VectorWithOffset& in_data, - const float zoom, - const float offset, - const bool assign_rest_with_zeroes); +template void overlap_interpolate<>(VectorWithOffset& out_data, const VectorWithOffset& in_data, const float zoom, + const float offset, const bool assign_rest_with_zeroes); + +template void overlap_interpolate<>(VectorWithOffset& out_data, const VectorWithOffset& in_data, const float zoom, + const float offset, const bool assign_rest_with_zeroes); END_NAMESPACE_STIR - // TODO remove +// TODO remove #if defined(OLDDESIGN) -#include "stir/Tensor2D.h" - -template -void -overlap_interpolate<>(VectorWithOffset >& out_data, - const VectorWithOffset >& in_data, - const float zoom, - const float offset, - const bool assign_rest_with_zeroes); +# include "stir/Tensor2D.h" -template -void -overlap_interpolate<>(VectorWithOffset >& out_data, - const VectorWithOffset >& in_data, - const float zoom, - const float offset, - const bool assign_rest_with_zeroes); +template void overlap_interpolate<>(VectorWithOffset>& out_data, const VectorWithOffset>& in_data, + const float zoom, const float offset, const bool assign_rest_with_zeroes); +template void overlap_interpolate<>(VectorWithOffset>& out_data, const VectorWithOffset>& in_data, + const float zoom, const float offset, const bool assign_rest_with_zeroes); -#else +#else -#include "stir/Array.h" +# include "stir/Array.h" START_NAMESPACE_STIR -template -void -overlap_interpolate<>(VectorWithOffset >& out_data, - const VectorWithOffset >& in_data, - const float zoom, - const float offset, - const bool assign_rest_with_zeroes); - -template -void -overlap_interpolate<>(VectorWithOffset >& out_data, - const VectorWithOffset >& in_data, - const float zoom, - const float offset, - const bool assign_rest_with_zeroes); +template void overlap_interpolate<>(VectorWithOffset>& out_data, const VectorWithOffset>& in_data, + const float zoom, const float offset, const bool assign_rest_with_zeroes); + +template void overlap_interpolate<>(VectorWithOffset>& out_data, const VectorWithOffset>& in_data, + const float zoom, const float offset, const bool assign_rest_with_zeroes); #endif END_NAMESPACE_STIR diff --git a/src/buildblock/recon_array_functions.cxx b/src/buildblock/recon_array_functions.cxx index a966fe795a..51d7eb41c9 100644 --- a/src/buildblock/recon_array_functions.cxx +++ b/src/buildblock/recon_array_functions.cxx @@ -3,18 +3,18 @@ /* Copyright (C) 2000 PARAPET partners Copyright (C) 2000- 2011, Hammersmith Imanet Ltd - This file is part of STIR. - - This file is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - This file is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - + This file is part of STIR. + + This file is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + See STIR/LICENSE.txt for details */ /*! @@ -25,11 +25,11 @@ \author Matthew Jacobson \author Kris Thielemans \author PARAPET project - + */ -//some miscellaneous operators for sinograms and images +// some miscellaneous operators for sinograms and images #include "stir/recon_array_functions.h" #include "stir/min_positive_element.h" @@ -52,156 +52,135 @@ START_NAMESPACE_STIR const float SMALL_NUM = 0.000001F; - // AZ 07/10/99: added -void truncate_rim(Viewgram& viewgram, const int rim_truncation_sino) -{ - const int rs=viewgram.get_min_axial_pos_num(); - const int re=viewgram.get_max_axial_pos_num(); - const int bs=viewgram.get_min_tangential_pos_num(); - const int be=viewgram.get_max_tangential_pos_num(); - - //MJ 12/04/2000 to remove the necessity for grow in things like sum_over_projections() - int upper_truncation_offset=rim_truncation_sino; - if(viewgram.get_num_tangential_poss()%2!=0) upper_truncation_offset--; - - - for(int r=rs;r<=re;r++) - { - for(int b=bs;b& viewgram, const int rim_truncation_sino) { + const int rs = viewgram.get_min_axial_pos_num(); + const int re = viewgram.get_max_axial_pos_num(); + const int bs = viewgram.get_min_tangential_pos_num(); + const int be = viewgram.get_max_tangential_pos_num(); + + // MJ 12/04/2000 to remove the necessity for grow in things like sum_over_projections() + int upper_truncation_offset = rim_truncation_sino; + if (viewgram.get_num_tangential_poss() % 2 != 0) + upper_truncation_offset--; + + for (int r = rs; r <= re; r++) { + for (int b = bs; b < bs + rim_truncation_sino; b++) + viewgram[r][b] = 0; + for (int b = be - upper_truncation_offset; b <= be; b++) + viewgram[r][b] = 0; + } } -void truncate_rim(SegmentByView& seg, const int rim_truncation_sino) -{ - - const int vs=seg.get_min_view_num(); - const int ve=seg.get_max_view_num(); - const int rs=seg.get_min_axial_pos_num(); - const int re=seg.get_max_axial_pos_num(); - const int bs=seg.get_min_tangential_pos_num(); - const int be=seg.get_max_tangential_pos_num(); - - //MJ 25/03/2000 to remove the necessity for grow in things like sum_over_projections() - int upper_truncation_offset=rim_truncation_sino; - if(seg.get_num_tangential_poss()%2!=0) upper_truncation_offset--; - - for(int v=vs;v<=ve;v++) - for(int r=rs;r<=re;r++) - { - for(int b=bs;b& seg, const int rim_truncation_sino) { + + const int vs = seg.get_min_view_num(); + const int ve = seg.get_max_view_num(); + const int rs = seg.get_min_axial_pos_num(); + const int re = seg.get_max_axial_pos_num(); + const int bs = seg.get_min_tangential_pos_num(); + const int be = seg.get_max_tangential_pos_num(); + + // MJ 25/03/2000 to remove the necessity for grow in things like sum_over_projections() + int upper_truncation_offset = rim_truncation_sino; + if (seg.get_num_tangential_poss() % 2 != 0) + upper_truncation_offset--; + + for (int v = vs; v <= ve; v++) + for (int r = rs; r <= re; r++) { + for (int b = bs; b < bs + rim_truncation_sino; b++) + seg[v][r][b] = 0; + for (int b = be - upper_truncation_offset; b <= be; b++) + seg[v][r][b] = 0; } - } - -void truncate_rim(SegmentBySinogram& seg, const int rim_truncation_sino) -{ - - const int vs=seg.get_min_view_num(); - const int ve=seg.get_max_view_num(); - const int rs=seg.get_min_axial_pos_num(); - const int re=seg.get_max_axial_pos_num(); - const int bs=seg.get_min_tangential_pos_num(); - const int be=seg.get_max_tangential_pos_num(); - - //MJ 25/03/2000 to remove the necessity for grow in things like sum_over_projections() - int upper_truncation_offset=rim_truncation_sino; - if(seg.get_num_tangential_poss()%2!=0) upper_truncation_offset--; - - - for(int r=rs;r<=re;r++) - for(int v=vs;v<=ve;v++) - { - for(int b=bs;b& seg, const int rim_truncation_sino) { + + const int vs = seg.get_min_view_num(); + const int ve = seg.get_max_view_num(); + const int rs = seg.get_min_axial_pos_num(); + const int re = seg.get_max_axial_pos_num(); + const int bs = seg.get_min_tangential_pos_num(); + const int be = seg.get_max_tangential_pos_num(); + + // MJ 25/03/2000 to remove the necessity for grow in things like sum_over_projections() + int upper_truncation_offset = rim_truncation_sino; + if (seg.get_num_tangential_poss() % 2 != 0) + upper_truncation_offset--; + + for (int r = rs; r <= re; r++) + for (int v = vs; v <= ve; v++) { + for (int b = bs; b < bs + rim_truncation_sino; b++) + seg[r][v][b] = 0; + for (int b = be - upper_truncation_offset; b <= be; b++) + seg[r][v][b] = 0; } } -//MJ 18/9/98 new -void truncate_rim(DiscretisedDensity<3,float>& input_image, - const int rim_truncation_image, - const bool strictly_less_than_radius) -{ +// MJ 18/9/98 new +void +truncate_rim(DiscretisedDensity<3, float>& input_image, const int rim_truncation_image, const bool strictly_less_than_radius) { // TODO the 'rim_truncate' part of this function does not make a lot of sense in general - DiscretisedDensityOnCartesianGrid<3,float>& input_image_cartesian = - dynamic_cast&>(input_image); + DiscretisedDensityOnCartesianGrid<3, float>& input_image_cartesian = + dynamic_cast&>(input_image); if (!input_image_cartesian.is_regular()) error("truncate_rim called for non-regular grid. Not implemented"); - const int zs=input_image_cartesian.get_min_index(); - const int ys=input_image_cartesian[zs].get_min_index(); - const int xs=input_image_cartesian[zs][ys].get_min_index(); - - const int ze=input_image_cartesian.get_max_index(); - const int ye=input_image_cartesian[zs].get_max_index(); - const int xe=input_image_cartesian[zs][ys].get_max_index(); - + const int zs = input_image_cartesian.get_min_index(); + const int ys = input_image_cartesian[zs].get_min_index(); + const int xs = input_image_cartesian[zs][ys].get_min_index(); + + const int ze = input_image_cartesian.get_max_index(); + const int ye = input_image_cartesian[zs].get_max_index(); + const int xe = input_image_cartesian[zs][ys].get_max_index(); + // TODO check what happens with even-sized images (i.e. where is the centre?) - - //const int zm=(zs+ze)/2; - const int ym=(ys+ye)/2; - const int xm=(xs+xe)/2; - - const float truncated_radius = - static_cast((xe-xs)/2 - rim_truncation_image); - - - if (strictly_less_than_radius) - { - for (int z=zs; z<=ze; z++) - for (int y=ys; y <= ye; y++) - for (int x=xs; x<= xe; x++) - { - if(square(xm-x)+square(ym-y)>=square(truncated_radius)) - input_image[z][y][x]=0; - } - } - else - { - for (int z=zs; z<=ze; z++) - for (int y=ys; y <= ye; y++) - for (int x=xs; x<= xe; x++) - { - if(square(xm-x)+square(ym-y)>square(truncated_radius)) - input_image[z][y][x]=0; - } - } + + // const int zm=(zs+ze)/2; + const int ym = (ys + ye) / 2; + const int xm = (xs + xe) / 2; + + const float truncated_radius = static_cast((xe - xs) / 2 - rim_truncation_image); + + if (strictly_less_than_radius) { + for (int z = zs; z <= ze; z++) + for (int y = ys; y <= ye; y++) + for (int x = xs; x <= xe; x++) { + if (square(xm - x) + square(ym - y) >= square(truncated_radius)) + input_image[z][y][x] = 0; + } + } else { + for (int z = zs; z <= ze; z++) + for (int y = ys; y <= ye; y++) + for (int x = xs; x <= xe; x++) { + if (square(xm - x) + square(ym - y) > square(truncated_radius)) + input_image[z][y][x] = 0; + } + } } +// AZ&KT 04/10/99: added rim_truncation_sino +void +divide_and_truncate(Viewgram& numerator, const Viewgram& denominator, const int rim_truncation_sino, int& count, + int& count2, double* log_likelihood_ptr /* = NULL */) { + + const int rs = numerator.get_min_axial_pos_num(); + const int re = numerator.get_max_axial_pos_num(); + const int bs = numerator.get_min_tangential_pos_num(); + const int be = numerator.get_max_tangential_pos_num(); + const float small_value = max(numerator.find_max() * SMALL_NUM, 0.F); + + double result = 0; // use this for total result for this viewgram, reducing numerical error + for (int r = rs; r <= re; r++) { + double sub_result = 0; // use this for total result for this r, reducing numerical error + for (int b = bs; b <= be; b++) { -// AZ&KT 04/10/99: added rim_truncation_sino -void divide_and_truncate(Viewgram& numerator, - const Viewgram& denominator, - const int rim_truncation_sino, - int& count, int& count2, double* log_likelihood_ptr /* = NULL */) -{ - - const int rs=numerator.get_min_axial_pos_num(); - const int re=numerator.get_max_axial_pos_num(); - const int bs=numerator.get_min_tangential_pos_num(); - const int be=numerator.get_max_tangential_pos_num(); - - - const float small_value= - max(numerator.find_max()*SMALL_NUM, 0.F); - - double result=0; // use this for total result for this viewgram, reducing numerical error - for(int r=rs;r<=re;r++) - { - double sub_result=0; // use this for total result for this r, reducing numerical error - for(int b=bs;b<=be;b++){ - // KT&SM&MJ 21/05/2001 changed truncation strategy // before singularities (non-zero divided by zero) were set to 0 // now they are set to max_quotient @@ -224,138 +203,112 @@ void divide_and_truncate(Viewgram& numerator, }; #else - if(bbe-rim_truncation_sino ) - { - numerator[r][b] = 0; - } - else - { - float& num = numerator[r][b]; - if (num<=small_value) // KT Feb2011 was "num max_quotient*denom) - { - // cancel singularity - count++; - if (log_likelihood_ptr != NULL) - sub_result -= double(num*log(num/max_quotient)); - num = max_quotient; - } - else - { - if (log_likelihood_ptr != NULL) - sub_result -= double(num*log(denom)); - num = num/denom; - } - } - } + if (b < bs + rim_truncation_sino || b > be - rim_truncation_sino) { + numerator[r][b] = 0; + } else { + float& num = numerator[r][b]; + if (num <= small_value) // KT Feb2011 was "num max_quotient * denom) { + // cancel singularity + count++; + if (log_likelihood_ptr != NULL) + sub_result -= double(num * log(num / max_quotient)); + num = max_quotient; + } else { + if (log_likelihood_ptr != NULL) + sub_result -= double(num * log(denom)); + num = num / denom; + } + } + } #endif } - if (log_likelihood_ptr != NULL) + if (log_likelihood_ptr != NULL) result += sub_result; } - if (log_likelihood_ptr != NULL) + if (log_likelihood_ptr != NULL) *log_likelihood_ptr += result; - } - - -void divide_and_truncate(RelatedViewgrams& numerator, const RelatedViewgrams& denominator, - const int rim_truncation_sino, - int& count, int& count2, double* log_likelihood_ptr ) -{ +void +divide_and_truncate(RelatedViewgrams& numerator, const RelatedViewgrams& denominator, const int rim_truncation_sino, + int& count, int& count2, double* log_likelihood_ptr) { assert(numerator.get_num_viewgrams() == denominator.get_num_viewgrams()); assert(*(numerator.get_proj_data_info_sptr()) == (*denominator.get_proj_data_info_sptr())); RelatedViewgrams::iterator numerator_iter = numerator.begin(); RelatedViewgrams::const_iterator denominator_iter = denominator.begin(); - while(numerator_iter!=numerator.end()) - { - divide_and_truncate(*numerator_iter, - *denominator_iter, - rim_truncation_sino, - count, count2, log_likelihood_ptr); - ++numerator_iter; - ++denominator_iter; + while (numerator_iter != numerator.end()) { + divide_and_truncate(*numerator_iter, *denominator_iter, rim_truncation_sino, count, count2, log_likelihood_ptr); + ++numerator_iter; + ++denominator_iter; } } +void +divide_array(SegmentByView& numerator, const SegmentByView& denominator) { + + const int vs = numerator.get_min_view_num(); + const int ve = numerator.get_max_view_num(); + const int rs = numerator.get_min_axial_pos_num(); + const int re = numerator.get_max_axial_pos_num(); + const int bs = numerator.get_min_tangential_pos_num(); + const int be = numerator.get_max_tangential_pos_num(); -void divide_array(SegmentByView& numerator,const SegmentByView& denominator) -{ - - const int vs=numerator.get_min_view_num(); - const int ve=numerator.get_max_view_num(); - const int rs=numerator.get_min_axial_pos_num(); - const int re=numerator.get_max_axial_pos_num(); - const int bs=numerator.get_min_tangential_pos_num(); - const int be=numerator.get_max_tangential_pos_num(); - - for(int v=vs;v<=ve;v++) - for(int r=rs;r<=re;r++) - for(int b=bs;b<=be;b++) - { - - if (denominator[v][r][b] != 0.0) - numerator[v][r][b]=numerator[v][r][b]/denominator[v][r][b]; - else - numerator[v][r][b]=0.0; - - } - + for (int v = vs; v <= ve; v++) + for (int r = rs; r <= re; r++) + for (int b = bs; b <= be; b++) { + + if (denominator[v][r][b] != 0.0) + numerator[v][r][b] = numerator[v][r][b] / denominator[v][r][b]; + else + numerator[v][r][b] = 0.0; + } } -void divide_array(DiscretisedDensity<3,float>& numerator, const DiscretisedDensity<3,float>& denominator) -{ +void +divide_array(DiscretisedDensity<3, float>& numerator, const DiscretisedDensity<3, float>& denominator) { assert(numerator.get_index_range() == denominator.get_index_range()); - float small_value= numerator.find_max()*SMALL_NUM; - small_value=(small_value>0.0F)?small_value:0.0F; + float small_value = numerator.find_max() * SMALL_NUM; + small_value = (small_value > 0.0F) ? small_value : 0.0F; // TODO rewrite in terms of 'full' iterator - - for (int z=numerator.get_min_index(); z<=numerator.get_max_index(); z++) - for (int y=numerator[z].get_min_index(); y<=numerator[z].get_max_index(); y++) - for (int x=numerator[z][y].get_min_index(); x<=numerator[z][y].get_max_index(); x++) - { - - if(fabs(denominator[z][y][x])<=small_value && fabs(numerator[z][y][x])<=small_value) - { - numerator[z][y][x]=0; - } - else - numerator[z][y][x]/=denominator[z][y][x]; + + for (int z = numerator.get_min_index(); z <= numerator.get_max_index(); z++) + for (int y = numerator[z].get_min_index(); y <= numerator[z].get_max_index(); y++) + for (int x = numerator[z][y].get_min_index(); x <= numerator[z][y].get_max_index(); x++) { + + if (fabs(denominator[z][y][x]) <= small_value && fabs(numerator[z][y][x]) <= small_value) { + numerator[z][y][x] = 0; + } else + numerator[z][y][x] /= denominator[z][y][x]; } - } // MJ 03/01/2000 for loglikelihood computation // KT 21/05/2001 make sure it returns same result as divide_and_truncate above -void accumulate_loglikelihood(Viewgram& projection_data, - const Viewgram& estimated_projections, - const int rim_truncation_sino, - double* accum) -{ - - const int rs=projection_data.get_min_axial_pos_num(); - const int re=projection_data.get_max_axial_pos_num(); - const int bs=projection_data.get_min_tangential_pos_num(); - const int be=projection_data.get_max_tangential_pos_num(); +void +accumulate_loglikelihood(Viewgram& projection_data, const Viewgram& estimated_projections, + const int rim_truncation_sino, double* accum) { + + const int rs = projection_data.get_min_axial_pos_num(); + const int re = projection_data.get_max_axial_pos_num(); + const int bs = projection_data.get_min_tangential_pos_num(); + const int be = projection_data.get_max_tangential_pos_num(); /* note for implementation: First compute result for this viewgram in a local variable, @@ -366,85 +319,66 @@ void accumulate_loglikelihood(Viewgram& projection_data, accum would be no longer change because of the finite precision. */ double result = 0; - const float small_value= - max(projection_data.find_max()*SMALL_NUM, 0.F); + const float small_value = max(projection_data.find_max() * SMALL_NUM, 0.F); const float max_quotient = 10000.F; - for(int r=rs;r<=re;r++) - { - double sub_result=0; // use this for total result for this r, reducing numerical error - for(int b=bs;b<=be;b++) - if(!( - bbe-rim_truncation_sino )) - { - // if (estimated_projections[r][b] == 0) - // std::cerr << "Zero at " << r << ", " << b <<'\n'; - const float new_estimate = - max(estimated_projections[r][b], - projection_data[r][b]/max_quotient); - if (projection_data[r][b]<=small_value) - sub_result += - double(new_estimate); - else - sub_result += projection_data[r][b]*log(double(new_estimate)) - double(new_estimate); - } + for (int r = rs; r <= re; r++) { + double sub_result = 0; // use this for total result for this r, reducing numerical error + for (int b = bs; b <= be; b++) + if (!(b < bs + rim_truncation_sino || b > be - rim_truncation_sino)) { + // if (estimated_projections[r][b] == 0) + // std::cerr << "Zero at " << r << ", " << b <<'\n'; + const float new_estimate = max(estimated_projections[r][b], projection_data[r][b] / max_quotient); + if (projection_data[r][b] <= small_value) + sub_result += -double(new_estimate); + else + sub_result += projection_data[r][b] * log(double(new_estimate)) - double(new_estimate); + } result += sub_result; } *accum += result; } - - -void multiply_and_add(DiscretisedDensity<3,float> &image_res, const DiscretisedDensity<3,float> &image_scaled, float scalar) -{ +void +multiply_and_add(DiscretisedDensity<3, float>& image_res, const DiscretisedDensity<3, float>& image_scaled, float scalar) { assert(image_res.get_index_range() == image_scaled.get_index_range()); // TODO rewrite in terms of 'full' iterator - - for (int z=image_res.get_min_index(); z<=image_res.get_max_index(); z++) - for (int y=image_res[z].get_min_index(); y<=image_res[z].get_max_index(); y++) - for (int x=image_res[z][y].get_min_index(); x<=image_res[z][y].get_max_index(); x++) - { - image_res[z][y][x] += image_scaled[z][y][x] *scalar; - } + for (int z = image_res.get_min_index(); z <= image_res.get_max_index(); z++) + for (int y = image_res[z].get_min_index(); y <= image_res[z].get_max_index(); y++) + for (int x = image_res[z][y].get_min_index(); x <= image_res[z][y].get_max_index(); x++) { + image_res[z][y][x] += image_scaled[z][y][x] * scalar; + } } +// to be used with in_place_function +float +neg_trunc(float x) { - -//to be used with in_place_function -float neg_trunc(float x) -{ - - return (x<0.0F)?0.0F:x; - + return (x < 0.0F) ? 0.0F : x; } - - -void truncate_end_planes(DiscretisedDensity<3,float> &input_image, int input_num_planes) -{ +void +truncate_end_planes(DiscretisedDensity<3, float>& input_image, int input_num_planes) { // TODO this function does not make a lot of sense in general #ifndef NDEBUG // this will throw an exception when the cast is invalid - dynamic_cast&>(input_image); + dynamic_cast&>(input_image); #endif - const int zs=input_image.get_min_index(); - const int ze=input_image.get_max_index(); - - int upper_limit=(input_image.get_length() % 2 == 1)?input_image.get_length()/2+1:input_image.get_length()/2; + const int zs = input_image.get_min_index(); + const int ze = input_image.get_max_index(); - int num_planes=input_num_planes<=upper_limit?input_num_planes:upper_limit; - - for (int j=0;j scale_factors) -{ - const ProjDataInfo &proj_data_info = - dynamic_cast - (*scaled_scatter_proj_data.get_proj_data_info_sptr()); +Succeeded +scale_sinograms(ProjData& scaled_scatter_proj_data, const ProjData& scatter_proj_data, const Array<2, float> scale_factors) { + const ProjDataInfo& proj_data_info = dynamic_cast(*scaled_scatter_proj_data.get_proj_data_info_sptr()); Bin bin; - - for (bin.segment_num()=proj_data_info.get_min_segment_num(); - bin.segment_num()<=proj_data_info.get_max_segment_num(); - ++bin.segment_num()) - for (bin.axial_pos_num()= - proj_data_info.get_min_axial_pos_num(bin.segment_num()); - bin.axial_pos_num()<=proj_data_info.get_max_axial_pos_num(bin.segment_num()); - ++bin.axial_pos_num()) - { - Sinogram scatter_sinogram = scatter_proj_data.get_sinogram( - bin.axial_pos_num(),bin.segment_num(),0); - Sinogram scaled_sinogram = - scatter_sinogram; - scaled_sinogram*=scale_factors[bin.segment_num()][bin.axial_pos_num()]; - - if (scaled_scatter_proj_data.set_sinogram(scaled_sinogram) == Succeeded::no) - return Succeeded::no; - } + + for (bin.segment_num() = proj_data_info.get_min_segment_num(); bin.segment_num() <= proj_data_info.get_max_segment_num(); + ++bin.segment_num()) + for (bin.axial_pos_num() = proj_data_info.get_min_axial_pos_num(bin.segment_num()); + bin.axial_pos_num() <= proj_data_info.get_max_axial_pos_num(bin.segment_num()); ++bin.axial_pos_num()) { + Sinogram scatter_sinogram = scatter_proj_data.get_sinogram(bin.axial_pos_num(), bin.segment_num(), 0); + Sinogram scaled_sinogram = scatter_sinogram; + scaled_sinogram *= scale_factors[bin.segment_num()][bin.axial_pos_num()]; + + if (scaled_scatter_proj_data.set_sinogram(scaled_sinogram) == Succeeded::no) + return Succeeded::no; + } return Succeeded::yes; } +Array<2, float> +get_scale_factors_per_sinogram(const ProjData& numerator_proj_data, const ProjData& denominator_proj_data, + const ProjData& weights_proj_data) { -Array<2,float> -get_scale_factors_per_sinogram(const ProjData& numerator_proj_data, - const ProjData& denominator_proj_data, - const ProjData& weights_proj_data) -{ - - const ProjDataInfo &proj_data_info = - dynamic_cast - (*weights_proj_data.get_proj_data_info_sptr()); + const ProjDataInfo& proj_data_info = dynamic_cast(*weights_proj_data.get_proj_data_info_sptr()); Bin bin; // scale factor to use when the denominator is zero const float default_scale = 1.F; - IndexRange2D sinogram_range(proj_data_info.get_min_segment_num(),proj_data_info.get_max_segment_num(),0,0); - for (int segment_num=proj_data_info.get_min_segment_num(); - segment_num<=proj_data_info.get_max_segment_num(); - ++segment_num) - { - sinogram_range[segment_num].resize( - proj_data_info.get_min_axial_pos_num(segment_num), - proj_data_info.get_max_axial_pos_num(segment_num) ); - } - Array<2,float> total_in_denominator(sinogram_range), - total_in_numerator(sinogram_range); - Array<2,float> scale_factors(sinogram_range); - for (bin.segment_num()=proj_data_info.get_min_segment_num(); - bin.segment_num()<=proj_data_info.get_max_segment_num(); - ++bin.segment_num()) - for (bin.axial_pos_num()= - proj_data_info.get_min_axial_pos_num(bin.segment_num()); - bin.axial_pos_num()<=proj_data_info.get_max_axial_pos_num(bin.segment_num()); - ++bin.axial_pos_num()) - { - const Sinogram weights = - weights_proj_data.get_sinogram(bin.axial_pos_num(),bin.segment_num()); - const Sinogram denominator_sinogram = - denominator_proj_data.get_sinogram(bin.axial_pos_num(),bin.segment_num()); - const Array<2,float> weighted_denominator_sinogram = denominator_sinogram * weights; - const Array<2,float> weighted_numerator_sinogram = - numerator_proj_data.get_sinogram(bin.axial_pos_num(),bin.segment_num()) * weights; - total_in_denominator[bin.segment_num()][bin.axial_pos_num()] = weighted_denominator_sinogram.sum(); - total_in_numerator[bin.segment_num()][bin.axial_pos_num()] = weighted_numerator_sinogram.sum(); - - if (denominator_sinogram.sum()==0.f) - { - scale_factors[bin.segment_num()][bin.axial_pos_num()] = default_scale; - } - else - { - if (total_in_denominator[bin.segment_num()][bin.axial_pos_num()]<= - denominator_sinogram.sum()/ - (proj_data_info.get_num_views() * proj_data_info.get_num_tangential_poss()) * .001f - ) - { - warning("Problem at segment %d, axial pos %d in finding sinogram scaling factor.\n" - "Weighted data in denominator %g is very small compared to total in sinogram %g.\n" - "Adjust weights?.\n" - "I will use scale factor %g", - bin.segment_num(),bin.axial_pos_num(), - total_in_denominator[bin.segment_num()][bin.axial_pos_num()], - denominator_sinogram.sum(), - default_scale - ); - scale_factors[bin.segment_num()][bin.axial_pos_num()] = default_scale; - } - else - { - scale_factors[bin.segment_num()][bin.axial_pos_num()] = - total_in_numerator[bin.segment_num()][bin.axial_pos_num()]/ - total_in_denominator[bin.segment_num()][bin.axial_pos_num()]; - } - } + IndexRange2D sinogram_range(proj_data_info.get_min_segment_num(), proj_data_info.get_max_segment_num(), 0, 0); + for (int segment_num = proj_data_info.get_min_segment_num(); segment_num <= proj_data_info.get_max_segment_num(); + ++segment_num) { + sinogram_range[segment_num].resize(proj_data_info.get_min_axial_pos_num(segment_num), + proj_data_info.get_max_axial_pos_num(segment_num)); + } + Array<2, float> total_in_denominator(sinogram_range), total_in_numerator(sinogram_range); + Array<2, float> scale_factors(sinogram_range); + for (bin.segment_num() = proj_data_info.get_min_segment_num(); bin.segment_num() <= proj_data_info.get_max_segment_num(); + ++bin.segment_num()) + for (bin.axial_pos_num() = proj_data_info.get_min_axial_pos_num(bin.segment_num()); + bin.axial_pos_num() <= proj_data_info.get_max_axial_pos_num(bin.segment_num()); ++bin.axial_pos_num()) { + const Sinogram weights = weights_proj_data.get_sinogram(bin.axial_pos_num(), bin.segment_num()); + const Sinogram denominator_sinogram = denominator_proj_data.get_sinogram(bin.axial_pos_num(), bin.segment_num()); + const Array<2, float> weighted_denominator_sinogram = denominator_sinogram * weights; + const Array<2, float> weighted_numerator_sinogram = + numerator_proj_data.get_sinogram(bin.axial_pos_num(), bin.segment_num()) * weights; + total_in_denominator[bin.segment_num()][bin.axial_pos_num()] = weighted_denominator_sinogram.sum(); + total_in_numerator[bin.segment_num()][bin.axial_pos_num()] = weighted_numerator_sinogram.sum(); + + if (denominator_sinogram.sum() == 0.f) { + scale_factors[bin.segment_num()][bin.axial_pos_num()] = default_scale; + } else { + if (total_in_denominator[bin.segment_num()][bin.axial_pos_num()] <= + denominator_sinogram.sum() / (proj_data_info.get_num_views() * proj_data_info.get_num_tangential_poss()) * .001f) { + warning("Problem at segment %d, axial pos %d in finding sinogram scaling factor.\n" + "Weighted data in denominator %g is very small compared to total in sinogram %g.\n" + "Adjust weights?.\n" + "I will use scale factor %g", + bin.segment_num(), bin.axial_pos_num(), total_in_denominator[bin.segment_num()][bin.axial_pos_num()], + denominator_sinogram.sum(), default_scale); + scale_factors[bin.segment_num()][bin.axial_pos_num()] = default_scale; + } else { + scale_factors[bin.segment_num()][bin.axial_pos_num()] = total_in_numerator[bin.segment_num()][bin.axial_pos_num()] / + total_in_denominator[bin.segment_num()][bin.axial_pos_num()]; + } } - - return scale_factors; + } + + return scale_factors; } END_NAMESPACE_STIR - diff --git a/src/buildblock/utilities.cxx b/src/buildblock/utilities.cxx index 20420eb830..99fe921554 100644 --- a/src/buildblock/utilities.cxx +++ b/src/buildblock/utilities.cxx @@ -1,6 +1,6 @@ /*! - \file - + \file + \brief non-inline implementations for utility.h \author Kris Thielemans @@ -46,163 +46,140 @@ using std::string; START_NAMESPACE_STIR -bool -ask (const string& str, bool default_value) -{ +bool +ask(const string& str, bool default_value) { string input; - - while (true) - { - cerr << "\n" << str - << " [Y/N D:" - << (default_value ? 'Y' : 'N') - << "]: "; - std::getline(std::cin, input); - if (input.size()==0) - return default_value; - const char answer = input[0]; - switch (answer) - { - case 'N': - case 'n': - return false; - case 'Y': - case 'y': - return true; - default: - cerr << "\nPlease answer Y or N\n"; - } + + while (true) { + cerr << "\n" << str << " [Y/N D:" << (default_value ? 'Y' : 'N') << "]: "; + std::getline(std::cin, input); + if (input.size() == 0) + return default_value; + const char answer = input[0]; + switch (answer) { + case 'N': + case 'n': + return false; + case 'Y': + case 'y': + return true; + default: + cerr << "\nPlease answer Y or N\n"; + } } } - -string ask_string (const string& str, const string& default_value) -{ +string +ask_string(const string& str, const string& default_value) { string input; - - cerr << "\n" << str - << "\n[default_value : \"" - << default_value - << "\"]: \n"; + + cerr << "\n" << str << "\n[default_value : \"" << default_value << "\"]: \n"; std::getline(std::cin, input); - if (input.size()==0) + if (input.size() == 0) return default_value; else return input; } -FILE*& open_read_binary(FILE*& fptr, - const string& name) -{ - fptr = fopen(name.c_str(), "rb"); - if (ferror(fptr)) - { error("Error opening file %s\n", name.c_str()); } +FILE*& +open_read_binary(FILE*& fptr, const string& name) { + fptr = fopen(name.c_str(), "rb"); + if (ferror(fptr)) { + error("Error opening file %s\n", name.c_str()); + } return fptr; } -FILE*& open_write_binary(FILE*& fptr, - const string& name) -{ - fptr = fopen(name.c_str(), "wb"); - if (ferror(fptr)) - { error("Error opening file %s\n", name.c_str()); } +FILE*& +open_write_binary(FILE*& fptr, const string& name) { + fptr = fopen(name.c_str(), "wb"); + if (ferror(fptr)) { + error("Error opening file %s\n", name.c_str()); + } return fptr; } -void close_file(FILE*& fptr) -{ +void +close_file(FILE*& fptr) { fclose(fptr); - fptr=0; + fptr = 0; } -const char * -find_filename(const char * const filename_with_directory) -{ - const char * name; +const char* +find_filename(const char* const filename_with_directory) { + const char* name; #if defined(__OS_VAX__) - name = strrchr(filename_with_directory,']'); - if (name==NULL) - name = strrchr(filename_with_directory,':'); + name = strrchr(filename_with_directory, ']'); + if (name == NULL) + name = strrchr(filename_with_directory, ':'); #elif defined(__OS_WIN__) - name = strrchr(filename_with_directory,'\\'); - if (name==NULL) - name = strrchr(filename_with_directory,'/'); - if (name==NULL) - name = strrchr(filename_with_directory,':'); + name = strrchr(filename_with_directory, '\\'); + if (name == NULL) + name = strrchr(filename_with_directory, '/'); + if (name == NULL) + name = strrchr(filename_with_directory, ':'); #elif defined(__OS_MAC__) - name = strrchr(filename_with_directory,':'); + name = strrchr(filename_with_directory, ':'); #else // defined(__OS_UNIX__) - name = strrchr(filename_with_directory,'/'); -#endif - if (name!=NULL) - // KT 10/01/2000 name++ changed to name+1 - return name+1; - else - return filename_with_directory; + name = strrchr(filename_with_directory, '/'); +#endif + if (name != NULL) + // KT 10/01/2000 name++ changed to name+1 + return name + 1; + else + return filename_with_directory; } string::size_type -find_pos_of_filename(const string& filename_with_directory) -{ +find_pos_of_filename(const string& filename_with_directory) { string::size_type pos; #if defined(__OS_VAX__) - pos = filename_with_directory.find_last_of( ']'); - if (pos==string::npos) - pos = filename_with_directory.find_last_of( ':'); + pos = filename_with_directory.find_last_of(']'); + if (pos == string::npos) + pos = filename_with_directory.find_last_of(':'); #elif defined(__OS_WIN__) - pos = filename_with_directory.find_last_of( '\\'); - if (pos==string::npos) - pos = filename_with_directory.find_last_of( '/'); - if (pos==string::npos) - pos = filename_with_directory.find_last_of( ':'); + pos = filename_with_directory.find_last_of('\\'); + if (pos == string::npos) + pos = filename_with_directory.find_last_of('/'); + if (pos == string::npos) + pos = filename_with_directory.find_last_of(':'); #elif defined(__OS_MAC__) - pos = filename_with_directory.find_last_of( ':'); + pos = filename_with_directory.find_last_of(':'); #else // defined(__OS_UNIX__) - pos = filename_with_directory.find_last_of( '/'); -#endif + pos = filename_with_directory.find_last_of('/'); +#endif if (pos != string::npos) - return pos+1; + return pos + 1; else return 0; } - string -get_filename(const string& filename_with_directory) -{ - return - filename_with_directory.substr(find_pos_of_filename(filename_with_directory)); +get_filename(const string& filename_with_directory) { + return filename_with_directory.substr(find_pos_of_filename(filename_with_directory)); } -char * -get_directory_name(char *directory_name, - const char * const filename_with_directory) -{ - size_t num_chars_in_directory_name = - find_filename(filename_with_directory) - filename_with_directory; +char* +get_directory_name(char* directory_name, const char* const filename_with_directory) { + size_t num_chars_in_directory_name = find_filename(filename_with_directory) - filename_with_directory; strncpy(directory_name, filename_with_directory, num_chars_in_directory_name); directory_name[num_chars_in_directory_name] = '\0'; return directory_name; } string -get_directory_name(const string& filename_with_directory) -{ - string dir_name = - filename_with_directory.substr(0, find_pos_of_filename(filename_with_directory)); +get_directory_name(const string& filename_with_directory) { + string dir_name = filename_with_directory.substr(0, find_pos_of_filename(filename_with_directory)); if (dir_name.empty()) dir_name = "."; return dir_name; } string::size_type -find_pos_of_extension(const string& file_in_directory_name) -{ - string::size_type pos_of_dot = - file_in_directory_name.find_last_of('.'); - string::size_type pos_of_filename = - find_pos_of_filename(file_in_directory_name); +find_pos_of_extension(const string& file_in_directory_name) { + string::size_type pos_of_dot = file_in_directory_name.find_last_of('.'); + string::size_type pos_of_filename = find_pos_of_filename(file_in_directory_name); if (pos_of_dot >= pos_of_filename) return pos_of_dot; else @@ -222,19 +199,14 @@ char *add_extension(char *file_in_directory_name, } #endif -string& -add_extension(string& file_in_directory_name, - const string& extension) -{ - string::size_type pos = - find_pos_of_extension(file_in_directory_name); +string& +add_extension(string& file_in_directory_name, const string& extension) { + string::size_type pos = find_pos_of_extension(file_in_directory_name); if (pos == string::npos) file_in_directory_name += extension; return file_in_directory_name; } - - #if 0 // terribly dangerous for memory overrun. // will only work if new extension is shorter than old @@ -253,43 +225,35 @@ char *replace_extension(char *file_in_directory_name, } #endif -string& -replace_extension(string& file_in_directory_name, - const string& extension) -{ - string::size_type pos = - find_pos_of_extension(file_in_directory_name); +string& +replace_extension(string& file_in_directory_name, const string& extension) { + string::size_type pos = find_pos_of_extension(file_in_directory_name); if (pos != string::npos) file_in_directory_name.erase(pos); file_in_directory_name += extension; - + return file_in_directory_name; } bool -is_absolute_pathname(const char * const filename_with_directory) -{ +is_absolute_pathname(const char* const filename_with_directory) { #if defined(__OS_VAX__) // relative names either contain no '[', or have '[.' - const char * const ptr = strchr(filename_with_directory,'['); - if (ptr==NULL) + const char* const ptr = strchr(filename_with_directory, '['); + if (ptr == NULL) return false; else - return *(ptr+1) != '.'; + return *(ptr + 1) != '.'; #elif defined(__OS_WIN__) // relative names do not start with '\' or '?:\' - if (filename_with_directory[0] == '\\' || - filename_with_directory[0] == '/') + if (filename_with_directory[0] == '\\' || filename_with_directory[0] == '/') return true; else - return (strlen(filename_with_directory)>3 && - filename_with_directory[1] == ':' && - (filename_with_directory[2] == '\\' || - filename_with_directory[2] == '/') - ); + return (strlen(filename_with_directory) > 3 && filename_with_directory[1] == ':' && + (filename_with_directory[2] == '\\' || filename_with_directory[2] == '/')); #elif defined(__OS_MAC__) // relative names either have no ':' or do not start with ':' - const char * const ptr = strchr(filename_with_directory,':'); + const char* const ptr = strchr(filename_with_directory, ':'); if (ptr == NULL) return false; else @@ -297,48 +261,37 @@ is_absolute_pathname(const char * const filename_with_directory) #else // defined(__OS_UNIX__) // absolute names start with '/' return filename_with_directory[0] == '/'; -#endif +#endif } bool -is_absolute_pathname(const string& filename_with_directory) -{ - return - is_absolute_pathname(filename_with_directory.c_str()); +is_absolute_pathname(const string& filename_with_directory) { + return is_absolute_pathname(filename_with_directory.c_str()); } -// Warning: this function assumes that filename_with_directory +// Warning: this function assumes that filename_with_directory // points to sufficient allocated space to contain the new string -char * -prepend_directory_name(char * filename_with_directory, - const char * const directory_name) -{ - if (is_absolute_pathname(filename_with_directory) || - directory_name == 0 || - strlen(directory_name) == 0) +char* +prepend_directory_name(char* filename_with_directory, const char* const directory_name) { + if (is_absolute_pathname(filename_with_directory) || directory_name == 0 || strlen(directory_name) == 0) return filename_with_directory; - char * new_name = - new char[strlen(filename_with_directory) + strlen(directory_name) + 4]; + char* new_name = new char[strlen(filename_with_directory) + strlen(directory_name) + 4]; strcpy(new_name, directory_name); - char * end_of_new_name = new_name + strlen(directory_name)-1; - + char* end_of_new_name = new_name + strlen(directory_name) - 1; #if defined(__OS_VAX__) // relative names either contain no '[', or have '[.' - if (filename_with_directory[0] != '[' || - *end_of_new_name != ']') + if (filename_with_directory[0] != '[' || *end_of_new_name != ']') strcat(new_name, filename_with_directory); - else - { + else { // peel of the ][ pair *end_of_new_name = '\0'; - strcat(new_name, filename_with_directory+1); + strcat(new_name, filename_with_directory + 1); } #elif defined(__OS_WIN__) // append \ if necessary - if (*end_of_new_name != ':' && *end_of_new_name != '\\' && - *end_of_new_name != '/') + if (*end_of_new_name != ':' && *end_of_new_name != '\\' && *end_of_new_name != '/') strcat(new_name, "\\"); strcat(new_name, filename_with_directory); #elif defined(__OS_MAC__) @@ -348,7 +301,7 @@ prepend_directory_name(char * filename_with_directory, strcat(new_name, ":"); // do not copy starting ':' of filename if (filename_with_directory[0] == ':') - strcat(new_name, filename_with_directory+1); + strcat(new_name, filename_with_directory + 1); else strcat(new_name, filename_with_directory); #else // defined(__OS_UNIX__) @@ -356,7 +309,7 @@ prepend_directory_name(char * filename_with_directory, if (*end_of_new_name != '/') strcat(new_name, "/"); strcat(new_name, filename_with_directory); -#endif +#endif strcpy(filename_with_directory, new_name); delete[] new_name; @@ -364,122 +317,91 @@ prepend_directory_name(char * filename_with_directory, } string -ask_filename_with_extension( - const string& prompt, - const string& default_extension) -{ +ask_filename_with_extension(const string& prompt, const string& default_extension) { string file_in_directory_name; - while (file_in_directory_name.size()==0) - { + while (file_in_directory_name.size() == 0) { cerr << prompt; - if (default_extension.size()!=0) - { - cerr << "(default extension '" - << default_extension - << "'):"; - } + if (default_extension.size() != 0) { + cerr << "(default extension '" << default_extension << "'):"; + } std::getline(std::cin, file_in_directory_name); } - add_extension(file_in_directory_name,default_extension); - return(file_in_directory_name); + add_extension(file_in_directory_name, default_extension); + return (file_in_directory_name); } -char * -ask_filename_with_extension(char *file_in_directory_name, - const string& prompt, - const string& default_extension) -{ - const string answer = - ask_filename_with_extension(prompt, default_extension); +char* +ask_filename_with_extension(char* file_in_directory_name, const string& prompt, const string& default_extension) { + const string answer = ask_filename_with_extension(prompt, default_extension); strcpy(file_in_directory_name, answer.c_str()); - return(file_in_directory_name); + return (file_in_directory_name); } template void -ask_filename_and_open(FSTREAM& s, - const string& prompt, - const string& default_extension, - ios::openmode mode, - bool abort_if_failed) -{ - string filename = - ask_filename_with_extension(prompt, default_extension); - s.open( - filename.c_str(), - mode); - if (abort_if_failed && !s) - { error("Error opening file %s\n", filename.c_str()); } +ask_filename_and_open(FSTREAM& s, const string& prompt, const string& default_extension, ios::openmode mode, + bool abort_if_failed) { + string filename = ask_filename_with_extension(prompt, default_extension); + s.open(filename.c_str(), mode); + if (abort_if_failed && !s) { + error("Error opening file %s\n", filename.c_str()); + } } // instantiations -template -void -ask_filename_and_open(ifstream& s, - const string& prompt, - const string& default_extension, - ios::openmode mode, - bool abort_if_failed); -template -void -ask_filename_and_open(ofstream& s, - const string& prompt, - const string& default_extension, - ios::openmode mode, - bool abort_if_failed); -template -void -ask_filename_and_open(fstream& s, - const string& prompt, - const string& default_extension, - ios::openmode mode, - bool abort_if_failed); +template void ask_filename_and_open(ifstream& s, const string& prompt, const string& default_extension, ios::openmode mode, + bool abort_if_failed); +template void ask_filename_and_open(ofstream& s, const string& prompt, const string& default_extension, ios::openmode mode, + bool abort_if_failed); +template void ask_filename_and_open(fstream& s, const string& prompt, const string& default_extension, ios::openmode mode, + bool abort_if_failed); // find number of remaining characters -streamsize find_remaining_size (istream& input) -{ - streampos file_current_pos = input.tellg(); - input.seekg(0L, ios::end); - streampos file_end = input.tellg(); - input.clear(); // necessary because seek past EOF ? - input.seekg(file_current_pos); - return file_end - file_current_pos; +streamsize +find_remaining_size(istream& input) { + streampos file_current_pos = input.tellg(); + input.seekg(0L, ios::end); + streampos file_end = input.tellg(); + input.clear(); // necessary because seek past EOF ? + input.seekg(file_current_pos); + return file_end - file_current_pos; } -void * read_stream_in_memory(istream& input, streamsize& file_size) -{ +void* +read_stream_in_memory(istream& input, streamsize& file_size) { if (file_size == 0) file_size = find_remaining_size(input); - - cerr << "Reading " << file_size << " bytes from file." <(file_size)]; - if (memory == 0) - { error("Not enough memory\n"); } + char* memory = new char[static_cast(file_size)]; + if (memory == 0) { + error("Not enough memory\n"); + } { - const streamsize chunk_size = 1024*64; + const streamsize chunk_size = 1024 * 64; streamsize to_read = file_size; - char *current_location = memory; + char* current_location = memory; - while( to_read != 0) - { - const streamsize this_read_size = + while (to_read != 0) { + const streamsize this_read_size = #ifndef STIR_NO_NAMESPACES - std::min(to_read, chunk_size); + std::min(to_read, chunk_size); #else - min(to_read, chunk_size); + min(to_read, chunk_size); #endif - input.read(current_location, this_read_size); - if (!input) - { error("Error after reading from stream"); } - - to_read -= this_read_size; - current_location += this_read_size; + input.read(current_location, this_read_size); + if (!input) { + error("Error after reading from stream"); } + + to_read -= this_read_size; + current_location += this_read_size; + } } return memory; } diff --git a/src/buildblock/warning.cxx b/src/buildblock/warning.cxx index 162fcfc3ae..ce85eea18d 100644 --- a/src/buildblock/warning.cxx +++ b/src/buildblock/warning.cxx @@ -1,8 +1,8 @@ // // /*! - \file - + \file + \brief defines the stir::warning() function \author Kris Thielemans @@ -40,28 +40,26 @@ Visual Studio can be accomodated with the following work-around */ #ifdef BOOST_MSVC -#define vsnprintf _vsnprintf +# define vsnprintf _vsnprintf #endif START_NAMESPACE_STIR -void warning(const char *const s, ...) -{ +void +warning(const char* const s, ...) { va_list ap; va_start(ap, s); - const unsigned size=10000; + const unsigned size = 10000; char tmp[size]; - const int returned_size= vsnprintf(tmp,size, s, ap); + const int returned_size = vsnprintf(tmp, size, s, ap); std::stringstream ss; va_end(ap); if (returned_size < 0) - ss << "\nWARNING: error formatting warning message" << std::endl; - else - { - ss << "\nWARNING: " << tmp << std::endl; - if (static_cast(returned_size)>=size) - ss << "\nWARNING: previous warning message truncated as it exceeds " - << size << "bytes" << std::endl; + ss << "\nWARNING: error formatting warning message" << std::endl; + else { + ss << "\nWARNING: " << tmp << std::endl; + if (static_cast(returned_size) >= size) + ss << "\nWARNING: previous warning message truncated as it exceeds " << size << "bytes" << std::endl; } writeText(ss.str().c_str(), WARNING_CHANNEL); } diff --git a/src/buildblock/zoom.cxx b/src/buildblock/zoom.cxx index e2eee56584..fa90665cff 100644 --- a/src/buildblock/zoom.cxx +++ b/src/buildblock/zoom.cxx @@ -17,7 +17,7 @@ See STIR/LICENSE.txt for details */ /*! - \file + \file \ingroup buildblock \brief Implementations of the stir::zoom functions @@ -36,13 +36,13 @@ - KT introduced 3D zooming for images. - KT converted to new design */ - + #include "stir/interpolate.h" #include "stir/zoom.h" #include "stir/DataProcessor.h" #include "stir/DiscretisedDensity.h" -#include "stir/VoxelsOnCartesianGrid.h" -#include "stir/PixelsOnCartesianGrid.h" +#include "stir/VoxelsOnCartesianGrid.h" +#include "stir/PixelsOnCartesianGrid.h" #include "stir/Viewgram.h" #include "stir/RelatedViewgrams.h" #include "stir/ProjDataInfoCylindricalArcCorr.h" @@ -99,200 +99,150 @@ void zoom_segment (SegmentByView& segment, segment = out_segment; } - #endif void -zoom_viewgrams (RelatedViewgrams& in_viewgrams, - const float zoom, - const int min_tang_pos_num, const int max_tang_pos_num, - const float x_offset_in_mm, const float y_offset_in_mm) -{ +zoom_viewgrams(RelatedViewgrams& in_viewgrams, const float zoom, const int min_tang_pos_num, const int max_tang_pos_num, + const float x_offset_in_mm, const float y_offset_in_mm) { if (min_tang_pos_num == in_viewgrams.get_min_tangential_pos_num() && - max_tang_pos_num == in_viewgrams.get_max_tangential_pos_num() && - zoom == 1.0 && x_offset_in_mm == 0.0 && y_offset_in_mm == 0.0) + max_tang_pos_num == in_viewgrams.get_max_tangential_pos_num() && zoom == 1.0 && x_offset_in_mm == 0.0 && + y_offset_in_mm == 0.0) return; - - shared_ptr - new_proj_data_info_sptr(in_viewgrams.get_proj_data_info_sptr()->clone()); + + shared_ptr new_proj_data_info_sptr(in_viewgrams.get_proj_data_info_sptr()->clone()); ProjDataInfoCylindricalArcCorr* new_proj_data_info_arccorr_sptr = - dynamic_cast(new_proj_data_info_sptr.get()); + dynamic_cast(new_proj_data_info_sptr.get()); - if ( new_proj_data_info_arccorr_sptr==0) + if (new_proj_data_info_arccorr_sptr == 0) error("zoom_viewgram does not support non-arccorrected data. Sorry\n"); - + new_proj_data_info_arccorr_sptr->set_min_tangential_pos_num(min_tang_pos_num); new_proj_data_info_arccorr_sptr->set_max_tangential_pos_num(max_tang_pos_num); - - new_proj_data_info_arccorr_sptr-> - set_tangential_sampling(new_proj_data_info_arccorr_sptr-> - get_tangential_sampling() / zoom); - shared_ptr - symmetries_sptr(in_viewgrams.get_symmetries_ptr()->clone()); + new_proj_data_info_arccorr_sptr->set_tangential_sampling(new_proj_data_info_arccorr_sptr->get_tangential_sampling() / zoom); - RelatedViewgrams - out_viewgrams = - new_proj_data_info_arccorr_sptr-> - get_empty_related_viewgrams(in_viewgrams.get_basic_view_segment_num(), - symmetries_sptr,false,in_viewgrams.get_basic_timing_pos_num()); + shared_ptr symmetries_sptr(in_viewgrams.get_symmetries_ptr()->clone()); + + RelatedViewgrams out_viewgrams = new_proj_data_info_arccorr_sptr->get_empty_related_viewgrams( + in_viewgrams.get_basic_view_segment_num(), symmetries_sptr, false, in_viewgrams.get_basic_timing_pos_num()); { RelatedViewgrams::iterator out_iter = out_viewgrams.begin(); RelatedViewgrams::const_iterator in_iter = in_viewgrams.begin(); for (; out_iter != out_viewgrams.end(); ++out_iter, ++in_iter) - zoom_viewgram(*out_iter, *in_iter, - x_offset_in_mm, y_offset_in_mm); + zoom_viewgram(*out_iter, *in_iter, x_offset_in_mm, y_offset_in_mm); } in_viewgrams = out_viewgrams; } void -zoom_viewgram (Viewgram& in_view, - const float zoom, - const int min_tang_pos_num, const int max_tang_pos_num, - const float x_offset_in_mm, const float y_offset_in_mm) -{ - if (min_tang_pos_num == in_view.get_min_tangential_pos_num() && - max_tang_pos_num == in_view.get_max_tangential_pos_num() && - zoom == 1.0 && x_offset_in_mm == 0.0 && y_offset_in_mm == 0.0) +zoom_viewgram(Viewgram& in_view, const float zoom, const int min_tang_pos_num, const int max_tang_pos_num, + const float x_offset_in_mm, const float y_offset_in_mm) { + if (min_tang_pos_num == in_view.get_min_tangential_pos_num() && max_tang_pos_num == in_view.get_max_tangential_pos_num() && + zoom == 1.0 && x_offset_in_mm == 0.0 && y_offset_in_mm == 0.0) return; - - shared_ptr - new_proj_data_info_sptr(in_view.get_proj_data_info_sptr()->clone()); + + shared_ptr new_proj_data_info_sptr(in_view.get_proj_data_info_sptr()->clone()); ProjDataInfoCylindricalArcCorr* new_proj_data_info_arccorr_sptr = - dynamic_cast(new_proj_data_info_sptr.get()); + dynamic_cast(new_proj_data_info_sptr.get()); - if ( new_proj_data_info_arccorr_sptr==0) + if (new_proj_data_info_arccorr_sptr == 0) error("zoom_viewgram does not support non-arccorrected data. Sorry\n"); - + new_proj_data_info_arccorr_sptr->set_min_tangential_pos_num(min_tang_pos_num); new_proj_data_info_arccorr_sptr->set_max_tangential_pos_num(max_tang_pos_num); - - new_proj_data_info_arccorr_sptr-> - set_tangential_sampling(new_proj_data_info_arccorr_sptr-> - get_tangential_sampling() / zoom); - - Viewgram - out_view = new_proj_data_info_arccorr_sptr-> - get_empty_viewgram( - in_view.get_view_num(), - in_view.get_segment_num(), - false, - in_view.get_timing_pos_num()); - - zoom_viewgram(out_view, in_view, - x_offset_in_mm, y_offset_in_mm); + + new_proj_data_info_arccorr_sptr->set_tangential_sampling(new_proj_data_info_arccorr_sptr->get_tangential_sampling() / zoom); + + Viewgram out_view = new_proj_data_info_arccorr_sptr->get_empty_viewgram( + in_view.get_view_num(), in_view.get_segment_num(), false, in_view.get_timing_pos_num()); + + zoom_viewgram(out_view, in_view, x_offset_in_mm, y_offset_in_mm); in_view = out_view; } void -zoom_viewgram (Viewgram& out_view, - const Viewgram& in_view, - const float x_offset_in_mm, const float y_offset_in_mm) -{ +zoom_viewgram(Viewgram& out_view, const Viewgram& in_view, const float x_offset_in_mm, const float y_offset_in_mm) { // minimal checks on compatibility - assert(in_view.get_proj_data_info_sptr()->get_num_views() == - out_view.get_proj_data_info_sptr()->get_num_views()); + assert(in_view.get_proj_data_info_sptr()->get_num_views() == out_view.get_proj_data_info_sptr()->get_num_views()); assert(in_view.get_view_num() == out_view.get_view_num()); - assert(in_view.get_proj_data_info_sptr()->get_num_segments() == - out_view.get_proj_data_info_sptr()->get_num_segments()); + assert(in_view.get_proj_data_info_sptr()->get_num_segments() == out_view.get_proj_data_info_sptr()->get_num_segments()); assert(in_view.get_segment_num() == out_view.get_segment_num()); - assert(in_view.get_proj_data_info_sptr()->get_num_tof_poss() == - out_view.get_proj_data_info_sptr()->get_num_tof_poss()); + assert(in_view.get_proj_data_info_sptr()->get_num_tof_poss() == out_view.get_proj_data_info_sptr()->get_num_tof_poss()); assert(in_view.get_timing_pos_num() == out_view.get_timing_pos_num()); assert(in_view.get_min_axial_pos_num() == out_view.get_min_axial_pos_num()); assert(in_view.get_max_axial_pos_num() == out_view.get_max_axial_pos_num()); // get the pointers to the arc-corrected ProjDataInfo const shared_ptr in_proj_data_info_arccorr_sptr = - dynamic_pointer_cast(in_view.get_proj_data_info_sptr()); + dynamic_pointer_cast(in_view.get_proj_data_info_sptr()); const shared_ptr out_proj_data_info_arccorr_sptr = - dynamic_pointer_cast(out_view.get_proj_data_info_sptr()); + dynamic_pointer_cast(out_view.get_proj_data_info_sptr()); - if (is_null_ptr(in_proj_data_info_arccorr_sptr) || - is_null_ptr(out_proj_data_info_arccorr_sptr)) + if (is_null_ptr(in_proj_data_info_arccorr_sptr) || is_null_ptr(out_proj_data_info_arccorr_sptr)) error("zoom_viewgram does not support non-arccorrected data. Sorry\n"); - - const float in_bin_size = - in_proj_data_info_arccorr_sptr->get_tangential_sampling(); - const float out_bin_size = - out_proj_data_info_arccorr_sptr->get_tangential_sampling(); + const float in_bin_size = in_proj_data_info_arccorr_sptr->get_tangential_sampling(); + const float out_bin_size = out_proj_data_info_arccorr_sptr->get_tangential_sampling(); const float zoom = in_bin_size / out_bin_size; if (out_view.get_min_tangential_pos_num() == in_view.get_min_tangential_pos_num() && - out_view.get_max_tangential_pos_num() == in_view.get_max_tangential_pos_num() && - zoom == 1.0F && x_offset_in_mm == 0.0F && y_offset_in_mm == 0.0F) + out_view.get_max_tangential_pos_num() == in_view.get_max_tangential_pos_num() && zoom == 1.0F && x_offset_in_mm == 0.0F && + y_offset_in_mm == 0.0F) return; - - const float phi = - in_proj_data_info_arccorr_sptr-> - get_phi(Bin(in_view.get_segment_num(), in_view.get_view_num(), 0,0)); + + const float phi = in_proj_data_info_arccorr_sptr->get_phi(Bin(in_view.get_segment_num(), in_view.get_view_num(), 0, 0)); // compute offset in tangential_sampling_in units - const float offset = - (x_offset_in_mm*cos(phi) +y_offset_in_mm*sin(phi))/ in_bin_size; + const float offset = (x_offset_in_mm * cos(phi) + y_offset_in_mm * sin(phi)) / in_bin_size; - for (int axial_pos_num= out_view.get_min_axial_pos_num(); axial_pos_num <= out_view.get_max_axial_pos_num(); ++axial_pos_num) - { - overlap_interpolate(out_view[axial_pos_num], in_view[axial_pos_num], zoom, offset); - } + for (int axial_pos_num = out_view.get_min_axial_pos_num(); axial_pos_num <= out_view.get_max_axial_pos_num(); ++axial_pos_num) { + overlap_interpolate(out_view[axial_pos_num], in_view[axial_pos_num], zoom, offset); + } } - - - -static -VoxelsOnCartesianGrid -construct_new_image_from_zoom_parameters(const VoxelsOnCartesianGrid &image, - const CartesianCoordinate3D& zooms, - const CartesianCoordinate3D& offsets_in_mm, - const BasicCoordinate<3,int>& new_sizes_arg) -{ +static VoxelsOnCartesianGrid +construct_new_image_from_zoom_parameters(const VoxelsOnCartesianGrid& image, const CartesianCoordinate3D& zooms, + const CartesianCoordinate3D& offsets_in_mm, + const BasicCoordinate<3, int>& new_sizes_arg) { CartesianCoordinate3D new_sizes = new_sizes_arg; - assert(new_sizes.x()>=0); - assert(new_sizes.y()>=0); - assert(new_sizes.z()>=0); - CartesianCoordinate3D - voxel_size = image.get_grid_spacing() / zooms; + assert(new_sizes.x() >= 0); + assert(new_sizes.y() >= 0); + assert(new_sizes.z() >= 0); + CartesianCoordinate3D voxel_size = image.get_grid_spacing() / zooms; // first set origin to 0 - CartesianCoordinate3D - origin(0.F,0.F,0.F); - - VoxelsOnCartesianGrid - new_image(image.get_exam_info_sptr(), - IndexRange3D(0, new_sizes.z()-1, - -new_sizes.y()/2, -new_sizes.y()/2+new_sizes.y()-1, - -new_sizes.x()/2, -new_sizes.x()/2+new_sizes.x()-1), - origin, - voxel_size); + CartesianCoordinate3D origin(0.F, 0.F, 0.F); + + VoxelsOnCartesianGrid new_image(image.get_exam_info_sptr(), + IndexRange3D(0, new_sizes.z() - 1, -new_sizes.y() / 2, + -new_sizes.y() / 2 + new_sizes.y() - 1, -new_sizes.x() / 2, + -new_sizes.x() / 2 + new_sizes.x() - 1), + origin, voxel_size); // find coordinates of middle of images - BasicCoordinate<3,int> min_indices, max_indices; + BasicCoordinate<3, int> min_indices, max_indices; if (!image.get_regular_range(min_indices, max_indices)) error("zoom_image: Non-regular range of coordinates in input image. That's strange."); - BasicCoordinate<3,int> new_min_indices, new_max_indices; + BasicCoordinate<3, int> new_min_indices, new_max_indices; if (!new_image.get_regular_range(new_min_indices, new_max_indices)) error("zoom_image: Non-regular range of coordinates in output image. That's a bug."); - const BasicCoordinate<3,float> middle = - (image.get_physical_coordinates_for_indices(min_indices) + - image.get_physical_coordinates_for_indices(max_indices))/2; - const BasicCoordinate<3,float> new_middle = - (new_image.get_physical_coordinates_for_indices(new_min_indices) + - new_image.get_physical_coordinates_for_indices(new_max_indices))/2; + const BasicCoordinate<3, float> middle = + (image.get_physical_coordinates_for_indices(min_indices) + image.get_physical_coordinates_for_indices(max_indices)) / 2; + const BasicCoordinate<3, float> new_middle = (new_image.get_physical_coordinates_for_indices(new_min_indices) + + new_image.get_physical_coordinates_for_indices(new_max_indices)) / + 2; // now make sure that these are shifted as required new_image.set_origin(offsets_in_mm + middle - new_middle); // check { - const BasicCoordinate<3,float> final_middle = - (new_image.get_physical_coordinates_for_indices(new_min_indices) + - new_image.get_physical_coordinates_for_indices(new_max_indices))/2; + const BasicCoordinate<3, float> final_middle = (new_image.get_physical_coordinates_for_indices(new_min_indices) + + new_image.get_physical_coordinates_for_indices(new_max_indices)) / + 2; if (norm(final_middle - middle - offsets_in_mm) > 1) error("zoom_image bug in finding new origin"); } @@ -300,155 +250,109 @@ construct_new_image_from_zoom_parameters(const VoxelsOnCartesianGrid &ima return new_image; } void -zoom_image_in_place(VoxelsOnCartesianGrid &image, - const float zoom, - const float x_offset_in_mm, const float y_offset_in_mm, - const int new_size, - const ZoomOptions zoom_options) -{ - VoxelsOnCartesianGrid new_image = - zoom_image(image, zoom, x_offset_in_mm, y_offset_in_mm, new_size, zoom_options); +zoom_image_in_place(VoxelsOnCartesianGrid& image, const float zoom, const float x_offset_in_mm, const float y_offset_in_mm, + const int new_size, const ZoomOptions zoom_options) { + VoxelsOnCartesianGrid new_image = zoom_image(image, zoom, x_offset_in_mm, y_offset_in_mm, new_size, zoom_options); image = new_image; } VoxelsOnCartesianGrid -zoom_image(const VoxelsOnCartesianGrid &image, - const float zoom, - const float x_offset_in_mm, const float y_offset_in_mm, - const int new_size, - const ZoomOptions zoom_options) -{ - assert(new_size>=0); - if(zoom==1 && x_offset_in_mm==0 && y_offset_in_mm==0 && new_size== image.get_x_size()) +zoom_image(const VoxelsOnCartesianGrid& image, const float zoom, const float x_offset_in_mm, const float y_offset_in_mm, + const int new_size, const ZoomOptions zoom_options) { + assert(new_size >= 0); + if (zoom == 1 && x_offset_in_mm == 0 && y_offset_in_mm == 0 && new_size == image.get_x_size()) return image; - - const CartesianCoordinate3D zooms(1,zoom,zoom); + + const CartesianCoordinate3D zooms(1, zoom, zoom); const CartesianCoordinate3D offsets_in_mm(0.F, y_offset_in_mm, x_offset_in_mm); - const BasicCoordinate<3,int> new_sizes = - make_coordinate(image.get_length(), new_size, new_size); - - VoxelsOnCartesianGrid new_image = - construct_new_image_from_zoom_parameters(image, - zooms, - offsets_in_mm, - new_sizes); - - PixelsOnCartesianGrid - new_image2D = new_image.get_plane(new_image.get_min_z()); - for (int plane = image.get_min_z(); plane <= image.get_max_z(); plane++) - { - zoom_image(new_image2D, image.get_plane(plane), zoom_options); - new_image.set_plane(new_image2D, plane); - } - - assert(norm(new_image.get_voxel_size() - image.get_voxel_size()/zooms)<1); + const BasicCoordinate<3, int> new_sizes = make_coordinate(image.get_length(), new_size, new_size); + + VoxelsOnCartesianGrid new_image = construct_new_image_from_zoom_parameters(image, zooms, offsets_in_mm, new_sizes); + + PixelsOnCartesianGrid new_image2D = new_image.get_plane(new_image.get_min_z()); + for (int plane = image.get_min_z(); plane <= image.get_max_z(); plane++) { + zoom_image(new_image2D, image.get_plane(plane), zoom_options); + new_image.set_plane(new_image2D, plane); + } + + assert(norm(new_image.get_voxel_size() - image.get_voxel_size() / zooms) < 1); return new_image; } void -zoom_image_in_place(VoxelsOnCartesianGrid &image, - const CartesianCoordinate3D& zooms, - const CartesianCoordinate3D& offsets_in_mm, - const BasicCoordinate<3,int>& new_sizes, - const ZoomOptions zoom_options) -{ - const VoxelsOnCartesianGrid new_image = - zoom_image(image, zooms, offsets_in_mm, new_sizes, zoom_options); +zoom_image_in_place(VoxelsOnCartesianGrid& image, const CartesianCoordinate3D& zooms, + const CartesianCoordinate3D& offsets_in_mm, const BasicCoordinate<3, int>& new_sizes, + const ZoomOptions zoom_options) { + const VoxelsOnCartesianGrid new_image = zoom_image(image, zooms, offsets_in_mm, new_sizes, zoom_options); image = new_image; } VoxelsOnCartesianGrid -zoom_image(const VoxelsOnCartesianGrid &image, - const CartesianCoordinate3D& zooms, - const CartesianCoordinate3D& offsets_in_mm, - const BasicCoordinate<3,int>& new_sizes, - const ZoomOptions zoom_options) -{ +zoom_image(const VoxelsOnCartesianGrid& image, const CartesianCoordinate3D& zooms, + const CartesianCoordinate3D& offsets_in_mm, const BasicCoordinate<3, int>& new_sizes, + const ZoomOptions zoom_options) { - VoxelsOnCartesianGrid new_image = - construct_new_image_from_zoom_parameters(image, - zooms, - offsets_in_mm, - new_sizes); + VoxelsOnCartesianGrid new_image = construct_new_image_from_zoom_parameters(image, zooms, offsets_in_mm, new_sizes); zoom_image(new_image, image, zoom_options); return new_image; } -void -zoom_image(VoxelsOnCartesianGrid &image_out, - const VoxelsOnCartesianGrid &image_in, - const ZoomOptions zoom_options) -{ +void +zoom_image(VoxelsOnCartesianGrid& image_out, const VoxelsOnCartesianGrid& image_in, + const ZoomOptions zoom_options) { image_out.set_exam_info(image_in.get_exam_info()); -/* - interpolation routine uses the following relation: - x_in_index = x_out_index/zoom + offset - - compare to 'physical' coordinates - x_phys = (x_index) * voxel_size.x + origin.x - - as x_in_phys == x_out_phys, we find - (x_in_index)* voxel_size_in.x + origin_in.x == - (x_out_index )* voxel_size_out.x + origin_out.x - <=> - x_in_index = (x_out_index * voxel_size_out.x - + origin_out.x - origin_in.x) - / voxel_size_in.x - - so, zoom= voxel_size_in.x/ voxel_size_out.x - offset = (origin_out.x - origin_in.x)/ voxel_size_in.x + /* + interpolation routine uses the following relation: + x_in_index = x_out_index/zoom + offset - */ + compare to 'physical' coordinates + x_phys = (x_index) * voxel_size.x + origin.x + + as x_in_phys == x_out_phys, we find + (x_in_index)* voxel_size_in.x + origin_in.x == + (x_out_index )* voxel_size_out.x + origin_out.x + <=> + x_in_index = (x_out_index * voxel_size_out.x + + origin_out.x - origin_in.x) + / voxel_size_in.x + + so, zoom= voxel_size_in.x/ voxel_size_out.x + offset = (origin_out.x - origin_in.x)/ voxel_size_in.x + + */ // check relation between indices and physical coordinates { - const BasicCoordinate<3,int> indices = make_coordinate(1,2,3); - if (norm(image_in.get_physical_coordinates_for_indices(indices) - - (image_in.get_voxel_size() * BasicCoordinate<3,float>(indices) + image_in.get_origin()) - ) > 2.F) + const BasicCoordinate<3, int> indices = make_coordinate(1, 2, 3); + if (norm(image_in.get_physical_coordinates_for_indices(indices) - + (image_in.get_voxel_size() * BasicCoordinate<3, float>(indices) + image_in.get_origin())) > 2.F) error("zoom_image is confused about the relation between indices and physical coordinates"); } - const float zoom_x = - image_in.get_voxel_size().x() / image_out.get_voxel_size().x(); - const float zoom_y = - image_in.get_voxel_size().y() / image_out.get_voxel_size().y(); - const float zoom_z = - image_in.get_voxel_size().z() / image_out.get_voxel_size().z(); - const float x_offset = - (image_out.get_origin().x() - image_in.get_origin().x()) - / image_in.get_voxel_size().x(); - const float y_offset = - (image_out.get_origin().y() - image_in.get_origin().y()) - / image_in.get_voxel_size().y(); - const float z_offset = - (image_out.get_origin().z() - image_in.get_origin().z()) - / image_in.get_voxel_size().z(); - - - if(zoom_x==1.0F && zoom_y==1.0F && zoom_z==1.0F && - x_offset == 0.F && y_offset == 0.F && z_offset == 0.F && - image_in.get_index_range() == image_out.get_index_range() - ) - { - image_out = image_in; - return; - } + const float zoom_x = image_in.get_voxel_size().x() / image_out.get_voxel_size().x(); + const float zoom_y = image_in.get_voxel_size().y() / image_out.get_voxel_size().y(); + const float zoom_z = image_in.get_voxel_size().z() / image_out.get_voxel_size().z(); + const float x_offset = (image_out.get_origin().x() - image_in.get_origin().x()) / image_in.get_voxel_size().x(); + const float y_offset = (image_out.get_origin().y() - image_in.get_origin().y()) / image_in.get_voxel_size().y(); + const float z_offset = (image_out.get_origin().z() - image_in.get_origin().z()) / image_in.get_voxel_size().z(); + + if (zoom_x == 1.0F && zoom_y == 1.0F && zoom_z == 1.0F && x_offset == 0.F && y_offset == 0.F && z_offset == 0.F && + image_in.get_index_range() == image_out.get_index_range()) { + image_out = image_in; + return; + } // TODO creating a lot of new images here... - Array<3,float> - temp(IndexRange3D(image_in.get_min_z(), image_in.get_max_z(), - image_in.get_min_y(), image_in.get_max_y(), - image_out.get_min_x(), image_out.get_max_x())); + Array<3, float> temp(IndexRange3D(image_in.get_min_z(), image_in.get_max_z(), image_in.get_min_y(), image_in.get_max_y(), + image_out.get_min_x(), image_out.get_max_x())); - for (int z=image_in.get_min_z(); z<=image_in.get_max_z(); z++) - for (int y=image_in.get_min_y(); y<=image_in.get_max_y(); y++) + for (int z = image_in.get_min_z(); z <= image_in.get_max_z(); z++) + for (int y = image_in.get_min_y(); y <= image_in.get_max_y(); y++) overlap_interpolate(temp[z][y], image_in[z][y], zoom_x, x_offset); - Array<3,float> temp2(IndexRange3D(image_in.get_min_z(), image_in.get_max_z(), - image_out.get_min_y(), image_out.get_max_y(), - image_out.get_min_x(), image_out.get_max_x())); + Array<3, float> temp2(IndexRange3D(image_in.get_min_z(), image_in.get_max_z(), image_out.get_min_y(), image_out.get_max_y(), + image_out.get_min_x(), image_out.get_max_x())); - for (int z=image_in.get_min_z(); z<=image_in.get_max_z(); z++) + for (int z = image_in.get_min_z(); z <= image_in.get_max_z(); z++) overlap_interpolate(temp2[z], temp[z], zoom_y, y_offset); temp.recycle(); @@ -457,101 +361,77 @@ zoom_image(VoxelsOnCartesianGrid &image_out, float scale_image = 1.F; - switch (zoom_options.get_scaling_option()) - { - case ZoomOptions::preserve_values: - { - scale_image = zoom_x*zoom_y*zoom_z; - break; - } - - case ZoomOptions::preserve_projections: + switch (zoom_options.get_scaling_option()) { + case ZoomOptions::preserve_values: { + scale_image = zoom_x * zoom_y * zoom_z; + break; + } - { - scale_image = zoom_y*zoom_z; - break; - } + case ZoomOptions::preserve_projections: - case ZoomOptions::preserve_sum: - { - return; // no need to scale - } + { + scale_image = zoom_y * zoom_z; + break; + } - } + case ZoomOptions::preserve_sum: { + return; // no need to scale + } + } if (scale_image != 1.F) - image_out*= scale_image; - + image_out *= scale_image; } void -zoom_image(PixelsOnCartesianGrid &image2D_out, - const PixelsOnCartesianGrid &image2D_in, - const ZoomOptions zoom_options) -{ +zoom_image(PixelsOnCartesianGrid& image2D_out, const PixelsOnCartesianGrid& image2D_in, + const ZoomOptions zoom_options) { image2D_out.set_exam_info(image2D_in.get_exam_info()); /* see above for how to find zoom and offsets */ - const float zoom_x = - image2D_in.get_pixel_size().x() / image2D_out.get_pixel_size().x(); - const float zoom_y = - image2D_in.get_pixel_size().y() / image2D_out.get_pixel_size().y(); - const float x_offset = - ((image2D_out.get_origin().x() - image2D_in.get_origin().x()) - / image2D_in.get_pixel_size().x() - ); - const float y_offset = - ((image2D_out.get_origin().y() - image2D_in.get_origin().y()) - / image2D_in.get_pixel_size().y() - ); - - if(zoom_x==1.0F && zoom_y==1.0F && - x_offset == 0.F && y_offset == 0.F && - image2D_in.get_index_range() == image2D_out.get_index_range() - ) - { + const float zoom_x = image2D_in.get_pixel_size().x() / image2D_out.get_pixel_size().x(); + const float zoom_y = image2D_in.get_pixel_size().y() / image2D_out.get_pixel_size().y(); + const float x_offset = ((image2D_out.get_origin().x() - image2D_in.get_origin().x()) / image2D_in.get_pixel_size().x()); + const float y_offset = ((image2D_out.get_origin().y() - image2D_in.get_origin().y()) / image2D_in.get_pixel_size().y()); + + if (zoom_x == 1.0F && zoom_y == 1.0F && x_offset == 0.F && y_offset == 0.F && + image2D_in.get_index_range() == image2D_out.get_index_range()) { image2D_out = image2D_in; return; } - Array<2,float> - temp(IndexRange2D(image2D_in.get_min_y(), image2D_in.get_max_y(), - image2D_out.get_min_x(), image2D_out.get_max_x())); + Array<2, float> temp( + IndexRange2D(image2D_in.get_min_y(), image2D_in.get_max_y(), image2D_out.get_min_x(), image2D_out.get_max_x())); - for (int y=image2D_in.get_min_y(); y<=image2D_in.get_max_y(); y++) + for (int y = image2D_in.get_min_y(); y <= image2D_in.get_max_y(); y++) overlap_interpolate(temp[y], image2D_in[y], zoom_x, x_offset); - overlap_interpolate(image2D_out, temp, zoom_y, y_offset); + overlap_interpolate(image2D_out, temp, zoom_y, y_offset); float scale_image = 1.F; - switch (zoom_options.get_scaling_option()) - { - case ZoomOptions::preserve_values: - { - scale_image = zoom_x*zoom_y; - break; - } - - case ZoomOptions::preserve_projections: + switch (zoom_options.get_scaling_option()) { + case ZoomOptions::preserve_values: { + scale_image = zoom_x * zoom_y; + break; + } - { - scale_image = zoom_y; - break; - } + case ZoomOptions::preserve_projections: - case ZoomOptions::preserve_sum: - { - return; // no need to scale - } + { + scale_image = zoom_y; + break; + } - } + case ZoomOptions::preserve_sum: { + return; // no need to scale + } + } if (scale_image != 1.F) - image2D_out*= scale_image; - + image2D_out *= scale_image; } END_NAMESPACE_STIR diff --git a/src/data_buildblock/RegisteredObject.cxx b/src/data_buildblock/RegisteredObject.cxx index b39fd5bd86..b8b7f2b304 100644 --- a/src/data_buildblock/RegisteredObject.cxx +++ b/src/data_buildblock/RegisteredObject.cxx @@ -32,29 +32,26 @@ #include "stir/RegisteredObject.h" #ifdef __STIR_REGISTRY_NOT_INLINE -#pragma message("instantiating RegisteredObject") -#include "stir/data/SinglesRates.h" - +# pragma message("instantiating RegisteredObject") +# include "stir/data/SinglesRates.h" // and others START_NAMESPACE_STIR template -RegisteredObject::RegistryType& -RegisteredObject::registry () -{ +RegisteredObject::RegistryType& +RegisteredObject::registry() { static RegistryType the_registry("None", 0); return the_registry; } # ifdef _MSC_VER -// prevent warning message on reinstantiation, +// prevent warning message on reinstantiation, // note that we get a linking error if we don't have the explicit instantiation below -# pragma warning(disable:4660) +# pragma warning(disable : 4660) # endif - -template RegisteredObject; +template RegisteredObject; END_NAMESPACE_STIR diff --git a/src/data_buildblock/SinglesRates.cxx b/src/data_buildblock/SinglesRates.cxx index b7afcdfe93..5a2312b90f 100644 --- a/src/data_buildblock/SinglesRates.cxx +++ b/src/data_buildblock/SinglesRates.cxx @@ -31,70 +31,41 @@ using std::vector; START_NAMESPACE_STIR - /* *! FrameSinglesRates constructor. */ -FrameSinglesRates:: -FrameSinglesRates(vector& avg_singles_rates, - double start_time, - double end_time, - shared_ptr scanner_sptr) : - _start_time(start_time), - _end_time(end_time), - _singles(avg_singles_rates), - _scanner_sptr(scanner_sptr) -{ +FrameSinglesRates::FrameSinglesRates(vector& avg_singles_rates, double start_time, double end_time, + shared_ptr scanner_sptr) + : _start_time(start_time), _end_time(end_time), _singles(avg_singles_rates), _scanner_sptr(scanner_sptr) { assert(avg_singles_rates.size() == static_cast(scanner_sptr->get_num_singles_units())); } - - -float -FrameSinglesRates:: -get_singles_rate(int singles_bin_index) const { - return(_singles[singles_bin_index]); +float +FrameSinglesRates::get_singles_rate(int singles_bin_index) const { + return (_singles[singles_bin_index]); } - - -float -FrameSinglesRates:: -get_singles_rate(const DetectionPosition<>& det_pos) const { +float +FrameSinglesRates::get_singles_rate(const DetectionPosition<>& det_pos) const { int singles_bin_index = _scanner_sptr->get_singles_bin_index(det_pos); - return(get_singles_rate(singles_bin_index)); + return (get_singles_rate(singles_bin_index)); } - - - double -FrameSinglesRates:: -get_start_time() const { - return(_start_time); +FrameSinglesRates::get_start_time() const { + return (_start_time); } - + double -FrameSinglesRates:: -get_end_time() const { - return(_end_time); +FrameSinglesRates::get_end_time() const { + return (_end_time); } - - // Get the average singles rate for a particular bin. float -SinglesRates:: -get_singles_rate(const DetectionPosition<>& det_pos, - const double start_time, const double end_time) const -{ +SinglesRates::get_singles_rate(const DetectionPosition<>& det_pos, const double start_time, const double end_time) const { const int singles_bin_index = scanner_sptr->get_singles_bin_index(det_pos); - return(get_singles_rate(singles_bin_index, start_time, end_time)); + return (get_singles_rate(singles_bin_index, start_time, end_time)); } - - END_NAMESPACE_STIR - - - diff --git a/src/data_buildblock/SinglesRatesForTimeFrames.cxx b/src/data_buildblock/SinglesRatesForTimeFrames.cxx index 47825f7dc5..5316cbaa60 100644 --- a/src/data_buildblock/SinglesRatesForTimeFrames.cxx +++ b/src/data_buildblock/SinglesRatesForTimeFrames.cxx @@ -30,57 +30,39 @@ START_NAMESPACE_STIR -SinglesRatesForTimeFrames:: -SinglesRatesForTimeFrames() -{} - +SinglesRatesForTimeFrames::SinglesRatesForTimeFrames() {} -float -SinglesRatesForTimeFrames:: -get_singles_rate(const int singles_bin_index, - const unsigned int time_frame_num) const -{ - return(this->_singles[time_frame_num][singles_bin_index]); +float +SinglesRatesForTimeFrames::get_singles_rate(const int singles_bin_index, const unsigned int time_frame_num) const { + return (this->_singles[time_frame_num][singles_bin_index]); } -void -SinglesRatesForTimeFrames:: -set_singles_rate(const int singles_bin_index, - const unsigned time_frame_num, - const float new_rate) +void +SinglesRatesForTimeFrames::set_singles_rate(const int singles_bin_index, const unsigned time_frame_num, const float new_rate) { this->_singles[time_frame_num][singles_bin_index] = new_rate; } - float -SinglesRatesForTimeFrames:: -get_singles_rate(const int singles_bin_index, - const double start_time, - const double end_time) const -{ - const unsigned frame_number = - this->_time_frame_defs.get_time_frame_num(start_time, end_time); +SinglesRatesForTimeFrames::get_singles_rate(const int singles_bin_index, const double start_time, const double end_time) const { + const unsigned frame_number = this->_time_frame_defs.get_time_frame_num(start_time, end_time); if (frame_number == 0) return -1.F; else - return(get_singles_rate(singles_bin_index, frame_number)); + return (get_singles_rate(singles_bin_index, frame_number)); } -unsigned int +unsigned int SinglesRatesForTimeFrames::get_num_frames() const { - return(this->_time_frame_defs.get_num_frames()); + return (this->_time_frame_defs.get_num_frames()); } const TimeFrameDefinitions& -SinglesRatesForTimeFrames:: -get_time_frame_definitions() const -{ +SinglesRatesForTimeFrames::get_time_frame_definitions() const { return this->_time_frame_defs; } - #if 0 double SinglesRatesForTimeFrames:: @@ -106,5 +88,3 @@ get_frame_end(unsigned int frame_number) const { #endif END_NAMESPACE_STIR - - diff --git a/src/data_buildblock/SinglesRatesFromECAT7.cxx b/src/data_buildblock/SinglesRatesFromECAT7.cxx index d6ccf21feb..a7884fe79e 100644 --- a/src/data_buildblock/SinglesRatesFromECAT7.cxx +++ b/src/data_buildblock/SinglesRatesFromECAT7.cxx @@ -30,7 +30,7 @@ #include "stir/IO/stir_ecat7.h" #include "stir/IndexRange2D.h" #ifdef HAVE_LLN_MATRIX -#include "ecat_model.h" +# include "ecat_model.h" #endif #include @@ -44,20 +44,15 @@ START_NAMESPACE_STIR START_NAMESPACE_ECAT START_NAMESPACE_ECAT7 -const char * const -SinglesRatesFromECAT7::registered_name = "Singles From ECAT7"; - -SinglesRatesFromECAT7:: -SinglesRatesFromECAT7() -{} +const char* const SinglesRatesFromECAT7::registered_name = "Singles From ECAT7"; +SinglesRatesFromECAT7::SinglesRatesFromECAT7() {} int -SinglesRatesFromECAT7::read_singles_from_file(const std::string& ECAT7_filename, - const std::ios::openmode open_mode) +SinglesRatesFromECAT7::read_singles_from_file(const std::string& ECAT7_filename, const std::ios::openmode open_mode) { - + int num_frames = 0; #ifndef HAVE_LLN_MATRIX @@ -68,112 +63,78 @@ SinglesRatesFromECAT7::read_singles_from_file(const std::string& ECAT7_filename, MatrixFile* mptr = matrix_open(ECAT7_filename.c_str(), MAT_READ_ONLY, MAT_UNKNOWN_FTYPE); - if (mptr==0) { + if (mptr == 0) { error("Error opening '%s' as ECAT7\n", ECAT7_filename.c_str()); } - if (!(mptr->mhptr->file_type == Byte3dSinogram || - mptr->mhptr->file_type == Short3dSinogram || + if (!(mptr->mhptr->file_type == Byte3dSinogram || mptr->mhptr->file_type == Short3dSinogram || mptr->mhptr->file_type == Float3dSinogram)) { - error("SinglesRatesFromECAT7: filename %s should be an ECAT7 emission file\n", - ECAT7_filename.c_str()); + error("SinglesRatesFromECAT7: filename %s should be an ECAT7 emission file\n", ECAT7_filename.c_str()); } - - scanner_sptr.reset(find_scanner_from_ECAT_system_type(mptr->mhptr->system_type)); + scanner_sptr.reset(find_scanner_from_ECAT_system_type(mptr->mhptr->system_type)); if (scanner_sptr->get_type() != Scanner::E966) { warning("check SinglesRatesFromECAT7 for non-966\n"); } - - Main_header* main_header = reinterpret_cast( mptr->mhptr ) ; + Main_header* main_header = reinterpret_cast(mptr->mhptr); num_frames = main_header->num_frames; // Get total number of bins for this type of scanner. const int total_singles_units = scanner_sptr->get_num_singles_units(); - if ( total_singles_units > 0 ) { + if (total_singles_units > 0) { // Create the main array of data. - this->_singles = Array<2,float>(IndexRange2D(1, main_header->num_frames, - 0, total_singles_units - 1)); + this->_singles = Array<2, float>(IndexRange2D(1, main_header->num_frames, 0, total_singles_units - 1)); } - MatrixData* matrix; - std::vector > time_frames(main_header->num_frames); + std::vector> time_frames(main_header->num_frames); + for (int mat_frame = 1; mat_frame <= num_frames; mat_frame++) { + // cerr << "Reading frame " << mat_frame <(matrix->shptr); - time_frames[mat_frame-1].first=scan_subheader_ptr->frame_start_time/1000.; - time_frames[mat_frame-1].second= - time_frames[mat_frame-1].first + - scan_subheader_ptr->frame_duration/1000.; + Scan3D_subheader* scan_subheader_ptr = reinterpret_cast(matrix->shptr); + time_frames[mat_frame - 1].first = scan_subheader_ptr->frame_start_time / 1000.; + time_frames[mat_frame - 1].second = time_frames[mat_frame - 1].first + scan_subheader_ptr->frame_duration / 1000.; - float const* singles_ptr = reinterpret_cast(scan_subheader_ptr->uncor_singles);//matrix->data_ptr); + float const* singles_ptr = reinterpret_cast(scan_subheader_ptr->uncor_singles); // matrix->data_ptr); // The order of the singles units in the sub header is the same as required // by the main singles array. This may not be the case for other file formats. - for (int singles_bin = 0 ; singles_bin < total_singles_units ; ++singles_bin) { + for (int singles_bin = 0; singles_bin < total_singles_units; ++singles_bin) { this->_singles[mat_frame][singles_bin] = *(singles_ptr + singles_bin); } - } this->_time_frame_defs = TimeFrameDefinitions(time_frames); #endif - return(num_frames); - + return (num_frames); } - - - - - - - - -void -SinglesRatesFromECAT7:: -initialise_keymap() -{ +void +SinglesRatesFromECAT7::initialise_keymap() { parser.add_start_key("Singles Rates From ECAT7"); parser.add_key("ECAT7_filename", &ECAT7_filename); parser.add_stop_key("End Singles Rates From ECAT7"); } -bool -SinglesRatesFromECAT7:: -post_processing() -{ +bool +SinglesRatesFromECAT7::post_processing() { read_singles_from_file(ECAT7_filename); return false; } - -void -SinglesRatesFromECAT7::set_defaults() -{ +void +SinglesRatesFromECAT7::set_defaults() { ECAT7_filename = ""; } - - - - - END_NAMESPACE_ECAT7 END_NAMESPACE_ECAT END_NAMESPACE_STIR - - diff --git a/src/data_buildblock/SinglesRatesFromGEHDF5.cxx b/src/data_buildblock/SinglesRatesFromGEHDF5.cxx old mode 100755 new mode 100644 index 394a45189e..96ab6f4aaf --- a/src/data_buildblock/SinglesRatesFromGEHDF5.cxx +++ b/src/data_buildblock/SinglesRatesFromGEHDF5.cxx @@ -45,48 +45,39 @@ using std::streampos; using std::ios; #endif - - START_NAMESPACE_STIR namespace GE { namespace RDF_HDF5 { -const char * const -SinglesRatesFromGEHDF5::registered_name = "Singles From GE HDF5 listmode File"; +const char* const SinglesRatesFromGEHDF5::registered_name = "Singles From GE HDF5 listmode File"; const double MAX_INTERVAL_DIFFERENCE = 0.05; // 5% max difference. -//PW Will not change this bit of code. +// PW Will not change this bit of code. // Constructor -SinglesRatesFromGEHDF5:: -SinglesRatesFromGEHDF5() -{} - +SinglesRatesFromGEHDF5::SinglesRatesFromGEHDF5() {} // Generate a FramesSinglesRate - containing the average rates // for a frame begining at start_time and ending at end_time. FrameSinglesRates -SinglesRatesFromGEHDF5:: -get_rates_for_frame(double start_time, - double end_time) const { +SinglesRatesFromGEHDF5::get_rates_for_frame(double start_time, double end_time) const { int num_singles_units = SinglesRates::scanner_sptr->get_num_singles_units(); // Create a temporary vector std::vector average_singles_rates(num_singles_units); - // Loop over all bins. - for(int singles_bin = 0 ; singles_bin < num_singles_units ; ++singles_bin) { + for (int singles_bin = 0; singles_bin < num_singles_units; ++singles_bin) { average_singles_rates[singles_bin] = get_singles_rate(singles_bin, start_time, end_time); } - + // Determine that start and end slice indices. int start_slice = get_start_time_slice_index(start_time); int end_slice = get_end_time_slice_index(end_time); - + double frame_start_time; - if ( start_slice == 0 ) { + if (start_slice == 0) { frame_start_time = _times[0] - _singles_time_interval; } else { frame_start_time = _times[start_slice - 1]; @@ -95,290 +86,223 @@ get_rates_for_frame(double start_time, double frame_end_time = _times[end_slice]; // Create temp FrameSinglesRate object - FrameSinglesRates frame_rates(average_singles_rates, - frame_start_time, - frame_end_time, - SinglesRates::scanner_sptr); - - return(frame_rates); - -} - - - + FrameSinglesRates frame_rates(average_singles_rates, frame_start_time, frame_end_time, SinglesRates::scanner_sptr); + return (frame_rates); +} // Get time slice index. // Returns the index of the slice that contains the specified time. int -SinglesRatesFromGEHDF5:: -get_end_time_slice_index(double t) const { +SinglesRatesFromGEHDF5::get_end_time_slice_index(double t) const { unsigned int slice_index = 0; // Start with an initial estimate. - if ( _singles_time_interval != 0 ) { + if (_singles_time_interval != 0) { slice_index = static_cast(floor(t / _singles_time_interval)); } - if ( slice_index >= m_num_time_slices ) { + if (slice_index >= m_num_time_slices) { slice_index = m_num_time_slices - 1; - } - + } // Check estimate and determine whether to look further or backwards. // Note that we could just move fowards first and then backwards but this // method is more intuitive. - if ( _times[slice_index] < t ) { - + if (_times[slice_index] < t) { + // Check forwards. - while( slice_index < m_num_time_slices - 1 && - _times[slice_index] < t ) { + while (slice_index < m_num_time_slices - 1 && _times[slice_index] < t) { slice_index++; } } else { // Check backwards. - while( slice_index > 0 && _times[slice_index - 1] >= t ) { + while (slice_index > 0 && _times[slice_index - 1] >= t) { slice_index--; } - } - - return(slice_index); -} - - + return (slice_index); +} // Get time slice index. // Returns first slice ending _after_ t. int -SinglesRatesFromGEHDF5:: -get_start_time_slice_index(double t) const { +SinglesRatesFromGEHDF5::get_start_time_slice_index(double t) const { unsigned int slice_index = 0; // Start with an initial estimate. - if ( _singles_time_interval != 0 ) { + if (_singles_time_interval != 0) { slice_index = static_cast(floor(t / _singles_time_interval)); } - if ( slice_index >= m_num_time_slices ) { + if (slice_index >= m_num_time_slices) { slice_index = m_num_time_slices - 1; - } - + } // Check estimate and determine whether to look further or backwards. // Note that we could just move fowards first and then backwards but this // method is more intuitive. - if ( _times[slice_index] < t ) { - + if (_times[slice_index] < t) { + // Check forwards. - while( slice_index < m_num_time_slices - 1 && - _times[slice_index] <= t ) { + while (slice_index < m_num_time_slices - 1 && _times[slice_index] <= t) { slice_index++; } } else { // Check backwards. - while( slice_index > 0 && _times[slice_index - 1] > t ) { + while (slice_index > 0 && _times[slice_index - 1] > t) { slice_index--; } - } - - return(slice_index); -} - - - - + return (slice_index); +} // Get rates using time slice and singles bin indices. -int -SinglesRatesFromGEHDF5:: -get_singles_rate(int singles_bin_index,int time_slice) const { - +int +SinglesRatesFromGEHDF5::get_singles_rate(int singles_bin_index, int time_slice) const { + // Check ranges. unsigned int total_singles_units = SinglesRates::scanner_sptr->get_num_singles_units(); - - if ( singles_bin_index < 0 || singles_bin_index >= total_singles_units || - time_slice < 0 || time_slice >= m_num_time_slices ) { - return(0); + + if (singles_bin_index < 0 || singles_bin_index >= total_singles_units || time_slice < 0 || time_slice >= m_num_time_slices) { + return (0); } else { return (*m_singles_sptr)[time_slice][singles_bin_index]; } - } - - // Set a singles rate by bin index and time slice. -void -SinglesRatesFromGEHDF5:: -set_singles_rate(int singles_bin_index, int time_slice, int new_rate) { - +void +SinglesRatesFromGEHDF5::set_singles_rate(int singles_bin_index, int time_slice, int new_rate) { + unsigned int total_singles_units = SinglesRates::scanner_sptr->get_num_singles_units(); - - if ( singles_bin_index >= 0 && singles_bin_index < total_singles_units && - time_slice >= 0 && time_slice < m_num_time_slices ) { + + if (singles_bin_index >= 0 && singles_bin_index < total_singles_units && time_slice >= 0 && time_slice < m_num_time_slices) { (*m_singles_sptr)[time_slice][singles_bin_index] = new_rate; } } - - - -unsigned int SinglesRatesFromGEHDF5::rebin(std::vector& new_end_times) { +unsigned int +SinglesRatesFromGEHDF5::rebin(std::vector& new_end_times) { const int num_new_slices = new_end_times.size(); const int total_singles_units = SinglesRates::scanner_sptr->get_num_singles_units(); - + // Create the new array of singles data. - Array<2, unsigned int> new_singles = Array<2, unsigned int>(IndexRange2D(0, num_new_slices - 1, - 0, total_singles_units - 1)); - - + Array<2, unsigned int> new_singles = Array<2, unsigned int>(IndexRange2D(0, num_new_slices - 1, 0, total_singles_units - 1)); + // Sort the set of new time slices. std::sort(new_end_times.begin(), new_end_times.end()); // Start with initial time of 0.0 seconds. double start_time = 0; - // Loop over new time slices. - for(unsigned int new_slice = 0 ; new_slice < new_end_times.size(); ++new_slice) { + for (unsigned int new_slice = 0; new_slice < new_end_times.size(); ++new_slice) { // End time for the new time slice. double end_time = new_end_times[new_slice]; // If start time is beyond last end time in original data, then use zeros. - if ( start_time > _times[m_num_time_slices - 1] ) { - for(int singles_bin = 0 ; singles_bin < total_singles_units ; ++singles_bin) { + if (start_time > _times[m_num_time_slices - 1]) { + for (int singles_bin = 0; singles_bin < total_singles_units; ++singles_bin) { new_singles[new_slice][singles_bin] = 0; } } else { // Get the singles rate average between start and end times for all bins. - for(int singles_bin = 0 ; singles_bin < total_singles_units ; ++singles_bin ) { - new_singles[new_slice][singles_bin] = - round(get_singles_rate(singles_bin, start_time, end_time)); + for (int singles_bin = 0; singles_bin < total_singles_units; ++singles_bin) { + new_singles[new_slice][singles_bin] = round(get_singles_rate(singles_bin, start_time, end_time)); } - } - + // Next new time slice starts at the end of this slice. start_time = end_time; - } - - + // Set the singles and times using the new sets. m_singles_sptr.reset(new Array<2, unsigned int>(new_singles)); _times = new_end_times; m_num_time_slices = _times.size(); - - return(m_num_time_slices); -} + return (m_num_time_slices); +} - -std::vector -SinglesRatesFromGEHDF5::get_times() const -{ +std::vector +SinglesRatesFromGEHDF5::get_times() const { return _times; } - - - unsigned int -SinglesRatesFromGEHDF5:: -get_num_time_slices() const { - return(m_num_time_slices); +SinglesRatesFromGEHDF5::get_num_time_slices() const { + return (m_num_time_slices); } - - double -SinglesRatesFromGEHDF5:: -get_singles_time_interval() const { - return(_singles_time_interval); +SinglesRatesFromGEHDF5::get_singles_time_interval() const { + return (_singles_time_interval); } - - - - unsigned int -SinglesRatesFromGEHDF5:: -read_singles_from_listmode_file(const std::string& _listmode_filename) -{ - - unsigned int slice = 0; - - //PW Open the list mode file here. - m_input_sptr.reset(new GEHDF5Wrapper(_listmode_filename)); +SinglesRatesFromGEHDF5::read_singles_from_listmode_file(const std::string& _listmode_filename) { + unsigned int slice = 0; - SinglesRates::scanner_sptr = m_input_sptr->get_scanner_sptr(); - // Get total number of bins for this type of scanner. - const int total_singles_units = SinglesRates::scanner_sptr->get_num_singles_units(); + // PW Open the list mode file here. + m_input_sptr.reset(new GEHDF5Wrapper(_listmode_filename)); - m_input_sptr->initialise_singles_data(); + SinglesRates::scanner_sptr = m_input_sptr->get_scanner_sptr(); + // Get total number of bins for this type of scanner. + const int total_singles_units = SinglesRates::scanner_sptr->get_num_singles_units(); - // Allocate the main array. - m_num_time_slices = m_input_sptr->get_num_singles_samples(); - m_singles_sptr.reset(new Array<2, unsigned int>(IndexRange2D(0, m_num_time_slices - 1, 0, total_singles_units - 1))); - - while ( slice < m_num_time_slices) - { - m_input_sptr->read_singles((*m_singles_sptr)[slice],slice+1); - ++slice; - } - - //PW Modify this bit of code too. - if (slice != m_num_time_slices) - { - error("\nSinglesRatesFromGEHDF5: Couldn't read all records in the file. Read %d of %d. Exiting\n", - slice, m_num_time_slices); - //TODO resize singles to return array with new sizes - } - // AB TODO: if listmode or/and m_num_time_slices>! - _times = std::vector(m_num_time_slices); - if (m_num_time_slices>1) // this is the same as checking if the input file is a listmode file - { - for(unsigned int slice = 0;slice < m_num_time_slices;++slice) - _times[slice] = slice+1.0; - - assert(_times.size()!=0); - _singles_time_interval = _times[1] - _times[0]; - } - else // Then it must be a sinogram, and therefore only has 1 time and 1 interval. - { - TimeFrameDefinitions tf = m_input_sptr->get_exam_info_sptr()->get_time_frame_definitions(); - _times[0]= tf.get_duration(1); - _singles_time_interval = tf.get_duration(1); - } - - - // Return number of time slices read. - return slice; - -} + m_input_sptr->initialise_singles_data(); + // Allocate the main array. + m_num_time_slices = m_input_sptr->get_num_singles_samples(); + m_singles_sptr.reset(new Array<2, unsigned int>(IndexRange2D(0, m_num_time_slices - 1, 0, total_singles_units - 1))); + while (slice < m_num_time_slices) { + m_input_sptr->read_singles((*m_singles_sptr)[slice], slice + 1); + ++slice; + } + // PW Modify this bit of code too. + if (slice != m_num_time_slices) { + error("\nSinglesRatesFromGEHDF5: Couldn't read all records in the file. Read %d of %d. Exiting\n", slice, m_num_time_slices); + // TODO resize singles to return array with new sizes + } + // AB TODO: if listmode or/and m_num_time_slices>! + _times = std::vector(m_num_time_slices); + if (m_num_time_slices > 1) // this is the same as checking if the input file is a listmode file + { + for (unsigned int slice = 0; slice < m_num_time_slices; ++slice) + _times[slice] = slice + 1.0; + + assert(_times.size() != 0); + _singles_time_interval = _times[1] - _times[0]; + } else // Then it must be a sinogram, and therefore only has 1 time and 1 interval. + { + TimeFrameDefinitions tf = m_input_sptr->get_exam_info_sptr()->get_time_frame_definitions(); + _times[0] = tf.get_duration(1); + _singles_time_interval = tf.get_duration(1); + } + // Return number of time slices read. + return slice; +} // Write SinglesRatesFromGEHDF5 to a singles file. -std::ostream& +std::ostream& SinglesRatesFromGEHDF5::write(std::ostream& output) { output << (*m_singles_sptr) << std::endl; @@ -386,98 +310,81 @@ SinglesRatesFromGEHDF5::write(std::ostream& output) { return output; } -//PW Figure this out!Does it need any change? +// PW Figure this out!Does it need any change? float -SinglesRatesFromGEHDF5:: -get_singles_rate(const int singles_bin_index, - const double start_time, const double end_time) const { +SinglesRatesFromGEHDF5::get_singles_rate(const int singles_bin_index, const double start_time, const double end_time) const { - // First Calculate an inclusive range. start_time_slice is the + // First Calculate an inclusive range. start_time_slice is the // the first slice with an ending time greater than start_time. // end_time_slice is the first time slice that ends at, or after, // end_time. int start_slice = this->get_start_time_slice_index(start_time); int end_slice = this->get_end_time_slice_index(end_time); - - + // Total contribution from all slices. double total_singles; - - if ( start_slice == end_slice ) { + if (start_slice == end_slice) { // If the start and end slices are the same then just use that time slice. total_singles = static_cast((*m_singles_sptr)[start_slice][singles_bin_index]); } else { - + // Start and end times for starting and ending slices. double slice_start_time; double slice_end_time; - double included_duration; + double included_duration; double old_duration; double fraction; - - + // Total slices included (including fractional amounts) in the average. float total_slices; - - // Calculate the fraction of the start_slice to include. slice_start_time = get_slice_start(start_slice); slice_end_time = _times[start_slice]; - + old_duration = slice_end_time - slice_start_time; included_duration = slice_end_time - start_time; - + fraction = included_duration / old_duration; - - + // Set the total number of contributing bins to this fraction. total_slices = fraction; - + // Set the total singles so far to be the fraction of the bin. total_singles = fraction * (*m_singles_sptr)[start_slice][singles_bin_index]; - - - + // Calculate the fraction of the end_slice to include. slice_start_time = get_slice_start(end_slice); slice_end_time = _times[end_slice]; - + old_duration = slice_end_time - slice_start_time; included_duration = end_time - slice_start_time; - + fraction = included_duration / old_duration; - + // Add this fraction to the total of the number of bins contributing. total_slices += fraction; - + // Add the fraction of the bin to the running total. total_singles += fraction * (*m_singles_sptr)[end_slice][singles_bin_index]; - - + // Add all intervening slices. - for(int slice = start_slice + 1; slice < end_slice ; ++slice, total_slices += 1.0) { + for (int slice = start_slice + 1; slice < end_slice; ++slice, total_slices += 1.0) { total_singles += (*m_singles_sptr)[slice][singles_bin_index]; } - - + // Divide by total amount of contributing slices. total_singles = total_singles / total_slices; - } - return( static_cast(total_singles) ); - + return (static_cast(total_singles)); } float -SinglesRatesFromGEHDF5:: -get_singles_rate(const DetectionPosition<>& det_pos, - const double start_time, - const double end_time) const -{ - return SinglesRates::get_singles_rate(det_pos, start_time, end_time); +SinglesRatesFromGEHDF5::get_singles_rate(const DetectionPosition<>& det_pos, const double start_time, + const double end_time) const { + return SinglesRates::get_singles_rate(det_pos, start_time, end_time); } /* @@ -486,94 +393,72 @@ get_singles_rate(const DetectionPosition<>& det_pos, * */ +void +SinglesRatesFromGEHDF5::set_time_interval() { - - - -void -SinglesRatesFromGEHDF5:: -set_time_interval() { - - // Run through the _times vector and calculate an average difference + // Run through the _times vector and calculate an average difference // between the starts of consecutive time slices. - + // Min and max differences (slice durations). double min_diff = 0; double max_diff = 0; double total = 0; - for(std::vector::const_iterator t = _times.begin(); t < _times.end() - 1; ++t) { + for (std::vector::const_iterator t = _times.begin(); t < _times.end() - 1; ++t) { double diff = *(t + 1) - *t; total += diff; - if ( min_diff == 0 || diff < min_diff ) { + if (min_diff == 0 || diff < min_diff) { min_diff = diff; } - if ( diff > max_diff ) { + if (diff > max_diff) { max_diff = diff; } } _singles_time_interval = total / (_times.size() - 1); - if ( (max_diff - min_diff) / (_singles_time_interval) > MAX_INTERVAL_DIFFERENCE ) { + if ((max_diff - min_diff) / (_singles_time_interval) > MAX_INTERVAL_DIFFERENCE) { // Slice durations are not consistent enough to be considered the same. _singles_time_interval = 0; } - } - // get slice start time. -double -SinglesRatesFromGEHDF5:: -get_slice_start(int slice_index) const { +double +SinglesRatesFromGEHDF5::get_slice_start(int slice_index) const { - if ( slice_index >= m_num_time_slices ) { + if (slice_index >= m_num_time_slices) { slice_index = m_num_time_slices - 1; } - - if ( slice_index == 0 ) { - return(0); + + if (slice_index == 0) { + return (0); } else { - return(_times[slice_index - 1]); + return (_times[slice_index - 1]); } } - - - - - -void -SinglesRatesFromGEHDF5:: -initialise_keymap() -{ -//PW Modify this to change sgl to listmode +void +SinglesRatesFromGEHDF5::initialise_keymap() { + // PW Modify this to change sgl to listmode parser.add_start_key("Singles Rates From GE HDF5 listmode File"); parser.add_key("listmode_filename", &_listmode_filename); parser.add_stop_key("End Singles Rates From GE HDF5 listmode File"); } -bool -SinglesRatesFromGEHDF5:: -post_processing() -{ +bool +SinglesRatesFromGEHDF5::post_processing() { read_singles_from_listmode_file(_listmode_filename); return false; } - -void -SinglesRatesFromGEHDF5::set_defaults() -{ +void +SinglesRatesFromGEHDF5::set_defaults() { _listmode_filename = ""; } -} // namespace -} +} // namespace RDF_HDF5 +} // namespace GE END_NAMESPACE_STIR - - - diff --git a/src/data_buildblock/SinglesRatesFromSglFile.cxx b/src/data_buildblock/SinglesRatesFromSglFile.cxx index d839231809..15051ac6c9 100644 --- a/src/data_buildblock/SinglesRatesFromSglFile.cxx +++ b/src/data_buildblock/SinglesRatesFromSglFile.cxx @@ -32,8 +32,8 @@ #include #ifdef HAVE_LLN_MATRIX -#include "ecat_model.h" -#include "stir/IO/stir_ecat7.h" +# include "ecat_model.h" +# include "stir/IO/stir_ecat7.h" #endif #include #include @@ -44,39 +44,27 @@ using std::streampos; using std::ios; #endif - - START_NAMESPACE_STIR START_NAMESPACE_ECAT START_NAMESPACE_ECAT7 -const unsigned -SinglesRatesFromSglFile::SIZE_OF_SINGLES_RECORD = 4*128; - -const char * const -SinglesRatesFromSglFile::registered_name = "Singles From Sgl File"; +const unsigned SinglesRatesFromSglFile::SIZE_OF_SINGLES_RECORD = 4 * 128; +const char* const SinglesRatesFromSglFile::registered_name = "Singles From Sgl File"; const double MAX_INTERVAL_DIFFERENCE = 0.05; // 5% max difference. - -static inline -unsigned long int -convert_4_bytes(unsigned char * buffer) -{ +static inline unsigned long int +convert_4_bytes(unsigned char* buffer) { // The order from the file is always big endian. The native order doesn't matter // when converting by multiplying and adding the individual bytes. - //if (ByteOrder::get_native_order() == ByteOrder::big_endian) + // if (ByteOrder::get_native_order() == ByteOrder::big_endian) // return buffer[0] + 256UL*(buffer[1] + 256UL*(buffer[2] + 256UL*buffer[3])); - //else - return buffer[3] + 256UL*(buffer[2] + 256UL*(buffer[1] + 256UL*buffer[0])); - + // else + return buffer[3] + 256UL * (buffer[2] + 256UL * (buffer[1] + 256UL * buffer[0])); } - - -static inline -void -convert_int_to_4_bytes(unsigned long int val, unsigned char *buffer) { +static inline void +convert_int_to_4_bytes(unsigned long int val, unsigned char* buffer) { // Big endian buffer[0] = (val & 0xff000000) >> 24; buffer[1] = (val & 0x00ff0000) >> 16; @@ -84,40 +72,30 @@ convert_int_to_4_bytes(unsigned long int val, unsigned char *buffer) { buffer[3] = (val & 0x000000ff); } - - - - // Constructor -SinglesRatesFromSglFile:: -SinglesRatesFromSglFile() -{} - +SinglesRatesFromSglFile::SinglesRatesFromSglFile() {} // Generate a FramesSinglesRate - containing the average rates // for a frame begining at start_time and ending at end_time. FrameSinglesRates -SinglesRatesFromSglFile:: -get_rates_for_frame(double start_time, - double end_time) const { +SinglesRatesFromSglFile::get_rates_for_frame(double start_time, double end_time) const { int num_singles_units = scanner_sptr->get_num_singles_units(); // Create a temporary vector std::vector average_singles_rates(num_singles_units); - // Loop over all bins. - for(int singles_bin = 0 ; singles_bin < num_singles_units ; ++singles_bin) { + for (int singles_bin = 0; singles_bin < num_singles_units; ++singles_bin) { average_singles_rates[singles_bin] = get_singles_rate(singles_bin, start_time, end_time); } - + // Determine that start and end slice indices. int start_slice = get_start_time_slice_index(start_time); int end_slice = get_end_time_slice_index(end_time); - + double frame_start_time; - if ( start_slice == 0 ) { + if (start_slice == 0) { frame_start_time = _times[0] - _singles_time_interval; } else { frame_start_time = _times[start_slice - 1]; @@ -126,233 +104,175 @@ get_rates_for_frame(double start_time, double frame_end_time = _times[end_slice]; // Create temp FrameSinglesRate object - FrameSinglesRates frame_rates(average_singles_rates, - frame_start_time, - frame_end_time, - scanner_sptr); - - return(frame_rates); - -} - - - + FrameSinglesRates frame_rates(average_singles_rates, frame_start_time, frame_end_time, scanner_sptr); + return (frame_rates); +} // Get time slice index. // Returns the index of the slice that contains the specified time. int -SinglesRatesFromSglFile:: -get_end_time_slice_index(double t) const { +SinglesRatesFromSglFile::get_end_time_slice_index(double t) const { int slice_index = 0; // Start with an initial estimate. - if ( _singles_time_interval != 0 ) { + if (_singles_time_interval != 0) { slice_index = static_cast(floor(t / _singles_time_interval)); } - if ( slice_index >= _num_time_slices ) { + if (slice_index >= _num_time_slices) { slice_index = _num_time_slices - 1; - } - + } // Check estimate and determine whether to look further or backwards. // Note that we could just move fowards first and then backwards but this // method is more intuitive. - if ( _times[slice_index] < t ) { - + if (_times[slice_index] < t) { + // Check forwards. - while( slice_index < _num_time_slices - 1 && - _times[slice_index] < t ) { + while (slice_index < _num_time_slices - 1 && _times[slice_index] < t) { slice_index++; } } else { // Check backwards. - while( slice_index > 0 && _times[slice_index - 1] >= t ) { + while (slice_index > 0 && _times[slice_index - 1] >= t) { slice_index--; } - } - - return(slice_index); -} - - + return (slice_index); +} // Get time slice index. // Returns first slice ending _after_ t. int -SinglesRatesFromSglFile:: -get_start_time_slice_index(double t) const { +SinglesRatesFromSglFile::get_start_time_slice_index(double t) const { int slice_index = 0; // Start with an initial estimate. - if ( _singles_time_interval != 0 ) { + if (_singles_time_interval != 0) { slice_index = static_cast(floor(t / _singles_time_interval)); } - if ( slice_index >= _num_time_slices ) { + if (slice_index >= _num_time_slices) { slice_index = _num_time_slices - 1; - } - + } // Check estimate and determine whether to look further or backwards. // Note that we could just move fowards first and then backwards but this // method is more intuitive. - if ( _times[slice_index] < t ) { - + if (_times[slice_index] < t) { + // Check forwards. - while( slice_index < _num_time_slices - 1 && - _times[slice_index] <= t ) { + while (slice_index < _num_time_slices - 1 && _times[slice_index] <= t) { slice_index++; } } else { // Check backwards. - while( slice_index > 0 && _times[slice_index - 1] > t ) { + while (slice_index > 0 && _times[slice_index - 1] > t) { slice_index--; } - } - - return(slice_index); -} - - - - + return (slice_index); +} // Get rates using time slice and singles bin indices. -int -SinglesRatesFromSglFile:: -get_singles_rate(int singles_bin_index, int time_slice) const { - +int +SinglesRatesFromSglFile::get_singles_rate(int singles_bin_index, int time_slice) const { + // Check ranges. int total_singles_units = scanner_sptr->get_num_singles_units(); - - if ( singles_bin_index < 0 || singles_bin_index >= total_singles_units || - time_slice < 0 || time_slice >= _num_time_slices ) { - return(0); + + if (singles_bin_index < 0 || singles_bin_index >= total_singles_units || time_slice < 0 || time_slice >= _num_time_slices) { + return (0); } else { return _singles[time_slice][singles_bin_index]; } - } - - // Set a singles rate by bin index and time slice. -void -SinglesRatesFromSglFile:: -set_singles_rate(int singles_bin_index, int time_slice, int new_rate) { - +void +SinglesRatesFromSglFile::set_singles_rate(int singles_bin_index, int time_slice, int new_rate) { + int total_singles_units = scanner_sptr->get_num_singles_units(); - - if ( singles_bin_index >= 0 && singles_bin_index < total_singles_units && - time_slice >= 0 && time_slice < _num_time_slices ) { + + if (singles_bin_index >= 0 && singles_bin_index < total_singles_units && time_slice >= 0 && time_slice < _num_time_slices) { _singles[time_slice][singles_bin_index] = new_rate; } } - - - -int -SinglesRatesFromSglFile:: -rebin(std::vector& new_end_times) { +int +SinglesRatesFromSglFile::rebin(std::vector& new_end_times) { const int num_new_slices = new_end_times.size(); const int total_singles_units = scanner_sptr->get_num_singles_units(); - + // Create the new array of singles data. - Array<2, int> new_singles = Array<2, int>(IndexRange2D(0, num_new_slices - 1, - 0, total_singles_units - 1)); - - + Array<2, int> new_singles = Array<2, int>(IndexRange2D(0, num_new_slices - 1, 0, total_singles_units - 1)); + // Sort the set of new time slices. std::sort(new_end_times.begin(), new_end_times.end()); // Start with initial time of 0.0 seconds. double start_time = 0; - // Loop over new time slices. - for(unsigned int new_slice = 0 ; new_slice < new_end_times.size(); ++new_slice) { + for (unsigned int new_slice = 0; new_slice < new_end_times.size(); ++new_slice) { // End time for the new time slice. double end_time = new_end_times[new_slice]; // If start time is beyond last end time in original data, then use zeros. - if ( start_time > _times[_num_time_slices - 1] ) { - for(int singles_bin = 0 ; singles_bin < total_singles_units ; ++singles_bin) { + if (start_time > _times[_num_time_slices - 1]) { + for (int singles_bin = 0; singles_bin < total_singles_units; ++singles_bin) { new_singles[new_slice][singles_bin] = 0; } } else { // Get the singles rate average between start and end times for all bins. - for(int singles_bin = 0 ; singles_bin < total_singles_units ; ++singles_bin ) { - new_singles[new_slice][singles_bin] = - round(get_singles_rate(singles_bin, start_time, end_time)); + for (int singles_bin = 0; singles_bin < total_singles_units; ++singles_bin) { + new_singles[new_slice][singles_bin] = round(get_singles_rate(singles_bin, start_time, end_time)); } - } - + // Next new time slice starts at the end of this slice. start_time = end_time; - } - - + // Set the singles and times using the new sets. _singles = new_singles; _times = new_end_times; _num_time_slices = _times.size(); - - return(_num_time_slices); -} + return (_num_time_slices); +} - -std::vector -SinglesRatesFromSglFile::get_times() const -{ +std::vector +SinglesRatesFromSglFile::get_times() const { return _times; } - - - int -SinglesRatesFromSglFile:: -get_num_time_slices() const { - return(_num_time_slices); +SinglesRatesFromSglFile::get_num_time_slices() const { + return (_num_time_slices); } - - double -SinglesRatesFromSglFile:: -get_singles_time_interval() const { - return(_singles_time_interval); +SinglesRatesFromSglFile::get_singles_time_interval() const { + return (_singles_time_interval); } - - - - int -SinglesRatesFromSglFile:: -read_singles_from_sgl_file(const std::string& sgl_filename) -{ +SinglesRatesFromSglFile::read_singles_from_sgl_file(const std::string& sgl_filename) { int slice = 0; @@ -367,95 +287,78 @@ read_singles_from_sgl_file(const std::string& sgl_filename) error("\nSinglesRatesFromSglFile: Couldn't open \"%s\".\n", sgl_filename.c_str()); } - - //first find out the size of the file + // first find out the size of the file singles_file.seekg(0, ios::end); const streampos end_stream_position = singles_file.tellg(); if (!singles_file) { - error("\nSinglesRatesFromSglFile: Couldn't seek to end of file %s.",sgl_filename.c_str()); + error("\nSinglesRatesFromSglFile: Couldn't seek to end of file %s.", sgl_filename.c_str()); } - // go to the beginning and read the singles header singles_file.seekg(0, ios::beg); - + if (!singles_file) { - error("\nSinglesRatesFromSglFile: Couldn't seek to start of file %s.",sgl_filename.c_str()); + error("\nSinglesRatesFromSglFile: Couldn't seek to start of file %s.", sgl_filename.c_str()); } - { char buffer[sizeof(Main_header)]; - singles_file.read(buffer,sizeof(_singles_main_header)); - if (!singles_file) - { - error("\nSinglesRatesFromSglFile: Couldn't read main_header from %s.",sgl_filename.c_str()); - } - else - { + singles_file.read(buffer, sizeof(_singles_main_header)); + if (!singles_file) { + error("\nSinglesRatesFromSglFile: Couldn't read main_header from %s.", sgl_filename.c_str()); + } else { unmap_main_header(buffer, &_singles_main_header); ecat::ecat7::find_scanner(scanner_sptr, _singles_main_header); } } - if (scanner_sptr->get_type() != Scanner::E966) { warning("check SinglesRatesFromSglFile for non-966\n"); } - // Get total number of bins for this type of scanner. const int total_singles_units = scanner_sptr->get_num_singles_units(); // Calculate number of time slices from the length of the data (file size minus header). - _num_time_slices = - static_cast((end_stream_position - static_cast(512)) / - SIZE_OF_SINGLES_RECORD); + _num_time_slices = static_cast((end_stream_position - static_cast(512)) / SIZE_OF_SINGLES_RECORD); - // Allocate the main array. + // Allocate the main array. _singles = Array<2, int>(IndexRange2D(0, _num_time_slices - 1, 0, total_singles_units - 1)); - singles_file.seekg(512, ios::beg); - + while (singles_file && slice < _num_time_slices) { - + // Temporary space to store file data. sgl_str singles_str; - { unsigned char buffer[SIZE_OF_SINGLES_RECORD]; - - singles_file.read(reinterpret_cast(buffer), SIZE_OF_SINGLES_RECORD); + + singles_file.read(reinterpret_cast(buffer), SIZE_OF_SINGLES_RECORD); if (!singles_file) { - + if (!singles_file.eof()) { - warning("Error reading singles file record %d. Stopped reading from this point.", - slice); + warning("Error reading singles file record %d. Stopped reading from this point.", slice); } break; } singles_str.time = convert_4_bytes(buffer); - singles_str.num_sgl = convert_4_bytes(buffer+4); - - for (unsigned int i = 0; i < ( SIZE_OF_SINGLES_RECORD - 8)/4; ++i) { - singles_str.sgl[i] = convert_4_bytes(buffer+8+4*i); + singles_str.num_sgl = convert_4_bytes(buffer + 4); + + for (unsigned int i = 0; i < (SIZE_OF_SINGLES_RECORD - 8) / 4; ++i) { + singles_str.sgl[i] = convert_4_bytes(buffer + 8 + 4 * i); } } - if (singles_str.num_sgl != total_singles_units) { - error("Number of singles units should be %d, but is %d in singles file", - total_singles_units, singles_str.num_sgl); + error("Number of singles units should be %d, but is %d in singles file", total_singles_units, singles_str.num_sgl); } - - // Copy the singles values to the main array. - + // Note. The singles values are arranged num_axial sets of num_transaxial // values. // @@ -468,43 +371,35 @@ read_singles_from_sgl_file(const std::string& sgl_filename) for (int singles_bin = 0; singles_bin < total_singles_units; ++singles_bin) { _singles[slice][singles_bin] = static_cast(singles_str.sgl[singles_bin]); } - - + // singles in the sgl file given in msec.multiply with 0.001 to convert into sec. - _times.push_back(singles_str.time*0.001); + _times.push_back(singles_str.time * 0.001); // Add the last two words - total prompts and total randoms. _total_prompts.push_back(singles_str.sgl[total_singles_units]); _total_randoms.push_back(singles_str.sgl[total_singles_units + 1]); - + // Increment the slice index. ++slice; - } - - assert(_times.size()!=0); + + assert(_times.size() != 0); _singles_time_interval = _times[1] - _times[0]; - - if (slice != _num_time_slices) - { + + if (slice != _num_time_slices) { error("\nSinglesRatesFromSglFile: Couldn't read all records in the .sgl file %s. Read %d of %d. Exiting\n", - sgl_filename.c_str(), slice, _num_time_slices); - //TODO resize singles to return array with new sizes + sgl_filename.c_str(), slice, _num_time_slices); + // TODO resize singles to return array with new sizes } #endif // Return number of time slices read. - return slice; - + return slice; } - - - - // Write SinglesRatesFromSglFile to a singles file. -std::ostream& +std::ostream& SinglesRatesFromSglFile::write(std::ostream& output) { #ifndef HAVE_LLN_MATRIX @@ -512,36 +407,35 @@ SinglesRatesFromSglFile::write(std::ostream& output) { error("Compiled without ECAT7 support\n"); #else - + char header_buffer[SIZE_OF_SINGLES_RECORD]; unsigned char buffer[SIZE_OF_SINGLES_RECORD]; - + memset(header_buffer, 0, SIZE_OF_SINGLES_RECORD); // Write header to buffer. map_main_header(header_buffer, &(this->_singles_main_header)); - + // Write buffer to output. output.write(header_buffer, SIZE_OF_SINGLES_RECORD); if (!output) { error("\nSinglesRatesFromSglFile: Failed to write to output."); - return(output); + return (output); } - - + // Empty buffer. memset(buffer, 0, SIZE_OF_SINGLES_RECORD); - + int total_singles_units = scanner_sptr->get_num_singles_units(); unsigned long millisecs; - + // Write 512 byte blocks. One for each time slice recorded. - for(int slice = 0 ; slice < _num_time_slices ; ++slice) { - + for (int slice = 0; slice < _num_time_slices; ++slice) { + // Write data to buffer. millisecs = static_cast(floor(_times[slice] * 1000.0)); - + // Time and number of singles units convert_int_to_4_bytes(millisecs, buffer); convert_int_to_4_bytes(total_singles_units, buffer + 4); @@ -549,219 +443,169 @@ SinglesRatesFromSglFile::write(std::ostream& output) { // Singles units // Note that the order of values in _singles is the same as that of the file. // This may not be the case for other file formats. - for(int singles_bin = 0 ; singles_bin < total_singles_units ; ++singles_bin) { + for (int singles_bin = 0; singles_bin < total_singles_units; ++singles_bin) { convert_int_to_4_bytes(_singles[slice][singles_bin], buffer + ((2 + singles_bin) * 4)); } - + // Total prompts and total trues convert_int_to_4_bytes(_total_prompts[slice], buffer + ((2 + total_singles_units) * 4)); convert_int_to_4_bytes(_total_randoms[slice], buffer + ((2 + total_singles_units + 1) * 4)); - - + // Write buffer to output. - output.write(reinterpret_cast(buffer), SIZE_OF_SINGLES_RECORD); - + output.write(reinterpret_cast(buffer), SIZE_OF_SINGLES_RECORD); + if (!output) { error("\nSinglesRatesFromSglFile: Failed to write to output."); break; } - } - #endif return output; } - - float -SinglesRatesFromSglFile:: -get_singles_rate(const int singles_bin_index, - const double start_time, const double end_time) const { +SinglesRatesFromSglFile::get_singles_rate(const int singles_bin_index, const double start_time, const double end_time) const { - // First Calculate an inclusive range. start_time_slice is the + // First Calculate an inclusive range. start_time_slice is the // the first slice with an ending time greater than start_time. // end_time_slice is the first time slice that ends at, or after, // end_time. int start_slice = this->get_start_time_slice_index(start_time); int end_slice = this->get_end_time_slice_index(end_time); - - + // Total contribution from all slices. double total_singles; - - if ( start_slice == end_slice ) { + if (start_slice == end_slice) { // If the start and end slices are the same then just use that time slice. total_singles = static_cast(_singles[start_slice][singles_bin_index]); } else { - + // Start and end times for starting and ending slices. double slice_start_time; double slice_end_time; - double included_duration; + double included_duration; double old_duration; double fraction; - - + // Total slices included (including fractional amounts) in the average. float total_slices; - - // Calculate the fraction of the start_slice to include. slice_start_time = get_slice_start(start_slice); slice_end_time = _times[start_slice]; - + old_duration = slice_end_time - slice_start_time; included_duration = slice_end_time - start_time; - + fraction = included_duration / old_duration; - - + // Set the total number of contributing bins to this fraction. total_slices = fraction; - + // Set the total singles so far to be the fraction of the bin. total_singles = fraction * _singles[start_slice][singles_bin_index]; - - - + // Calculate the fraction of the end_slice to include. slice_start_time = get_slice_start(end_slice); slice_end_time = _times[end_slice]; - + old_duration = slice_end_time - slice_start_time; included_duration = end_time - slice_start_time; - + fraction = included_duration / old_duration; - + // Add this fraction to the total of the number of bins contributing. total_slices += fraction; - + // Add the fraction of the bin to the running total. total_singles += fraction * _singles[end_slice][singles_bin_index]; - - + // Add all intervening slices. - for(int slice = start_slice + 1; slice < end_slice ; ++slice, total_slices += 1.0) { + for (int slice = start_slice + 1; slice < end_slice; ++slice, total_slices += 1.0) { total_singles += _singles[slice][singles_bin_index]; } - - + // Divide by total amount of contributing slices. total_singles = total_singles / total_slices; - } - return( static_cast(total_singles) ); - + return (static_cast(total_singles)); } - - /* * * Private methods. * */ +void +SinglesRatesFromSglFile::set_time_interval() { - - - -void -SinglesRatesFromSglFile:: -set_time_interval() { - - // Run through the _times vector and calculate an average difference + // Run through the _times vector and calculate an average difference // between the starts of consecutive time slices. - + // Min and max differences (slice durations). double min_diff = 0; double max_diff = 0; double total = 0; - for(std::vector::const_iterator t = _times.begin(); t < _times.end() - 1; ++t) { - double diff = *(t + 1) - *t; + for (std::vector::const_iterator t = _times.begin(); t < _times.end() - 1; ++t) { + double diff = *(t + 1) - *t; total += diff; - if ( min_diff == 0 || diff < min_diff ) { + if (min_diff == 0 || diff < min_diff) { min_diff = diff; } - if ( diff > max_diff ) { + if (diff > max_diff) { max_diff = diff; } } - + _singles_time_interval = total / (_times.size() - 1); - - if ( (max_diff - min_diff) / (_singles_time_interval) > MAX_INTERVAL_DIFFERENCE ) { + + if ((max_diff - min_diff) / (_singles_time_interval) > MAX_INTERVAL_DIFFERENCE) { // Slice durations are not consistent enough to be considered the same. _singles_time_interval = 0; } - } - // get slice start time. -double -SinglesRatesFromSglFile:: -get_slice_start(int slice_index) const { +double +SinglesRatesFromSglFile::get_slice_start(int slice_index) const { - if ( slice_index >= _num_time_slices ) { + if (slice_index >= _num_time_slices) { slice_index = _num_time_slices - 1; } - - if ( slice_index == 0 ) { - return(0); + + if (slice_index == 0) { + return (0); } else { - return(_times[slice_index - 1]); + return (_times[slice_index - 1]); } } - - - - - -void -SinglesRatesFromSglFile:: -initialise_keymap() -{ +void +SinglesRatesFromSglFile::initialise_keymap() { parser.add_start_key("Singles Rates From Sgl File"); parser.add_key("sgl_filename", &_sgl_filename); parser.add_stop_key("End Singles Rates From Sgl File"); } -bool -SinglesRatesFromSglFile:: -post_processing() -{ +bool +SinglesRatesFromSglFile::post_processing() { read_singles_from_sgl_file(_sgl_filename); return false; } - -void -SinglesRatesFromSglFile::set_defaults() -{ +void +SinglesRatesFromSglFile::set_defaults() { _sgl_filename = ""; } - - - - - - - END_NAMESPACE_ECAT7 END_NAMESPACE_ECAT END_NAMESPACE_STIR - - - diff --git a/src/data_buildblock/data_buildblock_registries.cxx b/src/data_buildblock/data_buildblock_registries.cxx index b021e683ff..13b9589346 100644 --- a/src/data_buildblock/data_buildblock_registries.cxx +++ b/src/data_buildblock/data_buildblock_registries.cxx @@ -23,15 +23,15 @@ \brief File that registers all stir::RegisterObject children in data_buildblock \author Kris Thielemans - + */ #include "stir/common.h" #ifdef HAVE_LLN_MATRIX -#include "stir/data/SinglesRatesFromECAT7.h" -#include "stir/data/SinglesRatesFromSglFile.h" +# include "stir/data/SinglesRatesFromECAT7.h" +# include "stir/data/SinglesRatesFromSglFile.h" #endif #ifdef HAVE_HDF5 -#include "stir/data/SinglesRatesFromGEHDF5.h" +# include "stir/data/SinglesRatesFromGEHDF5.h" #endif START_NAMESPACE_STIR #ifdef HAVE_LLN_MATRIX @@ -47,4 +47,3 @@ static GE::RDF_HDF5::SinglesRatesFromGEHDF5::RegisterIt dummy300; #endif END_NAMESPACE_STIR - diff --git a/src/display/display_array.cxx b/src/display/display_array.cxx index cc75e6019e..c39461077b 100644 --- a/src/display/display_array.cxx +++ b/src/display/display_array.cxx @@ -16,15 +16,15 @@ */ /*! - \file + \file \ingroup display - + \brief Functions to display stir::Array objects (2d and 3d) and stir::RelatedViewgrams - + \author Kris Thielemans \author PARAPET project - - + + \see display.h for some comments on the interface. @@ -33,9 +33,9 @@
*/ -class ScatterSimulation : public RegisteredObject -{ +class ScatterSimulation : public RegisteredObject { public: + //! Default constructor + ScatterSimulation(); + + virtual ~ScatterSimulation(); + + virtual Succeeded process_data(); + //! gives method information + virtual std::string method_info() const = 0; + //! prompts the user to enter parameter values manually + virtual void ask_parameters(); + //! \name check functions + //@{ + inline bool has_template_proj_data_info() const { return !stir::is_null_ptr(proj_data_info_cyl_noarc_cor_sptr); } + //! Returns true if template_exam_info_sptr has been set. + inline bool has_exam_info() const { return !stir::is_null_ptr(template_exam_info_sptr); } + //@} - //! Default constructor - ScatterSimulation(); - - virtual ~ScatterSimulation(); - - virtual Succeeded process_data(); - //! gives method information - virtual std::string method_info() const = 0; - //! prompts the user to enter parameter values manually - virtual void ask_parameters(); - //! \name check functions - //@{ - inline bool has_template_proj_data_info() const - { return !stir::is_null_ptr(proj_data_info_cyl_noarc_cor_sptr); } - //! Returns true if template_exam_info_sptr has been set. - inline bool has_exam_info() const - { return !stir::is_null_ptr(template_exam_info_sptr);} - //@} - - //! \name get functions - //@{ - shared_ptr - get_output_proj_data_sptr() const; - - inline int get_num_scatter_points() const - { return static_cast(this->scatt_points_vector.size());} - //! Get the template ProjDataInfo - shared_ptr get_template_proj_data_info_sptr() const; - //! Get the ExamInfo - shared_ptr get_exam_info_sptr() const; - - const DiscretisedDensity<3,float>& get_activity_image() const; - const DiscretisedDensity<3,float>& get_attenuation_image() const; - const DiscretisedDensity<3,float>& get_attenuation_image_for_scatter_points() const; - //! \deprecated - shared_ptr > get_density_image_for_scatter_points_sptr() const; - //@} - - //! \name set functions - //@{ - - void set_template_proj_data_info(const std::string&); - - void set_template_proj_data_info(const ProjDataInfo&); - - void set_activity_image_sptr(const shared_ptr >); - - void set_activity_image(const std::string& filename); - //! \details Since July 2016, the information for the energy window and energy - //! resolution are stored in ExamInfo. - void set_exam_info(const ExamInfo&); - void set_exam_info_sptr(const shared_ptr); - - void set_output_proj_data_sptr(shared_ptr); - - void set_density_image_sptr(const shared_ptr >); - - void set_density_image(const std::string&); - //! This function depends on the ProjDataInfo of the scanner. - //! You first have to set that. - void set_output_proj_data(const std::string&); - - void - set_output_proj_data_sptr(const shared_ptr, - const shared_ptr, - const std::string &); - - void set_density_image_for_scatter_points_sptr(shared_ptr >); - - void set_image_downsample_factors(float factor_xy = 1.f, float factor_z = 1.f, - int _size_zoom_xy = -1, int _size_zoom_z = -1); - //! set_density_image_for_scatter_points - void set_density_image_for_scatter_points(const std::string&); - //! set the attenuation threshold - void set_attenuation_threshold(const float); - //! The scattering point in the voxel will be chosen randomly, instead of choosing the centre. - /*! This was first recommended by Watson. It is recommended to leave this on, as otherwise - discretisation errors are more obvious. - - Note that the random generator is seeded via date/time, so re-running the scatter - simulation will give a slightly different result if this boolean is on. - */ - void set_randomly_place_scatter_points(const bool); - - void set_cache_enabled(const bool); - - //@} - - //! This function is a less powerfull tool than directly zooming the image. - //! However it will check that the downsampling is done in manner compatible with the - //! ScatterSimulation. - void downsample_density_image_for_scatter_points(float _zoom_xy, float _zoom_z, - int _size_xy = -1, int _size_z = -1); - - //! Downsample the scanner keeping the total axial length the same. - /*! If \c new_num_rings<=0, use rings of approximately 2 cm thickness. - If \c new_num_dets <=0, use the default set (currently set in set_defaults()) - */ - Succeeded downsample_scanner(int new_num_rings = -1, int new_num_dets = -1); - //! Downsamples activity and attenuation images to voxel sizes appropriate for the (downsampled) scanner. - /*! This step is not necessary but could result in a speed-up in computing the line integrals. - It also avoids problems with too high resolution images compared to the downsampled scanner. - - Another way to resolve that is to smooth the images before the scatter simulation. - This is currently not implemented in this class. - \warning This function should be called after having set all data. - */ - Succeeded downsample_images_to_scanner_size(); - - //! \name Compton scatter cross sections - //@{ - static - inline float - dif_Compton_cross_section(const float cos_theta, float energy); - - static - inline float - total_Compton_cross_section(float energy); - - static - inline float - photon_energy_after_Compton_scatter(const float cos_theta, const float energy); - - static - inline float - photon_energy_after_Compton_scatter_511keV(const float cos_theta); - - static - inline float - total_Compton_cross_section_relative_to_511keV(const float energy); - //@} - - virtual Succeeded set_up(); - - //! Output the log of the process. - virtual void write_log(const double simulation_time, const float total_scatter); - + //! \name get functions + //@{ + shared_ptr get_output_proj_data_sptr() const; -protected: + inline int get_num_scatter_points() const { return static_cast(this->scatt_points_vector.size()); } + //! Get the template ProjDataInfo + shared_ptr get_template_proj_data_info_sptr() const; + //! Get the ExamInfo + shared_ptr get_exam_info_sptr() const; + + const DiscretisedDensity<3, float>& get_activity_image() const; + const DiscretisedDensity<3, float>& get_attenuation_image() const; + const DiscretisedDensity<3, float>& get_attenuation_image_for_scatter_points() const; + //! \deprecated + shared_ptr> get_density_image_for_scatter_points_sptr() const; + //@} + + //! \name set functions + //@{ + + void set_template_proj_data_info(const std::string&); + + void set_template_proj_data_info(const ProjDataInfo&); + + void set_activity_image_sptr(const shared_ptr>); + + void set_activity_image(const std::string& filename); + //! \details Since July 2016, the information for the energy window and energy + //! resolution are stored in ExamInfo. + void set_exam_info(const ExamInfo&); + void set_exam_info_sptr(const shared_ptr); + + void set_output_proj_data_sptr(shared_ptr); + + void set_density_image_sptr(const shared_ptr>); + + void set_density_image(const std::string&); + //! This function depends on the ProjDataInfo of the scanner. + //! You first have to set that. + void set_output_proj_data(const std::string&); + + void set_output_proj_data_sptr(const shared_ptr, const shared_ptr, const std::string&); - //! computes scatter for one viewgram - /*! \return total scatter estimated for this viewgram */ - virtual double - process_data_for_view_segment_num(const ViewSegmentNumbers& vs_num); + void set_density_image_for_scatter_points_sptr(shared_ptr>); - float - compute_emis_to_det_points_solid_angle_factor(const CartesianCoordinate3D& emis_point, - const CartesianCoordinate3D& detector_coord); + void set_image_downsample_factors(float factor_xy = 1.f, float factor_z = 1.f, int _size_zoom_xy = -1, int _size_zoom_z = -1); + //! set_density_image_for_scatter_points + void set_density_image_for_scatter_points(const std::string&); + //! set the attenuation threshold + void set_attenuation_threshold(const float); + //! The scattering point in the voxel will be chosen randomly, instead of choosing the centre. + /*! This was first recommended by Watson. It is recommended to leave this on, as otherwise + discretisation errors are more obvious. + Note that the random generator is seeded via date/time, so re-running the scatter + simulation will give a slightly different result if this boolean is on. + */ + void set_randomly_place_scatter_points(const bool); + void set_cache_enabled(const bool); - virtual void set_defaults(); - virtual void initialise_keymap(); - //! \warning post_processing will set everything that has a file name in - //! the par file. The corresponding set functions should be used either - //! for files that are not stored in the drive. - virtual bool post_processing(); + //@} - enum image_type{act_image_type, att_image_type}; - struct ScatterPoint - { - CartesianCoordinate3D coord; - float mu_value; - }; + //! This function is a less powerfull tool than directly zooming the image. + //! However it will check that the downsampling is done in manner compatible with the + //! ScatterSimulation. + void downsample_density_image_for_scatter_points(float _zoom_xy, float _zoom_z, int _size_xy = -1, int _size_z = -1); - std::vector< ScatterPoint> scatt_points_vector; + //! Downsample the scanner keeping the total axial length the same. + /*! If \c new_num_rings<=0, use rings of approximately 2 cm thickness. + If \c new_num_dets <=0, use the default set (currently set in set_defaults()) + */ + Succeeded downsample_scanner(int new_num_rings = -1, int new_num_dets = -1); + //! Downsamples activity and attenuation images to voxel sizes appropriate for the (downsampled) scanner. + /*! This step is not necessary but could result in a speed-up in computing the line integrals. + It also avoids problems with too high resolution images compared to the downsampled scanner. - float scatter_volume; + Another way to resolve that is to smooth the images before the scatter simulation. + This is currently not implemented in this class. + \warning This function should be called after having set all data. + */ + Succeeded downsample_images_to_scanner_size(); - //! find scatter points - /*! This function sets scatt_points_vector and scatter_volume. It will also - remove any cached integrals as they would be incorrect otherwise. - */ - void - sample_scatter_points(); + //! \name Compton scatter cross sections + //@{ + static inline float dif_Compton_cross_section(const float cos_theta, float energy); - //! remove cached attenuation integrals - /*! should be used before recalculating scatter for a new attenuation image or - when changing the sampling of the detector etc */ - virtual void remove_cache_for_integrals_over_attenuation(); + static inline float total_Compton_cross_section(float energy); - //! reset cached activity integrals - /*! should be used before recalculating scatter for a new activity image or - when changing the sampling of the detector etc */ - virtual void remove_cache_for_integrals_over_activity(); + static inline float photon_energy_after_Compton_scatter(const float cos_theta, const float energy); - /** \name detection related functions - * - * @{ - */ + static inline float photon_energy_after_Compton_scatter_511keV(const float cos_theta); - float detection_efficiency(const float energy) const; + static inline float total_Compton_cross_section_relative_to_511keV(const float energy); + //@} + virtual Succeeded set_up(); - //! maximum angle to consider above which detection after Compton scatter is considered too small - static - float - max_cos_angle(const float low, const float approx, const float resolution_at_511keV); + //! Output the log of the process. + virtual void write_log(const double simulation_time, const float total_scatter); - //! mimumum energy to consider above which detection after Compton scatter is considered too small - static - float - energy_lower_limit(const float low, const float approx, const float resolution_at_511keV); +protected: + //! computes scatter for one viewgram + /*! \return total scatter estimated for this viewgram */ + virtual double process_data_for_view_segment_num(const ViewSegmentNumbers& vs_num); + + float compute_emis_to_det_points_solid_angle_factor(const CartesianCoordinate3D& emis_point, + const CartesianCoordinate3D& detector_coord); + + virtual void set_defaults(); + virtual void initialise_keymap(); + //! \warning post_processing will set everything that has a file name in + //! the par file. The corresponding set functions should be used either + //! for files that are not stored in the drive. + virtual bool post_processing(); + + enum image_type { act_image_type, att_image_type }; + struct ScatterPoint { + CartesianCoordinate3D coord; + float mu_value; + }; + + std::vector scatt_points_vector; + + float scatter_volume; - virtual - void - find_detectors(unsigned& det_num_A, unsigned& det_num_B, const Bin& bin) const; + //! find scatter points + /*! This function sets scatt_points_vector and scatter_volume. It will also + remove any cached integrals as they would be incorrect otherwise. + */ + void sample_scatter_points(); - unsigned - find_in_detection_points_vector(const CartesianCoordinate3D& coord) const; + //! remove cached attenuation integrals + /*! should be used before recalculating scatter for a new attenuation image or + when changing the sampling of the detector etc */ + virtual void remove_cache_for_integrals_over_attenuation(); - CartesianCoordinate3D shift_detector_coordinates_to_origin; + //! reset cached activity integrals + /*! should be used before recalculating scatter for a new activity image or + when changing the sampling of the detector etc */ + virtual void remove_cache_for_integrals_over_activity(); - //! average detection efficiency of unscattered counts - double - detection_efficiency_no_scatter(const unsigned det_num_A, - const unsigned det_num_B) const; + /** \name detection related functions + * + * @{ + */ - // next needs to be mutable because find_in_detection_points_vector is const - mutable std::vector > detection_points_vector; + float detection_efficiency(const float energy) const; - //!@} + //! maximum angle to consider above which detection after Compton scatter is considered too small + static float max_cos_angle(const float low, const float approx, const float resolution_at_511keV); - //! virtual function that computes the scatter for one (downsampled) bin - virtual double - scatter_estimate(const Bin& bin) = 0; + //! mimumum energy to consider above which detection after Compton scatter is considered too small + static float energy_lower_limit(const float low, const float approx, const float resolution_at_511keV); - //! \name integrating functions - //@{ - static - float - integral_between_2_points(const DiscretisedDensity<3,float>& density, - const CartesianCoordinate3D& point1, - const CartesianCoordinate3D& point2); + virtual void find_detectors(unsigned& det_num_A, unsigned& det_num_B, const Bin& bin) const; - float - exp_integral_over_attenuation_image_between_scattpoint_det (const CartesianCoordinate3D& scatter_point, - const CartesianCoordinate3D& detector_coord); + unsigned find_in_detection_points_vector(const CartesianCoordinate3D& coord) const; + CartesianCoordinate3D shift_detector_coordinates_to_origin; + //! average detection efficiency of unscattered counts + double detection_efficiency_no_scatter(const unsigned det_num_A, const unsigned det_num_B) const; - float - integral_over_activity_image_between_scattpoint_det (const CartesianCoordinate3D& scatter_point, - const CartesianCoordinate3D& detector_coord); + // next needs to be mutable because find_in_detection_points_vector is const + mutable std::vector> detection_points_vector; + //!@} + //! virtual function that computes the scatter for one (downsampled) bin + virtual double scatter_estimate(const Bin& bin) = 0; - float - cached_integral_over_activity_image_between_scattpoint_det(const unsigned scatter_point_num, - const unsigned det_num); + //! \name integrating functions + //@{ + static float integral_between_2_points(const DiscretisedDensity<3, float>& density, const CartesianCoordinate3D& point1, + const CartesianCoordinate3D& point2); - float - cached_exp_integral_over_attenuation_image_between_scattpoint_det(const unsigned scatter_point_num, - const unsigned det_num); - //@} + float exp_integral_over_attenuation_image_between_scattpoint_det(const CartesianCoordinate3D& scatter_point, + const CartesianCoordinate3D& detector_coord); - std::string template_proj_data_filename; + float integral_over_activity_image_between_scattpoint_det(const CartesianCoordinate3D& scatter_point, + const CartesianCoordinate3D& detector_coord); - shared_ptr proj_data_info_cyl_noarc_cor_sptr; + float cached_integral_over_activity_image_between_scattpoint_det(const unsigned scatter_point_num, const unsigned det_num); - //! \details Exam info extracted from the scanner template - shared_ptr template_exam_info_sptr; + float cached_exp_integral_over_attenuation_image_between_scattpoint_det(const unsigned scatter_point_num, + const unsigned det_num); + //@} - std::string density_image_filename; + std::string template_proj_data_filename; - std::string density_image_for_scatter_points_filename; + shared_ptr proj_data_info_cyl_noarc_cor_sptr; - std::string density_image_for_scatter_points_output_filename; + //! \details Exam info extracted from the scanner template + shared_ptr template_exam_info_sptr; - shared_ptr< const DiscretisedDensity<3, float> > density_image_sptr; + std::string density_image_filename; - //! Pointer to hold the current activity estimation - shared_ptr > activity_image_sptr; + std::string density_image_for_scatter_points_filename; - //! set-up cache for attenuation integrals - /*! \warning This will not remove existing cached data (if the sizes match). If you need this, - call remove_cache_for_scattpoint_det_integrals_over_attenuation() first. - */ - void initialise_cache_for_scattpoint_det_integrals_over_attenuation(); - //! set-up cache for activity integrals - /*! \warning This will not remove existing cached data (if the sizes match). If you need this, - call remove_cache_for_scattpoint_det_integrals_over_activity() first. - */ - void initialise_cache_for_scattpoint_det_integrals_over_activity(); + std::string density_image_for_scatter_points_output_filename; - //! Output proj_data fileanme prefix - std::string output_proj_data_filename; - //! Shared ptr to hold the simulated data. - shared_ptr output_proj_data_sptr; + shared_ptr> density_image_sptr; - //! threshold below which a voxel in the attenuation image will not be considered as a candidate scatter point - float attenuation_threshold; + //! Pointer to hold the current activity estimation + shared_ptr> activity_image_sptr; - //! boolean to see if we need to move the scatter point randomly within in its voxel - bool randomly_place_scatter_points; - //! boolean to see if we need to cache the integrals - /*! By default, we cache the integrals over the emission and attenuation image. If you run out - of memory, you can switch this off, but performance will suffer dramatically. - */ - bool use_cache; - //! Filename for the initial activity estimate. - std::string activity_image_filename; - //! Zoom factor on plane XY. Defaults on 1.f. - float zoom_xy; - //! Zoom factor on Z axis. Defaults on 1.f. - float zoom_z; - //! Zoomed image size on plane XY. Defaults on -1. - int zoom_size_xy; - //! Zoomed image size on Z axis. Defaults on -1. - int zoom_size_z; - //! Number of rings of downsampled scanner - int downsample_scanner_rings; - //! Number of detectors per ring of downsampled scanner - int downsample_scanner_dets; + //! set-up cache for attenuation integrals + /*! \warning This will not remove existing cached data (if the sizes match). If you need this, + call remove_cache_for_scattpoint_det_integrals_over_attenuation() first. + */ + void initialise_cache_for_scattpoint_det_integrals_over_attenuation(); + //! set-up cache for activity integrals + /*! \warning This will not remove existing cached data (if the sizes match). If you need this, + call remove_cache_for_scattpoint_det_integrals_over_activity() first. + */ + void initialise_cache_for_scattpoint_det_integrals_over_activity(); - bool downsample_scanner_bool; + //! Output proj_data fileanme prefix + std::string output_proj_data_filename; + //! Shared ptr to hold the simulated data. + shared_ptr output_proj_data_sptr; - private: - int total_detectors; + //! threshold below which a voxel in the attenuation image will not be considered as a candidate scatter point + float attenuation_threshold; - Array<2,float> cached_activity_integral_scattpoint_det; - Array<2,float> cached_attenuation_integral_scattpoint_det; - shared_ptr< DiscretisedDensity<3, float> > density_image_for_scatter_points_sptr; + //! boolean to see if we need to move the scatter point randomly within in its voxel + bool randomly_place_scatter_points; + //! boolean to see if we need to cache the integrals + /*! By default, we cache the integrals over the emission and attenuation image. If you run out + of memory, you can switch this off, but performance will suffer dramatically. + */ + bool use_cache; + //! Filename for the initial activity estimate. + std::string activity_image_filename; + //! Zoom factor on plane XY. Defaults on 1.f. + float zoom_xy; + //! Zoom factor on Z axis. Defaults on 1.f. + float zoom_z; + //! Zoomed image size on plane XY. Defaults on -1. + int zoom_size_xy; + //! Zoomed image size on Z axis. Defaults on -1. + int zoom_size_z; + //! Number of rings of downsampled scanner + int downsample_scanner_rings; + //! Number of detectors per ring of downsampled scanner + int downsample_scanner_dets; - // numbers that we don't want to recompute all the time - mutable float detector_efficiency_no_scatter; + bool downsample_scanner_bool; - bool _already_set_up; +private: + int total_detectors; - //! a function that checks if image sizes are ok - /*! It will call \c error() if not. + Array<2, float> cached_activity_integral_scattpoint_det; + Array<2, float> cached_attenuation_integral_scattpoint_det; + shared_ptr> density_image_for_scatter_points_sptr; - Currently, STIR shifts the middle of the image to the middle of the scanner. This - is dangerous when using image zooming. - This function currently checks if \a _image is consistent with the \c activity_image_sptr. + // numbers that we don't want to recompute all the time + mutable float detector_efficiency_no_scatter; - See https://github.com/UCL/STIR/issues/495 for more information. - */ - void check_z_to_middle_consistent(const DiscretisedDensity<3,float>& _image, const std::string& name) const; + bool _already_set_up; + + //! a function that checks if image sizes are ok + /*! It will call \c error() if not. + + Currently, STIR shifts the middle of the image to the middle of the scanner. This + is dangerous when using image zooming. + This function currently checks if \a _image is consistent with the \c activity_image_sptr. + + See https://github.com/UCL/STIR/issues/495 for more information. + */ + void check_z_to_middle_consistent(const DiscretisedDensity<3, float>& _image, const std::string& name) const; }; END_NAMESPACE_STIR @@ -437,5 +385,3 @@ END_NAMESPACE_STIR #include "stir/scatter/ScatterSimulation.inl" #endif - - diff --git a/src/include/stir/scatter/ScatterSimulation.inl b/src/include/stir/scatter/ScatterSimulation.inl index cf572f1eeb..03d2c722bb 100644 --- a/src/include/stir/scatter/ScatterSimulation.inl +++ b/src/include/stir/scatter/ScatterSimulation.inl @@ -38,53 +38,43 @@ START_NAMESPACE_STIR /**************** Functions to set images ****************/ float -ScatterSimulation:: -dif_Compton_cross_section(const float cos_theta, float energy) -{ - const double Re = 2.818E-13; // aktina peristrofis electroniou gia to atomo tou H - const double sin_theta_2= 1-cos_theta*cos_theta ; - const double P= 1.0/(1.0+(energy/511.0)*(1.0-cos_theta)); - return static_cast( (Re*Re/2) * P * (1 - P * sin_theta_2 + P * P)); +ScatterSimulation::dif_Compton_cross_section(const float cos_theta, float energy) { + const double Re = 2.818E-13; // aktina peristrofis electroniou gia to atomo tou H + const double sin_theta_2 = 1 - cos_theta * cos_theta; + const double P = 1.0 / (1.0 + (energy / 511.0) * (1.0 - cos_theta)); + return static_cast((Re * Re / 2) * P * (1 - P * sin_theta_2 + P * P)); } float -ScatterSimulation:: -photon_energy_after_Compton_scatter(const float cos_theta, const float energy) -{ - return static_cast(energy/(1+(energy/511.0f)*(1-cos_theta))); // For an arbitrary energy +ScatterSimulation::photon_energy_after_Compton_scatter(const float cos_theta, const float energy) { + return static_cast(energy / (1 + (energy / 511.0f) * (1 - cos_theta))); // For an arbitrary energy } float -ScatterSimulation:: -photon_energy_after_Compton_scatter_511keV(const float cos_theta) -{ - return 511.f/(2.f-cos_theta); // for a given energy, energy := 511 keV +ScatterSimulation::photon_energy_after_Compton_scatter_511keV(const float cos_theta) { + return 511.f / (2.f - cos_theta); // for a given energy, energy := 511 keV } float -ScatterSimulation:: -total_Compton_cross_section(const float energy) -{ - const double a= energy/511.0; - const double l= log(1.0+2.0*a); - const double sigma0= 6.65E-25; // sigma0=8*pi*a*a/(3*m*m) - return static_cast( 0.75*sigma0 * ( (1.0+a)/(a*a)*( 2.0*(1.0+a)/(1.0+2.0*a)- l/a ) + l/(2.0*a) - (1.0+3.0*a)/(1.0+2.0*a)/(1.0+2.0*a) ) ); // Klein - Nishina formula = sigma / sigma0 +ScatterSimulation::total_Compton_cross_section(const float energy) { + const double a = energy / 511.0; + const double l = log(1.0 + 2.0 * a); + const double sigma0 = 6.65E-25; // sigma0=8*pi*a*a/(3*m*m) + return static_cast(0.75 * sigma0 * + ((1.0 + a) / (a * a) * (2.0 * (1.0 + a) / (1.0 + 2.0 * a) - l / a) + l / (2.0 * a) - + (1.0 + 3.0 * a) / (1.0 + 2.0 * a) / (1.0 + 2.0 * a))); // Klein - Nishina formula = sigma / sigma0 } - float -ScatterSimulation:: -total_Compton_cross_section_relative_to_511keV(const float energy) -{ - const double a= energy/511.0; - static const double prefactor = 9.0/(-40 + 27*log(3.)); //Klein-Nishina formula for a=1 & devided with 0.75 == (40 - 27*log(3)) / 9 +ScatterSimulation::total_Compton_cross_section_relative_to_511keV(const float energy) { + const double a = energy / 511.0; + static const double prefactor = + 9.0 / (-40 + 27 * log(3.)); // Klein-Nishina formula for a=1 & devided with 0.75 == (40 - 27*log(3)) / 9 - return //checked this in Mathematica - static_cast - (prefactor* - (((-4 - a*(16 + a*(18 + 2*a)))/square(1 + 2*a) + - ((2 + (2 - a)*a)*log(1 + 2*a))/a)/square(a) - )); + return // checked this in Mathematica + static_cast( + prefactor * + (((-4 - a * (16 + a * (18 + 2 * a))) / square(1 + 2 * a) + ((2 + (2 - a) * a) * log(1 + 2 * a)) / a) / square(a))); } END_NAMESPACE_STIR diff --git a/src/include/stir/scatter/SingleScatterSimulation.h b/src/include/stir/scatter/SingleScatterSimulation.h index 70e4914416..6e3096c29a 100644 --- a/src/include/stir/scatter/SingleScatterSimulation.h +++ b/src/include/stir/scatter/SingleScatterSimulation.h @@ -29,7 +29,6 @@ #include "stir/scatter/ScatterSimulation.h" #include "stir/RegisteredParsingObject.h" - START_NAMESPACE_STIR /*! @@ -38,71 +37,54 @@ START_NAMESPACE_STIR \todo The class is specific to PET so should be renamed accordingly. */ -class SingleScatterSimulation : public - RegisteredParsingObject< - SingleScatterSimulation, - ScatterSimulation, - ScatterSimulation > -{ +class SingleScatterSimulation : public RegisteredParsingObject { private: - typedef RegisteredParsingObject< - SingleScatterSimulation, - ScatterSimulation, - ScatterSimulation > base_type; + typedef RegisteredParsingObject base_type; + public: + //! Name which will be used when parsing a ScatterSimulation object + static const char* const registered_name; - //! Name which will be used when parsing a ScatterSimulation object - static const char * const registered_name; + //! Default constructor + SingleScatterSimulation(); - //! Default constructor - SingleScatterSimulation(); + //! Constructor with initialisation from parameter file + explicit SingleScatterSimulation(const std::string& parameter_filename); - //! Constructor with initialisation from parameter file - explicit - SingleScatterSimulation(const std::string& parameter_filename); + virtual ~SingleScatterSimulation(); - virtual ~SingleScatterSimulation(); + virtual Succeeded process_data(); + //! gives method information + virtual std::string method_info() const; + //! prompts the user to enter parameter values manually + virtual void ask_parameters(); + //! Perform checks and intialisations + virtual Succeeded set_up(); - virtual Succeeded process_data(); - //! gives method information - virtual std::string method_info() const; - //! prompts the user to enter parameter values manually - virtual void ask_parameters(); - //! Perform checks and intialisations - virtual Succeeded set_up(); protected: + void initialise(const std::string& parameter_filename); - void initialise(const std::string& parameter_filename); - - virtual void set_defaults(); - virtual void initialise_keymap(); + virtual void set_defaults(); + virtual void initialise_keymap(); - //! used to check acceptable parameter ranges, etc... - virtual bool post_processing(); + //! used to check acceptable parameter ranges, etc... + virtual bool post_processing(); + //! + //! \brief simulate_for_one_scatter_point + //! \param scatter_point_num + //! \param det_num_A + //! \param det_num_B + //! \return + float simulate_for_one_scatter_point(const std::size_t scatter_point_num, const unsigned det_num_A, const unsigned det_num_B); - //! - //! \brief simulate_for_one_scatter_point - //! \param scatter_point_num - //! \param det_num_A - //! \param det_num_B - //! \return - float - simulate_for_one_scatter_point(const std::size_t scatter_point_num, - const unsigned det_num_A, - const unsigned det_num_B); + virtual double scatter_estimate(const Bin& bin); - virtual double - scatter_estimate(const Bin& bin); + virtual void actual_scatter_estimate(double& scatter_ratio_singles, const unsigned det_num_A, const unsigned det_num_B); - virtual void - actual_scatter_estimate(double& scatter_ratio_singles, - const unsigned det_num_A, - const unsigned det_num_B); - - private: - //! larger angles will be ignored - float max_single_scatter_cos_angle; +private: + //! larger angles will be ignored + float max_single_scatter_cos_angle; }; END_NAMESPACE_STIR diff --git a/src/include/stir/shared_ptr.h b/src/include/stir/shared_ptr.h index 7de2b9bd13..1eb18eba3b 100644 --- a/src/include/stir/shared_ptr.h +++ b/src/include/stir/shared_ptr.h @@ -3,12 +3,12 @@ /*! \file \ingroup buildblock - + \brief Import of std::shared_ptr, std::dynamic_pointer_cast and - std::static_pointer_cast (or corresponding boost versions if - STIR_USE_BOOST_SHARED_PTR is set, i.e. normally when std::shared_ptr doesn't exist) - into the stir namespace. -*/ + std::static_pointer_cast (or corresponding boost versions if + STIR_USE_BOOST_SHARED_PTR is set, i.e. normally when std::shared_ptr doesn't exist) + into the stir namespace. +*/ /* Copyright (C) 2011-07-01 - 2012, Kris Thielemans This file is part of STIR. @@ -31,25 +31,25 @@ #include "stir/common.h" #if defined(STIR_USE_BOOST_SHARED_PTR) -#include "boost/shared_ptr.hpp" -#include "boost/make_shared.hpp" -#include "boost/pointer_cast.hpp" +# include "boost/shared_ptr.hpp" +# include "boost/make_shared.hpp" +# include "boost/pointer_cast.hpp" namespace stir { - using boost::shared_ptr; - using boost::dynamic_pointer_cast; - using boost::static_pointer_cast; - //! work-around for using std::make_shared on old compilers -#define MAKE_SHARED boost::make_shared -} +using boost::shared_ptr; +using boost::dynamic_pointer_cast; +using boost::static_pointer_cast; +//! work-around for using std::make_shared on old compilers +# define MAKE_SHARED boost::make_shared +} // namespace stir #else -#include +# include namespace stir { - using std::shared_ptr; - using std::dynamic_pointer_cast; - using std::static_pointer_cast; - //! work-around for using std::make_shared on old compilers -#define MAKE_SHARED std::make_shared -} +using std::shared_ptr; +using std::dynamic_pointer_cast; +using std::static_pointer_cast; +//! work-around for using std::make_shared on old compilers +# define MAKE_SHARED std::make_shared +} // namespace stir #endif #endif diff --git a/src/include/stir/spatial_transformation/GatedSpatialTransformation.h b/src/include/stir/spatial_transformation/GatedSpatialTransformation.h index c6e6c3419b..f8b9e2df1a 100644 --- a/src/include/stir/spatial_transformation/GatedSpatialTransformation.h +++ b/src/include/stir/spatial_transformation/GatedSpatialTransformation.h @@ -2,25 +2,25 @@ /* Copyright (C) 2009 - 2013, King's College London This file is part of STIR. - + This file is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.3 of the License, or (at your option) any later version. - + This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - + See STIR/LICENSE.txt for details - */ + */ /*! \file \ingroup spatial_transformation \brief Declaration of class stir::GatedSpatialTransformation \author Charalampos Tsoumpas - + */ #ifndef __stir_spatial_transformation_GatedSpatialTransformation_H__ @@ -41,53 +41,43 @@ START_NAMESPACE_STIR /*! \ingroup spatial_transformation */ -class GatedSpatialTransformation: public RegisteredParsingObject -{ - public: - static const char * const registered_name; +class GatedSpatialTransformation : public RegisteredParsingObject { +public: + static const char* const registered_name; - GatedSpatialTransformation(); //!< default constructor + GatedSpatialTransformation(); //!< default constructor ~GatedSpatialTransformation(); //!< default destructor //! Construct an empty GatedSpatialTransformation based on a shared_ptr > GatedSpatialTransformation(const TimeGateDefinitions& time_gate_definitions, - const shared_ptr >& density_sptr); + const shared_ptr>& density_sptr); void read_from_files(const std::string input_string); - void write_to_files(const std::string output_string); + void write_to_files(const std::string output_string); //! \name Functions to get parameters @{ GatedDiscretisedDensity get_spatial_transformation_z() const; GatedDiscretisedDensity get_spatial_transformation_y() const; GatedDiscretisedDensity get_spatial_transformation_x() const; - const TimeGateDefinitions & get_time_gate_definitions() const; + const TimeGateDefinitions& get_time_gate_definitions() const; //!@} //! \name Functions to set parameters @{ - void set_spatial_transformations(const GatedDiscretisedDensity & motion_z, - const GatedDiscretisedDensity & motion_y, - const GatedDiscretisedDensity & motion_x); - void set_gate_defs(const TimeGateDefinitions & gate_defs); + void set_spatial_transformations(const GatedDiscretisedDensity& motion_z, const GatedDiscretisedDensity& motion_y, + const GatedDiscretisedDensity& motion_x); + void set_gate_defs(const TimeGateDefinitions& gate_defs); //!@} //! Warping functions from to gated images. @{ - void - warp_image(GatedDiscretisedDensity & new_gated_image, - const GatedDiscretisedDensity & gated_image) const ; - void - warp_image(DiscretisedDensity<3, float> & new_reference_image, - const GatedDiscretisedDensity & gated_image) const ; - void - warp_image(GatedDiscretisedDensity & gated_image, - const DiscretisedDensity<3, float> & reference_image) const; - void - accumulate_warp_image(DiscretisedDensity<3, float> & new_reference_image, - const GatedDiscretisedDensity & gated_image) const ; + void warp_image(GatedDiscretisedDensity& new_gated_image, const GatedDiscretisedDensity& gated_image) const; + void warp_image(DiscretisedDensity<3, float>& new_reference_image, const GatedDiscretisedDensity& gated_image) const; + void warp_image(GatedDiscretisedDensity& gated_image, const DiscretisedDensity<3, float>& reference_image) const; + void accumulate_warp_image(DiscretisedDensity<3, float>& new_reference_image, const GatedDiscretisedDensity& gated_image) const; void set_defaults(); - Succeeded set_up(); + Succeeded set_up(); //@} - private: - typedef RegisteredParsingObject base_type; +private: + typedef RegisteredParsingObject base_type; void initialise_keymap(); - bool post_processing(); + bool post_processing(); std::string _transformation_filename_prefix; GatedDiscretisedDensity _spatial_transformation_z; GatedDiscretisedDensity _spatial_transformation_y; diff --git a/src/include/stir/spatial_transformation/InvertAxis.h b/src/include/stir/spatial_transformation/InvertAxis.h index 7f14030349..be03267548 100644 --- a/src/include/stir/spatial_transformation/InvertAxis.h +++ b/src/include/stir/spatial_transformation/InvertAxis.h @@ -2,23 +2,23 @@ /* Copyright (C) 2019 National Physical Laboratory This file is part of STIR. - + This file is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.3 of the License, or (at your option) any later version. - + This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - + See STIR/LICENSE.txt for details - */ + */ /*! \file \ingroup spatial_transformation - + \brief This invert the selected image axis. \author Daniel Deidda */ @@ -30,28 +30,22 @@ START_NAMESPACE_STIR -/*! +/*! \ingroup spatial_transformation - + \brief a utility class to "invert" an axis \warning this will reorder the voxel values without adjusting the geometric information. */ -class InvertAxis{ +class InvertAxis { public: - //! transform the image /*! \a axis_name has to be x, y, z. Otherwise error() will be called. */ -void -invert_axis(DiscretisedDensity<3, float> &inverted_image, - const DiscretisedDensity<3, float> &input_image, - const std::string & axis_name); - -int -invert_axis_index(const int input_index, - const int size, - const std::string & axis_name); + void invert_axis(DiscretisedDensity<3, float>& inverted_image, const DiscretisedDensity<3, float>& input_image, + const std::string& axis_name); + + int invert_axis_index(const int input_index, const int size, const std::string& axis_name); }; END_NAMESPACE_STIR diff --git a/src/include/stir/spatial_transformation/SpatialTransformation.h b/src/include/stir/spatial_transformation/SpatialTransformation.h index 377c3b88c4..eaca5c4285 100644 --- a/src/include/stir/spatial_transformation/SpatialTransformation.h +++ b/src/include/stir/spatial_transformation/SpatialTransformation.h @@ -2,25 +2,25 @@ /* Copyright (C) 2009 - 2013, King's College London This file is part of STIR. - + This file is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.3 of the License, or (at your option) any later version. - + This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - + See STIR/LICENSE.txt for details - */ + */ /*! \file \ingroup spatial_transformation \brief Definition of class stir::SpatialTransformation \author Charalampos Tsoumpas - + */ #ifndef __stir_spatial_transformation_SpatialTransformation_H__ @@ -31,15 +31,14 @@ START_NAMESPACE_STIR -/*! +/*! \brief base class for any type of motion fields \ingroup spatial_transformation At present very basic. It just provides the parsing mechanism. */ -class SpatialTransformation: public RegisteredObject -{ - public: - static const char * const registered_name ; +class SpatialTransformation : public RegisteredObject { +public: + static const char* const registered_name; //! default constructor SpatialTransformation(); diff --git a/src/include/stir/spatial_transformation/warp_image.h b/src/include/stir/spatial_transformation/warp_image.h index 28dd2ca621..6b777f2c9b 100644 --- a/src/include/stir/spatial_transformation/warp_image.h +++ b/src/include/stir/spatial_transformation/warp_image.h @@ -2,23 +2,23 @@ /* Copyright (C) 2009 - 2013, King's College London This file is part of STIR. - + This file is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.3 of the License, or (at your option) any later version. - + This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - + See STIR/LICENSE.txt for details - */ + */ /*! \file \ingroup spatial_transformation - + \brief This warps an image. \author Charalampos Tsoumpas */ @@ -31,12 +31,11 @@ START_NAMESPACE_STIR -VoxelsOnCartesianGrid -warp_image(const shared_ptr > & density_sptr, - const shared_ptr > & motion_x_sptr, - const shared_ptr > & motion_y_sptr, - const shared_ptr > & motion_z_sptr, - const BSpline::BSplineType spline_type, const bool extend_borders); +VoxelsOnCartesianGrid warp_image(const shared_ptr>& density_sptr, + const shared_ptr>& motion_x_sptr, + const shared_ptr>& motion_y_sptr, + const shared_ptr>& motion_z_sptr, + const BSpline::BSplineType spline_type, const bool extend_borders); END_NAMESPACE_STIR diff --git a/src/include/stir/stir_math.h b/src/include/stir/stir_math.h index a5f611a5be..d3aa6bba01 100644 --- a/src/include/stir/stir_math.h +++ b/src/include/stir/stir_math.h @@ -1,17 +1,17 @@ /* Copyright (C) 2016, UCL This file is part of STIR. - + This file is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. - + This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - + See STIR/LICENSE.txt for details */ @@ -37,20 +37,17 @@ USING_NAMESPACE_STIR // places in the code. // a function object that takes a power of a float, and then multiplies with a float, and finally adds a float -class pow_times_add: public unary_function -{ +class pow_times_add : public unary_function { public: - pow_times_add(const float add_scalar, const float mult_scalar, const float power, - const float min_threshold, const float max_threshold) - : add(add_scalar), mult(mult_scalar), power(power), - min_threshold(min_threshold), max_threshold(max_threshold) - {} - - float operator()(float const arg) const - { + pow_times_add(const float add_scalar, const float mult_scalar, const float power, const float min_threshold, + const float max_threshold) + : add(add_scalar), mult(mult_scalar), power(power), min_threshold(min_threshold), max_threshold(max_threshold) {} + + float operator()(float const arg) const { const float value = min(max(arg, min_threshold), max_threshold); - return add+mult*(power==1?value : pow(value,power)); + return add + mult * (power == 1 ? value : pow(value, power)); } + private: const float add; const float mult; @@ -60,4 +57,3 @@ class pow_times_add: public unary_function }; #endif - diff --git a/src/include/stir/stream.h b/src/include/stir/stream.h index 54b7fad6f2..4fabf71af5 100644 --- a/src/include/stir/stream.h +++ b/src/include/stir/stream.h @@ -42,78 +42,68 @@ START_NAMESPACE_STIR /*! \brief Outputs a VectorWithOffset to a stream. - Output is of the form + Output is of the form \verbatim {1, 2, 3} \endverbatim - with an endl at the end. - - This can be used for higher dimensional arrays as well, where each 1D subobject + with an endl at the end. + + This can be used for higher dimensional arrays as well, where each 1D subobject will be on its own line. */ - template -inline -std::ostream& -operator<<(std::ostream& str, const VectorWithOffset& v); +inline std::ostream& operator<<(std::ostream& str, const VectorWithOffset& v); /*! \brief Outputs a BasicCoordinate to a stream. - Output is of the form + Output is of the form \verbatim {1, 2, 3} \endverbatim - with no endl at the end. + with no endl at the end. */ template -inline -std::ostream& -operator<<(std::ostream& str, const BasicCoordinate& v); - +inline std::ostream& operator<<(std::ostream& str, const BasicCoordinate& v); /*! \brief Outputs a vector to a stream. - Output is of the form + Output is of the form \verbatim {1, 2, 3} \endverbatim - with an endl at the end. - + with an endl at the end. + For each element of the vector std::ostream::operator<<() will be called. */ template -inline -std::ostream& -operator<<(std::ostream& str, const std::vector& v); +inline std::ostream& operator<<(std::ostream& str, const std::vector& v); /*! \brief Inputs a vector from a stream. - Input is of the form + Input is of the form \verbatim {1, 2, 3} \endverbatim - - Input is stopped when either the beginning '{', an intermediate ',' or the - trailing '}' is not found. The size of the vector will be the number of + + Input is stopped when either the beginning '{', an intermediate ',' or the + trailing '}' is not found. The size of the vector will be the number of correctly read elemT elements. - + For each element of the vector std::istream::operator>>(element) will be called. elemT needs to have a default constructor. */ template -inline -std::istream& -operator>>(std::istream& str, std::vector& v); +inline std::istream& operator>>(std::istream& str, std::vector& v); /*! \brief Inputs a VectorWithOffset from a stream. - Input is of the form + Input is of the form \verbatim {1, 2, 3} \endverbatim @@ -122,12 +112,12 @@ operator>>(std::istream& str, std::vector& v); \verbatim {{1,2}, {2,4,5}, {5}} \endverbatim - - - Input is stopped when either the beginning '{', an intermediate ',' or the - trailing '}' is not found. The size of the vector will be the number of + + + Input is stopped when either the beginning '{', an intermediate ',' or the + trailing '}' is not found. The size of the vector will be the number of correctly read elemT elements. - + v.get_min_index() will be 0 at the end of the call. For each element of the vector std::istream::operator>>(element) will be called. @@ -135,34 +125,29 @@ operator>>(std::istream& str, std::vector& v); elemT needs to have a default constructor. */ template -inline -std::istream& -operator>>(std::istream& str, VectorWithOffset& v); +inline std::istream& operator>>(std::istream& str, VectorWithOffset& v); /*! \brief Inputs a coordinate from a stream. - Input is of the form + Input is of the form \verbatim {1, 2, 3} \endverbatim - - Input is stopped when either the beginning '{', an intermediate ',' or the + + Input is stopped when either the beginning '{', an intermediate ',' or the trailing '}' is not found. If the number of correctly read elements is not \a num_dimensions, the last few will have undefined values. - + For each element of the vector std::istream::operator>>(element) will be called. elemT needs to have a default constructor. */ template -inline -std::istream& -operator>>(std::istream& str, BasicCoordinate& v); +inline std::istream& operator>>(std::istream& str, BasicCoordinate& v); END_NAMESPACE_STIR #include "stir/stream.inl" #endif - diff --git a/src/include/stir/stream.inl b/src/include/stir/stream.inl index 058e62c064..e441a2db9f 100644 --- a/src/include/stir/stream.inl +++ b/src/include/stir/stream.inl @@ -39,95 +39,82 @@ START_NAMESPACE_STIR template -std::ostream& -operator<<(std::ostream& str, const VectorWithOffset& v) -{ - str << '{'; - for (int i=v.get_min_index(); i0) - str << v[v.get_max_index()]; - str << '}' << std::endl; - return str; +std::ostream& +operator<<(std::ostream& str, const VectorWithOffset& v) { + str << '{'; + for (int i = v.get_min_index(); i < v.get_max_index(); i++) + str << v[i] << ", "; + + if (v.get_length() > 0) + str << v[v.get_max_index()]; + str << '}' << std::endl; + return str; } template -std::ostream& -operator<<(std::ostream& str, const BasicCoordinate& v) -{ - str << '{'; - for (int i=1; i0) - str << v[num_dimensions]; - str << '}'; - return str; +std::ostream& +operator<<(std::ostream& str, const BasicCoordinate& v) { + str << '{'; + for (int i = 1; i < num_dimensions; i++) + str << v[i] << ", "; + + if (num_dimensions > 0) + str << v[num_dimensions]; + str << '}'; + return str; } - template -std::ostream& -operator<<(std::ostream& str, const std::vector& v) -{ - str << '{'; - // slightly different from above because vector::size() is unsigned - // so 0-1 == 0xFFFFFFFF (and not -1) - if (v.size()>0) - { - for (unsigned int i=0; i& v) { + str << '{'; + // slightly different from above because vector::size() is unsigned + // so 0-1 == 0xFFFFFFFF (and not -1) + if (v.size() > 0) { + for (unsigned int i = 0; i < v.size() - 1; i++) + str << v[i] << ", "; + str << v[v.size() - 1]; + } + str << '}' << std::endl; + return str; } template -std::istream& -operator>>(std::istream& str, std::vector& v) -{ +std::istream& +operator>>(std::istream& str, std::vector& v) { v.resize(0); char c; str >> std::ws >> c; if (!str || c != '{') return str; - + elemT t; - do - { + do { str >> t; - if (!str.fail()) - { + if (!str.fail()) { v.push_back(t); str >> std::ws >> c; - } - else + } else break; - } - while (str && c == ','); + } while (str && c == ','); - if (str.fail()) - { + if (str.fail()) { str.clear(); str >> std::ws >> c; } - if (!str) - { + if (!str) { warning("\nreading a vector, expected closing }, but found EOF or worse. Length of vector returned is %ud\n", v.size()); return str; } - - if (c!= '}') + + if (c != '}') warning("\nreading a vector, expected closing }, found %c instead. Length of vector returned is %u\n", c, v.size()); return str; } template -std::istream& -operator>>(std::istream& str, VectorWithOffset& v) -{ +std::istream& +operator>>(std::istream& str, VectorWithOffset& v) { std::vector vv; str >> vv; v = VectorWithOffset(static_cast(vv.size())); @@ -136,36 +123,32 @@ operator>>(std::istream& str, VectorWithOffset& v) } template -std::istream& -operator>>(std::istream& str, BasicCoordinate& v) -{ +std::istream& +operator>>(std::istream& str, BasicCoordinate& v) { char c = '\0'; str >> std::ws >> c; - if (!str || c != '{') - { + if (!str || c != '{') { warning("reading a coordinate of dimension %d, expected opening {, found %c instead.\n" - "Elements will be undefined", num_dimensions, c); + "Elements will be undefined", + num_dimensions, c); return str; } - for (int i=1; i<=num_dimensions; i++) - { + for (int i = 1; i <= num_dimensions; i++) { c = '\0'; str >> v[i]; str >> std::ws >> c; - if (i
  • \c forw_iterT is a forward iterator
  • \c elemT must be assignable to *forw_iterT @@ -50,54 +50,44 @@ START_NAMESPACE_STIR */ template inline void -threshold_upper_lower(forw_iterT begin, forw_iterT end, - const elemT new_min, const elemT new_max) -{ - for (forw_iterT iter = begin; iter != end; ++iter) - { - if (*iter > new_max) - *iter = new_max; - else - if (new_min > *iter) - *iter = new_min; - } +threshold_upper_lower(forw_iterT begin, forw_iterT end, const elemT new_min, const elemT new_max) { + for (forw_iterT iter = begin; iter != end; ++iter) { + if (*iter > new_max) + *iter = new_max; + else if (new_min > *iter) + *iter = new_min; + } } //! Threshold a sequence from above -/*! +/*! \see threshold_upper_lower for type requirements */ template inline void -threshold_upper(forw_iterT begin, forw_iterT end, - const elemT new_max) -{ - for (forw_iterT iter = begin; iter != end; ++iter) - { - if (*iter > new_max) - *iter = new_max; - } +threshold_upper(forw_iterT begin, forw_iterT end, const elemT new_max) { + for (forw_iterT iter = begin; iter != end; ++iter) { + if (*iter > new_max) + *iter = new_max; + } } //! Threshold a sequence from below -/*! +/*! \see threshold_upper_lower for type requirements */ template inline void -threshold_lower(forw_iterT begin, forw_iterT end, - const elemT new_min) -{ - for (forw_iterT iter = begin; iter != end; ++iter) - { - if (new_min > *iter) - *iter = new_min; - } +threshold_lower(forw_iterT begin, forw_iterT end, const elemT new_min) { + for (forw_iterT iter = begin; iter != end; ++iter) { + if (new_min > *iter) + *iter = new_min; + } } //! sets non-positive values in the sequence to small positive ones /*! - Thresholds the sequence from below to + Thresholds the sequence from below to *min_positive_element()*small_number. - However, if all values are less than or equal to 0, they are + However, if all values are less than or equal to 0, they are set to \a small_number. \param begin start of the sequence. Usually object.begin(). @@ -107,17 +97,14 @@ threshold_lower(forw_iterT begin, forw_iterT end, The iterator type has to satisfy the requirements of a forward iterator, and its value_type has to be comparable using < and <=. -*/ +*/ template void -threshold_min_to_small_positive_value(ForwardIter_t begin, ForwardIter_t end, - const elemT& small_number) -{ - const ForwardIter_t smallest_positive_element_iter = - min_positive_element(begin, end); - - if (smallest_positive_element_iter!= end) - threshold_lower(begin, end, (*smallest_positive_element_iter)*small_number); +threshold_min_to_small_positive_value(ForwardIter_t begin, ForwardIter_t end, const elemT& small_number) { + const ForwardIter_t smallest_positive_element_iter = min_positive_element(begin, end); + + if (smallest_positive_element_iter != end) + threshold_lower(begin, end, (*smallest_positive_element_iter) * small_number); else std::fill(begin, end, small_number); } @@ -127,4 +114,3 @@ threshold_min_to_small_positive_value(ForwardIter_t begin, ForwardIter_t end, END_NAMESPACE_STIR #endif - diff --git a/src/include/stir/unique_ptr.h b/src/include/stir/unique_ptr.h index a3d2bb8731..38068bc755 100644 --- a/src/include/stir/unique_ptr.h +++ b/src/include/stir/unique_ptr.h @@ -1,8 +1,8 @@ /*! \file \ingroup buildblock - - \brief Import of std::unique_ptr into the stir namespace, together with + + \brief Import of std::unique_ptr into the stir namespace, together with work-arounds for other compilers. If std::unique doesn't exist, we will define unique_ptr to auto_ptr. This is @@ -10,7 +10,7 @@ (normally by adding the -std=c++11 flag) \author Kris Thielemans -*/ +*/ /* Copyright (C) 2016, University College London This file is part of STIR. @@ -37,7 +37,7 @@ #if !defined(STIR_NO_UNIQUE_PTR) // simply use std::unique_ptr namespace stir { - using std::unique_ptr; +using std::unique_ptr; } #else // we need to replace it with something else @@ -46,16 +46,16 @@ namespace stir { // std::unique_ptr but even for Cxx03 compilers. However, end 2016 this still generated errors on OSX Sierra // with CLang (it could not return a unique_ptr). // So, this is attempt is now disabled. -#if 0 -#include -#if (BOOST_VERSION >= 105700) +# if 0 +# include +# if (BOOST_VERSION >= 105700) // Boost is recent enough to have a drop-in replacement -#include +# include namespace stir { using boost::movelib::unique_ptr; } -#endif -#endif +# endif +# endif // desperate measures. We will use a #define to auto_ptr. // Caveat: @@ -67,21 +67,21 @@ namespace stir { // We first include a bunch of system files which use std::unique_ptr such that we don't have a conflict. // You might have to add a few more... -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include -#define unique_ptr auto_ptr +# define unique_ptr auto_ptr using std::auto_ptr; #endif diff --git a/src/include/stir/utilities.h b/src/include/stir/utilities.h index d7d2dd94e8..522eca375c 100644 --- a/src/include/stir/utilities.h +++ b/src/include/stir/utilities.h @@ -16,9 +16,9 @@ See STIR/LICENSE.txt for details */ #ifndef __stir_UTILITIES_H__ -#define __stir_UTILITIES_H__ +#define __stir_UTILITIES_H__ /*! - \file + \file \ingroup buildblock \brief This file declares various utility functions. @@ -32,42 +32,34 @@ START_NAMESPACE_STIR - /******************************************************/ /*! \ingroup buildblock \name Functions for user input */ //@{ -//! A function to ask a number from the user +//! A function to ask a number from the user template -inline NUMBER -ask_num (const std::string& prompt, - NUMBER minimum_value, - NUMBER maximum_value, - NUMBER default_value); +inline NUMBER ask_num(const std::string& prompt, NUMBER minimum_value, NUMBER maximum_value, NUMBER default_value); -//! A function to ask a string from the user -std::string -ask_string(const std::string& prompt, const std::string& default_value = ""); +//! A function to ask a string from the user +std::string ask_string(const std::string& prompt, const std::string& default_value = ""); -/*! +/*! \brief A function to ask a yes/no question from the user - + \param prompt a text string which is supposed to be a question \param default_value When set to \c true , the default is Yes. The question is currently presented as \verbatim - prompt [Y/N D:default_value]: + prompt [Y/N D:default_value]: \endverbatim - Simply pressing 'enter' will select the default value. Otherwise, - the first charachter of the response will be checked against + Simply pressing 'enter' will select the default value. Otherwise, + the first charachter of the response will be checked against y/Y/n/N to determine the return value. If it is none of these, the question will be asked again. */ -bool -ask (const std::string& prompt, bool default_value); - +bool ask(const std::string& prompt, bool default_value); /***** filename functions ****/ @@ -83,16 +75,13 @@ ask (const std::string& prompt, bool default_value); char filename[max_filename_length]; ask_filename_with_extension(filename, "Input file name ?", ".img"); \endcode - \warning \a file_in_directory_name has to be preallocated + \warning \a file_in_directory_name has to be preallocated (with size \c max_filename_length) \warning starting or ending spaces are NOT stripped. */ -char * -ask_filename_with_extension(char *file_in_directory_name, - const std::string& prompt, - const std::string& default_extension); +char* ask_filename_with_extension(char* file_in_directory_name, const std::string& prompt, const std::string& default_extension); -/*! +/*! \brief Asks for a filename (appending an extension if none is provided). @@ -103,14 +92,12 @@ ask_filename_with_extension(char *file_in_directory_name, \endcode \warning starting or ending spaces are NOT stripped. */ -std::string -ask_filename_with_extension(const std::string& prompt, - const std::string& default_extension); +std::string ask_filename_with_extension(const std::string& prompt, const std::string& default_extension); /*! \brief Asks for a filename (with default extension) and opens the stream \a s - with \a mode giving the specifics. + with \a mode giving the specifics. Example: open a binary input file, aborting if it is not found \code @@ -123,22 +110,13 @@ ask_filename_with_extension(const std::string& prompt, // Implementation note: gcc 2.8.1 seems to have problems with default // values when templates are around, so I overload the function template -void -ask_filename_and_open(FSTREAM& s, - const std::string& prompt, - const std::string& default_extension, - std::ios::openmode mode, - bool abort_if_failed); +void ask_filename_and_open(FSTREAM& s, const std::string& prompt, const std::string& default_extension, std::ios::openmode mode, + bool abort_if_failed); //! as above, but with default \c abort_if_failed = true template -void -inline -ask_filename_and_open(FSTREAM& s, - const std::string& prompt, - const std::string& default_extension, - std::ios::openmode mode) -{ +void inline ask_filename_and_open(FSTREAM& s, const std::string& prompt, const std::string& default_extension, + std::ios::openmode mode) { ask_filename_and_open(s, prompt, default_extension, mode, true); } @@ -149,65 +127,60 @@ ask_filename_and_open(FSTREAM& s, \name Functions for stream/file manipulations */ //@{ -/*! +/*! \brief reads data into memory, returning a pointer to the memory If the file_size parameter is zero, the stream is read till EOF - and 'file_size' is set to the number of bytes in the file. + and 'file_size' is set to the number of bytes in the file. Otherwise 'file_size' bytes are read. The data is read from the current position in the stream. - At the end of this function, the 'input' stream will be positioned + At the end of this function, the 'input' stream will be positioned at original_position + file_size. */ -void * read_stream_in_memory(std::istream& input, std::streamsize& file_size); +void* read_stream_in_memory(std::istream& input, std::streamsize& file_size); -/*! +/*! \brief Find number of remaining characters in the stream - At the end of this function, the 'input' stream will be positioned + At the end of this function, the 'input' stream will be positioned at the original_position. \warning Works only properly for binary streams. */ -std::streamsize find_remaining_size (std::istream& input); +std::streamsize find_remaining_size(std::istream& input); //! opens a stream for reading binary data. Calls error() when it does not succeed. /*! \warning probably does not work if you are not in the C-locale */ template -inline IFSTREAM& open_read_binary(IFSTREAM& s, - const std::string& name); +inline IFSTREAM& open_read_binary(IFSTREAM& s, const std::string& name); //! opens a FILE for reading binary data. Calls error() when it does not succeed. /*! \warning probably does not work if you are not in the C-locale*/ -FILE*& open_read_binary(FILE*& fptr, - const std::string& name); +FILE*& open_read_binary(FILE*& fptr, const std::string& name); //! opens a stream for writing binary data. Calls error() when it does not succeed. -/*! +/*! Templated such that it works on std::ofstream and std::fstream. \warning probably does not work if you are not in the C-locale */ template -inline OFSTREAM& open_write_binary(OFSTREAM& s, - const std::string& name); +inline OFSTREAM& open_write_binary(OFSTREAM& s, const std::string& name); //! opens a FILE for writing binary data. Calls error() when it does not succeed. /*! \warning probably does not work if you are not in the C-locale*/ -FILE*& open_write_binary(FILE*& fptr, - const std::string& name); - +FILE*& open_write_binary(FILE*& fptr, const std::string& name); //! closes a stream without error checking. -/*! +/*! Templated such that it works on std::ofstream, std::ifstream and std::fstream. - This function is only provided in case you need to write code that works with + This function is only provided in case you need to write code that works with both std::fstream and stdio FILE. */ template inline void close_file(FSTREAM& s); //! closes a FILE without error checking. -/*! - This function is only provided in case you need to write code that works with +/*! + This function is only provided in case you need to write code that works with both std::fstream and stdio FILE. */ void close_file(FILE*& fptr); @@ -215,7 +188,7 @@ void close_file(FILE*& fptr); //@} /*! \brief - some large value to say how long filenames can be in + some large value to say how long filenames can be in the (deprecated) function ask_filename_with_extension(char *,const std::string&, const std::string&) \ingroup buildblock @@ -226,7 +199,7 @@ const int max_filename_length = 1000; /*! \ingroup buildblock \name Functions for filename manipulations - These functions work on different platforms, i.e. Unix, VAX, Windows. Also on + These functions work on different platforms, i.e. Unix, VAX, Windows. Also on older MacOS versions. \warning Functions that work on char* might be removed at some point. @@ -234,7 +207,7 @@ const int max_filename_length = 1000; //@{ //! return a pointer to the start of the filename (i.e. after directory specifications) -/*! The returned pointer is between filename_with_directory and +/*! The returned pointer is between filename_with_directory and (filename_with_directory+strlen(filename_with_directory)+1). This highest value is used when it looks like a directory name. @@ -242,8 +215,7 @@ const int max_filename_length = 1000; \warning This function works only with string manipulations. There is no check if the 'filename' part actually corresponds to a directory on disk. */ -extern const char * -find_filename(const char * const filename_with_directory); +extern const char* find_filename(const char* const filename_with_directory); //! return the position of the start of the filename (i.e. after directory specifications) /*! The returned number is between 0 and filename_with_directory.size()+1. This @@ -251,18 +223,16 @@ find_filename(const char * const filename_with_directory); \warning This function works only with string manipulations. There is no check if the 'filename' part actually corresponds to a directory on disk. */ -std::string::size_type -find_pos_of_filename(const std::string& filename_with_directory); +std::string::size_type find_pos_of_filename(const std::string& filename_with_directory); //! return a std::string containing only the filename (i.e. after directory specifications) /*! \warning This function works only with string manipulations. There is no check if the 'filename' part actually corresponds to a directory on disk. */ -std::string -get_filename(const std::string& filename_with_directory); +std::string get_filename(const std::string& filename_with_directory); -/*! +/*! \brief Copies the directory part from 'filename_with_directory' into 'directory_name' and returns the 'directory_name' pointer. @@ -272,9 +242,7 @@ get_filename(const std::string& filename_with_directory); \warning assumes that directory_name points to enough allocated space \deprecated Use get_directory_name(const std::string&) */ -char * -get_directory_name(char *directory_name, - const char * const filename_with_directory); +char* get_directory_name(char* directory_name, const char* const filename_with_directory); //! Returns a string with the directory part from 'filename_with_directory'. /*! @@ -284,27 +252,23 @@ get_directory_name(char *directory_name, \warning This function works only with string manipulations. There is no check if the 'filename' part actually corresponds to a directory on disk. */ -std::string -get_directory_name(const std::string& filename_with_directory); +std::string get_directory_name(const std::string& filename_with_directory); -/*! +/*! \brief Checks if the filename points to an absolute location, or is a relative (e.g. to current directory) pathname. */ -extern bool -is_absolute_pathname(const std::string& filename_with_directory); +extern bool is_absolute_pathname(const std::string& filename_with_directory); -/*! +/*! \brief Checks if the filename points to an absolute location, or is a relative (e.g. to current directory) pathname. */ -extern bool -is_absolute_pathname(const char * const filename_with_directory); +extern bool is_absolute_pathname(const char* const filename_with_directory); - -/*! +/*! \brief Prepend directory_name to the filename, but only if !is_absolute_pathname(filename_with_directory) @@ -312,20 +276,17 @@ is_absolute_pathname(const char * const filename_with_directory); If necessary, a directory separator is inserted. If 'directory_name' == 0, nothing happens. \return a pointer to the start of the new filename - \warning this function assumes that filename_with_directory + \warning this function assumes that filename_with_directory points to sufficient allocated space to contain the new string. */ -extern char * -prepend_directory_name(char * filename_with_directory, - const char * const directory_name); +extern char* prepend_directory_name(char* filename_with_directory, const char* const directory_name); //! find the position of the '.' of the extension -/*! +/*! If no '.' is found in the filename part (i.e. ignoring the directory name), the function returns \c std::string::npos */ -std::string::size_type -find_pos_of_extension(const std::string& file_in_directory_name); +std::string::size_type find_pos_of_extension(const std::string& file_in_directory_name); #if 0 // terribly dangerous for memory overrun. @@ -351,9 +312,7 @@ add_extension(char * file_in_directory_name, #endif //! Append extension if none present -std::string& -add_extension(std::string& file_in_directory_name, - const std::string& extension); +std::string& add_extension(std::string& file_in_directory_name, const std::string& extension); #if 0 // disabled because possible memory overrun @@ -380,10 +339,7 @@ replace_extension(char *file_in_directory_name, #endif //! Replace extension (or append if none present) -std::string& -replace_extension(std::string& file_in_directory_name, - const std::string& extension); - +std::string& replace_extension(std::string& file_in_directory_name, const std::string& extension); //@} @@ -394,10 +350,10 @@ replace_extension(std::string& file_in_directory_name, //! make C-string uppercase /*! \ingroup buildblock -*/ -inline char *strupr(char * const str); + */ +inline char* strupr(char* const str); #else -#define strupr _strupr +# define strupr _strupr #endif END_NAMESPACE_STIR diff --git a/src/include/stir/utilities.inl b/src/include/stir/utilities.inl index 84ddedd578..07f446937e 100644 --- a/src/include/stir/utilities.inl +++ b/src/include/stir/utilities.inl @@ -1,6 +1,6 @@ /*! - \file - \ingroup buildblock + \file + \ingroup buildblock \brief inline implementations for utility.h \author Kris Thielemans @@ -25,95 +25,85 @@ */ #include #ifdef BOOST_NO_STRINGSTREAM -#include +# include #else -#include +# include #endif START_NAMESPACE_STIR /*! The question is currently presented as \verbatim - str_text : [minimum_value, maximum_value, D: default_value]: + str_text : [minimum_value, maximum_value, D: default_value]: \endverbatim - Simply pressing 'enter' will select the default value. Otherwise, range + Simply pressing 'enter' will select the default value. Otherwise, range checking is performed, and the question asked again if necessary. */ template -inline NUMBER -ask_num (const std::string& str, - NUMBER minimum_value, - NUMBER maximum_value, - NUMBER default_value) -{ - - while(1) - { +inline NUMBER +ask_num(const std::string& str, NUMBER minimum_value, NUMBER maximum_value, NUMBER default_value) { + + while (1) { std::string input; - std::cerr << "\n" << str - << "[" << minimum_value << "," << maximum_value - << " D:" << default_value << "]: "; + std::cerr << "\n" << str << "[" << minimum_value << "," << maximum_value << " D:" << default_value << "]: "; std::getline(std::cin, input); #ifdef BOOST_NO_STRINGSTREAM istrstream ss(input.c_str()); #else std::istringstream ss(input.c_str()); #endif - + NUMBER value = default_value; ss >> value; - if ((value>=minimum_value) && (maximum_value>=value)) + if ((value >= minimum_value) && (maximum_value >= value)) return value; std::cerr << "\nOut of bounds. Try again."; } } - - template -inline IFSTREAM& open_read_binary(IFSTREAM& s, - const std::string& name) -{ +inline IFSTREAM& +open_read_binary(IFSTREAM& s, const std::string& name) { #if 0 //KT 30/07/98 The next lines are only necessary (in VC 5.0) when importing // . We use now, so they are disabled. // Visual C++ does not complain when opening a nonexisting file for reading, // unless using std::ios::nocreate - s.open(name.c_str(), std::ios::in | std::ios::binary | std::ios::nocreate); + s.open(name.c_str(), std::ios::in | std::ios::binary | std::ios::nocreate); #else - s.open(name.c_str(), std::ios::in | std::ios::binary); + s.open(name.c_str(), std::ios::in | std::ios::binary); #endif // KT 14/01/2000 added name of file in error message - if (s.fail() || s.bad()) - { error("Error opening file %s\n", name.c_str()); } + if (s.fail() || s.bad()) { + error("Error opening file %s\n", name.c_str()); + } return s; } template -inline OFSTREAM& open_write_binary(OFSTREAM& s, - const std::string& name) -{ - s.open(name.c_str(), std::ios::out | std::ios::binary); - // KT 14/01/2000 added name of file in error message - if (s.fail() || s.bad()) - { error("Error opening file %s\n", name.c_str()); } - return s; +inline OFSTREAM& +open_write_binary(OFSTREAM& s, const std::string& name) { + s.open(name.c_str(), std::ios::out | std::ios::binary); + // KT 14/01/2000 added name of file in error message + if (s.fail() || s.bad()) { + error("Error opening file %s\n", name.c_str()); + } + return s; } template -inline void close_file(FSTREAM& s) -{ +inline void +close_file(FSTREAM& s) { s.close(); } - #ifndef _MSC_VER -char *strupr(char * const str) -{ - for (char *a = str; *a; a++) - { - if ((*a >= 'a')&&(*a <= 'z')) *a += 'A'-'a'; +char* +strupr(char* const str) { + for (char* a = str; *a; a++) { + if ((*a >= 'a') && (*a <= 'z')) + *a += 'A' - 'a'; }; return str; } diff --git a/src/include/stir/warning.h b/src/include/stir/warning.h index 81aaba9985..68006b9a01 100644 --- a/src/include/stir/warning.h +++ b/src/include/stir/warning.h @@ -43,9 +43,7 @@ START_NAMESPACE_STIR \deprecated (use 1 argument version instead) */ -void -warning(const char *const s, ...); - +void warning(const char* const s, ...); //! Use this function for writing warning messages /*! \ingroup buildblock @@ -54,7 +52,7 @@ warning(const char *const s, ...); std::ostream::operator\<\< would work. This function currently first writes a newline, then \c WARNING:, then \c string - and then another newline to std::cerr. + and then another newline to std::cerr. \todo At a later stage, it will also write to a log-file. @@ -70,11 +68,8 @@ warning(const char *const s, ...); template inline void -warning(const STRING& string) -{ - std::cerr << "\nWARNING: " - << string - << std::endl; +warning(const STRING& string) { + std::cerr << "\nWARNING: " << string << std::endl; } END_NAMESPACE_STIR #endif diff --git a/src/include/stir/zoom.h b/src/include/stir/zoom.h index 4e25da9182..e38892311a 100644 --- a/src/include/stir/zoom.h +++ b/src/include/stir/zoom.h @@ -17,11 +17,11 @@ See STIR/LICENSE.txt for details */ #ifndef __stir_ZOOM_H__ -#define __stir_ZOOM_H__ +#define __stir_ZOOM_H__ /*! - \file - + \file + \brief This file declares various zooming functions. \ingroup buildblock \author Kris Thielemans @@ -32,15 +32,15 @@ These functions can be used for zooming of projection data or image data. - Zooming requires interpolation. Currently, this is done using + Zooming requires interpolation. Currently, this is done using stir::overlap_interpolate. - + The first set of functions allows zooming and translation in transaxial planes only. These parameters are the same for projection data or image data. That is, zoomed projection data are (approximately) the forward projections of zoomed images with the same parameters. These functions have the following parameters: - + \param zoom scales the projection bins (zoom larger than 1 means more detail, so smaller pixels) \param x_offset_in_mm x-coordinate of new origin (in mm) \param y_offset_in_mm y-coordinate of new origin (in mm) @@ -50,13 +50,13 @@ This allows 2-dimensional zooming and translation on arccorrected data, here translation means a translation in 'image' space which gives a \c sin shift of origin in the \c s - coordinate in - projection space. + projection space. The new range of \c s coordinates is given by - \param min_tang_pos_num [for projection data only] the minimum tangential position number in the new + \param min_tang_pos_num [for projection data only] the minimum tangential position number in the new projection line - \param max_tang_pos_num [for projection data only] the maximum tangential position number in the new + \param max_tang_pos_num [for projection data only] the maximum tangential position number in the new projection line - Note that the (projection of the) centre of the scanner axis is supposed to be + Note that the (projection of the) centre of the scanner axis is supposed to be at \a tang_pos_num = 0. \par images @@ -65,27 +65,27 @@ zooming and translation. Parameters are derived either from stir::CartesianCoordinate3D objects, or from the information in the \a in and \a out images. - + In the case that the offsets are 0, the following holds:
    • If \c size is equal to \c zoom*old_size, the same amount of data is represented.
    • If it is less, data are truncated.
    • If it is larger, the outer ends are filled with 0.
    - - + + \warning Because overlap_interpolate is used, the zooming is 'count-preserving', i.e. when the output range is large enough, the in.sum() == out.sum(). \warning In STIR 1.x, origins were taken relative to the centre of the coordinate range: -\code - x_in_mm = (x_index - x_ctr_index) * voxel_size.x() + origin.x() +\code + x_in_mm = (x_index - x_ctr_index) * voxel_size.x() + origin.x() \endcode - where -\code + where +\code x_ctr_index = (x_max_index + x_min_index)/2 \endcode - This is no longer true. Instead we use + This is no longer true. Instead we use DiscretisedDensity\<3,float\>::get_physical_coordinates_for_indices. */ @@ -94,12 +94,18 @@ START_NAMESPACE_STIR -template class Viewgram; -template class RelatedViewgrams; -template class VoxelsOnCartesianGrid; -template class PixelsOnCartesianGrid; -template class CartesianCoordinate3D; -template class BasicCoordinate; +template +class Viewgram; +template +class RelatedViewgrams; +template +class VoxelsOnCartesianGrid; +template +class PixelsOnCartesianGrid; +template +class CartesianCoordinate3D; +template +class BasicCoordinate; /*! \ingroup buildblock @@ -111,34 +117,24 @@ template class BasicCoordinate; \brief zoom a RelatedViewgrams object, replacing it with the new data \see zoom.h for parameter info */ -void -zoom_viewgrams (RelatedViewgrams& viewgrams, - const float zoom, - const int min_tang_pos_num, const int max_tang_pos_num, - const float x_offset_in_mm = 0, const float y_offset_in_mm = 0); +void zoom_viewgrams(RelatedViewgrams& viewgrams, const float zoom, const int min_tang_pos_num, const int max_tang_pos_num, + const float x_offset_in_mm = 0, const float y_offset_in_mm = 0); /*! \brief zoom \a viewgram, replacing it with the new data \see zoom.h for parameter info */ -void -zoom_viewgram (Viewgram& viewgram, - const float zoom, - const int min_tang_pos_num, const int max_tang_pos_num, - const float x_offset_in_mm = 0, const float y_offset_in_mm = 0); +void zoom_viewgram(Viewgram& viewgram, const float zoom, const int min_tang_pos_num, const int max_tang_pos_num, + const float x_offset_in_mm = 0, const float y_offset_in_mm = 0); /*! \brief zoom \a in_viewgram, replacing \a out_viewgram with the new data - - This version of zoom_viewgram gets the info on the new sizes, sampling etc. + + This version of zoom_viewgram gets the info on the new sizes, sampling etc. from \a out_viewgram. \see zoom.h for parameter info */ -void -zoom_viewgram (Viewgram& out_viewgram, - const Viewgram& in_viewgram, - const float x_offset_in_mm = 0, const float y_offset_in_mm = 0); - - +void zoom_viewgram(Viewgram& out_viewgram, const Viewgram& in_viewgram, const float x_offset_in_mm = 0, + const float y_offset_in_mm = 0); /*! \brief zoom \a image, returning the new image @@ -146,21 +142,18 @@ zoom_viewgram (Viewgram& out_viewgram, \see zoom_image_in_place */ -VoxelsOnCartesianGrid -zoom_image(const VoxelsOnCartesianGrid &image, - const CartesianCoordinate3D& zooms, - const CartesianCoordinate3D& offsets_in_mm, - const BasicCoordinate<3,int>& new_sizes, - const ZoomOptions = ZoomOptions::preserve_sum); - -/*! - \brief - zoom \a image, replacing the first argument with the new data. - Full 3D shifts and zooms. +VoxelsOnCartesianGrid zoom_image(const VoxelsOnCartesianGrid& image, const CartesianCoordinate3D& zooms, + const CartesianCoordinate3D& offsets_in_mm, + const BasicCoordinate<3, int>& new_sizes, const ZoomOptions = ZoomOptions::preserve_sum); + +/*! + \brief + zoom \a image, replacing the first argument with the new data. + Full 3D shifts and zooms. \see zoom.h for parameter info. Zooming is done such that the physical coordinates of a point - (as returned by + (as returned by DiscretisedDensity\<3,float\>::get_physical_coordinates_for_indices) remain the same. @@ -177,19 +170,16 @@ zoom_image(const VoxelsOnCartesianGrid &image, \warning: For even-sized images, this convention can lead to somewhat non-intuitive results (half-pixel shifts etc). */ -void -zoom_image_in_place(VoxelsOnCartesianGrid &image, - const CartesianCoordinate3D& zooms, - const CartesianCoordinate3D& offsets_in_mm, - const BasicCoordinate<3,int>& new_sizes, - const ZoomOptions = ZoomOptions::preserve_sum); +void zoom_image_in_place(VoxelsOnCartesianGrid& image, const CartesianCoordinate3D& zooms, + const CartesianCoordinate3D& offsets_in_mm, const BasicCoordinate<3, int>& new_sizes, + const ZoomOptions = ZoomOptions::preserve_sum); /*! \brief zoom \a image_in according to dimensions, origin and voxel_size of \a image_out. \see zoom.h for parameter info Zooming is done such that the physical coordinates of a point - (as returned by + (as returned by DiscretisedDensity\<3,float\>::get_physical_coordinates_for_indices) remain the same. @@ -198,49 +188,37 @@ zoom_image_in_place(VoxelsOnCartesianGrid &image, (ii) preserving the image projectors: should be used when zooming an activity image (iii) preserving the image sum: default */ -void -zoom_image(VoxelsOnCartesianGrid &image_out, - const VoxelsOnCartesianGrid &image_in, - const ZoomOptions = ZoomOptions::preserve_sum); +void zoom_image(VoxelsOnCartesianGrid& image_out, const VoxelsOnCartesianGrid& image_in, + const ZoomOptions = ZoomOptions::preserve_sum); //------------------ 2D zooms--------------------- -/*! -\brief -zoom \a image2D_in according to dimensions, origin and pixel_size of +/*! +\brief +zoom \a image2D_in according to dimensions, origin and pixel_size of \a image2D_out. \see zoom.h for parameter info */ -void -zoom_image(PixelsOnCartesianGrid &image2D_out, - const PixelsOnCartesianGrid &image2D_in, - const ZoomOptions = ZoomOptions::preserve_sum); +void zoom_image(PixelsOnCartesianGrid& image2D_out, const PixelsOnCartesianGrid& image2D_in, + const ZoomOptions = ZoomOptions::preserve_sum); /*! \brief zoom \a image, replacing the first argument with the new data \see zoom.h for parameter info */ -VoxelsOnCartesianGrid -zoom_image(const VoxelsOnCartesianGrid &image, - const float zoom, - const float x_offset_in_mm, const float y_offset_in_mm, - const int new_size, - const ZoomOptions = ZoomOptions::preserve_sum); +VoxelsOnCartesianGrid zoom_image(const VoxelsOnCartesianGrid& image, const float zoom, const float x_offset_in_mm, + const float y_offset_in_mm, const int new_size, + const ZoomOptions = ZoomOptions::preserve_sum); /*! \brief zoom \a image, replacing the first argument with the new data \see zoom.h for parameter info */ -void -zoom_image_in_place(VoxelsOnCartesianGrid &image, - const float zoom, - const float x_offset_in_mm, const float y_offset_in_mm, - const int new_size, - const ZoomOptions = ZoomOptions::preserve_sum); +void zoom_image_in_place(VoxelsOnCartesianGrid& image, const float zoom, const float x_offset_in_mm, + const float y_offset_in_mm, const int new_size, const ZoomOptions = ZoomOptions::preserve_sum); //@} END_NAMESPACE_STIR #endif - diff --git a/src/include/stir_experimental/AbsTimeInterval.h b/src/include/stir_experimental/AbsTimeInterval.h index 8d4ad0e2d7..ec98cccfaf 100644 --- a/src/include/stir_experimental/AbsTimeInterval.h +++ b/src/include/stir_experimental/AbsTimeInterval.h @@ -27,35 +27,22 @@ START_NAMESPACE_STIR Absolute time means at present 'secs since midnight 1/1/1970 UTC' */ -class AbsTimeInterval: public RegisteredObject -{ +class AbsTimeInterval : public RegisteredObject { public: virtual ~AbsTimeInterval() {} - AbsTimeInterval() - : - _start_time_in_secs_since_1970(0), - _end_time_in_secs_since_1970(0) - {} - AbsTimeInterval( double start_time_in_secs_since_1970, - double end_time_in_secs_since_1970) - : - _start_time_in_secs_since_1970(start_time_in_secs_since_1970), - _end_time_in_secs_since_1970(end_time_in_secs_since_1970) - {} - - - double get_start_time_in_secs_since_1970() const - { return _start_time_in_secs_since_1970; } - double get_end_time_in_secs_since_1970() const - { return _end_time_in_secs_since_1970; } - double get_duration_in_secs() const - { return _end_time_in_secs_since_1970 - _start_time_in_secs_since_1970; } - - protected: + AbsTimeInterval() : _start_time_in_secs_since_1970(0), _end_time_in_secs_since_1970(0) {} + AbsTimeInterval(double start_time_in_secs_since_1970, double end_time_in_secs_since_1970) + : _start_time_in_secs_since_1970(start_time_in_secs_since_1970), _end_time_in_secs_since_1970(end_time_in_secs_since_1970) { + } + + double get_start_time_in_secs_since_1970() const { return _start_time_in_secs_since_1970; } + double get_end_time_in_secs_since_1970() const { return _end_time_in_secs_since_1970; } + double get_duration_in_secs() const { return _end_time_in_secs_since_1970 - _start_time_in_secs_since_1970; } + +protected: double _start_time_in_secs_since_1970; double _end_time_in_secs_since_1970; - }; END_NAMESPACE_STIR diff --git a/src/include/stir_experimental/AbsTimeIntervalFromDynamicData.h b/src/include/stir_experimental/AbsTimeIntervalFromDynamicData.h index 11ff451c99..570ccd0d34 100644 --- a/src/include/stir_experimental/AbsTimeIntervalFromDynamicData.h +++ b/src/include/stir_experimental/AbsTimeIntervalFromDynamicData.h @@ -29,7 +29,7 @@ class Succeeded; The dynamic scan can be either a sinogram of an image. It is read via DynamicProjData or DynamicDiscretisedDensity. - + \par Example .par file \verbatim ; example keyword in some .par file @@ -43,27 +43,23 @@ class Succeeded; */ class AbsTimeIntervalFromDynamicData -: public RegisteredParsingObject -{ + : public RegisteredParsingObject { public: - //! Name which will be used when parsing a AbsTimeInterval object - static const char * const registered_name; + //! Name which will be used when parsing a AbsTimeInterval object + static const char* const registered_name; virtual ~AbsTimeIntervalFromDynamicData() {} //! default constructor gives ill-defined values AbsTimeIntervalFromDynamicData(); //! read info from file /*! will call error() if something goes wrong */ - AbsTimeIntervalFromDynamicData(const std::string& filename, - const unsigned int start_time_frame_num, + AbsTimeIntervalFromDynamicData(const std::string& filename, const unsigned int start_time_frame_num, const unsigned int end_time_frame_num); - - private: - std::string _filename; - //TimeFrameDefinitions _time_frame_defs; + +private: + std::string _filename; + // TimeFrameDefinitions _time_frame_defs; double _scan_start_time_in_secs_since_1970; // start of scan, not the time-interval unsigned int _start_time_frame_num; unsigned int _end_time_frame_num; @@ -72,9 +68,7 @@ class AbsTimeIntervalFromDynamicData virtual void initialise_keymap(); virtual bool post_processing(); - Succeeded set_times(); - }; END_NAMESPACE_STIR diff --git a/src/include/stir_experimental/AbsTimeIntervalFromECAT7ACF.h b/src/include/stir_experimental/AbsTimeIntervalFromECAT7ACF.h index 7693defa86..b0853ac73e 100644 --- a/src/include/stir_experimental/AbsTimeIntervalFromECAT7ACF.h +++ b/src/include/stir_experimental/AbsTimeIntervalFromECAT7ACF.h @@ -27,30 +27,26 @@ class Succeeded; \brief class for specifying a time interval via an ECAT7 .a file The ECAT7 header for a .a file does not record the scan duration, but only - the start time of the transmission scan. So, we have to explicitly ask for + the start time of the transmission scan. So, we have to explicitly ask for the duration. - + */ class AbsTimeIntervalFromECAT7ACF -: public RegisteredParsingObject -{ + : public RegisteredParsingObject { public: - //! Name which will be used when parsing a AbsTimeInterval object - static const char * const registered_name; + //! Name which will be used when parsing a AbsTimeInterval object + static const char* const registered_name; virtual ~AbsTimeIntervalFromECAT7ACF() {} //! default constructor sets duration to -1 (i.e. ill-defined) AbsTimeIntervalFromECAT7ACF(); //! read info from ECAT7 file /*! will call error() if something goes wrong */ - AbsTimeIntervalFromECAT7ACF(const std::string& filename, - const double duration_in_secs); - - private: - std::string _attenuation_filename; + AbsTimeIntervalFromECAT7ACF(const std::string& filename, const double duration_in_secs); + +private: + std::string _attenuation_filename; double _transmission_duration; virtual void set_defaults(); @@ -58,7 +54,6 @@ class AbsTimeIntervalFromECAT7ACF virtual bool post_processing(); Succeeded set_times(); - }; END_NAMESPACE_STIR diff --git a/src/include/stir_experimental/AbsTimeIntervalWithParsing.h b/src/include/stir_experimental/AbsTimeIntervalWithParsing.h index dae449bd51..88ff94b55b 100644 --- a/src/include/stir_experimental/AbsTimeIntervalWithParsing.h +++ b/src/include/stir_experimental/AbsTimeIntervalWithParsing.h @@ -29,28 +29,22 @@ class Succeeded; start and end times from a file. This functionality could potentially be put in AbsTimeInterval, but that would give an overlap/conflict with keywords of other derived classes. - + */ -class AbsTimeIntervalWithParsing -: public RegisteredParsingObject -{ +class AbsTimeIntervalWithParsing : public RegisteredParsingObject { public: - //! Name which will be used when parsing a AbsTimeInterval object - static const char * const registered_name; + //! Name which will be used when parsing a AbsTimeInterval object + static const char* const registered_name; virtual ~AbsTimeIntervalWithParsing() {} //! default constructor sets times to invalid values AbsTimeIntervalWithParsing(); - - private: +private: virtual void set_defaults(); virtual void initialise_keymap(); virtual bool post_processing(); - }; END_NAMESPACE_STIR diff --git a/src/include/stir_experimental/DAVArrayFilter3D.h b/src/include/stir_experimental/DAVArrayFilter3D.h index 489050f659..917e18f38d 100644 --- a/src/include/stir_experimental/DAVArrayFilter3D.h +++ b/src/include/stir_experimental/DAVArrayFilter3D.h @@ -4,13 +4,13 @@ \file - \brief + \brief \author Sanida Mustafovic \author Kris Thielemans - Warning: - At the moment it is essential to have + Warning: + At the moment it is essential to have mask_radius_x = mask_radius_y =mask_radius_z = odd. */ @@ -19,39 +19,32 @@ See STIR/LICENSE.txt for details */ - #ifndef __stir_DAVArrayFilter3D_H__ #define __stir_DAVArrayFilter3D_H__ #include "stir/ArrayFunctionObject_2ArgumentImplementation.h" - START_NAMESPACE_STIR -template class Coordinate3D; - +template +class Coordinate3D; template -class DAVArrayFilter3D: public ArrayFunctionObject_2ArgumentImplementation<3,elemT> -{ +class DAVArrayFilter3D : public ArrayFunctionObject_2ArgumentImplementation<3, elemT> { public: - DAVArrayFilter3D (const Coordinate3D& mask_radius = Coordinate3D()); + DAVArrayFilter3D(const Coordinate3D& mask_radius = Coordinate3D()); bool is_trivial() const; private: - int mask_radius_x; - int mask_radius_y; - int mask_radius_z; - - virtual void do_it (Array<3,elemT>& out_array, const Array<3,elemT>& in_array) const; + int mask_radius_x; + int mask_radius_y; + int mask_radius_z; - void extract_neighbours_and_average(elemT& out_elem, const Array<3,elemT>& in_array,const Coordinate3D& c_pixel) const; + virtual void do_it(Array<3, elemT>& out_array, const Array<3, elemT>& in_array) const; + void extract_neighbours_and_average(elemT& out_elem, const Array<3, elemT>& in_array, const Coordinate3D& c_pixel) const; }; END_NAMESPACE_STIR #endif - - - diff --git a/src/include/stir_experimental/DAVImageFilter3D.h b/src/include/stir_experimental/DAVImageFilter3D.h index 3c841dfcc1..9c5b758ca6 100644 --- a/src/include/stir_experimental/DAVImageFilter3D.h +++ b/src/include/stir_experimental/DAVImageFilter3D.h @@ -24,13 +24,12 @@ \author Sanida Mustafovic \author Kris Thielemans - + */ #ifndef __stir_DAVImageFilter3D_H__ #define __stir_DAVImageFilter3D_H__ - #include "stir/DataProcessor.h" #include "stir/DiscretisedDensity.h" #include "stir_experimental/DAVArrayFilter3D.h" @@ -39,42 +38,35 @@ START_NAMESPACE_STIR -template class CartesianCoordinate3D; +template +class CartesianCoordinate3D; template -class DAVImageFilter3D: - public - RegisteredParsingObject< - DAVImageFilter3D, - DataProcessor >, - DataProcessor > - > +class DAVImageFilter3D : public RegisteredParsingObject, DataProcessor>, + DataProcessor>> { public: - static const char * const registered_name; + static const char* const registered_name; DAVImageFilter3D(); - DAVImageFilter3D(const CartesianCoordinate3D& mask_radius); - + DAVImageFilter3D(const CartesianCoordinate3D& mask_radius); + private: DAVArrayFilter3D dav_filter; int mask_radius_x; int mask_radius_y; int mask_radius_z; - virtual void set_defaults(); virtual void initialise_keymap(); - Succeeded virtual_set_up (const DiscretisedDensity< 3,elemT>& density); - void virtual_apply(DiscretisedDensity<3,elemT>& density, const DiscretisedDensity<3,elemT>& in_density) const; - void virtual_apply(DiscretisedDensity<3,elemT>& density) const; + Succeeded virtual_set_up(const DiscretisedDensity<3, elemT>& density); + void virtual_apply(DiscretisedDensity<3, elemT>& density, const DiscretisedDensity<3, elemT>& in_density) const; + void virtual_apply(DiscretisedDensity<3, elemT>& density) const; }; - END_NAMESPACE_STIR - -#endif // DAVImageFilter3D +#endif // DAVImageFilter3D diff --git a/src/include/stir_experimental/Filter.h b/src/include/stir_experimental/Filter.h index 13d1263343..c38a1cd905 100644 --- a/src/include/stir_experimental/Filter.h +++ b/src/include/stir_experimental/Filter.h @@ -1,8 +1,8 @@ // // -/*! - \file +/*! + \file \brief Filter classes (filter defined in Fourier space) \author Kris Thielemans \author Claire LABBE @@ -14,9 +14,8 @@ See STIR/LICENSE.txt for details */ - #ifndef __FILTER_H__ -#define __FILTER_H__ +#define __FILTER_H__ #include "stir/TimedObject.h" #include "stir/Array.h" @@ -25,8 +24,8 @@ START_NAMESPACE_STIR -template class Array; - +template +class Array; /*! \brief Preliminary class for 1D filtering using FFTs @@ -34,112 +33,107 @@ template class Array; \warning The filtering will be performed using the convlvC() function. See warnings in its documentation. \todo apply() members can't be const as they call TimedObject::start_timers() */ -template class Filter1D : public TimedObject - { - // AZ public, as need to access length for parallel FBP3DRP code (TODO) -// protected: - public: - //! Stores the filter in frequency space (will be private sometime) - Array<1,float> filter; - public: - Filter1D(const int length_v) - : filter(1,length_v) - {} - Filter1D(const Array<1,T> &filter) - : filter(filter) - {} - virtual ~Filter1D() {} - - //! Filters data (which has to be in the 'spatial' domain - /*! data will be padded with zeroes to the same length as the stored filter before - filtering. This is done on a local copy of the data, such that the index range of - \a data is not modified. - */ - inline void apply(Array<1,T> &data); - //! Applies the filter on each 1D subarray - inline void apply(Array<2,T> &data); - //! Applies the filter on each 1D subarray - inline void apply(Array<3,T> &data); - - - virtual std::string parameter_info() const = 0; +template +class Filter1D : public TimedObject { + // AZ public, as need to access length for parallel FBP3DRP code (TODO) + // protected: +public: + //! Stores the filter in frequency space (will be private sometime) + Array<1, float> filter; + +public: + Filter1D(const int length_v) : filter(1, length_v) {} + Filter1D(const Array<1, T>& filter) : filter(filter) {} + virtual ~Filter1D() {} + + //! Filters data (which has to be in the 'spatial' domain + /*! data will be padded with zeroes to the same length as the stored filter before + filtering. This is done on a local copy of the data, such that the index range of + \a data is not modified. + */ + inline void apply(Array<1, T>& data); + //! Applies the filter on each 1D subarray + inline void apply(Array<2, T>& data); + //! Applies the filter on each 1D subarray + inline void apply(Array<3, T>& data); + + virtual std::string parameter_info() const = 0; }; // TODO can't be const due to start_timers() -template void Filter1D ::apply(Array<1,T> &data) //const +template +void +Filter1D::apply(Array<1, T>& data) // const { start_timers(); const int length = filter.get_length(); - if (length==0) + if (length == 0) return; - assert(length>0); + assert(length > 0); - Array<1,T> Padded = data; + Array<1, T> Padded = data; Padded.set_offset(1); - Padded.grow(1,length); - convlvC(Padded, filter,length); + Padded.grow(1, length); + convlvC(Padded, filter, length); Padded.set_offset(data.get_min_index()); - for (int j=data.get_min_index();j<=data.get_max_index();j++) - data[j]= Padded[j]; + for (int j = data.get_min_index(); j <= data.get_max_index(); j++) + data[j] = Padded[j]; stop_timers(); } -template void Filter1D ::apply(Array<2,T> &data)// const +template +void +Filter1D::apply(Array<2, T>& data) // const { - for (int i= data.get_min_index(); i <= data.get_max_index(); i++) - apply(data[i]); + for (int i = data.get_min_index(); i <= data.get_max_index(); i++) + apply(data[i]); } -template void Filter1D ::apply(Array<3,T> &data)// const +template +void +Filter1D::apply(Array<3, T>& data) // const { - for (int i= data.get_min_index(); i <= data.get_max_index(); i++) - apply(data[i]); + for (int i = data.get_min_index(); i <= data.get_max_index(); i++) + apply(data[i]); } - - /*! \brief 2-dimensional filters (filtering done by FFTs) */ -template class Filter2D : public TimedObject -{ +template +class Filter2D : public TimedObject { public: - Filter2D(int height_v, int width_v) - : height(height_v), width(width_v), filter(1,width_v*height_v) - {} + Filter2D(int height_v, int width_v) : height(height_v), width(width_v), filter(1, width_v * height_v) {} - virtual ~Filter2D() {} - inline void apply(Array<1,T> &data) const; + virtual ~Filter2D() {} + inline void apply(Array<1, T>& data) const; // TODO ??? void padd_scale_filter(int height_proj, int width_proj); - + virtual std::string parameter_info() const = 0; protected: - int height; - int width; + int height; + int width; //! Stores the filter in the 'funny' but efficient Numerical Recipes format - Array<1,T> filter; - + Array<1, T> filter; }; +template +void +Filter2D::apply(Array<1, T>& data) const { - - -template void Filter2D::apply(Array<1,T> &data) const -{ - - assert(data.get_length() == 2*height*width); + assert(data.get_length() == 2 * height * width); assert(data.get_min_index() == 1); - int j,k; + int j, k; /*TODO copy lines from Filter_proj_Colsher for this convertion, and call apply with a Array<2,T> Array<1,T> data(1, 2*height*width); @@ -148,24 +142,21 @@ template void Filter2D::apply(Array<1,T> &data) const data[j++] = data2d[h][w]; } */ - - Array<1,int> nn(1,2); + + Array<1, int> nn(1, 2); nn[1] = height; nn[2] = width; fourn(data, nn, 2, 1); - for (j = 1, k = 1; j < width * height + 1; j++) - { + for (j = 1, k = 1; j < width * height + 1; j++) { // KT 11/04/2000 normalise with total length // divide by width*height to take Num Recipes convention into account that the // 'inverse' Fourier transform needs scaling by the total number of data points. - data[k++] *= filter[j]/(width * height); - data[k++] *= filter[j]/(width * height); + data[k++] *= filter[j] / (width * height); + data[k++] *= filter[j] / (width * height); } fourn(data, nn, 2, -1); - }; END_NAMESPACE_STIR #endif - diff --git a/src/include/stir_experimental/ModifiedInverseAveragingImageFilterAll.h b/src/include/stir_experimental/ModifiedInverseAveragingImageFilterAll.h index 046e11e2d6..d4bb8cdaf7 100644 --- a/src/include/stir_experimental/ModifiedInverseAveragingImageFilterAll.h +++ b/src/include/stir_experimental/ModifiedInverseAveragingImageFilterAll.h @@ -3,15 +3,15 @@ /*! \file - \ingroup buildblock - \brief This is a messy, first, attempt to design spatially varying filter - Given the kernel which in this case is a lospass filter with a DC gain 1 the filter + \ingroup buildblock + \brief This is a messy, first, attempt to design spatially varying filter + Given the kernel which in this case is a lospass filter with a DC gain 1 the filter is design such that the output kernel varies depending on the k0 and k1 ( for more details on these factors look at Fessler) - + \author Sanida Mustafovic \author Kris Thielemans - + */ /* Copyright (C) 2000- 2007, Hammersmith Imanet @@ -21,8 +21,6 @@ #ifndef __stir_ModifiedInverseAveragingImageFilterAll_H__ #define __stir_ModifiedInverseAveragingImageFilterAll_H__ - - #include "stir_experimental/ModifiedInverseAverigingArrayFilter.h" #include "stir/DiscretisedDensity.h" #include "stir/DataProcessor.h" @@ -34,106 +32,91 @@ #include "stir/shared_ptr.h" - - - START_NAMESPACE_STIR // TODO!! remove define #define num_dimensions 3 - template -class ModifiedInverseAveragingImageFilterAll: -public - RegisteredParsingObject< - ModifiedInverseAveragingImageFilterAll, - DataProcessor >, - DataProcessor > - > -{ - private: - typedef - RegisteredParsingObject< - ModifiedInverseAveragingImageFilterAll, - DataProcessor >, - DataProcessor > - > - base_type; -public: - static const char * const registered_name; +class ModifiedInverseAveragingImageFilterAll + : public RegisteredParsingObject, + DataProcessor>, + DataProcessor>> { +private: + typedef RegisteredParsingObject, + DataProcessor>, + DataProcessor>> + base_type; + +public: + static const char* const registered_name; //! Default constructor - ModifiedInverseAveragingImageFilterAll(); - - ModifiedInverseAveragingImageFilterAll(string proj_data_filename, - string attenuation_proj_data_filename, - const VectorWithOffset& filter_coefficients, - shared_ptr proj_data_ptr, - shared_ptr attenuation_proj_data_ptr, - DiscretisedDensity<3,float>* initial_image, - DiscretisedDensity<3,float>* sensitivity_image, - DiscretisedDensity<3,float>* precomputed_coefficients_image, - DiscretisedDensity<3,float>* normalised_bck_image, - int mask_size, - int num_dim); - - - -private: + ModifiedInverseAveragingImageFilterAll(); + ModifiedInverseAveragingImageFilterAll(string proj_data_filename, string attenuation_proj_data_filename, + const VectorWithOffset& filter_coefficients, shared_ptr proj_data_ptr, + shared_ptr attenuation_proj_data_ptr, + DiscretisedDensity<3, float>* initial_image, + DiscretisedDensity<3, float>* sensitivity_image, + DiscretisedDensity<3, float>* precomputed_coefficients_image, + DiscretisedDensity<3, float>* normalised_bck_image, int mask_size, int num_dim); + +private: int mask_size; vector filter_coefficients_for_parsing; VectorWithOffset filter_coefficients; - + shared_ptr proj_data_ptr; string proj_data_filename; shared_ptr attenuation_proj_data_ptr; string attenuation_proj_data_filename; - DiscretisedDensity<3,float>* initial_image; + DiscretisedDensity<3, float>* initial_image; string initial_image_filename; - - DiscretisedDensity<3,float>* sensitivity_image; + + DiscretisedDensity<3, float>* sensitivity_image; string sensitivity_image_filename; - DiscretisedDensity<3,float>* precomputed_coefficients_image ; + DiscretisedDensity<3, float>* precomputed_coefficients_image; string precomputed_coefficients_filename; - DiscretisedDensity<3,float>* normalised_bck_image ; + DiscretisedDensity<3, float>* normalised_bck_image; string normalised_bck_filename; int num_dim; - Succeeded virtual_set_up(const DiscretisedDensity& density); - // new - void virtual_apply(DiscretisedDensity& out_density, const DiscretisedDensity& in_density) const; - void virtual_apply(DiscretisedDensity& density) const; - void precalculate_filter_coefficients (VectorWithOffset < VectorWithOffset < VectorWithOffset > > > >& all_filter_coefficients, - DiscretisedDensity<3,elemT>* in_density) const; - - void precalculate_filter_coefficients_2D (VectorWithOffset < VectorWithOffset < VectorWithOffset > > > >& all_filter_coefficients, - DiscretisedDensity<3,elemT>* in_density) const; - - void precalculate_filter_coefficients_separable(VectorWithOffset < VectorWithOffset < VectorWithOffset > > > >& all_filter_coefficients, - DiscretisedDensity<3,elemT>* in_density) const; - + Succeeded virtual_set_up(const DiscretisedDensity& density); + // new + void virtual_apply(DiscretisedDensity& out_density, + const DiscretisedDensity& in_density) const; + void virtual_apply(DiscretisedDensity& density) const; + void precalculate_filter_coefficients( + VectorWithOffset>>>>& + all_filter_coefficients, + DiscretisedDensity<3, elemT>* in_density) const; + + void precalculate_filter_coefficients_2D( + VectorWithOffset>>>>& + all_filter_coefficients, + DiscretisedDensity<3, elemT>* in_density) const; + + void precalculate_filter_coefficients_separable( + VectorWithOffset>>>>& + all_filter_coefficients, + DiscretisedDensity<3, elemT>* in_density) const; virtual void set_defaults(); virtual void initialise_keymap(); virtual bool post_processing(); - mutable - ModifiedInverseAverigingArrayFilter inverse_filter; + mutable ModifiedInverseAverigingArrayFilter inverse_filter; }; - #undef num_dimensions END_NAMESPACE_STIR #endif - - diff --git a/src/include/stir_experimental/ModifiedInverseAverigingArrayFilter.h b/src/include/stir_experimental/ModifiedInverseAverigingArrayFilter.h index a9fd5d23e4..e995bcec5b 100644 --- a/src/include/stir_experimental/ModifiedInverseAverigingArrayFilter.h +++ b/src/include/stir_experimental/ModifiedInverseAverigingArrayFilter.h @@ -1,22 +1,22 @@ // -// $Id: +// $Id: // /*! \file - \ingroup buildblock - \brief - This is a messy first attempt to design spatially varying filter - Given the kernel which in this case is a lospass filter with a DC gain 1 the filter + \ingroup buildblock + \brief + This is a messy first attempt to design spatially varying filter + Given the kernel which in this case is a lospass filter with a DC gain 1 the filter is design such that the output kernel varies depending on the k0 and k1 ( for more details on these factors look at Fessler) - + \author Sanida Mustafovic \author Kris Thielemans - - $Date: - $Revision: + + $Date: + $Revision: */ /* Copyright (C) 2000- 2001, IRSL @@ -32,48 +32,39 @@ #include "stir/Array.h" #include "stir/IndexRange.h" - START_NAMESPACE_STIR -class FFT_routines -{ - public: - void find_fft_filter(Array<1,float>& filter_coefficients); - void find_fft_unity(Array<1,float>& unity); - private: - Array<1,float> filter_coefficients; - Array<1,float> unity; - +class FFT_routines { +public: + void find_fft_filter(Array<1, float>& filter_coefficients); + void find_fft_unity(Array<1, float>& unity); +private: + Array<1, float> filter_coefficients; + Array<1, float> unity; }; - template -class ModifiedInverseAverigingArrayFilter: public SeparableLowPassArrayFilter2 -//public SeparableArrayFunctionObject +class ModifiedInverseAverigingArrayFilter : public SeparableLowPassArrayFilter2 +// public SeparableArrayFunctionObject { -public: - +public: //! Default constructor ModifiedInverseAverigingArrayFilter(); - - //ModifiedInverseAverigingArrayFilter(const float kapa0_over_kapa1); - - ModifiedInverseAverigingArrayFilter(const VectorWithOffset& filter_coefficients, - const float kapa0_over_kapa1); - + + // ModifiedInverseAverigingArrayFilter(const float kapa0_over_kapa1); + + ModifiedInverseAverigingArrayFilter(const VectorWithOffset& filter_coefficients, const float kapa0_over_kapa1); + // temporary (?) member - IndexRange get_kernel_index_range() const - { return kernel_index_range; } + IndexRange get_kernel_index_range() const { return kernel_index_range; } private: - VectorWithOffset filter_coefficients; - IndexRange kernel_index_range; - float kapa0_over_kapa1; - + VectorWithOffset filter_coefficients; + IndexRange kernel_index_range; + float kapa0_over_kapa1; }; - END_NAMESPACE_STIR #endif diff --git a/src/include/stir_experimental/ModifiedInverseAverigingImageFilter.h b/src/include/stir_experimental/ModifiedInverseAverigingImageFilter.h index 239f8624ca..7cc5c15ab1 100644 --- a/src/include/stir_experimental/ModifiedInverseAverigingImageFilter.h +++ b/src/include/stir_experimental/ModifiedInverseAverigingImageFilter.h @@ -3,15 +3,15 @@ /*! \file - \ingroup buildblock - \brief This is a messy, first, attempt to design spatially varying filter - Given the kernel which in this case is a lospass filter with a DC gain 1 the filter + \ingroup buildblock + \brief This is a messy, first, attempt to design spatially varying filter + Given the kernel which in this case is a lospass filter with a DC gain 1 the filter is design such that the output kernel varies depending on the k0 and k1 ( for more details on these factors look at Fessler) - + \author Sanida Mustafovic \author Kris Thielemans - + */ /* Copyright (C) 2000- 2007, Hammersmith Imanet @@ -21,7 +21,6 @@ #ifndef __stir_ModifiedInverseAverigingImageFilter_H__ #define __stir_ModifiedInverseAverigingImageFilter_H__ - #include "stir_experimental/ModifiedInverseAverigingArrayFilter.h" #include "stir/DiscretisedDensity.h" #include "stir/DataProcessor.h" @@ -30,89 +29,68 @@ #include "stir/ProjData.h" #include "stir_experimental/ArrayFilter3DUsingConvolution.h" - - - START_NAMESPACE_STIR // TODO!! remove define #define num_dimensions 3 - template -class ModifiedInverseAverigingImageFilter: -public - RegisteredParsingObject< - ModifiedInverseAverigingImageFilter, - DataProcessor >, - DataProcessor > - > -{ - private: - typedef - RegisteredParsingObject< - ModifiedInverseAverigingImageFilter, - DataProcessor >, - DataProcessor > - > - base_type; -public: - static const char * const registered_name; +class ModifiedInverseAverigingImageFilter + : public RegisteredParsingObject, + DataProcessor>, + DataProcessor>> { +private: + typedef RegisteredParsingObject, + DataProcessor>, + DataProcessor>> + base_type; + +public: + static const char* const registered_name; //! Default constructor - ModifiedInverseAverigingImageFilter(); - - ModifiedInverseAverigingImageFilter(string proj_data_filename, - string attenuation_proj_data_filename, - const VectorWithOffset& filter_coefficients, - shared_ptr proj_data_ptr, - shared_ptr attenuation_proj_data_ptr, - DiscretisedDensity<3,float>* initial_image, - DiscretisedDensity<3,float>* sensitivity_image, - int mask_size, bool z_direction_trivial); - - - -private: + ModifiedInverseAverigingImageFilter(); + ModifiedInverseAverigingImageFilter(string proj_data_filename, string attenuation_proj_data_filename, + const VectorWithOffset& filter_coefficients, shared_ptr proj_data_ptr, + shared_ptr attenuation_proj_data_ptr, DiscretisedDensity<3, float>* initial_image, + DiscretisedDensity<3, float>* sensitivity_image, int mask_size, bool z_direction_trivial); + +private: int mask_size; vector filter_coefficients_for_parsing; VectorWithOffset filter_coefficients; - + shared_ptr proj_data_ptr; string proj_data_filename; shared_ptr attenuation_proj_data_ptr; string attenuation_proj_data_filename; - DiscretisedDensity<3,float>* initial_image; + DiscretisedDensity<3, float>* initial_image; string initial_image_filename; - - DiscretisedDensity<3,float>* sensitivity_image; + + DiscretisedDensity<3, float>* sensitivity_image; string sensitivity_image_filename; bool z_direction_trivial; + Succeeded virtual_set_up(const DiscretisedDensity& density); + // new + void virtual_apply(DiscretisedDensity& out_density, + const DiscretisedDensity& in_density) const; + void virtual_apply(DiscretisedDensity& density) const; + void precalculate_filter_coefficients( + VectorWithOffset>>>>& all_filter_coefficients, + DiscretisedDensity<3, elemT>* in_density) const; - - - Succeeded virtual_set_up(const DiscretisedDensity& density); - // new - void virtual_apply(DiscretisedDensity& out_density, const DiscretisedDensity& in_density) const; - void virtual_apply(DiscretisedDensity& density) const; - void precalculate_filter_coefficients (VectorWithOffset < VectorWithOffset < VectorWithOffset > > > >& all_filter_coefficients, - DiscretisedDensity<3,elemT>* in_density) const; - - virtual void set_defaults(); virtual void initialise_keymap(); virtual bool post_processing(); - }; - #undef num_dimensions END_NAMESPACE_STIR diff --git a/src/include/stir_experimental/NonseparableSpatiallyVaryingFilters.h b/src/include/stir_experimental/NonseparableSpatiallyVaryingFilters.h index 1e60a343f8..65dcbf8326 100644 --- a/src/include/stir_experimental/NonseparableSpatiallyVaryingFilters.h +++ b/src/include/stir_experimental/NonseparableSpatiallyVaryingFilters.h @@ -3,15 +3,15 @@ /*! \file - \ingroup buildblock - \brief This is a messy, first, attempt to design spatially varying filter - Given the kernel which in this case is a lospass filter with a DC gain 1 the filter + \ingroup buildblock + \brief This is a messy, first, attempt to design spatially varying filter + Given the kernel which in this case is a lospass filter with a DC gain 1 the filter is design such that the output kernel varies depending on the k0 and k1 ( for more details on these factors look at Fessler) - + \author Sanida Mustafovic \author Kris Thielemans - + */ /* Copyright (C) 2000- 2007, Hammersmith Imanet @@ -21,8 +21,6 @@ #ifndef __stir_NonseparableSpatiallyVaryingFilters_H__ #define __stir_NonseparableSpatiallyVaryingFilters_H__ - - #include "stir_experimental/ModifiedInverseAverigingArrayFilter.h" #include "stir/DiscretisedDensity.h" #include "stir/DataProcessor.h" @@ -34,101 +32,79 @@ #include "stir/shared_ptr.h" - - - START_NAMESPACE_STIR // TODO!! remove define #define num_dimensions 3 - template -class NonseparableSpatiallyVaryingFilters: -public - RegisteredParsingObject< - NonseparableSpatiallyVaryingFilters, - DataProcessor >, - DataProcessor > - > -{ - private: - typedef - RegisteredParsingObject< - NonseparableSpatiallyVaryingFilters, - DataProcessor >, - DataProcessor > - > - base_type; -public: - static const char * const registered_name; +class NonseparableSpatiallyVaryingFilters + : public RegisteredParsingObject, + DataProcessor>, + DataProcessor>> { +private: + typedef RegisteredParsingObject, + DataProcessor>, + DataProcessor>> + base_type; + +public: + static const char* const registered_name; //! Default constructor - NonseparableSpatiallyVaryingFilters(); - - NonseparableSpatiallyVaryingFilters(string proj_data_filename, - string attenuation_proj_data_filename, - const Array<2,float>& filter_coefficients, - shared_ptr proj_data_ptr, - shared_ptr attenuation_proj_data_ptr, - DiscretisedDensity<3,float>* initial_image, - DiscretisedDensity<3,float>* sensitivity_image, - DiscretisedDensity<3,float>* precomputed_coefficients_image, - DiscretisedDensity<3,float>* normalised_bck_image, - int mask_size, - int num_dim); - - - -private: + NonseparableSpatiallyVaryingFilters(); + NonseparableSpatiallyVaryingFilters(string proj_data_filename, string attenuation_proj_data_filename, + const Array<2, float>& filter_coefficients, shared_ptr proj_data_ptr, + shared_ptr attenuation_proj_data_ptr, DiscretisedDensity<3, float>* initial_image, + DiscretisedDensity<3, float>* sensitivity_image, + DiscretisedDensity<3, float>* precomputed_coefficients_image, + DiscretisedDensity<3, float>* normalised_bck_image, int mask_size, int num_dim); + +private: int mask_size; - Array<2,float> filter_coefficients_for_parsing; - mutable Array<2,float> filter_coefficients; - + Array<2, float> filter_coefficients_for_parsing; + mutable Array<2, float> filter_coefficients; + shared_ptr proj_data_ptr; string proj_data_filename; shared_ptr attenuation_proj_data_ptr; string attenuation_proj_data_filename; - DiscretisedDensity<3,float>* initial_image; + DiscretisedDensity<3, float>* initial_image; string initial_image_filename; - - DiscretisedDensity<3,float>* sensitivity_image; + + DiscretisedDensity<3, float>* sensitivity_image; string sensitivity_image_filename; - DiscretisedDensity<3,float>* precomputed_coefficients_image ; + DiscretisedDensity<3, float>* precomputed_coefficients_image; string precomputed_coefficients_filename; - DiscretisedDensity<3,float>* normalised_bck_image ; + DiscretisedDensity<3, float>* normalised_bck_image; string normalised_bck_filename; int num_dim; int number_of_coefficients_before_padding; - Succeeded virtual_set_up(const DiscretisedDensity& density); - // new - void virtual_apply(DiscretisedDensity& out_density, const DiscretisedDensity& in_density) const; - void virtual_apply(DiscretisedDensity& density) const; - void precalculate_filter_coefficients_2D (VectorWithOffset < VectorWithOffset < VectorWithOffset > > > >& all_filter_coefficients, - DiscretisedDensity<3,elemT>* in_density) const; - - - - - virtual void set_defaults(); - virtual void initialise_keymap(); - - virtual bool post_processing(); - -}; + Succeeded virtual_set_up(const DiscretisedDensity& density); + // new + void virtual_apply(DiscretisedDensity& out_density, + const DiscretisedDensity& in_density) const; + void virtual_apply(DiscretisedDensity& density) const; + void precalculate_filter_coefficients_2D( + VectorWithOffset>>>>& + all_filter_coefficients, + DiscretisedDensity<3, elemT>* in_density) const; + virtual void set_defaults(); + virtual void initialise_keymap(); + + virtual bool post_processing(); +}; #undef num_dimensions END_NAMESPACE_STIR #endif - - diff --git a/src/include/stir_experimental/NonseparableSpatiallyVaryingFilters3D.h b/src/include/stir_experimental/NonseparableSpatiallyVaryingFilters3D.h index 29a3538c5e..ad2db8063a 100644 --- a/src/include/stir_experimental/NonseparableSpatiallyVaryingFilters3D.h +++ b/src/include/stir_experimental/NonseparableSpatiallyVaryingFilters3D.h @@ -2,15 +2,15 @@ /*! \file - \ingroup buildblock - \brief This is a messy, first, attempt to design spatially varying filter - Given the kernel which in this case is a lospass filter with a DC gain 1 the filter + \ingroup buildblock + \brief This is a messy, first, attempt to design spatially varying filter + Given the kernel which in this case is a lospass filter with a DC gain 1 the filter is design such that the output kernel varies depending on the k0 and k1 ( for more details on these factors look at Fessler) - + \author Sanida Mustafovic \author Kris Thielemans - + */ /* Copyright (C) 2000- 2007, Hammersmith Imanet @@ -20,8 +20,6 @@ #ifndef __stir_NonseparableSpatiallyVaryingFilters3D_H__ #define __stir_NonseparableSpatiallyVaryingFilters3D_H__ - - #include "stir_experimental/ModifiedInverseAverigingArrayFilter.h" #include "stir/DiscretisedDensity.h" #include "stir/DataProcessor.h" @@ -33,101 +31,83 @@ #include "stir/shared_ptr.h" - - - START_NAMESPACE_STIR // TODO!! remove define #define num_dimensions 3 - template -class NonseparableSpatiallyVaryingFilters3D: -public - RegisteredParsingObject< - NonseparableSpatiallyVaryingFilters3D, - DataProcessor >, - DataProcessor > - > -{ - private: - typedef - RegisteredParsingObject< - NonseparableSpatiallyVaryingFilters3D, - DataProcessor >, - DataProcessor > - > - base_type; -public: - static const char * const registered_name; +class NonseparableSpatiallyVaryingFilters3D + : public RegisteredParsingObject, + DataProcessor>, + DataProcessor>> { +private: + typedef RegisteredParsingObject, + DataProcessor>, + DataProcessor>> + base_type; + +public: + static const char* const registered_name; //! Default constructor - NonseparableSpatiallyVaryingFilters3D(); - - NonseparableSpatiallyVaryingFilters3D(string proj_data_filename, - string attenuation_proj_data_filename, - const Array<3,float>& filter_coefficients, - shared_ptr proj_data_ptr, - shared_ptr attenuation_proj_data_ptr, - DiscretisedDensity<3,float>* initial_image, - DiscretisedDensity<3,float>* sensitivity_image, - DiscretisedDensity<3,float>* precomputed_coefficients_image, - DiscretisedDensity<3,float>* normalised_bck_image, - int mask_size, - float rescaling_coefficient); - - -private: - + NonseparableSpatiallyVaryingFilters3D(); + + NonseparableSpatiallyVaryingFilters3D(string proj_data_filename, string attenuation_proj_data_filename, + const Array<3, float>& filter_coefficients, shared_ptr proj_data_ptr, + shared_ptr attenuation_proj_data_ptr, + DiscretisedDensity<3, float>* initial_image, + DiscretisedDensity<3, float>* sensitivity_image, + DiscretisedDensity<3, float>* precomputed_coefficients_image, + DiscretisedDensity<3, float>* normalised_bck_image, int mask_size, + float rescaling_coefficient); + +private: int mask_size; - Array<3,float> filter_coefficients_for_parsing; - mutable Array<3,float> filter_coefficients; - + Array<3, float> filter_coefficients_for_parsing; + mutable Array<3, float> filter_coefficients; + shared_ptr proj_data_ptr; string proj_data_filename; shared_ptr attenuation_proj_data_ptr; string attenuation_proj_data_filename; - DiscretisedDensity<3,float>* initial_image; + DiscretisedDensity<3, float>* initial_image; string initial_image_filename; - - DiscretisedDensity<3,float>* sensitivity_image; + + DiscretisedDensity<3, float>* sensitivity_image; string sensitivity_image_filename; - mutable DiscretisedDensity<3,float>* precomputed_coefficients_image ; + mutable DiscretisedDensity<3, float>* precomputed_coefficients_image; string precomputed_coefficients_filename; - DiscretisedDensity<3,float>* normalised_bck_image ; + DiscretisedDensity<3, float>* normalised_bck_image; string normalised_bck_filename; float rescaling_coefficient; mutable float k_interval; int recompute_every_num_subiterations; int recompute_from_subiteration_num; - Succeeded virtual_set_up(const DiscretisedDensity& density); - // new - void virtual_apply(DiscretisedDensity& out_density, const DiscretisedDensity& in_density) const; - void virtual_apply(DiscretisedDensity& density) const; - void precalculate_filter_coefficients_3D (VectorWithOffset < VectorWithOffset < VectorWithOffset > > > >& all_filter_coefficients, - DiscretisedDensity<3,elemT>* in_density) const; - - - - - virtual void set_defaults(); - virtual void initialise_keymap(); - - virtual bool post_processing(); - -}; + Succeeded virtual_set_up(const DiscretisedDensity& density); + // new + void virtual_apply(DiscretisedDensity& out_density, + const DiscretisedDensity& in_density) const; + void virtual_apply(DiscretisedDensity& density) const; + void precalculate_filter_coefficients_3D( + VectorWithOffset>>>>& + all_filter_coefficients, + DiscretisedDensity<3, elemT>* in_density) const; + virtual void set_defaults(); + virtual void initialise_keymap(); + + virtual bool post_processing(); +}; #undef num_dimensions END_NAMESPACE_STIR #endif - diff --git a/src/include/stir_experimental/Quaternion.h b/src/include/stir_experimental/Quaternion.h index ac240c5c1c..a463d33492 100644 --- a/src/include/stir_experimental/Quaternion.h +++ b/src/include/stir_experimental/Quaternion.h @@ -14,8 +14,6 @@ \author Kris Thielemans */ - - #ifndef __stir_Quaternion_H__ #define __stir_Quaternion_H__ @@ -24,41 +22,39 @@ START_NAMESPACE_STIR template -class Quaternion : public BasicCoordinate<4, coordT> // TODO kris wants private inheritance +class Quaternion : public BasicCoordinate<4, coordT> // TODO kris wants private inheritance { protected: typedef BasicCoordinate<4, coordT> base_type; public: - - Quaternion(); - Quaternion(const coordT&, const coordT&, const coordT&, const coordT&); - Quaternion(const base_type& q); - - /*coordT component_1() const; - coordT component_2() const; - coordT component_3() const; - coordT component_4() const;*/ - - // Overload multiplication - inline Quaternion & operator*= (const coordT& a); - inline Quaternion & operator*= (const Quaternion& q); - inline Quaternion operator* (const Quaternion& q) const; - inline Quaternion operator* (const coordT& a) const; - // Overload division - inline Quaternion & operator/= (const coordT& a); - inline Quaternion & operator/= (const Quaternion& q); - inline Quaternion operator/ (const Quaternion& q) const; - inline Quaternion operator/ (const coordT& a) const; - - inline void neg_quaternion(); - inline void conjugate(); - inline void normalise(); - inline void inverse(); - inline static coordT dot_product (const Quaternion&, const Quaternion&); + Quaternion(); + Quaternion(const coordT&, const coordT&, const coordT&, const coordT&); + Quaternion(const base_type& q); + + /*coordT component_1() const; + coordT component_2() const; + coordT component_3() const; + coordT component_4() const;*/ + + // Overload multiplication + inline Quaternion& operator*=(const coordT& a); + inline Quaternion& operator*=(const Quaternion& q); + inline Quaternion operator*(const Quaternion& q) const; + inline Quaternion operator*(const coordT& a) const; + // Overload division + inline Quaternion& operator/=(const coordT& a); + inline Quaternion& operator/=(const Quaternion& q); + inline Quaternion operator/(const Quaternion& q) const; + inline Quaternion operator/(const coordT& a) const; + + inline void neg_quaternion(); + inline void conjugate(); + inline void normalise(); + inline void inverse(); + inline static coordT dot_product(const Quaternion&, const Quaternion&); private: - }; template diff --git a/src/include/stir_experimental/Quaternion.inl b/src/include/stir_experimental/Quaternion.inl index b943d7db0c..3cbabeb03e 100644 --- a/src/include/stir_experimental/Quaternion.inl +++ b/src/include/stir_experimental/Quaternion.inl @@ -14,59 +14,49 @@ \author Kris Thielemans */ - - START_NAMESPACE_STIR - template -coordT norm_squared(const Quaternion& q) -{ - return square(q[1]) + square(q[2]) +square(q[3])+ square(q[4]); +coordT +norm_squared(const Quaternion& q) { + return square(q[1]) + square(q[2]) + square(q[3]) + square(q[4]); } template -coordT norm(const Quaternion& q) -{ +coordT +norm(const Quaternion& q) { return sqrt(norm_squared(q)); } template -coordT -Quaternion::dot_product (const Quaternion& q1, const Quaternion& q2) -{ - return((q1[1]*q2[1])+(q1[2]*q2[2])+(q1[3]*q2[3])+(q1[4]*q2[4])); +coordT +Quaternion::dot_product(const Quaternion& q1, const Quaternion& q2) { + return ((q1[1] * q2[1]) + (q1[2] * q2[2]) + (q1[3] * q2[3]) + (q1[4] * q2[4])); } template -Quaternion& -Quaternion:: -operator*=(const Quaternion& q) -{ - const Quaternion tmp (*this); - (*this)[1] = tmp[1]*q[1]-tmp[2]*q[2]-tmp[3]*q[3]-tmp[4]*q[4]; - (*this)[2] = tmp[1]*q[2]+tmp[2]*q[1]+tmp[3]*q[4]-tmp[4]*q[3]; - (*this)[3] = tmp[1]*q[3]+tmp[3]*q[1]+tmp[4]*q[2]-tmp[2]*q[4]; - (*this)[4] = tmp[1]*q[4]+tmp[4]*q[1]+tmp[2]*q[3]-tmp[3]*q[2]; +Quaternion& +Quaternion::operator*=(const Quaternion& q) { + const Quaternion tmp(*this); + (*this)[1] = tmp[1] * q[1] - tmp[2] * q[2] - tmp[3] * q[3] - tmp[4] * q[4]; + (*this)[2] = tmp[1] * q[2] + tmp[2] * q[1] + tmp[3] * q[4] - tmp[4] * q[3]; + (*this)[3] = tmp[1] * q[3] + tmp[3] * q[1] + tmp[4] * q[2] - tmp[2] * q[4]; + (*this)[4] = tmp[1] * q[4] + tmp[4] * q[1] + tmp[2] * q[3] - tmp[3] * q[2]; return *this; - } template -Quaternion& -Quaternion::operator*= (const coordT& a) -{ - for (int i=1; i<=4; i++) +Quaternion& +Quaternion::operator*=(const coordT& a) { + for (int i = 1; i <= 4; i++) (*this)[i] *= a; return *this; - } template -Quaternion -Quaternion::operator* (const Quaternion& q) const -{ +Quaternion +Quaternion::operator*(const Quaternion& q) const { Quaternion tmp(*this); tmp *= q; @@ -74,29 +64,25 @@ Quaternion::operator* (const Quaternion& q) const } template -Quaternion -Quaternion::operator* (const coordT& a) const -{ +Quaternion +Quaternion::operator*(const coordT& a) const { Quaternion tmp(*this); tmp *= a; return tmp; } template -Quaternion& -Quaternion:: operator/= (const coordT& a) -{ - for (int i=1; i<=4; i++) +Quaternion& +Quaternion::operator/=(const coordT& a) { + for (int i = 1; i <= 4; i++) (*this)[i] /= a; return *this; - } template -Quaternion& -Quaternion::operator/= (const Quaternion& q) -{ - const Quaternion con_q(q[1],-q[2],-q[3],-q[4]); - const coordT norm_squared =(square(q[1])+square(q[2])+square(q[3])+square(q[4])); +Quaternion& +Quaternion::operator/=(const Quaternion& q) { + const Quaternion con_q(q[1], -q[2], -q[3], -q[4]); + const coordT norm_squared = (square(q[1]) + square(q[2]) + square(q[3]) + square(q[4])); *this *= con_q; *this /= norm_squared; @@ -104,81 +90,70 @@ Quaternion::operator/= (const Quaternion& q) } template -Quaternion -Quaternion::operator/(const Quaternion& q) const -{ +Quaternion +Quaternion::operator/(const Quaternion& q) const { Quaternion tmp(*this); - tmp/=q; + tmp /= q; return tmp; } template -Quaternion -Quaternion::operator/ (const coordT& a) const -{ +Quaternion +Quaternion::operator/(const coordT& a) const { Quaternion tmp(*this); tmp /= a; return tmp; - } template -void -Quaternion:: neg_quaternion () -{ - (*this)[1] =-(*this)[1]; - (*this)[2] =-(*this)[2]; - (*this)[3] =-(*this)[3]; - (*this)[4] =-(*this)[4]; - +void +Quaternion::neg_quaternion() { + (*this)[1] = -(*this)[1]; + (*this)[2] = -(*this)[2]; + (*this)[3] = -(*this)[3]; + (*this)[4] = -(*this)[4]; } - - template -void -Quaternion:: conjugate() -{ -// (*this)[1] =(*this)[1]; - (*this)[2] =-(*this)[2]; - (*this)[3] =-(*this)[3]; - (*this)[4] =-(*this)[4]; +void +Quaternion::conjugate() { + // (*this)[1] =(*this)[1]; + (*this)[2] = -(*this)[2]; + (*this)[3] = -(*this)[3]; + (*this)[4] = -(*this)[4]; } template -void -Quaternion:: normalise() -{ +void +Quaternion::normalise() { const coordT n = norm(*this); - (*this)[1] /=n; - (*this)[2] /=n; - (*this)[3] /=n; - (*this)[4] /=n; + (*this)[1] /= n; + (*this)[2] /= n; + (*this)[3] /= n; + (*this)[4] /= n; } template -void -Quaternion::inverse() -{ +void +Quaternion::inverse() { const coordT dp = norm_squared(*this); - (*this)[1] = (*this)[1]/dp; - (*this)[2] = -(*this)[2]/dp; - (*this)[3] = -(*this)[3]/dp; - (*this)[4] = -(*this)[4]/dp; + (*this)[1] = (*this)[1] / dp; + (*this)[2] = -(*this)[2] / dp; + (*this)[3] = -(*this)[3] / dp; + (*this)[4] = -(*this)[4] / dp; } - template -Quaternion conjugate(const Quaternion& q) -{ +Quaternion +conjugate(const Quaternion& q) { Quaternion tmp = q; tmp.conjugate(); return tmp; } template -Quaternion inverse(const Quaternion& q) -{ +Quaternion +inverse(const Quaternion& q) { Quaternion tmp = q; tmp.inverse(); return tmp; diff --git a/src/include/stir_experimental/SeparableGaussianArrayFilter.h b/src/include/stir_experimental/SeparableGaussianArrayFilter.h index 582a970b77..03b0eae969 100644 --- a/src/include/stir_experimental/SeparableGaussianArrayFilter.h +++ b/src/include/stir_experimental/SeparableGaussianArrayFilter.h @@ -1,18 +1,18 @@ // -// $Id: +// $Id: // /*! \file - \ingroup local/buildblock - \brief - - + \ingroup local/buildblock + \brief + + \author Sanida Mustafovic \author Kris Thielemans - - $Date: - $Revision: + + $Date: + $Revision: */ /* Copyright (C) 2000- 2002, IRSL @@ -30,30 +30,21 @@ START_NAMESPACE_STIR - template -class SeparableGaussianArrayFilter: - public SeparableArrayFunctionObject -{ -public: - +class SeparableGaussianArrayFilter : public SeparableArrayFunctionObject { +public: //! Default constructor - SeparableGaussianArrayFilter(); - - SeparableGaussianArrayFilter(const float standard_deviation, - const int number_of_coefficients); - - + SeparableGaussianArrayFilter(); + + SeparableGaussianArrayFilter(const float standard_deviation, const int number_of_coefficients); + private: - void calculate_coefficients(VectorWithOffset& filter_coefficients, - const int number_of_coefficients, - const float standard_deviation); - float standard_deviation; + void calculate_coefficients(VectorWithOffset& filter_coefficients, const int number_of_coefficients, + const float standard_deviation); + float standard_deviation; int number_of_coefficients; - }; - END_NAMESPACE_STIR #endif diff --git a/src/include/stir_experimental/SeparableGaussianImageFilter.h b/src/include/stir_experimental/SeparableGaussianImageFilter.h index 9e3438d0a6..634fa5127e 100644 --- a/src/include/stir_experimental/SeparableGaussianImageFilter.h +++ b/src/include/stir_experimental/SeparableGaussianImageFilter.h @@ -3,12 +3,12 @@ /*! \file - \ingroup buildblock + \ingroup buildblock \brief Declaration of class SeparableGaussianImageFilter - + \author Sanida Mustafovic \author Kris Thielemans - + */ /* Copyright (C) 2000- 2007, Hammersmith Imanet @@ -18,13 +18,11 @@ #ifndef __stir_SeparableGaussianImageFilter_H__ #define __stir_SeparableGaussianImageFilter_H__ - #include "stir_experimental/SeparableGaussianArrayFilter.h" #include "stir/RegisteredParsingObject.h" #include "stir/DataProcessor.h" #include "stir/DiscretisedDensity.h" - START_NAMESPACE_STIR // TODO!! remove define @@ -32,46 +30,37 @@ START_NAMESPACE_STIR #define num_dimensions 3 template -class SeparableGaussianImageFilter : - public - RegisteredParsingObject< - SeparableGaussianImageFilter, - DataProcessor >, - DataProcessor > - > -{ - private: - typedef - RegisteredParsingObject< - SeparableGaussianImageFilter, - DataProcessor >, - DataProcessor > - > - base_type; +class SeparableGaussianImageFilter : public RegisteredParsingObject, + DataProcessor>, + DataProcessor>> { +private: + typedef RegisteredParsingObject, DataProcessor>, + DataProcessor>> + base_type; + public: - static const char * const registered_name; - + static const char* const registered_name; + //! Default constructor SeparableGaussianImageFilter(); float get_standard_deviation(); - - + private: float standard_deviation; int number_of_coefficients; - - SeparableGaussianArrayFilter gaussian_filter; + + SeparableGaussianArrayFilter gaussian_filter; virtual void set_defaults(); virtual void initialise_keymap(); virtual bool post_processing(); - - Succeeded virtual_set_up(const DiscretisedDensity& image); + + Succeeded virtual_set_up(const DiscretisedDensity& image); // new - void virtual_apply(DiscretisedDensity& out_density, const DiscretisedDensity& in_density) const; - void virtual_apply(DiscretisedDensity& density) const ; - + void virtual_apply(DiscretisedDensity& out_density, + const DiscretisedDensity& in_density) const; + void virtual_apply(DiscretisedDensity& density) const; }; #undef num_dimensions @@ -79,5 +68,3 @@ class SeparableGaussianImageFilter : END_NAMESPACE_STIR #endif - - diff --git a/src/include/stir_experimental/SeparableLowPassArrayFilter.h b/src/include/stir_experimental/SeparableLowPassArrayFilter.h index 6628c58d0c..35253d2bbf 100644 --- a/src/include/stir_experimental/SeparableLowPassArrayFilter.h +++ b/src/include/stir_experimental/SeparableLowPassArrayFilter.h @@ -1,18 +1,18 @@ // -// $Id: +// $Id: // /*! \file - \ingroup buildblock - \brief - - + \ingroup buildblock + \brief + + \author Sanida Mustafovic \author Kris Thielemans - - $Date: - $Revision: + + $Date: + $Revision: */ /* Copyright (C) 2000- 2002, IRSL @@ -28,24 +28,19 @@ START_NAMESPACE_STIR - template -class SeparableLowPassArrayFilter: - public SeparableArrayFunctionObject -{ -public: - +class SeparableLowPassArrayFilter : public SeparableArrayFunctionObject { +public: //! Default constructor - SeparableLowPassArrayFilter(); - + SeparableLowPassArrayFilter(); + SeparableLowPassArrayFilter(const VectorWithOffset& filter_coefficients, int z_trivial); - + private: - VectorWithOffset filter_coefficients; - int z_trivial; + VectorWithOffset filter_coefficients; + int z_trivial; }; - END_NAMESPACE_STIR #endif diff --git a/src/include/stir_experimental/SeparableLowPassArrayFilter2.h b/src/include/stir_experimental/SeparableLowPassArrayFilter2.h index b3b4da69b3..eea173388f 100644 --- a/src/include/stir_experimental/SeparableLowPassArrayFilter2.h +++ b/src/include/stir_experimental/SeparableLowPassArrayFilter2.h @@ -3,7 +3,7 @@ /*! \file \ingroup test - \brief + \brief \author Kris Thielemans */ @@ -32,127 +32,94 @@ START_NAMESPACE_STIR is separable. 'Separable' means that its operation consists of \c n 1D operations, one on each - index of the \c n -dimensional array. + index of the \c n -dimensional array. \see in_place_apply_array_functions_on_each_index() TODO - + */ // TODO don't use 2Argument, but do 1arg explicitly as well template -class SeparableArrayFunctionObject2 : - public ArrayFunctionObject_2ArgumentImplementation -{ +class SeparableArrayFunctionObject2 : public ArrayFunctionObject_2ArgumentImplementation { public: - SeparableArrayFunctionObject2 (); - SeparableArrayFunctionObject2 (const VectorWithOffset< shared_ptr > >&); + SeparableArrayFunctionObject2(); + SeparableArrayFunctionObject2(const VectorWithOffset>>&); bool is_trivial() const; // TODO reimplement get_*indices protected: - - VectorWithOffset< shared_ptr > > all_1d_array_filters; - virtual void do_it(Array& out_array, const Array& in_array) const; - + VectorWithOffset>> all_1d_array_filters; + virtual void do_it(Array& out_array, const Array& in_array) const; }; - template -SeparableArrayFunctionObject2:: -SeparableArrayFunctionObject2() -: all_1d_array_filters(VectorWithOffset< shared_ptr > >(num_dim)) -{} - +SeparableArrayFunctionObject2::SeparableArrayFunctionObject2() + : all_1d_array_filters(VectorWithOffset>>(num_dim)) {} template -SeparableArrayFunctionObject2:: -SeparableArrayFunctionObject2(const VectorWithOffset< shared_ptr > >& array_filters) -: all_1d_array_filters(array_filters) -{ +SeparableArrayFunctionObject2::SeparableArrayFunctionObject2( + const VectorWithOffset>>& array_filters) + : all_1d_array_filters(array_filters) { assert(all_1d_array_filters.get_length() == num_dim); } template -void -SeparableArrayFunctionObject2:: -do_it(Array& out_array, const Array& in_array) const -{ - - //if (!is_trivial()) - apply_array_functions_on_each_index(out_array, in_array, - all_1d_array_filters.begin(), - all_1d_array_filters.end()); - //else somehow copy in_array into out_array but keeping index ranges - //TODO - +void +SeparableArrayFunctionObject2::do_it(Array& out_array, + const Array& in_array) const { + + // if (!is_trivial()) + apply_array_functions_on_each_index(out_array, in_array, all_1d_array_filters.begin(), all_1d_array_filters.end()); + // else somehow copy in_array into out_array but keeping index ranges + // TODO } template -bool -SeparableArrayFunctionObject2:: -is_trivial() const -{ - for (typename VectorWithOffset< shared_ptr > >::const_iterator iter=all_1d_array_filters.begin(); - iter!=all_1d_array_filters.end();++iter) - { - // TODO insert condition on is_null_ptr here (see SeparableArrayFunctionObject) - if (!(*iter)->is_trivial()) - return false; - } - return true; +bool +SeparableArrayFunctionObject2::is_trivial() const { + for (typename VectorWithOffset>>::const_iterator iter = all_1d_array_filters.begin(); + iter != all_1d_array_filters.end(); ++iter) { + // TODO insert condition on is_null_ptr here (see SeparableArrayFunctionObject) + if (!(*iter)->is_trivial()) + return false; + } + return true; } //---------------------------------------------- template -class SeparableLowPassArrayFilter2: - public SeparableArrayFunctionObject2 -{ -public: - +class SeparableLowPassArrayFilter2 : public SeparableArrayFunctionObject2 { +public: //! Default constructor - SeparableLowPassArrayFilter2(); - + SeparableLowPassArrayFilter2(); + SeparableLowPassArrayFilter2(const VectorWithOffset& filter_coefficients); - + private: - VectorWithOffset filter_coefficients; - + VectorWithOffset filter_coefficients; }; - template -SeparableLowPassArrayFilter2:: -SeparableLowPassArrayFilter2() -{ - for (int i=1;i<=num_dimensions;i++) - { - all_1d_array_filters[i-1] = - new ArrayFilter1DUsingConvolution(); +SeparableLowPassArrayFilter2::SeparableLowPassArrayFilter2() { + for (int i = 1; i <= num_dimensions; i++) { + all_1d_array_filters[i - 1] = new ArrayFilter1DUsingConvolution(); } } - -template -SeparableLowPassArrayFilter2:: -SeparableLowPassArrayFilter2(const VectorWithOffset& filter_coefficients_v) -:filter_coefficients(filter_coefficients_v) -{ - assert(num_dimensions==3); +template +SeparableLowPassArrayFilter2::SeparableLowPassArrayFilter2( + const VectorWithOffset& filter_coefficients_v) + : filter_coefficients(filter_coefficients_v) { + assert(num_dimensions == 3); std::cerr << "Printing filter coefficients" << endl; - for (int i =filter_coefficients_v.get_min_index();i<=filter_coefficients_v.get_max_index();i++) - std::cerr << i<<" "<< filter_coefficients_v[i] <<" " << endl; - + for (int i = filter_coefficients_v.get_min_index(); i <= filter_coefficients_v.get_max_index(); i++) + std::cerr << i << " " << filter_coefficients_v[i] << " " << endl; - all_1d_array_filters[2] = - new ArrayFilter1DUsingConvolution(filter_coefficients_v); + all_1d_array_filters[2] = new ArrayFilter1DUsingConvolution(filter_coefficients_v); - all_1d_array_filters[0] = - new ArrayFilter1DUsingConvolution(); - all_1d_array_filters[1] = - new ArrayFilter1DUsingConvolution(filter_coefficients_v); - - + all_1d_array_filters[0] = new ArrayFilter1DUsingConvolution(); + all_1d_array_filters[1] = new ArrayFilter1DUsingConvolution(filter_coefficients_v); } END_NAMESPACE_STIR diff --git a/src/include/stir_experimental/SeparableLowPassImageFilter.h b/src/include/stir_experimental/SeparableLowPassImageFilter.h index 6de6e234a7..9aa877611f 100644 --- a/src/include/stir_experimental/SeparableLowPassImageFilter.h +++ b/src/include/stir_experimental/SeparableLowPassImageFilter.h @@ -3,12 +3,12 @@ /*! \file - \ingroup buildblock + \ingroup buildblock \brief Declaration of class SeparableLowPassImageFilter - + \author Sanida Mustafovic \author Kris Thielemans - + */ /* Copyright (C) 2000- 2007, IRSL @@ -18,13 +18,11 @@ #ifndef __stir_SeparableLowPassImageFilter_H__ #define __stir_SeparableLowPassImageFilter_H__ - #include "stir_experimental/SeparableLowPassArrayFilter.h" #include "stir/RegisteredParsingObject.h" #include "stir/DataProcessor.h" #include "stir/DiscretisedDensity.h" - START_NAMESPACE_STIR // TODO!! remove define @@ -32,49 +30,38 @@ START_NAMESPACE_STIR #define num_dimensions 3 template -class SeparableLowPassImageFilter : - public - RegisteredParsingObject< - SeparableLowPassImageFilter, - DataProcessor >, - DataProcessor > - > -{ - private: - typedef - RegisteredParsingObject< - SeparableLowPassImageFilter, - DataProcessor >, - DataProcessor > - > - base_type; +class SeparableLowPassImageFilter + : public RegisteredParsingObject, DataProcessor>, + DataProcessor>> { +private: + typedef RegisteredParsingObject, DataProcessor>, + DataProcessor>> + base_type; + public: - static const char * const registered_name; - + static const char* const registered_name; + //! Default constructor SeparableLowPassImageFilter(); VectorWithOffset get_filter_coefficients(); - - + private: vector filter_coefficients_for_parsing; VectorWithOffset filter_coefficients; - int z_trivial ; - - - - SeparableLowPassArrayFilter lowpass_filter; + int z_trivial; + + SeparableLowPassArrayFilter lowpass_filter; virtual void set_defaults(); virtual void initialise_keymap(); virtual bool post_processing(); - - Succeeded virtual_set_up(const DiscretisedDensity& image); + + Succeeded virtual_set_up(const DiscretisedDensity& image); // new - void virtual_apply(DiscretisedDensity& out_density, const DiscretisedDensity& in_density) const; - void virtual_apply(DiscretisedDensity& density) const ; - + void virtual_apply(DiscretisedDensity& out_density, + const DiscretisedDensity& in_density) const; + void virtual_apply(DiscretisedDensity& density) const; }; #undef num_dimensions @@ -82,5 +69,3 @@ class SeparableLowPassImageFilter : END_NAMESPACE_STIR #endif - - diff --git a/src/include/stir_experimental/fft.h b/src/include/stir_experimental/fft.h index f760ac76b6..8f52831aaf 100644 --- a/src/include/stir_experimental/fft.h +++ b/src/include/stir_experimental/fft.h @@ -29,47 +29,48 @@ START_NAMESPACE_STIR -template class Array; +template +class Array; //! 1-dimensional FFT /*! Replaces data by its discrete Fourier transform, if isign is input - as 1; or replaces data by nn times its inverse discrete Fourier transform, - if isign is input as -1. data is a complex array of length nn, input as a - real array data[1..2*nn]. nn MUST be an integer power of 2 ( this is not + as 1; or replaces data by nn times its inverse discrete Fourier transform, + if isign is input as -1. data is a complex array of length nn, input as a + real array data[1..2*nn]. nn MUST be an integer power of 2 ( this is not checked for !). */ -void four1(Array<1,float> &data, int nn, int isign); +void four1(Array<1, float>& data, int nn, int isign); //! n-dimensional FFT /*! - Replaces data by its ndim-dimensional discrete Fourier transform, - if isign is input as 1. nn[1..ndim] is an integer array containing the + Replaces data by its ndim-dimensional discrete Fourier transform, + if isign is input as 1. nn[1..ndim] is an integer array containing the lengths of each dimension (number of complex values), which MUST all be powers of 2. data is a real array of length twice the product of - thes lengths, in which the data are stored as in a multidimensional - complex array: real and imaginary parts of each element are in - consecutive locations , and the rightmost index of the array - increases most rapidly as one proceeds along data. For a - two-dimensional array, this is equivalent to storing the - array by rows . If isign is in input as -1 , data is replaced by + thes lengths, in which the data are stored as in a multidimensional + complex array: real and imaginary parts of each element are in + consecutive locations , and the rightmost index of the array + increases most rapidly as one proceeds along data. For a + two-dimensional array, this is equivalent to storing the + array by rows . If isign is in input as -1 , data is replaced by its inverse transform time the product of the lengths of all dimensions. */ -void fourn (Array<1,float> &data, Array<1,int> &nn, int ndim, int isign); -//void convlv (Array<1,float> &data, const Array<1,float> &filter, int n); +void fourn(Array<1, float>& data, Array<1, int>& nn, int ndim, int isign); +// void convlv (Array<1,float> &data, const Array<1,float> &filter, int n); //! Convolve data with a filter which is given in frequency space /*! \param data has to have a min_index == 1 - \param filter has to in the format produced by realft. + \param filter has to in the format produced by realft. \param n has to be equal to th elength of \a data \warning Currently, only the real elements of the filter will be used. (Historical reason?) - So, the result will be incorrect of the filter has complex components (i.e. its + So, the result will be incorrect of the filter has complex components (i.e. its spatial kernel is not symmetric). */ -void convlvC (Array<1,float> &data, const Array<1,float> &filter, int n); +void convlvC(Array<1, float>& data, const Array<1, float>& filter, int n); //! 3D FFT of real numbers -void rlft3(Array<3,float> &data, Array<2,float> &speq, int nn1, int nn2, int nn3, int isign); +void rlft3(Array<3, float>& data, Array<2, float>& speq, int nn1, int nn2, int nn3, int isign); //! Calculates the Fourier Transform of a set of 2n real-valued data points. /*! Replaces this data ( which is stored in array data[1..2n]) by the positive @@ -80,9 +81,8 @@ void rlft3(Array<3,float> &data, Array<2,float> &speq, int nn1, int nn2, int nn3 data. (Result in this case must be multiplied by 1/n.) */ -void realft ( Array<1,float> &data, int n, int isign); +void realft(Array<1, float>& data, int n, int isign); END_NAMESPACE_STIR #endif - diff --git a/src/include/stir_experimental/fwd_and_bck_manipulation_for_SAF.h b/src/include/stir_experimental/fwd_and_bck_manipulation_for_SAF.h index 9fd606e1f2..50d470fde8 100644 --- a/src/include/stir_experimental/fwd_and_bck_manipulation_for_SAF.h +++ b/src/include/stir_experimental/fwd_and_bck_manipulation_for_SAF.h @@ -5,49 +5,23 @@ #include "stir/SegmentByView.h" #include "stir_experimental/recon_buildblock/ProjMatrixByDensel.h" - - START_NAMESPACE_STIR -void -fwd_project(ProjData& proj_data,VoxelsOnCartesianGrid* vox_image_ptr, - const int start_segment_num, const int end_segment_num, - const int start_axial_pos_num, const int end_axial_pos_num, - const int start_view, const int end_view, - const int start_tang_pos_num,const int end_tang_pos_num); - - - -void -do_segments_densels_fwd(const VoxelsOnCartesianGrid& image, - ProjData& proj_data, - VectorWithOffset *>& all_segments, - const int min_z, const int max_z, - const int min_y, const int max_y, - const int min_x, const int max_x, - ProjMatrixByDensel& proj_matrix); - - -void -fwd_densels_all(VectorWithOffset *>& all_segments, - shared_ptr proj_matrix_ptr, - shared_ptr proj_data_ptr, - const int min_z, const int max_z, - const int min_y, const int max_y, - const int min_x, const int max_x, - const DiscretisedDensity<3,float>& in_density); - -void -find_inverse_and_bck_densels(DiscretisedDensity<3,float>& image, - VectorWithOffset *>& all_segments, - VectorWithOffset *>& attenuation_segmnets, - const int min_z, const int max_z, - const int min_y, const int max_y, - const int min_x, const int max_x, - ProjMatrixByDensel& proj_matrix, - bool do_attenuation, - const float threshold, bool normalize_result); - - - +void fwd_project(ProjData& proj_data, VoxelsOnCartesianGrid* vox_image_ptr, const int start_segment_num, + const int end_segment_num, const int start_axial_pos_num, const int end_axial_pos_num, const int start_view, + const int end_view, const int start_tang_pos_num, const int end_tang_pos_num); + +void do_segments_densels_fwd(const VoxelsOnCartesianGrid& image, ProjData& proj_data, + VectorWithOffset*>& all_segments, const int min_z, const int max_z, + const int min_y, const int max_y, const int min_x, const int max_x, ProjMatrixByDensel& proj_matrix); + +void fwd_densels_all(VectorWithOffset*>& all_segments, shared_ptr proj_matrix_ptr, + shared_ptr proj_data_ptr, const int min_z, const int max_z, const int min_y, const int max_y, + const int min_x, const int max_x, const DiscretisedDensity<3, float>& in_density); + +void find_inverse_and_bck_densels(DiscretisedDensity<3, float>& image, VectorWithOffset*>& all_segments, + VectorWithOffset*>& attenuation_segmnets, const int min_z, const int max_z, + const int min_y, const int max_y, const int min_x, const int max_x, + ProjMatrixByDensel& proj_matrix, bool do_attenuation, const float threshold, + bool normalize_result); END_NAMESPACE_STIR diff --git a/src/include/stir_experimental/listmode/CListModeDataLMF.h b/src/include/stir_experimental/listmode/CListModeDataLMF.h index 2419dc8497..92a03e7d22 100644 --- a/src/include/stir_experimental/listmode/CListModeDataLMF.h +++ b/src/include/stir_experimental/listmode/CListModeDataLMF.h @@ -4,14 +4,14 @@ \file \ingroup ClearPET_utilities \brief Declaration of class stir::CListModeDataLMF - + \author Kris Thielemans - + */ /* Copyright (C) 2003- 2004, Hammersmith Imanet Ltd - This software is distributed under the terms + This software is distributed under the terms of the GNU Lesser General Public Licence (LGPL) See STIR/LICENSE.txt for details */ @@ -23,7 +23,7 @@ #include "stir/shared_ptr.h" #include "LMF/lmf.h" // TODO adjust location //#include "LMF/LMF_ClearPET.h" // TODO don't know which is needed -//#include "LMF/LMF_Interfile.h" +//#include "LMF/LMF_Interfile.h" #include #include @@ -31,53 +31,39 @@ START_NAMESPACE_STIR - //! A class that reads the listmode data from an LMF file -class CListModeDataLMF : public CListModeData -{ +class CListModeDataLMF : public CListModeData { public: - //! Constructor taking a filename CListModeDataLMF(const std::string& listmode_filename); // Destructor closes the file and destroys various structures ~CListModeDataLMF(); - virtual std::time_t - get_scan_start_time_in_secs_since_1970() const - { return std::time_t(-1); } // TODO - - virtual - shared_ptr get_empty_record_sptr() const; + virtual std::time_t get_scan_start_time_in_secs_since_1970() const { return std::time_t(-1); } // TODO + virtual shared_ptr get_empty_record_sptr() const; //! LMF listmode data stores delayed events as well (as opposed to prompts) - virtual bool has_delayeds() const - { return true; } // TODO always? + virtual bool has_delayeds() const { return true; } // TODO always? - virtual - Succeeded get_next_record(CListRecord& event) const; + virtual Succeeded get_next_record(CListRecord& event) const; - virtual - Succeeded reset(); + virtual Succeeded reset(); - virtual - SavedPosition save_get_position(); + virtual SavedPosition save_get_position(); - virtual - Succeeded set_get_position(const SavedPosition&); + virtual Succeeded set_get_position(const SavedPosition&); private: - string listmode_filename; // TODO we really want to make this a shared_ptr I think to avoid memory leaks when throwing exceptions - struct LMF_ccs_encodingHeader *pEncoH; - FILE *pfCCS; + struct LMF_ccs_encodingHeader* pEncoH; + FILE* pfCCS; // possibly use this from LMF2Projection // SCANNER_CHECK_LIST scanCheckList; std::vector saved_get_positions; - }; END_NAMESPACE_STIR diff --git a/src/include/stir_experimental/listmode/CListRecordLMF.h b/src/include/stir_experimental/listmode/CListRecordLMF.h index ced054ad59..648188664a 100644 --- a/src/include/stir_experimental/listmode/CListRecordLMF.h +++ b/src/include/stir_experimental/listmode/CListRecordLMF.h @@ -3,15 +3,15 @@ /*! \file \ingroup ClearPET_utilities - \brief Preliminary code to handle listmode events - + \brief Preliminary code to handle listmode events + \author Kris Thielemans - + */ /* Copyright (C) 2003- 2011, Hammersmith Imanet Ltd - This software is distributed under the terms + This software is distributed under the terms of the GNU Lesser General Public Licence (LGPL) See STIR/LICENSE.txt for details */ @@ -31,27 +31,22 @@ class CListModeDataLMF; //! Class for storing and using a coincidence event from a listmode file /*! \ingroup ClearPET_utilities */ -class CListEventDataLMF -{ - public: - inline bool is_prompt() const { return true; } // TODO +class CListEventDataLMF { +public: + inline bool is_prompt() const { return true; } // TODO inline Succeeded set_prompt(const bool prompt = true) // TODO - { return Succeeded::no; } - - inline LORAs2Points get_LOR() const - { return this->lor; } - - - CartesianCoordinate3D pos1() const - { return lor.p1(); } - CartesianCoordinate3D& pos1() - { return lor.p1(); } - CartesianCoordinate3D pos2() const - { return lor.p2(); } - CartesianCoordinate3D& pos2() - { return lor.p1(); } - - private: + { + return Succeeded::no; + } + + inline LORAs2Points get_LOR() const { return this->lor; } + + CartesianCoordinate3D pos1() const { return lor.p1(); } + CartesianCoordinate3D& pos1() { return lor.p1(); } + CartesianCoordinate3D pos2() const { return lor.p2(); } + CartesianCoordinate3D& pos2() { return lor.p1(); } + +private: LORAs2Points lor; }; /*-coincidence event*/ @@ -60,18 +55,18 @@ class CListRecordLMF; //! A class for storing and using a timing 'event' from a listmode file /*! \ingroup ClearPET_utilities */ -class CListTimeDataLMF -{ - public: - inline unsigned long get_time_in_millisecs() const - { return time; }// TODO - inline Succeeded set_time_in_millisecs(const unsigned long time_in_millisecs)//TODO - { return Succeeded::no; } +class CListTimeDataLMF { +public: + inline unsigned long get_time_in_millisecs() const { return time; } // TODO + inline Succeeded set_time_in_millisecs(const unsigned long time_in_millisecs) // TODO + { + return Succeeded::no; + } + private: unsigned long time; // in millisecs TODO }; - //! A class for a general element of a listmode file /*! \ingroup ClearPET_utilities @@ -80,57 +75,40 @@ I store both time and CListEvent info in one CListRecordLMF. That's obviously not necessary nor desirable. */ -class CListRecordLMF : public CListRecord, public ListTime, public CListEvent -{ +class CListRecordLMF : public CListRecord, public ListTime, public CListEvent { public: - - CListRecordLMF& operator=(const CListEventDataLMF& event) - { - is_time_flag=false; - event_data = event; - } - bool is_time() const - { return is_time_flag == true; } - bool is_event() const - { return is_time_flag == false; } - virtual CListEvent& event() - { return *this; } - virtual const CListEvent& event() const - { return *this; } - virtual ListTime& time() - { return *this; } - virtual const ListTime& time() const - { return *this; } + CListRecordLMF& operator=(const CListEventDataLMF& event) { + is_time_flag = false; + event_data = event; + } + bool is_time() const { return is_time_flag == true; } + bool is_event() const { return is_time_flag == false; } + virtual CListEvent& event() { return *this; } + virtual const CListEvent& event() const { return *this; } + virtual ListTime& time() { return *this; } + virtual const ListTime& time() const { return *this; } bool operator==(const CListRecord& e2) const; - // time - inline double get_time_in_secs() const - { return time_data.get_time_in_secs(); } - inline Succeeded set_time_in_secs(const double time_in_secs) - { return time_data.set_time_in_secs(time_in_secs); } - inline unsigned int get_gating() const - { return time_data.get_gating(); } - inline Succeeded set_gating(unsigned int g) - { return time_data.set_gating(g); } + // time + inline double get_time_in_secs() const { return time_data.get_time_in_secs(); } + inline Succeeded set_time_in_secs(const double time_in_secs) { return time_data.set_time_in_secs(time_in_secs); } + inline unsigned int get_gating() const { return time_data.get_gating(); } + inline Succeeded set_gating(unsigned int g) { return time_data.set_gating(g); } // event inline bool is_prompt() const { return event_data.is_prompt(); } - inline Succeeded set_prompt(const bool prompt = true) - { return event_data.set_prompt(prompt); } - + inline Succeeded set_prompt(const bool prompt = true) { return event_data.set_prompt(prompt); } - private: +private: friend class CListModeDataLMF; - CListEventDataLMF event_data; - CListTimeDataLMF time_data; + CListEventDataLMF event_data; + CListTimeDataLMF time_data; bool is_time_flag; }; - - END_NAMESPACE_STIR #endif diff --git a/src/include/stir_experimental/listmode/LmToProjDataWithMC.h b/src/include/stir_experimental/listmode/LmToProjDataWithMC.h index 5361319ae9..4176ac3fa3 100644 --- a/src/include/stir_experimental/listmode/LmToProjDataWithMC.h +++ b/src/include/stir_experimental/listmode/LmToProjDataWithMC.h @@ -9,16 +9,15 @@ \ingroup listmode \brief Declaration of class stir::LmToProjDataWithMC - + \author Sanida Mustafovic \author Kris Thielemans - + */ #ifndef __stir_listmode_LmToProjDataWithMC_H__ #define __stir_listmode_LmToProjDataWithMC_H__ - #include "stir/listmode/LmToProjData.h" #include "stir/CartesianCoordinate3D.h" #include "stir_experimental/AbsTimeInterval.h" @@ -32,39 +31,33 @@ START_NAMESPACE_STIR Implements LOR repositioning during bining of list mode data into sinograms. */ -class LmToProjDataWithMC : public LmToProjData -{ +class LmToProjDataWithMC : public LmToProjData { public: - - LmToProjDataWithMC(const char * const par_filename); + LmToProjDataWithMC(const char* const par_filename); virtual void get_bin_from_event(Bin& bin, const CListEvent&) const; virtual void process_new_time_event(const ListTime& time_event); -protected: +protected: //! motion information shared_ptr ro3d_ptr; //! switch between constant reference position, or one for each frame bool reference_position_is_average_position_in_frame; //! constant reference position (if used) - shared_ptr _reference_abs_time_sptr; + shared_ptr _reference_abs_time_sptr; virtual void start_new_time_frame(const unsigned int new_frame_num); - virtual void set_defaults(); virtual void initialise_keymap(); virtual bool post_processing(); private: - RigidObject3DTransformation _transformation_to_reference_position; RigidObject3DTransformation ro3dtrans; // actual motion for current_time - }; END_NAMESPACE_STIR - #endif diff --git a/src/include/stir_experimental/local_helping_functions.h b/src/include/stir_experimental/local_helping_functions.h index ff8c04677a..f6f6ff2714 100644 --- a/src/include/stir_experimental/local_helping_functions.h +++ b/src/include/stir_experimental/local_helping_functions.h @@ -1,58 +1,44 @@ - - #include "stir/Array.h" #include "stir/VoxelsOnCartesianGrid.h" - - /* Here there are helping function used locally in ModifiedInverseAverigingImageFilter.cxx */ - START_NAMESPACE_STIR // Complex multiplication -void mulitply_complex_arrays(Array<1,float>& out_array, const Array<1,float>& array_nom, - const Array<1,float>& array_denom); +void mulitply_complex_arrays(Array<1, float>& out_array, const Array<1, float>& array_nom, const Array<1, float>& array_denom); -// Complex division -void divide_complex_arrays( Array<1,float>& out_array, const Array<1,float>& array_nom, - const Array<1,float>& array_denom); +// Complex division +void divide_complex_arrays(Array<1, float>& out_array, const Array<1, float>& array_nom, const Array<1, float>& array_denom); // two argument implementation -void mulitply_complex_arrays(Array<1,float>& array_nom, - const Array<1,float>& array_denom); -void divide_complex_arrays( Array<1,float>& array_nom, - const Array<1,float>& array_denom); +void mulitply_complex_arrays(Array<1, float>& array_nom, const Array<1, float>& array_denom); +void divide_complex_arrays(Array<1, float>& array_nom, const Array<1, float>& array_denom); // convert 3D arra into 1D array -void convert_array_3D_into_1D_array ( Array<1,float>& out_array,const Array<3,float>& in_array); -// convert 1d array into 3d array -void convert_array_1D_into_3D_array( Array<3,float>& out_array,const Array<1,float>& in_array); +void convert_array_3D_into_1D_array(Array<1, float>& out_array, const Array<3, float>& in_array); +// convert 1d array into 3d array +void convert_array_1D_into_3D_array(Array<3, float>& out_array, const Array<1, float>& in_array); // create 3d kernel -void create_kernel_3d ( Array<3,float>& kernel_3d, const VectorWithOffset < float>& kernel_1d); -void create_kernel_2d ( Array<2, float> & kernel_2d, const VectorWithOffset < float>& kernel_1d); +void create_kernel_3d(Array<3, float>& kernel_3d, const VectorWithOffset& kernel_1d); +void create_kernel_2d(Array<2, float>& kernel_2d, const VectorWithOffset& kernel_1d); // padd filter coefficients and make them symmetric -void padd_filter_coefficients_3D_and_make_them_symmetric(VectorWithOffset < VectorWithOffset < VectorWithOffset < float> > > &padded_filter_coefficients_3D, - VectorWithOffset < VectorWithOffset < VectorWithOffset < float> > > &filter_coefficients); +void padd_filter_coefficients_3D_and_make_them_symmetric( + VectorWithOffset>>& padded_filter_coefficients_3D, + VectorWithOffset>>& filter_coefficients); +void convert_array_2D_into_1D_array(Array<1, float>& out_array, Array<2, float>& in_array); -void convert_array_2D_into_1D_array( Array<1,float>& out_array,Array<2,float>& in_array); - -void convert_array_1D_into_2D_array( Array<2,float>& out_array,Array<1,float>& in_array); - +void convert_array_1D_into_2D_array(Array<2, float>& out_array, Array<1, float>& in_array); void precompute_filter_coefficients_for_second_apporach(VoxelsOnCartesianGrid& precomputed_coefficients, - const VoxelsOnCartesianGrid& input_image, - VoxelsOnCartesianGrid& sensitivity_image, - VoxelsOnCartesianGrid& normalised_bck); - - - - + const VoxelsOnCartesianGrid& input_image, + VoxelsOnCartesianGrid& sensitivity_image, + VoxelsOnCartesianGrid& normalised_bck); END_NAMESPACE_STIR diff --git a/src/include/stir_experimental/modelling/BloodFrame.h b/src/include/stir_experimental/modelling/BloodFrame.h index 20bee8e500..0ab1d1483c 100644 --- a/src/include/stir_experimental/modelling/BloodFrame.h +++ b/src/include/stir_experimental/modelling/BloodFrame.h @@ -23,7 +23,7 @@ \brief Declaration of class stir::PlasmaData \author Charalampos Tsoumpas - + */ #ifndef __stir_modelling_BloodFrame_H__ @@ -35,42 +35,39 @@ START_NAMESPACE_STIR -class BloodFrame -{ +class BloodFrame { public: - //! default constructor + //! default constructor inline BloodFrame(); - - //! constructor of a frame, and its blood_counts_in_kBq, based on the acquired image. + + //! constructor of a frame, and its blood_counts_in_kBq, based on the acquired image. inline BloodFrame(const unsigned int frame_num, const float blood_counts); - //! constructor, mean time of a frame in seconds, and its blood_counts_in_kBq, based on the acquired image. - inline BloodFrame(const unsigned int frame_num, - const float frame_start_time_in_s, - const float frame_end_time_in_s, - const float blood_counts); + //! constructor, mean time of a frame in seconds, and its blood_counts_in_kBq, based on the acquired image. + inline BloodFrame(const unsigned int frame_num, const float frame_start_time_in_s, const float frame_end_time_in_s, + const float blood_counts); //! default destructor inline ~BloodFrame(); - - //! set the time of the sample - inline void set_frame_start_time_in_s( const float frame_start_time_in_s ); - //! set the time of the sample - inline void set_frame_end_time_in_s( const float frame_end_time_in_s ); - //! get the time of the sample - inline float get_frame_start_time_in_s() const; - //! get the time of the sample - inline float get_frame_end_time_in_s() const; - //! set the frame number of the sample, if the plasma is based on the acquired image. - inline void set_frame_num( const unsigned int frame_num ); - //! get the frame number of the sample, if the plasma is based on the acquired image. + + //! set the time of the sample + inline void set_frame_start_time_in_s(const float frame_start_time_in_s); + //! set the time of the sample + inline void set_frame_end_time_in_s(const float frame_end_time_in_s); + //! get the time of the sample + inline float get_frame_start_time_in_s() const; + //! get the time of the sample + inline float get_frame_end_time_in_s() const; + //! set the frame number of the sample, if the plasma is based on the acquired image. + inline void set_frame_num(const unsigned int frame_num); + //! get the frame number of the sample, if the plasma is based on the acquired image. inline unsigned int get_frame_num() const; - //! set the blood counts of the sample - inline void set_blood_counts_in_kBq( const float blood_counts ); - //! get the blood counts of the sample - inline float get_blood_counts_in_kBq() const; - -private : + //! set the blood counts of the sample + inline void set_blood_counts_in_kBq(const float blood_counts); + //! get the blood counts of the sample + inline float get_blood_counts_in_kBq() const; + +private: float _blood_counts; float _frame_start_time_in_s; float _frame_end_time_in_s; diff --git a/src/include/stir_experimental/modelling/BloodFrame.inl b/src/include/stir_experimental/modelling/BloodFrame.inl index a31ed7edb0..28f06ef1ee 100644 --- a/src/include/stir_experimental/modelling/BloodFrame.inl +++ b/src/include/stir_experimental/modelling/BloodFrame.inl @@ -27,61 +27,72 @@ START_NAMESPACE_STIR - //! default constructor -BloodFrame::BloodFrame() -{ } - //! constructor, time in s -BloodFrame::BloodFrame(const unsigned int frame_num, const float frame_start_time_in_s, const float frame_end_time_in_s, const float blood_counts) -{ - BloodFrame::set_frame_num( frame_num ); - BloodFrame::set_frame_start_time_in_s( frame_start_time_in_s ); - BloodFrame::set_frame_end_time_in_s( frame_end_time_in_s ); - BloodFrame::set_blood_counts_in_kBq( blood_counts ); +//! default constructor +BloodFrame::BloodFrame() {} +//! constructor, time in s +BloodFrame::BloodFrame(const unsigned int frame_num, const float frame_start_time_in_s, const float frame_end_time_in_s, + const float blood_counts) { + BloodFrame::set_frame_num(frame_num); + BloodFrame::set_frame_start_time_in_s(frame_start_time_in_s); + BloodFrame::set_frame_end_time_in_s(frame_end_time_in_s); + BloodFrame::set_blood_counts_in_kBq(blood_counts); } - //! constructor, frame number, if the plasma is based on the acquired image. -BloodFrame::BloodFrame(const unsigned int frame_num, const float blood_sample_counts) -{ - BloodFrame::set_frame_num( frame_num ); - BloodFrame::set_blood_counts_in_kBq( blood_sample_counts ); +//! constructor, frame number, if the plasma is based on the acquired image. +BloodFrame::BloodFrame(const unsigned int frame_num, const float blood_sample_counts) { + BloodFrame::set_frame_num(frame_num); + BloodFrame::set_blood_counts_in_kBq(blood_sample_counts); } - //! default destructor -BloodFrame::~BloodFrame() -{ } - - //! set the start_time of the sample -void BloodFrame::set_frame_start_time_in_s( const float frame_start_time_in_s ) -{ BloodFrame::_frame_start_time_in_s=frame_start_time_in_s ; } +//! default destructor +BloodFrame::~BloodFrame() {} - //! get the start_time of the sample -float BloodFrame::get_frame_start_time_in_s() const -{ return BloodFrame::_frame_start_time_in_s ; } - - //! set the start_time of the sample -void BloodFrame::set_frame_end_time_in_s( const float frame_end_time_in_s ) -{ BloodFrame::_frame_end_time_in_s=frame_end_time_in_s ; } +//! set the start_time of the sample +void +BloodFrame::set_frame_start_time_in_s(const float frame_start_time_in_s) { + BloodFrame::_frame_start_time_in_s = frame_start_time_in_s; +} - //! get the start_time of the sample -float BloodFrame::get_frame_end_time_in_s() const -{ return BloodFrame::_frame_end_time_in_s ; } +//! get the start_time of the sample +float +BloodFrame::get_frame_start_time_in_s() const { + return BloodFrame::_frame_start_time_in_s; +} - //! set the frame number of the sample, if the plasma is based on the acquired image. -void BloodFrame::set_frame_num( const unsigned int frame_num ) -{ BloodFrame::_frame_num=frame_num ; } +//! set the start_time of the sample +void +BloodFrame::set_frame_end_time_in_s(const float frame_end_time_in_s) { + BloodFrame::_frame_end_time_in_s = frame_end_time_in_s; +} - //! get the frame number of the sample, if the plasma is based on the acquired image. -unsigned int BloodFrame::get_frame_num() const -{ return BloodFrame::_frame_num ; } +//! get the start_time of the sample +float +BloodFrame::get_frame_end_time_in_s() const { + return BloodFrame::_frame_end_time_in_s; +} - //! set the blood counts of the sample -void BloodFrame::set_blood_counts_in_kBq( const float blood_counts ) -{ BloodFrame::_blood_counts=blood_counts ; } +//! set the frame number of the sample, if the plasma is based on the acquired image. +void +BloodFrame::set_frame_num(const unsigned int frame_num) { + BloodFrame::_frame_num = frame_num; +} - //! get the blood counts of the sample -float BloodFrame::get_blood_counts_in_kBq() const -{ return BloodFrame::_blood_counts ; } +//! get the frame number of the sample, if the plasma is based on the acquired image. +unsigned int +BloodFrame::get_frame_num() const { + return BloodFrame::_frame_num; +} +//! set the blood counts of the sample +void +BloodFrame::set_blood_counts_in_kBq(const float blood_counts) { + BloodFrame::_blood_counts = blood_counts; +} +//! get the blood counts of the sample +float +BloodFrame::get_blood_counts_in_kBq() const { + return BloodFrame::_blood_counts; +} END_NAMESPACE_STIR diff --git a/src/include/stir_experimental/modelling/BloodFrameData.h b/src/include/stir_experimental/modelling/BloodFrameData.h index 20f05f12f1..15fab18f0d 100644 --- a/src/include/stir_experimental/modelling/BloodFrameData.h +++ b/src/include/stir_experimental/modelling/BloodFrameData.h @@ -23,7 +23,7 @@ \brief Declaration of class stir::BloodFrameData \author Charalampos Tsoumpas - + */ #ifndef __stir_modelling_BloodFrameData_H__ @@ -35,86 +35,81 @@ START_NAMESPACE_STIR - -/*! +/*! \ingroup modelling \brief A class for storing plasma samples of a single study. */ -class BloodFrameData -{ - typedef std::vector plot_type; - - public: - - typedef plot_type::const_iterator const_iterator; - /* enum VolumeUnits - { ml , litre }; - enum SamplingTimeUnits - { seconds , minutes }; - enum RadioactivityUnits - { counts_per_sec , counts_per_min , kBq }; - - inline void set_input_units(const SamplingTimeUnits input_sampling_time_units, - const VolumeUnits input_volume_units, - const RadioactivityUnits input_radioactivity_units ) ; - - */ +class BloodFrameData { + typedef std::vector plot_type; + +public: + typedef plot_type::const_iterator const_iterator; + /* enum VolumeUnits + { ml , litre }; + enum SamplingTimeUnits + { seconds , minutes }; + enum RadioactivityUnits + { counts_per_sec , counts_per_min , kBq }; + + inline void set_input_units(const SamplingTimeUnits input_sampling_time_units, + const VolumeUnits input_volume_units, + const RadioactivityUnits input_radioactivity_units ) ; + + */ //! Implementation to read the input function from ONLY a 2-columns frame data (FrameNumber-InputFunctionRadioactivity). - inline void read_blood_frame_data(const std::string input_string) ; - inline void set_plot(const std::vector & blood_plot) ; + inline void read_blood_frame_data(const std::string input_string); + inline void set_plot(const std::vector& blood_plot); //! Implementation to set the input units not currently used. Always, it assumed to use kBq, seconds, ml. - //!Function to shift the time data + //! Function to shift the time data inline void shift_time(const float time_shift); - //!Function to get the time data + //! Function to get the time data inline float get_time_shift(); - //!Function to set the isotope halflife + //! Function to set the isotope halflife inline void set_isotope_halflife(const float isotope_halflife); - //!Function to set _is_decay_corrected boolean true ar false + //! Function to set _is_decay_corrected boolean true ar false inline void set_if_decay_corrected(const bool is_decay_corrected); - //!Function to set _is_decay_corrected boolean true ar false + //! Function to set _is_decay_corrected boolean true ar false inline bool get_if_decay_corrected(); - //!Function to decay correct the data + //! Function to decay correct the data inline void decay_correct_BloodFrameData(); //! default constructor inline BloodFrameData(); //! constructor giving a vector //ChT::ToDO: Better to use iterators - inline BloodFrameData(const std::vector & blood_plot); + inline BloodFrameData(const std::vector& blood_plot); //! default constructor inline ~BloodFrameData(); //! void begin() and end() iterators for the plasma curve ; -inline const_iterator begin() const ; -inline const_iterator end() const ; - inline unsigned int size() const ; - // non const_iterator should be defined if the plasma data needs to be changed -//inline iterator begin() ; -//inline iterator end() ; - - private: - /* VolumeUnits _input_volume_units ; - SamplingTimeUnits _input_sampling_time_units ; - RadioactivityUnits _input_radioactivity_units ;*/ - bool _is_decay_corrected ; + inline const_iterator begin() const; + inline const_iterator end() const; + inline unsigned int size() const; + // non const_iterator should be defined if the plasma data needs to be changed + // inline iterator begin() ; + // inline iterator end() ; + +private: + /* VolumeUnits _input_volume_units ; + SamplingTimeUnits _input_sampling_time_units ; + RadioactivityUnits _input_radioactivity_units ;*/ + bool _is_decay_corrected; float _isotope_halflife; - std::vector _blood_plot ; + std::vector _blood_plot; int _num_frames; - float _time_shift ; + float _time_shift; }; - END_NAMESPACE_STIR - #include "stir_experimental/modelling/BloodFrameData.inl" #endif //__stir_modelling_BloodFrameData_H__ diff --git a/src/include/stir_experimental/modelling/BloodFrameData.inl b/src/include/stir_experimental/modelling/BloodFrameData.inl index 21e4ccc4b0..57b996c9de 100644 --- a/src/include/stir_experimental/modelling/BloodFrameData.inl +++ b/src/include/stir_experimental/modelling/BloodFrameData.inl @@ -28,115 +28,114 @@ START_NAMESPACE_STIR //! default constructor -BloodFrameData::BloodFrameData() -{ } +BloodFrameData::BloodFrameData() {} //! constructor giving a vector //ChT::ToDO: Better to use iterators -BloodFrameData::BloodFrameData(const std::vector & blood_plot) -{this->_blood_plot=blood_plot;} +BloodFrameData::BloodFrameData(const std::vector& blood_plot) { this->_blood_plot = blood_plot; } //! default destructor -BloodFrameData::~BloodFrameData() -{ } +BloodFrameData::~BloodFrameData() {} //! Implementation to read the input function from ONLY a 2-columns frame data (FrameNumber-InputFunctionRadioactivity). -void BloodFrameData::read_blood_frame_data(const std::string input_string) -{ - std::ifstream data_stream(input_string.c_str()); - if(!data_stream) - error("cannot read blood frame data from file.\n"); - else - { - data_stream >> _num_frames ; - while(true) - { - unsigned int frame_num=0; - float blood_sample_radioactivity=0.F; - data_stream >> frame_num ; - data_stream >> blood_sample_radioactivity ; - if(!data_stream) - break; - const BloodFrame current_sample(frame_num,blood_sample_radioactivity); - (this->_blood_plot).push_back(current_sample); - } - } +void +BloodFrameData::read_blood_frame_data(const std::string input_string) { + std::ifstream data_stream(input_string.c_str()); + if (!data_stream) + error("cannot read blood frame data from file.\n"); + else { + data_stream >> _num_frames; + while (true) { + unsigned int frame_num = 0; + float blood_sample_radioactivity = 0.F; + data_stream >> frame_num; + data_stream >> blood_sample_radioactivity; + if (!data_stream) + break; + const BloodFrame current_sample(frame_num, blood_sample_radioactivity); + (this->_blood_plot).push_back(current_sample); + } + } } //! Implementation to set the input units not currently used. /* void -BloodFrameData::set_input_units( SamplingTimeUnits input_sampling_time_units, - VolumeUnits input_volume_units, - RadioactivityUnits input_radioactivity_units ) +BloodFrameData::set_input_units( SamplingTimeUnits input_sampling_time_units, + VolumeUnits input_volume_units, + RadioactivityUnits input_radioactivity_units ) { _input_sampling_time_units=input_sampling_time_units ; _input_volume_units=input_volume_units ; _input_radioactivity_units=input_radioactivity_units ; -} +} */ -//!Function to shift the time data -void BloodFrameData::shift_time(const float time_shift) -{ - _time_shift=time_shift; - for(std::vector::iterator cur_iter=this->_blood_plot.begin() ; - cur_iter!=this->_blood_plot.end() ; ++cur_iter) - { - cur_iter->set_frame_start_time_in_s(cur_iter->get_frame_start_time_in_s()+time_shift); - cur_iter->set_frame_end_time_in_s(cur_iter->get_frame_end_time_in_s()+time_shift); - } +//! Function to shift the time data +void +BloodFrameData::shift_time(const float time_shift) { + _time_shift = time_shift; + for (std::vector::iterator cur_iter = this->_blood_plot.begin(); cur_iter != this->_blood_plot.end(); ++cur_iter) { + cur_iter->set_frame_start_time_in_s(cur_iter->get_frame_start_time_in_s() + time_shift); + cur_iter->set_frame_end_time_in_s(cur_iter->get_frame_end_time_in_s() + time_shift); + } +} +//! Function to get the time data +float +BloodFrameData::get_time_shift() { + return BloodFrameData::_time_shift; } -//!Function to get the time data -float BloodFrameData::get_time_shift() -{ return BloodFrameData::_time_shift ; } -void BloodFrameData::set_isotope_halflife(const float isotope_halflife) -{ _isotope_halflife=isotope_halflife; } +void +BloodFrameData::set_isotope_halflife(const float isotope_halflife) { + _isotope_halflife = isotope_halflife; +} -void BloodFrameData:: -set_if_decay_corrected(const bool is_decay_corrected) -{ this->_is_decay_corrected=is_decay_corrected; } +void +BloodFrameData::set_if_decay_corrected(const bool is_decay_corrected) { + this->_is_decay_corrected = is_decay_corrected; +} -bool BloodFrameData:: -get_if_decay_corrected() -{ return this->_is_decay_corrected; } +bool +BloodFrameData::get_if_decay_corrected() { + return this->_is_decay_corrected; +} -void BloodFrameData:: -decay_correct_BloodFrameData() -{ - if (BloodFrameData::_is_decay_corrected==true) +void +BloodFrameData::decay_correct_BloodFrameData() { + if (BloodFrameData::_is_decay_corrected == true) warning("BloodFrameData are already decay corrected"); - else - { - for(std::vector::iterator cur_iter=this->_blood_plot.begin() ; - cur_iter!=this->_blood_plot.end() ; ++cur_iter) - cur_iter->set_blood_counts_in_kBq(cur_iter->get_blood_counts_in_kBq() - *decay_correct_factor(_isotope_halflife,cur_iter->get_frame_start_time_in_s(),cur_iter->get_frame_end_time_in_s())); - BloodFrameData::set_if_decay_corrected(true); - } + else { + for (std::vector::iterator cur_iter = this->_blood_plot.begin(); cur_iter != this->_blood_plot.end(); ++cur_iter) + cur_iter->set_blood_counts_in_kBq( + cur_iter->get_blood_counts_in_kBq() * + decay_correct_factor(_isotope_halflife, cur_iter->get_frame_start_time_in_s(), cur_iter->get_frame_end_time_in_s())); + BloodFrameData::set_if_decay_corrected(true); + } } +void +BloodFrameData::set_plot(const std::vector& blood_plot) { + this->_blood_plot = blood_plot; +} -void BloodFrameData::set_plot(const std::vector & blood_plot) -{this->_blood_plot=blood_plot;} - - -//BloodFrameData begin() and end() of the BloodFrameData ; +// BloodFrameData begin() and end() of the BloodFrameData ; BloodFrameData::const_iterator -BloodFrameData::begin() const -{ return this->_blood_plot.begin() ; } +BloodFrameData::begin() const { + return this->_blood_plot.begin(); +} BloodFrameData::const_iterator -BloodFrameData::end() const -{ return this->_blood_plot.end() ; } +BloodFrameData::end() const { + return this->_blood_plot.end(); +} unsigned int -BloodFrameData::size() const -{ return this->_blood_plot.size() ; } +BloodFrameData::size() const { + return this->_blood_plot.size(); +} /* //BloodFrameData begin() and end() of the BloodFrameData ; BloodFrameData::iterator -BloodFrameData::begin() +BloodFrameData::begin() { return this->_blood_plot.begin() ; } BloodFrameData::iterator BloodFrameData::end() { return this->_blood_plot.end() ; } */ - END_NAMESPACE_STIR diff --git a/src/include/stir_experimental/modelling/OneParamModel.h b/src/include/stir_experimental/modelling/OneParamModel.h index 753dce753b..6db2bd5582 100644 --- a/src/include/stir_experimental/modelling/OneParamModel.h +++ b/src/include/stir_experimental/modelling/OneParamModel.h @@ -32,23 +32,21 @@ START_NAMESPACE_STIR -class OneParamModel -{ - public: - +class OneParamModel { +public: //! default constructor inline OneParamModel(); //! constructor inline OneParamModel(const int starting_frame, const int last_frame); - //! Create a unit model matrix for a single frame and single parameter + //! Create a unit model matrix for a single frame and single parameter inline ModelMatrix<1> get_unit_matrix(const int starting_frame, const int last_frame); //! default destructor inline ~OneParamModel(); - private: +private: ModelMatrix<1> _unit_matrix; int _starting_frame; int _last_frame; diff --git a/src/include/stir_experimental/modelling/OneParamModel.inl b/src/include/stir_experimental/modelling/OneParamModel.inl index fbea5c5773..7f7b4b7ea7 100644 --- a/src/include/stir_experimental/modelling/OneParamModel.inl +++ b/src/include/stir_experimental/modelling/OneParamModel.inl @@ -28,43 +28,39 @@ START_NAMESPACE_STIR //! default constructor -OneParamModel::OneParamModel() -{ _matrix_is_stored=false; } +OneParamModel::OneParamModel() { _matrix_is_stored = false; } -OneParamModel::OneParamModel(const int starting_frame, const int last_frame) -{ - this->_matrix_is_stored=false; - this->_starting_frame=starting_frame; - this->_last_frame=last_frame; +OneParamModel::OneParamModel(const int starting_frame, const int last_frame) { + this->_matrix_is_stored = false; + this->_starting_frame = starting_frame; + this->_last_frame = last_frame; } //! default destructor -OneParamModel::~OneParamModel() -{ } +OneParamModel::~OneParamModel() {} -//! Create a unit model matrix for a single frame and single parameter -ModelMatrix<1> -OneParamModel:: -get_unit_matrix(const int starting_frame, const int last_frame) -{ - if(_matrix_is_stored==false) - { - this->_starting_frame=starting_frame; - this->_last_frame=last_frame; - BasicCoordinate<2,int> min_range; - BasicCoordinate<2,int> max_range; - min_range[1]=1; min_range[2]=this->_starting_frame; - max_range[1]=1; max_range[2]=this->_last_frame; - IndexRange<2> data_range(min_range,max_range); - Array<2,float> unit_array(data_range); - - for(int frame_num=this->_starting_frame ; frame_num<=this->_last_frame; ++frame_num) - unit_array[1][frame_num]=1.F; +//! Create a unit model matrix for a single frame and single parameter +ModelMatrix<1> +OneParamModel::get_unit_matrix(const int starting_frame, const int last_frame) { + if (_matrix_is_stored == false) { + this->_starting_frame = starting_frame; + this->_last_frame = last_frame; + BasicCoordinate<2, int> min_range; + BasicCoordinate<2, int> max_range; + min_range[1] = 1; + min_range[2] = this->_starting_frame; + max_range[1] = 1; + max_range[2] = this->_last_frame; + IndexRange<2> data_range(min_range, max_range); + Array<2, float> unit_array(data_range); - _unit_matrix.set_model_array(unit_array); - _matrix_is_stored=true; - } - return _unit_matrix ; + for (int frame_num = this->_starting_frame; frame_num <= this->_last_frame; ++frame_num) + unit_array[1][frame_num] = 1.F; + + _unit_matrix.set_model_array(unit_array); + _matrix_is_stored = true; + } + return _unit_matrix; } END_NAMESPACE_STIR diff --git a/src/include/stir_experimental/motion/MatchTrackerAndScanner.h b/src/include/stir_experimental/motion/MatchTrackerAndScanner.h index a8d1474233..0f4ba04cbe 100644 --- a/src/include/stir_experimental/motion/MatchTrackerAndScanner.h +++ b/src/include/stir_experimental/motion/MatchTrackerAndScanner.h @@ -14,7 +14,7 @@ \author Kris Thielemans - + */ #include "stir_experimental/motion/RigidObject3DTransformation.h" #include "stir/TimeFrameDefinitions.h" @@ -32,13 +32,13 @@ START_NAMESPACE_STIR You need to have performed a scan where the '0' marker of the tracker is filled with some radioactive material, and the marker is then moved to various stationary positions inside - the scanner, while performing a (list-mode) scan, while the tracker is running. - Remember: discrete movements. - + the scanner, while performing a (list-mode) scan, while the tracker is running. + Remember: discrete movements. + Then you need to make a frame-definition file where all non-stationary parts are skipped. Then you sort the list mode data into sinograms, and reconstruct images (preferably with large zoom) for each of these discrete positions. - + \par What does it do? This implements Horn's method, as discussed in detail in Roger Fulton's thesis. Briefly: @@ -46,7 +46,7 @@ START_NAMESPACE_STIR a threshold) in the image as the location of the point source. - for each tracker-sample in the time frame, we find where the tracker says that the 0 marker moved to. - - All the data for all time frames is then put through + - All the data for all time frames is then put through RigidObject3DTransformation::find_closest_transformation to find a least-squares fit between the 2 sets of coordinates. - The class writes various diagnostics to stdout, including the value of the fit. @@ -59,9 +59,9 @@ START_NAMESPACE_STIR MoveImage Parameters:= ; see TimeFrameDefinitions time frame_definition filename := frame_definition_filename - + ; next parameter is optional (and not normally necessary) - ; it can be used if the frame definitions are relative to another scan as what + ; it can be used if the frame definitions are relative to another scan as what ; is used to for the rigid object motion (i.e. currently the list mode data used ; for the Polaris synchronisation) ; scan_start_time_secs_since_1970_UTC @@ -70,7 +70,7 @@ START_NAMESPACE_STIR ; specify motion, see stir::RigidObject3DMotion Rigid Object 3D Motion Type := type - ; optional field to determine relative threshold to apply to + ; optional field to determine relative threshold to apply to ; the image before taking the centre of gravity ; it is relative to the maximum in each image (i.e. .5 would be at half the maximum) ; default is .1 @@ -84,44 +84,37 @@ START_NAMESPACE_STIR \warning Currently the motion object needs to be defined using a transformation_from_scanner_coords file. However, the value of the transformation is completely ignored by the current class. -*/ -class MatchTrackerAndScanner : public ParsingObject -{ +*/ +class MatchTrackerAndScanner : public ParsingObject { public: - MatchTrackerAndScanner(const char * const par_filename); + MatchTrackerAndScanner(const char* const par_filename); //! finds the match when all parameters have been set /*! will store the transformation as part of this object, but also write it to stdout */ Succeeded run(); - const TimeFrameDefinitions& - get_time_frame_defs() const; + const TimeFrameDefinitions& get_time_frame_defs() const; - double get_frame_start_time(unsigned frame_num) const - { return frame_defs.get_start_time(frame_num) + scan_start_time; } + double get_frame_start_time(unsigned frame_num) const { return frame_defs.get_start_time(frame_num) + scan_start_time; } - double get_frame_end_time(unsigned frame_num) const - { return frame_defs.get_end_time(frame_num) + scan_start_time; } + double get_frame_end_time(unsigned frame_num) const { return frame_defs.get_end_time(frame_num) + scan_start_time; } - const string& get_image_filename_prefix() const - { return _image_filename_prefix; } - - const RigidObject3DMotion& get_motion() const - { return *_ro3d_sptr; } + const string& get_image_filename_prefix() const { return _image_filename_prefix; } - const RigidObject3DTransformation& - get_transformation_from_scanner_coords() const - { return _transformation_from_scanner_coords; } + const RigidObject3DMotion& get_motion() const { return *_ro3d_sptr; } -protected: + const RigidObject3DTransformation& get_transformation_from_scanner_coords() const { + return _transformation_from_scanner_coords; + } +protected: // all of these really should be in a AbsTimeFrameDefinitions class or so TimeFrameDefinitions frame_defs; int scan_start_time_secs_since_1970_UTC; double _current_frame_end_time; double _current_frame_start_time; - + //! parsing functions virtual void set_defaults(); virtual void initialise_keymap(); @@ -135,11 +128,12 @@ class MatchTrackerAndScanner : public ParsingObject string _image_filename_prefix; float relative_threshold; + private: shared_ptr _ro3d_sptr; // will be set to new value - RigidObject3DTransformation _transformation_from_scanner_coords; + RigidObject3DTransformation _transformation_from_scanner_coords; }; END_NAMESPACE_STIR diff --git a/src/include/stir_experimental/motion/NonRigidObjectTransformationUsingBSplines.h b/src/include/stir_experimental/motion/NonRigidObjectTransformationUsingBSplines.h index e0f7e1843e..204f977941 100644 --- a/src/include/stir_experimental/motion/NonRigidObjectTransformationUsingBSplines.h +++ b/src/include/stir_experimental/motion/NonRigidObjectTransformationUsingBSplines.h @@ -16,7 +16,6 @@ #ifndef __stir_motion_NonRigidObjectTransformationUsingBSplines_H__ #define __stir_motion_NonRigidObjectTransformationUsingBSplines_H__ - #include "stir_experimental/motion/ObjectTransformation.h" #include "stir/CartesianCoordinate3D.h" #include "stir/numerics/BSplines.h" @@ -29,13 +28,11 @@ START_NAMESPACE_STIR class Succeeded; template - class DeformationFieldOnCartesianGrid : - public BasicCoordinate > +class DeformationFieldOnCartesianGrid : public BasicCoordinate> // public DiscretisedDensityOnCartesianGrid > { - public: +public: DeformationFieldOnCartesianGrid() {} - }; /*! \ingroup motion @@ -44,46 +41,42 @@ template */ template class NonRigidObjectTransformationUsingBSplines - : - public - RegisteredParsingObject, - ObjectTransformation, - ObjectTransformation > -{ + : public RegisteredParsingObject, + ObjectTransformation, ObjectTransformation> { public: - static const char * const registered_name; + static const char* const registered_name; // Default constructor NonRigidObjectTransformationUsingBSplines(); /// Give x, y and z components of the deformation field images separately. /// N.B., this will only work if the three components are in STIR orientation. - NonRigidObjectTransformationUsingBSplines(const std::string &filename_x, const std::string &filename_y, const std::string &filename_z, const int bspline_order); + NonRigidObjectTransformationUsingBSplines(const std::string& filename_x, const std::string& filename_y, + const std::string& filename_z, const int bspline_order); /// Give x, y and z components of the deformation field images together (e.g., multicomponent nifti) NonRigidObjectTransformationUsingBSplines(const std::string& filename, const int bspline_order); - //! Transform point - virtual - BasicCoordinate - transform_point(const BasicCoordinate& point) const; + //! Transform point + virtual BasicCoordinate transform_point(const BasicCoordinate& point) const; - float jacobian(const BasicCoordinate& point) const; + float jacobian(const BasicCoordinate& point) const; virtual void set_defaults(); virtual void initialise_keymap(); virtual bool post_processing(); virtual void set_key_values(); + private: - BasicCoordinate > interpolator; - //BasicCoordinate _grid_spacing; - //BasicCoordinate _origin; + BasicCoordinate> interpolator; + // BasicCoordinate _grid_spacing; + // BasicCoordinate _origin; CartesianCoordinate3D _grid_spacing; CartesianCoordinate3D _origin; BSpline::BSplineType _bspline_type; // use for parsing only - shared_ptr< DeformationFieldOnCartesianGrid > deformation_field_sptr; + shared_ptr> deformation_field_sptr; int _bspline_order; // for NCAT only std::string _deformation_field_from_NCAT_file; @@ -110,7 +103,6 @@ operator>>(std::istream& , NonRigidObjectTransformationUsingBSplines& rigid_object_transformation); #endif - END_NAMESPACE_STIR #endif diff --git a/src/include/stir_experimental/motion/ObjectTransformation.h b/src/include/stir_experimental/motion/ObjectTransformation.h index 49df86751b..22c6cfea30 100644 --- a/src/include/stir_experimental/motion/ObjectTransformation.h +++ b/src/include/stir_experimental/motion/ObjectTransformation.h @@ -16,7 +16,6 @@ #ifndef __stir_motion_ObjectTransformation_H__ #define __stir_motion_ObjectTransformation_H__ - #include "stir/BasicCoordinate.h" #include "stir/RegisteredObject.h" #include "stir/ParsingObject.h" @@ -24,26 +23,22 @@ START_NAMESPACE_STIR /*! \ingroup motion - \brief Base-class for performing (potentially non-rigid) object transformations + \brief Base-class for performing (potentially non-rigid) object transformations */ template -class ObjectTransformation : - public RegisteredObject > -{ +class ObjectTransformation : public RegisteredObject> { public: //! typedef used by read_from_file typedef ObjectTransformation hierarchy_base_type; virtual ~ObjectTransformation() {} - //! Transform point + //! Transform point /* \todo should be CartesianCoordinate, but we don't have that class yet*/ - virtual BasicCoordinate - transform_point(const BasicCoordinate& point) const = 0; + virtual BasicCoordinate transform_point(const BasicCoordinate& point) const = 0; //! Returns the determinant of the Jacobian matrix /*! This is related to the volume-element change due to the transformation. */ - virtual float - jacobian(const BasicCoordinate& point) const = 0; + virtual float jacobian(const BasicCoordinate& point) const = 0; }; END_NAMESPACE_STIR diff --git a/src/include/stir_experimental/motion/PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion.h b/src/include/stir_experimental/motion/PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion.h index 98c9f9be7c..c0349f2d66 100644 --- a/src/include/stir_experimental/motion/PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion.h +++ b/src/include/stir_experimental/motion/PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion.h @@ -28,7 +28,6 @@ #ifndef __stir_recon_buildblock_PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion_H__ #define __stir_recon_buildblock_PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion_H__ - #include "stir/RegisteredParsingObject.h" #include "stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.h" #include "stir/GatedProjData.h" @@ -46,38 +45,29 @@ class GatedProjData; \brief */ template -class PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion -: public RegisteredParsingObject - , - GeneralisedObjectiveFunction, - SumOfGeneralisedObjectiveFunctions, - TargetT, - PoissonLogLikelihoodWithLinearModelForMean > -> -{ - private: - typedef - RegisteredParsingObject - , - GeneralisedObjectiveFunction, - SumOfGeneralisedObjectiveFunctions, - TargetT, - PoissonLogLikelihoodWithLinearModelForMean > - > - base_type; +class PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion + : public RegisteredParsingObject< + PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion, GeneralisedObjectiveFunction, + SumOfGeneralisedObjectiveFunctions, TargetT, + PoissonLogLikelihoodWithLinearModelForMean>> { +private: + typedef RegisteredParsingObject< + PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion, GeneralisedObjectiveFunction, + SumOfGeneralisedObjectiveFunctions, TargetT, + PoissonLogLikelihoodWithLinearModelForMean>> + base_type; public: - //! Name which will be used when parsing a GeneralisedObjectiveFunction object - static const char * const registered_name; + static const char* const registered_name; //! Default constructor calls set_defaults() PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion(); - + /*! \name Functions to set parameters This can be used as alternative to the parsing mechanism. \warning After using any of these, you have to call set_up(). - \warning Be careful with setting shared pointers. If you modify the objects in + \warning Be careful with setting shared pointers. If you modify the objects in one place, all objects that use the shared pointer will be affected. */ @@ -86,28 +76,20 @@ class PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion void set_max_segment_num_to_process(const int); void set_zero_seg0_end_planes(const bool); void set_additive_proj_data_sptr(const shared_ptr&); - void set_projector_pair_sptr(const shared_ptr&) ; + void set_projector_pair_sptr(const shared_ptr&); void set_frame_num(const int); void set_frame_definitions(const TimeFrameDefinitions&); //@} - virtual - TargetT * - construct_target_ptr() const; + virtual TargetT* construct_target_ptr() const; - virtual - void - compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, - const TargetT& target, - const int subset_num); -protected: + virtual void compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, const TargetT& target, + const int subset_num); - virtual - Succeeded - set_up_before_sensitivity(shared_ptr const& target_sptr); +protected: + virtual Succeeded set_up_before_sensitivity(shared_ptr const& target_sptr); - virtual void - add_subset_sensitivity(TargetT& sensitivity, const int subset_num) const; + virtual void add_subset_sensitivity(TargetT& sensitivity, const int subset_num) const; //! Filename with input projection data std::string _input_filename; @@ -121,12 +103,12 @@ class PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion // TODO to be replaced with single class or so (TargetT obviously) //! the output image size in x and y direction /*! convention: if -1, use a size such that the whole FOV is covered - */ + */ int output_image_size_xy; // KT 10122001 appended _xy //! the output image size in z direction /*! convention: if -1, use default as provided by VoxelsOnCartesianGrid constructor - */ + */ int output_image_size_z; // KT 10122001 new //! the zoom factor @@ -143,7 +125,6 @@ class PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion double Zoffset; /********************************/ - //! Stores the projectors that are used for the computations shared_ptr projector_pair_ptr; @@ -153,13 +134,13 @@ class PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion //! name of file in which loglikelihood measurements are stored std::string _additive_projection_data_filename; - // TODO doc + // TODO doc int frame_num; std::string frame_definition_filename; TimeFrameDefinitions frame_defs; - VectorWithOffset > _normalisation_sptrs; + VectorWithOffset> _normalisation_sptrs; - private: +private: virtual void set_defaults(); virtual void initialise_keymap(); virtual bool post_processing(); @@ -169,7 +150,7 @@ class PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion shared_ptr symmetries_sptr; - VectorWithOffset > > _forward_transformations; + VectorWithOffset>> _forward_transformations; }; END_NAMESPACE_STIR diff --git a/src/include/stir_experimental/motion/Polaris_MT_File.h b/src/include/stir_experimental/motion/Polaris_MT_File.h index 1b57460daf..adfbd703b5 100644 --- a/src/include/stir_experimental/motion/Polaris_MT_File.h +++ b/src/include/stir_experimental/motion/Polaris_MT_File.h @@ -25,11 +25,14 @@ #include #include -# ifdef BOOST_NO_STDC_NAMESPACE -namespace std { using ::time_t; using ::tm; using ::localtime; } +#ifdef BOOST_NO_STDC_NAMESPACE +namespace std { +using ::time_t; +using ::tm; +using ::localtime; +} // namespace std #endif - START_NAMESPACE_STIR /*!\ingroup motion @@ -49,7 +52,7 @@ START_NAMESPACE_STIR Tool Number (as a character) Q0, Qx, Qy, Qz Tx, Ty, Tz - RMS Error + RMS Error \endverbatim or something like \verbatim @@ -59,55 +62,54 @@ START_NAMESPACE_STIR \warning All times are in local time, and are hence subject to the settings of your TZ environment variable. This means that if the - data is processed in a different time zone, you will run into + data is processed in a different time zone, you will run into trouble. */ -class Polaris_MT_File -{ +class Polaris_MT_File { public: - struct Record - { - double sample_time; - unsigned int frame_num; - unsigned int rand_num; - char total_num; - Quaternion quat; - CartesianCoordinate3D trans; - float rms ; - unsigned int out_of_FOV; - }; + struct Record { + double sample_time; + unsigned int frame_num; + unsigned int rand_num; + char total_num; + Quaternion quat; + CartesianCoordinate3D trans; + float rms; + unsigned int out_of_FOV; + }; typedef std::vector::const_iterator const_iterator; - ~Polaris_MT_File () {}; - Polaris_MT_File(const std::string& filename); - - //! get the \a n-th complete record - /*! \warning This skips the 'missing data' records*/ - Record operator[](unsigned int n) const; - - //! iterators that go through complete records - const_iterator begin() const { return vector_of_records.begin();} - const_iterator end() const { return vector_of_records.end();} - unsigned long num_samples() const { return vector_of_records.size(); } - - //! iterators that go through all tags recorded by the Polaris - const_iterator begin_all_tags() const { return vector_of_tags.begin();} - const_iterator end_all_tags() const { return vector_of_tags.end();} - unsigned long num_tags() const { return vector_of_tags.size(); } - - //! start of acquisition as would have been returned by std::time() - std::time_t get_start_time_in_secs_since_1970(); + ~Polaris_MT_File(){}; + Polaris_MT_File(const std::string& filename); + + //! get the \a n-th complete record + /*! \warning This skips the 'missing data' records*/ + Record operator[](unsigned int n) const; + + //! iterators that go through complete records + const_iterator begin() const { return vector_of_records.begin(); } + const_iterator end() const { return vector_of_records.end(); } + unsigned long num_samples() const { return vector_of_records.size(); } + + //! iterators that go through all tags recorded by the Polaris + const_iterator begin_all_tags() const { return vector_of_tags.begin(); } + const_iterator end_all_tags() const { return vector_of_tags.end(); } + unsigned long num_tags() const { return vector_of_tags.size(); } + + //! start of acquisition as would have been returned by std::time() + std::time_t get_start_time_in_secs_since_1970(); + private: - std::time_t start_time_in_secs_since_1970; + std::time_t start_time_in_secs_since_1970; - std::vector vector_of_records; - // this contains all tags and times (even those with 'missing data') - std::vector vector_of_tags; + std::vector vector_of_records; + // this contains all tags and times (even those with 'missing data') + std::vector vector_of_tags; - void read_Peter_Bloomfield_mt_file(const std::string& mt_filename, std::istream& mt_stream, const char * const first_line); - void read_NDI_Toolviewer_mt_file(const std::string& mt_filename, std::istream& mt_stream, const char * const first_line); + void read_Peter_Bloomfield_mt_file(const std::string& mt_filename, std::istream& mt_stream, const char* const first_line); + void read_NDI_Toolviewer_mt_file(const std::string& mt_filename, std::istream& mt_stream, const char* const first_line); }; END_NAMESPACE_STIR diff --git a/src/include/stir_experimental/motion/RigidObject3DMotion.h b/src/include/stir_experimental/motion/RigidObject3DMotion.h index 328d83e1b5..bd5dd891d9 100644 --- a/src/include/stir_experimental/motion/RigidObject3DMotion.h +++ b/src/include/stir_experimental/motion/RigidObject3DMotion.h @@ -29,51 +29,42 @@ class AbsTimeInterval; \brief Base class for 3D rigid motion - This is really a class for encoding motion of an object in a scanner. So, there is + This is really a class for encoding motion of an object in a scanner. So, there is some stuff in here to go from tracker coordinates to scanner coordinates etc. Preliminary. Things that need to be worked out: - - time issues. Relative time is supposed to be relative to the scan start, but - this is really dependent on the derived class. It would be far - better to stick to secs_since_1970 in the class hierarchy, and use have a "set_reference_time" + - time issues. Relative time is supposed to be relative to the scan start, but + this is really dependent on the derived class. It would be far + better to stick to secs_since_1970 in the class hierarchy, and use have a "set_reference_time" member here or so. - - synchronisation: this is supposed to synchornise the tracker clock to a master clock. Again, that behaviour + - synchronisation: this is supposed to synchornise the tracker clock to a master clock. Again, that behaviour is completely dependent on what the derived class does. - + */ -class RigidObject3DMotion: public RegisteredObject -{ +class RigidObject3DMotion : public RegisteredObject { public: virtual ~RigidObject3DMotion() {} - //! get motion in tracker coordinates - virtual - RigidObject3DTransformation - get_motion_in_tracker_coords_rel_time(const double time) const =0; + //! get motion in tracker coordinates + virtual RigidObject3DTransformation get_motion_in_tracker_coords_rel_time(const double time) const = 0; //! get motion in scanner coordinates - virtual - RigidObject3DTransformation - get_motion_in_scanner_coords_rel_time(const double time) const; + virtual RigidObject3DTransformation get_motion_in_scanner_coords_rel_time(const double time) const; //! \name Average motion for a time interval //@{ - virtual - RigidObject3DTransformation - compute_average_motion_in_tracker_coords(const AbsTimeInterval&) const; + virtual RigidObject3DTransformation compute_average_motion_in_tracker_coords(const AbsTimeInterval&) const; - virtual - RigidObject3DTransformation - compute_average_motion_in_scanner_coords(const AbsTimeInterval&) const; + virtual RigidObject3DTransformation compute_average_motion_in_scanner_coords(const AbsTimeInterval&) const; - virtual RigidObject3DTransformation - compute_average_motion_in_tracker_coords_rel_time(const double start_time, const double end_time)const = 0; + virtual RigidObject3DTransformation compute_average_motion_in_tracker_coords_rel_time(const double start_time, + const double end_time) const = 0; - virtual RigidObject3DTransformation - compute_average_motion_in_scanner_coords_rel_time(const double start_time, const double end_time)const; + virtual RigidObject3DTransformation compute_average_motion_in_scanner_coords_rel_time(const double start_time, + const double end_time) const; //@} @@ -86,19 +77,16 @@ class RigidObject3DMotion: public RegisteredObject a lot of sense. So, it probably should be moved to a derived class \c SampledRigidObject3DMotion or so. */ - virtual std::vector - get_rel_time_of_samples(const double start_time, const double end_time)const = 0; + virtual std::vector get_rel_time_of_samples(const double start_time, const double end_time) const = 0; //! Has to be called and will be used to synchronise the target-system time and motion tracking time /*! In practice, this should make sure that a 'rel_time' of 0 corresponds to the start of the scan */ - virtual Succeeded synchronise() =0; - + virtual Succeeded synchronise() = 0; virtual double secs_since_1970_to_rel_time(std::time_t) const = 0; - - protected: +protected: #if 0 //! Option to set time offset manually in case synchronisation cannot be performed void @@ -108,21 +96,17 @@ class RigidObject3DMotion: public RegisteredObject #endif //! Temporary (?) function to allow base class to see if synchronised was called or not virtual bool is_synchronised() const = 0; - public: - virtual const RigidObject3DTransformation& - get_transformation_to_scanner_coords() const = 0; - virtual const RigidObject3DTransformation& - get_transformation_from_scanner_coords() const = 0; +public: + virtual const RigidObject3DTransformation& get_transformation_to_scanner_coords() const = 0; + virtual const RigidObject3DTransformation& get_transformation_from_scanner_coords() const = 0; - virtual void - set_transformation_from_scanner_coords(const RigidObject3DTransformation&) = 0; + virtual void set_transformation_from_scanner_coords(const RigidObject3DTransformation&) = 0; protected: virtual void set_defaults(); virtual void initialise_keymap(); virtual bool post_processing(); - }; END_NAMESPACE_STIR diff --git a/src/include/stir_experimental/motion/RigidObject3DMotionFromPolaris.h b/src/include/stir_experimental/motion/RigidObject3DMotionFromPolaris.h index 62506c8036..ac2246f434 100644 --- a/src/include/stir_experimental/motion/RigidObject3DMotionFromPolaris.h +++ b/src/include/stir_experimental/motion/RigidObject3DMotionFromPolaris.h @@ -47,36 +47,27 @@ START_NAMESPACE_STIR \todo move synchronisation out of this class */ -class RigidObject3DMotionFromPolaris: - public - RegisteredParsingObject< RigidObject3DMotionFromPolaris, - RigidObject3DMotion, - RigidObject3DMotion> - +class RigidObject3DMotionFromPolaris + : public RegisteredParsingObject + { public: - //! Name which will be used when parsing a MotionTracking object - static const char * const registered_name; + //! Name which will be used when parsing a MotionTracking object + static const char* const registered_name; //! Convert from Polaris transformation to STIR conventions /* see more info in .cxx file */ - static - RigidObject3DTransformation - make_transformation_from_polaris_data(Polaris_MT_File::Record const& record); - + static RigidObject3DTransformation make_transformation_from_polaris_data(Polaris_MT_File::Record const& record); // only need this to enable LmToProjDataWithMC(const char * const par_filename) function RigidObject3DMotionFromPolaris(); - virtual RigidObject3DTransformation - compute_average_motion_in_tracker_coords_rel_time(const double start_time, const double end_time) const; + virtual RigidObject3DTransformation compute_average_motion_in_tracker_coords_rel_time(const double start_time, + const double end_time) const; - virtual - RigidObject3DTransformation - get_motion_in_tracker_coords_rel_time(const double time) const; + virtual RigidObject3DTransformation get_motion_in_tracker_coords_rel_time(const double time) const; - virtual std::vector - get_rel_time_of_samples(const double start_time, const double end_time) const; + virtual std::vector get_rel_time_of_samples(const double start_time, const double end_time) const; //! set mask to be able to ignore one or more channels in the listmode gating data void set_mask_for_tags(const unsigned int mask_for_tags); @@ -85,12 +76,9 @@ class RigidObject3DMotionFromPolaris: virtual Succeeded synchronise(); virtual double secs_since_1970_to_rel_time(std::time_t) const; - virtual const RigidObject3DTransformation& - get_transformation_to_scanner_coords() const; - virtual const RigidObject3DTransformation& - get_transformation_from_scanner_coords() const; - virtual void - set_transformation_from_scanner_coords(const RigidObject3DTransformation&); + virtual const RigidObject3DTransformation& get_transformation_to_scanner_coords() const; + virtual const RigidObject3DTransformation& get_transformation_from_scanner_coords() const; + virtual void set_transformation_from_scanner_coords(const RigidObject3DTransformation&); Succeeded set_mt_file(const std::string& mt_filename); Succeeded set_list_mode_data_file(const std::string& lm_filename); @@ -100,38 +88,29 @@ class RigidObject3DMotionFromPolaris: //! Gets boundaries to determine when the time offset is out of bounds //*! Currently, the time offset is compared to the start of the listmode scan.*/ - double get_max_time_offset_deviation() const - { return max_time_offset_deviation; } + double get_max_time_offset_deviation() const { return max_time_offset_deviation; } //! Sets boundaries to determine when the time offset is out of bounds - void set_max_time_offset_deviation(const double v) - { max_time_offset_deviation = v; } + void set_max_time_offset_deviation(const double v) { max_time_offset_deviation = v; } //! Gets boundaries to determine when the time drift is too large /*! deviation is measured as fabs(time_drift-1) */ - double get_max_time_drift_deviation() const - { return max_time_drift_deviation; } + double get_max_time_drift_deviation() const { return max_time_drift_deviation; } //! Sets boundaries to determine when the time drift is too large - void set_max_time_drift_deviation(const double v) - { max_time_drift_deviation = v; } - -private: + void set_max_time_drift_deviation(const double v) { max_time_drift_deviation = v; } +private: void do_synchronisation(CListModeData& listmode_data); virtual bool is_synchronised() const; double rel_time_to_polaris_time(const double time) const; double polaris_time_to_rel_time(const double time) const; - RigidObject3DTransformation - compute_average_motion_polaris_time(const double start_time, const double end_time)const; - + RigidObject3DTransformation compute_average_motion_polaris_time(const double start_time, const double end_time) const; shared_ptr mt_file_ptr; - std::string mt_filename; + std::string mt_filename; std::string list_mode_filename; - - - private: +private: //! allow masking out certain bits of the tags in case a cable is not connected unsigned int _mask_for_tags; @@ -153,6 +132,5 @@ class RigidObject3DMotionFromPolaris: std::time_t listmode_data_start_time_in_secs; }; - END_NAMESPACE_STIR #endif diff --git a/src/include/stir_experimental/motion/RigidObject3DTransformation.h b/src/include/stir_experimental/motion/RigidObject3DTransformation.h index c3432a83b9..115a47750e 100644 --- a/src/include/stir_experimental/motion/RigidObject3DTransformation.h +++ b/src/include/stir_experimental/motion/RigidObject3DTransformation.h @@ -16,7 +16,6 @@ #ifndef __stir_motion_RigidObject3DTransformation_H__ #define __stir_motion_RigidObject3DTransformation_H__ - #include "stir_experimental/motion/ObjectTransformation.h" #include "stir/RegisteredParsingObject.h" #include "stir_experimental/Quaternion.h" @@ -35,11 +34,11 @@ class Succeeded; Supported transformations include rotations and translations. Rotations are encoded using quaternions. The convention used is described in
    - B.K. Horn, Closed-form solution of absolute orientation using + B.K. Horn, Closed-form solution of absolute orientation using unit quaternions, J. Opt. Soc. Am. A Vol.4 No. 6, (1987) p.629. - \warning STIR uses a left-handed coordinate-system. + \warning STIR uses a left-handed coordinate-system. The transformation that is applied is as follows \f[ r' = \mathrm{conj}(q)(r-t)q \f] @@ -62,16 +61,11 @@ class Succeeded; \warning The Euler angles are probably different from the ones used in the Shape3D hierarchy. \todo define Euler angles (the code is derived from the Polaris manual) */ -class RigidObject3DTransformation - : - public - RegisteredParsingObject, - ObjectTransformation<3,float> > -{ +class RigidObject3DTransformation : public RegisteredParsingObject, + ObjectTransformation<3, float>> { public: - static const char * const registered_name; - /*! + static const char* const registered_name; + /*! \brief Find the rigid transformation that gives the closest match between 2 sets of points. Minimises the Mean Square Error, i.e. the sum of @@ -80,7 +74,7 @@ class RigidObject3DTransformation \endcode The implementation uses Horn's algorithm. - + Horn's method needs to compute the maximum eigenvector of a matrix, which is done here using the Power method (see max_eigenvector_using_power_method()). @@ -90,35 +84,28 @@ class RigidObject3DTransformation choice would correspond to another eigenvector of the matrix (giving a very bad match). */ template - static - Succeeded - find_closest_transformation(RigidObject3DTransformation& result, - Iter1T start_orig_points, - Iter1T end_orig_points, - Iter2T start_transformed_points, - const Quaternion& initial_rotation = Quaternion(1.F,0.F,0.F,0.F)); + static Succeeded find_closest_transformation(RigidObject3DTransformation& result, Iter1T start_orig_points, + Iter1T end_orig_points, Iter2T start_transformed_points, + const Quaternion& initial_rotation = Quaternion(1.F, 0.F, 0.F, 0.F)); /*! \brief Compute Root Mean Square Error for 2 sets of points */ template - static double - RMSE(const RigidObject3DTransformation& transformation, - Iter1T start_orig_points, - Iter1T end_orig_points, - Iter2T start_transformed_points); + static double RMSE(const RigidObject3DTransformation& transformation, Iter1T start_orig_points, Iter1T end_orig_points, + Iter2T start_transformed_points); - RigidObject3DTransformation (); + RigidObject3DTransformation(); //! Constructor taking quaternion and translation info /*! \see RigidObject3DTransformation class documentation for conventions */ - RigidObject3DTransformation (const Quaternion& quat, const CartesianCoordinate3D& translation); - + RigidObject3DTransformation(const Quaternion& quat, const CartesianCoordinate3D& translation); + //! Compute the inverse transformation RigidObject3DTransformation inverse() const; //! Get quaternion Quaternion get_quaternion() const; - + //! Get translation CartesianCoordinate3D get_translation() const; @@ -130,25 +117,21 @@ class RigidObject3DTransformation Succeeded set_euler_angles(); #endif - //! Transform point + //! Transform point // can't return CartesianCoordinate3D anymore because virtual function - virtual - BasicCoordinate<3,float> - transform_point(const BasicCoordinate<3,float>& point) const; + virtual BasicCoordinate<3, float> transform_point(const BasicCoordinate<3, float>& point) const; //! Computes the jacobian for the transformation (which is always 1) - float jacobian(const BasicCoordinate<3,float>& point) const - { return 1; } + float jacobian(const BasicCoordinate<3, float>& point) const { return 1; } //! Transform bin from some projection data /*! Finds 'closest' (in some sense) bin to the transformed LOR. - if NEW_ROT is not #defined at compilation time, + if NEW_ROT is not #defined at compilation time, it will throw an exception when arc-corrected data is used.*/ - void transform_bin(Bin& bin,const ProjDataInfo& out_proj_data_info, - const ProjDataInfo& in_proj_data_info) const; + void transform_bin(Bin& bin, const ProjDataInfo& out_proj_data_info, const ProjDataInfo& in_proj_data_info) const; //! Get relative transformation (not implemented at present) - void get_relative_transformation(RigidObject3DTransformation& output, const RigidObject3DTransformation& reference); + void get_relative_transformation(RigidObject3DTransformation& output, const RigidObject3DTransformation& reference); #if 0 //! \name conversion to other conventions for rotations /*! \warning Currently disabled Code probably only works when FIRSTROT is defined. @@ -166,24 +149,20 @@ class RigidObject3DTransformation private: Quaternion quat; CartesianCoordinate3D translation; - friend RigidObject3DTransformation compose ( const RigidObject3DTransformation& apply_last, - const RigidObject3DTransformation& apply_first); + friend RigidObject3DTransformation compose(const RigidObject3DTransformation& apply_last, + const RigidObject3DTransformation& apply_first); }; //! Output to (text) stream /*! \ingroup motion Will be written as \verbatim { quaternion, translation } \endverbatim */ -std::ostream& -operator<<(std::ostream& out, - const RigidObject3DTransformation& rigid_object_transformation); +std::ostream& operator<<(std::ostream& out, const RigidObject3DTransformation& rigid_object_transformation); //! Input from (text) stream /*! \ingroup motion Should have format \verbatim { quaternion, translation } \endverbatim */ -std::istream& -operator>>(std::istream& , - RigidObject3DTransformation& rigid_object_transformation); +std::istream& operator>>(std::istream&, RigidObject3DTransformation& rigid_object_transformation); //! Composition of 2 transformations /*! \ingroup motion @@ -199,9 +178,8 @@ operator>>(std::istream& , \endcode */ -RigidObject3DTransformation -compose (const RigidObject3DTransformation& apply_last, - const RigidObject3DTransformation& apply_first); +RigidObject3DTransformation compose(const RigidObject3DTransformation& apply_last, + const RigidObject3DTransformation& apply_first); END_NAMESPACE_STIR diff --git a/src/include/stir_experimental/motion/TimeFrameMotion.h b/src/include/stir_experimental/motion/TimeFrameMotion.h index d233663db8..204a942098 100644 --- a/src/include/stir_experimental/motion/TimeFrameMotion.h +++ b/src/include/stir_experimental/motion/TimeFrameMotion.h @@ -23,7 +23,7 @@ #include "stir/ParsingObject.h" START_NAMESPACE_STIR -/***********************************************************/ +/***********************************************************/ /*! \ingroup motion \brief A class for encoding average motion in the frames. @@ -36,9 +36,9 @@ START_NAMESPACE_STIR ; see stir::TimeFrameDefinitions time frame_definition filename := frame_definition_filename - + ; next parameter is optional (and not normally necessary) - ; it can be used if the frame definitions are relative to another scan as what + ; it can be used if the frame definitions are relative to another scan as what ; is used to for the rigid object motion (i.e. currently the list mode data used ; for the Polaris synchronisation) ; scan_start_time_secs_since_1970_UTC @@ -57,9 +57,8 @@ START_NAMESPACE_STIR END := \endverbatim -*/ -class TimeFrameMotion : public ParsingObject -{ +*/ +class TimeFrameMotion : public ParsingObject { public: /* don't have this constructor, as it would need to be repeated by all derived classes anyway. @@ -74,40 +73,31 @@ class TimeFrameMotion : public ParsingObject int get_frame_num_to_process() const; //! get transformation from (or to) reference for current frame - /*! This is computed using + /*! This is computed using RigidObject3DTransformation::compute_average_motion_in_scanner_coords for the current frame. */ - const RigidObject3DTransformation& - get_current_rigid_object_transformation() const; + const RigidObject3DTransformation& get_current_rigid_object_transformation() const; //! Get the transformation to the reference as returned by the RigidObject3DMotion object - const RigidObject3DTransformation& - get_rigid_object_transformation_to_reference() const - { return _transformation_to_reference_position; } + const RigidObject3DTransformation& get_rigid_object_transformation_to_reference() const { + return _transformation_to_reference_position; + } - const TimeFrameDefinitions& - get_time_frame_defs() const; + const TimeFrameDefinitions& get_time_frame_defs() const; - double get_frame_start_time(unsigned frame_num) const - { return _frame_defs.get_start_time(frame_num) + _scan_start_time; } + double get_frame_start_time(unsigned frame_num) const { return _frame_defs.get_start_time(frame_num) + _scan_start_time; } - double get_frame_end_time(unsigned frame_num) const - { return _frame_defs.get_end_time(frame_num) + _scan_start_time; } - - const RigidObject3DMotion& get_motion() const - { return *_ro3d_sptr; } - -protected: + double get_frame_end_time(unsigned frame_num) const { return _frame_defs.get_end_time(frame_num) + _scan_start_time; } + const RigidObject3DMotion& get_motion() const { return *_ro3d_sptr; } - +protected: //! parsing functions virtual void set_defaults(); virtual void initialise_keymap(); virtual bool post_processing(); - private: std::string _frame_definition_filename; bool _do_move_to_reference; @@ -116,13 +106,13 @@ class TimeFrameMotion : public ParsingObject shared_ptr _ro3d_sptr; shared_ptr _reference_abs_time_sptr; RigidObject3DTransformation _current_rigid_object_transformation; - + RigidObject3DTransformation _transformation_to_reference_position; int _scan_start_time_secs_since_1970_UTC; double _scan_start_time; - int _frame_num_to_process; + int _frame_num_to_process; }; END_NAMESPACE_STIR diff --git a/src/include/stir_experimental/motion/Transform3DObjectImageProcessor.h b/src/include/stir_experimental/motion/Transform3DObjectImageProcessor.h index 759b34936a..4a3d3c5f35 100644 --- a/src/include/stir_experimental/motion/Transform3DObjectImageProcessor.h +++ b/src/include/stir_experimental/motion/Transform3DObjectImageProcessor.h @@ -6,11 +6,11 @@ /*! \file - \ingroup ImageProcessor + \ingroup ImageProcessor \ingroup motion \brief Declaration of class stir::Transform3DObjectImageProcessor \author Kris Thielemans - + */ #ifndef __stir_motion_Transform3DObjectImageProcessor_H__ @@ -23,7 +23,6 @@ // next is currently needed to get Array> to compile (definition of assign() in there) #include "stir_experimental/motion/transform_3d_object.h" - START_NAMESPACE_STIR // TODO!! remove define @@ -31,29 +30,25 @@ START_NAMESPACE_STIR #define num_dimensions 3 /*! - \ingroup ImageProcessor + \ingroup ImageProcessor \brief A class in the ImageProcessor hierarchy that performs movement by reinterpolation - \warning This class is currently restricted to 3d. + \warning This class is currently restricted to 3d. */ template -class Transform3DObjectImageProcessor : - public - RegisteredParsingObject< - Transform3DObjectImageProcessor, - DataProcessor >, - DataProcessor > - > -{ - typedef DataProcessor > base_type; +class Transform3DObjectImageProcessor + : public RegisteredParsingObject, DataProcessor>, + DataProcessor>> { + typedef DataProcessor> base_type; + public: - static const char * const registered_name; - + static const char* const registered_name; + //! Default constructor - //Transform3DObjectImageProcessor(); + // Transform3DObjectImageProcessor(); //! Constructor that set the transformation - explicit - Transform3DObjectImageProcessor(const shared_ptr > = shared_ptr >()); + explicit Transform3DObjectImageProcessor( + const shared_ptr> = shared_ptr>()); bool get_do_transpose() const; void set_do_transpose(const bool); @@ -63,23 +58,23 @@ class Transform3DObjectImageProcessor : void set_do_cache(const bool); private: - //motion - shared_ptr > transformation_sptr; + // motion + shared_ptr> transformation_sptr; bool _do_transpose; - bool _do_jacobian; + bool _do_jacobian; bool _cache_transformed_coords; - Array<3, BasicCoordinate<3,elemT> > _transformed_coords; - Array<3, std::pair, elemT> > _transformed_coords_and_jacobian; + Array<3, BasicCoordinate<3, elemT>> _transformed_coords; + Array<3, std::pair, elemT>> _transformed_coords_and_jacobian; virtual void set_defaults(); virtual void initialise_keymap(); - virtual bool post_processing(); - - Succeeded virtual_set_up(const DiscretisedDensity& image); - void virtual_apply(DiscretisedDensity& out_density, const DiscretisedDensity& in_density) const; - void virtual_apply(DiscretisedDensity& density) const ; - + virtual bool post_processing(); + + Succeeded virtual_set_up(const DiscretisedDensity& image); + void virtual_apply(DiscretisedDensity& out_density, + const DiscretisedDensity& in_density) const; + void virtual_apply(DiscretisedDensity& density) const; }; #undef num_dimensions @@ -87,5 +82,3 @@ class Transform3DObjectImageProcessor : END_NAMESPACE_STIR #endif - - diff --git a/src/include/stir_experimental/motion/bin_interpolate.h b/src/include/stir_experimental/motion/bin_interpolate.h index 3e7a894a01..30f99b3c67 100644 --- a/src/include/stir_experimental/motion/bin_interpolate.h +++ b/src/include/stir_experimental/motion/bin_interpolate.h @@ -25,13 +25,9 @@ START_NAMESPACE_STIR -static -Succeeded -get_transformed_LOR(LORInAxialAndNoArcCorrSinogramCoordinates& out_lor, - const RigidObject3DTransformation& transformation, - const Bin& bin, - const ProjDataInfo& in_proj_data_info) -{ +static Succeeded +get_transformed_LOR(LORInAxialAndNoArcCorrSinogramCoordinates& out_lor, const RigidObject3DTransformation& transformation, + const Bin& bin, const ProjDataInfo& in_proj_data_info) { LORInAxialAndNoArcCorrSinogramCoordinates lor; in_proj_data_info.get_LOR(lor, bin); LORAs2Points lor_as_points; @@ -39,40 +35,34 @@ get_transformed_LOR(LORInAxialAndNoArcCorrSinogramCoordinates& out_lor, // TODO origin // currently, the origin used for proj_data_info is in the centre of the scanner, // while for standard images it is in the centre of the first ring. - // This is pretty horrible though, as the transform_point function has no clue + // This is pretty horrible though, as the transform_point function has no clue // where the origin is - // Note that the present shift will make this version compatible with the + // Note that the present shift will make this version compatible with the // version above, as find_bin_given_cartesian_coordinates_of_detection // also uses an origin in the centre of the first ring - const float z_shift = - (in_proj_data_info.get_scanner_ptr()->get_num_rings()-1)/2.F * - in_proj_data_info.get_scanner_ptr()->get_ring_spacing(); + const float z_shift = + (in_proj_data_info.get_scanner_ptr()->get_num_rings() - 1) / 2.F * in_proj_data_info.get_scanner_ptr()->get_ring_spacing(); lor_as_points.p1().z() += z_shift; lor_as_points.p2().z() += z_shift; - LORAs2Points - transformed_lor_as_points(transformation.transform_point(lor_as_points.p1()), - transformation.transform_point(lor_as_points.p2())); + LORAs2Points transformed_lor_as_points(transformation.transform_point(lor_as_points.p1()), + transformation.transform_point(lor_as_points.p2())); transformed_lor_as_points.p1().z() -= z_shift; transformed_lor_as_points.p2().z() -= z_shift; return transformed_lor_as_points.change_representation(out_lor, lor.radius()); } -static -Coordinate4D -lor_to_coords(const LORInAxialAndNoArcCorrSinogramCoordinates& lor) -{ +static Coordinate4D +lor_to_coords(const LORInAxialAndNoArcCorrSinogramCoordinates& lor) { Coordinate4D coord; - coord[1] = lor.z2()-lor.z1(); - coord[2] = (lor.z2()+lor.z1())/2; + coord[1] = lor.z2() - lor.z1(); + coord[2] = (lor.z2() + lor.z1()) / 2; coord[3] = lor.phi(); coord[4] = lor.beta(); return coord; } -static -Coordinate4D -bin_to_coords(const Bin& bin) -{ +static Coordinate4D +bin_to_coords(const Bin& bin) { Coordinate4D coord; coord[1] = bin.segment_num(); coord[2] = bin.axial_pos_num(); @@ -81,11 +71,8 @@ bin_to_coords(const Bin& bin) return coord; } - -static -Bin -coords_to_bin(const Coordinate4D& coord) -{ +static Bin +coords_to_bin(const Coordinate4D& coord) { Bin bin; bin.segment_num() = coord[1]; bin.axial_pos_num() = coord[2]; @@ -94,127 +81,102 @@ coords_to_bin(const Coordinate4D& coord) return bin; } -inline int sign(float x) -{ return x>=0 ? 1 : -1; } +inline int +sign(float x) { + return x >= 0 ? 1 : -1; +} -inline -Coordinate4D sign (const Coordinate4D& c) -{ - return Coordinate4D (sign(c[1]),sign(c[2]),sign(c[3]),sign(c[4])); +inline Coordinate4D +sign(const Coordinate4D& c) { + return Coordinate4D(sign(c[1]), sign(c[2]), sign(c[3]), sign(c[4])); } -inline -Coordinate4D abs (const Coordinate4D& c) -{ - return Coordinate4D (fabs(c[1]),fabs(c[2]),fabs(c[3]),fabs(c[4])); +inline Coordinate4D +abs(const Coordinate4D& c) { + return Coordinate4D(fabs(c[1]), fabs(c[2]), fabs(c[3]), fabs(c[4])); } -inline -void add_to_bin(VectorWithOffset > > & segments, - const Bin& bin, - const float value) -{ - (*segments[bin.segment_num()])[bin.view_num()][bin.axial_pos_num()][bin.tangential_pos_num()] += - value; +inline void +add_to_bin(VectorWithOffset>>& segments, const Bin& bin, const float value) { + (*segments[bin.segment_num()])[bin.view_num()][bin.axial_pos_num()][bin.tangential_pos_num()] += value; } -static -bool -is_in_range(const Bin& bin, const ProjDataInfo& proj_data_info) -{ - if (bin.segment_num()>= proj_data_info.get_min_segment_num() - && bin.segment_num()<= proj_data_info.get_max_segment_num() - && bin.tangential_pos_num()>= proj_data_info.get_min_tangential_pos_num() - && bin.tangential_pos_num()<= proj_data_info.get_max_tangential_pos_num() - && bin.axial_pos_num()>=proj_data_info.get_min_axial_pos_num(bin.segment_num()) - && bin.axial_pos_num()<=proj_data_info.get_max_axial_pos_num(bin.segment_num()) - ) - { - assert(bin.view_num()>=proj_data_info.get_min_view_num()); - assert(bin.view_num()<=proj_data_info.get_max_view_num()); - return true; - } - else +static bool +is_in_range(const Bin& bin, const ProjDataInfo& proj_data_info) { + if (bin.segment_num() >= proj_data_info.get_min_segment_num() && bin.segment_num() <= proj_data_info.get_max_segment_num() && + bin.tangential_pos_num() >= proj_data_info.get_min_tangential_pos_num() && + bin.tangential_pos_num() <= proj_data_info.get_max_tangential_pos_num() && + bin.axial_pos_num() >= proj_data_info.get_min_axial_pos_num(bin.segment_num()) && + bin.axial_pos_num() <= proj_data_info.get_max_axial_pos_num(bin.segment_num())) { + assert(bin.view_num() >= proj_data_info.get_min_view_num()); + assert(bin.view_num() <= proj_data_info.get_max_view_num()); + return true; + } else return false; } -static -Bin -standardise(const Bin& in_bin, const ProjDataInfo& proj_data_info) -{ +static Bin +standardise(const Bin& in_bin, const ProjDataInfo& proj_data_info) { Bin bin = in_bin; bool swap_direction = false; - if (bin.view_num()< proj_data_info.get_min_view_num()) - { - swap_direction = true; - bin.view_num()+=proj_data_info.get_num_views(); - } - else if (bin.view_num() > proj_data_info.get_max_view_num()) - { - swap_direction = true; - bin.view_num()-=proj_data_info.get_num_views(); - } - assert(bin.view_num()>=proj_data_info.get_min_view_num()); - assert(bin.view_num()<=proj_data_info.get_max_view_num()); - - if (swap_direction) - { + if (bin.view_num() < proj_data_info.get_min_view_num()) { + swap_direction = true; + bin.view_num() += proj_data_info.get_num_views(); + } else if (bin.view_num() > proj_data_info.get_max_view_num()) { + swap_direction = true; + bin.view_num() -= proj_data_info.get_num_views(); + } + assert(bin.view_num() >= proj_data_info.get_min_view_num()); + assert(bin.view_num() <= proj_data_info.get_max_view_num()); + + if (swap_direction) { bin.tangential_pos_num() *= -1; bin.segment_num() *= -1; - } + } return bin; } - -static -Succeeded -find_sampling(Coordinate4D& sampling, const Bin& bin, const ProjDataInfo& proj_data_info) -{ +static Succeeded +find_sampling(Coordinate4D& sampling, const Bin& bin, const ProjDataInfo& proj_data_info) { LORInAxialAndNoArcCorrSinogramCoordinates tmp_lor; LORInAxialAndNoArcCorrSinogramCoordinates lor; proj_data_info.get_LOR(lor, bin); - for (int d=1; d<=4; ++d) - { - Coordinate4D neighbour_coords = bin_to_coords(bin); - neighbour_coords[d]+=1; - const Bin neighbour = standardise(coords_to_bin(neighbour_coords), - proj_data_info); - if (!is_in_range(neighbour, proj_data_info)) - return Succeeded::no; - proj_data_info.get_LOR(tmp_lor, neighbour); - sampling[d] = (lor_to_coords(tmp_lor) - lor_to_coords(lor))[d]; - } + for (int d = 1; d <= 4; ++d) { + Coordinate4D neighbour_coords = bin_to_coords(bin); + neighbour_coords[d] += 1; + const Bin neighbour = standardise(coords_to_bin(neighbour_coords), proj_data_info); + if (!is_in_range(neighbour, proj_data_info)) + return Succeeded::no; + proj_data_info.get_LOR(tmp_lor, neighbour); + sampling[d] = (lor_to_coords(tmp_lor) - lor_to_coords(lor))[d]; + } return Succeeded::yes; } -static -void -bin_interpolate(VectorWithOffset > > & seg_ptr, - const LORInAxialAndNoArcCorrSinogramCoordinates& lor, - const ProjDataInfo& out_proj_data_info, - const ProjDataInfo& proj_data_info, - const float value) -{ +static void +bin_interpolate(VectorWithOffset>>& seg_ptr, + const LORInAxialAndNoArcCorrSinogramCoordinates& lor, const ProjDataInfo& out_proj_data_info, + const ProjDataInfo& proj_data_info, const float value) { Coordinate4D sampling; - //warning assuming uniform sampling to avoid problem of neighbour dropping of at the edge - if (find_sampling(sampling, Bin(0,0,0,0), proj_data_info)!= Succeeded::yes) + // warning assuming uniform sampling to avoid problem of neighbour dropping of at the edge + if (find_sampling(sampling, Bin(0, 0, 0, 0), proj_data_info) != Succeeded::yes) error("error in finding sampling"); const Bin central_bin = proj_data_info.get_bin(lor); - if (central_bin.get_bin_value()<0) + if (central_bin.get_bin_value() < 0) return; LORInAxialAndNoArcCorrSinogramCoordinates central_lor; proj_data_info.get_LOR(central_lor, central_bin); const Coordinate4D central_lor_coords = lor_to_coords(central_lor); const Coordinate4D lor_coords = lor_to_coords(lor); const Coordinate4D central_bin_coords = bin_to_coords(central_bin); - const Coordinate4D diff = (lor_coords - central_lor_coords)/ sampling; - assert(diff[1]<=1.001 && diff[1]>=-1.001); - assert(diff[2]<=.5001 && diff[2]>=-.5001); - assert(diff[3]<=.5001 && diff[3]>=-.5001); - assert(diff[4]<=.5001 && diff[4]>=-.5001); + const Coordinate4D diff = (lor_coords - central_lor_coords) / sampling; + assert(diff[1] <= 1.001 && diff[1] >= -1.001); + assert(diff[2] <= .5001 && diff[2] >= -.5001); + assert(diff[3] <= .5001 && diff[3] >= -.5001); + assert(diff[4] <= .5001 && diff[4] >= -.5001); const Coordinate4D bin_inc = sign(sampling); - assert(bin_inc == Coordinate4D(1,1,1,1)); + assert(bin_inc == Coordinate4D(1, 1, 1, 1)); const Coordinate4D inc = sign(diff); Coordinate4D offset; @@ -222,74 +184,67 @@ bin_interpolate(VectorWithOffset > > & seg_ptr, for (offset[1]=0; offset[1]!=2*inc[1]; offset[1]+=inc[1]) for (offset[2]=0; offset[2]!=2*inc[2]; offset[2]+=inc[2]) for (offset[3]=0; offset[3]!=2*inc[3]; offset[3]+=inc[3]) - for (offset[4]=0; offset[4]!=2*inc[4]; offset[4]+=inc[4]) + for (offset[4]=0; offset[4]!=2*inc[4]; offset[4]+=inc[4]) #else - for (offset[1]=-1; offset[1]!=2; offset[1]++) - for (offset[2]=-1; offset[2]!=2; offset[2]++) - for (offset[3]=-1; offset[3]!=2; offset[3]++) - for (offset[4]=-1; offset[4]!=2; offset[4]++) + for (offset[1] = -1; offset[1] != 2; offset[1]++) + for (offset[2] = -1; offset[2] != 2; offset[2]++) + for (offset[3] = -1; offset[3] != 2; offset[3]++) + for (offset[4] = -1; offset[4] != 2; offset[4]++) #endif - { - const Bin target_bin = - standardise(coords_to_bin(central_bin_coords + offset), proj_data_info); - if (is_in_range(target_bin, proj_data_info)) - { - - //const BasicCoordinate<4,float> float_offset(offset); - //Coordinate4D weights = abs(diff - float_offset); - + { + const Bin target_bin = standardise(coords_to_bin(central_bin_coords + offset), proj_data_info); + if (is_in_range(target_bin, proj_data_info)) { + + // const BasicCoordinate<4,float> float_offset(offset); + // Coordinate4D weights = abs(diff - float_offset); + + LORInAxialAndNoArcCorrSinogramCoordinates new_lor; + proj_data_info.get_LOR(new_lor, target_bin); + Coordinate4D new_lor_coords = lor_to_coords(new_lor); + Coordinate4D new_diff = (lor_coords - new_lor_coords) / sampling; + if (fabs(new_diff[3]) > 2) { + new_lor_coords[1] *= -1; + new_lor_coords[3] += _PI * sign(new_diff[3]); + new_lor_coords[4] *= -1; + new_diff = (lor_coords - new_lor_coords) / sampling; + } - LORInAxialAndNoArcCorrSinogramCoordinates new_lor; - proj_data_info.get_LOR(new_lor, target_bin); - Coordinate4D new_lor_coords = lor_to_coords(new_lor); - Coordinate4D new_diff = (lor_coords - new_lor_coords)/ sampling; - if (fabs(new_diff[3])>2) - { - new_lor_coords[1] *= -1; - new_lor_coords[3] += _PI*sign(new_diff[3]); - new_lor_coords[4]*=-1; - new_diff = (lor_coords - new_lor_coords)/ sampling; - } - #if 0 assert(new_diff[1]<=1.001 && new_diff[1]>=-1.001); assert(new_diff[2]<=1.101 && new_diff[2]>=-1.101); assert(new_diff[3]<=1.001 && new_diff[3]>=-1.001); assert(new_diff[4]<=1.001 && new_diff[4]>=-1.001); #endif - // assert(norm(new_diff - (diff - float_offset))<.001); - Coordinate4D weights = abs(new_diff); - if (weights[4]>1 || weights[3]>1 || weights[2]>1 || weights[1]>1) - continue; - const float weight = (1-weights[1])*(1-weights[2])*(1-weights[3])*(1-weights[4]); - if (weight<0.001) - continue; + // assert(norm(new_diff - (diff - float_offset))<.001); + Coordinate4D weights = abs(new_diff); + if (weights[4] > 1 || weights[3] > 1 || weights[2] > 1 || weights[1] > 1) + continue; + const float weight = (1 - weights[1]) * (1 - weights[2]) * (1 - weights[3]) * (1 - weights[4]); + if (weight < 0.001) + continue; #if 0 const Bin out_bin = target_bin; #else -#if 0 +# if 0 const Bin out_bin = out_proj_data_info.get_bin(new_lor); if (out_bin.get_bin_value()<0) continue; -#else - DetectionPositionPair<> detection_positions; - dynamic_cast - (proj_data_info). - get_det_pos_pair_for_bin(detection_positions, target_bin); - Bin out_bin; - if (dynamic_cast - (out_proj_data_info). - get_bin_for_det_pos_pair(out_bin, detection_positions) == Succeeded::no) - continue; - if (!is_in_range(out_bin, out_proj_data_info)) - continue; -#endif +# else + DetectionPositionPair<> detection_positions; + dynamic_cast(proj_data_info) + .get_det_pos_pair_for_bin(detection_positions, target_bin); + Bin out_bin; + if (dynamic_cast(out_proj_data_info) + .get_bin_for_det_pos_pair(out_bin, detection_positions) == Succeeded::no) + continue; + if (!is_in_range(out_bin, out_proj_data_info)) + continue; +# endif #endif - add_to_bin(seg_ptr, out_bin, value*weight); - } - - } + add_to_bin(seg_ptr, out_bin, value * weight); + } + } } END_NAMESPACE_STIR diff --git a/src/include/stir_experimental/motion/transform_3d_object.h b/src/include/stir_experimental/motion/transform_3d_object.h index ddf2ad78b8..2328b1ddc9 100644 --- a/src/include/stir_experimental/motion/transform_3d_object.h +++ b/src/include/stir_experimental/motion/transform_3d_object.h @@ -10,7 +10,7 @@ \file \ingroup motion - \brief Declaration of functions to re-interpolate an image or projection data + \brief Declaration of functions to re-interpolate an image or projection data to a new coordinate system. \author Kris Thielemans @@ -29,14 +29,13 @@ START_NAMESPACE_STIR class RigidObject3DTransformation; template - class ObjectTransformation; +class ObjectTransformation; -template - class DiscretisedDensity; +template +class DiscretisedDensity; class ProjData; class Succeeded; - //! transform image data /*! \ingroup motion \brief the interpolator has to push values from the input into the output image @@ -48,13 +47,10 @@ class Succeeded; This function is inline to avoid template instantiation problems. */ template -inline -Succeeded -transform_3d_object_push_interpolation(DiscretisedDensity<3,float>& out_density, - const DiscretisedDensity<3,float>& in_density, - const ObjectTransformationT& transformation_in_to_out, - const PushInterpolatorT& interpolator, - const bool do_jacobian); +inline Succeeded transform_3d_object_push_interpolation(DiscretisedDensity<3, float>& out_density, + const DiscretisedDensity<3, float>& in_density, + const ObjectTransformationT& transformation_in_to_out, + const PushInterpolatorT& interpolator, const bool do_jacobian); //! transform image data /*! \ingroup motion @@ -67,13 +63,10 @@ transform_3d_object_push_interpolation(DiscretisedDensity<3,float>& out_density, This function is inline to avoid template instantiation problems. */ template -inline -Succeeded -transform_3d_object_pull_interpolation(DiscretisedDensity<3,float>& out_density, - const DiscretisedDensity<3,float>& in_density, - const ObjectTransformationT& transformation_out_to_in, - const PullInterpolatorT& interpolator, - const bool do_jacobian); +inline Succeeded transform_3d_object_pull_interpolation(DiscretisedDensity<3, float>& out_density, + const DiscretisedDensity<3, float>& in_density, + const ObjectTransformationT& transformation_out_to_in, + const PullInterpolatorT& interpolator, const bool do_jacobian); //! transform image data /*! \ingroup motion @@ -82,69 +75,59 @@ transform_3d_object_pull_interpolation(DiscretisedDensity<3,float>& out_density, \todo cannot use ObjectTransformation yet as it needs the inverse transformation */ -Succeeded -transform_3d_object(DiscretisedDensity<3,float>& out_density, - const DiscretisedDensity<3,float>& in_density, - const RigidObject3DTransformation& transformation_in_to_out); - // const ObjectTransformation<3,float>& transformation_in_to_out); - +Succeeded transform_3d_object(DiscretisedDensity<3, float>& out_density, const DiscretisedDensity<3, float>& in_density, + const RigidObject3DTransformation& transformation_in_to_out); +// const ObjectTransformation<3,float>& transformation_in_to_out); // ugly functions for storing transformed points. // TODO clean up at some point -Array<3, BasicCoordinate<3,float> > -find_grid_coords_of_transformed_centres(const DiscretisedDensity<3,float>& source_density, - const DiscretisedDensity<3,float>& target_density, - const ObjectTransformation<3,float>& transformation_source_to_target); +Array<3, BasicCoordinate<3, float>> +find_grid_coords_of_transformed_centres(const DiscretisedDensity<3, float>& source_density, + const DiscretisedDensity<3, float>& target_density, + const ObjectTransformation<3, float>& transformation_source_to_target); // TODO need this for now to get Array> to work template - inline - void assign(std::pair, float>& x, T2 y) -{ - BasicCoordinate<3,float> tmp; - assign(tmp,y); - x = std::make_pair(tmp,float(y)); +inline void +assign(std::pair, float>& x, T2 y) { + BasicCoordinate<3, float> tmp; + assign(tmp, y); + x = std::make_pair(tmp, float(y)); } -Array<3, std::pair, float> > -find_grid_coords_of_transformed_centres_and_jacobian(const DiscretisedDensity<3,float>& source_density, - const DiscretisedDensity<3,float>& target_density, - const ObjectTransformation<3,float>& transformation_source_to_target); +Array<3, std::pair, float>> +find_grid_coords_of_transformed_centres_and_jacobian(const DiscretisedDensity<3, float>& source_density, + const DiscretisedDensity<3, float>& target_density, + const ObjectTransformation<3, float>& transformation_source_to_target); //! transform projection data /*! \ingroup motion Currently only supports non-arccorrected data. - + Uses all available input segments */ -Succeeded -transform_3d_object(ProjData& out_proj_data, - const ProjData& in_proj_data, - const RigidObject3DTransformation& object_transformation); +Succeeded transform_3d_object(ProjData& out_proj_data, const ProjData& in_proj_data, + const RigidObject3DTransformation& object_transformation); //! transform image data using transposed matrix /*! \ingroup motion - Implements the transpose (not the inverse) of + Implements the transpose (not the inverse) of transform_3d_object(DiscretisedDensity<3,float>&, const DiscretisedDensity<3,float>&,const RigidObject3DTransformation&) \todo cannot use ObjectTransformation yet as it needs the inverse transformation */ -Succeeded -transpose_of_transform_3d_object(DiscretisedDensity<3,float>& out_density, - const DiscretisedDensity<3,float>& in_density, - const RigidObject3DTransformation& transformation_in_to_out); +Succeeded transpose_of_transform_3d_object(DiscretisedDensity<3, float>& out_density, + const DiscretisedDensity<3, float>& in_density, + const RigidObject3DTransformation& transformation_in_to_out); // const ObjectTransformation<3,float>& transformation_in_to_out); //! transform projection data /*! \ingroup motion Currently only supports non-arccorrected data. */ -Succeeded -transform_3d_object(ProjData& out_proj_data, - const ProjData& in_proj_data, - const RigidObject3DTransformation& rigid_object_transformation, - const int min_in_segment_num_to_process, - const int max_in_segment_num_to_process); +Succeeded transform_3d_object(ProjData& out_proj_data, const ProjData& in_proj_data, + const RigidObject3DTransformation& rigid_object_transformation, + const int min_in_segment_num_to_process, const int max_in_segment_num_to_process); END_NAMESPACE_STIR diff --git a/src/include/stir_experimental/motion/transform_3d_object.inl b/src/include/stir_experimental/motion/transform_3d_object.inl index ddc0848bc6..ff66968329 100644 --- a/src/include/stir_experimental/motion/transform_3d_object.inl +++ b/src/include/stir_experimental/motion/transform_3d_object.inl @@ -7,7 +7,7 @@ /*! \file \ingroup motion - \brief Functions to re-interpolate an image + \brief Functions to re-interpolate an image \author Kris Thielemans @@ -19,80 +19,57 @@ START_NAMESPACE_STIR template -Succeeded -transform_3d_object_push_interpolation(DiscretisedDensity<3,float>& out_density, - const DiscretisedDensity<3,float>& in_density, - const ObjectTransformationT& transformation_in_to_out, - const PushInterpolatorT& interpolator, - const bool do_jacobian) -{ +Succeeded +transform_3d_object_push_interpolation(DiscretisedDensity<3, float>& out_density, const DiscretisedDensity<3, float>& in_density, + const ObjectTransformationT& transformation_in_to_out, + const PushInterpolatorT& interpolator, const bool do_jacobian) { - const VoxelsOnCartesianGrid& in_image = - dynamic_cast const&>(in_density); - VoxelsOnCartesianGrid& out_image = - dynamic_cast&>(out_density); + const VoxelsOnCartesianGrid& in_image = dynamic_cast const&>(in_density); + VoxelsOnCartesianGrid& out_image = dynamic_cast&>(out_density); interpolator.set_output(out_image); - for (int z= in_image.get_min_index(); z<= in_image.get_max_index(); ++z) - for (int y= in_image[z].get_min_index(); y<= in_image[z].get_max_index(); ++y) - for (int x= in_image[z][y].get_min_index(); x<= in_image[z][y].get_max_index(); ++x) - { + for (int z = in_image.get_min_index(); z <= in_image.get_max_index(); ++z) + for (int y = in_image[z].get_min_index(); y <= in_image[z].get_max_index(); ++y) + for (int x = in_image[z][y].get_min_index(); x <= in_image[z][y].get_max_index(); ++x) { const CartesianCoordinate3D current_point = - CartesianCoordinate3D(z,y,x) * in_image.get_voxel_size() + - in_image.get_origin(); - const CartesianCoordinate3D new_point = - transformation_in_to_out.transform_point(current_point); + CartesianCoordinate3D(z, y, x) * in_image.get_voxel_size() + in_image.get_origin(); + const CartesianCoordinate3D new_point = transformation_in_to_out.transform_point(current_point); const CartesianCoordinate3D new_point_in_image_coords = - (new_point - out_image.get_origin()) / out_image.get_voxel_size(); - const float jacobian = - do_jacobian - ? transformation_in_to_out.jacobian(current_point) - : 1; - interpolator.add_to(new_point_in_image_coords, in_image[z][y][x]*jacobian); + (new_point - out_image.get_origin()) / out_image.get_voxel_size(); + const float jacobian = do_jacobian ? transformation_in_to_out.jacobian(current_point) : 1; + interpolator.add_to(new_point_in_image_coords, in_image[z][y][x] * jacobian); } return Succeeded::yes; } template -Succeeded -transform_3d_object_pull_interpolation(DiscretisedDensity<3,float>& out_density, - const DiscretisedDensity<3,float>& in_density, - const ObjectTransformationT& transformation_out_to_in, - const PullInterpolatorT& interpolator, - const bool do_jacobian) -{ +Succeeded +transform_3d_object_pull_interpolation(DiscretisedDensity<3, float>& out_density, const DiscretisedDensity<3, float>& in_density, + const ObjectTransformationT& transformation_out_to_in, + const PullInterpolatorT& interpolator, const bool do_jacobian) { - const VoxelsOnCartesianGrid& in_image = - dynamic_cast const&>(in_density); - VoxelsOnCartesianGrid& out_image = - dynamic_cast&>(out_density); + const VoxelsOnCartesianGrid& in_image = dynamic_cast const&>(in_density); + VoxelsOnCartesianGrid& out_image = dynamic_cast&>(out_density); interpolator.set_input(in_density); - for (int z= out_image.get_min_index(); z<= out_image.get_max_index(); ++z) - for (int y= out_image[z].get_min_index(); y<= out_image[z].get_max_index(); ++y) - for (int x= out_image[z][y].get_min_index(); x<= out_image[z][y].get_max_index(); ++x) - { + for (int z = out_image.get_min_index(); z <= out_image.get_max_index(); ++z) + for (int y = out_image[z].get_min_index(); y <= out_image[z].get_max_index(); ++y) + for (int x = out_image[z][y].get_min_index(); x <= out_image[z][y].get_max_index(); ++x) { const CartesianCoordinate3D current_point = - CartesianCoordinate3D(static_cast(z), - static_cast(y), - static_cast(x)) * - out_image.get_voxel_size() + - out_image.get_origin(); - const CartesianCoordinate3D new_point = - transformation_out_to_in.transform_point(current_point); + CartesianCoordinate3D(static_cast(z), static_cast(y), static_cast(x)) * + out_image.get_voxel_size() + + out_image.get_origin(); + const CartesianCoordinate3D new_point = transformation_out_to_in.transform_point(current_point); const CartesianCoordinate3D new_point_in_image_coords = - (new_point - in_image.get_origin()) / in_image.get_voxel_size(); - out_image[z][y][x] = - interpolator(new_point_in_image_coords); - if (do_jacobian) - out_image[z][y][x] *= transformation_out_to_in.jacobian(current_point); - + (new_point - in_image.get_origin()) / in_image.get_voxel_size(); + out_image[z][y][x] = interpolator(new_point_in_image_coords); + if (do_jacobian) + out_image[z][y][x] *= transformation_out_to_in.jacobian(current_point); } return Succeeded::yes; } - END_NAMESPACE_STIR diff --git a/src/include/stir_experimental/multiply_plane_scale_factorsImageProcessor.h b/src/include/stir_experimental/multiply_plane_scale_factorsImageProcessor.h index fbe5c26a2a..91cf70152b 100644 --- a/src/include/stir_experimental/multiply_plane_scale_factorsImageProcessor.h +++ b/src/include/stir_experimental/multiply_plane_scale_factorsImageProcessor.h @@ -3,11 +3,11 @@ /*! \file - \ingroup ImageProcessor + \ingroup ImageProcessor \brief Declaration of class multiply_plane_scale_factorsImageProcessor - + \author Kris Thielemans - + */ /* Copyright (C) 2003- 2007, Hammersmith Imanet @@ -17,7 +17,6 @@ #ifndef __stir_multiply_plane_scale_factorsImageProcessor_H__ #define __stir_multiply_plane_scale_factorsImageProcessor_H__ - #include "stir/RegisteredParsingObject.h" #include "stir/DataProcessor.h" #include "stir/DiscretisedDensity.h" @@ -25,51 +24,40 @@ START_NAMESPACE_STIR -template class VectorWithOffset; +template +class VectorWithOffset; /*! \brief Simply multiplies each plane in an image with a scale factor. */ template -class multiply_plane_scale_factorsImageProcessor : - public - RegisteredParsingObject< - multiply_plane_scale_factorsImageProcessor, - DataProcessor >, - DataProcessor > - > -{ +class multiply_plane_scale_factorsImageProcessor + : public RegisteredParsingObject, + DataProcessor>, DataProcessor>> { private: - typedef - RegisteredParsingObject< - multiply_plane_scale_factorsImageProcessor, - DataProcessor >, - DataProcessor > - > - base_type; + typedef RegisteredParsingObject, DataProcessor>, + DataProcessor>> + base_type; + public: - static const char * const registered_name; + static const char* const registered_name; multiply_plane_scale_factorsImageProcessor(); - multiply_plane_scale_factorsImageProcessor(const VectorWithOffset& plane_scale_factors); - multiply_plane_scale_factorsImageProcessor(const std::vector& plane_scale_factors); - - + multiply_plane_scale_factorsImageProcessor(const VectorWithOffset& plane_scale_factors); + multiply_plane_scale_factorsImageProcessor(const std::vector& plane_scale_factors); + private: - std::vector plane_scale_factors; + std::vector plane_scale_factors; virtual void set_defaults(); virtual void initialise_keymap(); - - Succeeded virtual_set_up(const DiscretisedDensity<3,elemT>& image); - void virtual_apply(DiscretisedDensity<3,elemT>& out_density, const DiscretisedDensity<3,elemT>& in_density) const; - void virtual_apply(DiscretisedDensity<3,elemT>& density) const ; - + Succeeded virtual_set_up(const DiscretisedDensity<3, elemT>& image); + + void virtual_apply(DiscretisedDensity<3, elemT>& out_density, const DiscretisedDensity<3, elemT>& in_density) const; + void virtual_apply(DiscretisedDensity<3, elemT>& density) const; }; END_NAMESPACE_STIR #endif - - diff --git a/src/include/stir_experimental/numerics/linear_extrapolation.h b/src/include/stir_experimental/numerics/linear_extrapolation.h index 17b1ddcead..78bfa118d8 100644 --- a/src/include/stir_experimental/numerics/linear_extrapolation.h +++ b/src/include/stir_experimental/numerics/linear_extrapolation.h @@ -17,7 +17,7 @@ See STIR/LICENSE.txt for details */ /*! - \file + \file \ingroup numerics \brief stir::linear_extrapolation @@ -25,17 +25,14 @@ */ - #include "stir/common.h" START_NAMESPACE_STIR - - template - inline void - linear_extrapolation(std::vector &input_vector) - { - input_vector.push_back(*(input_vector.end()-1)*2 - *(input_vector.end()-2)); - input_vector.insert(input_vector.begin(), *input_vector.begin()*2 - *(input_vector.begin()+1)); - } +template +inline void +linear_extrapolation(std::vector& input_vector) { + input_vector.push_back(*(input_vector.end() - 1) * 2 - *(input_vector.end() - 2)); + input_vector.insert(input_vector.begin(), *input_vector.begin() * 2 - *(input_vector.begin() + 1)); +} END_NAMESPACE_STIR diff --git a/src/include/stir_experimental/numerics/more_interpolators.h b/src/include/stir_experimental/numerics/more_interpolators.h index 9be43b2001..30e2801ef2 100644 --- a/src/include/stir_experimental/numerics/more_interpolators.h +++ b/src/include/stir_experimental/numerics/more_interpolators.h @@ -19,7 +19,6 @@ #include "stir/BasicCoordinate.h" #include "stir/Array.h" - START_NAMESPACE_STIR /*! \ingroup numerics @@ -28,9 +27,7 @@ START_NAMESPACE_STIR Adds \a value to the grid point nearest to \a point_in_output_coords */ template -elemT -pull_nearest_neighbour_interpolate(const Array<3, elemT>& in, - const BasicCoordinate<3, positionT>& point_in_input_coords); +elemT pull_nearest_neighbour_interpolate(const Array<3, elemT>& in, const BasicCoordinate<3, positionT>& point_in_input_coords); /*! \ingroup numerics \brief Push \a value into the output array using nearest neigbour interpolation. @@ -38,28 +35,21 @@ pull_nearest_neighbour_interpolate(const Array<3, elemT>& in, Adds \a value to the grid point nearest to \a point_in_output_coords */ template -void -push_nearest_neighbour_interpolate(Array& out, - const BasicCoordinate& point_in_output_coords, - valueT value); - +void push_nearest_neighbour_interpolate(Array& out, + const BasicCoordinate& point_in_output_coords, valueT value); /*! \ingroup numerics \brief Returns an interpolated value according to \a point_in_input_coords. */ template -elemT -pull_linear_interpolate(const Array<3, elemT>& in, - const BasicCoordinate<3, positionT>& point_in_input_coords); +elemT pull_linear_interpolate(const Array<3, elemT>& in, const BasicCoordinate<3, positionT>& point_in_input_coords); /*! \ingroup numerics \brief Push \a value into the output array using the transpose of linear interpolation. */ template -void -push_transpose_linear_interpolate(Array<3, elemT>& out, - const BasicCoordinate<3, positionT>& point_in_output_coords, - valueT value); +void push_transpose_linear_interpolate(Array<3, elemT>& out, const BasicCoordinate<3, positionT>& point_in_output_coords, + valueT value); /*! \ingroup numerics \brief A function object to pull interpolated values from the input array into the grid points of the output array. @@ -67,68 +57,48 @@ push_transpose_linear_interpolate(Array<3, elemT>& out, \todo preliminary. We might want to derive this from a class or so. */ template -class -PullLinearInterpolator -{ +class PullLinearInterpolator { public: - PullLinearInterpolator() - : _input_ptr(0) - {} + PullLinearInterpolator() : _input_ptr(0) {} - void set_input(const Array<3, elemT>& input) const - { - this->_input_ptr = &input; - } + void set_input(const Array<3, elemT>& input) const { this->_input_ptr = &input; } template - elemT operator()(const BasicCoordinate<3, positionT>& point_in_input_coords) const - { - return - pull_linear_interpolate(*(this->_input_ptr), - point_in_input_coords); + elemT operator()(const BasicCoordinate<3, positionT>& point_in_input_coords) const { + return pull_linear_interpolate(*(this->_input_ptr), point_in_input_coords); } + private: // todo terribly dangerous // we have it such that we can have a default constructor without any arguments // this means we cannot use a reference // we could use a shared_ptr, but then we can only interpolate data for which we have a shared_ptr - mutable const Array<3, elemT> * _input_ptr; + mutable const Array<3, elemT>* _input_ptr; }; - - /*! \ingroup numerics - \brief A function object to push values at the grid of the input array into the output array + \brief A function object to push values at the grid of the input array into the output array \todo preliminary. We might want to derive this from a class or so. */ template -class -PushTransposeLinearInterpolator -{ +class PushTransposeLinearInterpolator { public: - PushTransposeLinearInterpolator() - :_output_ptr(0) - {} + PushTransposeLinearInterpolator() : _output_ptr(0) {} - void set_output(Array<3, elemT>& output) const - { - this->_output_ptr = &output; - } + void set_output(Array<3, elemT>& output) const { this->_output_ptr = &output; } template - void add_to(const BasicCoordinate<3, positionT>& point_in_output_coords, const valueT value) const - { - push_transpose_linear_interpolate(*(this->_output_ptr), - point_in_output_coords, - value); + void add_to(const BasicCoordinate<3, positionT>& point_in_output_coords, const valueT value) const { + push_transpose_linear_interpolate(*(this->_output_ptr), point_in_output_coords, value); } + private: // todo terribly dangerous // we have it such that we can have a default constructor without any arguments // this means we cannot use a reference // we could use a shared_ptr, but then we can only interpolate data for which we have a shared_ptr - mutable Array<3, elemT> * _output_ptr; + mutable Array<3, elemT>* _output_ptr; }; /*! \ingroup numerics @@ -137,68 +107,48 @@ PushTransposeLinearInterpolator \todo preliminary. We might want to derive this from a class or so. */ template -class -PullNearestNeighbourInterpolator -{ +class PullNearestNeighbourInterpolator { public: - PullNearestNeighbourInterpolator() - : _input_ptr(0) - {} + PullNearestNeighbourInterpolator() : _input_ptr(0) {} - void set_input(const Array<3, elemT>& input) const - { - this->_input_ptr = &input; - } + void set_input(const Array<3, elemT>& input) const { this->_input_ptr = &input; } template - elemT operator()(const BasicCoordinate<3, positionT>& point_in_input_coords) const - { - return - pull_nearest_neighbour_interpolate(*(this->_input_ptr), - point_in_input_coords); + elemT operator()(const BasicCoordinate<3, positionT>& point_in_input_coords) const { + return pull_nearest_neighbour_interpolate(*(this->_input_ptr), point_in_input_coords); } + private: // todo terribly dangerous // we have it such that we can have a default constructor without any arguments // this means we cannot use a reference // we could use a shared_ptr, but then we can only interpolate data for which we have a shared_ptr - mutable const Array<3, elemT> * _input_ptr; + mutable const Array<3, elemT>* _input_ptr; }; - - /*! \ingroup numerics - \brief A function object to push values at the grid of the input array into the output array + \brief A function object to push values at the grid of the input array into the output array \todo preliminary. We might want to derive this from a class or so. */ template -class -PushNearestNeighbourInterpolator -{ +class PushNearestNeighbourInterpolator { public: - PushNearestNeighbourInterpolator() - :_output_ptr(0) - {} + PushNearestNeighbourInterpolator() : _output_ptr(0) {} - void set_output(Array<3, elemT>& output) const - { - this->_output_ptr = &output; - } + void set_output(Array<3, elemT>& output) const { this->_output_ptr = &output; } template - void add_to(const BasicCoordinate<3, positionT>& point_in_output_coords, const valueT value) const - { - push_nearest_neighbour_interpolate(*(this->_output_ptr), - point_in_output_coords, - value); + void add_to(const BasicCoordinate<3, positionT>& point_in_output_coords, const valueT value) const { + push_nearest_neighbour_interpolate(*(this->_output_ptr), point_in_output_coords, value); } + private: // todo terribly dangerous // we have it such that we can have a default constructor without any arguments // this means we cannot use a reference // we could use a shared_ptr, but then we can only interpolate data for which we have a shared_ptr - mutable Array<3, elemT> * _output_ptr; + mutable Array<3, elemT>* _output_ptr; }; END_NAMESPACE_STIR diff --git a/src/include/stir_experimental/numerics/more_interpolators.inl b/src/include/stir_experimental/numerics/more_interpolators.inl index c04de3c1d6..a9c959586a 100644 --- a/src/include/stir_experimental/numerics/more_interpolators.inl +++ b/src/include/stir_experimental/numerics/more_interpolators.inl @@ -17,145 +17,108 @@ #include "stir/round.h" #include - START_NAMESPACE_STIR template elemT -pull_nearest_neighbour_interpolate(const Array<3, elemT>& in, - const BasicCoordinate<3, positionT>& point_in_input_coords) -{ - // find nearest neighbour - const Coordinate3D - nearest_neighbour = round(point_in_input_coords); +pull_nearest_neighbour_interpolate(const Array<3, elemT>& in, const BasicCoordinate<3, positionT>& point_in_input_coords) { + // find nearest neighbour + const Coordinate3D nearest_neighbour = round(point_in_input_coords); - if (nearest_neighbour[1] <= in.get_max_index() && - nearest_neighbour[1] >= in.get_min_index() && + if (nearest_neighbour[1] <= in.get_max_index() && nearest_neighbour[1] >= in.get_min_index() && nearest_neighbour[2] <= in[nearest_neighbour[1]].get_max_index() && nearest_neighbour[2] >= in[nearest_neighbour[1]].get_min_index() && nearest_neighbour[3] <= in[nearest_neighbour[1]][nearest_neighbour[2]].get_max_index() && - nearest_neighbour[3] >= in[nearest_neighbour[1]][nearest_neighbour[2]].get_min_index()) - { - return in[nearest_neighbour]; - } - else + nearest_neighbour[3] >= in[nearest_neighbour[1]][nearest_neighbour[2]].get_min_index()) { + return in[nearest_neighbour]; + } else return 0; } template void -push_nearest_neighbour_interpolate(Array& out, - const BasicCoordinate& point_in_output_coords, - valueT value) -{ - if (value==0) - return; - const BasicCoordinate nearest_neighbour = - round(point_in_output_coords); - - if (nearest_neighbour[1] <= out.get_max_index() && - nearest_neighbour[1] >= out.get_min_index() && +push_nearest_neighbour_interpolate(Array& out, + const BasicCoordinate& point_in_output_coords, valueT value) { + if (value == 0) + return; + const BasicCoordinate nearest_neighbour = round(point_in_output_coords); + + if (nearest_neighbour[1] <= out.get_max_index() && nearest_neighbour[1] >= out.get_min_index() && nearest_neighbour[2] <= out[nearest_neighbour[1]].get_max_index() && nearest_neighbour[2] >= out[nearest_neighbour[1]].get_min_index() && nearest_neighbour[3] <= out[nearest_neighbour[1]][nearest_neighbour[2]].get_max_index() && nearest_neighbour[3] >= out[nearest_neighbour[1]][nearest_neighbour[2]].get_min_index()) - out[nearest_neighbour] += - static_cast(value); + out[nearest_neighbour] += static_cast(value); } - - template elemT -pull_linear_interpolate(const Array<3, elemT>& in, - const BasicCoordinate<3, positionT>& point_in_input_coords) -{ - // find left neighbour - const Coordinate3D - left_neighbour(round(std::floor(point_in_input_coords[1])), - round(std::floor(point_in_input_coords[2])), - round(std::floor(point_in_input_coords[3]))); +pull_linear_interpolate(const Array<3, elemT>& in, const BasicCoordinate<3, positionT>& point_in_input_coords) { + // find left neighbour + const Coordinate3D left_neighbour(round(std::floor(point_in_input_coords[1])), round(std::floor(point_in_input_coords[2])), + round(std::floor(point_in_input_coords[3]))); // TODO handle boundary conditions - if (left_neighbour[1] < in.get_max_index() && - left_neighbour[1] >= in.get_min_index() && - left_neighbour[2] < in[left_neighbour[1]].get_max_index() && - left_neighbour[2] >= in[left_neighbour[1]].get_min_index() && + if (left_neighbour[1] < in.get_max_index() && left_neighbour[1] >= in.get_min_index() && + left_neighbour[2] < in[left_neighbour[1]].get_max_index() && left_neighbour[2] >= in[left_neighbour[1]].get_min_index() && left_neighbour[3] < in[left_neighbour[1]][left_neighbour[2]].get_max_index() && - left_neighbour[3] >= in[left_neighbour[1]][left_neighbour[2]].get_min_index()) - { - const int x1=left_neighbour[3]; - const int y1=left_neighbour[2]; - const int z1=left_neighbour[1]; - const int x2=left_neighbour[3]+1; - const int y2=left_neighbour[2]+1; - const int z2=left_neighbour[1]+1; - const positionT ix = point_in_input_coords[3]-x1; - const positionT iy = point_in_input_coords[2]-y1; - const positionT iz = point_in_input_coords[1]-z1; - const positionT ixc = 1 - ix; - const positionT iyc = 1 - iy; - const positionT izc = 1 - iz; - return - static_cast - ( - ixc * (iyc * (izc * in[z1][y1][x1] - + iz * in[z2][y1][x1]) - + iy * (izc * in[z1][y2][x1] - + iz * in[z2][y2][x1])) - + ix * (iyc * (izc * in[z1][y1][x2] - + iz * in[z2][y1][x2]) - + iy * (izc * in[z1][y2][x2] - + iz * in[z2][y2][x2])) - ); - } - else + left_neighbour[3] >= in[left_neighbour[1]][left_neighbour[2]].get_min_index()) { + const int x1 = left_neighbour[3]; + const int y1 = left_neighbour[2]; + const int z1 = left_neighbour[1]; + const int x2 = left_neighbour[3] + 1; + const int y2 = left_neighbour[2] + 1; + const int z2 = left_neighbour[1] + 1; + const positionT ix = point_in_input_coords[3] - x1; + const positionT iy = point_in_input_coords[2] - y1; + const positionT iz = point_in_input_coords[1] - z1; + const positionT ixc = 1 - ix; + const positionT iyc = 1 - iy; + const positionT izc = 1 - iz; + return static_cast( + ixc * (iyc * (izc * in[z1][y1][x1] + iz * in[z2][y1][x1]) + iy * (izc * in[z1][y2][x1] + iz * in[z2][y2][x1])) + + ix * (iyc * (izc * in[z1][y1][x2] + iz * in[z2][y1][x2]) + iy * (izc * in[z1][y2][x2] + iz * in[z2][y2][x2]))); + } else return 0; } template void -push_transpose_linear_interpolate(Array<3, elemT>& out, - const BasicCoordinate<3, positionT>& point_in_output_coords, - valueT value) -{ - if (value==0) - return; +push_transpose_linear_interpolate(Array<3, elemT>& out, const BasicCoordinate<3, positionT>& point_in_output_coords, + valueT value) { + if (value == 0) + return; // find left neighbour - const Coordinate3D - left_neighbour(round(std::floor(point_in_output_coords[1])), - round(std::floor(point_in_output_coords[2])), - round(std::floor(point_in_output_coords[3]))); + const Coordinate3D left_neighbour(round(std::floor(point_in_output_coords[1])), + round(std::floor(point_in_output_coords[2])), + round(std::floor(point_in_output_coords[3]))); // TODO handle boundary conditions - if (left_neighbour[1] < out.get_max_index() && - left_neighbour[1] >= out.get_min_index() && - left_neighbour[2] < out[left_neighbour[1]].get_max_index() && - left_neighbour[2] >= out[left_neighbour[1]].get_min_index() && + if (left_neighbour[1] < out.get_max_index() && left_neighbour[1] >= out.get_min_index() && + left_neighbour[2] < out[left_neighbour[1]].get_max_index() && left_neighbour[2] >= out[left_neighbour[1]].get_min_index() && left_neighbour[3] < out[left_neighbour[1]][left_neighbour[2]].get_max_index() && - left_neighbour[3] >= out[left_neighbour[1]][left_neighbour[2]].get_min_index()) - { - const int x1=left_neighbour[3]; - const int y1=left_neighbour[2]; - const int z1=left_neighbour[1]; - const int x2=left_neighbour[3]+1; - const int y2=left_neighbour[2]+1; - const int z2=left_neighbour[1]+1; - const float ix = point_in_output_coords[3]-x1; - const float iy = point_in_output_coords[2]-y1; - const float iz = point_in_output_coords[1]-z1; - const float ixc = 1 - ix; - const float iyc = 1 - iy; - const float izc = 1 - iz; - out[z1][y1][x1] += ixc * iyc * izc * value; - out[z2][y1][x1] += ixc * iyc * iz * value; - out[z1][y2][x1] += ixc * iy * izc * value; - out[z2][y2][x1] += ixc * iy * iz * value; - out[z1][y1][x2] += ix * iyc * izc * value; - out[z2][y1][x2] += ix * iyc * iz * value; - out[z1][y2][x2] += ix * iy * izc * value; - out[z2][y2][x2] += ix * iy * iz * value; - } + left_neighbour[3] >= out[left_neighbour[1]][left_neighbour[2]].get_min_index()) { + const int x1 = left_neighbour[3]; + const int y1 = left_neighbour[2]; + const int z1 = left_neighbour[1]; + const int x2 = left_neighbour[3] + 1; + const int y2 = left_neighbour[2] + 1; + const int z2 = left_neighbour[1] + 1; + const float ix = point_in_output_coords[3] - x1; + const float iy = point_in_output_coords[2] - y1; + const float iz = point_in_output_coords[1] - z1; + const float ixc = 1 - ix; + const float iyc = 1 - iy; + const float izc = 1 - iz; + out[z1][y1][x1] += ixc * iyc * izc * value; + out[z2][y1][x1] += ixc * iyc * iz * value; + out[z1][y2][x1] += ixc * iy * izc * value; + out[z2][y2][x1] += ixc * iy * iz * value; + out[z1][y1][x2] += ix * iyc * izc * value; + out[z2][y1][x2] += ix * iyc * iz * value; + out[z1][y2][x2] += ix * iy * izc * value; + out[z2][y2][x2] += ix * iy * iz * value; + } } END_NAMESPACE_STIR diff --git a/src/include/stir_experimental/phantoms/CylindersWithLineSource.h b/src/include/stir_experimental/phantoms/CylindersWithLineSource.h index 29ac08cc0d..da4acf4c18 100644 --- a/src/include/stir_experimental/phantoms/CylindersWithLineSource.h +++ b/src/include/stir_experimental/phantoms/CylindersWithLineSource.h @@ -6,153 +6,122 @@ #include "stir_experimental/Shape/EllipsoidalCylinder.h" #include "stir_experimental/Shape/CombinedShape3D.h" - - - START_NAMESPACE_STIR -class LineSource_phantom -{ +class LineSource_phantom { public: - inline LineSource_phantom(); - - inline const shared_ptr& get_A_ptr() const - { return A_ptr; } - + + inline const shared_ptr& get_A_ptr() const { return A_ptr; } + inline shared_ptr make_union_ptr(const float fraction) const; - + inline void translate(const CartesianCoordinate3D& direction); inline void scale(const CartesianCoordinate3D& scale3D); -private: +private: shared_ptr A_ptr; - }; - -LineSource_phantom::LineSource_phantom() -{ - A_ptr = new EllipsoidalCylinder (150,0,0, - CartesianCoordinate3D(0,0,0), - 0,0,0); +LineSource_phantom::LineSource_phantom() { + A_ptr = new EllipsoidalCylinder(150, 0, 0, CartesianCoordinate3D(0, 0, 0), 0, 0, 0); } -void -LineSource_phantom::translate(const CartesianCoordinate3D& direction) -{ - A_ptr->translate(direction); +void +LineSource_phantom::translate(const CartesianCoordinate3D& direction) { + A_ptr->translate(direction); } -void -LineSource_phantom::scale(const CartesianCoordinate3D& scale3D) -{ - //check this!!! +void +LineSource_phantom::scale(const CartesianCoordinate3D& scale3D) { + // check this!!! A_ptr->scale_around_origin(scale3D); } shared_ptr -LineSource_phantom::make_union_ptr(const float fraction) const -{ - shared_ptr full_A = get_A_ptr()->clone(); - - return full_A ; - +LineSource_phantom::make_union_ptr(const float fraction) const { + shared_ptr full_A = get_A_ptr()->clone(); + return full_A; } -class CylindersWithLineSource_phantom -{ +class CylindersWithLineSource_phantom { public: - inline CylindersWithLineSource_phantom(); - - //inline const shared_ptr& get_A_ptr() const + + // inline const shared_ptr& get_A_ptr() const //{ return A_ptr; } - - inline const shared_ptr& get_B_ptr() const - { return B_ptr; } - inline const shared_ptr& get_C_ptr() const - { return C_ptr; } + inline const shared_ptr& get_B_ptr() const { return B_ptr; } + + inline const shared_ptr& get_C_ptr() const { return C_ptr; } - //inline const shared_ptr& get_B1_ptr() const + // inline const shared_ptr& get_B1_ptr() const //{ return B1_ptr; } - //inline const shared_ptr& get_C1_ptr() const + // inline const shared_ptr& get_C1_ptr() const //{ return C1_ptr; } - inline shared_ptr make_union_ptr(const float fraction) const; - + inline void translate(const CartesianCoordinate3D& direction); inline void scale(const CartesianCoordinate3D& scale3D); -private: - //shared_ptr A_ptr; +private: + // shared_ptr A_ptr; shared_ptr B_ptr; shared_ptr C_ptr; - //shared_ptr B1_ptr; - //shared_ptr C1_ptr; - + // shared_ptr B1_ptr; + // shared_ptr C1_ptr; }; - -CylindersWithLineSource_phantom::CylindersWithLineSource_phantom() -{ +CylindersWithLineSource_phantom::CylindersWithLineSource_phantom() { // A_ptr = new EllipsoidalCylinder (150,19,19, - //CartesianCoordinate3D(0,0,0), - //0,0,0); + // CartesianCoordinate3D(0,0,0), + // 0,0,0); // for 966 - //B_ptr = new EllipsoidalCylinder (500,60,60, - // CartesianCoordinate3D(0,0,0), + // B_ptr = new EllipsoidalCylinder (500,60,60, + // CartesianCoordinate3D(0,0,0), // 0,0,0); - - // two planes only - /* B_ptr = new EllipsoidalCylinder (1,6,6, - CartesianCoordinate3D(0,-14,0), - 0,0,0); - - C_ptr = new EllipsoidalCylinder (1,6,6, - CartesianCoordinate3D(0,14,0), - 0,0,0);*/ + /* B_ptr = new EllipsoidalCylinder (1,6,6, + CartesianCoordinate3D(0,-14,0), + 0,0,0); + + C_ptr = new EllipsoidalCylinder (1,6,6, + CartesianCoordinate3D(0,14,0), + 0,0,0);*/ // off centre // this is to check the bloody arthifacts in new filters /*B_ptr = new EllipsoidalCylinder (1,6,6, - CartesianCoordinate3D(0,-13,4), + CartesianCoordinate3D(0,-13,4), 0,0,0); - + C_ptr = new EllipsoidalCylinder (1,6,6, CartesianCoordinate3D(0,13,4), 0,0,0);*/ - // these are the ones noramly used - B_ptr = new EllipsoidalCylinder (1,8,8, - CartesianCoordinate3D(0,-14,5), - 0,0,0); - - C_ptr = new EllipsoidalCylinder (1,8,8, - CartesianCoordinate3D(0,14,5), - 0,0,0); + B_ptr = new EllipsoidalCylinder(1, 8, 8, CartesianCoordinate3D(0, -14, 5), 0, 0, 0); - // the one that I use for resolution - /* B_ptr = new EllipsoidalCylinder (100,8,8, - CartesianCoordinate3D(0,-14,0), - 0,0,0); - - C_ptr = new EllipsoidalCylinder (100,8,8, - CartesianCoordinate3D(0,14,0), - 0,0,0);*/ + C_ptr = new EllipsoidalCylinder(1, 8, 8, CartesianCoordinate3D(0, 14, 5), 0, 0, 0); + + // the one that I use for resolution + /* B_ptr = new EllipsoidalCylinder (100,8,8, + CartesianCoordinate3D(0,-14,0), + 0,0,0); + + C_ptr = new EllipsoidalCylinder (100,8,8, + CartesianCoordinate3D(0,14,0), + 0,0,0);*/ - // the one that I use for resolution + // the one that I use for resolution /* B1_ptr = new EllipsoidalCylinder (1,6,6, - CartesianCoordinate3D(0,0,-14), + CartesianCoordinate3D(0,0,-14), 0,0,0); C1_ptr = new EllipsoidalCylinder (1,6,6, @@ -161,72 +130,56 @@ CylindersWithLineSource_phantom::CylindersWithLineSource_phantom() // off centre /*B1_ptr = new EllipsoidalCylinder (1,6,6, - CartesianCoordinate3D(0,-2,-14), + CartesianCoordinate3D(0,-2,-14), 0,0,0); C1_ptr = new EllipsoidalCylinder (1,6,6, CartesianCoordinate3D(0,-2,14), 0,0,0);*/ - } -void -CylindersWithLineSource_phantom::translate(const CartesianCoordinate3D& direction) -{ -// A_ptr->translate(direction); - B_ptr->translate(direction); - C_ptr->translate(direction); -// B1_ptr->translate(direction); -// C1_ptr->translate(direction); +void +CylindersWithLineSource_phantom::translate(const CartesianCoordinate3D& direction) { + // A_ptr->translate(direction); + B_ptr->translate(direction); + C_ptr->translate(direction); + // B1_ptr->translate(direction); + // C1_ptr->translate(direction); } -void -CylindersWithLineSource_phantom::scale(const CartesianCoordinate3D& scale3D) -{ - //check this!!! - //A_ptr->scale_around_origin(scale3D); +void +CylindersWithLineSource_phantom::scale(const CartesianCoordinate3D& scale3D) { + // check this!!! + // A_ptr->scale_around_origin(scale3D); B_ptr->scale_around_origin(scale3D); C_ptr->scale_around_origin(scale3D); - //B1_ptr->scale_around_origin(scale3D); - //C1_ptr->scale_around_origin(scale3D); - - - + // B1_ptr->scale_around_origin(scale3D); + // C1_ptr->scale_around_origin(scale3D); } shared_ptr -CylindersWithLineSource_phantom::make_union_ptr(const float fraction) const -{ - //shared_ptr full_A = get_A_ptr()->clone(); - shared_ptr full_B = get_B_ptr()->clone(); - shared_ptr full_C = get_C_ptr()->clone(); - - //shared_ptr full_B1 = get_B1_ptr()->clone(); - //shared_ptr full_C1 = get_C1_ptr()->clone(); - - shared_ptr AB_union = - new CombinedShape3D >( full_C,full_B); - - //shared_ptr AB1_union = - //new CombinedShape3D >( full_C1,full_B1); - - //shared_ptr AB1_AB_union = - //new CombinedShape3D >( AB_union, AB1_union); - - // shared_ptr ABC_union = - // new CombinedShape3D >( AB_union,full_C); - - //return AB1_AB_union; - return AB_union; - //return full_B; - - -} +CylindersWithLineSource_phantom::make_union_ptr(const float fraction) const { + // shared_ptr full_A = get_A_ptr()->clone(); + shared_ptr full_B = get_B_ptr()->clone(); + shared_ptr full_C = get_C_ptr()->clone(); + // shared_ptr full_B1 = get_B1_ptr()->clone(); + // shared_ptr full_C1 = get_C1_ptr()->clone(); + shared_ptr AB_union = new CombinedShape3D>(full_C, full_B); + // shared_ptr AB1_union = + // new CombinedShape3D >( full_C1,full_B1); + // shared_ptr AB1_AB_union = + // new CombinedShape3D >( AB_union, AB1_union); + // shared_ptr ABC_union = + // new CombinedShape3D >( AB_union,full_C); + // return AB1_AB_union; + return AB_union; + // return full_B; +} // OLD #if 0 diff --git a/src/include/stir_experimental/phantoms/Utah.h b/src/include/stir_experimental/phantoms/Utah.h index aa6d07d9bf..1cc2e34d22 100644 --- a/src/include/stir_experimental/phantoms/Utah.h +++ b/src/include/stir_experimental/phantoms/Utah.h @@ -19,7 +19,7 @@ #ifndef __stir_phantoms_Utah_H__ #define __stir_phantoms_Utah_H__ /*! - \file + \file \ingroup Shape \brief inline implementations for stir::Utah_phantom. @@ -33,54 +33,46 @@ #include "stir/CombinedShape3D.h" START_NAMESPACE_STIR - + /*! \brief A class that represents a Utah phantom. \todo dims here are wrong A: cylinder, 20cm diam, 10cm height B: cylinder, 18cm diam, 15cm height - C: outer annulus, 20cm extern.diam, + C: outer annulus, 20cm extern.diam, 2cm thick, 15cm height D: cylinder in B, 4.5cm diam, 18cm??? height - E: shorter cylinder in B, 4.5cm diam, + E: shorter cylinder in B, 4.5cm diam, 5.5cm height */ -class Utah_phantom -{ +class Utah_phantom { public: - /*! + /*! \brief Construct a Utah phantom. It is oriented along z, with edge between A and B at z=0. */ inline Utah_phantom(); - - inline const shared_ptr& get_A_ptr() const - { return A_ptr; } - - inline const shared_ptr& get_B_ptr() const - { return B_ptr; } - + + inline const shared_ptr& get_A_ptr() const { return A_ptr; } + + inline const shared_ptr& get_B_ptr() const { return B_ptr; } + //! get B without holes - inline const shared_ptr& get_full_B_ptr() const - { return full_B_ptr; } + inline const shared_ptr& get_full_B_ptr() const { return full_B_ptr; } + + inline const shared_ptr& get_C_ptr() const { return C_ptr; } - inline const shared_ptr& get_C_ptr() const - { return C_ptr; } - //! get C without hole - inline const shared_ptr& get_full_C_ptr() const - { return full_C_ptr; } + inline const shared_ptr& get_full_C_ptr() const { return full_C_ptr; } + + inline const shared_ptr& get_D_ptr() const { return D_ptr; } - inline const shared_ptr& get_D_ptr() const - { return D_ptr; } - - inline const shared_ptr& get_E_ptr() const - { return E_ptr; } + inline const shared_ptr& get_E_ptr() const { return E_ptr; } - /*! + /*! \brief make a region inside B when \c fraction<1. The region has a smaller outer cylinder (scaled with fraction) @@ -88,7 +80,7 @@ class Utah_phantom */ inline shared_ptr make_inside_B_ptr(const float fraction) const; - /*! + /*! \brief make a region inside C when \c fraction<1. The region has a smaller outer cylinder (scaled with fraction) @@ -109,67 +101,52 @@ class Utah_phantom shared_ptr E_ptr; }; -Utah_phantom::Utah_phantom() -{ -//EllipsoidalCylinder (Lcyl,Rcyl_a,Rcyl_b, -// CartesianCoordinate3D(zc,yc,xc), -// alpha,beta,gamma) - - - A_ptr = new EllipsoidalCylinder (100,100,100, - CartesianCoordinate3D(-50,0,0)); - full_B_ptr = new EllipsoidalCylinder (150,80,80, - CartesianCoordinate3D(75,0,0)); - full_C_ptr = new EllipsoidalCylinder (150,100,100, - CartesianCoordinate3D(75,0,0)); - D_ptr = new EllipsoidalCylinder (105,22.5,22.5, - CartesianCoordinate3D(52.5,0,-47.5)); - E_ptr = new EllipsoidalCylinder (55,22.5,22.5, - CartesianCoordinate3D(27.5,0,47.5)); - - //CombinedShape3D< logical_and_not > C( &FullC,&FullB); - C_ptr = new CombinedShape3D< logical_and_not > ( full_C_ptr,full_B_ptr); - shared_ptr full_B_notD_ptr = - new CombinedShape3D< logical_and_not > ( full_B_ptr,D_ptr); - B_ptr = new CombinedShape3D< logical_and_not > ( full_B_notD_ptr,E_ptr); +Utah_phantom::Utah_phantom() { + // EllipsoidalCylinder (Lcyl,Rcyl_a,Rcyl_b, + // CartesianCoordinate3D(zc,yc,xc), + // alpha,beta,gamma) + + A_ptr = new EllipsoidalCylinder(100, 100, 100, CartesianCoordinate3D(-50, 0, 0)); + full_B_ptr = new EllipsoidalCylinder(150, 80, 80, CartesianCoordinate3D(75, 0, 0)); + full_C_ptr = new EllipsoidalCylinder(150, 100, 100, CartesianCoordinate3D(75, 0, 0)); + D_ptr = new EllipsoidalCylinder(105, 22.5, 22.5, CartesianCoordinate3D(52.5, 0, -47.5)); + E_ptr = new EllipsoidalCylinder(55, 22.5, 22.5, CartesianCoordinate3D(27.5, 0, 47.5)); + + // CombinedShape3D< logical_and_not > C( &FullC,&FullB); + C_ptr = new CombinedShape3D>(full_C_ptr, full_B_ptr); + shared_ptr full_B_notD_ptr = new CombinedShape3D>(full_B_ptr, D_ptr); + B_ptr = new CombinedShape3D>(full_B_notD_ptr, E_ptr); } -shared_ptr -Utah_phantom::make_inside_B_ptr(const float fraction) const -{ +shared_ptr +Utah_phantom::make_inside_B_ptr(const float fraction) const { // first get a new copy of full_B shared_ptr small_full_B_ptr = get_full_B_ptr()->clone(); - // make it smaller - small_full_B_ptr ->scale_around_origin(CartesianCoordinate3D(1.F,fraction,fraction)); + // make it smaller + small_full_B_ptr->scale_around_origin(CartesianCoordinate3D(1.F, fraction, fraction)); // the same for D shared_ptr large_D_ptr = get_D_ptr()->clone(); - large_D_ptr ->scale_around_origin(CartesianCoordinate3D(1.F,1/fraction,1/fraction)); + large_D_ptr->scale_around_origin(CartesianCoordinate3D(1.F, 1 / fraction, 1 / fraction)); // and E shared_ptr large_E_ptr = get_E_ptr()->clone(); - large_E_ptr ->scale_around_origin(CartesianCoordinate3D(1.F,1/fraction,1/fraction)); - + large_E_ptr->scale_around_origin(CartesianCoordinate3D(1.F, 1 / fraction, 1 / fraction)); + // combine the whole thing - shared_ptr small_full_B_notD_ptr = - new CombinedShape3D< logical_and_not > ( small_full_B_ptr,large_D_ptr); - return - new CombinedShape3D< logical_and_not > ( small_full_B_notD_ptr,large_E_ptr); + shared_ptr small_full_B_notD_ptr = new CombinedShape3D>(small_full_B_ptr, large_D_ptr); + return new CombinedShape3D>(small_full_B_notD_ptr, large_E_ptr); } - -shared_ptr -Utah_phantom::make_inside_C_ptr(const float fraction) const -{ +shared_ptr +Utah_phantom::make_inside_C_ptr(const float fraction) const { shared_ptr small_full_C_ptr = get_full_C_ptr()->clone(); - small_full_C_ptr ->scale_around_origin(CartesianCoordinate3D(1.F,fraction,fraction)); + small_full_C_ptr->scale_around_origin(CartesianCoordinate3D(1.F, fraction, fraction)); shared_ptr large_full_B_ptr = get_full_B_ptr()->clone(); - large_full_B_ptr ->scale_around_origin(CartesianCoordinate3D(1.F,1/fraction,1/fraction)); - return - new CombinedShape3D< logical_and_not > ( small_full_C_ptr,large_full_B_ptr); + large_full_B_ptr->scale_around_origin(CartesianCoordinate3D(1.F, 1 / fraction, 1 / fraction)); + return new CombinedShape3D>(small_full_C_ptr, large_full_B_ptr); } -void -Utah_phantom::translate(const CartesianCoordinate3D& direction) -{ +void +Utah_phantom::translate(const CartesianCoordinate3D& direction) { A_ptr->translate(direction); B_ptr->translate(direction); full_B_ptr->translate(direction); @@ -179,9 +156,8 @@ Utah_phantom::translate(const CartesianCoordinate3D& direction) E_ptr->translate(direction); } -void -Utah_phantom::scale(const CartesianCoordinate3D& scale3D) -{ +void +Utah_phantom::scale(const CartesianCoordinate3D& scale3D) { A_ptr->scale(scale3D); B_ptr->scale(scale3D); full_B_ptr->scale(scale3D); diff --git a/src/include/stir_experimental/recon_buildblock/BinNormalisationFromML2D.h b/src/include/stir_experimental/recon_buildblock/BinNormalisationFromML2D.h index 62ff47dd24..71a8492443 100644 --- a/src/include/stir_experimental/recon_buildblock/BinNormalisationFromML2D.h +++ b/src/include/stir_experimental/recon_buildblock/BinNormalisationFromML2D.h @@ -42,7 +42,7 @@ START_NAMESPACE_STIR \brief A BinNormalisation class that gets the normalisation factors from the files output by find_ML_normfactors. - \warning the ProjData object has to be 2D, no mashing, no span, no arc-correction. + \warning the ProjData object has to be 2D, no mashing, no span, no arc-correction. I'm not sure if this is properly checked at run-time. \par Parsing details @@ -62,16 +62,14 @@ START_NAMESPACE_STIR */ -class BinNormalisationFromML2D : - public RegisteredParsingObject -{ +class BinNormalisationFromML2D : public RegisteredParsingObject { public: //! Name which will be used when parsing a BinNormalisation object - static const char * const registered_name; - + static const char* const registered_name; + //! Default constructor - /*! - \warning You should not call any member functions for any object just + /*! + \warning You should not call any member functions for any object just constructed with this constructor. Initialise the object properly first by parsing. */ @@ -81,16 +79,16 @@ class BinNormalisationFromML2D : virtual Succeeded set_up(const shared_ptr&); //! Normalise some data - /*! - This means \c multiply with the data in the projdata object - passed in the constructor. + /*! + This means \c multiply with the data in the projdata object + passed in the constructor. */ virtual void apply(RelatedViewgrams& viewgrams) const; //! Undo the normalisation of some data - /*! - This means \c divide with the data in the projdata object - passed in the constructor. + /*! + This means \c divide with the data in the projdata object + passed in the constructor. */ virtual void undo(RelatedViewgrams& viewgrams) const; @@ -109,7 +107,6 @@ class BinNormalisationFromML2D : shared_ptr norm_factors_ptr; }; - END_NAMESPACE_STIR #endif diff --git a/src/include/stir_experimental/recon_buildblock/BinNormalisationSinogramRescaling.h b/src/include/stir_experimental/recon_buildblock/BinNormalisationSinogramRescaling.h index 499bfae83b..a863638a2a 100644 --- a/src/include/stir_experimental/recon_buildblock/BinNormalisationSinogramRescaling.h +++ b/src/include/stir_experimental/recon_buildblock/BinNormalisationSinogramRescaling.h @@ -13,8 +13,6 @@ \author Sanida Mustafovic */ - - #ifndef __stir_recon_buildblock_BinNormalisationSinogramRescaling_H__ #define __stir_recon_buildblock_BinNormalisationSinogramRescaling_H__ @@ -29,23 +27,20 @@ START_NAMESPACE_STIR - /*! \ingroup recon_buildblock - \brief The BinNormalisationSinogramRescaling class gets normaliastion factors by dividing + \brief The BinNormalisationSinogramRescaling class gets normaliastion factors by dividing forward projection of the fitted cyl. to the precorrecred data - + */ -class BinNormalisationSinogramRescaling : - public RegisteredParsingObject -{ +class BinNormalisationSinogramRescaling : public RegisteredParsingObject { public: //! Name which will be used when parsing a BinNormalisation object - static const char * const registered_name; - + static const char* const registered_name; + //! Default constructor - /*! - \warning You should not call any member functions for any object just + /*! + \warning You should not call any member functions for any object just constructed with this constructor. Initialise the object properly first by parsing. */ @@ -58,13 +53,13 @@ class BinNormalisationSinogramRescaling : float get_bin_efficiency(const Bin& bin, const double start_time, const double end_time) const; //! Normalise some data - /*! + /*! This means \c multiply with the data in the scale factors file. */ - virtual void apply(RelatedViewgrams& viewgrams,const double start_time, const double end_time) const; + virtual void apply(RelatedViewgrams& viewgrams, const double start_time, const double end_time) const; //! Undo the normalisation of some data - /*! + /*! This means \c divide with the data in th scale factors file. */ virtual void undo(RelatedViewgrams& viewgrams, const double start_time, const double end_time) const; @@ -73,7 +68,7 @@ class BinNormalisationSinogramRescaling : // the proj data info used for obtaining axial position num, segment num // will be set by set_up() shared_ptr proj_data_info_sptr; - Array<3,float> rescaling_factors; + Array<3, float> rescaling_factors; // parsing stuff virtual void set_defaults(); @@ -83,7 +78,6 @@ class BinNormalisationSinogramRescaling : std::string sinogram_rescaling_factors_filename; }; - END_NAMESPACE_STIR #endif diff --git a/src/include/stir_experimental/recon_buildblock/BinNormalisationUsingProfile.h b/src/include/stir_experimental/recon_buildblock/BinNormalisationUsingProfile.h index 27340036c5..b2bf68b1b6 100644 --- a/src/include/stir_experimental/recon_buildblock/BinNormalisationUsingProfile.h +++ b/src/include/stir_experimental/recon_buildblock/BinNormalisationUsingProfile.h @@ -24,25 +24,23 @@ START_NAMESPACE_STIR -class BinNormalisationUsingProfile : - public RegisteredParsingObject -{ +class BinNormalisationUsingProfile : public RegisteredParsingObject { public: //! Name which will be used when parsing a BinNormalisation object - static const char * const registered_name; + static const char* const registered_name; BinNormalisationUsingProfile(); BinNormalisationUsingProfile(const std::string& filename); - virtual void apply(RelatedViewgrams& viewgrams,const double start_time, const double end_time) const; + virtual void apply(RelatedViewgrams& viewgrams, const double start_time, const double end_time) const; - virtual void undo(RelatedViewgrams& viewgrams,const double start_time, const double end_time) const; + virtual void undo(RelatedViewgrams& viewgrams, const double start_time, const double end_time) const; + + virtual float get_bin_efficiency(const Bin& bin, const double start_time, const double end_time) const { return 1; } - virtual float get_bin_efficiency(const Bin& bin,const double start_time, const double end_time) const { return 1;} - private: - mutable Array<1,float> profile; + mutable Array<1, float> profile; std::string profile_filename; virtual void set_defaults(); @@ -53,4 +51,3 @@ class BinNormalisationUsingProfile : END_NAMESPACE_STIR #endif - diff --git a/src/include/stir_experimental/recon_buildblock/DataSymmetriesForDensels_PET_CartesianGrid.h b/src/include/stir_experimental/recon_buildblock/DataSymmetriesForDensels_PET_CartesianGrid.h index 6ad81f9ec0..e5fe4e8aee 100644 --- a/src/include/stir_experimental/recon_buildblock/DataSymmetriesForDensels_PET_CartesianGrid.h +++ b/src/include/stir_experimental/recon_buildblock/DataSymmetriesForDensels_PET_CartesianGrid.h @@ -24,12 +24,11 @@ \brief Declaration of class stir::DataSymmetriesForDensels_PET_CartesianGrid \author Kris Thielemans - + */ #ifndef __stir_recon_buildblock_DataSymmetriesForDensels_PET_CartesianGrid_H__ #define __stir_recon_buildblock_DataSymmetriesForDensels_PET_CartesianGrid_H__ - #include "stir/recon_buildblock/DataSymmetriesForDensels.h" #include "stir/ProjDataInfo.h" //#include "stir/SymmetryOperations_PET_CartesianGrid.h" @@ -40,38 +39,37 @@ START_NAMESPACE_STIR -template class DiscretisedDensity; -template class DiscretisedDensityOnCartesianGrid; +template +class DiscretisedDensity; +template +class DiscretisedDensityOnCartesianGrid; /*! \ingroup recon_buildblock - \brief Symmetries appropriate for a (cylindrical) PET scanner, and + \brief Symmetries appropriate for a (cylindrical) PET scanner, and a discretised density on a Cartesian grid. All operations (except the constructor) are inline as timing of the methods of this class is critical. */ -class DataSymmetriesForDensels_PET_CartesianGrid : public DataSymmetriesForDensels -{ +class DataSymmetriesForDensels_PET_CartesianGrid : public DataSymmetriesForDensels { private: typedef DataSymmetriesForDensels base_type; typedef DataSymmetriesForDensels_PET_CartesianGrid self_type; -public: +public: DataSymmetriesForDensels_PET_CartesianGrid(const shared_ptr& proj_data_info_ptr, - const shared_ptr >& image_info_ptr); - + const shared_ptr>& image_info_ptr); - virtual + virtual #ifndef STIR_NO_COVARIANT_RETURN_TYPES - DataSymmetriesForDensels_PET_CartesianGrid * + DataSymmetriesForDensels_PET_CartesianGrid* #else - DataSymmetriesForDensels * + DataSymmetriesForDensels* #endif - clone() const; + clone() const; - bool - operator ==(const DataSymmetriesForDensels_PET_CartesianGrid&) const; + bool operator==(const DataSymmetriesForDensels_PET_CartesianGrid&) const; #if 0 TODO! @@ -80,33 +78,26 @@ class DataSymmetriesForDensels_PET_CartesianGrid : public DataSymmetriesForDense get_basic_densel_index_range() const; #endif - inline void - get_related_densels(vector&, const Densel& b) const; + inline void get_related_densels(vector&, const Densel& b) const; - inline int - num_related_densels(const Densel& b) const; + inline int num_related_densels(const Densel& b) const; - inline unique_ptr - find_symmetry_operation_from_basic_densel(Densel&) const; + inline unique_ptr find_symmetry_operation_from_basic_densel(Densel&) const; - inline bool - find_basic_densel(Densel& b) const; - + inline bool find_basic_densel(Densel& b) const; //! find out how many image planes there are for every scanner ring inline float get_num_planes_per_scanner_ring() const; - - //! find correspondence between axial_pos_num and image coordinates /*! z = num_planes_per_axial_pos * axial_pos_num + axial_pos_to_z_offset - - compute the offset by matching up the centre of the scanner + + compute the offset by matching up the centre of the scanner in the 2 coordinate systems */ inline float get_num_planes_per_axial_pos(const int segment_num) const; inline float get_axial_pos_to_z_offset(const int segment_num) const; - + private: const shared_ptr& proj_data_info_ptr; int num_planes; @@ -129,11 +120,9 @@ class DataSymmetriesForDensels_PET_CartesianGrid : public DataSymmetriesForDense cartesian_grid_info_ptr() const; #endif - virtual bool blindly_equals(const root_type * const) const; - - inline SymmetryOperation* - find_sym_op_general_densel( const int z, const int y, const int x) const; - + virtual bool blindly_equals(const root_type* const) const; + + inline SymmetryOperation* find_sym_op_general_densel(const int z, const int y, const int x) const; }; END_NAMESPACE_STIR diff --git a/src/include/stir_experimental/recon_buildblock/DataSymmetriesForDensels_PET_CartesianGrid.inl b/src/include/stir_experimental/recon_buildblock/DataSymmetriesForDensels_PET_CartesianGrid.inl index 80ebe1f829..afa559195c 100644 --- a/src/include/stir_experimental/recon_buildblock/DataSymmetriesForDensels_PET_CartesianGrid.inl +++ b/src/include/stir_experimental/recon_buildblock/DataSymmetriesForDensels_PET_CartesianGrid.inl @@ -31,97 +31,78 @@ cartesian_grid_info_ptr() const #endif float -DataSymmetriesForDensels_PET_CartesianGrid:: -get_num_planes_per_axial_pos(const int segment_num) const -{ +DataSymmetriesForDensels_PET_CartesianGrid::get_num_planes_per_axial_pos(const int segment_num) const { return static_cast(num_planes_per_axial_pos[segment_num]); } float -DataSymmetriesForDensels_PET_CartesianGrid:: -get_num_planes_per_scanner_ring() const -{ +DataSymmetriesForDensels_PET_CartesianGrid::get_num_planes_per_scanner_ring() const { return static_cast(num_planes_per_scanner_ring); } -float -DataSymmetriesForDensels_PET_CartesianGrid:: -get_axial_pos_to_z_offset(const int segment_num) const -{ +float +DataSymmetriesForDensels_PET_CartesianGrid::get_axial_pos_to_z_offset(const int segment_num) const { return axial_pos_to_z_offset[segment_num]; -} - - +} -SymmetryOperation* -DataSymmetriesForDensels_PET_CartesianGrid:: -find_sym_op_general_densel(const int z, const int y, const int x) const -{ - const int z_shift = z - (z%num_independent_planes); +SymmetryOperation* +DataSymmetriesForDensels_PET_CartesianGrid::find_sym_op_general_densel(const int z, const int y, const int x) const { + const int z_shift = z - (z % num_independent_planes); // TODO next shift might depend on the segment. // Solving this will require removing the axial_pos_num_shift argument from the symmetry operations - const int axial_pos_num_shift = z_shift/num_independent_planes; + const int axial_pos_num_shift = z_shift / num_independent_planes; const int view180 = num_views; - - if (x>=y && y>=0) // [0,45] - { - if (z_shift==0) - return new TrivialSymmetryOperation(); - else - return new SymmetryOperation_PET_CartesianGrid_z_shift(axial_pos_num_shift, z_shift); - } - else if ( x>=0 && y>x) // [ 45, 90] + + if (x >= y && y >= 0) // [0,45] + { + if (z_shift == 0) + return new TrivialSymmetryOperation(); + else + return new SymmetryOperation_PET_CartesianGrid_z_shift(axial_pos_num_shift, z_shift); + } else if (x >= 0 && y > x) // [ 45, 90] return new SymmetryOperation_PET_CartesianGrid_swap_xy_yx(view180, axial_pos_num_shift, z_shift); - else if (x<0 && y>-x) //[90, 135 ] + else if (x < 0 && y > -x) //[90, 135 ] return new SymmetryOperation_PET_CartesianGrid_swap_xmy_yx(view180, axial_pos_num_shift, z_shift); - else if ( x<0 && y>=0) // [ 135, 180] - { - assert(y<=-x); - return new SymmetryOperation_PET_CartesianGrid_swap_xmx(view180, axial_pos_num_shift, z_shift); - } - else if ( x<0 && y<=0 && -y<=-x ) // [ 180, 225] + else if (x < 0 && y >= 0) // [ 135, 180] + { + assert(y <= -x); + return new SymmetryOperation_PET_CartesianGrid_swap_xmx(view180, axial_pos_num_shift, z_shift); + } else if (x < 0 && y <= 0 && -y <= -x) // [ 180, 225] return new SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy(view180, axial_pos_num_shift, z_shift); - else if ( x<=0 && y<0) // [ 225, 270] - { - assert(-y>-x); - return new SymmetryOperation_PET_CartesianGrid_swap_xmy_ymx(view180, axial_pos_num_shift, z_shift); - } - else if ( x>0 && -y>x) // [ 270, 315] + else if (x <= 0 && y < 0) // [ 225, 270] + { + assert(-y > -x); + return new SymmetryOperation_PET_CartesianGrid_swap_xmy_ymx(view180, axial_pos_num_shift, z_shift); + } else if (x > 0 && -y > x) // [ 270, 315] return new SymmetryOperation_PET_CartesianGrid_swap_xy_ymx(view180, axial_pos_num_shift, z_shift); else // [ 315, 360] - { - assert(x>0 && y<0 && -y<=x); - return new SymmetryOperation_PET_CartesianGrid_swap_ymy(view180, axial_pos_num_shift, z_shift); - } + { + assert(x > 0 && y < 0 && -y <= x); + return new SymmetryOperation_PET_CartesianGrid_swap_ymy(view180, axial_pos_num_shift, z_shift); + } } - -bool -DataSymmetriesForDensels_PET_CartesianGrid:: -find_basic_densel(Densel& c) const -{ +bool +DataSymmetriesForDensels_PET_CartesianGrid::find_basic_densel(Densel& c) const { int& z = c[1]; int& y = c[2]; int& x = c[3]; - if (z==z%num_independent_planes && x>=0 && y>=0 && y<=x) + if (z == z % num_independent_planes && x >= 0 && y >= 0 && y <= x) return false; - z = z%num_independent_planes; - if (x<0) x = -x; - if (y<0) y = -y; - if (y>x) std::swap(x,y); + z = z % num_independent_planes; + if (x < 0) + x = -x; + if (y < 0) + y = -y; + if (y > x) + std::swap(x, y); return true; } - // TODO, optimise unique_ptr -DataSymmetriesForDensels_PET_CartesianGrid:: - find_symmetry_operation_from_basic_densel(Densel& c) const -{ - unique_ptr - sym_op( - find_sym_op_general_densel(c[1], c[2], c[3]) - ); +DataSymmetriesForDensels_PET_CartesianGrid::find_symmetry_operation_from_basic_densel(Densel& c) const { + unique_ptr sym_op(find_sym_op_general_densel(c[1], c[2], c[3])); #ifndef NDEBUG const Densel copy_original = c; #endif @@ -129,32 +110,27 @@ DataSymmetriesForDensels_PET_CartesianGrid:: #ifndef NDEBUG Densel copy = c; sym_op->transform_image_coordinates(copy); - assert(copy_original==copy); + assert(copy_original == copy); #endif return sym_op; } - int -DataSymmetriesForDensels_PET_CartesianGrid:: -num_related_densels(const Densel& b) const -{ +DataSymmetriesForDensels_PET_CartesianGrid::num_related_densels(const Densel& b) const { int num = 1; - if (b[3]!=0) + if (b[3] != 0) num *= 2; - if (b[2]!=0) + if (b[2] != 0) num *= 2; - if (abs(b[3])!=abs(b[2])) + if (abs(b[3]) != abs(b[2])) num *= 2; - num *= static_cast(ceil(static_cast(num_planes)/num_independent_planes)); + num *= static_cast(ceil(static_cast(num_planes) / num_independent_planes)); return num; } void -DataSymmetriesForDensels_PET_CartesianGrid:: -get_related_densels(vector& v, const Densel& d) const -{ +DataSymmetriesForDensels_PET_CartesianGrid::get_related_densels(vector& v, const Densel& d) const { #ifndef NDEBUG { Densel dcopy = d; @@ -163,54 +139,46 @@ get_related_densels(vector& v, const Densel& d) const #endif v.reserve(num_related_densels(d)); v.resize(0); - + const int x = d[3]; const int y = d[2]; - const int basic_z =d[1]; + const int basic_z = d[1]; { - for (int z=basic_z; z0) - { - for (int z=basic_z; z 0) { + for (int z = basic_z; z < num_planes; z += num_independent_planes) v.push_back(Densel(z, y, -x)); } - if (y>0) - { - for (int z=basic_z; z 0) { + for (int z = basic_z; z < num_planes; z += num_independent_planes) v.push_back(Densel(z, -y, x)); } - if (x>0 && y>0) - { - for (int z=basic_z; z 0 && y > 0) { + for (int z = basic_z; z < num_planes; z += num_independent_planes) v.push_back(Densel(z, -y, -x)); } - if (x!=y) + if (x != y) { { - { - for (int z=basic_z; z0) - { - for (int z=basic_z; z0) - { - for (int z=basic_z; z0 && y>0) - { - for (int z=basic_z; z 0) { + for (int z = basic_z; z < num_planes; z += num_independent_planes) + v.push_back(Densel(z, x, -y)); + } + if (y > 0) { + for (int z = basic_z; z < num_planes; z += num_independent_planes) + v.push_back(Densel(z, -x, y)); + } + if (x > 0 && y > 0) { + for (int z = basic_z; z < num_planes; z += num_independent_planes) + v.push_back(Densel(z, -x, -y)); + } + } assert(v.size() == static_cast(num_related_densels(d))); - } - + END_NAMESPACE_STIR diff --git a/src/include/stir_experimental/recon_buildblock/ParametricQuadraticPrior.h b/src/include/stir_experimental/recon_buildblock/ParametricQuadraticPrior.h index f8bd610650..ad171f3245 100644 --- a/src/include/stir_experimental/recon_buildblock/ParametricQuadraticPrior.h +++ b/src/include/stir_experimental/recon_buildblock/ParametricQuadraticPrior.h @@ -27,11 +27,9 @@ */ - #ifndef __stir_recon_buildblock_ParametricQuadraticPrior_H__ #define __stir_recon_buildblock_ParametricQuadraticPrior_H__ - #include "stir/RegisteredParsingObject.h" #include "stir/recon_buildblock/QuadraticPrior.h" #include "stir/recon_buildblock/PriorWithParabolicSurrogate.h" @@ -51,26 +49,26 @@ START_NAMESPACE_STIR A class in the GeneralisedPrior hierarchy. This implements a quadratic Gibbs prior. The gradient of the prior is computed as follows: - + \f[ g_r = \sum_dr w_{dr} (\lambda_r - \lambda_{r+dr}) * \kappa_r * \kappa_{r+dr} \f] where \f$\lambda\f$ is the image and \f$r\f$ and \f$dr\f$ are indices and the sum is over the neighbourhood where the weights \f$w_{dr}\f$ are non-zero. - The \f$\kappa\f$ image can be used to have spatially-varying penalties such as in + The \f$\kappa\f$ image can be used to have spatially-varying penalties such as in Jeff Fessler's papers. It should have identical dimensions to the image for which the penalty is computed. If \f$\kappa\f$ is not set, this class will effectively use 1 for all \f$\kappa\f$'s. - By default, a 3x3 or 3x3x3 neigbourhood is used where the weights are set to + By default, a 3x3 or 3x3x3 neigbourhood is used where the weights are set to x-voxel_size divided by the Euclidean distance between the points. - + \par Parsing These are the keywords that can be used in addition to the ones in GeneralPrior. \verbatim Quadratic Prior Parameters:= - ; next defaults to 0, set to 1 for 2D inverse Euclidean weights, 0 for 3D + ; next defaults to 0, set to 1 for 2D inverse Euclidean weights, 0 for 3D only 2D:= 0 ; next can be used to set weights explicitly. Needs to be a 3D array (of floats). ' value of only_2D is ignored @@ -81,54 +79,43 @@ START_NAMESPACE_STIR ; kappa filename:= ; use next parameter to get gradient images at every subiteration ; see class documentation - gradient filename prefix:= + gradient filename prefix:= END Quadratic Prior Parameters:= \endverbatim */ template -class ParametricQuadraticPrior: public - RegisteredParsingObject< ParametricQuadraticPrior, - GeneralisedPrior, - PriorWithParabolicSurrogate - > -{ - private: - typedef - RegisteredParsingObject< ParametricQuadraticPrior, - GeneralisedPrior, - PriorWithParabolicSurrogate > - base_type; - shared_ptr kappa_ptr; - VectorWithOffset > _single_quadratic_priors; - - public: +class ParametricQuadraticPrior : public RegisteredParsingObject, GeneralisedPrior, + PriorWithParabolicSurrogate> { +private: + typedef RegisteredParsingObject, GeneralisedPrior, + PriorWithParabolicSurrogate> + base_type; + shared_ptr kappa_ptr; + VectorWithOffset> _single_quadratic_priors; + +public: //! Name which will be used when parsing a GeneralisedPrior object - static const char * const registered_name; + static const char* const registered_name; - //! Default constructor + //! Default constructor ParametricQuadraticPrior(); //! Constructs it explicitly ParametricQuadraticPrior(const bool only_2D, float penalization_factor); - virtual bool - parabolic_surrogate_curvature_depends_on_argument() const - { return false; } - + virtual bool parabolic_surrogate_curvature_depends_on_argument() const { return false; } + //! compute the value of the function - double - compute_value(const TargetT ¤t_image_estimate); + double compute_value(const TargetT& current_image_estimate); - //! compute gradient - void compute_gradient(TargetT& prior_gradient, - const TargetT ¤t_image_estimate); + //! compute gradient + void compute_gradient(TargetT& prior_gradient, const TargetT& current_image_estimate); //! compute the parabolic surrogate for the prior /*! in the case of quadratic priors this will just be the sum of weighting coefficients*/ - void parabolic_surrogate_curvature(TargetT& parabolic_surrogate_curvature, - const TargetT ¤t_image_estimate); + void parabolic_surrogate_curvature(TargetT& parabolic_surrogate_curvature, const TargetT& current_image_estimate); #if 0 //! compute Hessian void compute_Hessian(TargetT& prior_Hessian_for_single_densel, @@ -136,59 +123,55 @@ class ParametricQuadraticPrior: public const TargetT ¤t_image_estimate); #endif - virtual Succeeded - add_multiplication_with_approximate_Hessian(TargetT& output, - const TargetT& input) const; + virtual Succeeded add_multiplication_with_approximate_Hessian(TargetT& output, const TargetT& input) const; //! get penalty weights for the neigbourhood - Array<3,float> get_weights() const; + Array<3, float> get_weights() const; //! set penalty weights for the neigbourhood - void set_weights(const Array<3,float>&); + void set_weights(const Array<3, float>&); //! get current kappa image /*! \warning As this function returns a shared_ptr, this is dangerous. You should not modify the image by manipulating the image refered to by this pointer. Unpredictable results will occur. */ - shared_ptr get_kappa_sptr() const; + shared_ptr get_kappa_sptr() const; //! set kappa image - void set_kappa_sptr(const shared_ptr&); + void set_kappa_sptr(const shared_ptr&); //! Has to be called before using this object - virtual Succeeded set_up(shared_ptr > const& target_sptr); - + virtual Succeeded set_up(shared_ptr> const& target_sptr); + protected: //! can be set during parsing to restrict the weights to the 2D case bool only_2D; //! filename prefix for outputing the gradient whenever compute_gradient() is called. /*! An internal counter is used to keep track of the number of times the - gradient is computed. The filename will be constructed by concatenating + gradient is computed. The filename will be constructed by concatenating gradient_filename_prefix and the counter. */ string gradient_filename_prefix; //! penalty weights - /*! + /*! \todo This member is mutable at present because some const functions initialise it. That initialisation should be moved to a new set_up() function. */ - mutable Array<3,float> weights; + mutable Array<3, float> weights; //! Filename for the \f$\kappa\f$ image that will be read by post_processing() - std::string kappa_filename; //CHECK IF THERE IS A CONFILCT WHEN GIVING A KAPPA FILE...// THINK IF IT IS BETTER TO ESTIMATE KAPPA FILE IN THE CODE... + std::string kappa_filename; // CHECK IF THERE IS A CONFILCT WHEN GIVING A KAPPA FILE...// THINK IF IT IS BETTER TO ESTIMATE + // KAPPA FILE IN THE CODE... //! Check that the prior is ready to be used - virtual void check(DiscretisedDensity<3,elemT> const& current_image_estimate) const; + virtual void check(DiscretisedDensity<3, elemT> const& current_image_estimate) const; virtual void set_defaults(); virtual void initialise_keymap(); virtual bool post_processing(); - }; - END_NAMESPACE_STIR #endif - diff --git a/src/include/stir_experimental/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndDynamicProjData.h b/src/include/stir_experimental/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndDynamicProjData.h index 15cca6baf6..0fe93ebcbc 100644 --- a/src/include/stir_experimental/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndDynamicProjData.h +++ b/src/include/stir_experimental/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndDynamicProjData.h @@ -35,11 +35,11 @@ #include "stir/DynamicDiscretisedDensity.h" START_NAMESPACE_STIR -// ChT::ToDo: If this class appears to be useful I have to define some of the functions that are not specifically defined, yet. +// ChT::ToDo: If this class appears to be useful I have to define some of the functions that are not specifically defined, yet. /*! \ingroup GeneralisedObjectiveFunction - \brief a base class for LogLikelihood of independent Poisson variables + \brief a base class for LogLikelihood of independent Poisson variables where the mean values are linear combinations of the frames. \par Parameters for parsing @@ -47,22 +47,20 @@ START_NAMESPACE_STIR */ template -class PoissonLogLikelihoodWithLinearModelForMeanAndDynamicProjData: -public RegisteredParsingObject, - GeneralisedObjectiveFunction, - PoissonLogLikelihoodWithLinearModelForMean > -{ - private: - typedef RegisteredParsingObject, - GeneralisedObjectiveFunction, - PoissonLogLikelihoodWithLinearModelForMean > base_type; - typedef PoissonLogLikelihoodWithLinearModelForMeanAndProjData > SingleFrameObjFunc ; +class PoissonLogLikelihoodWithLinearModelForMeanAndDynamicProjData + : public RegisteredParsingObject, + GeneralisedObjectiveFunction, PoissonLogLikelihoodWithLinearModelForMean> { +private: + typedef RegisteredParsingObject, + GeneralisedObjectiveFunction, PoissonLogLikelihoodWithLinearModelForMean> + base_type; + typedef PoissonLogLikelihoodWithLinearModelForMeanAndProjData> SingleFrameObjFunc; VectorWithOffset _single_frame_obj_funcs; - public: - + +public: //! Name which will be used when parsing a GeneralisedObjectiveFunction object - static const char * const registered_name; -#if 0 // ChT::ToDo + static const char* const registered_name; +#if 0 // ChT::ToDo PoissonLogLikelihoodWithLinearModelForMeanAndDynamicProjData(); //! Returns a pointer to a newly allocated target object (with 0 data). @@ -100,7 +98,7 @@ public RegisteredParsingObject _additive_dyn_proj_data_sptr; /*! the normalisation or/and attenuation data */ @@ -154,14 +152,13 @@ public RegisteredParsingObject class Viewgram; +template +class Viewgram; /*! \brief A very preliminary class that first forward projects, and then smooths the viewgrams */ -class PostsmoothingForwardProjectorByBin : - public - RegisteredParsingObject -{ +class PostsmoothingForwardProjectorByBin + : public RegisteredParsingObject { public: //! Name which will be used when parsing a PostsmoothingForwardProjectorByBin object - static const char * const registered_name; + static const char* const registered_name; //! Default constructor (calls set_defaults()) PostsmoothingForwardProjectorByBin(); //! Stores all necessary geometric info /*! Note that the density_info_ptr is not stored in this object. It's only used to get some info on sizes etc. - */ - virtual void set_up( - const shared_ptr& proj_data_info_ptr, - const shared_ptr >& density_info_ptr // TODO should be Info only - ); + */ + virtual void set_up(const shared_ptr& proj_data_info_ptr, + const shared_ptr>& density_info_ptr // TODO should be Info only + ); - - PostsmoothingForwardProjectorByBin( - const shared_ptr& original_forward_projector_ptr, - const VectorWithOffset& tangential_kernel, - const VectorWithOffset& axial_kernel, - const bool smooth_segment_0_axially = false); + PostsmoothingForwardProjectorByBin(const shared_ptr& original_forward_projector_ptr, + const VectorWithOffset& tangential_kernel, + const VectorWithOffset& axial_kernel, const bool smooth_segment_0_axially = false); // Informs on which symmetries the projector handles // It should get data related by at least those symmetries. // Otherwise, a run-time error will occur (unless the derived // class has other behaviour). - const DataSymmetriesForViewSegmentNumbers * get_symmetries_used() const; - + const DataSymmetriesForViewSegmentNumbers* get_symmetries_used() const; private: - shared_ptr original_forward_projector_ptr; VectorWithOffset tang_kernel; VectorWithOffset ax_kernel; @@ -77,19 +69,14 @@ class PostsmoothingForwardProjectorByBin : std::vector ax_kernel_double; #ifdef STIR_PROJECTORS_AS_V3 - void actual_forward_project(RelatedViewgrams&, - const DiscretisedDensity<3,float>&, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num); + void actual_forward_project(RelatedViewgrams&, const DiscretisedDensity<3, float>&, const int min_axial_pos_num, + const int max_axial_pos_num, const int min_tangential_pos_num, const int max_tangential_pos_num); #endif /// Actual forward project where input has already been set. - void actual_forward_project(RelatedViewgrams&, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num); - void smooth(Viewgram&, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) const; - + void actual_forward_project(RelatedViewgrams&, const int min_axial_pos_num, const int max_axial_pos_num, + const int min_tangential_pos_num, const int max_tangential_pos_num); + void smooth(Viewgram&, const int min_axial_pos_num, const int max_axial_pos_num, const int min_tangential_pos_num, + const int max_tangential_pos_num) const; virtual void set_defaults(); virtual void initialise_keymap(); diff --git a/src/include/stir_experimental/recon_buildblock/ProjMatrixByBinSinglePhoton.h b/src/include/stir_experimental/recon_buildblock/ProjMatrixByBinSinglePhoton.h index 9da5fc078d..f19a6946a0 100644 --- a/src/include/stir_experimental/recon_buildblock/ProjMatrixByBinSinglePhoton.h +++ b/src/include/stir_experimental/recon_buildblock/ProjMatrixByBinSinglePhoton.h @@ -4,9 +4,9 @@ \file \ingroup recon_buildblock - \brief ProjMatrixByBinSinglePhoton's definition + \brief ProjMatrixByBinSinglePhoton's definition - \author Kris + \author Kris */ /* @@ -22,15 +22,14 @@ #include "stir/CartesianCoordinate3D.h" #include "stir/shared_ptr.h" - - START_NAMESPACE_STIR -template class DiscretisedDensity; +template +class DiscretisedDensity; /*! \ingroup recon_buildblock - \brief a 'projection matrix' to implement a model for a single + \brief a 'projection matrix' to implement a model for a single photon acquisition in terms of the detector efficiencies. \todo This is a horrible work-around for the fact that STIR currently @@ -38,49 +37,35 @@ template class DiscretisedDensity; */ -class ProjMatrixByBinSinglePhoton : - public RegisteredParsingObject< - ProjMatrixByBinSinglePhoton, - ProjMatrixByBin, - ProjMatrixByBin - > -{ -public : - //! Name which will be used when parsing a ProjMatrixByBin object - static const char * const registered_name; +class ProjMatrixByBinSinglePhoton + : public RegisteredParsingObject { +public: + //! Name which will be used when parsing a ProjMatrixByBin object + static const char* const registered_name; //! Default constructor (calls set_defaults()) ProjMatrixByBinSinglePhoton(); //! Stores all necessary geometric info /*! Note that the density_info_ptr is not stored in this object. It's only used to get some info on sizes etc. - */ - virtual void set_up( - const shared_ptr& proj_data_info_ptr, - const shared_ptr >& density_info_ptr // TODO should be Info only - ); + */ + virtual void set_up(const shared_ptr& proj_data_info_ptr, + const shared_ptr>& density_info_ptr // TODO should be Info only + ); private: - // explicitly list necessary members for image details (should use an Info object instead) CartesianCoordinate3D min_index; CartesianCoordinate3D max_index; shared_ptr proj_data_info_ptr; + virtual void calculate_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin&) const; - virtual void - calculate_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin&) const; - - virtual void set_defaults(); - virtual void initialise_keymap(); - + virtual void set_defaults(); + virtual void initialise_keymap(); }; END_NAMESPACE_STIR #endif - - - diff --git a/src/include/stir_experimental/recon_buildblock/ProjMatrixByBinUsingSolidAngle.h b/src/include/stir_experimental/recon_buildblock/ProjMatrixByBinUsingSolidAngle.h index 1973f1637c..0259b21305 100644 --- a/src/include/stir_experimental/recon_buildblock/ProjMatrixByBinUsingSolidAngle.h +++ b/src/include/stir_experimental/recon_buildblock/ProjMatrixByBinUsingSolidAngle.h @@ -4,9 +4,9 @@ \file \ingroup recon_buildblock - \brief ProjMatrixByBinUsingSolidAngle's definition + \brief ProjMatrixByBinUsingSolidAngle's definition - \author Kris + \author Kris */ /* @@ -22,65 +22,50 @@ #include "stir/CartesianCoordinate3D.h" #include "stir/shared_ptr.h" - - START_NAMESPACE_STIR -template class DiscretisedDensity; +template +class DiscretisedDensity; /*! \ingroup recon_buildblock \brief Computes projection matrix elements for VoxelsOnCartesianGrid images - by using a Solid Angle model. + by using a Solid Angle model. */ -class ProjMatrixByBinUsingSolidAngle : - public RegisteredParsingObject< - ProjMatrixByBinUsingSolidAngle, - ProjMatrixByBin, - ProjMatrixByBin - > -{ -public : - //! Name which will be used when parsing a ProjMatrixByBin object - static const char * const registered_name; +class ProjMatrixByBinUsingSolidAngle + : public RegisteredParsingObject { +public: + //! Name which will be used when parsing a ProjMatrixByBin object + static const char* const registered_name; //! Default constructor (calls set_defaults()) ProjMatrixByBinUsingSolidAngle(); //! Stores all necessary geometric info /*! Note that the density_info_ptr is not stored in this object. It's only used to get some info on sizes etc. - */ - virtual void set_up( - const shared_ptr& proj_data_info_ptr, - const shared_ptr >& density_info_ptr // TODO should be Info only - ); + */ + virtual void set_up(const shared_ptr& proj_data_info_ptr, + const shared_ptr>& density_info_ptr // TODO should be Info only + ); private: - // explicitly list necessary members for image details (should use an Info object instead) // ideally these should be const, but I have some trouble initialising them in that case CartesianCoordinate3D voxel_size; - CartesianCoordinate3D origin; + CartesianCoordinate3D origin; CartesianCoordinate3D min_index; CartesianCoordinate3D max_index; shared_ptr proj_data_info_ptr; + virtual void calculate_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin&) const; - virtual void - calculate_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin&) const; - - virtual void set_defaults(); - virtual void initialise_keymap(); - + virtual void set_defaults(); + virtual void initialise_keymap(); }; END_NAMESPACE_STIR #endif - - - diff --git a/src/include/stir_experimental/recon_buildblock/ProjMatrixByBinWithPositronRange.h b/src/include/stir_experimental/recon_buildblock/ProjMatrixByBinWithPositronRange.h index 7e76eb2dcd..999573b7b5 100644 --- a/src/include/stir_experimental/recon_buildblock/ProjMatrixByBinWithPositronRange.h +++ b/src/include/stir_experimental/recon_buildblock/ProjMatrixByBinWithPositronRange.h @@ -4,9 +4,9 @@ \file \ingroup recon_buildblock - \brief ProjMatrixByBinWithPositronRange's definition + \brief ProjMatrixByBinWithPositronRange's definition - \author Kris + \author Kris */ /* @@ -22,47 +22,39 @@ #include "stir/CartesianCoordinate3D.h" #include "stir/shared_ptr.h" - - START_NAMESPACE_STIR -template class DiscretisedDensity; +template +class DiscretisedDensity; /*! \ingroup recon_buildblock \brief Computes projection matrix elements for VoxelsOnCartesianGrid images - by using a Solid Angle model. + by using a Solid Angle model. */ -class ProjMatrixByBinWithPositronRange : - public RegisteredParsingObject< - ProjMatrixByBinWithPositronRange, - ProjMatrixByBin, - ProjMatrixByBin - > -{ -public : - //! Name which will be used when parsing a ProjMatrixByBin object - static const char * const registered_name; +class ProjMatrixByBinWithPositronRange + : public RegisteredParsingObject { +public: + //! Name which will be used when parsing a ProjMatrixByBin object + static const char* const registered_name; //! Default constructor (calls set_defaults()) ProjMatrixByBinWithPositronRange(); //! Stores all necessary geometric info /*! Note that the density_info_ptr is not stored in this object. It's only used to get some info on sizes etc. - */ - virtual void set_up( - const shared_ptr& proj_data_info_ptr, - const shared_ptr >& density_info_ptr // TODO should be Info only - ); + */ + virtual void set_up(const shared_ptr& proj_data_info_ptr, + const shared_ptr>& density_info_ptr // TODO should be Info only + ); private: - // explicitly list necessary members for image details (should use an Info object instead) // ideally these should be const, but I have some trouble initialising them in that case CartesianCoordinate3D voxel_size; - CartesianCoordinate3D origin; + CartesianCoordinate3D origin; CartesianCoordinate3D min_index; CartesianCoordinate3D max_index; @@ -73,23 +65,15 @@ public : int positron_range_zoom; int positron_num_samples; - shared_ptr proj_data_info_ptr; + virtual void calculate_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin&) const; - virtual void - calculate_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin&) const; - - virtual void set_defaults(); - virtual void initialise_keymap(); - virtual bool post_processing(); - + virtual void set_defaults(); + virtual void initialise_keymap(); + virtual bool post_processing(); }; END_NAMESPACE_STIR #endif - - - diff --git a/src/include/stir_experimental/recon_buildblock/ProjMatrixByDensel.h b/src/include/stir_experimental/recon_buildblock/ProjMatrixByDensel.h index a8328383a5..15150f02ae 100644 --- a/src/include/stir_experimental/recon_buildblock/ProjMatrixByDensel.h +++ b/src/include/stir_experimental/recon_buildblock/ProjMatrixByDensel.h @@ -7,19 +7,17 @@ /*! \file - \ingroup recon_buildblock + \ingroup recon_buildblock \brief declaration of ProjMatrixByDensel and its helpers classes - + \author Kris Thielemans - + */ /* Copyright (C) 2000- 2011, Hammersmith Imanet Ltd See STIR/LICENSE.txt for details */ - - #include "stir/RegisteredObject.h" #include "stir/recon_buildblock/ProjMatrixElemsForOneDensel.h" #include "stir/recon_buildblock/DataSymmetriesForDensels.h" @@ -30,61 +28,55 @@ // define a local preprocessor symbol to keep code relatively clean #ifdef STIR_NO_MUTABLE -#define STIR_MUTABLE_CONST +# define STIR_MUTABLE_CONST #else -#define STIR_MUTABLE_CONST const +# define STIR_MUTABLE_CONST const #endif START_NAMESPACE_STIR - -//template class RelatedViewgrams; -//class Densel; -template class DiscretisedDensity; + +// template class RelatedViewgrams; +// class Densel; +template +class DiscretisedDensity; /*! \ingroup recon_buildblock -\brief - This is the (abstract) base class for all projection matrices +\brief + This is the (abstract) base class for all projection matrices which are organised by 'Densel'. - This class provides essentially only 2 public members: a method to get a - 'row' of the matrix, and a method to get information on the symmetries. + This class provides essentially only 2 public members: a method to get a + 'row' of the matrix, and a method to get information on the symmetries. Currently, the class provides for some (basic) caching. - This functionality will probably be moved to a new class + This functionality will probably be moved to a new class ProjMatrixByDenselWithCache. (TODO) */ -class ProjMatrixByDensel : public RegisteredObject -{ +class ProjMatrixByDensel : public RegisteredObject { public: - virtual ~ProjMatrixByDensel() {} //! To be called before any calculation is performed /*! Note that get_proj_matrix_elems_for_one_Densel() will expect objects of compatible sizes and other info. */ - virtual void set_up( - const shared_ptr& proj_data_info_ptr, - const shared_ptr >& density_info_ptr // TODO should be Info only - ) = 0; + virtual void set_up(const shared_ptr& proj_data_info_ptr, + const shared_ptr>& density_info_ptr // TODO should be Info only + ) = 0; //! get a pointer to an object encoding all symmetries that are used by this ProjMatrixByDensel virtual const DataSymmetriesForDensels* get_symmetries_ptr() const = 0; - - + //! The main method for getting a column of the matrix. - /*! + /*! The ProjMatrixElemsForOneDensel argument will be overwritten (i.e. data is NOT appended). - + The implementation is inline as it just gets it in - terms of the cached__proj_matrix_elems_for_one_densel or + terms of the cached__proj_matrix_elems_for_one_densel or calculate_proj_matrix_elems_for_one_densel.*/ - inline void - get_proj_matrix_elems_for_one_densel( - ProjMatrixElemsForOneDensel&, - const Densel&) STIR_MUTABLE_CONST; - + inline void get_proj_matrix_elems_for_one_densel(ProjMatrixElemsForOneDensel&, const Densel&) STIR_MUTABLE_CONST; + #if 0 // TODO /*! \brief Facility to write the 'independent' part of the matrix to file. @@ -94,82 +86,67 @@ class ProjMatrixByDensel : public RegisteredObject */ virtual void write_to_file_by_densel( const char * const file_name_without_extension) const; -#endif +#endif // TODO implement this one at some point ? /* virtual void write_to_file_by_bin( const char * const file_name_without_extension); */ - - //void set_maximum_cache_size(const unsigned long size){;} - void enable_cache(bool v){cache_disabled = !v;} + + // void set_maximum_cache_size(const unsigned long size){;} + void enable_cache(bool v) { cache_disabled = !v; } /* TODO void set_subset_usage(const SubsetInfo&, const int num_access_times); */ - - - -protected: - +protected: //! default ctor (enables caching) - ProjMatrixByDensel(); - + ProjMatrixByDensel(); + /*! \brief This method needs to be implemented in the derived class. - + Densel-coordinates are obtained via the ProjMatrixElemsForOneDensel::get_Densel() method. Note that 'calculate' could just as well mean 'get from file' */ - virtual void - calculate_proj_matrix_elems_for_one_densel( - ProjMatrixElemsForOneDensel& - ) const = 0; - + virtual void calculate_proj_matrix_elems_for_one_densel(ProjMatrixElemsForOneDensel&) const = 0; + /////////////////////////////// caching stuff ////////////////////// - bool cache_disabled; + bool cache_disabled; /*! \brief The method that tries to get data from the cache. - + If it succeeds, it overwrites the ProjMatrixElemsForOneDensel parameter and returns Succeeded::yes, otherwise it does not touch the ProjMatrixElemsForOneDensel and returns Succeeded::false. */ - Succeeded get_cached_proj_matrix_elems_for_one_densel( - ProjMatrixElemsForOneDensel& - ) const; - + Succeeded get_cached_proj_matrix_elems_for_one_densel(ProjMatrixElemsForOneDensel&) const; + //! The method to store data in the cache. - inline void cache_proj_matrix_elems_for_one_densel( const ProjMatrixElemsForOneDensel&) - STIR_MUTABLE_CONST; + inline void cache_proj_matrix_elems_for_one_densel(const ProjMatrixElemsForOneDensel&) STIR_MUTABLE_CONST; private: - typedef unsigned int CacheKey; #ifndef STIR_NO_NAMESPACES - typedef std::map MapProjMatrixElemsForOneDensel; + typedef std::map MapProjMatrixElemsForOneDensel; #else - typedef map MapProjMatrixElemsForOneDensel; - #endif + typedef map MapProjMatrixElemsForOneDensel; +#endif typedef MapProjMatrixElemsForOneDensel::iterator MapProjMatrixElemsForOneDenselIterator; typedef MapProjMatrixElemsForOneDensel::const_iterator const_MapProjMatrixElemsForOneDenselIterator; - - //! collection of ProjMatrixElemsForOneDensel (internal cache ) + + //! collection of ProjMatrixElemsForOneDensel (internal cache ) #ifndef STIR_NO_MUTABLE mutable #endif - MapProjMatrixElemsForOneDensel cache_collection; - + MapProjMatrixElemsForOneDensel cache_collection; + //! create the key for caching inline static CacheKey cache_key(const Densel& Densel); - - }; - - END_NAMESPACE_STIR #include "stir_experimental/recon_buildblock/ProjMatrixByDensel.inl" @@ -177,6 +154,3 @@ END_NAMESPACE_STIR #undef STIR_MUTABLE_CONST #endif // __ProjMatrixByDensel_H__ - - - diff --git a/src/include/stir_experimental/recon_buildblock/ProjMatrixByDensel.inl b/src/include/stir_experimental/recon_buildblock/ProjMatrixByDensel.inl index 8a94893542..e2281f6dd5 100644 --- a/src/include/stir_experimental/recon_buildblock/ProjMatrixByDensel.inl +++ b/src/include/stir_experimental/recon_buildblock/ProjMatrixByDensel.inl @@ -20,79 +20,64 @@ START_NAMESPACE_STIR -void ProjMatrixByDensel:: -get_proj_matrix_elems_for_one_densel( - ProjMatrixElemsForOneDensel& probabilities, - const Densel& densel) STIR_MUTABLE_CONST -{ +void +ProjMatrixByDensel::get_proj_matrix_elems_for_one_densel(ProjMatrixElemsForOneDensel& probabilities, + const Densel& densel) STIR_MUTABLE_CONST { // set to empty probabilities.erase(); - // find basic densel Densel basic_densel = densel; - - unique_ptr symm_ptr = - get_symmetries_ptr()->find_symmetry_operation_from_basic_densel(basic_densel); - + + unique_ptr symm_ptr = get_symmetries_ptr()->find_symmetry_operation_from_basic_densel(basic_densel); + probabilities.set_densel(basic_densel); - // check if in cache - if (get_cached_proj_matrix_elems_for_one_densel(probabilities) == - Succeeded::no) - + // check if in cache + if (get_cached_proj_matrix_elems_for_one_densel(probabilities) == Succeeded::no) + { // call 'calculate' just for the basic densel calculate_proj_matrix_elems_for_one_densel(probabilities); #ifndef NDEBUG probabilities.check_state(); #endif - cache_proj_matrix_elems_for_one_densel(probabilities); - } - + cache_proj_matrix_elems_for_one_densel(probabilities); + } + // now transform to original densel - symm_ptr->transform_proj_matrix_elems_for_one_densel(probabilities); - + symm_ptr->transform_proj_matrix_elems_for_one_densel(probabilities); } /*! -\warning Preconditions: +\warning Preconditions:
    • all coordinates non-negative
    • segment_num coded in 8 bits
    • view coded in 10 bits
    • axial_pos_num in 6 bits -
    • tangential_pos_num in 8 bits +
    • tangential_pos_num in 8 bits
    */ ProjMatrixByDensel::CacheKey -ProjMatrixByDensel::cache_key(const Densel& densel) -{ +ProjMatrixByDensel::cache_key(const Densel& densel) { assert(densel[1] >= 0); - assert(densel[1] < (1<<10)); + assert(densel[1] < (1 << 10)); assert(densel[2] >= 0); - assert(densel[2] < (1<<10)); + assert(densel[2] < (1 << 10)); assert(densel[3] >= 0); - assert(densel[3] < (1<<10)); - return (CacheKey)( - (static_cast(densel[1])<< 20) - | (static_cast(densel[2]) << 10) - | (static_cast(densel[3])) ); -} - - - -//! insert matrix elements for one densel into the cache collection -void -ProjMatrixByDensel:: -cache_proj_matrix_elems_for_one_densel( - const ProjMatrixElemsForOneDensel& probabilities) STIR_MUTABLE_CONST -{ - if ( cache_disabled ) return; - - // insert probabilities into the collection - cache_collection.insert(MapProjMatrixElemsForOneDensel::value_type( cache_key(probabilities.get_densel()), probabilities)); - + assert(densel[3] < (1 << 10)); + return (CacheKey)((static_cast(densel[1]) << 20) | (static_cast(densel[2]) << 10) | + (static_cast(densel[3]))); } +//! insert matrix elements for one densel into the cache collection +void +ProjMatrixByDensel::cache_proj_matrix_elems_for_one_densel(const ProjMatrixElemsForOneDensel& probabilities) STIR_MUTABLE_CONST { + if (cache_disabled) + return; + + // insert probabilities into the collection + cache_collection.insert(MapProjMatrixElemsForOneDensel::value_type(cache_key(probabilities.get_densel()), probabilities)); +} END_NAMESPACE_STIR diff --git a/src/include/stir_experimental/recon_buildblock/ProjMatrixByDenselOnCartesianGridUsingElement.h b/src/include/stir_experimental/recon_buildblock/ProjMatrixByDenselOnCartesianGridUsingElement.h index 47baf865db..f17347df35 100644 --- a/src/include/stir_experimental/recon_buildblock/ProjMatrixByDenselOnCartesianGridUsingElement.h +++ b/src/include/stir_experimental/recon_buildblock/ProjMatrixByDenselOnCartesianGridUsingElement.h @@ -4,7 +4,7 @@ \file \ingroup recon_buildblock - \brief ProjMatrixByDenselOnCartesianGridUsingElement's definition + \brief ProjMatrixByDenselOnCartesianGridUsingElement's definition \author Kris Thielemans @@ -21,54 +21,49 @@ #include "stir/CartesianCoordinate3D.h" #include "stir/shared_ptr.h" - - START_NAMESPACE_STIR -template class DiscretisedDensity; +template +class DiscretisedDensity; /*! \ingroup recon_buildblock \brief Computes projection matrix elements for VoxelsOnCartesianGrid images - by using a Length of Intersection (LOI) model. + by using a Length of Intersection (LOI) model. Currently, the LOIs are divided by voxel_size.x(), unless NEWSCALE is - #defined during compilation time of ProjMatrixByDenselOnCartesianGridUsingElement.cxx. + #defined during compilation time of ProjMatrixByDenselOnCartesianGridUsingElement.cxx. If the z voxel size is exactly twice the sampling in axial direction, multiple LORs are used, to avoid missing voxels. (TODOdoc describe how). - Currently, a FOV is used which is circular, and is slightly 'inside' the + Currently, a FOV is used which is circular, and is slightly 'inside' the image (i.e. the radius is about 1 voxel smaller than the maximum possible). The implementation uses RayTraceVoxelsOnCartesianGrid(). \warning Only appropriate for VoxelsOnCartesianGrid type of images (otherwise a run-time error occurs). - - \warning Current implementation assumes that x,y voxel sizes are at least as + + \warning Current implementation assumes that x,y voxel sizes are at least as large as the sampling in tangential direction, and that z voxel size is either smaller than or exactly twice the sampling in axial direction of the segments. */ -class ProjMatrixByDenselOnCartesianGridUsingElement : - public ProjMatrixByDensel -{ -public : - +class ProjMatrixByDenselOnCartesianGridUsingElement : public ProjMatrixByDensel { +public: //! Stores necessary geometric info /*! This function \c hsd to be called by any derived class. - Note that the density_info_ptr is not stored in this object. It's only + Note that the density_info_ptr is not stored in this object. It's only used to get some info on sizes etc. Currently, the proj_data_info_ptr argument is not used. */ - virtual void set_up( - const shared_ptr& proj_data_info_ptr, - const shared_ptr >& density_info_ptr // TODO should be Info only - ); + virtual void set_up(const shared_ptr& proj_data_info_ptr, + const shared_ptr>& density_info_ptr // TODO should be Info only + ); //! this member computes a single element of the projection matrix /*! \param bin The bin-coordinates specifying the row of the projection matrix @@ -78,39 +73,33 @@ public : Ideally, get_element should be implemented such that tiny elements are truncated to 0, to avoid storing (and using) elements that not really contribute to the result. - + \warning Currently, the densel_ctr has to be in mm and w.r.t. the centre of the scanner. This is a bad idea (it should use dicrete coordinates) and so will change in the future. It's there now for the RT projector to avoid recomputing the coordinates per mm fo revery bin. */ - virtual float - get_element(const Bin& bin, - const CartesianCoordinate3D& densel_ctr) const = 0; + virtual float get_element(const Bin& bin, const CartesianCoordinate3D& densel_ctr) const = 0; + protected: shared_ptr proj_data_info_ptr; // explicitly list necessary members for image details (should use an Info object instead) CartesianCoordinate3D grid_spacing; - CartesianCoordinate3D origin; + CartesianCoordinate3D origin; float min_z_index; float max_z_index; //! Calculates all non-zero elements for a particular densel /*! This implementation uses the get_element() member. It uses a generic - way of finding non-zero elements, which is slow but makes only a + way of finding non-zero elements, which is slow but makes only a few assumptions. TODO more doc. */ - void calculate_proj_matrix_elems_for_one_densel(ProjMatrixElemsForOneDensel &) const; - - + void calculate_proj_matrix_elems_for_one_densel(ProjMatrixElemsForOneDensel&) const; }; END_NAMESPACE_STIR #endif - - - diff --git a/src/include/stir_experimental/recon_buildblock/ProjMatrixByDenselUsingRayTracing.h b/src/include/stir_experimental/recon_buildblock/ProjMatrixByDenselUsingRayTracing.h index e3bb0adef7..4e7f11c777 100644 --- a/src/include/stir_experimental/recon_buildblock/ProjMatrixByDenselUsingRayTracing.h +++ b/src/include/stir_experimental/recon_buildblock/ProjMatrixByDenselUsingRayTracing.h @@ -4,7 +4,7 @@ \file \ingroup recon_buildblock - \brief ProjMatrixByDenselUsingRayTracing's definition + \brief ProjMatrixByDenselUsingRayTracing's definition \author Kris Thielemans @@ -20,64 +20,58 @@ #include "stir_experimental/recon_buildblock/ProjMatrixByDenselOnCartesianGridUsingElement.h" #include "stir/CartesianCoordinate3D.h" - - START_NAMESPACE_STIR -template class DiscretisedDensity; +template +class DiscretisedDensity; class DataSymmetriesForDensels_PET_CartesianGrid; /*! \ingroup recon_buildblock \brief Computes projection matrix elements for VoxelsOnCartesianGrid images - by using a Length of Intersection (LOI) model. + by using a Length of Intersection (LOI) model. Currently, the LOIs are divided by voxel_size.x(), unless NEWSCALE is - #defined during compilation time of ProjMatrixByDenselUsingRayTracing.cxx. + #defined during compilation time of ProjMatrixByDenselUsingRayTracing.cxx. If the z voxel size is exactly twice the sampling in axial direction, multiple LORs are used, to avoid missing voxels. (TODOdoc describe how). - Currently, a FOV is used which is circular, and is slightly 'inside' the + Currently, a FOV is used which is circular, and is slightly 'inside' the image (i.e. the radius is about 1 voxel smaller than the maximum possible). The implementation uses RayTraceVoxelsOnCartesianGrid(). \warning Only appropriate for VoxelsOnCartesianGrid type of images (otherwise a run-time error occurs). - - \warning Current implementation assumes that x,y voxel sizes are at least as + + \warning Current implementation assumes that x,y voxel sizes are at least as large as the sampling in tangential direction, and that z voxel size is either smaller than or exactly twice the sampling in axial direction of the segments. */ -class ProjMatrixByDenselUsingRayTracing : - public RegisteredParsingObject< - ProjMatrixByDenselUsingRayTracing, - ProjMatrixByDenselOnCartesianGridUsingElement - > -{ +class ProjMatrixByDenselUsingRayTracing + : public RegisteredParsingObject { typedef ProjMatrixByDenselOnCartesianGridUsingElement base_type; -public : - //! Name which will be used when parsing a ProjMatrixByDensel object - static const char * const registered_name; + +public: + //! Name which will be used when parsing a ProjMatrixByDensel object + static const char* const registered_name; //! Default constructor (calls set_defaults()) ProjMatrixByDenselUsingRayTracing(); //! Stores all necessary geometric info /*! Note that the density_info_ptr is not stored in this object. It's only used to get some info on sizes etc. - */ - virtual void set_up( - const shared_ptr& proj_data_info_ptr, - const shared_ptr >& density_info_ptr // TODO should be Info only - ); + */ + virtual void set_up(const shared_ptr& proj_data_info_ptr, + const shared_ptr>& density_info_ptr // TODO should be Info only + ); - virtual const DataSymmetriesForDensels* get_symmetries_ptr() const; + virtual const DataSymmetriesForDensels* get_symmetries_ptr() const; - virtual float - get_element(const Bin&, const CartesianCoordinate3D&) const; + virtual float get_element(const Bin&, const CartesianCoordinate3D&) const; private: shared_ptr symmetries_ptr; @@ -88,12 +82,12 @@ public : //! variable that determines how many rays will be traced in tangential direction for one bin int num_tangential_LORs; //! variable that determines if interleaved sinogram coordinates are used or not. - bool use_actual_detector_boundaries; + bool use_actual_detector_boundaries; // explicitly list necessary members for image details (should use an Info object instead) // ideally these should be const, but I have some trouble initialising them in that case CartesianCoordinate3D voxel_size; - CartesianCoordinate3D origin; + CartesianCoordinate3D origin; CartesianCoordinate3D min_index; CartesianCoordinate3D max_index; @@ -101,17 +95,11 @@ public : float yhalfsize; float zhalfsize; - - - virtual void set_defaults(); - virtual void initialise_keymap(); - virtual bool post_processing(); - + virtual void set_defaults(); + virtual void initialise_keymap(); + virtual bool post_processing(); }; END_NAMESPACE_STIR #endif - - - diff --git a/src/iterative/KOSMAPOSL/KOSMAPOSL.cxx b/src/iterative/KOSMAPOSL/KOSMAPOSL.cxx index 930e8f8cba..299d6ec231 100644 --- a/src/iterative/KOSMAPOSL/KOSMAPOSL.cxx +++ b/src/iterative/KOSMAPOSL/KOSMAPOSL.cxx @@ -44,9 +44,11 @@ using std::cout; using std::endl; #ifdef STIR_MPI -int stir::distributable_main(int argc, char **argv) +int +stir::distributable_main(int argc, char** argv) #else -int main(int argc, char **argv) +int +main(int argc, char** argv) #endif { @@ -55,23 +57,17 @@ int main(int argc, char **argv) HighResWallClockTimer t; t.reset(); t.start(); - - KOSMAPOSLReconstruction > - reconstruction_object(argc>1?argv[1]:""); - - //return reconstruction_object.reconstruct() == Succeeded::yes ? - // EXIT_SUCCESS : EXIT_FAILURE; - if (reconstruction_object.reconstruct() == Succeeded::yes) - { - t.stop(); - cout << "Total Wall clock time: " << t.value() << " seconds" << endl; - return EXIT_SUCCESS; - } - else - { - t.stop(); - return EXIT_FAILURE; - } - + KOSMAPOSLReconstruction> reconstruction_object(argc > 1 ? argv[1] : ""); + + // return reconstruction_object.reconstruct() == Succeeded::yes ? + // EXIT_SUCCESS : EXIT_FAILURE; + if (reconstruction_object.reconstruct() == Succeeded::yes) { + t.stop(); + cout << "Total Wall clock time: " << t.value() << " seconds" << endl; + return EXIT_SUCCESS; + } else { + t.stop(); + return EXIT_FAILURE; + } } diff --git a/src/iterative/KOSMAPOSL/KOSMAPOSLReconstruction.cxx b/src/iterative/KOSMAPOSL/KOSMAPOSLReconstruction.cxx index 5b5ce610e8..7870691c90 100644 --- a/src/iterative/KOSMAPOSL/KOSMAPOSLReconstruction.cxx +++ b/src/iterative/KOSMAPOSL/KOSMAPOSLReconstruction.cxx @@ -31,7 +31,7 @@ \author Ashley Gillman \author Palak Wadhwa \author Kris Thielemans - + */ #include "stir/KOSMAPOSL/KOSMAPOSLReconstruction.h" @@ -61,14 +61,14 @@ #include #ifdef STIR_OPENMP -#include +# include #endif #include "stir/num_threads.h" #ifdef BOOST_NO_STRINGSTREAM -#include +# include #else -#include +# include #endif #include "stir/unique_ptr.h" @@ -85,26 +85,21 @@ using std::endl; START_NAMESPACE_STIR - // worker functions - namespace { // priave namespace for internal functions -inline unsigned int ravel_index(int x, int y, int z, - int min_x, int min_y, int min_z, - int max_x, int max_y, int max_z) { - unsigned int ravelled_index= - (z-min_z)*(max_x-min_x +1)*(max_y-min_y +1) - + (y-min_y)*(max_x-min_x +1) - + (x-min_x); - return ravelled_index; - } - +inline unsigned int +ravel_index(int x, int y, int z, int min_x, int min_y, int min_z, int max_x, int max_y, int max_z) { + unsigned int ravelled_index = + (z - min_z) * (max_x - min_x + 1) * (max_y - min_y + 1) + (y - min_y) * (max_x - min_x + 1) + (x - min_x); + return ravelled_index; +} -inline double gaussian_kernel_already_sq(double distance_sq) { +inline double +gaussian_kernel_already_sq(double distance_sq) { // std::cout << "gaussian_kernel(" << distance_sq << ", " << sigma << ")" << std::endl; - return exp(-distance_sq ); + return exp(-distance_sq); } inline void @@ -114,225 +109,190 @@ precalculate_patch_euclidean_distances(Array<3, float>& distance, int num_neighb if (only_2D) { min_dz = max_dz = 0; + } else { + min_dz = -(num_neighbours - 1) / 2; + max_dz = (num_neighbours - 1) / 2; } - else { - min_dz = -(num_neighbours-1)/2; - max_dz = (num_neighbours-1)/2; - } - min_dy = -(num_neighbours-1)/2; - max_dy = (num_neighbours-1)/2; - min_dx = -(num_neighbours-1)/2; - max_dx = (num_neighbours-1)/2; + min_dy = -(num_neighbours - 1) / 2; + max_dy = (num_neighbours - 1) / 2; + min_dx = -(num_neighbours - 1) / 2; + max_dx = (num_neighbours - 1) / 2; - distance = - Array<3,float>(IndexRange3D(min_dz, max_dz, min_dy, max_dy, min_dx, max_dx)); + distance = Array<3, float>(IndexRange3D(min_dz, max_dz, min_dy, max_dy, min_dx, max_dx)); - for (int z=min_dz; z<=max_dz; ++z) { - for (int y=min_dy; y<=max_dy; ++y) { - for (int x=min_dx; x<=max_dx; ++x) { + for (int z = min_dz; z <= max_dz; ++z) { + for (int y = min_dy; y <= max_dy; ++y) { + for (int x = min_dx; x <= max_dx; ++x) { distance[z][y][x] = - sqrt(square(x * grid_spacing.x()) - + square(y * grid_spacing.y()) - + square(z * grid_spacing.z()))/grid_spacing.x(); + sqrt(square(x * grid_spacing.x()) + square(y * grid_spacing.y()) + square(z * grid_spacing.z())) / grid_spacing.x(); } } } -}} +} +} // namespace template -const char * const -KOSMAPOSLReconstruction ::registered_name = - "KOSMAPOSL"; +const char* const KOSMAPOSLReconstruction::registered_name = "KOSMAPOSL"; //*********** parameters *********** template void -KOSMAPOSLReconstruction:: -set_defaults() -{ +KOSMAPOSLReconstruction::set_defaults() { base_type::set_defaults(); this->sigma_m.clear(); this->anatomical_image_filenames.clear(); this->anatomical_prior_sptrs.clear(); - - this->num_neighbours=3; - this->num_non_zero_feat=1; -// this->sigma_m.push_back(1); -// this->anatomical_image_filenames.push_back(""); - this->sigma_p=1; - this->sigma_dp=1; - this->sigma_dm=1; + + this->num_neighbours = 3; + this->num_non_zero_feat = 1; + // this->sigma_m.push_back(1); + // this->anatomical_image_filenames.push_back(""); + this->sigma_p = 1; + this->sigma_dp = 1; + this->sigma_dm = 1; this->only_2D = 0; - this->kernelised_output_filename_prefix=""; - this->hybrid=0; + this->kernelised_output_filename_prefix = ""; + this->hybrid = 0; } template void -KOSMAPOSLReconstruction:: -initialise_keymap() -{ +KOSMAPOSLReconstruction::initialise_keymap() { base_type::initialise_keymap(); this->parser.add_start_key("KOSMAPOSLParameters"); this->parser.add_stop_key("End KOSMAPOSLParameters"); -// this->parser.add_key("anatomical image filename",&this->anatomical_image_filenames); - this->parser.add_key("number of neighbours",&this->num_neighbours); - this->parser.add_key("number of non-zero feature elements",&this->num_non_zero_feat); - this->parser.add_key("sigma_m",&this->sigma_m); - this->parser.add_key("sigma_p",&this->sigma_p); - this->parser.add_key("sigma_dp",&this->sigma_dp); - this->parser.add_key("sigma_dm",&this->sigma_dm); - this->parser.add_key("only_2D",&this->only_2D); - this->parser.add_key("hybrid",&this->hybrid); + // this->parser.add_key("anatomical image filename",&this->anatomical_image_filenames); + this->parser.add_key("number of neighbours", &this->num_neighbours); + this->parser.add_key("number of non-zero feature elements", &this->num_non_zero_feat); + this->parser.add_key("sigma_m", &this->sigma_m); + this->parser.add_key("sigma_p", &this->sigma_p); + this->parser.add_key("sigma_dp", &this->sigma_dp); + this->parser.add_key("sigma_dm", &this->sigma_dm); + this->parser.add_key("only_2D", &this->only_2D); + this->parser.add_key("hybrid", &this->hybrid); this->parser.add_key("anatomical image filenames", &anatomical_image_filenames); - this->parser.add_key("kernelised output filename prefix",&this->kernelised_output_filename_prefix); + this->parser.add_key("kernelised output filename prefix", &this->kernelised_output_filename_prefix); } - template -void KOSMAPOSLReconstruction:: -ask_parameters() -{ - OSMAPOSLReconstruction::ask_parameters(); - - +void +KOSMAPOSLReconstruction::ask_parameters() { + OSMAPOSLReconstruction::ask_parameters(); } - template -bool KOSMAPOSLReconstruction:: -post_processing() -{ +bool +KOSMAPOSLReconstruction::post_processing() { if (base_type::post_processing()) return true; - - if (this->anatomical_image_filenames.size()!=sigma_m.size()){ - error("The number of sigma_m parameters must be the same as the number of anatomical image filenames"); - return false; + + if (this->anatomical_image_filenames.size() != sigma_m.size()) { + error("The number of sigma_m parameters must be the same as the number of anatomical image filenames"); + return false; } - for(unsigned int i = 0; i < this->anatomical_image_filenames.size(); i++) - { - info(boost::format("Reading anatomical data '%1%'") - % anatomical_image_filenames[i] ); - set_anatomical_prior_sptr (shared_ptr(read_from_file(anatomical_image_filenames[i])),i); - - if (is_null_ptr(this->anatomical_prior_sptrs[i])) - { - error("Failed to read anatomical file %s", anatomical_image_filenames[i].c_str()); - return false; - } + for (unsigned int i = 0; i < this->anatomical_image_filenames.size(); i++) { + info(boost::format("Reading anatomical data '%1%'") % anatomical_image_filenames[i]); + set_anatomical_prior_sptr(shared_ptr(read_from_file(anatomical_image_filenames[i])), i); + + if (is_null_ptr(this->anatomical_prior_sptrs[i])) { + error("Failed to read anatomical file %s", anatomical_image_filenames[i].c_str()); + return false; } + } return false; } //*********** other functions *********** - - template -KOSMAPOSLReconstruction:: -KOSMAPOSLReconstruction() -{ +KOSMAPOSLReconstruction::KOSMAPOSLReconstruction() { set_defaults(); } template -KOSMAPOSLReconstruction:: -KOSMAPOSLReconstruction(const std::string& parameter_filename) -{ +KOSMAPOSLReconstruction::KOSMAPOSLReconstruction(const std::string& parameter_filename) { this->initialise(parameter_filename); info(this->parameter_info()); } - template -Succeeded -KOSMAPOSLReconstruction:: -set_up(shared_ptr const& target_image_sptr) -{ - - if (base_type::set_up(target_image_sptr) == Succeeded::no) - error("KOSMAPOSL::set_up(): Error setting-up underlying OSMAPOSLReconstruction object"); - - if ((this->anatomical_prior_sptrs.size()==0) && - (this->hybrid==0)) - error("KOSMAPOSL::set_up(): anatomical image has not been set"); - - this->subiteration_counter=0; - - if (this->anatomical_prior_sptrs.size()!=sigma_m.size()){ - error("The number of sigma_m parameters must be the same as the number of anatomical images"); - } - - for(unsigned int i = 0; i < this->anatomical_prior_sptrs.size(); i++) - { - if (is_null_ptr(anatomical_prior_sptrs[i])) - error("Not all the anatomical prior images have been set"); - } - if(!this->only_2D){ - this->num_elem_neighbourhood=this->num_neighbours*this->num_neighbours*this->num_neighbours ;} - else{ - this->num_elem_neighbourhood=this->num_neighbours*this->num_neighbours ; - } - - (*target_image_sptr).get_regular_range(min_ind, max_ind); - const int min_z = min_ind[1]; - const int max_z = max_ind[1]; - this->dimz = max_z -min_z+1; - const int min_y = min_ind[2]; - const int max_y = max_ind[2]; - this->dimy = max_y -min_y+1; - const int min_x = min_ind[3]; - const int max_x = max_ind[3]; - this->dimx = max_x -min_x +1; - this->num_voxels = dimz*dimy*dimx; - - if(this->anatomical_prior_sptrs.size()!=0){ - estimate_stand_dev_for_anatomical_image(this->anatomical_sd); - info(boost::format("Kernel from anatomical image calculated "));} - else - info(boost::format("Kernel will be calculated only from functional image ")); - - for(unsigned int i = 0; i < this->anatomical_prior_sptrs.size(); i++) - { - info(boost::format("SDs from anatomical images calculated = '%1%'") - % this->anatomical_sd[i]); - } - - const DiscretisedDensityOnCartesianGrid<3,float>* current_anatomical_cast = - dynamic_cast< const DiscretisedDensityOnCartesianGrid<3,float> *> - (target_image_sptr.get()); - - // TODO - which spacing to use? Need both? - const CartesianCoordinate3D& grid_spacing = - current_anatomical_cast->get_grid_spacing(); - precalculate_patch_euclidean_distances(distance,num_neighbours, only_2D, grid_spacing); - - if(num_non_zero_feat>1){ - this->kmnorm_sptrs.resize(anatomical_sd.size()); - for(unsigned int i = 0; i < this->anatomical_prior_sptrs.size(); i++){ - this->kmnorm_sptrs[i].reset(target_image_sptr->get_empty_copy ()); - this->kmnorm_sptrs[i]->resize(IndexRange3D(0,0,0,this->num_voxels-1,0,this->num_elem_neighbourhood-1)); - } - - this->kpnorm_sptr= shared_ptr(target_image_sptr->get_empty_copy ()); - this->kpnorm_sptr->resize(IndexRange3D(0,0,0,this->num_voxels-1,0,this->num_elem_neighbourhood-1)); - - int dimf_col = this->num_non_zero_feat-1; - int dimf_row=this->num_voxels; - - if (this->anatomical_prior_sptrs.size()!=0){ - calculate_norm_const_matrix(this->kmnorm_sptrs, - dimf_row, - dimf_col); - } - } +Succeeded +KOSMAPOSLReconstruction::set_up(shared_ptr const& target_image_sptr) { + + if (base_type::set_up(target_image_sptr) == Succeeded::no) + error("KOSMAPOSL::set_up(): Error setting-up underlying OSMAPOSLReconstruction object"); + + if ((this->anatomical_prior_sptrs.size() == 0) && (this->hybrid == 0)) + error("KOSMAPOSL::set_up(): anatomical image has not been set"); + + this->subiteration_counter = 0; + + if (this->anatomical_prior_sptrs.size() != sigma_m.size()) { + error("The number of sigma_m parameters must be the same as the number of anatomical images"); + } + + for (unsigned int i = 0; i < this->anatomical_prior_sptrs.size(); i++) { + if (is_null_ptr(anatomical_prior_sptrs[i])) + error("Not all the anatomical prior images have been set"); + } + if (!this->only_2D) { + this->num_elem_neighbourhood = this->num_neighbours * this->num_neighbours * this->num_neighbours; + } else { + this->num_elem_neighbourhood = this->num_neighbours * this->num_neighbours; + } + + (*target_image_sptr).get_regular_range(min_ind, max_ind); + const int min_z = min_ind[1]; + const int max_z = max_ind[1]; + this->dimz = max_z - min_z + 1; + const int min_y = min_ind[2]; + const int max_y = max_ind[2]; + this->dimy = max_y - min_y + 1; + const int min_x = min_ind[3]; + const int max_x = max_ind[3]; + this->dimx = max_x - min_x + 1; + this->num_voxels = dimz * dimy * dimx; + + if (this->anatomical_prior_sptrs.size() != 0) { + estimate_stand_dev_for_anatomical_image(this->anatomical_sd); + info(boost::format("Kernel from anatomical image calculated ")); + } else + info(boost::format("Kernel will be calculated only from functional image ")); + + for (unsigned int i = 0; i < this->anatomical_prior_sptrs.size(); i++) { + info(boost::format("SDs from anatomical images calculated = '%1%'") % this->anatomical_sd[i]); + } + + const DiscretisedDensityOnCartesianGrid<3, float>* current_anatomical_cast = + dynamic_cast*>(target_image_sptr.get()); + + // TODO - which spacing to use? Need both? + const CartesianCoordinate3D& grid_spacing = current_anatomical_cast->get_grid_spacing(); + precalculate_patch_euclidean_distances(distance, num_neighbours, only_2D, grid_spacing); + + if (num_non_zero_feat > 1) { + this->kmnorm_sptrs.resize(anatomical_sd.size()); + for (unsigned int i = 0; i < this->anatomical_prior_sptrs.size(); i++) { + this->kmnorm_sptrs[i].reset(target_image_sptr->get_empty_copy()); + this->kmnorm_sptrs[i]->resize(IndexRange3D(0, 0, 0, this->num_voxels - 1, 0, this->num_elem_neighbourhood - 1)); + } + + this->kpnorm_sptr = shared_ptr(target_image_sptr->get_empty_copy()); + this->kpnorm_sptr->resize(IndexRange3D(0, 0, 0, this->num_voxels - 1, 0, this->num_elem_neighbourhood - 1)); + + int dimf_col = this->num_non_zero_feat - 1; + int dimf_row = this->num_voxels; + + if (this->anatomical_prior_sptrs.size() != 0) { + calculate_norm_const_matrix(this->kmnorm_sptrs, dimf_row, dimf_col); + } + } this->_already_set_up = true; - + return Succeeded::yes; } @@ -342,194 +302,167 @@ set_up(shared_ptr const& target_image_sptr) template const std::vector -KOSMAPOSLReconstruction:: -get_anatomical_image_filenames() const -{ return this->anatomical_image_filenames; } +KOSMAPOSLReconstruction::get_anatomical_image_filenames() const { + return this->anatomical_image_filenames; +} template const int -KOSMAPOSLReconstruction:: -get_num_neighbours() const -{ return this->num_neighbours; } +KOSMAPOSLReconstruction::get_num_neighbours() const { + return this->num_neighbours; +} template const int -KOSMAPOSLReconstruction:: -get_num_non_zero_feat() const -{ return this->num_non_zero_feat; } +KOSMAPOSLReconstruction::get_num_non_zero_feat() const { + return this->num_non_zero_feat; +} template const std::vector -KOSMAPOSLReconstruction:: -get_sigma_m() const -{ return this->sigma_m; } +KOSMAPOSLReconstruction::get_sigma_m() const { + return this->sigma_m; +} template const double -KOSMAPOSLReconstruction:: -get_sigma_p() const -{ return this->sigma_p; } +KOSMAPOSLReconstruction::get_sigma_p() const { + return this->sigma_p; +} template const double -KOSMAPOSLReconstruction:: -get_sigma_dp() const -{ return this->sigma_dp; } +KOSMAPOSLReconstruction::get_sigma_dp() const { + return this->sigma_dp; +} template const double -KOSMAPOSLReconstruction:: -get_sigma_dm() const -{ return this->sigma_dm; } +KOSMAPOSLReconstruction::get_sigma_dm() const { + return this->sigma_dm; +} template const bool -KOSMAPOSLReconstruction:: -get_only_2D() const -{ return this->only_2D; } +KOSMAPOSLReconstruction::get_only_2D() const { + return this->only_2D; +} template const bool -KOSMAPOSLReconstruction:: -get_hybrid() const -{ return this->hybrid; } +KOSMAPOSLReconstruction::get_hybrid() const { + return this->hybrid; +} template -std::vector > KOSMAPOSLReconstruction::get_anatomical_prior_sptrs() -{ return this->anatomical_prior_sptrs; } - +std::vector> +KOSMAPOSLReconstruction::get_anatomical_prior_sptrs() { + return this->anatomical_prior_sptrs; +} /*************************************************************** set_ functions ***************************************************************/ - -template +template void -KOSMAPOSLReconstruction:: -set_anatomical_prior_sptr (shared_ptr arg, int index) -{ +KOSMAPOSLReconstruction::set_anatomical_prior_sptr(shared_ptr arg, int index) { this->_already_set_up = false; if (index < this->anatomical_prior_sptrs.size()) - this->anatomical_prior_sptrs.at(index) = arg; - else - this->anatomical_prior_sptrs.push_back(arg); + this->anatomical_prior_sptrs.at(index) = arg; + else + this->anatomical_prior_sptrs.push_back(arg); } -template +template void -KOSMAPOSLReconstruction:: -set_anatomical_prior_sptr (shared_ptr arg) -{ +KOSMAPOSLReconstruction::set_anatomical_prior_sptr(shared_ptr arg) { this->_already_set_up = false; - this->anatomical_prior_sptrs.resize(1); - this->anatomical_prior_sptrs[0] = arg; + this->anatomical_prior_sptrs.resize(1); + this->anatomical_prior_sptrs[0] = arg; } template void -KOSMAPOSLReconstruction:: -set_anatomical_image_filename(const std::string &arg, const int index) -{ +KOSMAPOSLReconstruction::set_anatomical_image_filename(const std::string& arg, const int index) { this->_already_set_up = false; if (static_cast(index) < this->anatomical_image_filenames.size()) - this->anatomical_image_filenames.at(index) = arg; - else - this->anatomical_image_filenames.push_back(arg); - + this->anatomical_image_filenames.at(index) = arg; + else + this->anatomical_image_filenames.push_back(arg); } template void -KOSMAPOSLReconstruction:: -set_anatomical_image_filename(const std::string &arg) -{ +KOSMAPOSLReconstruction::set_anatomical_image_filename(const std::string& arg) { this->_already_set_up = false; - this->anatomical_image_filenames[0] = arg; + this->anatomical_image_filenames[0] = arg; } template void -KOSMAPOSLReconstruction:: -set_num_neighbours(const int arg) -{ +KOSMAPOSLReconstruction::set_num_neighbours(const int arg) { this->_already_set_up = false; this->num_neighbours = arg; } template void -KOSMAPOSLReconstruction:: -set_num_non_zero_feat(const int arg) -{ +KOSMAPOSLReconstruction::set_num_non_zero_feat(const int arg) { this->_already_set_up = false; this->num_non_zero_feat = arg; } template void -KOSMAPOSLReconstruction:: -set_sigma_m(const double arg, const int index) -{ - this->_already_set_up = false; - if (static_cast(index) < this->sigma_m.size()) - this->sigma_m.at(index) = arg; - else - this->sigma_m.push_back(arg); +KOSMAPOSLReconstruction::set_sigma_m(const double arg, const int index) { + this->_already_set_up = false; + if (static_cast(index) < this->sigma_m.size()) + this->sigma_m.at(index) = arg; + else + this->sigma_m.push_back(arg); } template void -KOSMAPOSLReconstruction:: -set_sigma_m(const double arg) -{ - this->_already_set_up = false; - this->sigma_m.resize(1); - this->sigma_m[0] = arg; +KOSMAPOSLReconstruction::set_sigma_m(const double arg) { + this->_already_set_up = false; + this->sigma_m.resize(1); + this->sigma_m[0] = arg; } template void -KOSMAPOSLReconstruction:: -set_sigma_p(const double arg) -{ - this->_already_set_up = false; - this->sigma_p = arg; +KOSMAPOSLReconstruction::set_sigma_p(const double arg) { + this->_already_set_up = false; + this->sigma_p = arg; } template void -KOSMAPOSLReconstruction:: -set_sigma_dp(const double arg) -{ +KOSMAPOSLReconstruction::set_sigma_dp(const double arg) { this->_already_set_up = false; this->sigma_dp = arg; } template void -KOSMAPOSLReconstruction:: -set_sigma_dm(const double arg) -{ +KOSMAPOSLReconstruction::set_sigma_dm(const double arg) { this->_already_set_up = false; this->sigma_dm = arg; } template void -KOSMAPOSLReconstruction:: -set_only_2D(const bool arg) -{ +KOSMAPOSLReconstruction::set_only_2D(const bool arg) { this->_already_set_up = false; this->only_2D = arg; } template void -KOSMAPOSLReconstruction:: -set_hybrid(const bool arg) -{ +KOSMAPOSLReconstruction::set_hybrid(const bool arg) { this->_already_set_up = false; this->hybrid = arg; } @@ -538,20 +471,17 @@ set_hybrid(const bool arg) // Here start the definition of few functions that calculate the SD of the anatomical image, a norm matrix and // finally the Kernelised image -template -void KOSMAPOSLReconstruction:: -calculate_norm_matrix(TargetT &normp, - const int dimf_row, - const int dimf_col, - const TargetT& emission) -{ -// The following is the 2D matrix containing the feature vector for each voxel of the image "emission" - Array<2,float> fp; -// The following are the indexes obtained when reshaping a 3D matrix to a 1D vector and they depend -// on x y and z, and dx dy and dz respectively -// int l=0,m=0; - - fp = Array<2,float>(IndexRange2D(0,dimf_row,0,dimf_col)); +template +void +KOSMAPOSLReconstruction::calculate_norm_matrix(TargetT& normp, const int dimf_row, const int dimf_col, + const TargetT& emission) { + // The following is the 2D matrix containing the feature vector for each voxel of the image "emission" + Array<2, float> fp; + // The following are the indexes obtained when reshaping a 3D matrix to a 1D vector and they depend + // on x y and z, and dx dy and dz respectively + // int l=0,m=0; + + fp = Array<2, float>(IndexRange2D(0, dimf_row, 0, dimf_col)); const int min_z = min_ind[1]; const int max_z = max_ind[1]; @@ -561,595 +491,471 @@ calculate_norm_matrix(TargetT &normp, const int max_x = max_ind[3]; #ifdef STIR_OPENMP -# if _OPENMP <201107 - #pragma omp parallel for +# if _OPENMP < 201107 +# pragma omp parallel for # else - #pragma omp parallel for collapse(3) schedule(dynamic) +# pragma omp parallel for collapse(3) schedule(dynamic) # endif #endif -//The following loop extracts the feature vector related to each voxel in the "emission" image and save it in "fp" - for (int z=min_z; z<=max_z; z++) - { - for (int y=min_y;y<= max_y;y++) - { - for (int x=min_x;x<= max_x;x++) - { - const int min_dz = max(distance.get_min_index(), min_z-z); - const int max_dz = min(distance.get_max_index(), max_z-z); - const int min_dy = max(distance[0].get_min_index(), min_y-y); - const int max_dy = min(distance[0].get_max_index(), max_y-y); - - const int min_dx = max(distance[0][0].get_min_index(), min_x-x); - const int max_dx = min(distance[0][0].get_max_index(), max_x-x); - - - int l = (z-min_z)*(max_x-min_x +1)*(max_y-min_y +1) - + (y-min_y)*(max_x-min_x +1) + (x-min_x); - - //here a matrix with the feature vectors is created - for (int dz=min_dz;dz<=max_dz;++dz) - for (int dy=min_dy;dy<=max_dy;++dy) - for (int dx=min_dx;dx<=max_dx;++dx) - { - int m = (dz)*(max_dx-min_dx +1)*(max_dy-min_dy +1) - + (dy)*(max_dx-min_dx +1) - + (dx); - int c = m; - - if(m<0){ - c = m+this->num_elem_neighbourhood ; - } else { - c=m; - } - - if ( z+dz > max_z || y+dy> max_y || x+dx > max_x - || z+dz < min_z || y+dy< min_y || x+dx < min_x - || m > this->num_non_zero_feat-1 || m <0) { - continue; - } - else{ - fp[l][c] = (emission[z+dz][y+dy][x+dx]) ; - } - } + // The following loop extracts the feature vector related to each voxel in the "emission" image and save it in "fp" + for (int z = min_z; z <= max_z; z++) { + for (int y = min_y; y <= max_y; y++) { + for (int x = min_x; x <= max_x; x++) { + const int min_dz = max(distance.get_min_index(), min_z - z); + const int max_dz = min(distance.get_max_index(), max_z - z); + const int min_dy = max(distance[0].get_min_index(), min_y - y); + const int max_dy = min(distance[0].get_max_index(), max_y - y); + + const int min_dx = max(distance[0][0].get_min_index(), min_x - x); + const int max_dx = min(distance[0][0].get_max_index(), max_x - x); + + int l = (z - min_z) * (max_x - min_x + 1) * (max_y - min_y + 1) + (y - min_y) * (max_x - min_x + 1) + (x - min_x); + + // here a matrix with the feature vectors is created + for (int dz = min_dz; dz <= max_dz; ++dz) + for (int dy = min_dy; dy <= max_dy; ++dy) + for (int dx = min_dx; dx <= max_dx; ++dx) { + int m = (dz) * (max_dx - min_dx + 1) * (max_dy - min_dy + 1) + (dy) * (max_dx - min_dx + 1) + (dx); + int c = m; + + if (m < 0) { + c = m + this->num_elem_neighbourhood; + } else { + c = m; + } + + if (z + dz > max_z || y + dy > max_y || x + dx > max_x || z + dz < min_z || y + dy < min_y || x + dx < min_x || + m > this->num_non_zero_feat - 1 || m < 0) { + continue; + } else { + fp[l][c] = (emission[z + dz][y + dy][x + dx]); + } } - } } + } + } // the norms of the difference between feature vectors related to the // same neighbourhood are calculated now #ifdef STIR_OPENMP -# if _OPENMP <201107 - #pragma omp parallel for +# if _OPENMP < 201107 +# pragma omp parallel for # else - #pragma omp parallel for collapse(3) schedule(dynamic) +# pragma omp parallel for collapse(3) schedule(dynamic) # endif #endif - for (int q=0; q<=dimf_row-1; ++q){ - for (int n=-(this->num_neighbours-1)/2*(!this->only_2D); - n<=(this->num_neighbours-1)/2*(!this->only_2D); - ++n) - for (int k=-(this->num_neighbours-1)/2; - k<=(this->num_neighbours-1)/2; - ++k) - for (int j=-(this->num_neighbours-1)/2; - j<=(this->num_neighbours-1)/2; - ++j) - for (int i=0; i<=dimf_col; ++i) - { - - int p = j - + k*(this->num_neighbours) - + n*(this->num_neighbours)*(this->num_neighbours) - + (this->num_elem_neighbourhood-1)/2; - int o; - - if (q%dimx==0 && (j+k*this->dimx+n*dimx*dimy)>=(dimx-1)) - { - if (j+k*this->dimx+n*dimx*dimy - >= dimx+(this->num_neighbours-1)/2) { - continue; - } - - o=q+j+k*this->dimx+n*dimx*dimy+1; - } - - else{ - o=q+j+k*this->dimx+n*dimx*dimy; - } - - if(o>=dimf_row-1 || o<0 || i<0|| i>this->num_non_zero_feat-1 - || q>=dimf_row-1 || q<0){ + for (int q = 0; q <= dimf_row - 1; ++q) { + for (int n = -(this->num_neighbours - 1) / 2 * (!this->only_2D); n <= (this->num_neighbours - 1) / 2 * (!this->only_2D); ++n) + for (int k = -(this->num_neighbours - 1) / 2; k <= (this->num_neighbours - 1) / 2; ++k) + for (int j = -(this->num_neighbours - 1) / 2; j <= (this->num_neighbours - 1) / 2; ++j) + for (int i = 0; i <= dimf_col; ++i) { + + int p = j + k * (this->num_neighbours) + n * (this->num_neighbours) * (this->num_neighbours) + + (this->num_elem_neighbourhood - 1) / 2; + int o; + + if (q % dimx == 0 && (j + k * this->dimx + n * dimx * dimy) >= (dimx - 1)) { + if (j + k * this->dimx + n * dimx * dimy >= dimx + (this->num_neighbours - 1) / 2) { continue; } - normp[0][q][p] += square(fp[q][i]-fp[o][i]); - } - } -} -template -void KOSMAPOSLReconstruction:: -calculate_norm_const_matrix(std::vector > &normm, - const int dimf_row, - const int dimf_col) -{ - for(unsigned int i=0; i < this->anatomical_prior_sptrs.size();i++){ - calculate_norm_matrix(*normm[i],dimf_row,dimf_col,*(this->anatomical_prior_sptrs[i])); - } + o = q + j + k * this->dimx + n * dimx * dimy + 1; + } + else { + o = q + j + k * this->dimx + n * dimx * dimy; + } + if (o >= dimf_row - 1 || o < 0 || i < 0 || i > this->num_non_zero_feat - 1 || q >= dimf_row - 1 || q < 0) { + continue; + } + normp[0][q][p] += square(fp[q][i] - fp[o][i]); + } + } +} +template +void +KOSMAPOSLReconstruction::calculate_norm_const_matrix(std::vector>& normm, const int dimf_row, + const int dimf_col) { + for (unsigned int i = 0; i < this->anatomical_prior_sptrs.size(); i++) { + calculate_norm_matrix(*normm[i], dimf_row, dimf_col, *(this->anatomical_prior_sptrs[i])); + } } -template -void KOSMAPOSLReconstruction::estimate_stand_dev_for_anatomical_image(std::vector &SD) -{ - SD.resize(this->anatomical_prior_sptrs.size()); - for(unsigned int i=0; i < this->anatomical_prior_sptrs.size();i++){ - - double kmean=0; - double kStand_dev=0; - int nv=0; - - const int min_z = min_ind[1]; - const int max_z = max_ind[1]; - const int min_y = min_ind[2]; - const int max_y = max_ind[2]; - const int min_x = min_ind[3]; - const int max_x = max_ind[3]; +template +void +KOSMAPOSLReconstruction::estimate_stand_dev_for_anatomical_image(std::vector& SD) { + SD.resize(this->anatomical_prior_sptrs.size()); + for (unsigned int i = 0; i < this->anatomical_prior_sptrs.size(); i++) { + + double kmean = 0; + double kStand_dev = 0; + int nv = 0; + + const int min_z = min_ind[1]; + const int max_z = max_ind[1]; + const int min_y = min_ind[2]; + const int max_y = max_ind[2]; + const int min_x = min_ind[3]; + const int max_x = max_ind[3]; #ifdef STIR_OPENMP -# if _OPENMP <201107 - #pragma omp parallel for +# if _OPENMP < 201107 +# pragma omp parallel for # else - #pragma omp parallel for collapse(3) reduction(+:kmean,nv) +# pragma omp parallel for collapse(3) reduction(+ : kmean, nv) # endif #endif - for (int z=min_z; z<=max_z; z++) - { - for (int y=min_y;y<= max_y;y++) - { - for (int x=min_x;x<= max_x;x++) - {// no break allowed inside a parallel for - if(!((*anatomical_prior_sptrs[i])[z][y][x]>=0 && (*anatomical_prior_sptrs[i])[z][y][x]<=1000000)) - warning("The anatomical image might contain nan, negatives or infinitive. You might get all-zero image!"); - - kmean += (*anatomical_prior_sptrs[i])[z][y][x]; - nv+=1; - } - } - } - kmean=kmean / nv; + for (int z = min_z; z <= max_z; z++) { + for (int y = min_y; y <= max_y; y++) { + for (int x = min_x; x <= max_x; x++) { // no break allowed inside a parallel for + if (!((*anatomical_prior_sptrs[i])[z][y][x] >= 0 && (*anatomical_prior_sptrs[i])[z][y][x] <= 1000000)) + warning("The anatomical image might contain nan, negatives or infinitive. You might get all-zero image!"); + + kmean += (*anatomical_prior_sptrs[i])[z][y][x]; + nv += 1; + } + } + } + kmean = kmean / nv; #ifdef STIR_OPENMP -# if _OPENMP <201107 - #pragma omp parallel for +# if _OPENMP < 201107 +# pragma omp parallel for # else - #pragma omp parallel for collapse(3) reduction(+:kStand_dev) +# pragma omp parallel for collapse(3) reduction(+ : kStand_dev) # endif #endif - for (int z=min_z; z<=max_z; z++) - { - for (int y=min_y;y<= max_y;y++) - { - for (int x=min_x;x<= max_x;x++) - { - kStand_dev += square((*anatomical_prior_sptrs[i])[z][y][x] - kmean); - } - } - } - SD[i] = ((double)sqrt(kStand_dev/(nv-1) )); + for (int z = min_z; z <= max_z; z++) { + for (int y = min_y; y <= max_y; y++) { + for (int x = min_x; x <= max_x; x++) { + kStand_dev += square((*anatomical_prior_sptrs[i])[z][y][x] - kmean); + } + } } + SD[i] = ((double)sqrt(kStand_dev / (nv - 1))); + } } -template -void KOSMAPOSLReconstruction::compute_kernelised_image( - TargetT& kernelised_image_out, - const TargetT& image_to_kernelise, - const TargetT& current_alpha_estimate) -{ - - for(unsigned int i=0; i < this->anatomical_prior_sptrs.size();i++){ - if(!current_alpha_estimate.has_same_characteristics(*this->anatomical_prior_sptrs[i])) - error("anatomical and emission image have different sizes! Make sure they are the same"); - } - - bool use_compact_implementation = this->num_non_zero_feat == 1; +template +void +KOSMAPOSLReconstruction::compute_kernelised_image(TargetT& kernelised_image_out, const TargetT& image_to_kernelise, + const TargetT& current_alpha_estimate) { - // Something very weird happens here if I do not get_empty_copy() - // KImage elements will be all nan + for (unsigned int i = 0; i < this->anatomical_prior_sptrs.size(); i++) { + if (!current_alpha_estimate.has_same_characteristics(*this->anatomical_prior_sptrs[i])) + error("anatomical and emission image have different sizes! Make sure they are the same"); + } - unique_ptr kImage_uptr(current_alpha_estimate.get_empty_copy()); + bool use_compact_implementation = this->num_non_zero_feat == 1; - if (!use_compact_implementation && this->get_hybrid()) { - // Going to need the full emission regional normalised differences - int dimf_row = this->num_voxels; - int dimf_col = this->num_non_zero_feat-1; - calculate_norm_matrix(*this->kpnorm_sptr, dimf_row, dimf_col, - current_alpha_estimate); - } + // Something very weird happens here if I do not get_empty_copy() + // KImage elements will be all nan + unique_ptr kImage_uptr(current_alpha_estimate.get_empty_copy()); - // calculate kernelised image - int min_z, max_z, min_y, max_y, min_x, max_x; + if (!use_compact_implementation && this->get_hybrid()) { + // Going to need the full emission regional normalised differences + int dimf_row = this->num_voxels; + int dimf_col = this->num_non_zero_feat - 1; + calculate_norm_matrix(*this->kpnorm_sptr, dimf_row, dimf_col, current_alpha_estimate); + } - min_z = current_alpha_estimate.get_min_index(); - max_z = current_alpha_estimate.get_max_index(); - min_y = current_alpha_estimate[min_z].get_min_index(); - max_y = current_alpha_estimate[min_z].get_max_index(); - min_x = current_alpha_estimate[min_z][min_y].get_min_index(); - max_x = current_alpha_estimate[min_z][min_y].get_max_index(); + // calculate kernelised image + int min_z, max_z, min_y, max_y, min_x, max_x; - // Iterate over the image + min_z = current_alpha_estimate.get_min_index(); + max_z = current_alpha_estimate.get_max_index(); + min_y = current_alpha_estimate[min_z].get_min_index(); + max_y = current_alpha_estimate[min_z].get_max_index(); + min_x = current_alpha_estimate[min_z][min_y].get_min_index(); + max_x = current_alpha_estimate[min_z][min_y].get_max_index(); + // Iterate over the image #ifdef STIR_OPENMP -# if _OPENMP <201107 - #pragma omp parallel for +# if _OPENMP < 201107 +# pragma omp parallel for # else - #pragma omp parallel for collapse(3) schedule(dynamic) +# pragma omp parallel for collapse(3) schedule(dynamic) # endif #endif - for (int z=min_z; z<=max_z; z++) { - for (int y=min_y; y<= max_y; y++) { - for (int x=min_x; x<= max_x; x++) { - const int min_dz = max(distance.get_min_index(), min_z-z); - const int max_dz = min(distance.get_max_index(), max_z-z); - const int min_dy = max(distance[0].get_min_index(), min_y-y); - const int max_dy = min(distance[0].get_max_index(), max_y-y); - const int min_dx = max(distance[0][0].get_min_index(), min_x-x); - const int max_dx = min(distance[0][0].get_max_index(), max_x-x); - - // Iterate over the kernel patch, centered at the current voxel - - double kernel_sum = 0; - - for (int dz=min_dz; dz<=max_dz; ++dz) { - for (int dy=min_dy; dy<=max_dy; ++dy) { - for (int dx=min_dx; dx<=max_dx; ++dx) { - - const int current_ravelled_idx - = ravel_index(x, y, z, min_x, min_y, min_z, max_x, max_y, max_z); - const int delta_ravelled_idx - = ravel_index(dx, dy, dz, min_dx, min_dy, min_dz, max_dx, max_dy, max_dz); - -// std::cout << "d " <anatomical_prior_sptrs.size();i++){ - anatomical_kernel = anatomical_kernel * calc_anatomical_kernel((*anatomical_prior_sptrs[i])[z][y][x], - (*anatomical_prior_sptrs[i])[z+dz][y+dy][x+dx], - distance[dz][dy][dx], - use_compact_implementation, - current_ravelled_idx, - delta_ravelled_idx, - i); - } - - const double kernel = anatomical_kernel * emission_kernel; - - kernelised_image_out[z][y][x] - += kernel * image_to_kernelise[z+dz][y+dy][x+dx]; - kernel_sum += kernel; - } - } - } - - if (current_alpha_estimate[z][y][x] == 0) { - continue; + for (int z = min_z; z <= max_z; z++) { + for (int y = min_y; y <= max_y; y++) { + for (int x = min_x; x <= max_x; x++) { + const int min_dz = max(distance.get_min_index(), min_z - z); + const int max_dz = min(distance.get_max_index(), max_z - z); + const int min_dy = max(distance[0].get_min_index(), min_y - y); + const int max_dy = min(distance[0].get_max_index(), max_y - y); + const int min_dx = max(distance[0][0].get_min_index(), min_x - x); + const int max_dx = min(distance[0][0].get_max_index(), max_x - x); + + // Iterate over the kernel patch, centered at the current voxel + + double kernel_sum = 0; + + for (int dz = min_dz; dz <= max_dz; ++dz) { + for (int dy = min_dy; dy <= max_dy; ++dy) { + for (int dx = min_dx; dx <= max_dx; ++dx) { + + const int current_ravelled_idx = ravel_index(x, y, z, min_x, min_y, min_z, max_x, max_y, max_z); + const int delta_ravelled_idx = ravel_index(dx, dy, dz, min_dx, min_dy, min_dz, max_dx, max_dy, max_dz); + + // std::cout << "d " <anatomical_prior_sptrs.size(); i++) { + anatomical_kernel = + anatomical_kernel * calc_anatomical_kernel((*anatomical_prior_sptrs[i])[z][y][x], + (*anatomical_prior_sptrs[i])[z + dz][y + dy][x + dx], + distance[dz][dy][dx], use_compact_implementation, + current_ravelled_idx, delta_ravelled_idx, i); } + const double kernel = anatomical_kernel * emission_kernel; - kernelised_image_out[z][y][x] /= kernel_sum; - } - } - } + kernelised_image_out[z][y][x] += kernel * image_to_kernelise[z + dz][y + dy][x + dx]; + kernel_sum += kernel; + } + } + } + + if (current_alpha_estimate[z][y][x] == 0) { + continue; + } + + kernelised_image_out[z][y][x] /= kernel_sum; + } } + } +} template double -KOSMAPOSLReconstruction:: -calc_emission_kernel(const double current_alpha_estimate_zyx, - const double current_alpha_estimate_zyx_dr, - const double distance_dzdydx, - const bool use_compact_implementation, - const int l, - const int m) { +KOSMAPOSLReconstruction::calc_emission_kernel(const double current_alpha_estimate_zyx, + const double current_alpha_estimate_zyx_dr, const double distance_dzdydx, + const bool use_compact_implementation, const int l, const int m) { const double emission_kernel = - use_compact_implementation - ? calc_kernel_compact(current_alpha_estimate_zyx- - current_alpha_estimate_zyx_dr, - sigma_p*sigma_p, - sigma_dp*sigma_dp, - distance_dzdydx*distance_dzdydx, - current_alpha_estimate_zyx*current_alpha_estimate_zyx) - : calc_kernel_from_precalculated((*kpnorm_sptr)[0][l][m], - sigma_p*sigma_p, - sigma_dp*sigma_dp, - distance_dzdydx*distance_dzdydx, - current_alpha_estimate_zyx*current_alpha_estimate_zyx); - - return emission_kernel; + use_compact_implementation ? calc_kernel_compact(current_alpha_estimate_zyx - current_alpha_estimate_zyx_dr, + sigma_p * sigma_p, sigma_dp * sigma_dp, distance_dzdydx * distance_dzdydx, + current_alpha_estimate_zyx * current_alpha_estimate_zyx) + : calc_kernel_from_precalculated((*kpnorm_sptr)[0][l][m], sigma_p * sigma_p, sigma_dp * sigma_dp, + distance_dzdydx * distance_dzdydx, + current_alpha_estimate_zyx * current_alpha_estimate_zyx); + + return emission_kernel; } template double -KOSMAPOSLReconstruction:: -calc_kernel_from_precalculated(const double precalculated_norm_zxy, - const double sq_sigma_int, - const double sq_sigma_dist, - const double sq_distance_dzdydx, - const double sq_precalc_denom) { +KOSMAPOSLReconstruction::calc_kernel_from_precalculated(const double precalculated_norm_zxy, const double sq_sigma_int, + const double sq_sigma_dist, const double sq_distance_dzdydx, + const double sq_precalc_denom) { - const double norm_distance_sq - = precalculated_norm_zxy/ sq_precalc_denom/sq_sigma_int+ - sq_distance_dzdydx/sq_sigma_dist/2; + const double norm_distance_sq = + precalculated_norm_zxy / sq_precalc_denom / sq_sigma_int + sq_distance_dzdydx / sq_sigma_dist / 2; return gaussian_kernel_already_sq(norm_distance_sq); } template double -KOSMAPOSLReconstruction:: -calc_anatomical_kernel(const double anatomical_prior_zyx, - const double anatomical_prior_zyx_dr, - const double distance_dzdydx, - const bool use_compact_implementation, - const int l, - const int m, - const int index) { +KOSMAPOSLReconstruction::calc_anatomical_kernel(const double anatomical_prior_zyx, const double anatomical_prior_zyx_dr, + const double distance_dzdydx, const bool use_compact_implementation, + const int l, const int m, const int index) { const double anatomical_kernel = - use_compact_implementation - ? calc_kernel_compact(anatomical_prior_zyx- - anatomical_prior_zyx_dr, - sigma_m[index]*sigma_m[index], - sigma_dm*sigma_dm, - distance_dzdydx*distance_dzdydx, - anatomical_sd[index]*anatomical_sd[index]) - : calc_kernel_from_precalculated((*kmnorm_sptrs[index])[0][l][m], - sigma_m[index]*sigma_m[index], - sigma_dm*sigma_dm, - distance_dzdydx*distance_dzdydx, - anatomical_sd[index]*anatomical_sd[index]); + use_compact_implementation + ? calc_kernel_compact(anatomical_prior_zyx - anatomical_prior_zyx_dr, sigma_m[index] * sigma_m[index], + sigma_dm * sigma_dm, distance_dzdydx * distance_dzdydx, + anatomical_sd[index] * anatomical_sd[index]) + : calc_kernel_from_precalculated((*kmnorm_sptrs[index])[0][l][m], sigma_m[index] * sigma_m[index], sigma_dm * sigma_dm, + distance_dzdydx * distance_dzdydx, anatomical_sd[index] * anatomical_sd[index]); return anatomical_kernel; } template double -KOSMAPOSLReconstruction:: -calc_kernel_compact(const double prior_image_zyx_diff, - const double sq_sigma_int, - const double sq_sigma_dist, - const double sq_distance_dzdydx, - const double sq_precalc_denom) { - - const double norm_distance_sq - = ((prior_image_zyx_diff)/sq_precalc_denom/sq_sigma_int)* - ((prior_image_zyx_diff)/2)+ - sq_distance_dzdydx/sq_sigma_dist/2; - - return gaussian_kernel_already_sq(norm_distance_sq); +KOSMAPOSLReconstruction::calc_kernel_compact(const double prior_image_zyx_diff, const double sq_sigma_int, + const double sq_sigma_dist, const double sq_distance_dzdydx, + const double sq_precalc_denom) { + + const double norm_distance_sq = ((prior_image_zyx_diff) / sq_precalc_denom / sq_sigma_int) * ((prior_image_zyx_diff) / 2) + + sq_distance_dzdydx / sq_sigma_dist / 2; + + return gaussian_kernel_already_sq(norm_distance_sq); } template -void -KOSMAPOSLReconstruction:: -update_estimate(TargetT ¤t_alpha_coefficent_image) -{ +void +KOSMAPOSLReconstruction::update_estimate(TargetT& current_alpha_coefficent_image) { this->check(current_alpha_coefficent_image); - // TODO should use something like iterator_traits to figure out the + // TODO should use something like iterator_traits to figure out the // type instead of hard-wiring float static const float small_num = 0.000001F; #ifndef PARALLEL - //CPUTimer subset_timer; - //subset_timer.start(); -#else // PARALLEL + // CPUTimer subset_timer; + // subset_timer.start(); +#else // PARALLEL PTimer timerSubset; timerSubset.Start(); #endif // PARALLEL - + // TODO make member parameter to avoid reallocation all the time - unique_ptr< TargetT > multiplicative_update_image_ptr - (current_alpha_coefficent_image.get_empty_copy()); + unique_ptr multiplicative_update_image_ptr(current_alpha_coefficent_image.get_empty_copy()); - const int subset_num=this->get_subset_num(); + const int subset_num = this->get_subset_num(); info(boost::format("Now processing subset #: %1%") % subset_num); - unique_ptr< TargetT > current_update_image_ptr(current_alpha_coefficent_image.get_empty_copy()); - compute_kernelised_image (*current_update_image_ptr, current_alpha_coefficent_image, current_alpha_coefficent_image); + unique_ptr current_update_image_ptr(current_alpha_coefficent_image.get_empty_copy()); + compute_kernelised_image(*current_update_image_ptr, current_alpha_coefficent_image, current_alpha_coefficent_image); + base_type::compute_sub_gradient_without_penalty_plus_sensitivity(*multiplicative_update_image_ptr, *current_update_image_ptr, + subset_num); + unique_ptr kmultiplicative_update_ptr((*multiplicative_update_image_ptr).get_empty_copy()); + const TargetT& sensitivity = base_type::get_subset_sensitivity(subset_num); - base_type::compute_sub_gradient_without_penalty_plus_sensitivity (*multiplicative_update_image_ptr, - *current_update_image_ptr, - subset_num); - unique_ptr< TargetT > kmultiplicative_update_ptr((*multiplicative_update_image_ptr).get_empty_copy()); + unique_ptr ksens_ptr(sensitivity.get_empty_copy()); - const TargetT& sensitivity = - base_type::get_subset_sensitivity(subset_num); + // apply kernel to the multiplicative update + compute_kernelised_image(*kmultiplicative_update_ptr, *multiplicative_update_image_ptr, current_alpha_coefficent_image); -unique_ptr< TargetT > ksens_ptr(sensitivity.get_empty_copy()); + // divide by subset sensitivity + compute_kernelised_image(*ksens_ptr, sensitivity, current_alpha_coefficent_image); - //apply kernel to the multiplicative update - compute_kernelised_image (*kmultiplicative_update_ptr, *multiplicative_update_image_ptr, current_alpha_coefficent_image); + int count = 0; - // divide by subset sensitivity - compute_kernelised_image (*ksens_ptr, sensitivity, current_alpha_coefficent_image); + // std::cerr <MAP_model << std::endl; - int count = 0; - - //std::cerr <MAP_model << std::endl; - - if (this->objective_function_sptr->prior_is_zero()) - { - divide(kmultiplicative_update_ptr->begin_all(), - kmultiplicative_update_ptr->end_all(), - (*ksens_ptr).begin_all(), - small_num); - - } - else - { - unique_ptr< TargetT > denominator_ptr - (current_alpha_coefficent_image.get_empty_copy()); - - - this->objective_function_sptr-> - get_prior_ptr()->compute_gradient(*denominator_ptr, current_alpha_coefficent_image); - - typename TargetT::full_iterator denominator_iter = denominator_ptr->begin_all(); - const typename TargetT::full_iterator denominator_end = denominator_ptr->end_all(); - typename TargetT::const_full_iterator sensitivity_iter = (*ksens_ptr).begin_all(); - - if(this->MAP_model =="additive" ) - { - // lambda_new = lambda / (p_v + beta*prior_gradient/ num_subsets) * + if (this->objective_function_sptr->prior_is_zero()) { + divide(kmultiplicative_update_ptr->begin_all(), kmultiplicative_update_ptr->end_all(), (*ksens_ptr).begin_all(), small_num); + + } else { + unique_ptr denominator_ptr(current_alpha_coefficent_image.get_empty_copy()); + + this->objective_function_sptr->get_prior_ptr()->compute_gradient(*denominator_ptr, current_alpha_coefficent_image); + + typename TargetT::full_iterator denominator_iter = denominator_ptr->begin_all(); + const typename TargetT::full_iterator denominator_end = denominator_ptr->end_all(); + typename TargetT::const_full_iterator sensitivity_iter = (*ksens_ptr).begin_all(); + + if (this->MAP_model == "additive") { + // lambda_new = lambda / (p_v + beta*prior_gradient/ num_subsets) * + // sum_subset backproj(measured/forwproj(lambda)) + // with p_v = sum_{b in subset} p_bv + // actually, we restrict 1 + beta*prior_gradient/num_subsets/p_v between .1 and 10 + while (denominator_iter != denominator_end) { + *denominator_iter = *denominator_iter / this->get_num_subsets() + (*sensitivity_iter); + // bound denominator between (*sensitivity_iter)/10 and (*sensitivity_iter)*10 + *denominator_iter = std::max(std::min(*denominator_iter, (*sensitivity_iter) * 10), (*sensitivity_iter) / 10); + ++denominator_iter; + ++sensitivity_iter; + } + } else { + if (this->MAP_model == "multiplicative") { + // multiplicative form + // lambda_new = lambda / (p_v*(1 + beta*prior_gradient)) * // sum_subset backproj(measured/forwproj(lambda)) // with p_v = sum_{b in subset} p_bv - // actually, we restrict 1 + beta*prior_gradient/num_subsets/p_v between .1 and 10 - while (denominator_iter != denominator_end) - { - *denominator_iter = *denominator_iter/this->get_num_subsets() + (*sensitivity_iter); - // bound denominator between (*sensitivity_iter)/10 and (*sensitivity_iter)*10 - *denominator_iter = - std::max(std::min(*denominator_iter, (*sensitivity_iter)*10),(*sensitivity_iter)/10); - ++denominator_iter; - ++sensitivity_iter; - } - } - else - { - if(this->MAP_model =="multiplicative" ) - { - // multiplicative form - // lambda_new = lambda / (p_v*(1 + beta*prior_gradient)) * - // sum_subset backproj(measured/forwproj(lambda)) - // with p_v = sum_{b in subset} p_bv - // actually, we restrict 1 + beta*prior_gradient between .1 and 10 - while (denominator_iter != denominator_end) - { - *denominator_iter += 1; - // bound denominator between 1/10 and 1*10 - // TODO code will fail if *denominator_iter is not a float - *denominator_iter = - std::max(std::min(*denominator_iter, 10.F),1/10.F); - *denominator_iter *= (*sensitivity_iter); - ++denominator_iter; - ++sensitivity_iter; - } + // actually, we restrict 1 + beta*prior_gradient between .1 and 10 + while (denominator_iter != denominator_end) { + *denominator_iter += 1; + // bound denominator between 1/10 and 1*10 + // TODO code will fail if *denominator_iter is not a float + *denominator_iter = std::max(std::min(*denominator_iter, 10.F), 1 / 10.F); + *denominator_iter *= (*sensitivity_iter); + ++denominator_iter; + ++sensitivity_iter; } - } - divide(kmultiplicative_update_ptr->begin_all(), - kmultiplicative_update_ptr->end_all(), - denominator_ptr->begin_all(), - small_num); + } } - - info(boost::format("Number of (cancelled) singularities in Sensitivity division: %1%") % count); - - - - if(this->inter_update_filter_interval>0 && - !is_null_ptr(this->inter_update_filter_ptr) && - !(this->subiteration_num%this->inter_update_filter_interval)) - { + divide(kmultiplicative_update_ptr->begin_all(), kmultiplicative_update_ptr->end_all(), denominator_ptr->begin_all(), + small_num); + } + + info(boost::format("Number of (cancelled) singularities in Sensitivity division: %1%") % count); + + if (this->inter_update_filter_interval > 0 && !is_null_ptr(this->inter_update_filter_ptr) && + !(this->subiteration_num % this->inter_update_filter_interval)) { info("Applying inter-update filter"); this->inter_update_filter_ptr->apply(current_alpha_coefficent_image); } - + // KT 17/08/2000 limit update // TODO move below thresholding? - if (this->write_update_image && !this->_disable_output) - { + if (this->write_update_image && !this->_disable_output) { // allocate space for the filename assuming that // we never have more than 10^49 subiterations ... - char * fname = new char[this->output_filename_prefix.size() + 60]; + char* fname = new char[this->output_filename_prefix.size() + 60]; sprintf(fname, "%s_update_%d", this->output_filename_prefix.c_str(), this->subiteration_num); - + // Write it to file - this->output_file_format_ptr-> - write_to_file(fname, *kmultiplicative_update_ptr); + this->output_file_format_ptr->write_to_file(fname, *kmultiplicative_update_ptr); delete[] fname; } - - if (this->subiteration_num != 1) - { - const float current_min = - *std::min_element(kmultiplicative_update_ptr->begin_all(), - kmultiplicative_update_ptr->end_all()); - const float current_max = - *std::max_element(kmultiplicative_update_ptr->begin_all(), - kmultiplicative_update_ptr->end_all()); - const float new_min = - static_cast(this->minimum_relative_change); - const float new_max = - static_cast(this->maximum_relative_change); - info(boost::format("Update image old min,max: %1%, %2%, new min,max %3%, %4%") % current_min % current_max % (min(current_min, new_min)) % (max(current_max, new_max))); - - threshold_upper_lower(kmultiplicative_update_ptr->begin_all(), - kmultiplicative_update_ptr->end_all(), - new_min, new_max); - } - - //current_alpha_coefficent_image *= *kmultiplicative_update_ptr; + + if (this->subiteration_num != 1) { + const float current_min = *std::min_element(kmultiplicative_update_ptr->begin_all(), kmultiplicative_update_ptr->end_all()); + const float current_max = *std::max_element(kmultiplicative_update_ptr->begin_all(), kmultiplicative_update_ptr->end_all()); + const float new_min = static_cast(this->minimum_relative_change); + const float new_max = static_cast(this->maximum_relative_change); + info(boost::format("Update image old min,max: %1%, %2%, new min,max %3%, %4%") % current_min % current_max % + (min(current_min, new_min)) % (max(current_max, new_max))); + + threshold_upper_lower(kmultiplicative_update_ptr->begin_all(), kmultiplicative_update_ptr->end_all(), new_min, new_max); + } + + // current_alpha_coefficent_image *= *kmultiplicative_update_ptr; { typename TargetT::const_full_iterator multiplicative_update_image_iter = kmultiplicative_update_ptr->begin_all_const(); - const typename TargetT::const_full_iterator end_multiplicative_update_image_iter = kmultiplicative_update_ptr->end_all_const(); + const typename TargetT::const_full_iterator end_multiplicative_update_image_iter = + kmultiplicative_update_ptr->end_all_const(); typename TargetT::full_iterator current_alpha_coefficent_image_iter = current_alpha_coefficent_image.begin_all(); - while (multiplicative_update_image_iter!=end_multiplicative_update_image_iter) - { - *current_alpha_coefficent_image_iter *= (*multiplicative_update_image_iter); - ++current_alpha_coefficent_image_iter; ++multiplicative_update_image_iter; - } - + while (multiplicative_update_image_iter != end_multiplicative_update_image_iter) { + *current_alpha_coefficent_image_iter *= (*multiplicative_update_image_iter); + ++current_alpha_coefficent_image_iter; + ++multiplicative_update_image_iter; + } unique_ptr kcurrent_ptr(current_alpha_coefficent_image.get_empty_copy()); // compute the emission image from the alpha coefficient image - compute_kernelised_image (*kcurrent_ptr, current_alpha_coefficent_image,current_alpha_coefficent_image); + compute_kernelised_image(*kcurrent_ptr, current_alpha_coefficent_image, current_alpha_coefficent_image); - //Write the emission image estimate: - if(!(this->subiteration_num % this->save_interval) || // every save_interval'th + // Write the emission image estimate: + if (!(this->subiteration_num % this->save_interval) || // every save_interval'th this->subiteration_num == this->num_subiterations) { // or on last iteration - this->current_kimage_filename = this->make_filename_prefix_subiteration_num( - this->kernelised_output_filename_prefix); - write_to_file(this->current_kimage_filename, *kcurrent_ptr); + this->current_kimage_filename = this->make_filename_prefix_subiteration_num(this->kernelised_output_filename_prefix); + write_to_file(this->current_kimage_filename, *kcurrent_ptr); } } - + #ifndef PARALLEL - //cerr << "Subset : " << subset_timer.value() << "secs " < >; -//template class KOSMAPOSLReconstruction; - +template class KOSMAPOSLReconstruction>; +// template class KOSMAPOSLReconstruction; END_NAMESPACE_STIR - - diff --git a/src/iterative/OSMAPOSL/OSMAPOSL.cxx b/src/iterative/OSMAPOSL/OSMAPOSL.cxx index 13ff863f15..cabfa8b3b5 100644 --- a/src/iterative/OSMAPOSL/OSMAPOSL.cxx +++ b/src/iterative/OSMAPOSL/OSMAPOSL.cxx @@ -39,9 +39,11 @@ using std::cout; using std::endl; #ifdef STIR_MPI -int stir::distributable_main(int argc, char **argv) +int +stir::distributable_main(int argc, char** argv) #else -int main(int argc, char **argv) +int +main(int argc, char** argv) #endif { @@ -50,23 +52,17 @@ int main(int argc, char **argv) HighResWallClockTimer t; t.reset(); t.start(); - - OSMAPOSLReconstruction > - reconstruction_object(argc>1?argv[1]:""); - - //return reconstruction_object.reconstruct() == Succeeded::yes ? - // EXIT_SUCCESS : EXIT_FAILURE; - if (reconstruction_object.reconstruct() == Succeeded::yes) - { - t.stop(); - cout << "Total Wall clock time: " << t.value() << " seconds" << endl; - return EXIT_SUCCESS; - } - else - { - t.stop(); - return EXIT_FAILURE; - } - + OSMAPOSLReconstruction> reconstruction_object(argc > 1 ? argv[1] : ""); + + // return reconstruction_object.reconstruct() == Succeeded::yes ? + // EXIT_SUCCESS : EXIT_FAILURE; + if (reconstruction_object.reconstruct() == Succeeded::yes) { + t.stop(); + cout << "Total Wall clock time: " << t.value() << " seconds" << endl; + return EXIT_SUCCESS; + } else { + t.stop(); + return EXIT_FAILURE; + } } diff --git a/src/iterative/OSMAPOSL/OSMAPOSLReconstruction.cxx b/src/iterative/OSMAPOSL/OSMAPOSLReconstruction.cxx index b42a6942b9..1bce5dc1d2 100644 --- a/src/iterative/OSMAPOSL/OSMAPOSLReconstruction.cxx +++ b/src/iterative/OSMAPOSL/OSMAPOSLReconstruction.cxx @@ -59,9 +59,9 @@ #include #include #ifdef BOOST_NO_STRINGSTREAM -#include +# include #else -#include +# include #endif #include "stir/unique_ptr.h" @@ -73,39 +73,26 @@ using std::cerr; using std::endl; #endif - START_NAMESPACE_STIR template -const char * const -OSMAPOSLReconstruction ::registered_name = - "OSMAPOSL"; +const char* const OSMAPOSLReconstruction::registered_name = "OSMAPOSL"; template -PoissonLogLikelihoodWithLinearModelForMean& -OSMAPOSLReconstruction:: -objective_function() -{ - return - static_cast&> - (*this->objective_function_sptr); +PoissonLogLikelihoodWithLinearModelForMean& +OSMAPOSLReconstruction::objective_function() { + return static_cast&>(*this->objective_function_sptr); } template -PoissonLogLikelihoodWithLinearModelForMean const& -OSMAPOSLReconstruction:: -objective_function() const -{ - return - static_cast&> - (*this->objective_function_sptr); +PoissonLogLikelihoodWithLinearModelForMean const& +OSMAPOSLReconstruction::objective_function() const { + return static_cast&>(*this->objective_function_sptr); } template -PoissonLogLikelihoodWithLinearModelForMean const& -OSMAPOSLReconstruction:: -get_objective_function() const -{ +PoissonLogLikelihoodWithLinearModelForMean const& +OSMAPOSLReconstruction::get_objective_function() const { // just use the above (private) function return this->objective_function(); } @@ -113,10 +100,8 @@ get_objective_function() const //*********** parameters *********** template -void -OSMAPOSLReconstruction:: -set_defaults() -{ +void +OSMAPOSLReconstruction::set_defaults() { base_type::set_defaults(); enforce_initial_positivity = true; maximum_relative_change = NumericInfo().max_value(); @@ -124,82 +109,65 @@ set_defaults() write_update_image = 0; inter_update_filter_interval = 0; inter_update_filter_ptr.reset(); - MAP_model="additive"; + MAP_model = "additive"; } template void -OSMAPOSLReconstruction:: -initialise_keymap() -{ +OSMAPOSLReconstruction::initialise_keymap() { base_type::initialise_keymap(); this->parser.add_start_key("OSMAPOSLParameters"); this->parser.add_stop_key("End"); this->parser.add_stop_key("End OSMAPOSLParameters"); - this->parser.add_key("enforce initial positivity condition",&this->enforce_initial_positivity); - this->parser.add_key("inter-update filter subiteration interval",&this->inter_update_filter_interval); + this->parser.add_key("enforce initial positivity condition", &this->enforce_initial_positivity); + this->parser.add_key("inter-update filter subiteration interval", &this->inter_update_filter_interval); this->parser.add_parsing_key("inter-update filter type", &this->inter_update_filter_ptr); this->parser.add_key("MAP_model", &this->MAP_model); this->parser.add_key("maximum relative change", &this->maximum_relative_change); - this->parser.add_key("minimum relative change",&this->minimum_relative_change); - this->parser.add_key("write update image",&this->write_update_image); + this->parser.add_key("minimum relative change", &this->minimum_relative_change); + this->parser.add_key("write update image", &this->write_update_image); } - template -void OSMAPOSLReconstruction:: -ask_parameters() -{ +void +OSMAPOSLReconstruction::ask_parameters() { base_type::ask_parameters(); - enforce_initial_positivity= - ask("Enforce initial positivity condition?",true); - - inter_update_filter_interval= - ask_num("Do inter-update filtering at sub-iteration intervals of: ", - 0, this->num_subiterations, 0); - - if(inter_update_filter_interval>0) - { - - cerr<::list_registered_names(cerr); - + enforce_initial_positivity = ask("Enforce initial positivity condition?", true); + + inter_update_filter_interval = + ask_num("Do inter-update filtering at sub-iteration intervals of: ", 0, this->num_subiterations, 0); + + if (inter_update_filter_interval > 0) { + + cerr << endl << "Supply inter-update filter type:\nPossible values:\n"; + DataProcessor::list_registered_names(cerr); + const std::string inter_update_filter_type = ask_string(""); - - inter_update_filter_ptr.reset(DataProcessor::read_registered_object(0, inter_update_filter_type)); - - } + + inter_update_filter_ptr.reset(DataProcessor::read_registered_object(0, inter_update_filter_type)); + } if (!this->objective_function_sptr->prior_is_zero()) - MAP_model = - ask_string("Use additive or multiplicative form of MAP-OSL ('additive' or 'multiplicative')","additive"); - + MAP_model = ask_string("Use additive or multiplicative form of MAP-OSL ('additive' or 'multiplicative')", "additive"); + // KT 17/08/2000 3 new parameters const double max_in_double = static_cast(NumericInfo().max_value()); - maximum_relative_change = ask_num("maximum relative change", - 1.,max_in_double,max_in_double); - minimum_relative_change = ask_num("minimum relative change", - 0.,1.,0.); - - write_update_image = ask_num("write update image", 0,1,0); + maximum_relative_change = ask_num("maximum relative change", 1., max_in_double, max_in_double); + minimum_relative_change = ask_num("minimum relative change", 0., 1., 0.); + write_update_image = ask_num("write update image", 0, 1, 0); } - - - template -bool OSMAPOSLReconstruction:: -post_processing() -{ +bool +OSMAPOSLReconstruction::post_processing() { if (base_type::post_processing()) return true; - if (!this->objective_function_sptr->prior_is_zero()) - { + if (!this->objective_function_sptr->prior_is_zero()) { // TODO MAP_model really should be an ASCIIlist, without automatic checking on values // let set_MAP_model do the checking this->set_MAP_model(this->MAP_model); @@ -210,79 +178,58 @@ post_processing() //*********** set_ functions *********** template void -OSMAPOSLReconstruction:: -set_inter_update_filter_interval(const int arg) -{ - this->inter_update_filter_interval = arg; +OSMAPOSLReconstruction::set_inter_update_filter_interval(const int arg) { + this->inter_update_filter_interval = arg; } template void -OSMAPOSLReconstruction:: -set_inter_update_filter_ptr(const shared_ptr > & arg) -{ - this->inter_update_filter_ptr = arg; +OSMAPOSLReconstruction::set_inter_update_filter_ptr(const shared_ptr>& arg) { + this->inter_update_filter_ptr = arg; } template void -OSMAPOSLReconstruction:: -set_maximum_relative_change(const double arg) -{ - this->maximum_relative_change = arg; +OSMAPOSLReconstruction::set_maximum_relative_change(const double arg) { + this->maximum_relative_change = arg; } template void -OSMAPOSLReconstruction:: -set_minimum_relative_change(const double arg) -{ - this->minimum_relative_change = arg; +OSMAPOSLReconstruction::set_minimum_relative_change(const double arg) { + this->minimum_relative_change = arg; } - + template void -OSMAPOSLReconstruction:: -set_write_update_image(const int arg) -{ - this->write_update_image = arg; +OSMAPOSLReconstruction::set_write_update_image(const int arg) { + this->write_update_image = arg; } template void -OSMAPOSLReconstruction:: -set_MAP_model(const std::string& arg) -{ - this->MAP_model = arg; +OSMAPOSLReconstruction::set_MAP_model(const std::string& arg) { + this->MAP_model = arg; if (MAP_model != "additive" && MAP_model != "multiplicative") - error(boost::format("MAP model should have as value 'additive' or 'multiplicative', while it is '%s'") % - MAP_model); + error(boost::format("MAP model should have as value 'additive' or 'multiplicative', while it is '%s'") % MAP_model); } //*********** other functions *********** - - template -OSMAPOSLReconstruction:: -OSMAPOSLReconstruction() -{ +OSMAPOSLReconstruction::OSMAPOSLReconstruction() { set_defaults(); } template -OSMAPOSLReconstruction:: -OSMAPOSLReconstruction(const std::string& parameter_filename) -{ +OSMAPOSLReconstruction::OSMAPOSLReconstruction(const std::string& parameter_filename) { this->initialise(parameter_filename); info(this->parameter_info()); } template -std::string -OSMAPOSLReconstruction:: -method_info() const -{ +std::string +OSMAPOSLReconstruction::method_info() const { // TODO add prior name? @@ -294,97 +241,80 @@ method_info() const std::ostringstream s; #endif - if(this->inter_update_filter_interval>0) s<<"IUF-"; - if(this->num_subsets>1) s<<"OS"; + if (this->inter_update_filter_interval > 0) + s << "IUF-"; + if (this->num_subsets > 1) + s << "OS"; if (this->objective_function_sptr->prior_is_zero()) - s<<"EM"; + s << "EM"; else s << "MAPOSL"; - if(this->inter_iteration_filter_interval>0) s<<"S"; + if (this->inter_iteration_filter_interval > 0) + s << "S"; return s.str(); - } template -Succeeded -OSMAPOSLReconstruction:: -set_up(shared_ptr const& target_image_ptr) -{ - // TODO should use something like iterator_traits to figure out the +Succeeded +OSMAPOSLReconstruction::set_up(shared_ptr const& target_image_ptr) { + // TODO should use something like iterator_traits to figure out the // type instead of hard-wiring float static const float small_num = 0.000001F; if (base_type::set_up(target_image_ptr) == Succeeded::no) return Succeeded::no; - if (is_null_ptr(dynamic_cast const *> - (this->objective_function_sptr.get()))) - { error("OSMAPOSL can only work with an objective function of type PoissonLogLikelihoodWithLinearModelForMean"); return Succeeded::no; } + if (is_null_ptr( + dynamic_cast const*>(this->objective_function_sptr.get()))) { + error("OSMAPOSL can only work with an objective function of type PoissonLogLikelihoodWithLinearModelForMean"); + return Succeeded::no; + } // check subset balancing { std::string warning_message = "OSMAPOSL\n"; - if (!this->objective_function().subsets_are_approximately_balanced(warning_message)) - { - error("%s\nOSMAPOSL cannot handle this.", - warning_message.c_str()); - return Succeeded::no; - } + if (!this->objective_function().subsets_are_approximately_balanced(warning_message)) { + error("%s\nOSMAPOSL cannot handle this.", warning_message.c_str()); + return Succeeded::no; + } } // end check balancing + if (this->enforce_initial_positivity) + threshold_min_to_small_positive_value(target_image_ptr->begin_all(), target_image_ptr->end_all(), small_num); - if(this->enforce_initial_positivity) - threshold_min_to_small_positive_value(target_image_ptr->begin_all(), - target_image_ptr->end_all(), - small_num); - - if (this->inter_update_filter_interval<0) - { error("Range error in inter-update filter interval"); return Succeeded::no; } - - if(this->inter_update_filter_interval>0 && - !is_null_ptr(this->inter_update_filter_ptr)) - { - // ensure that the result image of the filter is positive - shared_ptr > - thresholding_sptr(new ThresholdMinToSmallPositiveValueDataProcessor); - this->inter_update_filter_ptr.reset( - new ChainedDataProcessor( - this->inter_update_filter_ptr, - thresholding_sptr)); - // KT 04/06/2003 moved set_up after chaining the filter. Otherwise it would be - // called again later on anyway. - // Note however that at present, - info("Building inter-update filter kernel"); - if (this->inter_update_filter_ptr->set_up(*target_image_ptr) - == Succeeded::no) - { - error("Error building inter-update filter"); - return Succeeded::no; - } + if (this->inter_update_filter_interval < 0) { + error("Range error in inter-update filter interval"); + return Succeeded::no; + } + if (this->inter_update_filter_interval > 0 && !is_null_ptr(this->inter_update_filter_ptr)) { + // ensure that the result image of the filter is positive + shared_ptr> thresholding_sptr( + new ThresholdMinToSmallPositiveValueDataProcessor); + this->inter_update_filter_ptr.reset(new ChainedDataProcessor(this->inter_update_filter_ptr, thresholding_sptr)); + // KT 04/06/2003 moved set_up after chaining the filter. Otherwise it would be + // called again later on anyway. + // Note however that at present, + info("Building inter-update filter kernel"); + if (this->inter_update_filter_ptr->set_up(*target_image_ptr) == Succeeded::no) { + error("Error building inter-update filter"); + return Succeeded::no; } - if (this->inter_iteration_filter_interval>0 && - !is_null_ptr(this->inter_iteration_filter_ptr)) - { - // ensure that the result image of the filter is positive - shared_ptr > - thresholding_sptr(new ThresholdMinToSmallPositiveValueDataProcessor); - this->inter_iteration_filter_ptr.reset( - new ChainedDataProcessor( - this->inter_iteration_filter_ptr, - thresholding_sptr - )); - // KT 04/06/2003 moved set_up after chaining the filter (and removed it from IterativeReconstruction) - info("Building inter-iteration filter kernel"); - if (this->inter_iteration_filter_ptr->set_up(*target_image_ptr) - == Succeeded::no) - { - error("Error building inter iteration filter"); - return Succeeded::no; - } - + } + if (this->inter_iteration_filter_interval > 0 && !is_null_ptr(this->inter_iteration_filter_ptr)) { + // ensure that the result image of the filter is positive + shared_ptr> thresholding_sptr( + new ThresholdMinToSmallPositiveValueDataProcessor); + this->inter_iteration_filter_ptr.reset( + new ChainedDataProcessor(this->inter_iteration_filter_ptr, thresholding_sptr)); + // KT 04/06/2003 moved set_up after chaining the filter (and removed it from IterativeReconstruction) + info("Building inter-iteration filter kernel"); + if (this->inter_iteration_filter_ptr->set_up(*target_image_ptr) == Succeeded::no) { + error("Error building inter iteration filter"); + return Succeeded::no; } + } // initialise mutliplicative update to zeros multiplicative_update_image_ptr = unique_ptr(target_image_ptr->get_empty_copy()); @@ -392,190 +322,148 @@ set_up(shared_ptr const& target_image_ptr) return Succeeded::yes; } - template void -OSMAPOSLReconstruction:: -compute_sub_gradient_without_penalty_plus_sensitivity( - TargetT& gradient, const TargetT& current_estimate, const int subset_num) -{ - this->objective_function().compute_sub_gradient_without_penalty_plus_sensitivity( - gradient, current_estimate, subset_num); +OSMAPOSLReconstruction::compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, + const TargetT& current_estimate, + const int subset_num) { + this->objective_function().compute_sub_gradient_without_penalty_plus_sensitivity(gradient, current_estimate, subset_num); }; - template const TargetT& -OSMAPOSLReconstruction::get_subset_sensitivity(const int subset_num) -{ +OSMAPOSLReconstruction::get_subset_sensitivity(const int subset_num) { return this->objective_function().get_subset_sensitivity(subset_num); }; - template void -OSMAPOSLReconstruction::apply_multiplicative_update( - TargetT& current_image_estimate, const TargetT& multiplicative_update_image) -{ +OSMAPOSLReconstruction::apply_multiplicative_update(TargetT& current_image_estimate, + const TargetT& multiplicative_update_image) { this->check(current_image_estimate); - typename TargetT::const_full_iterator multiplicative_update_image_iter = - multiplicative_update_image.begin_all_const(); - const typename TargetT::const_full_iterator end_multiplicative_update_image_iter = - multiplicative_update_image.end_all_const(); - typename TargetT::full_iterator current_image_estimate_iter = - current_image_estimate.begin_all(); - while (multiplicative_update_image_iter != end_multiplicative_update_image_iter) - { + typename TargetT::const_full_iterator multiplicative_update_image_iter = multiplicative_update_image.begin_all_const(); + const typename TargetT::const_full_iterator end_multiplicative_update_image_iter = multiplicative_update_image.end_all_const(); + typename TargetT::full_iterator current_image_estimate_iter = current_image_estimate.begin_all(); + while (multiplicative_update_image_iter != end_multiplicative_update_image_iter) { *current_image_estimate_iter *= (*multiplicative_update_image_iter); ++current_image_estimate_iter; ++multiplicative_update_image_iter; } } - template -void -OSMAPOSLReconstruction:: -update_estimate(TargetT ¤t_image_estimate) -{ +void +OSMAPOSLReconstruction::update_estimate(TargetT& current_image_estimate) { this->check(current_image_estimate); - // TODO should use something like iterator_traits to figure out the + // TODO should use something like iterator_traits to figure out the // type instead of hard-wiring float static const float small_num = 0.000001F; - + #ifndef PARALLEL - //CPUTimer subset_timer; - //subset_timer.start(); -#else // PARALLEL + // CPUTimer subset_timer; + // subset_timer.start(); +#else // PARALLEL PTimer timerSubset; timerSubset.Start(); #endif // PARALLEL - const int subset_num=this->get_subset_num(); + const int subset_num = this->get_subset_num(); info(boost::format("Now processing subset #: %1%") % subset_num); - this->compute_sub_gradient_without_penalty_plus_sensitivity( - *multiplicative_update_image_ptr, current_image_estimate, subset_num); + this->compute_sub_gradient_without_penalty_plus_sensitivity(*multiplicative_update_image_ptr, current_image_estimate, + subset_num); // divide by subset sensitivity { const TargetT& sensitivity = this->get_subset_sensitivity(subset_num); int count = 0; - - //std::cerr <MAP_model << std::endl; - - if (this->objective_function_sptr->prior_is_zero()) - { - divide(multiplicative_update_image_ptr->begin_all(), - multiplicative_update_image_ptr->end_all(), - sensitivity.begin_all(), + + // std::cerr <MAP_model << std::endl; + + if (this->objective_function_sptr->prior_is_zero()) { + divide(multiplicative_update_image_ptr->begin_all(), multiplicative_update_image_ptr->end_all(), sensitivity.begin_all(), small_num); - } - else - { - unique_ptr< TargetT > denominator_ptr - (current_image_estimate.get_empty_copy()); - - - this->objective_function_sptr-> - get_prior_ptr()->compute_gradient(*denominator_ptr, current_image_estimate); - + } else { + unique_ptr denominator_ptr(current_image_estimate.get_empty_copy()); + + this->objective_function_sptr->get_prior_ptr()->compute_gradient(*denominator_ptr, current_image_estimate); + typename TargetT::full_iterator denominator_iter = denominator_ptr->begin_all(); const typename TargetT::full_iterator denominator_end = denominator_ptr->end_all(); typename TargetT::const_full_iterator sensitivity_iter = sensitivity.begin_all(); - if(this->MAP_model =="additive" ) - { + if (this->MAP_model == "additive") { // lambda_new = lambda / (p_v + beta*prior_gradient/ num_subsets) * // sum_subset backproj(measured/forwproj(lambda)) // with p_v = sum_{b in subset} p_bv // actually, we restrict 1 + beta*prior_gradient/num_subsets/p_v between .1 and 10 - while (denominator_iter != denominator_end) - { - *denominator_iter = *denominator_iter/this->get_num_subsets() + (*sensitivity_iter); - // bound denominator between (*sensitivity_iter)/10 and (*sensitivity_iter)*10 - *denominator_iter = - std::max(std::min(*denominator_iter, (*sensitivity_iter)*10),(*sensitivity_iter)/10); - ++denominator_iter; - ++sensitivity_iter; - } - } - else - { - if(this->MAP_model =="multiplicative" ) - { + while (denominator_iter != denominator_end) { + *denominator_iter = *denominator_iter / this->get_num_subsets() + (*sensitivity_iter); + // bound denominator between (*sensitivity_iter)/10 and (*sensitivity_iter)*10 + *denominator_iter = std::max(std::min(*denominator_iter, (*sensitivity_iter) * 10), (*sensitivity_iter) / 10); + ++denominator_iter; + ++sensitivity_iter; + } + } else { + if (this->MAP_model == "multiplicative") { // multiplicative form // lambda_new = lambda / (p_v*(1 + beta*prior_gradient)) * // sum_subset backproj(measured/forwproj(lambda)) // with p_v = sum_{b in subset} p_bv // actually, we restrict 1 + beta*prior_gradient between .1 and 10 - while (denominator_iter != denominator_end) - { + while (denominator_iter != denominator_end) { *denominator_iter += 1; // bound denominator between 1/10 and 1*10 // TODO code will fail if *denominator_iter is not a float - *denominator_iter = - std::max(std::min(*denominator_iter, 10.F),1/10.F); + *denominator_iter = std::max(std::min(*denominator_iter, 10.F), 1 / 10.F); *denominator_iter *= (*sensitivity_iter); ++denominator_iter; ++sensitivity_iter; } } - } - divide(multiplicative_update_image_ptr->begin_all(), - multiplicative_update_image_ptr->end_all(), - denominator_ptr->begin_all(), - small_num); + } + divide(multiplicative_update_image_ptr->begin_all(), multiplicative_update_image_ptr->end_all(), + denominator_ptr->begin_all(), small_num); } - + info(boost::format("Number of (cancelled) singularities in Sensitivity division: %1%") % count); } - - - if(this->inter_update_filter_interval>0 && - !is_null_ptr(this->inter_update_filter_ptr) && - !(this->subiteration_num%this->inter_update_filter_interval)) - { + + if (this->inter_update_filter_interval > 0 && !is_null_ptr(this->inter_update_filter_ptr) && + !(this->subiteration_num % this->inter_update_filter_interval)) { info("Applying inter-update filter"); - this->inter_update_filter_ptr->apply(current_image_estimate); + this->inter_update_filter_ptr->apply(current_image_estimate); } - + // KT 17/08/2000 limit update // TODO move below thresholding? - if (this->write_update_image && !this->_disable_output) - { + if (this->write_update_image && !this->_disable_output) { // allocate space for the filename assuming that // we never have more than 10^49 subiterations ... - char * fname = new char[this->output_filename_prefix.size() + 60]; + char* fname = new char[this->output_filename_prefix.size() + 60]; sprintf(fname, "%s_update_%d", this->output_filename_prefix.c_str(), this->subiteration_num); - + // Write it to file - this->output_file_format_ptr-> - write_to_file(fname, *multiplicative_update_image_ptr); + this->output_file_format_ptr->write_to_file(fname, *multiplicative_update_image_ptr); delete[] fname; } - - if (this->subiteration_num != 1) - { - const float current_min = - *std::min_element(multiplicative_update_image_ptr->begin_all(), - multiplicative_update_image_ptr->end_all()); - const float current_max = - *std::max_element(multiplicative_update_image_ptr->begin_all(), - multiplicative_update_image_ptr->end_all()); - const float new_min = - static_cast(this->minimum_relative_change); - const float new_max = - static_cast(this->maximum_relative_change); - info(boost::format("Update image old min,max: %1%, %2%, new min,max %3%, %4%") % current_min % current_max % (min(current_min, new_min)) % (max(current_max, new_max))); - - threshold_upper_lower(multiplicative_update_image_ptr->begin_all(), - multiplicative_update_image_ptr->end_all(), - new_min, new_max); - } - //current_image_estimate *= *multiplicative_update_image_ptr; + if (this->subiteration_num != 1) { + const float current_min = + *std::min_element(multiplicative_update_image_ptr->begin_all(), multiplicative_update_image_ptr->end_all()); + const float current_max = + *std::max_element(multiplicative_update_image_ptr->begin_all(), multiplicative_update_image_ptr->end_all()); + const float new_min = static_cast(this->minimum_relative_change); + const float new_max = static_cast(this->maximum_relative_change); + info(boost::format("Update image old min,max: %1%, %2%, new min,max %3%, %4%") % current_min % current_max % + (min(current_min, new_min)) % (max(current_max, new_max))); + + threshold_upper_lower(multiplicative_update_image_ptr->begin_all(), multiplicative_update_image_ptr->end_all(), new_min, + new_max); + } + + // current_image_estimate *= *multiplicative_update_image_ptr; apply_multiplicative_update(current_image_estimate, *multiplicative_update_image_ptr); #ifdef PARALLEL @@ -584,7 +472,7 @@ update_estimate(TargetT ¤t_image_estimate) #endif } -template class OSMAPOSLReconstruction >; -template class OSMAPOSLReconstruction; +template class OSMAPOSLReconstruction>; +template class OSMAPOSLReconstruction; END_NAMESPACE_STIR diff --git a/src/iterative/OSSPS/OSSPS.cxx b/src/iterative/OSSPS/OSSPS.cxx index 54f4968eb5..49cf83e956 100644 --- a/src/iterative/OSSPS/OSSPS.cxx +++ b/src/iterative/OSSPS/OSSPS.cxx @@ -23,7 +23,7 @@ \author Sanida Mustafovic \author Kris Thielemans - + */ #include "stir/OSSPS/OSSPSReconstruction.h" #include "stir/DiscretisedDensity.h" @@ -33,18 +33,15 @@ USING_NAMESPACE_STIR #ifdef STIR_MPI -int stir::distributable_main(int argc, char **argv) +int +stir::distributable_main(int argc, char** argv) #else -int main(int argc, char **argv) +int +main(int argc, char** argv) #endif { - OSSPSReconstruction > reconstruction_object(argc>1?argv[1]:""); - - - return reconstruction_object.reconstruct() == Succeeded::yes ? - EXIT_SUCCESS : EXIT_FAILURE; - + OSSPSReconstruction> reconstruction_object(argc > 1 ? argv[1] : ""); + return reconstruction_object.reconstruct() == Succeeded::yes ? EXIT_SUCCESS : EXIT_FAILURE; } - diff --git a/src/iterative/OSSPS/OSSPSReconstruction.cxx b/src/iterative/OSSPS/OSSPSReconstruction.cxx index c549ec12ce..e5e0e95106 100644 --- a/src/iterative/OSSPS/OSSPSReconstruction.cxx +++ b/src/iterative/OSSPS/OSSPSReconstruction.cxx @@ -18,13 +18,13 @@ */ /*! \file -\ingroup OSSPS +\ingroup OSSPS \ingroup reconstructors -\brief implementation of the stir::OSSPSReconstruction class +\brief implementation of the stir::OSSPSReconstruction class \author Sanida Mustafovic \author Kris Thielemans - + */ #include "stir/OSSPS/OSSPSReconstruction.h" @@ -45,9 +45,9 @@ #include #include #ifdef BOOST_NO_STRINGSTREAM -#include +# include #else -#include +# include #endif #include #include "boost/lambda/lambda.hpp" @@ -60,50 +60,42 @@ using boost::lambda::_1; using boost::lambda::_2; #endif - START_NAMESPACE_STIR //*************** parameters ************* template -const char * const -OSSPSReconstruction ::registered_name = - "OSSPS"; +const char* const OSSPSReconstruction::registered_name = "OSSPS"; template -void -OSSPSReconstruction:: -set_defaults() -{ +void +OSSPSReconstruction::set_defaults() { base_type::set_defaults(); enforce_initial_positivity = 0; upper_bound = NumericInfo().max_value(); write_update_image = 0; precomputed_denominator_filename = ""; - //MAP_model="additive"; + // MAP_model="additive"; relaxation_parameter = 1; relaxation_gamma = 0.1F; #if 0 this->do_line_search = false; #endif - } template -void -OSSPSReconstruction:: -initialise_keymap() -{ +void +OSSPSReconstruction::initialise_keymap() { base_type::initialise_keymap(); this->parser.add_start_key("OSSPSParameters"); this->parser.add_stop_key("End"); - - this->parser.add_key("enforce initial positivity condition",&enforce_initial_positivity); - //this->parser.add_key("MAP_model", &MAP_model); + + this->parser.add_key("enforce initial positivity condition", &enforce_initial_positivity); + // this->parser.add_key("MAP_model", &MAP_model); this->parser.add_key("upper bound", &upper_bound); - this->parser.add_key("write update image",&write_update_image); + this->parser.add_key("write update image", &write_update_image); this->parser.add_key("precomputed denominator", &precomputed_denominator_filename); this->parser.add_key("relaxation parameter", &relaxation_parameter); @@ -113,57 +105,45 @@ initialise_keymap() #endif } - template -void -OSSPSReconstruction:: -ask_parameters() -{ +void +OSSPSReconstruction::ask_parameters() { error("Currently incomplete code. Use a parameter file. Sorry."); base_type::ask_parameters(); - + // KT 05/07/2000 made enforce_initial_positivity int - enforce_initial_positivity= - ask("Enforce initial positivity condition?",true) ? 1 : 0; - + enforce_initial_positivity = ask("Enforce initial positivity condition?", true) ? 1 : 0; + char precomputed_denominator_filename_char[max_filename_length]; - - ask_filename_with_extension(precomputed_denominator_filename_char,"Enter file name of precomputed denominator (1 = 1's): ", ""); - - precomputed_denominator_filename=precomputed_denominator_filename_char; - + ask_filename_with_extension(precomputed_denominator_filename_char, + "Enter file name of precomputed denominator (1 = 1's): ", ""); + + precomputed_denominator_filename = precomputed_denominator_filename_char; - { - - cerr<::list_registered_names(cerr); + { + + cerr << endl << "Supply objective function type:\nPossible values:\n"; + GeneralisedObjectiveFunction::list_registered_names(cerr); const std::string objective_function_type = ask_string(""); - - this->objective_function_sptr. - reset(GeneralisedObjectiveFunction::read_registered_object(0, objective_function_type)); - - } - + + this->objective_function_sptr.reset( + GeneralisedObjectiveFunction::read_registered_object(0, objective_function_type)); + } + // KT 17/08/2000 3 new parameters const double max_in_double = static_cast(NumericInfo().max_value()); - upper_bound = ask_num("upper bound", - 1.,max_in_double,max_in_double); - - write_update_image = ask_num("write update image", 0,1,0); + upper_bound = ask_num("upper bound", 1., max_in_double, max_in_double); + + write_update_image = ask_num("write update image", 0, 1, 0); // TODO some more parameters here (relaxation et al) } - - - template -bool -OSSPSReconstruction:: -post_processing() -{ +bool +OSSPSReconstruction::post_processing() { if (base_type::post_processing()) return true; return false; @@ -172,52 +152,45 @@ post_processing() //*************** other functions ************* template -OSSPSReconstruction:: -OSSPSReconstruction() -{ +OSSPSReconstruction::OSSPSReconstruction() { set_defaults(); } template -OSSPSReconstruction:: -OSSPSReconstruction(const std::string& parameter_filename) -{ +OSSPSReconstruction::OSSPSReconstruction(const std::string& parameter_filename) { this->initialise(parameter_filename); info(this->parameter_info()); } template -std::string -OSSPSReconstruction:: -method_info() const -{ - +std::string +OSSPSReconstruction::method_info() const { + // TODO add prior name? - + #ifdef BOOST_NO_STRINGSTREAM char str[10000]; ostrstream s(str, 10000); #else std::ostringstream s; #endif - + // if(inter_update_filter_interval>0) s<<"IUF-"; if (!this->objective_function_sptr->prior_is_zero()) s << "MAP-"; - if(this->num_subsets>1) - s<<"OS-"; + if (this->num_subsets > 1) + s << "OS-"; s << "SPS"; - if(this->inter_iteration_filter_interval>0) s<<"S"; + if (this->inter_iteration_filter_interval > 0) + s << "S"; return s.str(); } template -Succeeded -OSSPSReconstruction:: -precompute_denominator_of_conditioner_without_penalty() -{ - +Succeeded +OSSPSReconstruction::precompute_denominator_of_conditioner_without_penalty() { + CPUTimer timer; timer.reset(); timer.start(); @@ -225,265 +198,204 @@ precompute_denominator_of_conditioner_without_penalty() assert(*std::max_element(precomputed_denominator_ptr->begin_all(), precomputed_denominator_ptr->end_all()) == 0); assert(*std::min_element(precomputed_denominator_ptr->begin_all(), precomputed_denominator_ptr->end_all()) == 0); - unique_ptr data_full_of_ones_aptr - ( precomputed_denominator_ptr->clone()); - std::fill(data_full_of_ones_aptr->begin_all(), - data_full_of_ones_aptr->end_all(), - 1.F); + unique_ptr data_full_of_ones_aptr(precomputed_denominator_ptr->clone()); + std::fill(data_full_of_ones_aptr->begin_all(), data_full_of_ones_aptr->end_all(), 1.F); - this->objective_function_sptr-> - add_multiplication_with_approximate_Hessian_without_penalty( - *precomputed_denominator_ptr, - *data_full_of_ones_aptr); + this->objective_function_sptr->add_multiplication_with_approximate_Hessian_without_penalty(*precomputed_denominator_ptr, + *data_full_of_ones_aptr); timer.stop(); info(boost::format("Precomputing denominator took %1% s CPU time") % timer.value()); - info(boost::format("min and max in precomputed denominator %1%, %2%") % *std::min_element(precomputed_denominator_ptr->begin_all(), precomputed_denominator_ptr->end_all()) % *std::max_element(precomputed_denominator_ptr->begin_all(), precomputed_denominator_ptr->end_all())); + info(boost::format("min and max in precomputed denominator %1%, %2%") % + *std::min_element(precomputed_denominator_ptr->begin_all(), precomputed_denominator_ptr->end_all()) % + *std::max_element(precomputed_denominator_ptr->begin_all(), precomputed_denominator_ptr->end_all())); // Write it to file { - std::string fname = - this->output_filename_prefix + - "_precomputed_denominator"; - - info(boost::format(" - Saving %1%") % fname); - this->output_file_format_ptr-> - write_to_file(fname, *precomputed_denominator_ptr); + std::string fname = this->output_filename_prefix + "_precomputed_denominator"; + + info(boost::format(" - Saving %1%") % fname); + this->output_file_format_ptr->write_to_file(fname, *precomputed_denominator_ptr); } return Succeeded::yes; } template -Succeeded -OSSPSReconstruction:: -set_up(shared_ptr const& target_image_ptr) -{ +Succeeded +OSSPSReconstruction::set_up(shared_ptr const& target_image_ptr) { if (base_type::set_up(target_image_ptr) == Succeeded::no) return Succeeded::no; - if (this->relaxation_parameter<=0) - { - warning("OSSPS: relaxation parameter should be positive but is %g", - this->relaxation_parameter); - return Succeeded::no; - } - if (this->relaxation_gamma<0) - { - warning("OSSPS: relaxation_gamma parameter should be non-negative but is %g", - this->relaxation_gamma); - return Succeeded::no; - } + if (this->relaxation_parameter <= 0) { + warning("OSSPS: relaxation parameter should be positive but is %g", this->relaxation_parameter); + return Succeeded::no; + } + if (this->relaxation_gamma < 0) { + warning("OSSPS: relaxation_gamma parameter should be non-negative but is %g", this->relaxation_gamma); + return Succeeded::no; + } - if (!is_null_ptr(this->get_prior_ptr())&& - dynamic_cast*>(this->get_prior_ptr())==0) - { + if (!is_null_ptr(this->get_prior_ptr()) && dynamic_cast*>(this->get_prior_ptr()) == 0) { warning("OSSPS: Prior must be of a type derived from PriorWithParabolicSurrogate\n"); return Succeeded::no; } - if(enforce_initial_positivity) - threshold_min_to_small_positive_value(target_image_ptr->begin_all(), - target_image_ptr->end_all(), - 10.E-6F); - - if(this->precomputed_denominator_filename=="") - { + if (enforce_initial_positivity) + threshold_min_to_small_positive_value(target_image_ptr->begin_all(), target_image_ptr->end_all(), 10.E-6F); + + if (this->precomputed_denominator_filename == "") { precomputed_denominator_ptr.reset(target_image_ptr->get_empty_copy()); precompute_denominator_of_conditioner_without_penalty(); - } - else if(this->precomputed_denominator_filename=="1") - { + } else if (this->precomputed_denominator_filename == "1") { precomputed_denominator_ptr.reset(target_image_ptr->get_empty_copy()); std::fill(precomputed_denominator_ptr->begin_all(), precomputed_denominator_ptr->end_all(), 1.F); - } - else - { - precomputed_denominator_ptr = - read_from_file(this->precomputed_denominator_filename); + } else { + precomputed_denominator_ptr = read_from_file(this->precomputed_denominator_filename); { std::string explanation; - if (!precomputed_denominator_ptr->has_same_characteristics(*target_image_ptr, explanation)) - { - warning("OSSPS: precomputed_denominator should have same characteristics as target image: %s", - explanation.c_str()); - return Succeeded::no; - } + if (!precomputed_denominator_ptr->has_same_characteristics(*target_image_ptr, explanation)) { + warning("OSSPS: precomputed_denominator should have same characteristics as target image: %s", explanation.c_str()); + return Succeeded::no; + } } - } + } return Succeeded::yes; } - - /*! \brief OSSPS additive update at every subiteration \warning This modifies *precomputed_denominator_ptr. So, you have to call set_up() before running a new reconstruction. */ template -void -OSSPSReconstruction:: -update_estimate(TargetT ¤t_image_estimate) -{ +void +OSSPSReconstruction::update_estimate(TargetT& current_image_estimate) { this->check(current_image_estimate); - if (this->get_subiteration_num() == this->get_start_subiteration_num()) - { - // set all voxels to 0 that cannot be estimated. - this->objective_function_sptr-> - fill_nonidentifiable_target_parameters(current_image_estimate, 0); - } + if (this->get_subiteration_num() == this->get_start_subiteration_num()) { + // set all voxels to 0 that cannot be estimated. + this->objective_function_sptr->fill_nonidentifiable_target_parameters(current_image_estimate, 0); + } // Check if we need to recompute the penalty term in the denominator during iterations . // For the quadratic prior, this is independent of the image (only on kappa's) // And of course, it's also independent when there is no prior // TODO by default, this should be off probably (to save time). const bool recompute_penalty_term_in_denominator = - !this->objective_function_sptr->prior_is_zero() && - static_cast const&>(*this->get_prior_ptr()). - parabolic_surrogate_curvature_depends_on_argument(); + !this->objective_function_sptr->prior_is_zero() && + static_cast const&>(*this->get_prior_ptr()) + .parabolic_surrogate_curvature_depends_on_argument(); #ifndef PARALLEL - //CPUTimer subset_timer; - //subset_timer.start(); -#else // PARALLEL + // CPUTimer subset_timer; + // subset_timer.start(); +#else // PARALLEL PTimer timerSubset; timerSubset.Start(); #endif // PARALLEL - - const int subset_num=this->get_subset_num(); + + const int subset_num = this->get_subset_num(); info(boost::format("Now processing subset #: %1%") % subset_num); - + // TODO make member or static parameter to avoid reallocation all the time - unique_ptr< TargetT > numerator_ptr - (current_image_estimate.get_empty_copy()); + unique_ptr numerator_ptr(current_image_estimate.get_empty_copy()); this->objective_function_sptr->compute_sub_gradient(*numerator_ptr, current_image_estimate, subset_num); //*numerator_ptr *= this->num_subsets; - std::transform(numerator_ptr->begin_all(), numerator_ptr->end_all(), - numerator_ptr->begin_all(), - _1 * this->num_subsets); + std::transform(numerator_ptr->begin_all(), numerator_ptr->end_all(), numerator_ptr->begin_all(), _1 * this->num_subsets); info(boost::format("num subsets %1%") % this->num_subsets); - info(boost::format("this->num_subsets*subgradient : max %1%, min %2%") % *std::max_element(numerator_ptr->begin_all(), numerator_ptr->end_all()) % *std::min_element(numerator_ptr->begin_all(), numerator_ptr->end_all())); + info(boost::format("this->num_subsets*subgradient : max %1%, min %2%") % + *std::max_element(numerator_ptr->begin_all(), numerator_ptr->end_all()) % + *std::min_element(numerator_ptr->begin_all(), numerator_ptr->end_all())); // now divide by denominator - if (recompute_penalty_term_in_denominator || - (this->get_subiteration_num() == this->get_start_subiteration_num())) - { - unique_ptr< TargetT > work_image_ptr - (current_image_estimate.get_empty_copy()); - - // avoid work (or crash) when penalty is 0 - if (!this->objective_function_sptr->prior_is_zero()) - { - static_cast&>(*get_prior_ptr()). - parabolic_surrogate_curvature(*work_image_ptr, current_image_estimate); - //*work_image_ptr *= 2; - //*work_image_ptr += *precomputed_denominator_ptr ; - std::transform(work_image_ptr->begin_all(), work_image_ptr->end_all(), - precomputed_denominator_ptr->begin_all(), - work_image_ptr->begin_all(), - _1 * 2 + _2); - } - else - *work_image_ptr = *precomputed_denominator_ptr ; - - // KT 09/12/2002 new - // avoid division by 0 by thresholding the denominator to be strictly positive - threshold_min_to_small_positive_value(work_image_ptr->begin_all(), - work_image_ptr->end_all(), - 10.E-6F); - info(boost::format(" denominator max %1%, min %2%") % *std::max_element(work_image_ptr->begin_all(), work_image_ptr->end_all()) % *std::min_element(work_image_ptr->begin_all(), work_image_ptr->end_all())); - - if (!recompute_penalty_term_in_denominator) - { - // store for future use - *precomputed_denominator_ptr = *work_image_ptr; - } - - //*numerator_ptr /= *work_image_ptr; - std::transform(numerator_ptr->begin_all(), numerator_ptr->end_all(), - work_image_ptr->begin_all(), - numerator_ptr->begin_all(), - _1 / _2); - + if (recompute_penalty_term_in_denominator || (this->get_subiteration_num() == this->get_start_subiteration_num())) { + unique_ptr work_image_ptr(current_image_estimate.get_empty_copy()); + + // avoid work (or crash) when penalty is 0 + if (!this->objective_function_sptr->prior_is_zero()) { + static_cast&>(*get_prior_ptr()) + .parabolic_surrogate_curvature(*work_image_ptr, current_image_estimate); + //*work_image_ptr *= 2; + //*work_image_ptr += *precomputed_denominator_ptr ; + std::transform(work_image_ptr->begin_all(), work_image_ptr->end_all(), precomputed_denominator_ptr->begin_all(), + work_image_ptr->begin_all(), _1 * 2 + _2); + } else + *work_image_ptr = *precomputed_denominator_ptr; + + // KT 09/12/2002 new + // avoid division by 0 by thresholding the denominator to be strictly positive + threshold_min_to_small_positive_value(work_image_ptr->begin_all(), work_image_ptr->end_all(), 10.E-6F); + info(boost::format(" denominator max %1%, min %2%") % + *std::max_element(work_image_ptr->begin_all(), work_image_ptr->end_all()) % + *std::min_element(work_image_ptr->begin_all(), work_image_ptr->end_all())); + + if (!recompute_penalty_term_in_denominator) { + // store for future use + *precomputed_denominator_ptr = *work_image_ptr; } - else - { - // we have computed the denominator already - //*numerator_ptr /= *precomputed_denominator_ptr; - std::transform(numerator_ptr->begin_all(), numerator_ptr->end_all(), - precomputed_denominator_ptr->begin_all(), - numerator_ptr->begin_all(), - _1 / _2); - } + //*numerator_ptr /= *work_image_ptr; + std::transform(numerator_ptr->begin_all(), numerator_ptr->end_all(), work_image_ptr->begin_all(), numerator_ptr->begin_all(), + _1 / _2); - //relaxation_parameter ~1/(1+n) where n is iteration number - const float relaxation_parameter = this->relaxation_parameter/ - (1+this->relaxation_gamma*(this->subiteration_num/this->num_subsets)); + } else { + // we have computed the denominator already + //*numerator_ptr /= *precomputed_denominator_ptr; + std::transform(numerator_ptr->begin_all(), numerator_ptr->end_all(), precomputed_denominator_ptr->begin_all(), + numerator_ptr->begin_all(), _1 / _2); + } + // relaxation_parameter ~1/(1+n) where n is iteration number + const float relaxation_parameter = + this->relaxation_parameter / (1 + this->relaxation_gamma * (this->subiteration_num / this->num_subsets)); info(boost::format("relaxation parameter = %1%") % relaxation_parameter); - const float alpha = 1.F; // line_search(current_image_estimate, *numerator_ptr); - // *numerator_ptr *= relaxation_parameter * alpha; - std::transform(numerator_ptr->begin_all(), numerator_ptr->end_all(), - numerator_ptr->begin_all(), - _1 * relaxation_parameter * alpha); + const float alpha = 1.F; // line_search(current_image_estimate, *numerator_ptr); + // *numerator_ptr *= relaxation_parameter * alpha; + std::transform(numerator_ptr->begin_all(), numerator_ptr->end_all(), numerator_ptr->begin_all(), + _1 * relaxation_parameter * alpha); - - if (write_update_image) - { - // Write it to file - const std::string fname = - this->make_filename_prefix_subiteration_num(this->output_filename_prefix + "_update"); - this->output_file_format_ptr-> - write_to_file(fname, *numerator_ptr); - } - - { - info(boost::format("additive update image min,max: %1%, %2%") % *std::min_element(numerator_ptr->begin_all(), numerator_ptr->end_all()) % *std::max_element(numerator_ptr->begin_all(), numerator_ptr->end_all())); + if (write_update_image) { + // Write it to file + const std::string fname = this->make_filename_prefix_subiteration_num(this->output_filename_prefix + "_update"); + this->output_file_format_ptr->write_to_file(fname, *numerator_ptr); + } - } - current_image_estimate += *numerator_ptr; + { + info(boost::format("additive update image min,max: %1%, %2%") % + *std::min_element(numerator_ptr->begin_all(), numerator_ptr->end_all()) % + *std::max_element(numerator_ptr->begin_all(), numerator_ptr->end_all())); + } + current_image_estimate += *numerator_ptr; // now threshold image { - const float current_min = - *std::min_element(current_image_estimate.begin_all(), - current_image_estimate.end_all()); - const float current_max = - *std::max_element(current_image_estimate.begin_all(), - current_image_estimate.end_all()); + const float current_min = *std::min_element(current_image_estimate.begin_all(), current_image_estimate.end_all()); + const float current_max = *std::max_element(current_image_estimate.begin_all(), current_image_estimate.end_all()); const float new_min = 0.F; - const float new_max = - static_cast(upper_bound); - info(boost::format("current image old min,max: %1%, %2%, new min,max %3%, %4%") % current_min % current_max % std::max(current_min, new_min) % std::min(current_max, new_max)); - - threshold_upper_lower(current_image_estimate.begin_all(), - current_image_estimate.end_all(), - new_min, new_max); - } + const float new_max = static_cast(upper_bound); + info(boost::format("current image old min,max: %1%, %2%, new min,max %3%, %4%") % current_min % current_max % + std::max(current_min, new_min) % std::min(current_max, new_max)); + + threshold_upper_lower(current_image_estimate.begin_all(), current_image_estimate.end_all(), new_min, new_max); + } #ifndef PARALLEL - //cerr << "Subset : " << subset_timer.value() << "secs " < >; -template class OSSPSReconstruction; +template class OSSPSReconstruction>; +template class OSSPSReconstruction; END_NAMESPACE_STIR - - diff --git a/src/iterative/POSMAPOSL/POSMAPOSL.cxx b/src/iterative/POSMAPOSL/POSMAPOSL.cxx index 2a6911f6e8..a8048f8d77 100644 --- a/src/iterative/POSMAPOSL/POSMAPOSL.cxx +++ b/src/iterative/POSMAPOSL/POSMAPOSL.cxx @@ -26,22 +26,17 @@ #include "stir/modelling/ParametricDiscretisedDensity.h" #include "stir/recon_buildblock/distributable_main.h" - - #ifdef STIR_MPI -int stir::distributable_main(int argc, char **argv) +int +stir::distributable_main(int argc, char** argv) #else -int main(int argc, char **argv) +int +main(int argc, char** argv) #endif { using namespace stir; - OSMAPOSLReconstruction - reconstruction_object(argc>1?argv[1]:""); - - return reconstruction_object.reconstruct() == Succeeded::yes ? - EXIT_SUCCESS : EXIT_FAILURE; - + OSMAPOSLReconstruction reconstruction_object(argc > 1 ? argv[1] : ""); + return reconstruction_object.reconstruct() == Succeeded::yes ? EXIT_SUCCESS : EXIT_FAILURE; } - diff --git a/src/iterative/POSSPS/POSSPS.cxx b/src/iterative/POSSPS/POSSPS.cxx index 36b0d3d6b2..4b65e6db23 100644 --- a/src/iterative/POSSPS/POSSPS.cxx +++ b/src/iterative/POSSPS/POSSPS.cxx @@ -29,17 +29,16 @@ #include "stir/Succeeded.h" #include "stir/recon_buildblock/distributable_main.h" - - #ifdef STIR_MPI -int stir::distributable_main(int argc, char **argv) +int +stir::distributable_main(int argc, char** argv) #else -int main(int argc, char **argv) +int +main(int argc, char** argv) #endif { using namespace stir; - OSSPSReconstruction reconstruction_object(argc>1?argv[1]:""); + OSSPSReconstruction reconstruction_object(argc > 1 ? argv[1] : ""); - return reconstruction_object.reconstruct() == Succeeded::yes ? - EXIT_SUCCESS : EXIT_FAILURE; + return reconstruction_object.reconstruct() == Succeeded::yes ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/src/listmode_buildblock/CListEvent.cxx b/src/listmode_buildblock/CListEvent.cxx index db96917b8d..a8577d3ee0 100644 --- a/src/listmode_buildblock/CListEvent.cxx +++ b/src/listmode_buildblock/CListEvent.cxx @@ -4,9 +4,9 @@ \file \ingroup listmode \brief Implementations of class stir::CListEvent. - + \author Kris Thielemans - + */ /* Copyright (C) 2003- 2011, Hammersmith Imanet Ltd @@ -25,7 +25,6 @@ See STIR/LICENSE.txt for details */ - #include "stir/listmode/CListRecord.h" #include "stir/ProjDataInfo.h" #include "stir/Bin.h" @@ -34,11 +33,9 @@ START_NAMESPACE_STIR -Succeeded -CListEvent:: -set_prompt(const bool) -{ - return Succeeded::no; +Succeeded +CListEvent::set_prompt(const bool) { + return Succeeded::no; } END_NAMESPACE_STIR diff --git a/src/listmode_buildblock/CListModeDataECAT.cxx b/src/listmode_buildblock/CListModeDataECAT.cxx index 40819056de..a9150e5446 100644 --- a/src/listmode_buildblock/CListModeDataECAT.cxx +++ b/src/listmode_buildblock/CListModeDataECAT.cxx @@ -17,22 +17,21 @@ */ /*! \file - \ingroup listmode + \ingroup listmode \brief Implementation of class stir::CListModeDataECAT - + \author Kris Thielemans */ - #include "stir/listmode/CListModeDataECAT.h" #include "stir/listmode/CListRecordECAT966.h" #include "stir/listmode/CListRecordECAT962.h" #include "stir/info.h" #include #ifdef HAVE_LLN_MATRIX -#include "stir/IO/stir_ecat7.h" +# include "stir/IO/stir_ecat7.h" #else -#error Need HAVE_LLN_MATRIX +# error Need HAVE_LLN_MATRIX #endif #include "boost/static_assert.hpp" #ifndef STIR_NO_NAMESPACES @@ -49,231 +48,199 @@ START_NAMESPACE_ECAT START_NAMESPACE_ECAT7 // compile time asserts -BOOST_STATIC_ASSERT(sizeof(CListTimeDataECAT966)==4); -BOOST_STATIC_ASSERT(sizeof(CListEventDataECAT966)==4); -BOOST_STATIC_ASSERT(sizeof(CListTimeDataECAT962)==4); -BOOST_STATIC_ASSERT(sizeof(CListEventDataECAT962)==4); +BOOST_STATIC_ASSERT(sizeof(CListTimeDataECAT966) == 4); +BOOST_STATIC_ASSERT(sizeof(CListEventDataECAT966) == 4); +BOOST_STATIC_ASSERT(sizeof(CListTimeDataECAT962) == 4); +BOOST_STATIC_ASSERT(sizeof(CListEventDataECAT962) == 4); template -CListModeDataECAT:: -CListModeDataECAT(const std::string& listmode_filename_prefix) - : listmode_filename_prefix(listmode_filename_prefix) -{ +CListModeDataECAT::CListModeDataECAT(const std::string& listmode_filename_prefix) + : listmode_filename_prefix(listmode_filename_prefix) { // initialise scanner_ptr before calling open_lm_file, as it is used in that function - shared_ptr scanner_sptr; - shared_ptr exam_info_sptr(new ExamInfo); - exam_info_sptr->imaging_modality = ImagingModality::PT; + shared_ptr scanner_sptr; + shared_ptr exam_info_sptr(new ExamInfo); + exam_info_sptr->imaging_modality = ImagingModality::PT; // attempt to read the .sgl file { const std::string singles_filename = listmode_filename_prefix + "_1.sgl"; ifstream singles_file(singles_filename.c_str(), ios::binary); char buffer[sizeof(Main_header)]; - if (!singles_file) - { - warning("CListModeDataECAT: Couldn't read main_header from %s. We forge ahead anyway (assuming this is ECAT 962 data).", singles_filename.c_str()); - // This should have been handled by the projdatainfo. - scanner_sptr.reset(new Scanner(Scanner::E962)); - - // TODO invalidate other fields in singles header - } - else - { - Main_header singles_main_header; - singles_file.read(buffer, - sizeof(singles_main_header)); - unmap_main_header(buffer, &singles_main_header); - // This should have been handled by the projdatainfo. - ecat::ecat7::find_scanner(scanner_sptr, singles_main_header); - - exam_info_sptr->start_time_in_secs_since_1970 = double(singles_main_header.scan_start_time); - - switch(singles_main_header.patient_orientation) - { - case FeetFirstProne: - exam_info_sptr->patient_position = PatientPosition(PatientPosition::FFP); break; - case HeadFirstProne: - exam_info_sptr->patient_position = PatientPosition(PatientPosition::HFP); break; - case FeetFirstSupine: - exam_info_sptr->patient_position = PatientPosition(PatientPosition::FFS); break; - case HeadFirstSupine: - exam_info_sptr->patient_position = PatientPosition(PatientPosition::HFS); break; - case FeetFirstRight: - exam_info_sptr->patient_position = PatientPosition(PatientPosition::FFDR); break; - case HeadFirstRight: - exam_info_sptr->patient_position = PatientPosition(PatientPosition::HFDR); break; - case FeetFirstLeft: - exam_info_sptr->patient_position = PatientPosition(PatientPosition::FFDL); break; - case HeadFirstLeft: - exam_info_sptr->patient_position = PatientPosition(PatientPosition::HFDL); break; - case UnknownOrientation: - default: - exam_info_sptr->patient_position = PatientPosition(PatientPosition::unknown_position); break; - } + if (!singles_file) { + warning("CListModeDataECAT: Couldn't read main_header from %s. We forge ahead anyway (assuming this is ECAT 962 data).", + singles_filename.c_str()); + // This should have been handled by the projdatainfo. + scanner_sptr.reset(new Scanner(Scanner::E962)); + + // TODO invalidate other fields in singles header + } else { + Main_header singles_main_header; + singles_file.read(buffer, sizeof(singles_main_header)); + unmap_main_header(buffer, &singles_main_header); + // This should have been handled by the projdatainfo. + ecat::ecat7::find_scanner(scanner_sptr, singles_main_header); + + exam_info_sptr->start_time_in_secs_since_1970 = double(singles_main_header.scan_start_time); + + switch (singles_main_header.patient_orientation) { + case FeetFirstProne: + exam_info_sptr->patient_position = PatientPosition(PatientPosition::FFP); + break; + case HeadFirstProne: + exam_info_sptr->patient_position = PatientPosition(PatientPosition::HFP); + break; + case FeetFirstSupine: + exam_info_sptr->patient_position = PatientPosition(PatientPosition::FFS); + break; + case HeadFirstSupine: + exam_info_sptr->patient_position = PatientPosition(PatientPosition::HFS); + break; + case FeetFirstRight: + exam_info_sptr->patient_position = PatientPosition(PatientPosition::FFDR); + break; + case HeadFirstRight: + exam_info_sptr->patient_position = PatientPosition(PatientPosition::HFDR); + break; + case FeetFirstLeft: + exam_info_sptr->patient_position = PatientPosition(PatientPosition::FFDL); + break; + case HeadFirstLeft: + exam_info_sptr->patient_position = PatientPosition(PatientPosition::HFDL); + break; + case UnknownOrientation: + default: + exam_info_sptr->patient_position = PatientPosition(PatientPosition::unknown_position); + break; } + } } this->set_exam_info(*exam_info_sptr); - shared_ptr tmp(ProjDataInfo::construct_proj_data_info(scanner_sptr, - 1, - scanner_sptr->get_num_rings() - 1, - scanner_sptr->get_max_num_views(), - scanner_sptr->get_max_num_non_arccorrected_bins(), - /* arc_correction*/false)); - this->set_proj_data_info_sptr( tmp ); - - if ((get_proj_data_info_sptr()->get_scanner_ptr()->get_type() == Scanner::E966 && typeid(CListRecordT) != typeid(CListRecordECAT966)) || - (get_proj_data_info_sptr()->get_scanner_ptr()->get_type() == Scanner::E962 && typeid(CListRecordT) != typeid(CListRecordECAT962))) - { - error("Data in %s is from a %s scanner, but reading with wrong type of CListModeData", - listmode_filename_prefix.c_str(), get_proj_data_info_sptr()->get_scanner_ptr()->get_name().c_str()); - } - else if (get_proj_data_info_sptr()->get_scanner_ptr()->get_type() != Scanner::E966 && get_proj_data_info_sptr()->get_scanner_ptr()->get_type() != Scanner::E962) - { - error("CListModeDataECAT: Unsupported scanner in %s", listmode_filename_prefix.c_str()); - } + shared_ptr tmp(ProjDataInfo::construct_proj_data_info(scanner_sptr, 1, scanner_sptr->get_num_rings() - 1, + scanner_sptr->get_max_num_views(), + scanner_sptr->get_max_num_non_arccorrected_bins(), + /* arc_correction*/ false)); + this->set_proj_data_info_sptr(tmp); + + if ((get_proj_data_info_sptr()->get_scanner_ptr()->get_type() == Scanner::E966 && + typeid(CListRecordT) != typeid(CListRecordECAT966)) || + (get_proj_data_info_sptr()->get_scanner_ptr()->get_type() == Scanner::E962 && + typeid(CListRecordT) != typeid(CListRecordECAT962))) { + error("Data in %s is from a %s scanner, but reading with wrong type of CListModeData", listmode_filename_prefix.c_str(), + get_proj_data_info_sptr()->get_scanner_ptr()->get_name().c_str()); + } else if (get_proj_data_info_sptr()->get_scanner_ptr()->get_type() != Scanner::E966 && + get_proj_data_info_sptr()->get_scanner_ptr()->get_type() != Scanner::E962) { + error("CListModeDataECAT: Unsupported scanner in %s", listmode_filename_prefix.c_str()); + } if (open_lm_file(1) == Succeeded::no) - error("CListModeDataECAT: error opening the first listmode file for filename %s\n", - listmode_filename_prefix.c_str()); + error("CListModeDataECAT: error opening the first listmode file for filename %s\n", listmode_filename_prefix.c_str()); } template std::string -CListModeDataECAT:: -get_name() const -{ +CListModeDataECAT::get_name() const { return listmode_filename_prefix + "_1.sgl"; } - template -shared_ptr -CListModeDataECAT:: -get_empty_record_sptr() const -{ +shared_ptr +CListModeDataECAT::get_empty_record_sptr() const { shared_ptr sptr(new CListRecordT); return sptr; } template Succeeded -CListModeDataECAT:: -open_lm_file(unsigned int new_lm_file) const -{ +CListModeDataECAT::open_lm_file(unsigned int new_lm_file) const { // current_lm_file and new_lm_file are 1-based - assert(new_lm_file>0); - - if (is_null_ptr(current_lm_data_ptr) || new_lm_file != current_lm_file) - { - // first store saved_get_positions - if (!is_null_ptr(current_lm_data_ptr)) - { - assert(current_lm_file>0); - if (current_lm_file>=saved_get_positions_for_each_lm_data.size()) - saved_get_positions_for_each_lm_data.resize(current_lm_file); - - saved_get_positions_for_each_lm_data[current_lm_file-1] = - current_lm_data_ptr->get_saved_get_positions(); - } - - // now open new file - std::string filename = listmode_filename_prefix; - char rest[50]; - sprintf(rest, "_%d.lm", new_lm_file); - filename += rest; - info(boost::format("CListModeDataECAT: opening file %1%") % filename); - shared_ptr stream_ptr(new fstream(filename.c_str(), ios::in | ios::binary)); - if (!(*stream_ptr)) - { - warning("CListModeDataECAT: cannot open file %s (probably this is perfectly ok)\n ", filename.c_str()); - return Succeeded::no; - } - current_lm_data_ptr.reset( - new InputStreamWithRecords(stream_ptr, - sizeof(CListTimeDataECAT966), - sizeof(CListTimeDataECAT966), - ByteOrder::big_endian != ByteOrder::get_native_order())); - current_lm_file = new_lm_file; + assert(new_lm_file > 0); + + if (is_null_ptr(current_lm_data_ptr) || new_lm_file != current_lm_file) { + // first store saved_get_positions + if (!is_null_ptr(current_lm_data_ptr)) { + assert(current_lm_file > 0); + if (current_lm_file >= saved_get_positions_for_each_lm_data.size()) + saved_get_positions_for_each_lm_data.resize(current_lm_file); - // now restore saved_get_positions for this file - if (!is_null_ptr(current_lm_data_ptr) && - current_lm_file - set_saved_get_positions(saved_get_positions_for_each_lm_data[current_lm_file-1]); + saved_get_positions_for_each_lm_data[current_lm_file - 1] = current_lm_data_ptr->get_saved_get_positions(); + } - return Succeeded::yes; + // now open new file + std::string filename = listmode_filename_prefix; + char rest[50]; + sprintf(rest, "_%d.lm", new_lm_file); + filename += rest; + info(boost::format("CListModeDataECAT: opening file %1%") % filename); + shared_ptr stream_ptr(new fstream(filename.c_str(), ios::in | ios::binary)); + if (!(*stream_ptr)) { + warning("CListModeDataECAT: cannot open file %s (probably this is perfectly ok)\n ", filename.c_str()); + return Succeeded::no; } - else + current_lm_data_ptr.reset( + new InputStreamWithRecords(stream_ptr, sizeof(CListTimeDataECAT966), sizeof(CListTimeDataECAT966), + ByteOrder::big_endian != ByteOrder::get_native_order())); + current_lm_file = new_lm_file; + + // now restore saved_get_positions for this file + if (!is_null_ptr(current_lm_data_ptr) && current_lm_file < saved_get_positions_for_each_lm_data.size()) + current_lm_data_ptr->set_saved_get_positions(saved_get_positions_for_each_lm_data[current_lm_file - 1]); + + return Succeeded::yes; + } else return current_lm_data_ptr->reset(); } -/*! \todo Currently switches over to the next .lm file whenever +/*! \todo Currently switches over to the next .lm file whenever get_next_record() on the current file fails. This even happens when it failed not because of EOF, or if the listmode file is shorter than 2 GB. */ template Succeeded -CListModeDataECAT:: -get_next_record(CListRecord& record_of_general_type) const -{ +CListModeDataECAT::get_next_record(CListRecord& record_of_general_type) const { CListRecordT& record = static_cast(record_of_general_type); if (current_lm_data_ptr->get_next_record(record) == Succeeded::yes) return Succeeded::yes; - else - { + else { // warning: do not modify current_lm_file here. This is done by open_lm_file // open_lm_file uses current_lm_file as well - if (open_lm_file(current_lm_file+1) == Succeeded::yes) + if (open_lm_file(current_lm_file + 1) == Succeeded::yes) return current_lm_data_ptr->get_next_record(record); else return Succeeded::no; } } - - template Succeeded -CListModeDataECAT:: -reset() -{ +CListModeDataECAT::reset() { // current_lm_file and new_lm_file are 1-based - assert(current_lm_file>0); - if (current_lm_file!=1) - { - return open_lm_file(1); - } - else - { - return current_lm_data_ptr->reset(); - } + assert(current_lm_file > 0); + if (current_lm_file != 1) { + return open_lm_file(1); + } else { + return current_lm_data_ptr->reset(); + } } - template CListModeData::SavedPosition -CListModeDataECAT:: -save_get_position() -{ +CListModeDataECAT::save_get_position() { GetPosition current_pos; - current_pos.first = current_lm_file; + current_pos.first = current_lm_file; current_pos.second = current_lm_data_ptr->save_get_position(); saved_get_positions.push_back(current_pos); - return saved_get_positions.size()-1; -} + return saved_get_positions.size() - 1; +} template Succeeded -CListModeDataECAT:: -set_get_position(const typename CListModeDataECAT::SavedPosition& pos) -{ +CListModeDataECAT::set_get_position(const typename CListModeDataECAT::SavedPosition& pos) { assert(pos < saved_get_positions.size()); if (open_lm_file(saved_get_positions[pos].first) == Succeeded::no) return Succeeded::no; - return - current_lm_data_ptr->set_get_position(saved_get_positions[pos].second); + return current_lm_data_ptr->set_get_position(saved_get_positions[pos].second); } #if 0 template @@ -336,11 +303,9 @@ get_num_records() const template shared_ptr -CListModeDataECAT:: -get_proj_data_info_sptr() const -{ - assert(!is_null_ptr(proj_data_info_sptr)); - return proj_data_info_sptr; +CListModeDataECAT::get_proj_data_info_sptr() const { + assert(!is_null_ptr(proj_data_info_sptr)); + return proj_data_info_sptr; } // instantiations diff --git a/src/listmode_buildblock/CListModeDataECAT8_32bit.cxx b/src/listmode_buildblock/CListModeDataECAT8_32bit.cxx index b44e193365..ba85e6dbbe 100644 --- a/src/listmode_buildblock/CListModeDataECAT8_32bit.cxx +++ b/src/listmode_buildblock/CListModeDataECAT8_32bit.cxx @@ -17,13 +17,12 @@ */ /*! \file - \ingroup listmode + \ingroup listmode \brief Implementation of class stir::CListModeDataECAT8_32bit \author Kris Thielemans */ - #include "stir/listmode/CListModeDataECAT8_32bit.h" #include "stir/listmode/CListRecordECAT8_32bit.h" #include "stir/Succeeded.h" @@ -32,108 +31,79 @@ #include "stir/error.h" #include - START_NAMESPACE_STIR namespace ecat { -CListModeDataECAT8_32bit:: -CListModeDataECAT8_32bit(const std::string& listmode_filename) - : listmode_filename(listmode_filename) -{ - this->interfile_parser.parse(listmode_filename.c_str());// , false /* no warnings about unrecognised keywords */); +CListModeDataECAT8_32bit::CListModeDataECAT8_32bit(const std::string& listmode_filename) : listmode_filename(listmode_filename) { + this->interfile_parser.parse(listmode_filename.c_str()); // , false /* no warnings about unrecognised keywords */); this->exam_info_sptr.reset(new ExamInfo(interfile_parser.get_exam_info())); const std::string originating_system(this->interfile_parser.get_exam_info().originating_system); shared_ptr this_scanner_sptr(Scanner::get_scanner_from_name(originating_system)); if (this_scanner_sptr->get_type() == Scanner::Unknown_scanner) - error(boost::format("Unknown value for originating_system keyword: '%s") % originating_system ); + error(boost::format("Unknown value for originating_system keyword: '%s") % originating_system); this->set_proj_data_info_sptr(interfile_parser.data_info_ptr->create_shared_clone()); if (this->open_lm_file() == Succeeded::no) - error("CListModeDataECAT8_32bit: error opening the first listmode file for filename %s\n", - listmode_filename.c_str()); + error("CListModeDataECAT8_32bit: error opening the first listmode file for filename %s\n", listmode_filename.c_str()); } std::string -CListModeDataECAT8_32bit:: -get_name() const -{ +CListModeDataECAT8_32bit::get_name() const { return listmode_filename; } - -shared_ptr -CListModeDataECAT8_32bit:: -get_empty_record_sptr() const -{ +shared_ptr +CListModeDataECAT8_32bit::get_empty_record_sptr() const { shared_ptr sptr(new CListRecordT(this->get_proj_data_info_sptr())); return sptr; } - Succeeded -CListModeDataECAT8_32bit:: -open_lm_file() -{ - //const std::string filename = interfile_parser.data_file_name; - std::string filename = interfile_parser.data_file_name; - - char directory_name[max_filename_length]; - get_directory_name(directory_name, listmode_filename.c_str()); - char full_data_file_name[max_filename_length]; - strcpy(full_data_file_name, filename.c_str()); - prepend_directory_name(full_data_file_name, directory_name); - filename = std::string(full_data_file_name); - - info(boost::format("CListModeDataECAT8_32bit: opening file %1%") % filename); +CListModeDataECAT8_32bit::open_lm_file() { + // const std::string filename = interfile_parser.data_file_name; + std::string filename = interfile_parser.data_file_name; + + char directory_name[max_filename_length]; + get_directory_name(directory_name, listmode_filename.c_str()); + char full_data_file_name[max_filename_length]; + strcpy(full_data_file_name, filename.c_str()); + prepend_directory_name(full_data_file_name, directory_name); + filename = std::string(full_data_file_name); + + info(boost::format("CListModeDataECAT8_32bit: opening file %1%") % filename); shared_ptr stream_ptr(new std::fstream(filename.c_str(), std::ios::in | std::ios::binary)); - if (!(*stream_ptr)) - { - warning("CListModeDataECAT8_32bit: cannot open file '%s'", filename.c_str()); - return Succeeded::no; - } - current_lm_data_ptr.reset( - new InputStreamWithRecords(stream_ptr, 4, 4, - ByteOrder::little_endian != ByteOrder::get_native_order())); + if (!(*stream_ptr)) { + warning("CListModeDataECAT8_32bit: cannot open file '%s'", filename.c_str()); + return Succeeded::no; + } + current_lm_data_ptr.reset(new InputStreamWithRecords( + stream_ptr, 4, 4, ByteOrder::little_endian != ByteOrder::get_native_order())); return Succeeded::yes; } - - Succeeded -CListModeDataECAT8_32bit:: -get_next_record(CListRecord& record_of_general_type) const -{ +CListModeDataECAT8_32bit::get_next_record(CListRecord& record_of_general_type) const { CListRecordT& record = static_cast(record_of_general_type); return current_lm_data_ptr->get_next_record(record); - } - +} Succeeded -CListModeDataECAT8_32bit:: -reset() -{ +CListModeDataECAT8_32bit::reset() { return current_lm_data_ptr->reset(); } - CListModeData::SavedPosition -CListModeDataECAT8_32bit:: -save_get_position() -{ +CListModeDataECAT8_32bit::save_get_position() { return static_cast(current_lm_data_ptr->save_get_position()); -} - +} Succeeded -CListModeDataECAT8_32bit:: -set_get_position(const CListModeDataECAT8_32bit::SavedPosition& pos) -{ - return - current_lm_data_ptr->set_get_position(pos); +CListModeDataECAT8_32bit::set_get_position(const CListModeDataECAT8_32bit::SavedPosition& pos) { + return current_lm_data_ptr->set_get_position(pos); } } // namespace ecat diff --git a/src/listmode_buildblock/CListModeDataGEHDF5.cxx b/src/listmode_buildblock/CListModeDataGEHDF5.cxx index d50cc8ce87..88d72c97a0 100644 --- a/src/listmode_buildblock/CListModeDataGEHDF5.cxx +++ b/src/listmode_buildblock/CListModeDataGEHDF5.cxx @@ -16,7 +16,6 @@ \author Palak Wadhwa */ - #include "stir/listmode/CListModeDataGEHDF5.h" #include "stir/Succeeded.h" #include "stir/ExamInfo.h" @@ -34,112 +33,79 @@ START_NAMESPACE_STIR namespace GE { namespace RDF_HDF5 { -CListModeDataGEHDF5:: -CListModeDataGEHDF5(const std::string& listmode_filename) - : listmode_filename(listmode_filename) -{ +CListModeDataGEHDF5::CListModeDataGEHDF5(const std::string& listmode_filename) : listmode_filename(listmode_filename) { if (open_lm_file() == Succeeded::no) - error(boost::format("CListModeDataGEHDF5: error opening the listmode file for filename %s") % - listmode_filename); + error(boost::format("CListModeDataGEHDF5: error opening the listmode file for filename %s") % listmode_filename); } std::string -CListModeDataGEHDF5:: -get_name() const -{ +CListModeDataGEHDF5::get_name() const { return listmode_filename; } std::time_t -CListModeDataGEHDF5:: -get_scan_start_time_in_secs_since_1970() const -{ +CListModeDataGEHDF5::get_scan_start_time_in_secs_since_1970() const { return this->get_exam_info().start_time_in_secs_since_1970; } - -shared_ptr -CListModeDataGEHDF5:: -get_empty_record_sptr() const -{ +shared_ptr +CListModeDataGEHDF5::get_empty_record_sptr() const { if (is_null_ptr(this->get_proj_data_info_sptr())) error("listmode file needs to be opened before calling get_empty_record_sptr()"); - shared_ptr sptr(new CListRecordT(this->get_proj_data_info_sptr(), - this->first_time_stamp)); + shared_ptr sptr(new CListRecordT(this->get_proj_data_info_sptr(), this->first_time_stamp)); return sptr; } Succeeded -CListModeDataGEHDF5:: -open_lm_file() -{ +CListModeDataGEHDF5::open_lm_file() { info(boost::format("CListModeDataGEHDF5: opening file %1%") % listmode_filename); - if(!GEHDF5Wrapper::check_GE_signature(listmode_filename)) - { - //! \todo N.E:Write a msg - return Succeeded::no; + if (!GEHDF5Wrapper::check_GE_signature(listmode_filename)) { + //! \todo N.E:Write a msg + return Succeeded::no; } - -// input_sptr.reset( new GEHDF5Wrapper(listmode_filename)); + // input_sptr.reset( new GEHDF5Wrapper(listmode_filename)); GEHDF5Wrapper inputFile(listmode_filename); this->set_proj_data_info_sptr(inputFile.get_proj_data_info_sptr()->create_shared_clone()); this->set_exam_info(*inputFile.get_exam_info_sptr()); - this->first_time_stamp = - inputFile.read_dataset_uint32("/HeaderData/ListHeader/firstTmAbsTimeStamp"); - const std::uint32_t last_time_stamp = - inputFile.read_dataset_uint32("/HeaderData/ListHeader/lastTmAbsTimeStamp"); + this->first_time_stamp = inputFile.read_dataset_uint32("/HeaderData/ListHeader/firstTmAbsTimeStamp"); + const std::uint32_t last_time_stamp = inputFile.read_dataset_uint32("/HeaderData/ListHeader/lastTmAbsTimeStamp"); this->lm_duration_in_millisecs = last_time_stamp - this->first_time_stamp; - info(boost::format("First/last time-stamp: %1%/%2%. Duration %3% ms.") - % this->first_time_stamp % last_time_stamp % this->lm_duration_in_millisecs, + info(boost::format("First/last time-stamp: %1%/%2%. Duration %3% ms.") % this->first_time_stamp % last_time_stamp % + this->lm_duration_in_millisecs, 2); //! \todo N.E: Remove hard-coded sizes; (they're stored in GEHDF5Wrapper) - current_lm_data_ptr. - reset( - new InputStreamWithRecordsFromHDF5(listmode_filename, - 6, 16)); + current_lm_data_ptr.reset(new InputStreamWithRecordsFromHDF5(listmode_filename, 6, 16)); return Succeeded::yes; } Succeeded -CListModeDataGEHDF5:: -get_next_record(CListRecord& record_of_general_type) const -{ +CListModeDataGEHDF5::get_next_record(CListRecord& record_of_general_type) const { CListRecordT& record = static_cast(record_of_general_type); return current_lm_data_ptr->get_next_record(record); } - - Succeeded -CListModeDataGEHDF5:: -reset() -{ +CListModeDataGEHDF5::reset() { return current_lm_data_ptr->reset(); } - CListModeData::SavedPosition -CListModeDataGEHDF5:: -save_get_position() -{ +CListModeDataGEHDF5::save_get_position() { return static_cast(current_lm_data_ptr->save_get_position()); } Succeeded -CListModeDataGEHDF5:: -set_get_position(const CListModeDataGEHDF5::SavedPosition& pos) -{ - return - current_lm_data_ptr->set_get_position(pos); +CListModeDataGEHDF5::set_get_position(const CListModeDataGEHDF5::SavedPosition& pos) { + return current_lm_data_ptr->set_get_position(pos); } -} // namespace -} +} // namespace RDF_HDF5 +} // namespace GE END_NAMESPACE_STIR diff --git a/src/listmode_buildblock/CListModeDataROOT.cxx b/src/listmode_buildblock/CListModeDataROOT.cxx index 4d7ab3762f..ad40c73b16 100644 --- a/src/listmode_buildblock/CListModeDataROOT.cxx +++ b/src/listmode_buildblock/CListModeDataROOT.cxx @@ -39,348 +39,295 @@ START_NAMESPACE_STIR -CListModeDataROOT:: -CListModeDataROOT(const std::string& hroot_filename) - : hroot_filename(hroot_filename) -{ - set_defaults(); - std::string error_str; - int num_virtual_axial_crystals_per_block = -1; // -1 means: use scanner default - int num_virtual_transaxial_crystals_per_block = -1; // -1 means: use scanner default - - this->parser.add_start_key("ROOT header"); - this->parser.add_stop_key("End ROOT header"); - - // Scanner related & Physical dimensions. - this->parser.add_key("originating system", &this->originating_system); - - this->parser.add_key("Number of rings", &this->num_rings); - this->parser.add_key("Number of detectors per ring", &this->num_detectors_per_ring); - this->parser.add_key("Inner ring diameter (cm)", &this->inner_ring_diameter); - this->parser.add_key("Average depth of interaction (cm)", &this->average_depth_of_interaction); - this->parser.add_key("Distance between rings (cm)", &this->ring_spacing); - this->parser.add_key("Default bin size (cm)", &this->bin_size); - this->parser.add_key("View offset (degrees)", &this->view_offset); - this->parser.add_key("Maximum number of non-arc-corrected bins", &this->max_num_non_arccorrected_bins); - this->parser.add_key("Default number of arc-corrected bins", &this->default_num_arccorrected_bins); - this->parser.add_key("Number of virtual axial crystals per block", &num_virtual_axial_crystals_per_block); - this->parser.add_key("Number of virtual transaxial crystals per block", &num_virtual_transaxial_crystals_per_block); - // end Scanner and physical dimensions. - - this->parser.add_key("energy resolution", &this->energy_resolution); - this->parser.add_key("reference energy", &this->reference_energy); - - this->parser.add_key("number of TOF time bins", &this->max_num_timing_bins); - this->parser.add_key("Size of timing bin (ps)", &this->size_timing_bin); - this->parser.add_key("Timing resolution (ps)", &this->timing_resolution); - - this->parser.add_key("%TOF mashing factor", &this->tof_mash_factor); - // - - // ROOT related - this->parser.add_parsing_key("GATE scanner type", &this->root_file_sptr); - if(!this->parser.parse(hroot_filename.c_str())) - error("CListModeDataROOT: error parsing '%s'", hroot_filename.c_str()); - - FilePath f(hroot_filename); - if (root_file_sptr->set_up( f.get_path_only()) == Succeeded::no) - error("CListModeDataROOT: Unable to set_up() from the input Header file (.hroot)."); - - // ExamInfo initialisation - shared_ptr _exam_info_sptr(new ExamInfo); - - // Only PET scanners supported - _exam_info_sptr->imaging_modality = ImagingModality::PT; - _exam_info_sptr->originating_system = this->originating_system; - _exam_info_sptr->set_low_energy_thres(this->root_file_sptr->get_low_energy_thres()); - _exam_info_sptr->set_high_energy_thres(this->root_file_sptr->get_up_energy_thres()); - - this->exam_info_sptr = _exam_info_sptr; - - shared_ptr this_scanner_sptr; - - // If the user set Scanner::User_defined_scanner then the local geometry valiables must be set. - bool give_it_a_try = false; - if (this->originating_system != "User_defined_scanner") // - { - this_scanner_sptr.reset(Scanner::get_scanner_from_name(this->originating_system)); - if (this_scanner_sptr->get_type() == Scanner::Unknown_scanner) - { - warning(boost::format("CListModeDataROOT: Unknown value for originating_system keyword: '%s.\n WIll try to " - "figure out the scanner's geometry from the parameters") % originating_system ); - give_it_a_try = true; - } - else - warning("CListModeDataROOT: I've set the scanner from STIR settings and ignored values in the hroot header."); +CListModeDataROOT::CListModeDataROOT(const std::string& hroot_filename) : hroot_filename(hroot_filename) { + set_defaults(); + std::string error_str; + int num_virtual_axial_crystals_per_block = -1; // -1 means: use scanner default + int num_virtual_transaxial_crystals_per_block = -1; // -1 means: use scanner default + + this->parser.add_start_key("ROOT header"); + this->parser.add_stop_key("End ROOT header"); + + // Scanner related & Physical dimensions. + this->parser.add_key("originating system", &this->originating_system); + + this->parser.add_key("Number of rings", &this->num_rings); + this->parser.add_key("Number of detectors per ring", &this->num_detectors_per_ring); + this->parser.add_key("Inner ring diameter (cm)", &this->inner_ring_diameter); + this->parser.add_key("Average depth of interaction (cm)", &this->average_depth_of_interaction); + this->parser.add_key("Distance between rings (cm)", &this->ring_spacing); + this->parser.add_key("Default bin size (cm)", &this->bin_size); + this->parser.add_key("View offset (degrees)", &this->view_offset); + this->parser.add_key("Maximum number of non-arc-corrected bins", &this->max_num_non_arccorrected_bins); + this->parser.add_key("Default number of arc-corrected bins", &this->default_num_arccorrected_bins); + this->parser.add_key("Number of virtual axial crystals per block", &num_virtual_axial_crystals_per_block); + this->parser.add_key("Number of virtual transaxial crystals per block", &num_virtual_transaxial_crystals_per_block); + // end Scanner and physical dimensions. + + this->parser.add_key("energy resolution", &this->energy_resolution); + this->parser.add_key("reference energy", &this->reference_energy); + + this->parser.add_key("number of TOF time bins", &this->max_num_timing_bins); + this->parser.add_key("Size of timing bin (ps)", &this->size_timing_bin); + this->parser.add_key("Timing resolution (ps)", &this->timing_resolution); + + this->parser.add_key("%TOF mashing factor", &this->tof_mash_factor); + // + + // ROOT related + this->parser.add_parsing_key("GATE scanner type", &this->root_file_sptr); + if (!this->parser.parse(hroot_filename.c_str())) + error("CListModeDataROOT: error parsing '%s'", hroot_filename.c_str()); + + FilePath f(hroot_filename); + if (root_file_sptr->set_up(f.get_path_only()) == Succeeded::no) + error("CListModeDataROOT: Unable to set_up() from the input Header file (.hroot)."); + + // ExamInfo initialisation + shared_ptr _exam_info_sptr(new ExamInfo); + + // Only PET scanners supported + _exam_info_sptr->imaging_modality = ImagingModality::PT; + _exam_info_sptr->originating_system = this->originating_system; + _exam_info_sptr->set_low_energy_thres(this->root_file_sptr->get_low_energy_thres()); + _exam_info_sptr->set_high_energy_thres(this->root_file_sptr->get_up_energy_thres()); + + this->exam_info_sptr = _exam_info_sptr; + + shared_ptr this_scanner_sptr; + + // If the user set Scanner::User_defined_scanner then the local geometry valiables must be set. + bool give_it_a_try = false; + if (this->originating_system != "User_defined_scanner") // + { + this_scanner_sptr.reset(Scanner::get_scanner_from_name(this->originating_system)); + if (this_scanner_sptr->get_type() == Scanner::Unknown_scanner) { + warning(boost::format("CListModeDataROOT: Unknown value for originating_system keyword: '%s.\n WIll try to " + "figure out the scanner's geometry from the parameters") % + originating_system); + give_it_a_try = true; + } else + warning("CListModeDataROOT: I've set the scanner from STIR settings and ignored values in the hroot header."); + } + // If the user provide a Scanner name then, the local variables will be ignored and the Scanner + // will be the selected. + else if (this->originating_system == "User_defined_scanner" || give_it_a_try) { + warning("CListModeDataROOT: Trying to figure out the scanner geometry from the information " + "given in the ROOT header file."); + + if (check_scanner_definition(error_str) == Succeeded::no) { + error(error_str.c_str()); } - // If the user provide a Scanner name then, the local variables will be ignored and the Scanner - // will be the selected. - else if (this->originating_system == "User_defined_scanner" || - give_it_a_try) - { - warning("CListModeDataROOT: Trying to figure out the scanner geometry from the information " - "given in the ROOT header file."); - - if (check_scanner_definition(error_str) == Succeeded::no) - { - error(error_str.c_str()); - } - if (default_num_arccorrected_bins == -1) - { - default_num_arccorrected_bins = max_num_non_arccorrected_bins; - } - - this_scanner_sptr.reset(new Scanner(Scanner::User_defined_scanner, - std::string ("ROOT_defined_scanner"), - /* num dets per ring */ - this->num_detectors_per_ring, - /* num of rings */ - this->num_rings, - /* number of non arccor bins */ - this->max_num_non_arccorrected_bins, - /* number of maximum arccor bins */ - this->default_num_arccorrected_bins, - /* inner ring radius */ - this->inner_ring_diameter/0.2f, - /* doi */ this->average_depth_of_interaction * 10.f, - /* ring spacing */ - this->ring_spacing * 10.f, - this->bin_size * 10.f, - /* offset*/ - this->view_offset * _PI /180, - /*num_axial_blocks_per_bucket_v */ - this->root_file_sptr->get_num_axial_blocks_per_bucket_v(), - /*num_transaxial_blocks_per_bucket_v*/ - this->root_file_sptr->get_num_transaxial_blocks_per_bucket_v(), - /*num_axial_crystals_per_block_v*/ - this->root_file_sptr->get_num_axial_crystals_per_block_v(), - /*num_transaxial_crystals_per_block_v*/ - this->root_file_sptr->get_num_transaxial_crystals_per_block_v(), - /*num_axial_crystals_per_singles_unit_v*/ - this->root_file_sptr->get_num_axial_crystals_per_singles_unit(), - /*num_transaxial_crystals_per_singles_unit_v*/ - this->root_file_sptr->get_num_trans_crystals_per_singles_unit(), - /*num_detector_layers_v*/ 1, - this->energy_resolution, - this->reference_energy, - /* maximum number of timing bins */ - max_num_timing_bins, - /* size of basic TOF bin */ - size_timing_bin, - /* Scanner's timing resolution */ - timing_resolution)); + if (default_num_arccorrected_bins == -1) { + default_num_arccorrected_bins = max_num_non_arccorrected_bins; } - // have to do this here currently as these variables cannot be set via the constructor - if (num_virtual_axial_crystals_per_block>=0) - this_scanner_sptr->set_num_virtual_axial_crystals_per_block(num_virtual_axial_crystals_per_block); - if (num_virtual_transaxial_crystals_per_block>=0) - this_scanner_sptr->set_num_virtual_transaxial_crystals_per_block(num_virtual_transaxial_crystals_per_block); - // put virtual block info in root_file_sptr - this->root_file_sptr->set_num_virtual_axial_crystals_per_block(this_scanner_sptr->get_num_virtual_axial_crystals_per_block()); - this->root_file_sptr->set_num_virtual_transaxial_crystals_per_block(this_scanner_sptr->get_num_virtual_transaxial_crystals_per_block()); - - // Compare with InputStreamFromROOTFile scanner generated geometry and throw error if wrong. - if (check_scanner_match_geometry(error_str, this_scanner_sptr) == Succeeded::no) - { - error(error_str.c_str()); - } - - proj_data_info_sptr = std::const_pointer_cast( ProjDataInfo::construct_proj_data_info(this_scanner_sptr, - 1, - this_scanner_sptr->get_num_rings()-1, - this_scanner_sptr->get_num_detectors_per_ring()/2, - this_scanner_sptr->get_max_num_non_arccorrected_bins(), - /* arc_correction*/false, - tof_mash_factor)->create_shared_clone()); - //this->set_proj_data_info_sptr(tmp); - - if (this->open_lm_file() == Succeeded::no) - error("CListModeDataROOT: error opening ROOT file for filename '%s'", - hroot_filename.c_str()); + this_scanner_sptr.reset(new Scanner(Scanner::User_defined_scanner, std::string("ROOT_defined_scanner"), + /* num dets per ring */ + this->num_detectors_per_ring, + /* num of rings */ + this->num_rings, + /* number of non arccor bins */ + this->max_num_non_arccorrected_bins, + /* number of maximum arccor bins */ + this->default_num_arccorrected_bins, + /* inner ring radius */ + this->inner_ring_diameter / 0.2f, + /* doi */ this->average_depth_of_interaction * 10.f, + /* ring spacing */ + this->ring_spacing * 10.f, this->bin_size * 10.f, + /* offset*/ + this->view_offset * _PI / 180, + /*num_axial_blocks_per_bucket_v */ + this->root_file_sptr->get_num_axial_blocks_per_bucket_v(), + /*num_transaxial_blocks_per_bucket_v*/ + this->root_file_sptr->get_num_transaxial_blocks_per_bucket_v(), + /*num_axial_crystals_per_block_v*/ + this->root_file_sptr->get_num_axial_crystals_per_block_v(), + /*num_transaxial_crystals_per_block_v*/ + this->root_file_sptr->get_num_transaxial_crystals_per_block_v(), + /*num_axial_crystals_per_singles_unit_v*/ + this->root_file_sptr->get_num_axial_crystals_per_singles_unit(), + /*num_transaxial_crystals_per_singles_unit_v*/ + this->root_file_sptr->get_num_trans_crystals_per_singles_unit(), + /*num_detector_layers_v*/ 1, this->energy_resolution, this->reference_energy, + /* maximum number of timing bins */ + max_num_timing_bins, + /* size of basic TOF bin */ + size_timing_bin, + /* Scanner's timing resolution */ + timing_resolution)); + } + // have to do this here currently as these variables cannot be set via the constructor + if (num_virtual_axial_crystals_per_block >= 0) + this_scanner_sptr->set_num_virtual_axial_crystals_per_block(num_virtual_axial_crystals_per_block); + if (num_virtual_transaxial_crystals_per_block >= 0) + this_scanner_sptr->set_num_virtual_transaxial_crystals_per_block(num_virtual_transaxial_crystals_per_block); + // put virtual block info in root_file_sptr + this->root_file_sptr->set_num_virtual_axial_crystals_per_block(this_scanner_sptr->get_num_virtual_axial_crystals_per_block()); + this->root_file_sptr->set_num_virtual_transaxial_crystals_per_block( + this_scanner_sptr->get_num_virtual_transaxial_crystals_per_block()); + + // Compare with InputStreamFromROOTFile scanner generated geometry and throw error if wrong. + if (check_scanner_match_geometry(error_str, this_scanner_sptr) == Succeeded::no) { + error(error_str.c_str()); + } + + proj_data_info_sptr = std::const_pointer_cast( + ProjDataInfo::construct_proj_data_info(this_scanner_sptr, 1, this_scanner_sptr->get_num_rings() - 1, + this_scanner_sptr->get_num_detectors_per_ring() / 2, + this_scanner_sptr->get_max_num_non_arccorrected_bins(), + /* arc_correction*/ false, tof_mash_factor) + ->create_shared_clone()); + // this->set_proj_data_info_sptr(tmp); + + if (this->open_lm_file() == Succeeded::no) + error("CListModeDataROOT: error opening ROOT file for filename '%s'", hroot_filename.c_str()); } std::string -CListModeDataROOT:: -get_name() const -{ - return hroot_filename; +CListModeDataROOT::get_name() const { + return hroot_filename; } -shared_ptr -CListModeDataROOT:: -get_empty_record_sptr() const -{ - shared_ptr sptr(new CListRecordROOT(this->get_proj_data_info_sptr())); - return sptr; +shared_ptr +CListModeDataROOT::get_empty_record_sptr() const { + shared_ptr sptr(new CListRecordROOT(this->get_proj_data_info_sptr())); + return sptr; } Succeeded -CListModeDataROOT:: -open_lm_file() -{ - info(boost::format("CListModeDataROOT: used ROOT file %s") % - this->root_file_sptr->get_ROOT_filename()); - return Succeeded::yes; +CListModeDataROOT::open_lm_file() { + info(boost::format("CListModeDataROOT: used ROOT file %s") % this->root_file_sptr->get_ROOT_filename()); + return Succeeded::yes; } - - Succeeded -CListModeDataROOT:: -get_next_record(CListRecord& record_of_general_type) const -{ - CListRecordROOT& record = dynamic_cast(record_of_general_type); - return root_file_sptr->get_next_record(record); +CListModeDataROOT::get_next_record(CListRecord& record_of_general_type) const { + CListRecordROOT& record = dynamic_cast(record_of_general_type); + return root_file_sptr->get_next_record(record); } Succeeded -CListModeDataROOT:: -reset() -{ - return root_file_sptr->reset(); +CListModeDataROOT::reset() { + return root_file_sptr->reset(); } -unsigned long CListModeDataROOT::get_total_number_of_events() const -{ - return root_file_sptr->get_total_number_of_events(); +unsigned long +CListModeDataROOT::get_total_number_of_events() const { + return root_file_sptr->get_total_number_of_events(); } CListModeData::SavedPosition -CListModeDataROOT:: -save_get_position() -{ - return static_cast(root_file_sptr->save_get_position()); +CListModeDataROOT::save_get_position() { + return static_cast(root_file_sptr->save_get_position()); } Succeeded -CListModeDataROOT:: -set_get_position(const CListModeDataROOT::SavedPosition& pos) -{ - return root_file_sptr->set_get_position(pos); +CListModeDataROOT::set_get_position(const CListModeDataROOT::SavedPosition& pos) { + return root_file_sptr->set_get_position(pos); } void -CListModeDataROOT:: -set_defaults() -{ - num_rings = -1; - num_detectors_per_ring = -1; - max_num_non_arccorrected_bins = -1; - default_num_arccorrected_bins = -1; - inner_ring_diameter = -1.f; - average_depth_of_interaction = -1.f; - ring_spacing = -.1f; - bin_size = -1.f; - view_offset = 0.f; +CListModeDataROOT::set_defaults() { + num_rings = -1; + num_detectors_per_ring = -1; + max_num_non_arccorrected_bins = -1; + default_num_arccorrected_bins = -1; + inner_ring_diameter = -1.f; + average_depth_of_interaction = -1.f; + ring_spacing = -.1f; + bin_size = -1.f; + view_offset = 0.f; } Succeeded -CListModeDataROOT:: -check_scanner_match_geometry(std::string& ret, const shared_ptr& scanner_sptr) -{ - std::ostringstream stream("CListModeDataROOT: The Scanner does not match the GATE geometry. Check: "); - bool ok = true; - - if (scanner_sptr->get_num_rings() != root_file_sptr->get_num_rings()) - { - stream << "the number of rings, "; - ok = false; - } - - if (scanner_sptr->get_num_detectors_per_ring() != root_file_sptr->get_num_dets_per_ring()) - { - stream << "the number of detector per ring, "; - ok = false; - } - - if (scanner_sptr->get_num_axial_blocks_per_bucket() != root_file_sptr->get_num_axial_blocks_per_bucket_v()) - { - stream << "the number of axial blocks per bucket, "; - ok = false; - } - - if(scanner_sptr->get_num_transaxial_blocks_per_bucket() != root_file_sptr->get_num_transaxial_blocks_per_bucket_v()) - { - stream << "the number of transaxial blocks per bucket, "; - ok = false; - } - - if(scanner_sptr->get_num_axial_crystals_per_block() != root_file_sptr->get_num_axial_crystals_per_block_v()) - { - stream << "the number of axial crystals per block, "; - ok = false; - } - - if(scanner_sptr->get_num_transaxial_crystals_per_block() != root_file_sptr->get_num_transaxial_crystals_per_block_v()) - { - stream << "the number of transaxial crystals per block, "; - ok = false; - } - - if(scanner_sptr->get_num_axial_crystals_per_singles_unit() != root_file_sptr->get_num_axial_crystals_per_singles_unit()) - { - stream << "the number of axial crystals per singles unit, "; - ok = false; - } - - if(scanner_sptr->get_num_transaxial_crystals_per_singles_unit() != root_file_sptr->get_num_trans_crystals_per_singles_unit()) - { - stream << "the number of transaxial crystals per singles unit, "; - ok = false; - } - - if (!ok) - { - ret = stream.str(); - return Succeeded::no; - } - - return Succeeded::yes; +CListModeDataROOT::check_scanner_match_geometry(std::string& ret, const shared_ptr& scanner_sptr) { + std::ostringstream stream("CListModeDataROOT: The Scanner does not match the GATE geometry. Check: "); + bool ok = true; + + if (scanner_sptr->get_num_rings() != root_file_sptr->get_num_rings()) { + stream << "the number of rings, "; + ok = false; + } + + if (scanner_sptr->get_num_detectors_per_ring() != root_file_sptr->get_num_dets_per_ring()) { + stream << "the number of detector per ring, "; + ok = false; + } + + if (scanner_sptr->get_num_axial_blocks_per_bucket() != root_file_sptr->get_num_axial_blocks_per_bucket_v()) { + stream << "the number of axial blocks per bucket, "; + ok = false; + } + + if (scanner_sptr->get_num_transaxial_blocks_per_bucket() != root_file_sptr->get_num_transaxial_blocks_per_bucket_v()) { + stream << "the number of transaxial blocks per bucket, "; + ok = false; + } + + if (scanner_sptr->get_num_axial_crystals_per_block() != root_file_sptr->get_num_axial_crystals_per_block_v()) { + stream << "the number of axial crystals per block, "; + ok = false; + } + + if (scanner_sptr->get_num_transaxial_crystals_per_block() != root_file_sptr->get_num_transaxial_crystals_per_block_v()) { + stream << "the number of transaxial crystals per block, "; + ok = false; + } + + if (scanner_sptr->get_num_axial_crystals_per_singles_unit() != root_file_sptr->get_num_axial_crystals_per_singles_unit()) { + stream << "the number of axial crystals per singles unit, "; + ok = false; + } + + if (scanner_sptr->get_num_transaxial_crystals_per_singles_unit() != root_file_sptr->get_num_trans_crystals_per_singles_unit()) { + stream << "the number of transaxial crystals per singles unit, "; + ok = false; + } + + if (!ok) { + ret = stream.str(); + return Succeeded::no; + } + + return Succeeded::yes; } Succeeded -CListModeDataROOT:: -check_scanner_definition(std::string& ret) -{ - if ( num_rings == -1 || - num_detectors_per_ring == -1 || - max_num_non_arccorrected_bins == -1 || - inner_ring_diameter == -1.f || - average_depth_of_interaction == -1.f || - ring_spacing == -.1f || - bin_size == -1.f ) - { - std::ostringstream stream("CListModeDataROOT: The User_defined_scanner has not been fully described.\nPlease include in the hroot:\n"); +CListModeDataROOT::check_scanner_definition(std::string& ret) { + if (num_rings == -1 || num_detectors_per_ring == -1 || max_num_non_arccorrected_bins == -1 || inner_ring_diameter == -1.f || + average_depth_of_interaction == -1.f || ring_spacing == -.1f || bin_size == -1.f) { + std::ostringstream stream( + "CListModeDataROOT: The User_defined_scanner has not been fully described.\nPlease include in the hroot:\n"); - if (num_rings == -1) - stream << "Number of rings := \n"; + if (num_rings == -1) + stream << "Number of rings := \n"; - if (num_detectors_per_ring == -1) - stream << "Number of detectors per ring := \n"; + if (num_detectors_per_ring == -1) + stream << "Number of detectors per ring := \n"; - if (max_num_non_arccorrected_bins == -1) - stream << "Maximum number of non-arc-corrected bins := \n"; + if (max_num_non_arccorrected_bins == -1) + stream << "Maximum number of non-arc-corrected bins := \n"; - if (inner_ring_diameter == -1) - stream << "Inner ring diameter (cm) := \n"; + if (inner_ring_diameter == -1) + stream << "Inner ring diameter (cm) := \n"; - if (average_depth_of_interaction == -1) - stream << "Average depth of interaction (cm) := \n"; + if (average_depth_of_interaction == -1) + stream << "Average depth of interaction (cm) := \n"; - if (ring_spacing == -1) - stream << "Distance between rings (cm) := \n"; + if (ring_spacing == -1) + stream << "Distance between rings (cm) := \n"; - if (bin_size == -1) - stream << "Default bin size (cm) := \n"; + if (bin_size == -1) + stream << "Default bin size (cm) := \n"; - ret = stream.str(); + ret = stream.str(); - return Succeeded::no; - } + return Succeeded::no; + } - return Succeeded::yes; + return Succeeded::yes; } - END_NAMESPACE_STIR diff --git a/src/listmode_buildblock/CListModeDataSAFIR.cxx b/src/listmode_buildblock/CListModeDataSAFIR.cxx index 4baf4382a7..541d7eef2a 100644 --- a/src/listmode_buildblock/CListModeDataSAFIR.cxx +++ b/src/listmode_buildblock/CListModeDataSAFIR.cxx @@ -2,20 +2,20 @@ Coincidence LM Data Class for SAFIR: Implementation - Copyright 2015 ETH Zurich, Institute of Particle Physics - Copyright 2020 Positrigo AG, Zurich + Copyright 2015 ETH Zurich, Institute of Particle Physics + Copyright 2020 Positrigo AG, Zurich - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ #include @@ -40,86 +40,70 @@ using std::istream; START_NAMESPACE_STIR; - template -CListModeDataSAFIR:: -CListModeDataSAFIR(const std::string& listmode_filename, const std::string& crystal_map_filename, const std::string& template_proj_data_filename, const double lor_randomization_sigma) - : listmode_filename(listmode_filename), map(MAKE_SHARED(crystal_map_filename, lor_randomization_sigma)) -{ - this->exam_info_sptr.reset(new ExamInfo); - - // Here we are reading the scanner data from the template projdata - shared_ptr template_proj_data_sptr = - ProjData::read_from_file(template_proj_data_filename); - this->set_proj_data_info_sptr(template_proj_data_sptr->get_proj_data_info_sptr()->create_shared_clone()); - - if( open_lm_file() == Succeeded::no ) - { - error("CListModeDataSAFIR: Could not open listmode file " +listmode_filename + "\n"); - } +CListModeDataSAFIR::CListModeDataSAFIR(const std::string& listmode_filename, + const std::string& crystal_map_filename, + const std::string& template_proj_data_filename, + const double lor_randomization_sigma) + : listmode_filename(listmode_filename), + map(MAKE_SHARED(crystal_map_filename, lor_randomization_sigma)) { + this->exam_info_sptr.reset(new ExamInfo); + + // Here we are reading the scanner data from the template projdata + shared_ptr template_proj_data_sptr = ProjData::read_from_file(template_proj_data_filename); + this->set_proj_data_info_sptr(template_proj_data_sptr->get_proj_data_info_sptr()->create_shared_clone()); + + if (open_lm_file() == Succeeded::no) { + error("CListModeDataSAFIR: Could not open listmode file " + listmode_filename + "\n"); + } } - template std::string -CListModeDataSAFIR:: -get_name() const -{ - return listmode_filename; +CListModeDataSAFIR::get_name() const { + return listmode_filename; } template -shared_ptr -CListModeDataSAFIR:: -get_empty_record_sptr() const -{ - shared_ptr sptr(new CListRecordSAFIR); - sptr->event_SAFIR().set_map(map); - return static_pointer_cast(sptr); +shared_ptr +CListModeDataSAFIR::get_empty_record_sptr() const { + shared_ptr sptr(new CListRecordSAFIR); + sptr->event_SAFIR().set_map(map); + return static_pointer_cast(sptr); } template Succeeded -CListModeDataSAFIR:: -get_next_record(CListRecord& record_of_general_type) const -{ - CListRecordT& record = static_cast(record_of_general_type); - Succeeded status = current_lm_data_ptr->get_next_record(record); - if( status == Succeeded::yes ) record.event_SAFIR().set_map(map); - return status; - +CListModeDataSAFIR::get_next_record(CListRecord& record_of_general_type) const { + CListRecordT& record = static_cast(record_of_general_type); + Succeeded status = current_lm_data_ptr->get_next_record(record); + if (status == Succeeded::yes) + record.event_SAFIR().set_map(map); + return status; } template Succeeded -CListModeDataSAFIR:: -reset() -{ - return current_lm_data_ptr->reset(); +CListModeDataSAFIR::reset() { + return current_lm_data_ptr->reset(); } template Succeeded -CListModeDataSAFIR:: -open_lm_file() const -{ - cerr << "CListModeDataSAFIR: opening file " << listmode_filename << endl; - shared_ptr stream_ptr(new fstream(listmode_filename.c_str(), ios::in | ios::binary )); - if(!(*stream_ptr)) - { - warning("CListModeDataSAFIR: cannot open file " + listmode_filename + "\n"); - return Succeeded::no; - } - stream_ptr->seekg((std::streamoff)32); - current_lm_data_ptr.reset( - new InputStreamWithRecords - ( stream_ptr, sizeof(CListTimeDataSAFIR), - sizeof(CListTimeDataSAFIR), - ByteOrder::little_endian !=ByteOrder::get_native_order())); - return Succeeded::yes; +CListModeDataSAFIR::open_lm_file() const { + cerr << "CListModeDataSAFIR: opening file " << listmode_filename << endl; + shared_ptr stream_ptr(new fstream(listmode_filename.c_str(), ios::in | ios::binary)); + if (!(*stream_ptr)) { + warning("CListModeDataSAFIR: cannot open file " + listmode_filename + "\n"); + return Succeeded::no; + } + stream_ptr->seekg((std::streamoff)32); + current_lm_data_ptr.reset( + new InputStreamWithRecords(stream_ptr, sizeof(CListTimeDataSAFIR), sizeof(CListTimeDataSAFIR), + ByteOrder::little_endian != ByteOrder::get_native_order())); + return Succeeded::yes; } - + template class CListModeDataSAFIR; END_NAMESPACE_STIR - diff --git a/src/listmode_buildblock/CListRecordECAT8_32bit.cxx b/src/listmode_buildblock/CListRecordECAT8_32bit.cxx index 40a78fa99a..b165f97c64 100644 --- a/src/listmode_buildblock/CListRecordECAT8_32bit.cxx +++ b/src/listmode_buildblock/CListRecordECAT8_32bit.cxx @@ -17,9 +17,9 @@ /*! \file \ingroup listmode - \brief Implementation of classes stir::ecat::CListEventECAT8_32bit and stir::ecat::CListRecordECAT8_32bit + \brief Implementation of classes stir::ecat::CListEventECAT8_32bit and stir::ecat::CListRecordECAT8_32bit for listmode events for the ECAT8 32bit listmode file format. - + \author Kris Thielemans */ @@ -34,64 +34,52 @@ START_NAMESPACE_STIR namespace ecat { -CListEventECAT8_32bit:: -CListEventECAT8_32bit(const shared_ptr& proj_data_info_sptr) : - CListEventCylindricalScannerWithDiscreteDetectors(proj_data_info_sptr) -{ - const ProjDataInfoCylindricalNoArcCorr * const proj_data_info_ptr = - dynamic_cast(proj_data_info_sptr.get()); +CListEventECAT8_32bit::CListEventECAT8_32bit(const shared_ptr& proj_data_info_sptr) + : CListEventCylindricalScannerWithDiscreteDetectors(proj_data_info_sptr) { + const ProjDataInfoCylindricalNoArcCorr* const proj_data_info_ptr = + dynamic_cast(proj_data_info_sptr.get()); if (proj_data_info_ptr == 0) error("CListEventECAT8_32bit can only be initialised with cylindrical projection data without arc-correction"); - if (proj_data_info_ptr->get_max_ring_difference(0) != proj_data_info_ptr->get_min_ring_difference(0)) - error("CListEventECAT8_32bit can only handle axial compression==1"); + if (proj_data_info_ptr->get_max_ring_difference(0) != proj_data_info_ptr->get_min_ring_difference(0)) + error("CListEventECAT8_32bit can only handle axial compression==1"); - this->segment_sequence = ecat::find_segment_sequence(*proj_data_info_ptr); - this->sizes.resize(this->segment_sequence.size()); - for (std::size_t s=0U; s < this->segment_sequence.size(); ++s) - this->sizes[s]=proj_data_info_ptr->get_num_axial_poss(segment_sequence[s]); + this->segment_sequence = ecat::find_segment_sequence(*proj_data_info_ptr); + this->sizes.resize(this->segment_sequence.size()); + for (std::size_t s = 0U; s < this->segment_sequence.size(); ++s) + this->sizes[s] = proj_data_info_ptr->get_num_axial_poss(segment_sequence[s]); } void -CListEventECAT8_32bit:: -get_detection_position(DetectionPositionPair<>& det_pos) const -{ +CListEventECAT8_32bit::get_detection_position(DetectionPositionPair<>& det_pos) const { /* data is organised by segment, axial coordinate, view, tangential */ const int num_tangential_poss = this->uncompressed_proj_data_info_sptr->get_scanner_ptr()->get_default_num_arccorrected_bins(); - const int num_views = this->uncompressed_proj_data_info_sptr->get_scanner_ptr()->get_num_detectors_per_ring()/2; + const int num_views = this->uncompressed_proj_data_info_sptr->get_scanner_ptr()->get_num_detectors_per_ring() / 2; - const int tang_pos_num = this->data.offset % num_tangential_poss;//(this->num_sinograms * this-> num_views); + const int tang_pos_num = this->data.offset % num_tangential_poss; //(this->num_sinograms * this-> num_views); const int rest = this->data.offset / num_tangential_poss; const int view_num = rest % num_views; int z = rest / num_views; int axial_pos_num = 0; int segment_num = 0; - for (std::size_t i=0; isegment_sequence.size();++i) - { - if (z< this->sizes[i]) - { - axial_pos_num = z; - segment_num = this->segment_sequence[i]; - break; - } - else - { - z -= this->sizes[i]; - } + for (std::size_t i = 0; i < this->segment_sequence.size(); ++i) { + if (z < this->sizes[i]) { + axial_pos_num = z; + segment_num = this->segment_sequence[i]; + break; + } else { + z -= this->sizes[i]; } + } // this is actually a compressed bin for many Siemens scanners. would have to go to det_pos somehow, or overload get_bin - const Bin uncompressed_bin(segment_num, view_num, axial_pos_num,tang_pos_num - (num_tangential_poss/2)); - this->get_uncompressed_proj_data_info_sptr()->get_det_pos_pair_for_bin(det_pos,uncompressed_bin); + const Bin uncompressed_bin(segment_num, view_num, axial_pos_num, tang_pos_num - (num_tangential_poss / 2)); + this->get_uncompressed_proj_data_info_sptr()->get_det_pos_pair_for_bin(det_pos, uncompressed_bin); } void -CListEventECAT8_32bit:: -set_detection_position(const DetectionPositionPair<>&) -{ +CListEventECAT8_32bit::set_detection_position(const DetectionPositionPair<>&) { error("cannot set events yet"); } - - } // namespace ecat END_NAMESPACE_STIR diff --git a/src/listmode_buildblock/CListRecordECAT962.cxx b/src/listmode_buildblock/CListRecordECAT962.cxx index ce4fcda683..7e06f82cf2 100644 --- a/src/listmode_buildblock/CListRecordECAT962.cxx +++ b/src/listmode_buildblock/CListRecordECAT962.cxx @@ -21,9 +21,9 @@ \ingroup listmode \brief Implementation of classes CListEventECAT962 and CListRecordECAT962 for listmode events for the ECAT 962 (aka Exact HR+). - + \author Kris Thielemans - + */ #include "stir/listmode/CListRecordECAT962.h" @@ -35,57 +35,48 @@ START_NAMESPACE_STIR START_NAMESPACE_ECAT START_NAMESPACE_ECAT7 - /* Global Definitions */ -static const int MAXPROJBIN = 512; +static const int MAXPROJBIN = 512; /* data for the 962 scanner */ static const int CRYSTALRINGSPERDETECTOR = 8; -//TODO NK check +// TODO NK check void -CListEventDataECAT962:: -get_sinogram_and_ring_coordinates( - int& view_num, int& tangential_pos_num, unsigned int& ring_a, unsigned int& ring_b) const -{ +CListEventDataECAT962::get_sinogram_and_ring_coordinates(int& view_num, int& tangential_pos_num, unsigned int& ring_a, + unsigned int& ring_b) const { const int NumProjBins = MAXPROJBIN; const int NumProjBinsBy2 = MAXPROJBIN / 2; view_num = view; tangential_pos_num = bin; /* KT 31/05/98 use >= in comparison now */ - if ( tangential_pos_num >= NumProjBinsBy2 ) - tangential_pos_num -= NumProjBins ; + if (tangential_pos_num >= NumProjBinsBy2) + tangential_pos_num -= NumProjBins; - ring_a = ( (block_A_ring_bit0 + 2*block_A_ring_bit1) - * CRYSTALRINGSPERDETECTOR ) + block_A_detector ; - ring_b = ( (block_B_ring_bit0 + 2*block_B_ring_bit1) - * CRYSTALRINGSPERDETECTOR ) + block_B_detector ; + ring_a = ((block_A_ring_bit0 + 2 * block_A_ring_bit1) * CRYSTALRINGSPERDETECTOR) + block_A_detector; + ring_b = ((block_B_ring_bit0 + 2 * block_B_ring_bit1) * CRYSTALRINGSPERDETECTOR) + block_B_detector; } -void -CListEventDataECAT962:: -set_sinogram_and_ring_coordinates( - const int view_num, const int tangential_pos_num, - const unsigned int ring_a, const unsigned int ring_b) -{ +void +CListEventDataECAT962::set_sinogram_and_ring_coordinates(const int view_num, const int tangential_pos_num, + const unsigned int ring_a, const unsigned int ring_b) { const int NumProjBins = MAXPROJBIN; type = 0; - const unsigned int block_A_ring = ring_a / CRYSTALRINGSPERDETECTOR; + const unsigned int block_A_ring = ring_a / CRYSTALRINGSPERDETECTOR; block_A_detector = ring_a % CRYSTALRINGSPERDETECTOR; - const unsigned int block_B_ring = ring_b / CRYSTALRINGSPERDETECTOR; + const unsigned int block_B_ring = ring_b / CRYSTALRINGSPERDETECTOR; block_B_detector = ring_b % CRYSTALRINGSPERDETECTOR; - assert(block_A_ring<4); + assert(block_A_ring < 4); block_A_ring_bit0 = block_A_ring | 0x1; - block_A_ring_bit1 = block_A_ring/2; - assert(block_B_ring<4); + block_A_ring_bit1 = block_A_ring / 2; + assert(block_B_ring < 4); block_B_ring_bit0 = block_B_ring | 0x1; - block_B_ring_bit1 = block_B_ring/2; - + block_B_ring_bit1 = block_B_ring / 2; + bin = tangential_pos_num < 0 ? tangential_pos_num + NumProjBins : tangential_pos_num; view = view_num; } - END_NAMESPACE_ECAT7 END_NAMESPACE_ECAT END_NAMESPACE_STIR diff --git a/src/listmode_buildblock/CListRecordECAT966.cxx b/src/listmode_buildblock/CListRecordECAT966.cxx index 84fb644433..9dd7978897 100644 --- a/src/listmode_buildblock/CListRecordECAT966.cxx +++ b/src/listmode_buildblock/CListRecordECAT966.cxx @@ -19,11 +19,11 @@ /*! \file \ingroup listmode - \brief Implementation of classes CListEventECAT966 and CListRecordECAT966 + \brief Implementation of classes CListEventECAT966 and CListRecordECAT966 for listmode events for the ECAT 966 (aka Exact 3d). - + \author Kris Thielemans - + */ #include "stir/listmode/CListRecordECAT966.h" @@ -39,47 +39,40 @@ START_NAMESPACE_ECAT7 // static members /* Global Definitions */ -static const int MAXPROJBIN = 512; +static const int MAXPROJBIN = 512; /* data for the 966 scanner */ static const int CRYSTALRINGSPERDETECTOR = 8; void -CListEventDataECAT966:: -get_sinogram_and_ring_coordinates( - int& view_num, int& tangential_pos_num, unsigned int& ring_a, unsigned int& ring_b) const -{ +CListEventDataECAT966::get_sinogram_and_ring_coordinates(int& view_num, int& tangential_pos_num, unsigned int& ring_a, + unsigned int& ring_b) const { const int NumProjBins = MAXPROJBIN; const int NumProjBinsBy2 = MAXPROJBIN / 2; view_num = view; tangential_pos_num = bin; /* KT 31/05/98 use >= in comparison now */ - if ( tangential_pos_num >= NumProjBinsBy2 ) - tangential_pos_num -= NumProjBins ; + if (tangential_pos_num >= NumProjBinsBy2) + tangential_pos_num -= NumProjBins; - ring_a = ( block_A_ring * CRYSTALRINGSPERDETECTOR ) + block_A_detector ; - ring_b = ( block_B_ring * CRYSTALRINGSPERDETECTOR ) + block_B_detector ; + ring_a = (block_A_ring * CRYSTALRINGSPERDETECTOR) + block_A_detector; + ring_b = (block_B_ring * CRYSTALRINGSPERDETECTOR) + block_B_detector; } -void -CListEventDataECAT966:: -set_sinogram_and_ring_coordinates( - const int view_num, const int tangential_pos_num, - const int ring_a, const int ring_b) -{ +void +CListEventDataECAT966::set_sinogram_and_ring_coordinates(const int view_num, const int tangential_pos_num, const int ring_a, + const int ring_b) { const int NumProjBins = MAXPROJBIN; type = 0; - block_A_ring = ring_a / CRYSTALRINGSPERDETECTOR; + block_A_ring = ring_a / CRYSTALRINGSPERDETECTOR; block_A_detector = ring_a % CRYSTALRINGSPERDETECTOR; - block_B_ring = ring_b / CRYSTALRINGSPERDETECTOR; + block_B_ring = ring_b / CRYSTALRINGSPERDETECTOR; block_B_detector = ring_b % CRYSTALRINGSPERDETECTOR; bin = tangential_pos_num < 0 ? tangential_pos_num + NumProjBins : tangential_pos_num; view = view_num; } - - END_NAMESPACE_ECAT7 END_NAMESPACE_ECAT END_NAMESPACE_STIR diff --git a/src/listmode_buildblock/CListRecordROOT.cxx b/src/listmode_buildblock/CListRecordROOT.cxx index c4f033c422..bd7df2ed49 100644 --- a/src/listmode_buildblock/CListRecordROOT.cxx +++ b/src/listmode_buildblock/CListRecordROOT.cxx @@ -30,12 +30,9 @@ START_NAMESPACE_STIR - -CListEventROOT:: -CListEventROOT(const shared_ptr &proj_data_info) : - CListEventCylindricalScannerWithDiscreteDetectors(proj_data_info) -{ - quarter_of_detectors = static_cast(proj_data_info->get_scanner_ptr()->get_num_detectors_per_ring()/4.f); +CListEventROOT::CListEventROOT(const shared_ptr& proj_data_info) + : CListEventCylindricalScannerWithDiscreteDetectors(proj_data_info) { + quarter_of_detectors = static_cast(proj_data_info->get_scanner_ptr()->get_num_detectors_per_ring() / 4.f); } //! @@ -43,46 +40,45 @@ CListEventROOT(const shared_ptr &proj_data_info) : //! \param det_pos //! \author Nikos Efthimiou //! -void CListEventROOT::get_detection_position(DetectionPositionPair<>& _det_pos) const -{ +void +CListEventROOT::get_detection_position(DetectionPositionPair<>& _det_pos) const { - DetectionPosition<> det1(this->det1, this->ring1, 0); - DetectionPosition<> det2(this->det2, this->ring2, 0); + DetectionPosition<> det1(this->det1, this->ring1, 0); + DetectionPosition<> det2(this->det2, this->ring2, 0); - _det_pos.pos1() = det1; - _det_pos.pos2() = det2; - _det_pos.timing_pos() = this->get_uncompressed_proj_data_info_sptr()->get_tof_bin(delta_time); -// _det_pos.timing_pos() = this->get_uncompressed_proj_data_info_sptr()->get_unmashed_tof_bin(delta_time); + _det_pos.pos1() = det1; + _det_pos.pos2() = det2; + _det_pos.timing_pos() = this->get_uncompressed_proj_data_info_sptr()->get_tof_bin(delta_time); + // _det_pos.timing_pos() = this->get_uncompressed_proj_data_info_sptr()->get_unmashed_tof_bin(delta_time); } -void CListEventROOT::set_detection_position(const DetectionPositionPair<>&) -{ - error("Cannot set events in a ROOT file!"); +void +CListEventROOT::set_detection_position(const DetectionPositionPair<>&) { + error("Cannot set events in a ROOT file!"); } -void CListEventROOT::init_from_data(const int& _ring1, const int& _ring2, - const int& crystal1, const int& crystal2, - const double& _delta_time) -{ - - // STIR assumes that 0 is on y whill GATE on the x axis - det1 = crystal1 + quarter_of_detectors; - det2 = crystal2 + quarter_of_detectors; - - if (det1 < 0 ) - det1 = this->uncompressed_proj_data_info_sptr->get_scanner_ptr()->get_num_detectors_per_ring() + det1; - else if ( det1 >= this->uncompressed_proj_data_info_sptr->get_scanner_ptr()->get_num_detectors_per_ring()) - det1 = det1 - this->uncompressed_proj_data_info_sptr->get_scanner_ptr()->get_num_detectors_per_ring(); - - if (det2 < 0 ) - det2 = this->uncompressed_proj_data_info_sptr->get_scanner_ptr()->get_num_detectors_per_ring() + det2; - else if ( det2 >= this->uncompressed_proj_data_info_sptr->get_scanner_ptr()->get_num_detectors_per_ring()) - det2 = det2 - this->uncompressed_proj_data_info_sptr->get_scanner_ptr()->get_num_detectors_per_ring(); - - ring1 = _ring1; - ring2 = _ring2; - delta_time = _delta_time; - swapped = false; +void +CListEventROOT::init_from_data(const int& _ring1, const int& _ring2, const int& crystal1, const int& crystal2, + const double& _delta_time) { + + // STIR assumes that 0 is on y whill GATE on the x axis + det1 = crystal1 + quarter_of_detectors; + det2 = crystal2 + quarter_of_detectors; + + if (det1 < 0) + det1 = this->uncompressed_proj_data_info_sptr->get_scanner_ptr()->get_num_detectors_per_ring() + det1; + else if (det1 >= this->uncompressed_proj_data_info_sptr->get_scanner_ptr()->get_num_detectors_per_ring()) + det1 = det1 - this->uncompressed_proj_data_info_sptr->get_scanner_ptr()->get_num_detectors_per_ring(); + + if (det2 < 0) + det2 = this->uncompressed_proj_data_info_sptr->get_scanner_ptr()->get_num_detectors_per_ring() + det2; + else if (det2 >= this->uncompressed_proj_data_info_sptr->get_scanner_ptr()->get_num_detectors_per_ring()) + det2 = det2 - this->uncompressed_proj_data_info_sptr->get_scanner_ptr()->get_num_detectors_per_ring(); + + ring1 = _ring1; + ring2 = _ring2; + delta_time = _delta_time; + swapped = false; } END_NAMESPACE_STIR diff --git a/src/listmode_buildblock/DetectorCoordinateMapFromFile.cxx b/src/listmode_buildblock/DetectorCoordinateMapFromFile.cxx index af06cff109..ef89bb7b5d 100644 --- a/src/listmode_buildblock/DetectorCoordinateMapFromFile.cxx +++ b/src/listmode_buildblock/DetectorCoordinateMapFromFile.cxx @@ -2,58 +2,62 @@ Read List-Mode Event Data using map from file: Implementation - Copyright 2015 ETH Zurich, Institute of Particle Physics + Copyright 2015 ETH Zurich, Institute of Particle Physics - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ #include "stir/error.h" #include "stir/listmode/DetectorCoordinateMapFromFile.h" START_NAMESPACE_STIR - -void DetectorCoordinateMapFromFile::read_detectormap_from_file( const std::string& filename ) -{ - std::ifstream myfile(filename.c_str()); - if( !myfile ) - { - error("Error opening file " + filename + ".\n"); - return; - } - -// char line[80]; - std::string line; - while( std::getline( myfile, line)) - { - if( line.size() && line[0] == '#' ) continue; - bool has_layer_index = false; - stir::CartesianCoordinate3D coord; - stir::DetectionPosition<> detpos; - std::vector col; - boost::split(col, line, boost::is_any_of("\t,")); - if( !col.size() ) break; - else if( col.size() == 5 ) has_layer_index = false; - else if( col.size() == 6 ) has_layer_index = true; - coord[1] = static_cast(atof(col[4+has_layer_index].c_str() )); - coord[2] = static_cast(atof(col[3+has_layer_index].c_str() )); - coord[3] = static_cast(atof(col[2+has_layer_index].c_str() )); - - if( !has_layer_index ) detpos.radial_coord() = 0; - else detpos.radial_coord() = atoi(col[2].c_str()); - detpos.axial_coord() = atoi(col[0].c_str()); - detpos.tangential_coord() = atoi(col[1].c_str()); - - coord_map[detpos] = coord; - } + +void +DetectorCoordinateMapFromFile::read_detectormap_from_file(const std::string& filename) { + std::ifstream myfile(filename.c_str()); + if (!myfile) { + error("Error opening file " + filename + ".\n"); + return; + } + + // char line[80]; + std::string line; + while (std::getline(myfile, line)) { + if (line.size() && line[0] == '#') + continue; + bool has_layer_index = false; + stir::CartesianCoordinate3D coord; + stir::DetectionPosition<> detpos; + std::vector col; + boost::split(col, line, boost::is_any_of("\t,")); + if (!col.size()) + break; + else if (col.size() == 5) + has_layer_index = false; + else if (col.size() == 6) + has_layer_index = true; + coord[1] = static_cast(atof(col[4 + has_layer_index].c_str())); + coord[2] = static_cast(atof(col[3 + has_layer_index].c_str())); + coord[3] = static_cast(atof(col[2 + has_layer_index].c_str())); + + if (!has_layer_index) + detpos.radial_coord() = 0; + else + detpos.radial_coord() = atoi(col[2].c_str()); + detpos.axial_coord() = atoi(col[0].c_str()); + detpos.tangential_coord() = atoi(col[1].c_str()); + + coord_map[detpos] = coord; + } } END_NAMESPACE_STIR diff --git a/src/listmode_buildblock/ListEvent.cxx b/src/listmode_buildblock/ListEvent.cxx index b0d29651f9..c40bf48889 100644 --- a/src/listmode_buildblock/ListEvent.cxx +++ b/src/listmode_buildblock/ListEvent.cxx @@ -4,7 +4,7 @@ \file \ingroup listmode \brief Implementations of class stir::ListEvent. - + \author Daniel Deidda \author Kris Thielemans @@ -27,7 +27,6 @@ See STIR/LICENSE.txt for details */ - #include "stir/listmode/ListRecord.h" #include "stir/ProjDataInfo.h" #include "stir/Bin.h" @@ -36,12 +35,9 @@ START_NAMESPACE_STIR -void -ListEvent:: -get_bin(Bin& bin, const ProjDataInfo& proj_data_info) const -{ +void +ListEvent::get_bin(Bin& bin, const ProjDataInfo& proj_data_info) const { bin = proj_data_info.get_bin(get_LOR()); } END_NAMESPACE_STIR - diff --git a/src/listmode_buildblock/ListModeData.cxx b/src/listmode_buildblock/ListModeData.cxx index 6fcafc0f9a..ac8fcd97bb 100644 --- a/src/listmode_buildblock/ListModeData.cxx +++ b/src/listmode_buildblock/ListModeData.cxx @@ -31,37 +31,27 @@ START_NAMESPACE_STIR -ListModeData:: -ListModeData() -{ -} +ListModeData::ListModeData() {} -ListModeData:: -~ListModeData() -{} +ListModeData::~ListModeData() {} shared_ptr -ListModeData:: -get_scanner_ptr() const -{ - if(is_null_ptr(proj_data_info_sptr)) - error("ListModeData: ProjDataInfo has not been set."); +ListModeData::get_scanner_ptr() const { + if (is_null_ptr(proj_data_info_sptr)) + error("ListModeData: ProjDataInfo has not been set."); return proj_data_info_sptr->get_scanner_sptr(); } void -ListModeData:: -set_proj_data_info_sptr(shared_ptr new_proj_data_info_sptr) -{ - proj_data_info_sptr = new_proj_data_info_sptr->create_shared_clone(); +ListModeData::set_proj_data_info_sptr(shared_ptr new_proj_data_info_sptr) { + proj_data_info_sptr = new_proj_data_info_sptr->create_shared_clone(); } shared_ptr -ListModeData::get_proj_data_info_sptr() const -{ - if(is_null_ptr(proj_data_info_sptr)) - error("ListModeData: ProjDataInfo has not been set."); - return proj_data_info_sptr; +ListModeData::get_proj_data_info_sptr() const { + if (is_null_ptr(proj_data_info_sptr)) + error("ListModeData: ProjDataInfo has not been set."); + return proj_data_info_sptr; } #if 0 diff --git a/src/listmode_buildblock/LmToProjData.cxx b/src/listmode_buildblock/LmToProjData.cxx index e108463a25..6ca365ed6d 100644 --- a/src/listmode_buildblock/LmToProjData.cxx +++ b/src/listmode_buildblock/LmToProjData.cxx @@ -1,9 +1,9 @@ /*! - \file + \file \ingroup listmode \brief Implementation of class stir::LmToProjData - + \author Nikos Efthimiou \author Kris Thielemans \author Sanida Mustafovic @@ -28,8 +28,8 @@ */ /* Possible compilation switches: - -USE_SegmentByView + +USE_SegmentByView Currently our ProjData classes store segments as floats, which is a waste of memory and time for simple binning of listmode data. This should be remedied at some point by having member template functions to allow different @@ -41,7 +41,7 @@ USE_SegmentByView FRAME_BASED_DT_CORR: dead-time correction based on the frame, or on the time of the event -*/ +*/ // (Note: can currently NOT be disabled) #define USE_SegmentByView @@ -50,16 +50,15 @@ USE_SegmentByView // set elem_type to what you want to use for the sinogram elements // we need a signed type, as randoms can be subtracted. However, signed char could do. -#if defined(USE_SegmentByView) - typedef float elem_type; +#if defined(USE_SegmentByView) +typedef float elem_type; # define OUTPUTNumericType NumericType::FLOAT #else - #error currently problem with normalisation code! - typedef short elem_type; +# error currently problem with normalisation code! +typedef short elem_type; # define OUTPUTNumericType NumericType::SHORT #endif - #include "stir/utilities.h" #include "stir/listmode/LmToProjData.h" @@ -70,13 +69,13 @@ USE_SegmentByView #include "stir/Scanner.h" #ifdef USE_SegmentByView -#include "stir/ProjDataInterfile.h" -#include "stir/SegmentByView.h" +# include "stir/ProjDataInterfile.h" +# include "stir/SegmentByView.h" #else -#include "stir/ProjDataFromStream.h" -#include "stir/IO/interfile.h" -#include "stir/Array.h" -#include "stir/IndexRange3D.h" +# include "stir/ProjDataFromStream.h" +# include "stir/IO/interfile.h" +# include "stir/Array.h" +# include "stir/IndexRange3D.h" #endif #include "stir/IO/read_from_file.h" #include "stir/ParsingObject.h" @@ -111,251 +110,191 @@ START_NAMESPACE_STIR #ifdef USE_SegmentByView typedef SegmentByView segment_type; #else -#error does not work at the moment +# error does not work at the moment #endif /******************** Prototypes for local routines ************************/ +static void allocate_segments(VectorWithOffset>& segments, const int start_timing_pos_index, + const int end_timing_pos_index, const int start_segment_index, const int end_segment_index, + const shared_ptr proj_data_info_ptr); - -static void -allocate_segments(VectorWithOffset >& segments, - const int start_timing_pos_index, - const int end_timing_pos_index, - const int start_segment_index, - const int end_segment_index, - const shared_ptr proj_data_info_ptr); - -// In the next 2 functions, the 'output' parameter needs to be passed +// In the next 2 functions, the 'output' parameter needs to be passed // because save_and_delete_segments needs it when we're not using SegmentByView /* last parameter only used if USE_SegmentByView first parameter only used when not USE_SegmentByView - */ -static void -save_and_delete_segments(shared_ptr& output, - VectorWithOffset >& segments, - const int start_timing_pos_index, - const int end_timing_pos_index, - const int start_segment_index, - const int end_segment_index, - ProjData& proj_data); -static -shared_ptr -construct_proj_data(shared_ptr& output, - const string& output_filename, - const ExamInfo& exam_info, - const shared_ptr& proj_data_info_ptr); + */ +static void save_and_delete_segments(shared_ptr& output, VectorWithOffset>& segments, + const int start_timing_pos_index, const int end_timing_pos_index, + const int start_segment_index, const int end_segment_index, ProjData& proj_data); +static shared_ptr construct_proj_data(shared_ptr& output, const string& output_filename, + const ExamInfo& exam_info, + const shared_ptr& proj_data_info_ptr); /************************************************************** The 3 parsing functions ***************************************************************/ -void -LmToProjData:: -set_defaults() -{ +void +LmToProjData::set_defaults() { max_segment_num_to_process = -1; store_prompts = true; store_delayeds = true; - interactive=false; + interactive = false; num_segments_in_memory = -1; num_timing_poss_in_memory = 1; normalisation_ptr.reset(new TrivialBinNormalisation); post_normalisation_ptr.reset(new TrivialBinNormalisation); - do_pre_normalisation =0; + do_pre_normalisation = 0; num_events_to_store = 0L; do_time_frame = false; } -void -LmToProjData:: -initialise_keymap() -{ +void +LmToProjData::initialise_keymap() { parser.add_start_key("lm_to_projdata Parameters"); - parser.add_key("input file",&input_filename); + parser.add_key("input file", &input_filename); parser.add_key("template_projdata", &template_proj_data_name); - parser.add_key("frame_definition file",&frame_definition_filename); - parser.add_key("num_events_to_store",&num_events_to_store); - parser.add_key("output filename prefix",&output_filename_prefix); + parser.add_key("frame_definition file", &frame_definition_filename); + parser.add_key("num_events_to_store", &num_events_to_store); + parser.add_key("output filename prefix", &output_filename_prefix); parser.add_parsing_key("Bin Normalisation type for pre-normalisation", &normalisation_ptr); parser.add_parsing_key("Bin Normalisation type for post-normalisation", &post_normalisation_ptr); - parser.add_key("maximum absolute segment number to process", &max_segment_num_to_process); + parser.add_key("maximum absolute segment number to process", &max_segment_num_to_process); parser.add_key("do pre normalisation ", &do_pre_normalisation); parser.add_key("num_TOF_bins_in_memory", &num_timing_poss_in_memory); parser.add_key("num_segments_in_memory", &num_segments_in_memory); - //if (lm_data_ptr->has_delayeds()) TODO we haven't read the ListModeData yet, so cannot access has_delayeds() yet + // if (lm_data_ptr->has_delayeds()) TODO we haven't read the ListModeData yet, so cannot access has_delayeds() yet // one could add the next 2 keywords as part of a callback function for the 'input file' keyword. // That's a bit too much trouble for now though... { - parser.add_key("Store prompts",&store_prompts); - parser.add_key("Store delayeds",&store_delayeds); - //parser.add_key("increment to use for 'delayeds'",&delayed_increment); + parser.add_key("Store prompts", &store_prompts); + parser.add_key("Store delayeds", &store_delayeds); + // parser.add_key("increment to use for 'delayeds'",&delayed_increment); } - parser.add_key("List event coordinates",&interactive); - parser.add_stop_key("END"); - + parser.add_key("List event coordinates", &interactive); + parser.add_stop_key("END"); } - bool -LmToProjData:: -post_processing() -{ +LmToProjData::post_processing() { - if (input_filename.size()==0) - { - warning("You have to specify an input_filename\n"); - return true; - } + if (input_filename.size() == 0) { + warning("You have to specify an input_filename\n"); + return true; + } - if (!interactive && output_filename_prefix.size()==0) - { - warning("You have to specify an output_filename_prefix\n"); - return true; - } + if (!interactive && output_filename_prefix.size() == 0) { + warning("You have to specify an output_filename_prefix\n"); + return true; + } lm_data_ptr = stir::read_from_file(input_filename); - if (template_proj_data_name.size()==0) - { - warning("You have to specify template_projdata\n"); - return true; - } - shared_ptr template_proj_data_ptr = - ProjData::read_from_file(template_proj_data_name); + if (template_proj_data_name.size() == 0) { + warning("You have to specify template_projdata\n"); + return true; + } + shared_ptr template_proj_data_ptr = ProjData::read_from_file(template_proj_data_name); template_proj_data_info_ptr.reset(template_proj_data_ptr->get_proj_data_info_sptr()->clone()); // propagate relevant metadata - template_proj_data_info_ptr->set_bed_position_horizontal - (lm_data_ptr->get_proj_data_info_sptr()->get_bed_position_horizontal()); - template_proj_data_info_ptr->set_bed_position_vertical - (lm_data_ptr->get_proj_data_info_sptr()->get_bed_position_vertical()); + template_proj_data_info_ptr->set_bed_position_horizontal(lm_data_ptr->get_proj_data_info_sptr()->get_bed_position_horizontal()); + template_proj_data_info_ptr->set_bed_position_vertical(lm_data_ptr->get_proj_data_info_sptr()->get_bed_position_vertical()); // initialise segment_num related variables - if (max_segment_num_to_process==-1) - max_segment_num_to_process = - template_proj_data_info_ptr->get_max_segment_num(); - else - { - max_segment_num_to_process = - min(max_segment_num_to_process, - template_proj_data_info_ptr->get_max_segment_num()); - template_proj_data_info_ptr-> - reduce_segment_range(-max_segment_num_to_process, - max_segment_num_to_process); - } + if (max_segment_num_to_process == -1) + max_segment_num_to_process = template_proj_data_info_ptr->get_max_segment_num(); + else { + max_segment_num_to_process = min(max_segment_num_to_process, template_proj_data_info_ptr->get_max_segment_num()); + template_proj_data_info_ptr->reduce_segment_range(-max_segment_num_to_process, max_segment_num_to_process); + } const int num_segments = template_proj_data_info_ptr->get_num_segments(); if (num_segments_in_memory == -1 || interactive) num_segments_in_memory = num_segments; else - num_segments_in_memory = - min(num_segments_in_memory, num_segments); - if (num_segments == 0) - { - warning("LmToProjData: num_segments_in_memory cannot be 0"); - return true; - } - + num_segments_in_memory = min(num_segments_in_memory, num_segments); + if (num_segments == 0) { + warning("LmToProjData: num_segments_in_memory cannot be 0"); + return true; + } + Scanner const* const scanner_ptr = template_proj_data_info_ptr->get_scanner_ptr(); - Scanner const * const scanner_ptr = - template_proj_data_info_ptr->get_scanner_ptr(); + if (*scanner_ptr != *lm_data_ptr->get_scanner_ptr()) { + warning("LmToProjData:\nScanner from list mode data (%s) is different from\n" + "scanner from template projdata (%s).\n" + "Full definition of scanner from list mode data:\n%s\n" + "Full definition of scanner from template:\n%s\n", + lm_data_ptr->get_scanner_ptr()->get_name().c_str(), scanner_ptr->get_name().c_str(), + lm_data_ptr->get_scanner_ptr()->parameter_info().c_str(), scanner_ptr->parameter_info().c_str()); + return true; + } - if (*scanner_ptr != *lm_data_ptr->get_scanner_ptr()) - { - warning("LmToProjData:\nScanner from list mode data (%s) is different from\n" - "scanner from template projdata (%s).\n" - "Full definition of scanner from list mode data:\n%s\n" - "Full definition of scanner from template:\n%s\n", - lm_data_ptr->get_scanner_ptr()->get_name().c_str(), - scanner_ptr->get_name().c_str(), - lm_data_ptr->get_scanner_ptr()->parameter_info().c_str(), - scanner_ptr->parameter_info().c_str()); - return true; - } - // handle store_prompts and store_delayeds - if (lm_data_ptr->has_delayeds()==false && store_delayeds==true) - { - warning("This list mode data does not seem to have delayed events.\n" - "Setting store_delayeds to false."); - store_delayeds=true; - } - - if (store_prompts) - { - if (store_delayeds) - delayed_increment = -1; - else - delayed_increment = 0; - } - else - { - if (store_delayeds) - delayed_increment = 1; - else - { - warning("At least one of store_prompts or store_delayeds should be true"); - return true; - } + if (lm_data_ptr->has_delayeds() == false && store_delayeds == true) { + warning("This list mode data does not seem to have delayed events.\n" + "Setting store_delayeds to false."); + store_delayeds = true; + } + + if (store_prompts) { + if (store_delayeds) + delayed_increment = -1; + else + delayed_increment = 0; + } else { + if (store_delayeds) + delayed_increment = 1; + else { + warning("At least one of store_prompts or store_delayeds should be true"); + return true; } + } // set up normalisation objects - if (is_null_ptr(normalisation_ptr)) - { - warning("Invalid pre-normalisation object\n"); - return true; - } - if (is_null_ptr(post_normalisation_ptr)) - { - warning("Invalid post-normalisation object\n"); - return true; - } + if (is_null_ptr(normalisation_ptr)) { + warning("Invalid pre-normalisation object\n"); + return true; + } + if (is_null_ptr(post_normalisation_ptr)) { + warning("Invalid post-normalisation object\n"); + return true; + } - if (do_pre_normalisation) - { - shared_ptr scanner_sptr(new Scanner(*scanner_ptr)); - // TODO this won't work for the HiDAC or so - // N.E: The following command used to do a dynamic cast which now I removed. - proj_data_info_cyl_uncompressed_ptr.reset(ProjDataInfo::ProjDataInfoCTI(scanner_sptr, - 1, scanner_ptr->get_num_rings()-1, - scanner_ptr->get_num_detectors_per_ring()/2, - scanner_ptr->get_default_num_arccorrected_bins(), - false, - 1)); - - if ( normalisation_ptr->set_up(lm_data_ptr->get_exam_info_sptr(), proj_data_info_cyl_uncompressed_ptr) - != Succeeded::yes) - error("LmToProjData: set-up of pre-normalisation failed\n"); - } - else - { - if ( post_normalisation_ptr->set_up(lm_data_ptr->get_exam_info_sptr(),template_proj_data_info_ptr) - != Succeeded::yes) - error("LmToProjData: set-up of post-normalisation failed\n"); - } + if (do_pre_normalisation) { + shared_ptr scanner_sptr(new Scanner(*scanner_ptr)); + // TODO this won't work for the HiDAC or so + // N.E: The following command used to do a dynamic cast which now I removed. + proj_data_info_cyl_uncompressed_ptr.reset(ProjDataInfo::ProjDataInfoCTI( + scanner_sptr, 1, scanner_ptr->get_num_rings() - 1, scanner_ptr->get_num_detectors_per_ring() / 2, + scanner_ptr->get_default_num_arccorrected_bins(), false, 1)); + + if (normalisation_ptr->set_up(lm_data_ptr->get_exam_info_sptr(), proj_data_info_cyl_uncompressed_ptr) != Succeeded::yes) + error("LmToProjData: set-up of pre-normalisation failed\n"); + } else { + if (post_normalisation_ptr->set_up(lm_data_ptr->get_exam_info_sptr(), template_proj_data_info_ptr) != Succeeded::yes) + error("LmToProjData: set-up of post-normalisation failed\n"); + } // handle time frame definitions etc // If num_events_to_store == 0 && frame_definition_filename.size == 0 - if(num_events_to_store==0 && frame_definition_filename.size() == 0) - do_time_frame = true; + if (num_events_to_store == 0 && frame_definition_filename.size() == 0) + do_time_frame = true; - if (frame_definition_filename.size()!=0) - { + if (frame_definition_filename.size() != 0) { frame_defs = TimeFrameDefinitions(frame_definition_filename); do_time_frame = true; + } else if (frame_defs.get_num_frames() < 1) { + // make a single frame starting from 0. End value will be ignored. + vector> frame_times(1, pair(0, 0)); + frame_defs = TimeFrameDefinitions(frame_times); } - else if (frame_defs.get_num_frames() < 1) - { - // make a single frame starting from 0. End value will be ignored. - vector > frame_times(1, pair(0,0)); - frame_defs = TimeFrameDefinitions(frame_times); - } #ifdef FRAME_BASED_DT_CORR cerr << "LmToProjData Using FRAME_BASED_DT_CORR\n"; @@ -370,22 +309,14 @@ post_processing() Constructors ***************************************************************/ -LmToProjData:: -LmToProjData() -{ - set_defaults(); -} +LmToProjData::LmToProjData() { set_defaults(); } -LmToProjData:: -LmToProjData(const char * const par_filename) -{ +LmToProjData::LmToProjData(const char* const par_filename) { set_defaults(); - if (par_filename!=0) - { - if (parse(par_filename)==false) - error("Exiting\n"); - } - else + if (par_filename != 0) { + if (parse(par_filename) == false) + error("Exiting\n"); + } else ask_parameters(); } @@ -396,127 +327,96 @@ LmToProjData(const char * const par_filename) N.E: Get_bin_from_event became Get_bin_from_record ***************************************************************/ void -LmToProjData:: -get_bin_from_event(Bin& bin, const ListEvent& event) const -{ - if (do_pre_normalisation) - { - Bin uncompressed_bin; - event.get_bin(uncompressed_bin, *proj_data_info_cyl_uncompressed_ptr); - if (uncompressed_bin.get_bin_value()<=0) +LmToProjData::get_bin_from_event(Bin& bin, const ListEvent& event) const { + if (do_pre_normalisation) { + Bin uncompressed_bin; + event.get_bin(uncompressed_bin, *proj_data_info_cyl_uncompressed_ptr); + if (uncompressed_bin.get_bin_value() <= 0) return; // rejected for some strange reason - - // do_normalisation + // do_normalisation #ifndef FRAME_BASED_DT_CORR - const double start_time = current_time; - const double end_time = current_time; + const double start_time = current_time; + const double end_time = current_time; #else - const double start_time = frame_defs.get_start_time(current_frame_num); - const double end_time =frame_defs.get_end_time(current_frame_num); + const double start_time = frame_defs.get_start_time(current_frame_num); + const double end_time = frame_defs.get_end_time(current_frame_num); #endif - - const float bin_efficiency = - normalisation_ptr->get_bin_efficiency(uncompressed_bin,start_time,end_time); - // TODO remove arbitrary number. Supposes that these bin_efficiencies are around 1 - if (bin_efficiency < 1.E-10) - { - warning("\nBin_efficiency %g too low for uncompressed bin (s:%d,v:%d,ax_pos:%d,tang_pos:%d). Event ignored\n", - bin_efficiency, - uncompressed_bin.segment_num(), uncompressed_bin.view_num(), - uncompressed_bin.axial_pos_num(), uncompressed_bin.tangential_pos_num()); - bin.set_bin_value(-1.f); - return; - } - + + const float bin_efficiency = normalisation_ptr->get_bin_efficiency(uncompressed_bin, start_time, end_time); + // TODO remove arbitrary number. Supposes that these bin_efficiencies are around 1 + if (bin_efficiency < 1.E-10) { + warning("\nBin_efficiency %g too low for uncompressed bin (s:%d,v:%d,ax_pos:%d,tang_pos:%d). Event ignored\n", + bin_efficiency, uncompressed_bin.segment_num(), uncompressed_bin.view_num(), uncompressed_bin.axial_pos_num(), + uncompressed_bin.tangential_pos_num()); + bin.set_bin_value(-1.f); + return; + } + // now find 'compressed' bin, i.e. taking mashing, span etc into account // Also, adjust the normalisation factor according to the number of // uncompressed bins in a compressed bin - const float bin_value = 1.f/bin_efficiency; + const float bin_value = 1.f / bin_efficiency; // TODO wasteful: we decode the event twice. replace by something like // template_proj_data_info_ptr->get_bin_from_uncompressed(bin, uncompressed_bin); - event.get_bin(bin, *template_proj_data_info_ptr); bin.set_bin_value(bin_value); + } else { + event.get_bin(bin, *template_proj_data_info_ptr); } - else - { - event.get_bin(bin, *template_proj_data_info_ptr); - } - -} +} /************************************************************** - Here follows the post_normalisation related stuff. + Here follows the post_normalisation related stuff. ***************************************************************/ -void -LmToProjData:: -do_post_normalisation(Bin& bin) const -{ - if (bin.get_bin_value()>0) - { - if (do_pre_normalisation) - { - bin.set_bin_value(bin.get_bin_value()/get_compression_count(bin)); - } - else - { +void +LmToProjData::do_post_normalisation(Bin& bin) const { + if (bin.get_bin_value() > 0) { + if (do_pre_normalisation) { + bin.set_bin_value(bin.get_bin_value() / get_compression_count(bin)); + } else { #ifndef FRAME_BASED_DT_CORR - const double start_time = current_time; - const double end_time = current_time; + const double start_time = current_time; + const double end_time = current_time; #else - const double start_time = frame_defs.get_start_time(current_frame_num); - const double end_time =frame_defs.get_end_time(current_frame_num); + const double start_time = frame_defs.get_start_time(current_frame_num); + const double end_time = frame_defs.get_end_time(current_frame_num); #endif - const float bin_efficiency = post_normalisation_ptr->get_bin_efficiency(bin,start_time,end_time); - // TODO remove arbitrary number. Supposes that these bin_efficiencies are around 1 - if (bin_efficiency < 1.E-10) - { - warning("\nBin_efficiency %g too low for bin (s:%d,v:%d,ax_pos:%d,tang_pos:%d). Event ignored\n", - bin_efficiency, - bin.segment_num(), bin.view_num(), bin.axial_pos_num(), bin.tangential_pos_num()); - bin.set_bin_value(-1); - } - else - { - bin.set_bin_value(bin.get_bin_value()/bin_efficiency); - } - } + const float bin_efficiency = post_normalisation_ptr->get_bin_efficiency(bin, start_time, end_time); + // TODO remove arbitrary number. Supposes that these bin_efficiencies are around 1 + if (bin_efficiency < 1.E-10) { + warning("\nBin_efficiency %g too low for bin (s:%d,v:%d,ax_pos:%d,tang_pos:%d). Event ignored\n", bin_efficiency, + bin.segment_num(), bin.view_num(), bin.axial_pos_num(), bin.tangential_pos_num()); + bin.set_bin_value(-1); + } else { + bin.set_bin_value(bin.get_bin_value() / bin_efficiency); + } } + } } - int -LmToProjData:: -get_compression_count(const Bin& bin) const -{ +LmToProjData::get_compression_count(const Bin& bin) const { // TODO this currently works ONLY for cylindrical PET scanners - const ProjDataInfoCylindrical& proj_data_info = - dynamic_cast(*template_proj_data_info_ptr); - - return static_cast(proj_data_info.get_num_ring_pairs_for_segment_axial_pos_num(bin.segment_num(),bin.axial_pos_num()))* - proj_data_info.get_view_mashing_factor(); + const ProjDataInfoCylindrical& proj_data_info = dynamic_cast(*template_proj_data_info_ptr); + return static_cast(proj_data_info.get_num_ring_pairs_for_segment_axial_pos_num(bin.segment_num(), bin.axial_pos_num())) * + proj_data_info.get_view_mashing_factor(); } /************************************************************** Empty functions for new time events and new time frames. ***************************************************************/ void -LmToProjData:: -process_new_time_event(const ListTime&) -{} - +LmToProjData::process_new_time_event(const ListTime&) {} void -LmToProjData:: -start_new_time_frame(const unsigned int) -{} +LmToProjData::start_new_time_frame(const unsigned int) {} /************************************************************** Here follows the actual rebinning code (finally). @@ -526,9 +426,7 @@ start_new_time_frame(const unsigned int) ***************************************************************/ void -LmToProjData:: -process_data() -{ +LmToProjData::process_data() { CPUTimer timer; timer.start(); @@ -539,598 +437,490 @@ process_data() double time_of_last_stored_event = 0; long num_stored_events = 0; - VectorWithOffset - segments (template_proj_data_info_ptr->get_min_segment_num(), - template_proj_data_info_ptr->get_max_segment_num()); - - VectorWithOffset - frame_start_positions(1, static_cast(frame_defs.get_num_frames())); - shared_ptr record_sptr = lm_data_ptr->get_empty_record_sptr(); + VectorWithOffset segments(template_proj_data_info_ptr->get_min_segment_num(), + template_proj_data_info_ptr->get_max_segment_num()); + + VectorWithOffset frame_start_positions(1, static_cast(frame_defs.get_num_frames())); + shared_ptr record_sptr = lm_data_ptr->get_empty_record_sptr(); ListRecord& record = *record_sptr; - if (!record.event().is_valid_template(*template_proj_data_info_ptr)) + if (!record.event().is_valid_template(*template_proj_data_info_ptr)) error("The scanner template is not valid for LmToProjData. This might be because of unsupported arc correction."); - /* Here starts the main loop which will store the listmode data. */ - for (current_frame_num = 1; - current_frame_num<=frame_defs.get_num_frames(); - ++current_frame_num) + /* Here starts the main loop which will store the listmode data. */ + for (current_frame_num = 1; current_frame_num <= frame_defs.get_num_frames(); ++current_frame_num) { + start_new_time_frame(current_frame_num); + + // construct ExamInfo appropriate for a single projdata with this time frame + ExamInfo this_frame_exam_info(*lm_data_ptr->get_exam_info_sptr()); { - start_new_time_frame(current_frame_num); + TimeFrameDefinitions this_time_frame_defs(frame_defs, current_frame_num); + this_frame_exam_info.set_time_frame_definitions(this_time_frame_defs); + } - // construct ExamInfo appropriate for a single projdata with this time frame - ExamInfo this_frame_exam_info(*lm_data_ptr->get_exam_info_sptr()); - { - TimeFrameDefinitions this_time_frame_defs(frame_defs, current_frame_num); - this_frame_exam_info.set_time_frame_definitions(this_time_frame_defs); - } + // *********** open output file + shared_ptr output; + shared_ptr proj_data_ptr; - // *********** open output file - shared_ptr output; - shared_ptr proj_data_ptr; + { + char rest[50]; + sprintf(rest, "_f%dg1d0b0", current_frame_num); + const string output_filename = output_filename_prefix + rest; - { - char rest[50]; - sprintf(rest, "_f%dg1d0b0", current_frame_num); - const string output_filename = output_filename_prefix + rest; + proj_data_ptr = construct_proj_data(output, output_filename, this_frame_exam_info, template_proj_data_info_ptr); + } - proj_data_ptr = - construct_proj_data(output, output_filename, this_frame_exam_info, template_proj_data_info_ptr); - } + long num_prompts_in_frame = 0; + long num_delayeds_in_frame = 0; + + const double start_time = frame_defs.get_start_time(current_frame_num); + const double end_time = frame_defs.get_end_time(current_frame_num); - long num_prompts_in_frame = 0; - long num_delayeds_in_frame = 0; - - const double start_time = frame_defs.get_start_time(current_frame_num); - const double end_time = frame_defs.get_end_time(current_frame_num); - - VectorWithOffset > - segments (template_proj_data_info_ptr->get_min_tof_pos_num(), - template_proj_data_info_ptr->get_max_tof_pos_num()); - for (int timing_pos_num=segments.get_min_index(); timing_pos_num<=segments.get_max_index(); ++timing_pos_num) - { - segments[timing_pos_num].resize(template_proj_data_info_ptr->get_min_segment_num(), - template_proj_data_info_ptr->get_max_segment_num()); - } - for (int start_timing_pos_index = proj_data_ptr->get_min_tof_pos_num(); - start_timing_pos_index <= proj_data_ptr->get_max_tof_pos_num(); - start_timing_pos_index += num_timing_poss_in_memory) + VectorWithOffset> segments(template_proj_data_info_ptr->get_min_tof_pos_num(), + template_proj_data_info_ptr->get_max_tof_pos_num()); + for (int timing_pos_num = segments.get_min_index(); timing_pos_num <= segments.get_max_index(); ++timing_pos_num) { + segments[timing_pos_num].resize(template_proj_data_info_ptr->get_min_segment_num(), + template_proj_data_info_ptr->get_max_segment_num()); + } + for (int start_timing_pos_index = proj_data_ptr->get_min_tof_pos_num(); + start_timing_pos_index <= proj_data_ptr->get_max_tof_pos_num(); start_timing_pos_index += num_timing_poss_in_memory) { + const int end_timing_pos_index = + min(proj_data_ptr->get_max_tof_pos_num() + 1, start_timing_pos_index + num_timing_poss_in_memory) - 1; + + /* + For each start_segment_index, we check which events occur in the + segments between start_segment_index and + start_segment_index+num_segments_in_memory. + */ + for (int start_segment_index = proj_data_ptr->get_min_segment_num(); + start_segment_index <= proj_data_ptr->get_max_segment_num(); start_segment_index += num_segments_in_memory) { + + const int end_segment_index = + min(proj_data_ptr->get_max_segment_num() + 1, start_segment_index + num_segments_in_memory) - 1; + + if (!interactive) + allocate_segments(segments, start_timing_pos_index, end_timing_pos_index, start_segment_index, end_segment_index, + proj_data_ptr->get_proj_data_info_sptr()); + + // the next variable is used to see if there are more events to store for the current segments + // num_events_to_store-more_events will be the number of allowed coincidence events currently seen in the file + // ('allowed' independent on the fact of we have its segment in memory or not) + // When do_time_frame=true, the number of events is irrelevant, so we + // just set more_events to 1, and never change it + unsigned long int more_events = do_time_frame ? 1 : num_events_to_store; + + if (start_segment_index != proj_data_ptr->get_min_segment_num() || + start_timing_pos_index > proj_data_ptr->get_min_tof_pos_num()) { + // we're going once more through the data (for the next batch of segments) + cerr << "\nProcessing next batch of segments for start TOF bin " << start_timing_pos_index << "\n"; + // go to the beginning of the listmode data for this frame + lm_data_ptr->set_get_position(frame_start_positions[current_frame_num]); + current_time = start_time; + } else { + cerr << "\nProcessing time frame " << current_frame_num << '\n'; + + // Note: we already have current_time from previous frame, so don't + // need to set it. In fact, setting it to start_time would be wrong + // as we first might have to skip some events before we get to start_time. + // So, let's do that now. + while (current_time < start_time && lm_data_ptr->get_next_record(record) == Succeeded::yes) { + if (record.is_time()) + current_time = record.time().get_time_in_secs(); + } + // now save position such that we can go back + frame_start_positions[current_frame_num] = lm_data_ptr->save_get_position(); + } { - const int end_timing_pos_index = - min( proj_data_ptr->get_max_tof_pos_num()+1, - start_timing_pos_index + num_timing_poss_in_memory) - 1; - - /* - For each start_segment_index, we check which events occur in the - segments between start_segment_index and - start_segment_index+num_segments_in_memory. - */ - for (int start_segment_index = proj_data_ptr->get_min_segment_num(); - start_segment_index <= proj_data_ptr->get_max_segment_num(); - start_segment_index += num_segments_in_memory) + // loop over all events in the listmode file + while (more_events) { + if (lm_data_ptr->get_next_record(record) == Succeeded::no) { + // no more events in file for some reason + break; // get out of while loop + } + if (record.is_time() && end_time > 0.01) // Direct comparison within doubles is unsafe. { - - const int end_segment_index = - min( proj_data_ptr->get_max_segment_num()+1, start_segment_index + num_segments_in_memory) - 1; - - if (!interactive) - allocate_segments(segments, - start_timing_pos_index,end_timing_pos_index, - start_segment_index, end_segment_index, - proj_data_ptr->get_proj_data_info_sptr()); - - // the next variable is used to see if there are more events to store for the current segments - // num_events_to_store-more_events will be the number of allowed coincidence events currently seen in the file - // ('allowed' independent on the fact of we have its segment in memory or not) - // When do_time_frame=true, the number of events is irrelevant, so we - // just set more_events to 1, and never change it - unsigned long int more_events = - do_time_frame? 1 : num_events_to_store; - - if (start_segment_index != proj_data_ptr->get_min_segment_num() || start_timing_pos_index > proj_data_ptr->get_min_tof_pos_num()) - { - // we're going once more through the data (for the next batch of segments) - cerr << "\nProcessing next batch of segments for start TOF bin " << start_timing_pos_index <<"\n"; - // go to the beginning of the listmode data for this frame - lm_data_ptr->set_get_position(frame_start_positions[current_frame_num]); - current_time = start_time; - } - else - { - cerr << "\nProcessing time frame " << current_frame_num << '\n'; - - // Note: we already have current_time from previous frame, so don't - // need to set it. In fact, setting it to start_time would be wrong - // as we first might have to skip some events before we get to start_time. - // So, let's do that now. - while (current_time < start_time && - lm_data_ptr->get_next_record(record) == Succeeded::yes) - { - if (record.is_time()) - current_time = record.time().get_time_in_secs(); - } - // now save position such that we can go back - frame_start_positions[current_frame_num] = - lm_data_ptr->save_get_position(); - } - { - // loop over all events in the listmode file - while (more_events) - { - if (lm_data_ptr->get_next_record(record) == Succeeded::no) - { - // no more events in file for some reason - break; //get out of while loop - } - if (record.is_time() && end_time > 0.01) // Direct comparison within doubles is unsafe. - { - current_time = record.time().get_time_in_secs(); - if (do_time_frame && current_time >= end_time) - break; // get out of while loop - assert(current_time>=start_time); - process_new_time_event(record.time()); - } - // note: could do "else if" here if we would be sure that - // a record can never be both timing and coincidence event - // and there might be a scanner around that has them both combined. - if (record.is_event()) - { - assert(start_time <= current_time); - Bin bin; - // set value in case the event decoder doesn't touch it - // otherwise it would be 0 and all events will be ignored - bin.set_bin_value(1.f); - - get_bin_from_event(bin, record.event()); - - // check if it's inside the range we want to store - if (bin.get_bin_value()>0 - && bin.tangential_pos_num()>= proj_data_ptr->get_min_tangential_pos_num() - && bin.tangential_pos_num()<= proj_data_ptr->get_max_tangential_pos_num() - && bin.axial_pos_num()>=proj_data_ptr->get_min_axial_pos_num(bin.segment_num()) - && bin.axial_pos_num()<=proj_data_ptr->get_max_axial_pos_num(bin.segment_num()) - && bin.timing_pos_num()>=proj_data_ptr->get_min_tof_pos_num() - && bin.timing_pos_num()<=proj_data_ptr->get_max_tof_pos_num() - ) - { - assert(bin.view_num()>=proj_data_ptr->get_min_view_num()); - assert(bin.view_num()<=proj_data_ptr->get_max_view_num()); - - // see if we increment or decrement the value in the sinogram - const int event_increment = - record.event().is_prompt() - ? ( store_prompts ? 1 : 0 ) // it's a prompt - : delayed_increment;//it is a delayed-coincidence event - - if (event_increment==0) - continue; - - if (!do_time_frame) - more_events -= event_increment; - - // Check if the timing position of the bin is in the range - if (bin.timing_pos_num() >= start_timing_pos_index && bin.timing_pos_num()<=end_timing_pos_index) - { - // now check if we have its segment in memory - if (bin.segment_num() >= start_segment_index && bin.segment_num()<=end_segment_index) - { - do_post_normalisation(bin); - - num_stored_events += event_increment; - if (record.event().is_prompt()) - ++num_prompts_in_frame; - else - ++num_delayeds_in_frame; - - if (num_stored_events%500000L==0) cout << "\r" << num_stored_events << " events stored" << flush; - - if (interactive) - printf("TOFbin %4d Seg %4d view %4d ax_pos %4d tang_pos %4d time %8g stored with incr %d \n", - bin.timing_pos_num(),bin.segment_num(), - bin.view_num(), bin.axial_pos_num(), bin.tangential_pos_num(), - current_time, event_increment); - else - (*segments[bin.timing_pos_num()][bin.segment_num()])[bin.view_num()][bin.axial_pos_num()][bin.tangential_pos_num()] += - bin.get_bin_value() * - event_increment; - } - } - } - else // event is rejected for some reason - { - if (interactive) - printf("TOFbin %4d Seg %4d view %4d ax_pos %4d tang_pos %4d time %8g ignored\n", - bin.timing_pos_num(), bin.segment_num(), bin.view_num(), - bin.axial_pos_num(), bin.tangential_pos_num(), current_time); - } - } // end of spatial event processing - } // end of while loop over all events - - time_of_last_stored_event = - max(time_of_last_stored_event,current_time); + current_time = record.time().get_time_in_secs(); + if (do_time_frame && current_time >= end_time) + break; // get out of while loop + assert(current_time >= start_time); + process_new_time_event(record.time()); + } + // note: could do "else if" here if we would be sure that + // a record can never be both timing and coincidence event + // and there might be a scanner around that has them both combined. + if (record.is_event()) { + assert(start_time <= current_time); + Bin bin; + // set value in case the event decoder doesn't touch it + // otherwise it would be 0 and all events will be ignored + bin.set_bin_value(1.f); + + get_bin_from_event(bin, record.event()); + + // check if it's inside the range we want to store + if (bin.get_bin_value() > 0 && bin.tangential_pos_num() >= proj_data_ptr->get_min_tangential_pos_num() && + bin.tangential_pos_num() <= proj_data_ptr->get_max_tangential_pos_num() && + bin.axial_pos_num() >= proj_data_ptr->get_min_axial_pos_num(bin.segment_num()) && + bin.axial_pos_num() <= proj_data_ptr->get_max_axial_pos_num(bin.segment_num()) && + bin.timing_pos_num() >= proj_data_ptr->get_min_tof_pos_num() && + bin.timing_pos_num() <= proj_data_ptr->get_max_tof_pos_num()) { + assert(bin.view_num() >= proj_data_ptr->get_min_view_num()); + assert(bin.view_num() <= proj_data_ptr->get_max_view_num()); + + // see if we increment or decrement the value in the sinogram + const int event_increment = record.event().is_prompt() ? (store_prompts ? 1 : 0) // it's a prompt + : delayed_increment; // it is a delayed-coincidence event + + if (event_increment == 0) + continue; + + if (!do_time_frame) + more_events -= event_increment; + + // Check if the timing position of the bin is in the range + if (bin.timing_pos_num() >= start_timing_pos_index && bin.timing_pos_num() <= end_timing_pos_index) { + // now check if we have its segment in memory + if (bin.segment_num() >= start_segment_index && bin.segment_num() <= end_segment_index) { + do_post_normalisation(bin); + + num_stored_events += event_increment; + if (record.event().is_prompt()) + ++num_prompts_in_frame; + else + ++num_delayeds_in_frame; + + if (num_stored_events % 500000L == 0) + cout << "\r" << num_stored_events << " events stored" << flush; + + if (interactive) + printf("TOFbin %4d Seg %4d view %4d ax_pos %4d tang_pos %4d time %8g stored with incr %d \n", + bin.timing_pos_num(), bin.segment_num(), bin.view_num(), bin.axial_pos_num(), + bin.tangential_pos_num(), current_time, event_increment); + else + (*segments[bin.timing_pos_num()][bin.segment_num()])[bin.view_num()][bin.axial_pos_num()] + [bin.tangential_pos_num()] += + bin.get_bin_value() * event_increment; + } } + } else // event is rejected for some reason + { + if (interactive) + printf("TOFbin %4d Seg %4d view %4d ax_pos %4d tang_pos %4d time %8g ignored\n", bin.timing_pos_num(), + bin.segment_num(), bin.view_num(), bin.axial_pos_num(), bin.tangential_pos_num(), current_time); + } + } // end of spatial event processing + } // end of while loop over all events + + time_of_last_stored_event = max(time_of_last_stored_event, current_time); + } + + if (!interactive) + save_and_delete_segments(output, segments, start_timing_pos_index, end_timing_pos_index, start_segment_index, + end_segment_index, *proj_data_ptr); + } // end of for loop for segment range - if (!interactive) - save_and_delete_segments(output, segments, - start_timing_pos_index,end_timing_pos_index, - start_segment_index, end_segment_index, - *proj_data_ptr); - } // end of for loop for segment range - - } // end of for loop for timing positions - cerr << "\nNumber of prompts stored in this time period : " << num_prompts_in_frame - << "\nNumber of delayeds stored in this time period: " << num_delayeds_in_frame - << '\n'; - } // end of loop over frames - - timer.stop(); - - cerr << "Last stored event was recorded before time-tick at " << time_of_last_stored_event << " secs\n"; - if (!do_time_frame && - (num_stored_events<=0 || - /*static_cast*/(num_stored_events)*/ (num_stored_events) < num_events_to_store)) + cerr << "Early stop due to EOF. " << endl; + cerr << "Total number of counts (either prompts/trues/delayeds) stored: " << num_stored_events << endl; + + cerr << "\nThis took " << timer.value() << "s CPU time." << endl; } void -LmToProjData::run_tof_test_function() -{ +LmToProjData::run_tof_test_function() { #if 1 error("TOF test function disabled"); #else <<<<<<< HEAD - VectorWithOffset > - segments (template_proj_data_info_ptr->get_min_segment_num(), - template_proj_data_info_ptr->get_max_segment_num()); + VectorWithOffset> segments(template_proj_data_info_ptr->get_min_segment_num(), + template_proj_data_info_ptr->get_max_segment_num()); ======= - // construct ExamInfo appropriate for a single projdata with this time frame - ExamInfo this_frame_exam_info(lm_data_ptr->get_exam_info()); - { - TimeFrameDefinitions this_time_frame_defs(frame_defs, current_frame_num); - this_frame_exam_info.set_time_frame_definitions(this_time_frame_defs); - } + // construct ExamInfo appropriate for a single projdata with this time frame + ExamInfo this_frame_exam_info(lm_data_ptr->get_exam_info()); + { + TimeFrameDefinitions this_time_frame_defs(frame_defs, current_frame_num); + this_frame_exam_info.set_time_frame_definitions(this_time_frame_defs); + } - // *********** open output file - shared_ptr output; - shared_ptr proj_data_sptr; + // *********** open output file + shared_ptr output; + shared_ptr proj_data_sptr; >>>>>>> master - // *********** open output file - shared_ptr output; - shared_ptr proj_data_ptr; + // *********** open output file + shared_ptr output; + shared_ptr proj_data_ptr; - ExamInfo this_frame_exam_info(*lm_data_ptr->get_exam_info_ptr()); - { - TimeFrameDefinitions this_time_frame_defs(frame_defs, current_frame_num); - this_frame_exam_info.set_time_frame_definitions(this_time_frame_defs); - } + ExamInfo this_frame_exam_info(*lm_data_ptr->get_exam_info_ptr()); + { + TimeFrameDefinitions this_time_frame_defs(frame_defs, current_frame_num); + this_frame_exam_info.set_time_frame_definitions(this_time_frame_defs); + } - { - char rest[50]; - sprintf(rest, "_f%dg1d0b0", current_frame_num); - const string output_filename = output_filename_prefix + rest; + { + char rest[50]; + sprintf(rest, "_f%dg1d0b0", current_frame_num); + const string output_filename = output_filename_prefix + rest; <<<<<<< HEAD - proj_data_ptr = - construct_proj_data(output, output_filename, this_frame_exam_info, template_proj_data_info_ptr); - } + proj_data_ptr = construct_proj_data(output, output_filename, this_frame_exam_info, template_proj_data_info_ptr); + } ======= - - proj_data_sptr = - construct_proj_data(output, output_filename, this_frame_exam_info, template_proj_data_info_ptr); - } - - long num_prompts_in_frame = 0; - long num_delayeds_in_frame = 0; - - const double start_time = frame_defs.get_start_time(current_frame_num); - const double end_time = frame_defs.get_end_time(current_frame_num); - /* - For each start_segment_index, we check which events occur in the - segments between start_segment_index and - start_segment_index+num_segments_in_memory. - */ - for (int start_segment_index = proj_data_sptr->get_min_segment_num(); - start_segment_index <= proj_data_sptr->get_max_segment_num(); - start_segment_index += num_segments_in_memory) - { - - const int end_segment_index = - min( proj_data_sptr->get_max_segment_num()+1, start_segment_index + num_segments_in_memory) - 1; - - if (!interactive) - allocate_segments(segments, start_segment_index, end_segment_index, proj_data_sptr->get_proj_data_info_sptr()); - - // the next variable is used to see if there are more events to store for the current segments - // num_events_to_store-more_events will be the number of allowed coincidence events currently seen in the file - // ('allowed' independent on the fact of we have its segment in memory or not) - // When do_time_frame=true, the number of events is irrelevant, so we - // just set more_events to 1, and never change it - long more_events = - do_time_frame? 1 : num_events_to_store; - - if (start_segment_index != proj_data_sptr->get_min_segment_num()) - { - // we're going once more through the data (for the next batch of segments) - cerr << "\nProcessing next batch of segments\n"; - // go to the beginning of the listmode data for this frame - lm_data_ptr->set_get_position(frame_start_positions[current_frame_num]); - current_time = start_time; - } - else - { - cerr << "\nProcessing time frame " << current_frame_num << '\n'; - - // Note: we already have current_time from previous frame, so don't - // need to set it. In fact, setting it to start_time would be wrong - // as we first might have to skip some events before we get to start_time. - // So, let's do that now. - while (current_time < start_time && - lm_data_ptr->get_next_record(record) == Succeeded::yes) - { - if (record.is_time()) - current_time = record.time().get_time_in_secs(); - } - // now save position such that we can go back - frame_start_positions[current_frame_num] = - lm_data_ptr->save_get_position(); - } - { - // loop over all events in the listmode file - while (more_events) - { - if (lm_data_ptr->get_next_record(record) == Succeeded::no) - { - // no more events in file for some reason - break; //get out of while loop - } - if (record.is_time() && end_time > 0.01) // Direct comparison within doubles is unsafe. - { - current_time = record.time().get_time_in_secs(); - if (do_time_frame && current_time >= end_time) - break; // get out of while loop - assert(current_time>=start_time); - process_new_time_event(record.time()); - } - // note: could do "else if" here if we would be sure that - // a record can never be both timing and coincidence event - // and there might be a scanner around that has them both combined. - if (record.is_event()) - { - assert(start_time <= current_time); - Bin bin; - // set value in case the event decoder doesn't touch it - // otherwise it would be 0 and all events will be ignored - bin.set_bin_value(1); - get_bin_from_event(bin, record.event()); - - // check if it's inside the range we want to store - if (bin.get_bin_value()>0 - && bin.tangential_pos_num()>= proj_data_sptr->get_min_tangential_pos_num() - && bin.tangential_pos_num()<= proj_data_sptr->get_max_tangential_pos_num() - && bin.axial_pos_num()>=proj_data_sptr->get_min_axial_pos_num(bin.segment_num()) - && bin.axial_pos_num()<=proj_data_sptr->get_max_axial_pos_num(bin.segment_num()) - ) - { - assert(bin.view_num()>=proj_data_sptr->get_min_view_num()); - assert(bin.view_num()<=proj_data_sptr->get_max_view_num()); - - // see if we increment or decrement the value in the sinogram - const int event_increment = - record.event().is_prompt() - ? ( store_prompts ? 1 : 0 ) // it's a prompt - : delayed_increment;//it is a delayed-coincidence event - - if (event_increment==0) - continue; - - if (!do_time_frame) - more_events-= event_increment; - - // now check if we have its segment in memory - if (bin.segment_num() >= start_segment_index && bin.segment_num()<=end_segment_index) - { - do_post_normalisation(bin); - - num_stored_events += event_increment; - if (record.event().is_prompt()) - ++num_prompts_in_frame; - else - ++num_delayeds_in_frame; - - if (num_stored_events%500000L==0) cout << "\r" << num_stored_events << " events stored" << flush; - - if (interactive) - printf("Seg %4d view %4d ax_pos %4d tang_pos %4d time %8g stored with incr %d \n", - bin.segment_num(), bin.view_num(), bin.axial_pos_num(), bin.tangential_pos_num(), - current_time, event_increment); - else - (*segments[bin.segment_num()])[bin.view_num()][bin.axial_pos_num()][bin.tangential_pos_num()] += - bin.get_bin_value() * - event_increment; - } - } - else // event is rejected for some reason - { - if (interactive) - printf("Seg %4d view %4d ax_pos %4d tang_pos %4d time %8g ignored\n", - bin.segment_num(), bin.view_num(), bin.axial_pos_num(), bin.tangential_pos_num(), current_time); - } - } // end of spatial event processing - } // end of while loop over all events - - time_of_last_stored_event = - max(time_of_last_stored_event,current_time); - } - - if (!interactive) - save_and_delete_segments(output, segments, - start_segment_index, end_segment_index, - *proj_data_sptr); - } // end of for loop for segment range - cerr << "\nNumber of prompts stored in this time period : " << num_prompts_in_frame - << "\nNumber of delayeds stored in this time period: " << num_delayeds_in_frame - << '\n'; - } // end of loop over frames - - timer.stop(); - - cerr << "Last stored event was recorded before time-tick at " << time_of_last_stored_event << " secs\n"; - if (!do_time_frame && - (num_stored_events<=0 || - /*static_cast*/(num_stored_events)>>>>>> master + proj_data_sptr = construct_proj_data(output, output_filename, this_frame_exam_info, template_proj_data_info_ptr); + } - for (int current_timing_pos_index = proj_data_ptr->get_min_tof_pos_num(); - current_timing_pos_index <= proj_data_ptr->get_max_tof_pos_num(); - current_timing_pos_index += 1) + long num_prompts_in_frame = 0; + long num_delayeds_in_frame = 0; + + const double start_time = frame_defs.get_start_time(current_frame_num); + const double end_time = frame_defs.get_end_time(current_frame_num); + + /* + For each start_segment_index, we check which events occur in the + segments between start_segment_index and + start_segment_index+num_segments_in_memory. + */ + for (int start_segment_index = proj_data_sptr->get_min_segment_num(); + start_segment_index <= proj_data_sptr->get_max_segment_num(); start_segment_index += num_segments_in_memory) { + + const int end_segment_index = + min(proj_data_sptr->get_max_segment_num() + 1, start_segment_index + num_segments_in_memory) - 1; + + if (!interactive) + allocate_segments(segments, start_segment_index, end_segment_index, proj_data_sptr->get_proj_data_info_sptr()); + + // the next variable is used to see if there are more events to store for the current segments + // num_events_to_store-more_events will be the number of allowed coincidence events currently seen in the file + // ('allowed' independent on the fact of we have its segment in memory or not) + // When do_time_frame=true, the number of events is irrelevant, so we + // just set more_events to 1, and never change it + long more_events = do_time_frame ? 1 : num_events_to_store; + + if (start_segment_index != proj_data_sptr->get_min_segment_num()) { + // we're going once more through the data (for the next batch of segments) + cerr << "\nProcessing next batch of segments\n"; + // go to the beginning of the listmode data for this frame + lm_data_ptr->set_get_position(frame_start_positions[current_frame_num]); + current_time = start_time; + } else { + cerr << "\nProcessing time frame " << current_frame_num << '\n'; + + // Note: we already have current_time from previous frame, so don't + // need to set it. In fact, setting it to start_time would be wrong + // as we first might have to skip some events before we get to start_time. + // So, let's do that now. + while (current_time < start_time && lm_data_ptr->get_next_record(record) == Succeeded::yes) { + if (record.is_time()) + current_time = record.time().get_time_in_secs(); + } + // now save position such that we can go back + frame_start_positions[current_frame_num] = lm_data_ptr->save_get_position(); + } { - for (int start_segment_index = proj_data_ptr->get_min_segment_num(); - start_segment_index <= proj_data_ptr->get_max_segment_num(); - start_segment_index += 1) + // loop over all events in the listmode file + while (more_events) { + if (lm_data_ptr->get_next_record(record) == Succeeded::no) { + // no more events in file for some reason + break; // get out of while loop + } + if (record.is_time() && end_time > 0.01) // Direct comparison within doubles is unsafe. { + current_time = record.time().get_time_in_secs(); + if (do_time_frame && current_time >= end_time) + break; // get out of while loop + assert(current_time >= start_time); + process_new_time_event(record.time()); + } + // note: could do "else if" here if we would be sure that + // a record can never be both timing and coincidence event + // and there might be a scanner around that has them both combined. + if (record.is_event()) { + assert(start_time <= current_time); + Bin bin; + // set value in case the event decoder doesn't touch it + // otherwise it would be 0 and all events will be ignored + bin.set_bin_value(1); + get_bin_from_event(bin, record.event()); + + // check if it's inside the range we want to store + if (bin.get_bin_value() > 0 && bin.tangential_pos_num() >= proj_data_sptr->get_min_tangential_pos_num() && + bin.tangential_pos_num() <= proj_data_sptr->get_max_tangential_pos_num() && + bin.axial_pos_num() >= proj_data_sptr->get_min_axial_pos_num(bin.segment_num()) && + bin.axial_pos_num() <= proj_data_sptr->get_max_axial_pos_num(bin.segment_num())) { + assert(bin.view_num() >= proj_data_sptr->get_min_view_num()); + assert(bin.view_num() <= proj_data_sptr->get_max_view_num()); + + // see if we increment or decrement the value in the sinogram + const int event_increment = record.event().is_prompt() ? (store_prompts ? 1 : 0) // it's a prompt + : delayed_increment; // it is a delayed-coincidence event + + if (event_increment == 0) + continue; + + if (!do_time_frame) + more_events -= event_increment; + + // now check if we have its segment in memory + if (bin.segment_num() >= start_segment_index && bin.segment_num() <= end_segment_index) { + do_post_normalisation(bin); + + num_stored_events += event_increment; + if (record.event().is_prompt()) + ++num_prompts_in_frame; + else + ++num_delayeds_in_frame; + + if (num_stored_events % 500000L == 0) + cout << "\r" << num_stored_events << " events stored" << flush; + + if (interactive) + printf("Seg %4d view %4d ax_pos %4d tang_pos %4d time %8g stored with incr %d \n", bin.segment_num(), + bin.view_num(), bin.axial_pos_num(), bin.tangential_pos_num(), current_time, event_increment); + else + (*segments[bin.segment_num()])[bin.view_num()][bin.axial_pos_num()][bin.tangential_pos_num()] += + bin.get_bin_value() * event_increment; + } + } else // event is rejected for some reason + { + if (interactive) + printf("Seg %4d view %4d ax_pos %4d tang_pos %4d time %8g ignored\n", bin.segment_num(), bin.view_num(), + bin.axial_pos_num(), bin.tangential_pos_num(), current_time); + } + } // end of spatial event processing + } // end of while loop over all events + + time_of_last_stored_event = max(time_of_last_stored_event, current_time); + } - const int end_segment_index = - min( proj_data_ptr->get_max_segment_num()+1, start_segment_index + num_segments_in_memory) - 1; + if (!interactive) + save_and_delete_segments(output, segments, start_segment_index, end_segment_index, *proj_data_sptr); + } // end of for loop for segment range + cerr << "\nNumber of prompts stored in this time period : " << num_prompts_in_frame + << "\nNumber of delayeds stored in this time period: " << num_delayeds_in_frame << '\n'; +} // end of loop over frames - if (!interactive) - allocate_segments(segments, start_segment_index, end_segment_index, - start_timing_pos_index, end_timing_pos_index, - proj_data_ptr->get_proj_data_info_ptr(), current_timing_pos_index); +timer.stop(); - for (int ax_num = proj_data_ptr->get_proj_data_info_ptr()->get_min_axial_pos_num(start_segment_index); - ax_num <= proj_data_ptr->get_proj_data_info_ptr()->get_max_axial_pos_num(start_segment_index); - ++ax_num) - { - for (int view_num = proj_data_ptr->get_proj_data_info_ptr()->get_min_view_num(); - view_num <= proj_data_ptr->get_proj_data_info_ptr()->get_max_view_num(); ++view_num) - { - for (int tang_num = proj_data_ptr->get_proj_data_info_ptr()->get_min_tangential_pos_num(); - tang_num <= proj_data_ptr->get_proj_data_info_ptr()->get_max_tangential_pos_num(); - ++tang_num) - { - (*segments[start_segment_index])[view_num][ax_num][tang_num] = current_timing_pos_index; - } - } - } +cerr << "Last stored event was recorded before time-tick at " << time_of_last_stored_event << " secs\n"; +if (!do_time_frame && (num_stored_events <= 0 || + /*static_cast*/ (num_stored_events) < num_events_to_store)) + cerr << "Early stop due to EOF. " << endl; +cerr << "Total number of counts (either prompts/trues/delayeds) stored: " << num_stored_events << endl; - if (!interactive) - save_and_delete_segments(output, segments, - start_segment_index, end_segment_index, - *proj_data_ptr); - } // end of for loop for segment range +cerr << "\nThis took " << timer.value() << "s CPU time." << endl; +>>>>>>> master - } // end of for loop for timing positions -#endif -} + for (int current_timing_pos_index = proj_data_ptr->get_min_tof_pos_num(); + current_timing_pos_index <= proj_data_ptr->get_max_tof_pos_num(); current_timing_pos_index += 1) { + for (int start_segment_index = proj_data_ptr->get_min_segment_num(); + start_segment_index <= proj_data_ptr->get_max_segment_num(); start_segment_index += 1) { + + const int end_segment_index = + min(proj_data_ptr->get_max_segment_num() + 1, start_segment_index + num_segments_in_memory) - 1; + + if (!interactive) + allocate_segments(segments, start_segment_index, end_segment_index, start_timing_pos_index, end_timing_pos_index, + proj_data_ptr->get_proj_data_info_ptr(), current_timing_pos_index); + + for (int ax_num = proj_data_ptr->get_proj_data_info_ptr()->get_min_axial_pos_num(start_segment_index); + ax_num <= proj_data_ptr->get_proj_data_info_ptr()->get_max_axial_pos_num(start_segment_index); ++ax_num) { + for (int view_num = proj_data_ptr->get_proj_data_info_ptr()->get_min_view_num(); + view_num <= proj_data_ptr->get_proj_data_info_ptr()->get_max_view_num(); ++view_num) { + for (int tang_num = proj_data_ptr->get_proj_data_info_ptr()->get_min_tangential_pos_num(); + tang_num <= proj_data_ptr->get_proj_data_info_ptr()->get_max_tangential_pos_num(); ++tang_num) { + (*segments[start_segment_index])[view_num][ax_num][tang_num] = current_timing_pos_index; + } + } + } + if (!interactive) + save_and_delete_segments(output, segments, start_segment_index, end_segment_index, *proj_data_ptr); + } // end of for loop for segment range + } // end of for loop for timing positions +#endif +} /************************* Local helper routines *************************/ +void +allocate_segments(VectorWithOffset>& segments, const int start_timing_pos_index, + const int end_timing_pos_index, const int start_segment_index, const int end_segment_index, + const shared_ptr proj_data_info_sptr) { -void -allocate_segments(VectorWithOffset > & segments, - const int start_timing_pos_index, - const int end_timing_pos_index, - const int start_segment_index, - const int end_segment_index, - const shared_ptr proj_data_info_sptr) -{ - - for (int timing_pos_num=start_timing_pos_index ; timing_pos_num<=end_timing_pos_index; timing_pos_num++) - for (int seg=start_segment_index ; seg<=end_segment_index; seg++) - { + for (int timing_pos_num = start_timing_pos_index; timing_pos_num <= end_timing_pos_index; timing_pos_num++) + for (int seg = start_segment_index; seg <= end_segment_index; seg++) { #ifdef USE_SegmentByView - segments[timing_pos_num][seg] = new SegmentByView( - proj_data_info_sptr->get_empty_segment_by_view (seg, false, timing_pos_num)); + segments[timing_pos_num][seg] = + new SegmentByView(proj_data_info_sptr->get_empty_segment_by_view(seg, false, timing_pos_num)); #else - segments[timing_pos_num][seg] = - new Array<3,elem_type>(IndexRange3D(0, proj_data_info_sptr->get_num_views()-1, - 0, proj_data_info_sptr->get_num_axial_poss(seg)-1, - -(proj_data_info_sptr->get_num_tangential_poss()/2), - proj_data_info_sptr->get_num_tangential_poss()-(proj_data_info_sptr->get_num_tangential_poss()/2)-1)); + segments[timing_pos_num][seg] = new Array<3, elem_type>(IndexRange3D( + 0, proj_data_info_sptr->get_num_views() - 1, 0, proj_data_info_sptr->get_num_axial_poss(seg) - 1, + -(proj_data_info_sptr->get_num_tangential_poss() / 2), + proj_data_info_sptr->get_num_tangential_poss() - (proj_data_info_sptr->get_num_tangential_poss() / 2) - 1)); #endif - } + } } -void -save_and_delete_segments(shared_ptr& output, - VectorWithOffset >& segments, - const int start_timing_pos_index, - const int end_timing_pos_index, - const int start_segment_index, - const int end_segment_index, - ProjData& proj_data) -{ - for (int timing_pos_num=start_timing_pos_index ; timing_pos_num<=end_timing_pos_index; timing_pos_num++) - for (int seg=start_segment_index; seg<=end_segment_index; seg++) - { +void +save_and_delete_segments(shared_ptr& output, VectorWithOffset>& segments, + const int start_timing_pos_index, const int end_timing_pos_index, const int start_segment_index, + const int end_segment_index, ProjData& proj_data) { + for (int timing_pos_num = start_timing_pos_index; timing_pos_num <= end_timing_pos_index; timing_pos_num++) + for (int seg = start_segment_index; seg <= end_segment_index; seg++) { #ifdef USE_SegmentByView - proj_data.set_segment(*segments[timing_pos_num][seg]); + proj_data.set_segment(*segments[timing_pos_num][seg]); #else - (*segments[timing_pos_num][seg]).write_data(*output); + (*segments[timing_pos_num][seg]).write_data(*output); #endif - delete segments[timing_pos_num][seg]; - } + delete segments[timing_pos_num][seg]; + } } - - -static -shared_ptr -construct_proj_data(shared_ptr& output, - const string& output_filename, - const ExamInfo& exam_info, - const shared_ptr& proj_data_info_ptr) -{ +static shared_ptr +construct_proj_data(shared_ptr& output, const string& output_filename, const ExamInfo& exam_info, + const shared_ptr& proj_data_info_ptr) { shared_ptr exam_info_sptr(new ExamInfo(exam_info)); shared_ptr proj_data_sptr; #ifdef USE_SegmentByView // don't need output stream in this case if (!proj_data_info_ptr->is_tof_data()) - proj_data_sptr.reset(new ProjDataInterfile(exam_info_sptr, - proj_data_info_ptr, output_filename, ios::out, - ProjDataFromStream::Segment_View_AxialPos_TangPos, - OUTPUTNumericType)); + proj_data_sptr.reset(new ProjDataInterfile(exam_info_sptr, proj_data_info_ptr, output_filename, ios::out, + ProjDataFromStream::Segment_View_AxialPos_TangPos, OUTPUTNumericType)); else - proj_data_sptr.reset(new ProjDataInterfile(exam_info_sptr, - proj_data_info_ptr, output_filename, ios::out, - ProjDataFromStream::Timing_Segment_View_AxialPos_TangPos, - OUTPUTNumericType)); + proj_data_sptr.reset(new ProjDataInterfile(exam_info_sptr, proj_data_info_ptr, output_filename, ios::out, + ProjDataFromStream::Timing_Segment_View_AxialPos_TangPos, OUTPUTNumericType)); return proj_data_sptr; #else // this code would work for USE_SegmentByView as well, but the above is far simpler... vector segment_sequence_in_stream(proj_data_info_ptr->get_num_segments()); - { - std::vector::iterator current_segment_iter = - segment_sequence_in_stream.begin(); - for (int segment_num=proj_data_info_ptr->get_min_segment_num(); - segment_num<=proj_data_info_ptr->get_max_segment_num(); + { + std::vector::iterator current_segment_iter = segment_sequence_in_stream.begin(); + for (int segment_num = proj_data_info_ptr->get_min_segment_num(); segment_num <= proj_data_info_ptr->get_max_segment_num(); ++segment_num) *current_segment_iter++ = segment_num; } - output = new fstream (output_filename.c_str(), ios::out|ios::binary); + output = new fstream(output_filename.c_str(), ios::out | ios::binary); if (!*output) - error("Error opening output file %s\n",output_filename.c_str()); - shared_ptr proj_data_ptr( - new ProjDataFromStream(exam_info_sptr, proj_data_info_ptr, output, - /*offset=*/std::streamoff(0), - segment_sequence_in_stream, - ProjDataFromStream::Segment_View_AxialPos_TangPos, - OUTPUTNumericType)); + error("Error opening output file %s\n", output_filename.c_str()); + shared_ptr proj_data_ptr(new ProjDataFromStream(exam_info_sptr, proj_data_info_ptr, output, + /*offset=*/std::streamoff(0), segment_sequence_in_stream, + ProjDataFromStream::Segment_View_AxialPos_TangPos, + OUTPUTNumericType)); write_basic_interfile_PDFS_header(output_filename, *proj_data_ptr); - return proj_data_ptr; + return proj_data_ptr; #endif } diff --git a/src/listmode_buildblock/LmToProjDataAbstract.cxx b/src/listmode_buildblock/LmToProjDataAbstract.cxx index a5271089e1..2f375ad384 100644 --- a/src/listmode_buildblock/LmToProjDataAbstract.cxx +++ b/src/listmode_buildblock/LmToProjDataAbstract.cxx @@ -1,9 +1,9 @@ /*! - \file + \file \ingroup listmode \brief Implementation of class stir::LmToProjDataAbstract - + \author Richard Brown */ /* @@ -27,5 +27,4 @@ START_NAMESPACE_STIR - END_NAMESPACE_STIR diff --git a/src/listmode_buildblock/LmToProjDataBootstrap.cxx b/src/listmode_buildblock/LmToProjDataBootstrap.cxx index 1108485546..2ba730da50 100644 --- a/src/listmode_buildblock/LmToProjDataBootstrap.cxx +++ b/src/listmode_buildblock/LmToProjDataBootstrap.cxx @@ -5,10 +5,10 @@ \file \ingroup listmode \brief Class stir::LmToProjDataBootstrap for rebinning listmode files with the bootstrap method - + \author Kris Thielemans \author Daniel Deidda - + */ /* Copyright (C) 2003- 2011, Hammersmith Imanet Ltd @@ -45,66 +45,53 @@ using std::endl; #include #include - - START_NAMESPACE_STIR template -void -LmToProjDataBootstrap::set_defaults() -{ +void +LmToProjDataBootstrap::set_defaults() { LmToProjData::set_defaults(); seed = 42; } template -void -LmToProjDataBootstrap::initialise_keymap() -{ +void +LmToProjDataBootstrap::initialise_keymap() { LmToProjData::initialise_keymap(); this->parser.add_start_key("LmToProjDataBootstrap Parameters"); - this->parser.add_key("seed", reinterpret_cast(&seed)); // TODO get rid of cast + this->parser.add_key("seed", reinterpret_cast(&seed)); // TODO get rid of cast } template -LmToProjDataBootstrap:: -LmToProjDataBootstrap(const char * const par_filename) -{ +LmToProjDataBootstrap::LmToProjDataBootstrap(const char* const par_filename) { set_defaults(); - if (par_filename!=0) - this->parse(par_filename) ; + if (par_filename != 0) + this->parse(par_filename); else this->ask_parameters(); } template -LmToProjDataBootstrap:: -LmToProjDataBootstrap(const char * const par_filename, const unsigned int seed_v) -{ +LmToProjDataBootstrap::LmToProjDataBootstrap(const char* const par_filename, const unsigned int seed_v) { set_defaults(); seed = seed_v; - if (par_filename!=0) - { - this->parse(par_filename); - // make sure that seed_v parameter overrides whatever was in the par file - if (seed != seed_v) - { - warning("LmToProjDataBootstrap: parameter file %s contains seed (%u) which is\n" - "different from the seed value (%u) passed to me.\n" - "I will use the latter.\n", - par_filename, seed, seed_v); - seed = seed_v; - } + if (par_filename != 0) { + this->parse(par_filename); + // make sure that seed_v parameter overrides whatever was in the par file + if (seed != seed_v) { + warning("LmToProjDataBootstrap: parameter file %s contains seed (%u) which is\n" + "different from the seed value (%u) passed to me.\n" + "I will use the latter.\n", + par_filename, seed, seed_v); + seed = seed_v; } - else + } else this->ask_parameters(); } template bool -LmToProjDataBootstrap:: -post_processing() -{ +LmToProjDataBootstrap::post_processing() { if (LmToProjData::post_processing()) return true; @@ -114,13 +101,9 @@ post_processing() return false; } - - -template -void -LmToProjDataBootstrap:: -start_new_time_frame(const unsigned int new_frame_num) -{ +template +void +LmToProjDataBootstrap::start_new_time_frame(const unsigned int new_frame_num) { base_type::start_new_time_frame(new_frame_num); ListModeData::SavedPosition start_of_this_frame = this->lm_data_ptr->save_get_position(); @@ -129,133 +112,107 @@ start_new_time_frame(const unsigned int new_frame_num) const double start_time = this->frame_defs.get_start_time(new_frame_num); const double end_time = this->frame_defs.get_end_time(new_frame_num); - // When do_time_frame=true, the number of events is irrelevant, so we + // When do_time_frame=true, the number of events is irrelevant, so we // just set more_events to 1, and never change it - long more_events = - this->do_time_frame? 1 : this->num_events_to_store; + long more_events = this->do_time_frame ? 1 : this->num_events_to_store; unsigned int total_num_events_in_this_frame = 0; // loop over all events in the listmode file - shared_ptr record_sptr = this->lm_data_ptr->get_empty_record_sptr(); + shared_ptr record_sptr = this->lm_data_ptr->get_empty_record_sptr(); ListRecord& record = *record_sptr; info("Going through listmode file to find number of events in this frame"); double current_time = start_time; - while (more_events) - { - if (this->lm_data_ptr->get_next_record(record) == Succeeded::no) - { - // no more events in file for some reason - break; //get out of while loop - } - if (record.is_time()) - { - const double new_time = record.time().get_time_in_secs(); - if (this->do_time_frame && new_time >= end_time) - break; // get out of while loop - current_time = new_time; - } - if (record.is_event() && start_time <= current_time) - { - ++total_num_events_in_this_frame; - - if (!this->do_time_frame) - { - // painful business to decrement more_events - - // TODO optimisation possible: - // if we reject an event below, we could force its replication count to 0 - // That way, we will not call get_bin_from_event for it anymore. - - Bin bin; - // set value in case the event decoder doesn't touch it - // otherwise it would be 0 and all events will be ignored - bin.set_bin_value(1); - base_type::get_bin_from_event(bin, record.event()); - // check if it's inside the range we want to store - if (bin.get_bin_value()>0 - && bin.tangential_pos_num()>= this->template_proj_data_info_ptr->get_min_tangential_pos_num() - && bin.tangential_pos_num()<= this->template_proj_data_info_ptr->get_max_tangential_pos_num() - && bin.axial_pos_num()>=this->template_proj_data_info_ptr->get_min_axial_pos_num(bin.segment_num()) - && bin.axial_pos_num()<=this->template_proj_data_info_ptr->get_max_axial_pos_num(bin.segment_num()) - && bin.segment_num()>=this->template_proj_data_info_ptr->get_min_segment_num() - && bin.segment_num()<=this->template_proj_data_info_ptr->get_max_segment_num() - ) - { - assert(bin.view_num()>=this->template_proj_data_info_ptr->get_min_view_num()); - assert(bin.view_num()<=this->template_proj_data_info_ptr->get_max_view_num()); - - // see if we increment or decrement the value in the sinogram - const int event_increment = - record.event().is_prompt() - ? ( this->store_prompts ? 1 : 0 ) // it's a prompt - : this->delayed_increment;//it is a delayed-coincidence event - - if (event_increment==0) - continue; - - - more_events-= event_increment; - } - } // !do_time_frame - } // if (record.is_event()) - } // while (more_events) - - // now initialise num_times_to_replicate + while (more_events) { + if (this->lm_data_ptr->get_next_record(record) == Succeeded::no) { + // no more events in file for some reason + break; // get out of while loop + } + if (record.is_time()) { + const double new_time = record.time().get_time_in_secs(); + if (this->do_time_frame && new_time >= end_time) + break; // get out of while loop + current_time = new_time; + } + if (record.is_event() && start_time <= current_time) { + ++total_num_events_in_this_frame; + + if (!this->do_time_frame) { + // painful business to decrement more_events + + // TODO optimisation possible: + // if we reject an event below, we could force its replication count to 0 + // That way, we will not call get_bin_from_event for it anymore. + + Bin bin; + // set value in case the event decoder doesn't touch it + // otherwise it would be 0 and all events will be ignored + bin.set_bin_value(1); + base_type::get_bin_from_event(bin, record.event()); + // check if it's inside the range we want to store + if (bin.get_bin_value() > 0 && + bin.tangential_pos_num() >= this->template_proj_data_info_ptr->get_min_tangential_pos_num() && + bin.tangential_pos_num() <= this->template_proj_data_info_ptr->get_max_tangential_pos_num() && + bin.axial_pos_num() >= this->template_proj_data_info_ptr->get_min_axial_pos_num(bin.segment_num()) && + bin.axial_pos_num() <= this->template_proj_data_info_ptr->get_max_axial_pos_num(bin.segment_num()) && + bin.segment_num() >= this->template_proj_data_info_ptr->get_min_segment_num() && + bin.segment_num() <= this->template_proj_data_info_ptr->get_max_segment_num()) { + assert(bin.view_num() >= this->template_proj_data_info_ptr->get_min_view_num()); + assert(bin.view_num() <= this->template_proj_data_info_ptr->get_max_view_num()); + + // see if we increment or decrement the value in the sinogram + const int event_increment = record.event().is_prompt() ? (this->store_prompts ? 1 : 0) // it's a prompt + : this->delayed_increment; // it is a delayed-coincidence event + + if (event_increment == 0) + continue; + + more_events -= event_increment; + } + } // !do_time_frame + } // if (record.is_event()) + } // while (more_events) + + // now initialise num_times_to_replicate typedef boost::mt19937 base_generator_type; - base_generator_type generator; + base_generator_type generator; generator.seed(static_cast(seed)); - boost::uniform_int - uniform_int_distribution(0U, total_num_events_in_this_frame-1); - boost::variate_generator > - random_int(generator, - uniform_int_distribution); - + boost::uniform_int uniform_int_distribution(0U, total_num_events_in_this_frame - 1); + boost::variate_generator> random_int(generator, uniform_int_distribution); + num_times_to_replicate.resize(total_num_events_in_this_frame); - - std::fill( num_times_to_replicate.begin(), num_times_to_replicate.end(), - static_cast(0)); - for (unsigned int i=total_num_events_in_this_frame; i!=0; --i) - { - const unsigned int event_num = random_int(); - num_times_to_replicate[event_num] += 1; - // warning this did not check for overflow - } - assert(std::accumulate(num_times_to_replicate.begin(), - num_times_to_replicate.end(), - 0U) == - total_num_events_in_this_frame); - + std::fill(num_times_to_replicate.begin(), num_times_to_replicate.end(), static_cast(0)); + for (unsigned int i = total_num_events_in_this_frame; i != 0; --i) { + const unsigned int event_num = random_int(); + num_times_to_replicate[event_num] += 1; + // warning this did not check for overflow + } + + assert(std::accumulate(num_times_to_replicate.begin(), num_times_to_replicate.end(), 0U) == total_num_events_in_this_frame); + num_times_to_replicate_iter = num_times_to_replicate.begin(); - + info(boost::format("Filled in replication vector for %1% events.") % total_num_events_in_this_frame); this->lm_data_ptr->set_get_position(start_of_this_frame); } -template -void -LmToProjDataBootstrap:: -get_bin_from_event(Bin& bin, const ListEvent& event) const -{ +template +void +LmToProjDataBootstrap::get_bin_from_event(Bin& bin, const ListEvent& event) const { assert(num_times_to_replicate_iter != num_times_to_replicate.end()); - if (*num_times_to_replicate_iter > 0) - { - base_type::get_bin_from_event(bin, event); - bin.set_bin_value(bin.get_bin_value() * *num_times_to_replicate_iter); - } - else + if (*num_times_to_replicate_iter > 0) { + base_type::get_bin_from_event(bin, event); + bin.set_bin_value(bin.get_bin_value() * *num_times_to_replicate_iter); + } else bin.set_bin_value(-1); ++num_times_to_replicate_iter; } - // instantiation template class LmToProjDataBootstrap; - - END_NAMESPACE_STIR diff --git a/src/listmode_buildblock/LmToProjDataWithRandomRejection.cxx b/src/listmode_buildblock/LmToProjDataWithRandomRejection.cxx index 892f744336..e91c423d5c 100644 --- a/src/listmode_buildblock/LmToProjDataWithRandomRejection.cxx +++ b/src/listmode_buildblock/LmToProjDataWithRandomRejection.cxx @@ -5,10 +5,10 @@ \file \ingroup listmode \brief Class stir::LmToProjDataWithRandomRejection for rebinning listmode files rejection some events randomly - + \author Kris Thielemans \author Daniel Deidda - + */ /* Copyright (C) 2003- 2012, Hammersmith Imanet Ltd @@ -35,129 +35,103 @@ #include #include - - START_NAMESPACE_STIR template -void -LmToProjDataWithRandomRejection::set_defaults() -{ +void +LmToProjDataWithRandomRejection::set_defaults() { LmToProjData::set_defaults(); this->seed = 42; this->reject_if_above = .5F; } template -void -LmToProjDataWithRandomRejection::initialise_keymap() -{ +void +LmToProjDataWithRandomRejection::initialise_keymap() { LmToProjData::initialise_keymap(); this->parser.add_start_key("LmToProjDataWithRandomRejection Parameters"); - this->parser.add_key("seed", reinterpret_cast(&seed)); // TODO get rid of cast + this->parser.add_key("seed", reinterpret_cast(&seed)); // TODO get rid of cast this->parser.add_key("reject_if_above", &reject_if_above); } template -LmToProjDataWithRandomRejection:: -LmToProjDataWithRandomRejection(const char * const par_filename) -{ +LmToProjDataWithRandomRejection::LmToProjDataWithRandomRejection(const char* const par_filename) { set_defaults(); - if (par_filename!=0) - this->parse(par_filename) ; + if (par_filename != 0) + this->parse(par_filename); else this->ask_parameters(); } template -LmToProjDataWithRandomRejection:: -LmToProjDataWithRandomRejection(const char * const par_filename, const unsigned int seed_v) -{ +LmToProjDataWithRandomRejection::LmToProjDataWithRandomRejection(const char* const par_filename, + const unsigned int seed_v) { set_defaults(); seed = seed_v; - if (par_filename!=0) - { - this->parse(par_filename); - // make sure that seed_v parameter overrides whatever was in the par file - if (seed != seed_v) - { - warning("LmToProjDataWithRandomRejection: parameter file %s contains seed (%u) which is\n" - "different from the seed value (%u) passed to me.\n" - "I will use the latter.\n", - par_filename, seed, seed_v); - seed = seed_v; - } + if (par_filename != 0) { + this->parse(par_filename); + // make sure that seed_v parameter overrides whatever was in the par file + if (seed != seed_v) { + warning("LmToProjDataWithRandomRejection: parameter file %s contains seed (%u) which is\n" + "different from the seed value (%u) passed to me.\n" + "I will use the latter.\n", + par_filename, seed, seed_v); + seed = seed_v; } - else + } else this->ask_parameters(); } template bool -LmToProjDataWithRandomRejection:: -post_processing() -{ +LmToProjDataWithRandomRejection::post_processing() { if (LmToProjData::post_processing()) return true; - if (this->seed == 0) - { - warning("Seed needs to be non-zero"); return true; - } + if (this->seed == 0) { + warning("Seed needs to be non-zero"); + return true; + } - if (this->reject_if_above<0.F || this->reject_if_above>1.F) - { - warning("reject_if_above needs to be between 0 and 1"); return true; - } + if (this->reject_if_above < 0.F || this->reject_if_above > 1.F) { + warning("reject_if_above needs to be between 0 and 1"); + return true; + } return false; } -template +template float -LmToProjDataWithRandomRejection:: -set_reject_if_above(const float v) -{ +LmToProjDataWithRandomRejection::set_reject_if_above(const float v) { const float ret = this->reject_if_above; this->reject_if_above = v; return ret; } - - - -template -void -LmToProjDataWithRandomRejection:: -start_new_time_frame(const unsigned int new_frame_num) -{ +template +void +LmToProjDataWithRandomRejection::start_new_time_frame(const unsigned int new_frame_num) { base_type::start_new_time_frame(new_frame_num); this->random_generator.seed(static_cast(seed)); } -template -void -LmToProjDataWithRandomRejection:: -get_bin_from_event(Bin& bin, const ListEvent& event) const -{ +template +void +LmToProjDataWithRandomRejection::get_bin_from_event(Bin& bin, const ListEvent& event) const { static boost::uniform_01 random01(random_generator); - const double randnum=random01(); - //std::cout << randnum << '\n'; - if (randnum <= this->reject_if_above) - { - base_type::get_bin_from_event(bin, event); - } - else + const double randnum = random01(); + // std::cout << randnum << '\n'; + if (randnum <= this->reject_if_above) { + base_type::get_bin_from_event(bin, event); + } else bin.set_bin_value(-1); } - // instantiation template class LmToProjDataWithRandomRejection; - - END_NAMESPACE_STIR diff --git a/src/listmode_buildblock/NiftyPET_listmode/LmToProjDataNiftyPET.cxx b/src/listmode_buildblock/NiftyPET_listmode/LmToProjDataNiftyPET.cxx index 34cc22a420..6c29b9c299 100644 --- a/src/listmode_buildblock/NiftyPET_listmode/LmToProjDataNiftyPET.cxx +++ b/src/listmode_buildblock/NiftyPET_listmode/LmToProjDataNiftyPET.cxx @@ -1,9 +1,9 @@ /*! - \file + \file \ingroup listmode \brief Implementation of class stir::LmToProjDataNiftyPET - + \author Richard Brown */ /* @@ -28,41 +28,38 @@ START_NAMESPACE_STIR -LmToProjDataNiftyPET::LmToProjDataNiftyPET() : - _span(11), _cuda_device(0), _cuda_verbosity(true), _start_time(-1), _stop_time(-1), _norm_binary_file("") -{ } +LmToProjDataNiftyPET::LmToProjDataNiftyPET() + : _span(11), _cuda_device(0), _cuda_verbosity(true), _start_time(-1), _stop_time(-1), _norm_binary_file("") {} -void LmToProjDataNiftyPET::check_input() const -{ - if (_listmode_binary_file.empty()) - throw std::runtime_error("LmToProjDataNiftyPET::process_data: listmode binary file not set."); +void +LmToProjDataNiftyPET::check_input() const { + if (_listmode_binary_file.empty()) + throw std::runtime_error("LmToProjDataNiftyPET::process_data: listmode binary file not set."); - if (_start_time < 0) - throw std::runtime_error("LmToProjDataNiftyPET::process_data: start time not set."); + if (_start_time < 0) + throw std::runtime_error("LmToProjDataNiftyPET::process_data: start time not set."); - if (_stop_time < 0) - throw std::runtime_error("LmToProjDataNiftyPET::process_data: stop time not set."); + if (_stop_time < 0) + throw std::runtime_error("LmToProjDataNiftyPET::process_data: stop time not set."); - // Check span - if (_span != 11) - throw std::runtime_error("LmToProjDataNiftyPET::process_data: currently only implemented for span 11."); + // Check span + if (_span != 11) + throw std::runtime_error("LmToProjDataNiftyPET::process_data: currently only implemented for span 11."); } -void LmToProjDataNiftyPET::process_data() -{ - // Set up the niftyPET binary helper - NiftyPETHelper helper; - helper.set_cuda_device_id ( _cuda_device ); - helper.set_span ( static_cast(_span) ); - helper.set_att(0); - helper.set_verbose(_cuda_verbosity); - helper.set_scanner_type(Scanner::Siemens_mMR); - helper.set_up(); +void +LmToProjDataNiftyPET::process_data() { + // Set up the niftyPET binary helper + NiftyPETHelper helper; + helper.set_cuda_device_id(_cuda_device); + helper.set_span(static_cast(_span)); + helper.set_att(0); + helper.set_verbose(_cuda_verbosity); + helper.set_scanner_type(Scanner::Siemens_mMR); + helper.set_up(); - helper.lm_to_proj_data(_prompts_sptr, _delayeds_sptr, - _randoms_sptr, _norm_sptr, - _start_time, _stop_time, - _listmode_binary_file , _norm_binary_file); + helper.lm_to_proj_data(_prompts_sptr, _delayeds_sptr, _randoms_sptr, _norm_sptr, _start_time, _stop_time, _listmode_binary_file, + _norm_binary_file); } END_NAMESPACE_STIR diff --git a/src/listmode_utilities/add_ecat7_header_to_sgl.cxx b/src/listmode_utilities/add_ecat7_header_to_sgl.cxx index 0c67f41353..c15528ef36 100644 --- a/src/listmode_utilities/add_ecat7_header_to_sgl.cxx +++ b/src/listmode_utilities/add_ecat7_header_to_sgl.cxx @@ -17,7 +17,7 @@ See STIR/LICENSE.txt for more details. */ -/*! +/*! \file \ingroup utilities \ingroup ECAT @@ -26,7 +26,6 @@ \author Nacer Kerrouche */ - #include "stir/IO/stir_ecat7.h" #include @@ -37,114 +36,94 @@ USING_NAMESPACE_STIR USING_NAMESPACE_ECAT USING_NAMESPACE_ECAT7 -static void update_main_header(Main_header& mh, const bool is_3d_scan) - { - strcpy(mh.study_description, "listmode"); - mh.acquisition_type = DynamicEmission; - mh.septa_state = - is_3d_scan ? SeptaRetracted : SeptaExtended; - // we set this to a sinogram-type such that header_doc can display the data - mh.file_type = Short3dSinogram; - } - - -void print_usage_and_exit(const char * const program_name) - { - std::cerr<< "\nPrepend contents of ECAT7 header to a sgl file.\n" - << "Usage: \n" - << "\t" << program_name << " [--2d|--3d] output_sgl_name input_sgl_name input_ECAT7_name \n" - << "Defaults to 3D (is used to set septa_state)\n"; - exit(EXIT_FAILURE); - } +static void +update_main_header(Main_header& mh, const bool is_3d_scan) { + strcpy(mh.study_description, "listmode"); + mh.acquisition_type = DynamicEmission; + mh.septa_state = is_3d_scan ? SeptaRetracted : SeptaExtended; + // we set this to a sinogram-type such that header_doc can display the data + mh.file_type = Short3dSinogram; +} +void +print_usage_and_exit(const char* const program_name) { + std::cerr << "\nPrepend contents of ECAT7 header to a sgl file.\n" + << "Usage: \n" + << "\t" << program_name << " [--2d|--3d] output_sgl_name input_sgl_name input_ECAT7_name \n" + << "Defaults to 3D (is used to set septa_state)\n"; + exit(EXIT_FAILURE); +} -int main(int argc, char *argv[]) -{ +int +main(int argc, char* argv[]) { bool is_3d_scan = true; - const char * const program_name = argv[0]; - - if (argc >= 3 && argv[1][0] == '-') - { - if (strcmp(argv[1], "--2d") == 0) - { - is_3d_scan = false; - --argc; ++argv; - } - else if (strcmp(argv[1], "--3d") == 0) - { - is_3d_scan = true; - --argc; ++argv; - } - else - print_usage_and_exit(program_name); - } - if(argc!=4) + const char* const program_name = argv[0]; + + if (argc >= 3 && argv[1][0] == '-') { + if (strcmp(argv[1], "--2d") == 0) { + is_3d_scan = false; + --argc; + ++argv; + } else if (strcmp(argv[1], "--3d") == 0) { + is_3d_scan = true; + --argc; + ++argv; + } else + print_usage_and_exit(program_name); + } + if (argc != 4) print_usage_and_exit(program_name); const std::string output_name = argv[1]; const std::string input_name_sgl = argv[2]; const std::string input_name_ecat7 = argv[3]; - + + { + FILE* sgl_fptr = fopen(input_name_sgl.c_str(), "rb"); + if (!sgl_fptr) { + error("Error opening '%s' for reading: %s", input_name_sgl.c_str(), strerror(errno)); + } + FILE* out_fptr = fopen(output_name.c_str(), "wb"); + if (!out_fptr) { + error("Error opening '%s' for writing: %s", output_name.c_str(), strerror(errno)); + } + // get ECAT7 header + Main_header mh_in; { - FILE * sgl_fptr = fopen(input_name_sgl.c_str(), "rb"); - if (!sgl_fptr) - { - error("Error opening '%s' for reading: %s", - input_name_sgl.c_str(), strerror(errno)); - } - FILE * out_fptr = fopen(output_name.c_str(), "wb"); - if (!out_fptr) - { - error("Error opening '%s' for writing: %s", - output_name.c_str(), strerror(errno)); - } - // get ECAT7 header - Main_header mh_in; - { - FILE * ecat7_fptr = fopen(input_name_ecat7.c_str(), "rb"); - if (!ecat7_fptr) - { - error("Error opening '%s' for reading: %s", - input_name_ecat7.c_str(), strerror(errno)); - } - if (mat_read_main_header(ecat7_fptr, &mh_in)!=0) - error("Error reading main header from %s", input_name_ecat7.c_str()); - fclose(ecat7_fptr); + FILE* ecat7_fptr = fopen(input_name_ecat7.c_str(), "rb"); + if (!ecat7_fptr) { + error("Error opening '%s' for reading: %s", input_name_ecat7.c_str(), strerror(errno)); } + if (mat_read_main_header(ecat7_fptr, &mh_in) != 0) + error("Error reading main header from %s", input_name_ecat7.c_str()); + fclose(ecat7_fptr); + } - update_main_header(mh_in, is_3d_scan); - if (mat_write_main_header(out_fptr, &mh_in)) - error("Error writing main header to %s", output_name.c_str()); - // copy rest of sgl file into output - - char buffer[512]; - int success = EXIT_SUCCESS; - while (!feof(sgl_fptr)) - { - size_t num_read = - fread(buffer, 1, 512, sgl_fptr); - if (ferror(sgl_fptr)) - { - warning("Error reading '%s' : %s", - input_name_sgl.c_str(), strerror(errno)); - success = EXIT_FAILURE; - break; - } - size_t num_written = - fwrite(buffer, 1, num_read, out_fptr); - if (ferror(sgl_fptr) || num_read!=num_written) - { - warning("Error writing '%s' : %s", - output_name.c_str(), strerror(errno)); - success = EXIT_FAILURE; - break; - } + update_main_header(mh_in, is_3d_scan); + if (mat_write_main_header(out_fptr, &mh_in)) + error("Error writing main header to %s", output_name.c_str()); + // copy rest of sgl file into output + + char buffer[512]; + int success = EXIT_SUCCESS; + while (!feof(sgl_fptr)) { + size_t num_read = fread(buffer, 1, 512, sgl_fptr); + if (ferror(sgl_fptr)) { + warning("Error reading '%s' : %s", input_name_sgl.c_str(), strerror(errno)); + success = EXIT_FAILURE; + break; + } + size_t num_written = fwrite(buffer, 1, num_read, out_fptr); + if (ferror(sgl_fptr) || num_read != num_written) { + warning("Error writing '%s' : %s", output_name.c_str(), strerror(errno)); + success = EXIT_FAILURE; + break; } - - fclose(out_fptr); - fclose(sgl_fptr); - return success; } + fclose(out_fptr); + fclose(sgl_fptr); + return success; + } } diff --git a/src/listmode_utilities/conv_NiftyPET_stir.cxx b/src/listmode_utilities/conv_NiftyPET_stir.cxx old mode 100755 new mode 100644 index a30a9bd999..0a7c5d5915 --- a/src/listmode_utilities/conv_NiftyPET_stir.cxx +++ b/src/listmode_utilities/conv_NiftyPET_stir.cxx @@ -33,191 +33,189 @@ USING_NAMESPACE_STIR -static void print_usage_and_exit( const char * const program_name, const int exit_status) -{ - std::cerr << "\n\nUsage : " << program_name << " [-h|--help] output_filename input_filename [--cuda_device ] [--stir_im_par ]\n\n"; - exit(exit_status); +static void +print_usage_and_exit(const char* const program_name, const int exit_status) { + std::cerr << "\n\nUsage : " << program_name + << " [-h|--help] output_filename input_filename [--cuda_device ] [--stir_im_par " + "]\n\n"; + exit(exit_status); } -static void save_disc_density(const DiscretisedDensity<3,float> &out_im_stir, const std::string &filename, const std::string &stir_im_par_fname) -{ - shared_ptr > > output_file_format_sptr; - // Use the default - if (stir_im_par_fname.empty()) - output_file_format_sptr = OutputFileFormat >::default_sptr(); - // If parameter file has been given, try to read it - else { - KeyParser parser; - parser.add_start_key("OutputFileFormat Parameters"); - parser.add_parsing_key("output file format type", &output_file_format_sptr); - parser.add_stop_key("END"); - std::ifstream in(stir_im_par_fname); - if (!parser.parse(in) || is_null_ptr(output_file_format_sptr)) - throw std::runtime_error("Failed to parse output format file (" + stir_im_par_fname + ")."); - } - output_file_format_sptr->write_to_file(filename, out_im_stir); +static void +save_disc_density(const DiscretisedDensity<3, float>& out_im_stir, const std::string& filename, + const std::string& stir_im_par_fname) { + shared_ptr>> output_file_format_sptr; + // Use the default + if (stir_im_par_fname.empty()) + output_file_format_sptr = OutputFileFormat>::default_sptr(); + // If parameter file has been given, try to read it + else { + KeyParser parser; + parser.add_start_key("OutputFileFormat Parameters"); + parser.add_parsing_key("output file format type", &output_file_format_sptr); + parser.add_stop_key("END"); + std::ifstream in(stir_im_par_fname); + if (!parser.parse(in) || is_null_ptr(output_file_format_sptr)) + throw std::runtime_error("Failed to parse output format file (" + stir_im_par_fname + ")."); + } + output_file_format_sptr->write_to_file(filename, out_im_stir); } -static shared_ptr > read_disc_density(const std::string &filename) -{ - // Read - shared_ptr > disc_sptr(read_from_file >(filename)); - // Check - if (is_null_ptr(disc_sptr)) - throw std::runtime_error("Failed to read file: " + filename + "."); +static shared_ptr> +read_disc_density(const std::string& filename) { + // Read + shared_ptr> disc_sptr(read_from_file>(filename)); + // Check + if (is_null_ptr(disc_sptr)) + throw std::runtime_error("Failed to read file: " + filename + "."); - return disc_sptr; + return disc_sptr; } -static void save_np_vec(const std::vector &vec, const std::string &filename) -{ - std::ofstream fout(filename, std::ios::out | std::ios::binary); - fout.write(reinterpret_cast(&vec[0]), vec.size()*sizeof(float)); - fout.close(); +static void +save_np_vec(const std::vector& vec, const std::string& filename) { + std::ofstream fout(filename, std::ios::out | std::ios::binary); + fout.write(reinterpret_cast(&vec[0]), vec.size() * sizeof(float)); + fout.close(); } template -static -std::vector -read_binary_file(const std::string &data_path) -{ - std::ifstream file(data_path, std::ios::in | std::ios::binary); - - // get its size: - file.seekg(0, std::ios::end); - long file_size = file.tellg(); - unsigned long num_elements = static_cast(file_size) / static_cast(sizeof(dataType)); - file.seekg(0, std::ios::beg); - - std::vector contents(num_elements); - file.read(reinterpret_cast(contents.data()), file_size); - - return contents; +static std::vector +read_binary_file(const std::string& data_path) { + std::ifstream file(data_path, std::ios::in | std::ios::binary); + + // get its size: + file.seekg(0, std::ios::end); + long file_size = file.tellg(); + unsigned long num_elements = static_cast(file_size) / static_cast(sizeof(dataType)); + file.seekg(0, std::ios::beg); + + std::vector contents(num_elements); + file.read(reinterpret_cast(contents.data()), file_size); + + return contents; } int -main(int argc, char **argv) -{ - try { - const char * const program_name = argv[0]; - - // Check for help request - for (int i=1; i0 && argv[0][0]=='-') { - if (strcmp(argv[0], "--cuda_device")==0) { - cuda_device = std::atoi(argv[1]); - argc-=2; argv+=2; - } - else if (strcmp(argv[0], "--stir_im_par")==0) { - stir_im_par_fname = argv[1]; - argc-=2; argv+=2; - if (!(is_image && toSTIR)) { - std::cerr << "--stir_im_par can only be supplied when converting to a STIR image.\n"; - print_usage_and_exit(program_name, EXIT_FAILURE); - } - } - else { - std::cerr << "Unknown option '" << argv[0] <<"'\n"; - print_usage_and_exit(program_name, EXIT_FAILURE); - } - } +main(int argc, char** argv) { + try { + const char* const program_name = argv[0]; + + // Check for help request + for (int i = 1; i < argc; ++i) + if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) + print_usage_and_exit(program_name, EXIT_SUCCESS); + + // Check for all compulsory arguments + if (argc < 5) + print_usage_and_exit(program_name, EXIT_FAILURE); + + // Get filenames + const std::string output_filename = argv[1]; + const std::string input_filename = argv[2]; + + // Am i dealing with images or sinograms? + bool is_image; + if (strcmp(argv[3], "image") == 0) + is_image = true; + else if (strcmp(argv[3], "sinogram") == 0) + is_image = false; + else { + std::cerr << "\nExpected \"image\" or \"sinogram\"\n, got: " << argv[3] << "\n"; + print_usage_and_exit(program_name, EXIT_FAILURE); + } + // to STIR or to NiftyPET? + bool toSTIR; + if (strcmp(argv[4], "toSTIR") == 0) + toSTIR = true; + else if (strcmp(argv[4], "toNP") == 0) + toSTIR = false; + else { + std::cerr << "\nExpected \"toSTIR\" or \"toNP\"\n, got: " << argv[3] << "\n"; + print_usage_and_exit(program_name, EXIT_FAILURE); + } - // Set up the niftyPET binary helper - typedef NiftyPETHelper Helper; - Helper helper; - helper.set_cuda_device_id ( cuda_device ); - helper.set_span ( 11 ); - helper.set_att(0); - helper.set_verbose(1); - helper.set_scanner_type(Scanner::Siemens_mMR); - helper.set_up(); - - // if image - if (is_image) { - // image NP -> STIR - if (toSTIR) { - std::vector input_im_np = read_binary_file(input_filename); - shared_ptr > out_im_stir_sptr = helper.create_stir_im(); - helper.convert_image_niftyPET_to_stir(*out_im_stir_sptr, input_im_np); - save_disc_density(*out_im_stir_sptr, output_filename, stir_im_par_fname); - } - // image STIR -> NP - else { - shared_ptr > input_im_stir_sptr = - read_disc_density(input_filename); - std::vector out_im_np = helper.create_niftyPET_image(); - helper.convert_image_stir_to_niftyPET(out_im_np, *input_im_stir_sptr); - save_np_vec(out_im_np, output_filename); - } - } - // if sinogram - else { - // sinogram NP -> STIR - if (toSTIR) { - std::vector input_sino_np = read_binary_file(input_filename); - shared_ptr output_sino_stir_sptr = Helper::create_stir_sino(); - helper.convert_proj_data_niftyPET_to_stir(*output_sino_stir_sptr, input_sino_np); - output_sino_stir_sptr->write_to_file(output_filename); - } - // sinogram STIR -> NP - else { - shared_ptr input_sino_stir_sptr = ProjDataInMemory::read_from_file(input_filename); - std::vector output_sino_np = helper.create_niftyPET_sinogram_with_gaps(); - helper.convert_proj_data_stir_to_niftyPET(output_sino_np,*input_sino_stir_sptr); - save_np_vec(output_sino_np, output_filename); - } + // skip past compulsory arguments + argc -= 5; + argv += 5; + + // Set default value for optional arguments + char cuda_device(0); + std::string stir_im_par_fname; + + // Loop over remaining input + while (argc > 0 && argv[0][0] == '-') { + if (strcmp(argv[0], "--cuda_device") == 0) { + cuda_device = std::atoi(argv[1]); + argc -= 2; + argv += 2; + } else if (strcmp(argv[0], "--stir_im_par") == 0) { + stir_im_par_fname = argv[1]; + argc -= 2; + argv += 2; + if (!(is_image && toSTIR)) { + std::cerr << "--stir_im_par can only be supplied when converting to a STIR image.\n"; + print_usage_and_exit(program_name, EXIT_FAILURE); } + } else { + std::cerr << "Unknown option '" << argv[0] << "'\n"; + print_usage_and_exit(program_name, EXIT_FAILURE); + } } - // If there was an error - catch(const std::exception &error) { - std::cerr << "\nError encountered:\n\t" << error.what() << "\n\n"; - return EXIT_FAILURE; + // Set up the niftyPET binary helper + typedef NiftyPETHelper Helper; + Helper helper; + helper.set_cuda_device_id(cuda_device); + helper.set_span(11); + helper.set_att(0); + helper.set_verbose(1); + helper.set_scanner_type(Scanner::Siemens_mMR); + helper.set_up(); + + // if image + if (is_image) { + // image NP -> STIR + if (toSTIR) { + std::vector input_im_np = read_binary_file(input_filename); + shared_ptr> out_im_stir_sptr = helper.create_stir_im(); + helper.convert_image_niftyPET_to_stir(*out_im_stir_sptr, input_im_np); + save_disc_density(*out_im_stir_sptr, output_filename, stir_im_par_fname); + } + // image STIR -> NP + else { + shared_ptr> input_im_stir_sptr = read_disc_density(input_filename); + std::vector out_im_np = helper.create_niftyPET_image(); + helper.convert_image_stir_to_niftyPET(out_im_np, *input_im_stir_sptr); + save_np_vec(out_im_np, output_filename); + } } - catch(...) { - std::cerr << "\nError encountered.\n\n"; - return EXIT_FAILURE; + // if sinogram + else { + // sinogram NP -> STIR + if (toSTIR) { + std::vector input_sino_np = read_binary_file(input_filename); + shared_ptr output_sino_stir_sptr = Helper::create_stir_sino(); + helper.convert_proj_data_niftyPET_to_stir(*output_sino_stir_sptr, input_sino_np); + output_sino_stir_sptr->write_to_file(output_filename); + } + // sinogram STIR -> NP + else { + shared_ptr input_sino_stir_sptr = ProjDataInMemory::read_from_file(input_filename); + std::vector output_sino_np = helper.create_niftyPET_sinogram_with_gaps(); + helper.convert_proj_data_stir_to_niftyPET(output_sino_np, *input_sino_stir_sptr); + save_np_vec(output_sino_np, output_filename); + } } - return(EXIT_SUCCESS); + } + + // If there was an error + catch (const std::exception& error) { + std::cerr << "\nError encountered:\n\t" << error.what() << "\n\n"; + return EXIT_FAILURE; + } catch (...) { + std::cerr << "\nError encountered.\n\n"; + return EXIT_FAILURE; + } + return (EXIT_SUCCESS); } diff --git a/src/listmode_utilities/list_lm_countrates.cxx b/src/listmode_utilities/list_lm_countrates.cxx index 20ac9aaad0..dd75c7bdda 100644 --- a/src/listmode_utilities/list_lm_countrates.cxx +++ b/src/listmode_utilities/list_lm_countrates.cxx @@ -31,74 +31,60 @@ #include #include - USING_NAMESPACE_STIR -int main(int argc, char * argv[]) -{ - if (argc<3 || argc>4) { +int +main(int argc, char* argv[]) { + if (argc < 3 || argc > 4) { std::cerr << "Usage: " << argv[0] << " output_filename listmode_file [time_interval_in_secs]\n" - << "time_interval_in_secs defaults to 1\n" - << "Output is a file with the count-rates per time interval in CSV format as in\n" - << "start_time_in_secs , end_time_in_secs , num_prompts , num_delayeds\n"; + << "time_interval_in_secs defaults to 1\n" + << "Output is a file with the count-rates per time interval in CSV format as in\n" + << "start_time_in_secs , end_time_in_secs , num_prompts , num_delayeds\n"; exit(EXIT_FAILURE); } - shared_ptr lm_data_ptr - (read_from_file(argv[2])); + shared_ptr lm_data_ptr(read_from_file(argv[2])); const std::string hc_filename = argv[1]; std::ofstream headcurve(hc_filename.c_str()); - if (!headcurve) - { - warning("Error opening headcurve file %s\n", hc_filename.c_str()); - exit(EXIT_FAILURE); - } + if (!headcurve) { + warning("Error opening headcurve file %s\n", hc_filename.c_str()); + exit(EXIT_FAILURE); + } - const double interval = argc>3 ? atof(argv[3]) : 1; + const double interval = argc > 3 ? atof(argv[3]) : 1; - shared_ptr record_sptr = lm_data_ptr->get_empty_record_sptr(); + shared_ptr record_sptr = lm_data_ptr->get_empty_record_sptr(); ListRecord& record = *record_sptr; double current_time = 0; - bool first_timing_event_read=false; + bool first_timing_event_read = false; unsigned long num_prompts = 0UL; unsigned long num_delayeds = 0UL; - while (true) - { - if (lm_data_ptr->get_next_record(record) == Succeeded::no) - { - // no more events in file for some reason - break; //get out of while loop - } - if (record.is_time()) - { - const double new_time = record.time().get_time_in_secs(); - if (!first_timing_event_read) - { - current_time = new_time; - num_prompts=0UL; - num_delayeds=0UL; - first_timing_event_read = true; - } - else if (new_time >= current_time+interval) - { - headcurve << std::fixed << std::setprecision(3) - << current_time - << " , " << current_time+interval - << " , " << num_prompts - << " , " << num_delayeds - << '\n'; - num_prompts=0UL; - num_delayeds=0UL; - current_time += interval; - } - } - if (record.is_event()) - { - if (record.event() .is_prompt()) - ++num_prompts; - else - ++num_delayeds; - } + while (true) { + if (lm_data_ptr->get_next_record(record) == Succeeded::no) { + // no more events in file for some reason + break; // get out of while loop + } + if (record.is_time()) { + const double new_time = record.time().get_time_in_secs(); + if (!first_timing_event_read) { + current_time = new_time; + num_prompts = 0UL; + num_delayeds = 0UL; + first_timing_event_read = true; + } else if (new_time >= current_time + interval) { + headcurve << std::fixed << std::setprecision(3) << current_time << " , " << current_time + interval << " , " + << num_prompts << " , " << num_delayeds << '\n'; + num_prompts = 0UL; + num_delayeds = 0UL; + current_time += interval; + } + } + if (record.is_event()) { + if (record.event().is_prompt()) + ++num_prompts; + else + ++num_delayeds; + } } return EXIT_SUCCESS; } diff --git a/src/listmode_utilities/list_lm_events.cxx b/src/listmode_utilities/list_lm_events.cxx index 414de32f08..3521a7e3d6 100644 --- a/src/listmode_utilities/list_lm_events.cxx +++ b/src/listmode_utilities/list_lm_events.cxx @@ -18,16 +18,15 @@ */ /*! - \file + \file \ingroup listmode_utilities \brief Program to show info about listmode data - + \author Kris Thielemans \author Daniel Deidda */ - #include "stir/listmode/ListRecord.h" #include "stir/listmode/ListEvent.h" #include "stir/listmode/ListTime.h" @@ -49,168 +48,127 @@ using std::endl; using std::vector; #endif - - USING_NAMESPACE_STIR -int main(int argc, char *argv[]) -{ - const char * const program_name = argv[0]; +int +main(int argc, char* argv[]) { + const char* const program_name = argv[0]; // skip program name --argc; ++argv; - bool list_time=true; - bool list_coincidence=false; - bool list_SPECT_event=false; - bool list_gating=true; - bool list_unknown=false; + bool list_time = true; + bool list_coincidence = false; + bool list_SPECT_event = false; + bool list_gating = true; + bool list_unknown = false; unsigned long num_events_to_list = 0; - while (argc>1 && argv[0][0]=='-') - { - if (strcmp(argv[0], "--num-events-to-list")==0) - { - num_events_to_list = atol(argv[1]); - } - else if (strcmp(argv[0], "--time")==0) - { - list_time = atoi(argv[1])!=0; - } - else if (strcmp(argv[0], "--gating")==0) - { - list_gating = atoi(argv[1])!=0; - } - else if (strcmp(argv[0], "--coincidence")==0) - { - list_coincidence = atoi(argv[1])!=0; - } - else if (strcmp(argv[0], "--SPECT-event")==0) - { - list_SPECT_event = atoi(argv[1])!=0; - } - else if (strcmp(argv[0], "--unknown")==0) - { - list_unknown = atoi(argv[1])!=0; - } - else - { - cerr << "Unrecognised option\n"; - return EXIT_FAILURE; - } - argc-=2; argv+=2; - } - - if (argc!=1) - { - cerr << "Usage: " << program_name << "[options] lm_filename\n" - << "Options:\n" - << "--time 0|1 : list time events or not (default: 1)\n" - << "--gating 0|1 : list gating events or not (default: 1)\n" - << "--coincidence 0|1 : list coincidence events or not (default: 0)\n" - << "--unknown 0|1 : list if event of unknown type encountered or not (default: 0)\n" - << "--num-events-to-list : limit number of events written to stdout\n"; + while (argc > 1 && argv[0][0] == '-') { + if (strcmp(argv[0], "--num-events-to-list") == 0) { + num_events_to_list = atol(argv[1]); + } else if (strcmp(argv[0], "--time") == 0) { + list_time = atoi(argv[1]) != 0; + } else if (strcmp(argv[0], "--gating") == 0) { + list_gating = atoi(argv[1]) != 0; + } else if (strcmp(argv[0], "--coincidence") == 0) { + list_coincidence = atoi(argv[1]) != 0; + } else if (strcmp(argv[0], "--SPECT-event") == 0) { + list_SPECT_event = atoi(argv[1]) != 0; + } else if (strcmp(argv[0], "--unknown") == 0) { + list_unknown = atoi(argv[1]) != 0; + } else { + cerr << "Unrecognised option\n"; return EXIT_FAILURE; } + argc -= 2; + argv += 2; + } + + if (argc != 1) { + cerr << "Usage: " << program_name << "[options] lm_filename\n" + << "Options:\n" + << "--time 0|1 : list time events or not (default: 1)\n" + << "--gating 0|1 : list gating events or not (default: 1)\n" + << "--coincidence 0|1 : list coincidence events or not (default: 0)\n" + << "--unknown 0|1 : list if event of unknown type encountered or not (default: 0)\n" + << "--num-events-to-list : limit number of events written to stdout\n"; + return EXIT_FAILURE; + } shared_ptr lm_data_ptr(read_from_file(argv[0])); cout << "Scanner: " << lm_data_ptr->get_scanner_ptr()->get_name() << endl; unsigned long num_listed_events = 0; - { + { // loop over all events in the listmode file - shared_ptr record_sptr = lm_data_ptr->get_empty_record_sptr(); + shared_ptr record_sptr = lm_data_ptr->get_empty_record_sptr(); ListRecord& record = *record_sptr; - while (num_events_to_list==0 || num_events_to_list!=num_listed_events) - { - bool recognised = false; - bool listed = false; -// std::cout<<"ciao"<get_next_record(record) == Succeeded::no) - { - // no more events in file for some reason - break; //get out of while loop + while (num_events_to_list == 0 || num_events_to_list != num_listed_events) { + bool recognised = false; + bool listed = false; + // std::cout<<"ciao"<get_next_record(record) == Succeeded::no) { + // no more events in file for some reason + break; // get out of while loop } - if (record.is_time()) - { - recognised=true; - if (list_time) - { - cout << "Time " << record.time().get_time_in_millisecs(); - listed = true; - } - } - { - ListRecordWithGatingInput * record_ptr = dynamic_cast(&record); - if (record_ptr!=0 && record_ptr->is_gating_input()) - { - recognised=true; - if (list_gating) - { - cout << "Gating " << std::hex << record_ptr->gating_input().get_gating() << std::dec; - listed = true; - } - } + if (record.is_time()) { + recognised = true; + if (list_time) { + cout << "Time " << record.time().get_time_in_millisecs(); + listed = true; } - if (record.is_event()) - { - recognised=true; - if (list_coincidence) - {CListEventCylindricalScannerWithDiscreteDetectors * event_ptr = - dynamic_cast(&record.event()); - if (event_ptr!=0) - { - DetectionPositionPair<> det_pos; - event_ptr->get_detection_position(det_pos); - cout << "Coincidence " << (event_ptr->is_prompt() ? "p " : "d ") - << "(c:" << det_pos.pos1().tangential_coord() - << ",r:" << det_pos.pos1().axial_coord() - << ",l:" << det_pos.pos1().radial_coord() - << ")-" - << "(c:" << det_pos.pos2().tangential_coord() - << ",r:" << det_pos.pos2().axial_coord() - << ",l:" << det_pos.pos2().radial_coord() - << ")"; - cout << " delta time: " << event_ptr->get_delta_time(); - listed = true; - } - } - if (list_SPECT_event) - {ListEvent * event_ptr = - dynamic_cast(&record.event()); - if (event_ptr!=0) - { - LORAs2Points lor; - lor=event_ptr->get_LOR(); - cout << "Gamma event: LOR as two points " - << "(x1:" << lor.p1().x() - << ",y1:" << lor.p1().y() - << ",z1:" << lor.p1().z() - << ")-" - << "(x2:" << lor.p2().x() - << ",y2:" << lor.p2().y() - << ",z2:" << lor.p2().z() - << ")"; - listed = true; - } - } + } + { + ListRecordWithGatingInput* record_ptr = dynamic_cast(&record); + if (record_ptr != 0 && record_ptr->is_gating_input()) { + recognised = true; + if (list_gating) { + cout << "Gating " << std::hex << record_ptr->gating_input().get_gating() << std::dec; + listed = true; + } } - if (!recognised && list_unknown) - { - cout << "Unknown type"; - listed = true; + } + if (record.is_event()) { + recognised = true; + if (list_coincidence) { + CListEventCylindricalScannerWithDiscreteDetectors* event_ptr = + dynamic_cast(&record.event()); + if (event_ptr != 0) { + DetectionPositionPair<> det_pos; + event_ptr->get_detection_position(det_pos); + cout << "Coincidence " << (event_ptr->is_prompt() ? "p " : "d ") << "(c:" << det_pos.pos1().tangential_coord() + << ",r:" << det_pos.pos1().axial_coord() << ",l:" << det_pos.pos1().radial_coord() << ")-" + << "(c:" << det_pos.pos2().tangential_coord() << ",r:" << det_pos.pos2().axial_coord() + << ",l:" << det_pos.pos2().radial_coord() << ")"; + cout << " delta time: " << event_ptr->get_delta_time(); + listed = true; + } } - if (listed) - { - ++num_listed_events; - cout << '\n'; + if (list_SPECT_event) { + ListEvent* event_ptr = dynamic_cast(&record.event()); + if (event_ptr != 0) { + LORAs2Points lor; + lor = event_ptr->get_LOR(); + cout << "Gamma event: LOR as two points " + << "(x1:" << lor.p1().x() << ",y1:" << lor.p1().y() << ",z1:" << lor.p1().z() << ")-" + << "(x2:" << lor.p2().x() << ",y2:" << lor.p2().y() << ",z2:" << lor.p2().z() << ")"; + listed = true; } + } + } + if (!recognised && list_unknown) { + cout << "Unknown type"; + listed = true; + } + if (listed) { + ++num_listed_events; + cout << '\n'; } + } cout << '\n'; - } return EXIT_SUCCESS; } - diff --git a/src/listmode_utilities/list_lm_info.cxx b/src/listmode_utilities/list_lm_info.cxx index 4a240619d5..8e106d510d 100644 --- a/src/listmode_utilities/list_lm_info.cxx +++ b/src/listmode_utilities/list_lm_info.cxx @@ -36,22 +36,22 @@ #include "stir/ProjDataInfo.h" #include "stir/is_null_ptr.h" #include "stir/IO/read_from_file.h" -#include +#include #include USING_NAMESPACE_STIR -void print_usage_and_exit(const std::string& program_name) -{ - std::cerr<<"Usage: " << program_name << " [--all | --geom | --exam] listmode_file\n" - <<"\nAdd one or more options to print the exam/geometric information.\n" - <<"\nIf no option is specified, exam info is printed.\n"; +void +print_usage_and_exit(const std::string& program_name) { + std::cerr << "Usage: " << program_name << " [--all | --geom | --exam] listmode_file\n" + << "\nAdd one or more options to print the exam/geometric information.\n" + << "\nIf no option is specified, exam info is printed.\n"; exit(EXIT_FAILURE); } -int main(int argc, char *argv[]) -{ - const char * const program_name = argv[0]; +int +main(int argc, char* argv[]) { + const char* const program_name = argv[0]; // skip program name --argc; ++argv; @@ -62,32 +62,27 @@ int main(int argc, char *argv[]) bool no_options = true; // need this for default behaviour // first process command line options - while (argc>0 && argv[0][0]=='-' && argc>=2) - { - no_options=false; - if (strcmp(argv[0], "--all")==0) - { - print_geom = print_exam = true; - --argc; ++argv; - } - else if (strcmp(argv[0], "--geom")==0) - { - print_geom = true; - --argc; ++argv; - } - else if (strcmp(argv[0], "--exam")==0) - { - print_exam = true; - --argc; ++argv; - } - else - print_usage_and_exit(program_name); - } + while (argc > 0 && argv[0][0] == '-' && argc >= 2) { + no_options = false; + if (strcmp(argv[0], "--all") == 0) { + print_geom = print_exam = true; + --argc; + ++argv; + } else if (strcmp(argv[0], "--geom") == 0) { + print_geom = true; + --argc; + ++argv; + } else if (strcmp(argv[0], "--exam") == 0) { + print_exam = true; + --argc; + ++argv; + } else + print_usage_and_exit(program_name); + } if (no_options) print_exam = true; - if(argc!=1) - { + if (argc != 1) { print_usage_and_exit(program_name); } @@ -96,11 +91,10 @@ int main(int argc, char *argv[]) shared_ptr lm_data_sptr(read_from_file(filename)); - if (is_null_ptr(lm_data_sptr)) - { - warning("Could not read %s", filename.c_str()); - return EXIT_FAILURE; - } + if (is_null_ptr(lm_data_sptr)) { + warning("Could not read %s", filename.c_str()); + return EXIT_FAILURE; + } if (print_exam) std::cout << lm_data_sptr->get_exam_info_sptr()->parameter_info(); diff --git a/src/listmode_utilities/lm_fansums.cxx b/src/listmode_utilities/lm_fansums.cxx index b35ba2e011..a94fb3b9ba 100644 --- a/src/listmode_utilities/lm_fansums.cxx +++ b/src/listmode_utilities/lm_fansums.cxx @@ -1,13 +1,13 @@ // // /*! - \file + \file \ingroup listmode \brief Program to compute detector fansums directly from listmode data - + \author Kris Thielemans - + $Revision $ */ /* @@ -27,7 +27,6 @@ See STIR/LICENSE.txt for details */ - #include "stir/utilities.h" #include "stir/shared_ptr.h" #include "stir/ParsingObject.h" @@ -62,12 +61,9 @@ using std::vector; START_NAMESPACE_STIR - -class LmFansums : public ParsingObject -{ +class LmFansums : public ParsingObject { public: - - LmFansums(const char * const par_filename); + LmFansums(const char* const par_filename); int max_segment_num_to_process; int fan_size; @@ -75,8 +71,8 @@ class LmFansums : public ParsingObject TimeFrameDefinitions frame_defs; void compute(); -private: +private: virtual void set_defaults(); virtual void initialise_keymap(); virtual bool post_processing(); @@ -88,193 +84,148 @@ class LmFansums : public ParsingObject bool interactive; - void write_fan_sums(const Array<2,float>& data_fan_sums, - const unsigned current_frame_num) const; + void write_fan_sums(const Array<2, float>& data_fan_sums, const unsigned current_frame_num) const; }; -void -LmFansums:: -set_defaults() -{ +void +LmFansums::set_defaults() { max_segment_num_to_process = -1; fan_size = -1; store_prompts = true; delayed_increment = -1; - interactive=false; + interactive = false; } -void -LmFansums:: -initialise_keymap() -{ +void +LmFansums::initialise_keymap() { parser.add_start_key("lm_fansums Parameters"); - parser.add_key("input file",&input_filename); - parser.add_key("frame_definition file",&frame_definition_filename); - parser.add_key("output filename prefix",&output_filename_prefix); + parser.add_key("input file", &input_filename); + parser.add_key("frame_definition file", &frame_definition_filename); + parser.add_key("output filename prefix", &output_filename_prefix); parser.add_key("tangential fan_size", &fan_size); - parser.add_key("maximum absolute segment number to process", &max_segment_num_to_process); + parser.add_key("maximum absolute segment number to process", &max_segment_num_to_process); // TODO can't do this yet // if (CListEvent::has_delayeds()) { - parser.add_key("Store 'prompts'",&store_prompts); - parser.add_key("increment to use for 'delayeds'",&delayed_increment); + parser.add_key("Store 'prompts'", &store_prompts); + parser.add_key("increment to use for 'delayeds'", &delayed_increment); } - parser.add_key("List event coordinates",&interactive); - parser.add_stop_key("END"); - + parser.add_key("List event coordinates", &interactive); + parser.add_stop_key("END"); } - bool -LmFansums:: -post_processing() -{ - lm_data_ptr = - read_from_file(input_filename); - - const int num_rings = - lm_data_ptr->get_scanner_ptr()->get_num_rings(); - if (max_segment_num_to_process==-1) - max_segment_num_to_process = num_rings-1; +LmFansums::post_processing() { + lm_data_ptr = read_from_file(input_filename); + + const int num_rings = lm_data_ptr->get_scanner_ptr()->get_num_rings(); + if (max_segment_num_to_process == -1) + max_segment_num_to_process = num_rings - 1; else - max_segment_num_to_process = - min(max_segment_num_to_process, num_rings-1); + max_segment_num_to_process = min(max_segment_num_to_process, num_rings - 1); - const int max_fan_size = - lm_data_ptr->get_scanner_ptr()->get_max_num_non_arccorrected_bins(); - if (fan_size==-1) + const int max_fan_size = lm_data_ptr->get_scanner_ptr()->get_max_num_non_arccorrected_bins(); + if (fan_size == -1) fan_size = max_fan_size; else - fan_size = - min(fan_size, max_fan_size); + fan_size = min(fan_size, max_fan_size); frame_defs = TimeFrameDefinitions(frame_definition_filename); return false; } -LmFansums:: -LmFansums(const char * const par_filename) -{ +LmFansums::LmFansums(const char* const par_filename) { set_defaults(); - if (par_filename!=0) - parse(par_filename) ; + if (par_filename != 0) + parse(par_filename); else ask_parameters(); - } - void -LmFansums:: -compute() -{ +LmFansums::compute() { //*********** get Scanner details - const int num_rings = - lm_data_ptr->get_scanner_ptr()->get_num_rings(); - const int num_detectors_per_ring = - lm_data_ptr->get_scanner_ptr()->get_num_detectors_per_ring(); - + const int num_rings = lm_data_ptr->get_scanner_ptr()->get_num_rings(); + const int num_detectors_per_ring = lm_data_ptr->get_scanner_ptr()->get_num_detectors_per_ring(); //*********** Finally, do the real work - + CPUTimer timer; timer.start(); - + double time_of_last_stored_event = 0; long num_stored_events = 0; - Array<2,float> data_fan_sums(IndexRange2D(num_rings, num_detectors_per_ring)); - + Array<2, float> data_fan_sums(IndexRange2D(num_rings, num_detectors_per_ring)); + // go to the beginning of the binary data lm_data_ptr->reset(); - + unsigned int current_frame_num = 1; - { + { // loop over all events in the listmode file - shared_ptr record_sptr = - lm_data_ptr->get_empty_record_sptr(); + shared_ptr record_sptr = lm_data_ptr->get_empty_record_sptr(); ListRecord& record = *record_sptr; - bool first_event=true; + bool first_event = true; double current_time = 0; - while (true) - { - if (lm_data_ptr->get_next_record(record) == Succeeded::no) - { - // no more events in file for some reason - write_fan_sums(data_fan_sums, current_frame_num); - break; //get out of while loop + while (true) { + if (lm_data_ptr->get_next_record(record) == Succeeded::no) { + // no more events in file for some reason + write_fan_sums(data_fan_sums, current_frame_num); + break; // get out of while loop + } + if (record.is_time()) { + const double new_time = record.time().get_time_in_secs(); + if (new_time >= frame_defs.get_end_time(current_frame_num)) { + while (current_frame_num <= frame_defs.get_num_frames() && new_time >= frame_defs.get_end_time(current_frame_num)) { + write_fan_sums(data_fan_sums, current_frame_num++); + data_fan_sums.fill(0); + } + if (current_frame_num > frame_defs.get_num_frames()) + break; // get out of while loop } - if (record.is_time()) - { - const double new_time = record.time().get_time_in_secs(); - if (new_time >= frame_defs.get_end_time(current_frame_num)) - { - while (current_frame_num <= frame_defs.get_num_frames() && - new_time >= frame_defs.get_end_time(current_frame_num)) - { - write_fan_sums(data_fan_sums, current_frame_num++); - data_fan_sums.fill(0); - } - if (current_frame_num > frame_defs.get_num_frames()) - break; // get out of while loop + current_time = new_time; + } else if (record.is_event() && frame_defs.get_start_time(current_frame_num) <= current_time) { + // do a consistency check with dynamic_cast first + if (first_event && dynamic_cast(&record.event()) == 0) + error("Currently only works for scanners with discrete detectors."); + first_event = false; + + // see if we increment or decrement the value in the sinogram + const int event_increment = record.event().is_prompt() ? (store_prompts ? 1 : 0) // it's a prompt + : delayed_increment; // it is a delayed-coincidence event + + if (event_increment == 0) + continue; + + DetectionPositionPair<> det_pos; + // because of above consistency check, we can use static_cast here (saving a bit of time) + dynamic_cast(record.event()).get_detection_position(det_pos); + const int ra = det_pos.pos1().axial_coord(); + const int rb = det_pos.pos2().axial_coord(); + const int a = det_pos.pos1().tangential_coord(); + const int b = det_pos.pos2().tangential_coord(); + if (abs(ra - rb) <= max_segment_num_to_process) { + const int det_num_diff = (a - b + 3 * num_detectors_per_ring / 2) % num_detectors_per_ring; + if (det_num_diff <= fan_size / 2 || det_num_diff >= num_detectors_per_ring - fan_size / 2) { + data_fan_sums[ra][a] += event_increment; + data_fan_sums[rb][b] += event_increment; + num_stored_events += event_increment; + } else { } - current_time = new_time; + } else { } - else if (record.is_event() && frame_defs.get_start_time(current_frame_num) <= current_time) - { - // do a consistency check with dynamic_cast first - if (first_event && dynamic_cast(&record.event()) == 0) - error("Currently only works for scanners with discrete detectors."); - first_event=false; - - // see if we increment or decrement the value in the sinogram - const int event_increment = - record.event().is_prompt() - ? ( store_prompts ? 1 : 0 ) // it's a prompt - : delayed_increment;//it is a delayed-coincidence event - - if (event_increment==0) - continue; - - DetectionPositionPair<> det_pos; - // because of above consistency check, we can use static_cast here (saving a bit of time) - dynamic_cast(record.event()). - get_detection_position(det_pos); - const int ra = det_pos.pos1().axial_coord(); - const int rb = det_pos.pos2().axial_coord(); - const int a = det_pos.pos1().tangential_coord(); - const int b = det_pos.pos2().tangential_coord(); - if (abs(ra-rb)<=max_segment_num_to_process) - { - const int det_num_diff = - (a-b+3*num_detectors_per_ring/2)%num_detectors_per_ring; - if (det_num_diff<=fan_size/2 || - det_num_diff>=num_detectors_per_ring-fan_size/2) - { - data_fan_sums[ra][a] += event_increment; - data_fan_sums[rb][b] += event_increment; - num_stored_events += event_increment; - } - else - { - } - } - else - { - } - - } // end of spatial event processing - } // end of while loop over all events - - time_of_last_stored_event = - max(time_of_last_stored_event,current_time); - } + } // end of spatial event processing + } // end of while loop over all events + + time_of_last_stored_event = max(time_of_last_stored_event, current_time); + } timer.stop(); - + cerr << "Last stored event was recorded after time-tick at " << time_of_last_stored_event << " secs\n"; if (current_frame_num <= frame_defs.get_num_frames()) cerr << "Early stop due to EOF. " << endl; @@ -283,13 +234,9 @@ compute() cerr << "\nThis took " << timer.value() << "s CPU time." << endl; } - // write fan sums to file -void -LmFansums:: -write_fan_sums(const Array<2,float>& data_fan_sums, - const unsigned current_frame_num) const -{ +void +LmFansums::write_fan_sums(const Array<2, float>& data_fan_sums, const unsigned current_frame_num) const { char txt[50]; sprintf(txt, "_f%u.dat", current_frame_num); std::string filename = output_filename_prefix; @@ -301,28 +248,19 @@ write_fan_sums(const Array<2,float>& data_fan_sums, END_NAMESPACE_STIR - USING_NAMESPACE_STIR - - - - /************************ main ************************/ +int +main(int argc, char* argv[]) { -int main(int argc, char * argv[]) -{ - - if (argc!=1 && argc!=2) { + if (argc != 1 && argc != 2) { cerr << "Usage: " << argv[0] << " [par_file]\n"; exit(EXIT_FAILURE); } - LmFansums lm_fansums(argc==2 ? argv[1] : 0); + LmFansums lm_fansums(argc == 2 ? argv[1] : 0); lm_fansums.compute(); return EXIT_SUCCESS; } - - - diff --git a/src/listmode_utilities/lm_to_projdata.cxx b/src/listmode_utilities/lm_to_projdata.cxx index 7fc72ce560..a531f8f7a7 100644 --- a/src/listmode_utilities/lm_to_projdata.cxx +++ b/src/listmode_utilities/lm_to_projdata.cxx @@ -1,7 +1,7 @@ // // /*! - \file + \file \ingroup listmode_utilities \brief Program to bin listmode data to 3d sinograms @@ -10,7 +10,7 @@ \author Kris Thielemans \author Sanida Mustafovic - + */ /* Copyright (C) 2000- 2009, Hammersmith Imanet Ltd @@ -39,42 +39,34 @@ using std::endl; USING_NAMESPACE_STIR - - -int main(int argc, char * argv[]) -{ - if (argc>1) - { - if (strcmp(argv[1], "--help") == 0 || - strcmp(argv[1], "-?") == 0) { - cerr << "\nUsage: " << argv[0] << " [par_file]\n" - << "Run " << argv[0] << " --input-formats to list the supported input formats\n"; - exit(EXIT_SUCCESS); - } - // Display the supported inputs, we need this in order to know - // which listmode files are supported - if (strcmp(argv[1], "--input-formats") == 0) - { - cerr << endl << "Supported input file formats:\n"; - InputFileFormatRegistry::default_sptr()-> - list_registered_names(cerr); - exit(EXIT_SUCCESS); - } - if (strcmp(argv[1], "--test_timing_positions") == 0) - { - cerr << "A test function for TOF data which I could not fit anywhere else right now:\n" - "It is going to fill every segment with the index number of the respective TOF position \n" - "and then stop.\n"; - std::cout << argc << std::endl; - std::cout << argv[0] << "\n" << argv[1] << "\n" << argv[2] << std::endl; - LmToProjData application(argc == 3 ? argv[2] : 0); - application.run_tof_test_function(); - exit(EXIT_SUCCESS); - } +int +main(int argc, char* argv[]) { + if (argc > 1) { + if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { + cerr << "\nUsage: " << argv[0] << " [par_file]\n" + << "Run " << argv[0] << " --input-formats to list the supported input formats\n"; + exit(EXIT_SUCCESS); + } + // Display the supported inputs, we need this in order to know + // which listmode files are supported + if (strcmp(argv[1], "--input-formats") == 0) { + cerr << endl << "Supported input file formats:\n"; + InputFileFormatRegistry::default_sptr()->list_registered_names(cerr); + exit(EXIT_SUCCESS); + } + if (strcmp(argv[1], "--test_timing_positions") == 0) { + cerr << "A test function for TOF data which I could not fit anywhere else right now:\n" + "It is going to fill every segment with the index number of the respective TOF position \n" + "and then stop.\n"; + std::cout << argc << std::endl; + std::cout << argv[0] << "\n" << argv[1] << "\n" << argv[2] << std::endl; + LmToProjData application(argc == 3 ? argv[2] : 0); + application.run_tof_test_function(); + exit(EXIT_SUCCESS); + } } - LmToProjData application(argc==2 ? argv[1] : 0); + LmToProjData application(argc == 2 ? argv[1] : 0); application.process_data(); return EXIT_SUCCESS; } - diff --git a/src/listmode_utilities/lm_to_projdata_NiftyPET.cxx b/src/listmode_utilities/lm_to_projdata_NiftyPET.cxx index 07f271d2da..760b2fa1e2 100644 --- a/src/listmode_utilities/lm_to_projdata_NiftyPET.cxx +++ b/src/listmode_utilities/lm_to_projdata_NiftyPET.cxx @@ -1,5 +1,5 @@ /*! - \file + \file \ingroup listmode_utilities \ingroup NiftyPET @@ -29,121 +29,123 @@ USING_NAMESPACE_STIR -static void print_usage_and_exit( const char * const program_name, const int exit_status) -{ - std::cerr << "\n\nUsage : " << program_name << " [-h|--help] listmode_binary_file tstart tstop [-N|--norm_binary ] [-p|--prompts ] [-d|--delayeds ] [-r|--randoms ] [-n|--norm_sino ] [--cuda_device ] [-v|--verbose ]\n\n"; - exit(exit_status); +static void +print_usage_and_exit(const char* const program_name, const int exit_status) { + std::cerr + << "\n\nUsage : " << program_name + << " [-h|--help] listmode_binary_file tstart tstop [-N|--norm_binary ] [-p|--prompts ] [-d|--delayeds " + "] [-r|--randoms ] [-n|--norm_sino ] [--cuda_device ] [-v|--verbose ]\n\n"; + exit(exit_status); } -int main(int argc, char * argv[]) -{ - try { - const char * const program_name = argv[0]; - - // Check for help request - for (int i=1; i0 && argv[0][0]=='-') { - - if (strcmp(argv[0], "-p")==0 || strcmp(argv[0], "--prompts")==0) { - p_filename = argv[1]; - argc-=2; argv+=2; - } - else if (strcmp(argv[0], "-d")==0 || strcmp(argv[0], "--delayeds")==0) { - d_filename = argv[1]; - argc-=2; argv+=2; - } - else if (strcmp(argv[0], "-r")==0 || strcmp(argv[0], "--randoms")==0) { - r_filename = argv[1]; - argc-=2; argv+=2; - } - else if (strcmp(argv[0], "-N")==0 || strcmp(argv[0], "--norm_binary")==0) { - input_norm_binary = argv[1]; - argc-=2; argv+=2; - } - else if (strcmp(argv[0], "-n")==0 || strcmp(argv[0], "--norm_sino")==0) { - n_filename = argv[1]; - argc-=2; argv+=2; - } - else if (strcmp(argv[0], "--cuda_device")==0) { - cuda_device = std::atoi(argv[1]); - argc-=2; argv+=2; - } - else if (strcmp(argv[0], "-v")==0 || strcmp(argv[0], "--verbose")==0) { - verbose = std::atoi(argv[1]); - argc-=2; argv+=2; - } - else { - std::cerr << "Unknown option '" << argv[0] <<"'\n"; - print_usage_and_exit(program_name, EXIT_FAILURE); - } - } - if (p_filename.empty() && d_filename.empty() && r_filename.empty() && n_filename.empty()) { - std::cerr << "At least one output filename required.\n"; - print_usage_and_exit(program_name, EXIT_FAILURE); - } - if (input_norm_binary.empty() && !n_filename.empty()) { - std::cerr << "To extract norm sinogram, need to supply norm binary file.\n"; - print_usage_and_exit(program_name, EXIT_FAILURE); - } - - LmToProjDataNiftyPET lmNP; - lmNP.set_cuda_device(cuda_device); - lmNP.set_cuda_verbosity(verbose); - lmNP.set_listmode_binary_file(input_filename); - lmNP.set_norm_binary_file(input_norm_binary); - lmNP.set_start_time(tstart); - lmNP.set_stop_time(tstop); - lmNP.process_data(); - - // Save outputs - if (!p_filename.empty()) { - std::cout << "\n saving prompts sinogram to " << p_filename << "\n"; - lmNP.get_prompts_sptr()->write_to_file(p_filename); - } - if (!d_filename.empty()) { - std::cout << "\n saving delayeds sinogram to " << d_filename << "\n"; - lmNP.get_delayeds_sptr()->write_to_file(d_filename); - } - if (!r_filename.empty()) { - std::cout << "\n saving randoms sinogram to " << r_filename << "\n"; - lmNP.get_randoms_sptr()->write_to_file(r_filename); - } - if (!n_filename.empty()) { - std::cout << "\n saving norm sinogram to " << n_filename << "\n"; - lmNP.get_norm_sptr()->write_to_file(n_filename); - } +int +main(int argc, char* argv[]) { + try { + const char* const program_name = argv[0]; + + // Check for help request + for (int i = 1; i < argc; ++i) + if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) + print_usage_and_exit(program_name, EXIT_SUCCESS); + + // Check for all compulsory arguments + if (argc < 4) + print_usage_and_exit(program_name, EXIT_FAILURE); + + // Get filenames + const std::string input_filename = argv[1]; + const int tstart = std::atoi(argv[2]); + const int tstop = std::atoi(argv[3]); + + // skip past compulsory arguments + argc -= 4; + argv += 4; + + // Set default value for optional arguments + std::string p_filename, d_filename, r_filename, input_norm_binary, n_filename; + char cuda_device(0); + bool verbose(true); + + // Loop over remaining input + while (argc > 0 && argv[0][0] == '-') { + + if (strcmp(argv[0], "-p") == 0 || strcmp(argv[0], "--prompts") == 0) { + p_filename = argv[1]; + argc -= 2; + argv += 2; + } else if (strcmp(argv[0], "-d") == 0 || strcmp(argv[0], "--delayeds") == 0) { + d_filename = argv[1]; + argc -= 2; + argv += 2; + } else if (strcmp(argv[0], "-r") == 0 || strcmp(argv[0], "--randoms") == 0) { + r_filename = argv[1]; + argc -= 2; + argv += 2; + } else if (strcmp(argv[0], "-N") == 0 || strcmp(argv[0], "--norm_binary") == 0) { + input_norm_binary = argv[1]; + argc -= 2; + argv += 2; + } else if (strcmp(argv[0], "-n") == 0 || strcmp(argv[0], "--norm_sino") == 0) { + n_filename = argv[1]; + argc -= 2; + argv += 2; + } else if (strcmp(argv[0], "--cuda_device") == 0) { + cuda_device = std::atoi(argv[1]); + argc -= 2; + argv += 2; + } else if (strcmp(argv[0], "-v") == 0 || strcmp(argv[0], "--verbose") == 0) { + verbose = std::atoi(argv[1]); + argc -= 2; + argv += 2; + } else { + std::cerr << "Unknown option '" << argv[0] << "'\n"; + print_usage_and_exit(program_name, EXIT_FAILURE); + } + } + if (p_filename.empty() && d_filename.empty() && r_filename.empty() && n_filename.empty()) { + std::cerr << "At least one output filename required.\n"; + print_usage_and_exit(program_name, EXIT_FAILURE); + } + if (input_norm_binary.empty() && !n_filename.empty()) { + std::cerr << "To extract norm sinogram, need to supply norm binary file.\n"; + print_usage_and_exit(program_name, EXIT_FAILURE); } - // If there was an error - catch(const std::exception &error) { - std::cerr << "\nError encountered:\n\t" << error.what() << "\n\n"; - return EXIT_FAILURE; + LmToProjDataNiftyPET lmNP; + lmNP.set_cuda_device(cuda_device); + lmNP.set_cuda_verbosity(verbose); + lmNP.set_listmode_binary_file(input_filename); + lmNP.set_norm_binary_file(input_norm_binary); + lmNP.set_start_time(tstart); + lmNP.set_stop_time(tstop); + lmNP.process_data(); + + // Save outputs + if (!p_filename.empty()) { + std::cout << "\n saving prompts sinogram to " << p_filename << "\n"; + lmNP.get_prompts_sptr()->write_to_file(p_filename); + } + if (!d_filename.empty()) { + std::cout << "\n saving delayeds sinogram to " << d_filename << "\n"; + lmNP.get_delayeds_sptr()->write_to_file(d_filename); + } + if (!r_filename.empty()) { + std::cout << "\n saving randoms sinogram to " << r_filename << "\n"; + lmNP.get_randoms_sptr()->write_to_file(r_filename); } - catch(...) { - std::cerr << "\nError encountered.\n\n"; - return EXIT_FAILURE; + if (!n_filename.empty()) { + std::cout << "\n saving norm sinogram to " << n_filename << "\n"; + lmNP.get_norm_sptr()->write_to_file(n_filename); } - return(EXIT_SUCCESS); + } + + // If there was an error + catch (const std::exception& error) { + std::cerr << "\nError encountered:\n\t" << error.what() << "\n\n"; + return EXIT_FAILURE; + } catch (...) { + std::cerr << "\nError encountered.\n\n"; + return EXIT_FAILURE; + } + return (EXIT_SUCCESS); } diff --git a/src/listmode_utilities/lm_to_projdata_bootstrap.cxx b/src/listmode_utilities/lm_to_projdata_bootstrap.cxx index d85e8ead13..cdd512bac9 100644 --- a/src/listmode_utilities/lm_to_projdata_bootstrap.cxx +++ b/src/listmode_utilities/lm_to_projdata_bootstrap.cxx @@ -1,13 +1,13 @@ // // /*! - \file + \file \ingroup listmode \brief Program to bin listmode data to projection data using bootstrapping (uses stir::LmToProjDataBootstrap) - + \author Kris Thielemans - + $Revision $ */ /* @@ -31,30 +31,21 @@ USING_NAMESPACE_STIR +int +main(int argc, char* argv[]) { - -int main(int argc, char * argv[]) -{ - - if (argc<1 && argc>3) { + if (argc < 1 && argc > 3) { std::cerr << "Usage: " << argv[0] << " [par_file [seed]]\n"; exit(EXIT_FAILURE); } // clumsy way of having extra argument - if (argc==3) - { - LmToProjDataBootstrap - application(argc>=2 ? argv[1] : 0, - atoi(argv[2])); - application.process_data(); - } - else - { - LmToProjDataBootstrap - application(argc==2 ? argv[1] : 0); - application.process_data(); - } + if (argc == 3) { + LmToProjDataBootstrap application(argc >= 2 ? argv[1] : 0, atoi(argv[2])); + application.process_data(); + } else { + LmToProjDataBootstrap application(argc == 2 ? argv[1] : 0); + application.process_data(); + } return EXIT_SUCCESS; } - diff --git a/src/listmode_utilities/lm_to_projdata_with_random_rejection.cxx b/src/listmode_utilities/lm_to_projdata_with_random_rejection.cxx index 28eec99034..18103619ae 100644 --- a/src/listmode_utilities/lm_to_projdata_with_random_rejection.cxx +++ b/src/listmode_utilities/lm_to_projdata_with_random_rejection.cxx @@ -1,13 +1,14 @@ // // /*! - \file + \file \ingroup listmode - \brief Program to bin listmode data to projection data using random rejection of counts (uses stir::LmToProjDataWithRandomRejection) - + \brief Program to bin listmode data to projection data using random rejection of counts (uses + stir::LmToProjDataWithRandomRejection) + \author Kris Thielemans - + */ /* Copyright (C) 2003- 2012, Hammersmith Imanet Ltd @@ -30,19 +31,16 @@ USING_NAMESPACE_STIR +int +main(int argc, char* argv[]) { - -int main(int argc, char * argv[]) -{ - - if ((argc<1) || (argc>3)) { + if ((argc < 1) || (argc > 3)) { std::cerr << "Usage: " << argv[0] << " [par_file fraction_of_counts_to_keep]]\n"; exit(EXIT_FAILURE); } - LmToProjDataWithRandomRejection - application(argc>=2 ? argv[1] : 0); - if (argc==3) + LmToProjDataWithRandomRejection application(argc >= 2 ? argv[1] : 0); + if (argc == 3) application.set_reject_if_above(float(atof(argv[2]))); application.process_data(); return EXIT_SUCCESS; diff --git a/src/listmode_utilities/print_sgl_values.cxx b/src/listmode_utilities/print_sgl_values.cxx index c68fd12e27..dbc553627c 100644 --- a/src/listmode_utilities/print_sgl_values.cxx +++ b/src/listmode_utilities/print_sgl_values.cxx @@ -24,16 +24,13 @@ \author Tim Borgeaud */ - #include "stir/data/SinglesRatesFromSglFile.h" - #include #include #include #include - #ifndef STIR_NO_NAMESPACES using std::cout; using std::cerr; @@ -45,16 +42,12 @@ using std::vector; USING_NAMESPACE_STIR - - - -int -main (int argc, char **argv) -{ +int +main(int argc, char** argv) { vector columns; - // Check arguments. + // Check arguments. // Singles filename + optional bin indices. if (argc < 2) { cerr << "Program to print out values from a singles file.\n\n"; @@ -65,11 +58,10 @@ main (int argc, char **argv) const string sgl_filename = argv[1]; - for (int arg = 2 ; arg < argc ; ++arg) { + for (int arg = 2; arg < argc; ++arg) { columns.push_back(atoi(argv[arg])); - } - - + } + // Singles file object. ecat::ecat7::SinglesRatesFromSglFile singles_from_sgl; @@ -78,53 +70,45 @@ main (int argc, char **argv) const vector times = singles_from_sgl.get_times(); - - // Get total number of time slices. int num_time_slices = singles_from_sgl.get_num_time_slices(); // Get scanner details and, from these, the number of singles units. - const Scanner *scanner = singles_from_sgl.get_scanner_ptr(); + const Scanner* scanner = singles_from_sgl.get_scanner_ptr(); int total_singles_units = scanner->get_num_singles_units(); - - + // If no columns are set. Create a vector with all columns. - if ( columns.size() == 0 ) { - for (int singles_bin = 0 ; singles_bin < total_singles_units ; ++singles_bin) { - columns.push_back(singles_bin); + if (columns.size() == 0) { + for (int singles_bin = 0; singles_bin < total_singles_units; ++singles_bin) { + columns.push_back(singles_bin); } } - // Print columns cout << "# Time "; - for (vector::iterator col = columns.begin() ; col < columns.end() ; ++col) { + for (vector::iterator col = columns.begin(); col < columns.end(); ++col) { cout << setw(9) << *col << " "; } cout << "\n"; - // Loop over all time slices. - for (int time_slice = 0 ; time_slice < num_time_slices ; ++time_slice) { + for (int time_slice = 0; time_slice < num_time_slices; ++time_slice) { // Output time. cout << setw(8) << times[time_slice] << " "; - - for (vector::iterator col = columns.begin() ; col < columns.end() ; ++col) { - if ( *col >= 0 && *col < total_singles_units ) { + for (vector::iterator col = columns.begin(); col < columns.end(); ++col) { + + if (*col >= 0 && *col < total_singles_units) { int val = singles_from_sgl.get_singles_rate(*col, time_slice); - + cout << setw(9) << val << " "; } } - + // Output the end of line. cout << "\n"; - } - - return EXIT_SUCCESS; } diff --git a/src/listmode_utilities/rebin_sgl_file.cxx b/src/listmode_utilities/rebin_sgl_file.cxx index 6994e7b385..3f781b329e 100644 --- a/src/listmode_utilities/rebin_sgl_file.cxx +++ b/src/listmode_utilities/rebin_sgl_file.cxx @@ -26,7 +26,6 @@ \author Tim Borgeaud */ - #include "stir/data/SinglesRatesFromSglFile.h" #include "stir/TimeFrameDefinitions.h" @@ -34,7 +33,6 @@ #include #include - #ifndef STIR_NO_NAMESPACES using std::cerr; using std::endl; @@ -44,114 +42,95 @@ using std::vector; USING_NAMESPACE_STIR - - void -usage(const char *progname) { +usage(const char* progname) { cerr << "A program to rebin an sgl file.\n\n"; - cerr << "There are two ways to use this program.\n"; - cerr << "1) " << progname - << " sgl_input_file sgl_output_file frame_end [frame_ends ...]\n\n"; - cerr << "2) " << progname - << " -f frame_definition_file sgl_input_file sgl_output_file\n\n"; + cerr << "There are two ways to use this program.\n"; + cerr << "1) " << progname << " sgl_input_file sgl_output_file frame_end [frame_ends ...]\n\n"; + cerr << "2) " << progname << " -f frame_definition_file sgl_input_file sgl_output_file\n\n"; cerr << "Frame end times are floating point numbers of seconds\n"; } +int +main(int argc, char** argv) { - -int -main(int argc, char **argv) -{ - - // Check arguments. + // Check arguments. // Singles filename + optional output filename - if (argc < 4 ) { + if (argc < 4) { usage(argv[0]); exit(EXIT_FAILURE); } - - string input_filename; string output_filename; - vector new_times; - - + vector new_times; // Check to see if -f was supplied as the first argument. - if ( argv[1][0] == '-' ) { + if (argv[1][0] == '-') { // Option supplied - + int arg_len = strlen(argv[1]); - if ( arg_len != 2 || argv[1][1] != 'f' ) { - - for (int i = 1 ; i < arg_len ; ++i ) { - if ( argv[1][i] != 'f' ) { + if (arg_len != 2 || argv[1][1] != 'f') { + + for (int i = 1; i < arg_len; ++i) { + if (argv[1][i] != 'f') { cerr << "Unknown option " << argv[1][i] << endl; } } - + usage(argv[0]); exit(EXIT_FAILURE); } - + const string fdef_filename = argv[2]; input_filename = argv[3]; output_filename = argv[4]; - TimeFrameDefinitions time_frames(fdef_filename); - + double last_end = 0.0; // Create the new ending times by looping over the frames. - for (unsigned int frame = 1 ; frame <= time_frames.get_num_frames() ; ++frame) { + for (unsigned int frame = 1; frame <= time_frames.get_num_frames(); ++frame) { double frame_start = time_frames.get_start_time(frame); double frame_end = time_frames.get_end_time(frame); - //cerr << "Start: " << frame_start << " End: " << frame_end << endl; - - if ( frame_start != last_end ) { - //cerr << "Added frame at: " << frame_start << endl; + // cerr << "Start: " << frame_start << " End: " << frame_end << endl; + + if (frame_start != last_end) { + // cerr << "Added frame at: " << frame_start << endl; // Add an additional frame that ends at the start of this frame. new_times.push_back(frame_start); } // Add the frame end. new_times.push_back(frame_end); - + last_end = frame_end; } - } else { - + input_filename = argv[1]; output_filename = argv[2]; - + // Set up vector of new ending times. new_times = vector(argc - 3); - + // Read frame end times - for(int arg = 3 ; arg < argc ; arg++) { + for (int arg = 3; arg < argc; arg++) { new_times[arg - 3] = atof(argv[arg]); } - } - - - // Singles file object. ecat::ecat7::SinglesRatesFromSglFile singles_from_sgl; // Read the singles file. singles_from_sgl.read_singles_from_sgl_file(input_filename); - - std::ofstream output; // If necessary, open the output file. @@ -161,13 +140,11 @@ main(int argc, char **argv) error("Error opening output file\n"); } - // rebin singles_from_sgl.rebin(new_times); - + // Output. singles_from_sgl.write(output); - - + return EXIT_SUCCESS; } diff --git a/src/listmode_utilities/scan_sgl_file.cxx b/src/listmode_utilities/scan_sgl_file.cxx index 30663c3338..54d5bed599 100644 --- a/src/listmode_utilities/scan_sgl_file.cxx +++ b/src/listmode_utilities/scan_sgl_file.cxx @@ -23,7 +23,6 @@ \author Tim Borgeaud */ - #include "stir/data/SinglesRatesFromSglFile.h" #include @@ -40,11 +39,8 @@ using std::vector; USING_NAMESPACE_STIR - - const int MAX_VALID_VALUE = 1000000; - // Minimum allowed fraction of previous value. const float MIN_PREVIOUS_FRACTION = 0.225; @@ -57,128 +53,103 @@ const float MIN_MEDIAN_FRACTION = 0.2; // Max allowed multiple of median of next set of values. const float MAX_MEDIAN_MULTIPLE = 3.5; - - // Tolerance between adjacent bins. const float ADJACENT_TOLERANCE = 0.75; const int MEDIAN_SIZE = 7; // Number of elements forward for which the median is calculated. - - // // Function to calculate the median of singles values from // start_index to end_index (inclusive). // -int -calcMedian(const ecat::ecat7::SinglesRatesFromSglFile& singles_rates, - int singles_bin_index, int start_slice, int end_slice) { - +int +calcMedian(const ecat::ecat7::SinglesRatesFromSglFile& singles_rates, int singles_bin_index, int start_slice, int end_slice) { + int num_elements = end_slice - start_slice + 1; - + // Create a new temporary vector. vector elements(num_elements); - + // Fill the temporary vector. - for(int index = 0 ; index < num_elements ; index++) { - elements[index] = singles_rates.get_singles_rate(singles_bin_index, - start_slice + index); + for (int index = 0; index < num_elements; index++) { + elements[index] = singles_rates.get_singles_rate(singles_bin_index, start_slice + index); } - + // Calculate which element, of the sorted set, will be the median. vector::iterator median = elements.begin() + (num_elements / 2); - + // Partially sort the elements. nth_element(elements.begin(), median, elements.end()); - + // Return the element at the median position. - return(*median); - + return (*median); } - - - - // // Function to calculate a median of up to MEDIAN_SIZE values // starting from start_slice. // int -getMedian(const ecat::ecat7::SinglesRatesFromSglFile& singles_rates, - int singles_bin_index, int start_slice) { +getMedian(const ecat::ecat7::SinglesRatesFromSglFile& singles_rates, int singles_bin_index, int start_slice) { int num_slices = singles_rates.get_num_time_slices(); int end_slice = start_slice + MEDIAN_SIZE - 1; - if ( end_slice >= num_slices ) { + if (end_slice >= num_slices) { end_slice = num_slices - 1; } - - return(calcMedian(singles_rates, singles_bin_index, start_slice, end_slice)); -} - + return (calcMedian(singles_rates, singles_bin_index, start_slice, end_slice)); +} bool -compareAdjacentBin(int value, int slice, int singles_bin_index, - const ecat::ecat7::SinglesRatesFromSglFile& singles_rates, +compareAdjacentBin(int value, int slice, int singles_bin_index, const ecat::ecat7::SinglesRatesFromSglFile& singles_rates, int transaxial_offset) { - const Scanner *scanner = singles_rates.get_scanner_ptr(); + const Scanner* scanner = singles_rates.get_scanner_ptr(); int axial = scanner->get_axial_singles_unit(singles_bin_index); int num_transaxial = scanner->get_num_transaxial_singles_units(); int transaxial = scanner->get_transaxial_singles_unit(singles_bin_index); - + transaxial = (transaxial + transaxial_offset) % num_transaxial; - if ( transaxial < 0 ) { + if (transaxial < 0) { transaxial += num_transaxial; } - - + int bin_index = scanner->get_singles_bin_index(axial, transaxial); int rate = singles_rates.get_singles_rate(bin_index, slice); - - if ( (value >= (1.0 - ADJACENT_TOLERANCE) * rate) && - (value <= (1.0 + ADJACENT_TOLERANCE) * rate) ) { - return(true); - } - - return(false); -} - + if ((value >= (1.0 - ADJACENT_TOLERANCE) * rate) && (value <= (1.0 + ADJACENT_TOLERANCE) * rate)) { + return (true); + } + return (false); +} // // Check a rate against transaxially adjacent bins. // -// Note that a single bucket controller encompasses a number of -// axial singles units, so checking bins with the same transaxial +// Note that a single bucket controller encompasses a number of +// axial singles units, so checking bins with the same transaxial // position may result checking bins from the same bucket. // bool -checkAdjacentBins(int value, int slice, int singles_bin_index, - const ecat::ecat7::SinglesRatesFromSglFile& singles_rates) { - - if ( compareAdjacentBin(value, slice, singles_bin_index, singles_rates, -1) ) { - if ( compareAdjacentBin(value, slice, singles_bin_index, singles_rates, -2) || - compareAdjacentBin(value, slice, singles_bin_index, singles_rates, 1)) { - return(true); +checkAdjacentBins(int value, int slice, int singles_bin_index, const ecat::ecat7::SinglesRatesFromSglFile& singles_rates) { + + if (compareAdjacentBin(value, slice, singles_bin_index, singles_rates, -1)) { + if (compareAdjacentBin(value, slice, singles_bin_index, singles_rates, -2) || + compareAdjacentBin(value, slice, singles_bin_index, singles_rates, 1)) { + return (true); } - } else if ( compareAdjacentBin(value, slice, singles_bin_index, singles_rates, 1) && - compareAdjacentBin(value, slice, singles_bin_index, singles_rates, 2) ) { - return(true); + } else if (compareAdjacentBin(value, slice, singles_bin_index, singles_rates, 1) && + compareAdjacentBin(value, slice, singles_bin_index, singles_rates, 2)) { + return (true); } - - return(false); -} - - - + return (false); +} // // Check that a value is either within a tolerance of the median @@ -186,214 +157,169 @@ checkAdjacentBins(int value, int slice, int singles_bin_index, // value and a supplied value. // // Returns true if the value is ok. -// +// bool -checkValue(int value, int slice, int singles_bin_index, - const ecat::ecat7::SinglesRatesFromSglFile& singles_rates, +checkValue(int value, int slice, int singles_bin_index, const ecat::ecat7::SinglesRatesFromSglFile& singles_rates, int previous_value) { - - // Check absolute range ( 0 <= Value <= MAX_VALID_VALUE ). - if ( value > 0 && value < MAX_VALID_VALUE ) { - + // Check absolute range ( 0 <= Value <= MAX_VALID_VALUE ). + if (value > 0 && value < MAX_VALID_VALUE) { + // Check against previous value. - if ( ((MIN_PREVIOUS_FRACTION * previous_value) - value) <= 0 && - ((MAX_PREVIOUS_MULTIPLE * previous_value) - value) >= 0 ) { + if (((MIN_PREVIOUS_FRACTION * previous_value) - value) <= 0 && ((MAX_PREVIOUS_MULTIPLE * previous_value) - value) >= 0) { // Value is good. - return(true); + return (true); } - // Check against values from adjacent (transaxial) bins. - if ( checkAdjacentBins(value, slice, singles_bin_index, singles_rates) ) { + if (checkAdjacentBins(value, slice, singles_bin_index, singles_rates)) { // Value is good. - return(true); + return (true); } - - // See if there are any further values to compare the current value with. int num_slices = singles_rates.get_num_time_slices(); - - if ( slice < num_slices - 1 ) { - + + if (slice < num_slices - 1) { + // Calculate the median (should work for only one value). int median = getMedian(singles_rates, singles_bin_index, slice + 1); - // Check against median value. Note that the first slice only needs be less than // median * MAX_MULTIPLE. - if ( (slice == 0 || ((MIN_MEDIAN_FRACTION * median - value)) <= 0) && - ((MAX_MEDIAN_MULTIPLE * median) - value) >= 0 ) { + if ((slice == 0 || ((MIN_MEDIAN_FRACTION * median - value)) <= 0) && ((MAX_MEDIAN_MULTIPLE * median) - value) >= 0) { // Value is good. - return(true); + return (true); } - // Check to see if the value lies between previous and median. - if ( ((previous_value > median) && (value >= median && value <= previous_value)) || - (value >= previous_value && value <= median) ) { + if (((previous_value > median) && (value >= median && value <= previous_value)) || + (value >= previous_value && value <= median)) { // Value is good. - return(true); + return (true); } } } - - return(false); -} - - - - - + return (false); +} // // Function to check and correct a particular value. // // Returns true if a value was corrected. bool -correctSinglesValue(int value, int slice, int singles_bin_index, - ecat::ecat7::SinglesRatesFromSglFile& singles_rates) { - - +correctSinglesValue(int value, int slice, int singles_bin_index, ecat::ecat7::SinglesRatesFromSglFile& singles_rates) { + // Previous value. Set to 0 for the first slice. int previous = 0; - - if ( slice > 0 ) { + if (slice > 0) { previous = singles_rates.get_singles_rate(singles_bin_index, slice - 1); } - - if ( checkValue(value, slice, singles_bin_index, singles_rates, previous) == true ) { - return(false); + if (checkValue(value, slice, singles_bin_index, singles_rates, previous) == true) { + return (false); } - // The value is not good. Therefore, correct the value using // the previous value (which must be good) and the next good value. int num_slices = singles_rates.get_num_time_slices(); int next_slice; int next_value; - - for(next_slice = slice + 1; next_slice < num_slices ; ++next_slice ) { + + for (next_slice = slice + 1; next_slice < num_slices; ++next_slice) { next_value = singles_rates.get_singles_rate(singles_bin_index, next_slice); - - if ( checkValue(next_value, next_slice, singles_bin_index, singles_rates, previous) ) { + + if (checkValue(next_value, next_slice, singles_bin_index, singles_rates, previous)) { break; } } - - if ( next_slice < num_slices ) { + + if (next_slice < num_slices) { // Correct using previous and next_slice. int next_sep = next_slice - slice; - + // Adjust value. - value = ((previous * next_sep) + next_value) / (1 + next_sep); - + value = ((previous * next_sep) + next_value) / (1 + next_sep); + singles_rates.set_singles_rate(singles_bin_index, slice, value); } - + // Value was bad so return true. - return(true); - + return (true); } - - - - - -// +// // check and correct all the singles values. Return true if any values were corrected. -// +// // bool -correctAllValues(ecat::ecat7::SinglesRatesFromSglFile& singles_rates, - std::ostream& output) { +correctAllValues(ecat::ecat7::SinglesRatesFromSglFile& singles_rates, std::ostream& output) { bool changed = false; // Get scanner details and, from these, the number of singles units. - const Scanner *scanner = singles_rates.get_scanner_ptr(); + const Scanner* scanner = singles_rates.get_scanner_ptr(); int total_singles_units = scanner->get_num_singles_units(); - + // Get total number of time slices. int num_slices = singles_rates.get_num_time_slices(); // Loop over all bins. - for(int singles_bin = 0 ; singles_bin < total_singles_units ; ++singles_bin) { - + for (int singles_bin = 0; singles_bin < total_singles_units; ++singles_bin) { + // Loop over each time slice. - for(int slice = 0 ; slice < num_slices ; ++slice) { - + for (int slice = 0; slice < num_slices; ++slice) { + int value = singles_rates.get_singles_rate(singles_bin, slice); - - if ( correctSinglesValue(value, slice, singles_bin, singles_rates) ) { - + + if (correctSinglesValue(value, slice, singles_bin, singles_rates)) { + changed = true; int new_value = singles_rates.get_singles_rate(singles_bin, slice); // Print details of the change to the output. - output << "Bin " << singles_bin - << " Slice index: " << slice - << " Rate: " << value - << " New estimate: " << new_value + output << "Bin " << singles_bin << " Slice index: " << slice << " Rate: " << value << " New estimate: " << new_value << "\n"; } - } - } - - return(changed); - -} - - - - - + return (changed); +} -int -main (int argc, char **argv) -{ +int +main(int argc, char** argv) { - // Check arguments. + // Check arguments. // Singles filename + optional output filename if (argc > 3 || argc < 2) { - cerr << "Usage: " << argv[0] << " sgl_filename [output_filename]\n"; - exit(EXIT_FAILURE); + cerr << "Usage: " << argv[0] << " sgl_filename [output_filename]\n"; + exit(EXIT_FAILURE); } - const string sgl_filename = argv[1]; bool write_file = false; string output_filename = ""; - if ( argc == 3 ) { + if (argc == 3) { write_file = true; output_filename = argv[2]; - } - - + } + // Singles file object. ecat::ecat7::SinglesRatesFromSglFile singles_from_sgl; // Read the singles file. singles_from_sgl.read_singles_from_sgl_file(sgl_filename); - const vector times = singles_from_sgl.get_times(); - - std::ofstream output; // If necessary, open the output file. - if ( output_filename.length() ) { + if (output_filename.length()) { output.open(output_filename.c_str(), std::ios_base::out); @@ -401,13 +327,10 @@ main (int argc, char **argv) error("Error opening output file\n"); } - - - if ( correctAllValues(singles_from_sgl, cout) && write_file ) { + if (correctAllValues(singles_from_sgl, cout) && write_file) { // Write data to output file. singles_from_sgl.write(output); } - - + return EXIT_SUCCESS; } diff --git a/src/modelling_buildblock/KineticModel.cxx b/src/modelling_buildblock/KineticModel.cxx index 94c21f9e07..b4252a9dbd 100644 --- a/src/modelling_buildblock/KineticModel.cxx +++ b/src/modelling_buildblock/KineticModel.cxx @@ -22,22 +22,19 @@ \author Charalampos Tsoumpas - This is the most basic class for including kinetic models. + This is the most basic class for including kinetic models. */ - #include "stir/modelling/KineticModel.h" - START_NAMESPACE_STIR -const char * const -KineticModel::registered_name = "Kinetic Model Type"; +const char* const KineticModel::registered_name = "Kinetic Model Type"; -KineticModel::KineticModel() //!< default constructor -{ } -KineticModel::~KineticModel() //!< default destructor -{ } +KineticModel::KineticModel() //!< default constructor +{} +KineticModel::~KineticModel() //!< default destructor +{} END_NAMESPACE_STIR diff --git a/src/modelling_buildblock/ParametricDiscretisedDensity.cxx b/src/modelling_buildblock/ParametricDiscretisedDensity.cxx index 08603dd812..f638adfce3 100644 --- a/src/modelling_buildblock/ParametricDiscretisedDensity.cxx +++ b/src/modelling_buildblock/ParametricDiscretisedDensity.cxx @@ -25,7 +25,7 @@ \author Kris Thielemans \author Richard Brown - + */ #include "stir/modelling/ParametricDiscretisedDensity.h" @@ -45,9 +45,7 @@ START_NAMESPACE_STIR ///////////////////////////////////////////////////////////////////////////////////// TEMPLATE unsigned int -ParamDiscDensity:: -get_num_params() -{ +ParamDiscDensity::get_num_params() { // somewhat naughty trick to get elemT of DiscDensityT typedef typename DiscDensityT::full_value_type KinParsT; const KinParsT dummy; @@ -55,23 +53,16 @@ get_num_params() } TEMPLATE -ParamDiscDensity:: -ParametricDiscretisedDensity(const DynamicDiscretisedDensity& dyn_im) - : base_type(dyn_im.get_density(1).get_index_range(), - dyn_im.get_density(1).get_origin(), - dynamic_cast&>(dyn_im.get_density(1)).get_grid_spacing()) -{ - this->set_exam_info(dyn_im.get_exam_info()); +ParamDiscDensity::ParametricDiscretisedDensity(const DynamicDiscretisedDensity& dyn_im) + : base_type(dyn_im.get_density(1).get_index_range(), dyn_im.get_density(1).get_origin(), + dynamic_cast&>(dyn_im.get_density(1)).get_grid_spacing()) { + this->set_exam_info(dyn_im.get_exam_info()); } TEMPLATE -ParamDiscDensity:: -ParametricDiscretisedDensity(const SingleDiscretisedDensityType& im) - : base_type(im.get_index_range(), - im.get_origin(), - dynamic_cast&>(im).get_grid_spacing()) -{ - this->set_exam_info(im.get_exam_info()); +ParamDiscDensity::ParametricDiscretisedDensity(const SingleDiscretisedDensityType& im) + : base_type(im.get_index_range(), im.get_origin(), dynamic_cast&>(im).get_grid_spacing()) { + this->set_exam_info(im.get_exam_info()); } #if 0 @@ -95,11 +86,11 @@ ParametricDiscretisedDensity(const VectorWithOffsetget_num_params(); ++f) { -#if 1 +# if 1 // for some reason, the following gives a segmentation fault in gcc 4.1 optimised mode. // Maybe because we're calling a member function in the constructor? this->update_parametric_image(*densities[f], f); -#else +# else // alternative (untested) const SingleDiscretisedDensityType& current_density = dynamic_cast(*densities[f]); @@ -114,7 +105,7 @@ ParametricDiscretisedDensity(const VectorWithOffsetget_num_params()); +ParamDiscDensity::update_parametric_image(const SingleDiscretisedDensityType& single_density, const unsigned int param_num) { + assert(param_num <= this->get_num_params()); assert(single_density.get_index_range() == this->get_index_range()); - const unsigned int f=param_num; - typename SingleDiscretisedDensityType::const_full_iterator single_density_iter = - single_density.begin_all(); - const typename SingleDiscretisedDensityType::const_full_iterator end_single_density_iter = - single_density.end_all(); - typename ParamDiscDensity::full_densel_iterator parametric_density_iter = - this->begin_all_densel(); - while (single_density_iter!=end_single_density_iter) - { - if (parametric_density_iter == this->end_all_densel()) - error("update ITER"); - //(*parametric_density_iter)[f] = *single_density_iter; - const float tmp = *single_density_iter; - (*parametric_density_iter)[f] = tmp; - ++single_density_iter; ++parametric_density_iter; - } + const unsigned int f = param_num; + typename SingleDiscretisedDensityType::const_full_iterator single_density_iter = single_density.begin_all(); + const typename SingleDiscretisedDensityType::const_full_iterator end_single_density_iter = single_density.end_all(); + typename ParamDiscDensity::full_densel_iterator parametric_density_iter = this->begin_all_densel(); + while (single_density_iter != end_single_density_iter) { + if (parametric_density_iter == this->end_all_densel()) + error("update ITER"); + //(*parametric_density_iter)[f] = *single_density_iter; + const float tmp = *single_density_iter; + (*parametric_density_iter)[f] = tmp; + ++single_density_iter; + ++parametric_density_iter; + } // TODO Currently need this to avoid segmentation fault with 4.1... // std::cerr << " Done\n"; } TEMPLATE -ParamDiscDensity * -ParamDiscDensity:: -read_from_file(const std::string& filename) // The written image is read in respect to its center as origin!!! +ParamDiscDensity* +ParamDiscDensity::read_from_file(const std::string& filename) // The written image is read in respect to its center as origin!!! { - unique_ptr param_sptr - (stir::read_from_file(filename)); + unique_ptr param_sptr(stir::read_from_file(filename)); return param_sptr.release(); } TEMPLATE -ParamDiscDensity * -ParamDiscDensity:: -get_empty_copy() const -{ +ParamDiscDensity* +ParamDiscDensity::get_empty_copy() const { // TODO maybe this can be done smarter by using base_type::get_empty_copy. Doesn't matter too much though. - ParamDiscDensity * res = this->clone(); - typename ParamDiscDensity::iterator parametric_density_iter = - res->begin(); - while (parametric_density_iter!=res->end()) - { - assign(*parametric_density_iter++, 0); - } + ParamDiscDensity* res = this->clone(); + typename ParamDiscDensity::iterator parametric_density_iter = res->begin(); + while (parametric_density_iter != res->end()) { + assign(*parametric_density_iter++, 0); + } return res; } TEMPLATE -ParamDiscDensity * -ParamDiscDensity:: -clone() const -{ +ParamDiscDensity* +ParamDiscDensity::clone() const { return new ParamDiscDensity(*this); } TEMPLATE template -void -ParamDiscDensity:: -construct_single_density_using_function(typename ParamDiscDensity::SingleDiscretisedDensityType& density, KPFunctionObject f) const -{ - std::transform(this->begin_all_densel(), - this->end_all_densel(), - density.begin_all(), - f); +void +ParamDiscDensity::construct_single_density_using_function(typename ParamDiscDensity::SingleDiscretisedDensityType& density, + KPFunctionObject f) const { + std::transform(this->begin_all_densel(), this->end_all_densel(), density.begin_all(), f); } - TEMPLATE template const typename ParamDiscDensity::SingleDiscretisedDensityType -ParamDiscDensity:: -construct_single_density_using_function(KPFunctionObject f) const -{ +ParamDiscDensity::construct_single_density_using_function(KPFunctionObject f) const { // TODO this will only work for VoxelsOnCartesianGrid - SingleDiscretisedDensityType - density(this->get_exam_info_sptr(), - this->get_index_range(), - this->get_origin(), - this->get_grid_spacing()); + SingleDiscretisedDensityType density(this->get_exam_info_sptr(), this->get_index_range(), this->get_origin(), + this->get_grid_spacing()); this->construct_single_density_using_function(density, f); return density; } TEMPLATE -void -ParamDiscDensity:: -construct_single_density(typename ParamDiscDensity::SingleDiscretisedDensityType& density, const int index) const -{ +void +ParamDiscDensity::construct_single_density(typename ParamDiscDensity::SingleDiscretisedDensityType& density, + const int index) const { using namespace boost::lambda; // TODO this will only work for elemT==float this->construct_single_density_using_function(density, ret(_1[index])); } - TEMPLATE const typename ParamDiscDensity::SingleDiscretisedDensityType -ParamDiscDensity:: -construct_single_density(const int index) const -{ +ParamDiscDensity::construct_single_density(const int index) const { using namespace boost::lambda; // TODO this will only work for elemT==float return this->construct_single_density_using_function(ret(_1[index])); } /////////////////////////////// -#if 0 //!< Implementation of non-const functions - which should be able to update a single parameter of a parametric image. +#if 0 //!< Implementation of non-const functions - which should be able to update a single parameter of a parametric image. TEMPLATE template void @@ -306,16 +270,13 @@ construct_single_density(const int index) return this->construct_single_density_using_function(ret(_1[index])); } -#endif - +#endif #undef ParamDiscDensity #undef TEMPLATE // instantiations // template class ParametricDiscretisedDensity<3,KineticParameters >; - template class ParametricDiscretisedDensity; +template class ParametricDiscretisedDensity; END_NAMESPACE_STIR - - diff --git a/src/modelling_buildblock/PatlakPlot.cxx b/src/modelling_buildblock/PatlakPlot.cxx index dac08a8033..ed67018a9b 100644 --- a/src/modelling_buildblock/PatlakPlot.cxx +++ b/src/modelling_buildblock/PatlakPlot.cxx @@ -25,308 +25,284 @@ */ - #include "stir/modelling/PatlakPlot.h" #include "stir/linear_regression.h" START_NAMESPACE_STIR void -PatlakPlot:: -set_defaults() -{ +PatlakPlot::set_defaults() { base_type::set_defaults(); - this->_blood_data_filename=""; - this->_cal_factor=1.F; - this->_starting_frame=0; - this->_time_shift=0.; - this->_in_correct_scale=false; - this->_in_total_cnt=false; + this->_blood_data_filename = ""; + this->_cal_factor = 1.F; + this->_starting_frame = 0; + this->_time_shift = 0.; + this->_in_correct_scale = false; + this->_in_total_cnt = false; } -const char * const -PatlakPlot::registered_name = "Patlak Plot"; +const char* const PatlakPlot::registered_name = "Patlak Plot"; //! default constructor -PatlakPlot::PatlakPlot() -{ - this->_matrix_is_stored=false; +PatlakPlot::PatlakPlot() { + this->_matrix_is_stored = false; this->set_defaults(); } -PatlakPlot::~PatlakPlot() //!< default destructor -{ } +PatlakPlot::~PatlakPlot() //!< default destructor +{} - //! Simply get model matrix if it has been already stored +//! Simply get model matrix if it has been already stored ModelMatrix<2> -PatlakPlot:: -get_model_matrix() const -{ - if(_matrix_is_stored==false) +PatlakPlot::get_model_matrix() const { + if (_matrix_is_stored == false) error("It seems that ModelMatrix has not been set, yet. "); - return _model_matrix ; + return _model_matrix; } -//! Simply set model matrix -void PatlakPlot::set_model_matrix(ModelMatrix<2> model_matrix) -{ - this->_model_matrix=model_matrix; - this->_matrix_is_stored=true; +//! Simply set model matrix +void +PatlakPlot::set_model_matrix(ModelMatrix<2> model_matrix) { + this->_model_matrix = model_matrix; + this->_matrix_is_stored = true; } - //! Create model matrix from private members +//! Create model matrix from private members void -PatlakPlot:: -create_model_matrix() -{ - if(_matrix_is_stored==false) - { - // Create empty Model matrix. this is a [2 x frames] matrix, that contains Cp(t) and \int{Ct(t)} for each frame (Cp(t): radiotracer concentration on plasma) - - // NOTE: as we are working in time frames, and not discrete time points, Cp(t) is not a value of Cp at a given single time, t, but instead - // it is the integral of Cp on that time frame , \int_{t_start}^{t_end} Cp(t) dt, for each time frame. The same happens with \int{Cp(t)} - // All this is handled in the PlasmaData class, and it's not visible here. - - // BasicCoordinate just changes the indexing range (instead of 0-N, to some min to some max) - BasicCoordinate<2,int> min_range; - BasicCoordinate<2,int> max_range; - min_range[1]=1; min_range[2]=this->_starting_frame; - max_range[1]=2; max_range[2]=this->_plasma_frame_data.size(); - IndexRange<2> data_range(min_range,max_range); - Array<2,float> patlak_array(data_range); - VectorWithOffset time_vector(min_range[2],max_range[2]); - PlasmaData::const_iterator cur_iter=this->_plasma_frame_data.begin(); - - double sum_value=0.; - unsigned int sample_num; - // Compute the value of the integral of Cp(t) for frames before the one we want to start applying Patlak to. - // Remember that this code requires all frames, from t=0 to be included, otherwise this integral will be wrongly computed. - // TODO: do not require the dynamic images to exist to do this integral. - for(sample_num=1 ; sample_num_starting_frame; ++sample_num, ++cur_iter ) - sum_value+=cur_iter->get_plasma_counts_in_kBq()*this->_plasma_frame_data.get_time_frame_definitions().get_duration(sample_num); - - assert(cur_iter==this->_plasma_frame_data.begin()+this->_starting_frame-1); - // For each frame that we are interested in, fill the model matrix. - for(sample_num=this->_starting_frame ; cur_iter!=this->_plasma_frame_data.end() ; ++sample_num, ++cur_iter ) - { - sum_value+=cur_iter->get_plasma_counts_in_kBq()*this->_plasma_frame_data.get_time_frame_definitions().get_duration(sample_num); - // integral of Cp(t) - patlak_array[1][sample_num]= static_cast(sum_value); - // Cp(t) - patlak_array[2][sample_num]=cur_iter->get_plasma_counts_in_kBq(); - // As we will do the reconstruction in un-corrected data, if the plasma data is corrected, we need to undo that - if(this->_plasma_frame_data.get_is_decay_corrected()) - { - const float dec_fact= - static_cast(decay_correction_factor(this->_plasma_frame_data.get_isotope_halflife(),this->_plasma_frame_data.get_time_frame_definitions().get_start_time(sample_num), - this->_plasma_frame_data.get_time_frame_definitions().get_end_time(sample_num))); - patlak_array[1][sample_num]/=dec_fact; - patlak_array[2][sample_num]/=dec_fact; - time_vector[sample_num]= static_cast(0.5*(this->_frame_defs.get_end_time(sample_num)+this->_frame_defs.get_start_time(sample_num))); - } - } - if(this->_plasma_frame_data.get_is_decay_corrected()) - warning("Uncorrecting previous decay correction, while putting the plasma_data into the model_matrix."); - else - error("plasma_data have not been corrected during the process, which will create wrong results!!!"); - - assert(sample_num-1==this->_plasma_frame_data.size()); - this->_model_matrix.set_model_array(patlak_array); - this->_model_matrix.set_time_vector(time_vector); - // Uncalibrate the ModelMatrix instead of Calibrating all the Dynamic Images. This should make faster the computation. - // Supposes the images are not calibrated. - this->_model_matrix.uncalibrate(this->_cal_factor); - if(this->_in_total_cnt) - this->_model_matrix.convert_to_total_frame_counts(this->_frame_defs); - this->_model_matrix.set_is_in_correct_scale(this->_in_correct_scale); - this->_model_matrix.threshold_model_array(.000000001F); - this->_matrix_is_stored=true; +PatlakPlot::create_model_matrix() { + if (_matrix_is_stored == false) { + // Create empty Model matrix. this is a [2 x frames] matrix, that contains Cp(t) and \int{Ct(t)} for each frame (Cp(t): + // radiotracer concentration on plasma) + + // NOTE: as we are working in time frames, and not discrete time points, Cp(t) is not a value of Cp at a given single time, t, + // but instead + // it is the integral of Cp on that time frame , \int_{t_start}^{t_end} Cp(t) dt, for each time frame. The same happens + // with \int{Cp(t)} All this is handled in the PlasmaData class, and it's not visible here. + + // BasicCoordinate just changes the indexing range (instead of 0-N, to some min to some max) + BasicCoordinate<2, int> min_range; + BasicCoordinate<2, int> max_range; + min_range[1] = 1; + min_range[2] = this->_starting_frame; + max_range[1] = 2; + max_range[2] = this->_plasma_frame_data.size(); + IndexRange<2> data_range(min_range, max_range); + Array<2, float> patlak_array(data_range); + VectorWithOffset time_vector(min_range[2], max_range[2]); + PlasmaData::const_iterator cur_iter = this->_plasma_frame_data.begin(); + + double sum_value = 0.; + unsigned int sample_num; + // Compute the value of the integral of Cp(t) for frames before the one we want to start applying Patlak to. + // Remember that this code requires all frames, from t=0 to be included, otherwise this integral will be wrongly computed. + // TODO: do not require the dynamic images to exist to do this integral. + for (sample_num = 1; sample_num < this->_starting_frame; ++sample_num, ++cur_iter) + sum_value += + cur_iter->get_plasma_counts_in_kBq() * this->_plasma_frame_data.get_time_frame_definitions().get_duration(sample_num); + + assert(cur_iter == this->_plasma_frame_data.begin() + this->_starting_frame - 1); + // For each frame that we are interested in, fill the model matrix. + for (sample_num = this->_starting_frame; cur_iter != this->_plasma_frame_data.end(); ++sample_num, ++cur_iter) { + sum_value += + cur_iter->get_plasma_counts_in_kBq() * this->_plasma_frame_data.get_time_frame_definitions().get_duration(sample_num); + // integral of Cp(t) + patlak_array[1][sample_num] = static_cast(sum_value); + // Cp(t) + patlak_array[2][sample_num] = cur_iter->get_plasma_counts_in_kBq(); + // As we will do the reconstruction in un-corrected data, if the plasma data is corrected, we need to undo that + if (this->_plasma_frame_data.get_is_decay_corrected()) { + const float dec_fact = static_cast( + decay_correction_factor(this->_plasma_frame_data.get_isotope_halflife(), + this->_plasma_frame_data.get_time_frame_definitions().get_start_time(sample_num), + this->_plasma_frame_data.get_time_frame_definitions().get_end_time(sample_num))); + patlak_array[1][sample_num] /= dec_fact; + patlak_array[2][sample_num] /= dec_fact; + time_vector[sample_num] = + static_cast(0.5 * (this->_frame_defs.get_end_time(sample_num) + this->_frame_defs.get_start_time(sample_num))); + } } - else + if (this->_plasma_frame_data.get_is_decay_corrected()) + warning("Uncorrecting previous decay correction, while putting the plasma_data into the model_matrix."); + else + error("plasma_data have not been corrected during the process, which will create wrong results!!!"); + + assert(sample_num - 1 == this->_plasma_frame_data.size()); + this->_model_matrix.set_model_array(patlak_array); + this->_model_matrix.set_time_vector(time_vector); + // Uncalibrate the ModelMatrix instead of Calibrating all the Dynamic Images. This should make faster the computation. + // Supposes the images are not calibrated. + this->_model_matrix.uncalibrate(this->_cal_factor); + if (this->_in_total_cnt) + this->_model_matrix.convert_to_total_frame_counts(this->_frame_defs); + this->_model_matrix.set_is_in_correct_scale(this->_in_correct_scale); + this->_model_matrix.threshold_model_array(.000000001F); + this->_matrix_is_stored = true; + } else warning("ModelMatrix has been already created"); } -Succeeded -PatlakPlot::set_up() -{ +Succeeded +PatlakPlot::set_up() { // if (base_type::set_up() != Succeeded::yes) // return Succeeded::no; this->create_model_matrix(); - if (this->_matrix_is_stored==true) + if (this->_matrix_is_stored == true) return Succeeded::yes; else return Succeeded::no; } -void -PatlakPlot::apply_linear_regression(ParametricVoxelsOnCartesianGrid & par_image, const DynamicDiscretisedDensity & dyn_image) const -{ - if (!this->_in_correct_scale) - { +void +PatlakPlot::apply_linear_regression(ParametricVoxelsOnCartesianGrid& par_image, + const DynamicDiscretisedDensity& dyn_image) const { + if (!this->_in_correct_scale) { #ifndef NDEBUG - this->_model_matrix.write_to_file("patlak_matrix_not_in_correct_scale.txt"); -#endif //NDEBUG - const DiscretisedDensityOnCartesianGrid <3,float>* image_cartesian_ptr = - dynamic_cast< DiscretisedDensityOnCartesianGrid<3,float>* > (((dyn_image.get_densities())[0]).get()); - const BasicCoordinate<3,float> this_grid_spacing = image_cartesian_ptr->get_grid_spacing(); - if (dyn_image.get_scanner_default_bin_size()<=0) - error("PatlakPlot: The dynamic image currently needs to know the Scanner's default_bin_size. Did you set the 'originating system'?"); - this->_model_matrix.scale_model_matrix(this_grid_spacing[2]/dyn_image.get_scanner_default_bin_size()); + this->_model_matrix.write_to_file("patlak_matrix_not_in_correct_scale.txt"); +#endif // NDEBUG + const DiscretisedDensityOnCartesianGrid<3, float>* image_cartesian_ptr = + dynamic_cast*>(((dyn_image.get_densities())[0]).get()); + const BasicCoordinate<3, float> this_grid_spacing = image_cartesian_ptr->get_grid_spacing(); + if (dyn_image.get_scanner_default_bin_size() <= 0) + error("PatlakPlot: The dynamic image currently needs to know the Scanner's default_bin_size. Did you set the 'originating " + "system'?"); + this->_model_matrix.scale_model_matrix(this_grid_spacing[2] / dyn_image.get_scanner_default_bin_size()); #ifndef NDEBUG - this->_model_matrix.write_to_file("patlak_matrix_in_correct_scale.txt"); -#endif //NDEBUG - } + this->_model_matrix.write_to_file("patlak_matrix_in_correct_scale.txt"); +#endif // NDEBUG + } // const DynamicDiscretisedDensity & dyn_image=this->_dyn_image; // TODO check consistency of time-frame definitions - const unsigned int num_frames=(this->_frame_defs).get_num_frames(); + const unsigned int num_frames = (this->_frame_defs).get_num_frames(); unsigned int frame_num; - unsigned int starting_frame= this->_starting_frame; - Array<2,float> patlak_model_array=this->_model_matrix.get_model_array(); - VectorWithOffset patlak_x(starting_frame-1,num_frames-1); - VectorWithOffset patlak_y(starting_frame-1,num_frames-1); - VectorWithOffset weights(starting_frame-1,num_frames-1); + unsigned int starting_frame = this->_starting_frame; + Array<2, float> patlak_model_array = this->_model_matrix.get_model_array(); + VectorWithOffset patlak_x(starting_frame - 1, num_frames - 1); + VectorWithOffset patlak_y(starting_frame - 1, num_frames - 1); + VectorWithOffset weights(starting_frame - 1, num_frames - 1); // Patlak Linear regression is applied to the data in the format: // C(t)/Cp(t)=Ki*\int{Cp(t)}/Cp(t)+Vb // therefore our "x" value for the regression is \int{Cp(t)}/Cp(t) (which we know from the model) // - // NOTE: as we are working in time frames, and not discrete time points, Cp(t) is not a value of Cp at a given single time, t, but instead - // it is the integral of Cp on that time frame , \int_{t_start}^{t_end} Cp(t) dt, for each time frame. The same happens with \int{Cp(t)} - // All this is handled in the PlasmaData class, and it's not visible here. - for(unsigned int frame_num = starting_frame; - frame_num<=num_frames ; ++frame_num ) - { - patlak_x[frame_num-1]=patlak_model_array[1][frame_num]/patlak_model_array[2][frame_num]; - weights[frame_num-1]=1; - } - { // Do linear_regression for each voxel // for k j i - float slope=0.F; - float y_intersection=0.F; - float variance_of_slope=0.F; - float variance_of_y_intersection=0.F; - float covariance_of_y_intersection_with_slope=0.F; - float chi_square = 0.F; - - const int min_k_index = dyn_image[1].get_min_index(); + // NOTE: as we are working in time frames, and not discrete time points, Cp(t) is not a value of Cp at a given single time, t, + // but instead + // it is the integral of Cp on that time frame , \int_{t_start}^{t_end} Cp(t) dt, for each time frame. The same happens + // with \int{Cp(t)} All this is handled in the PlasmaData class, and it's not visible here. + for (unsigned int frame_num = starting_frame; frame_num <= num_frames; ++frame_num) { + patlak_x[frame_num - 1] = patlak_model_array[1][frame_num] / patlak_model_array[2][frame_num]; + weights[frame_num - 1] = 1; + } + { // Do linear_regression for each voxel // for k j i + float slope = 0.F; + float y_intersection = 0.F; + float variance_of_slope = 0.F; + float variance_of_y_intersection = 0.F; + float covariance_of_y_intersection_with_slope = 0.F; + float chi_square = 0.F; + + const int min_k_index = dyn_image[1].get_min_index(); const int max_k_index = dyn_image[1].get_max_index(); - for ( int k = min_k_index; k<= max_k_index; ++k) - { - const int min_j_index = dyn_image[1][k].get_min_index(); - const int max_j_index = dyn_image[1][k].get_max_index(); - for ( int j = min_j_index; j<= max_j_index; ++j) - { - const int min_i_index = dyn_image[1][k][j].get_min_index(); - const int max_i_index = dyn_image[1][k][j].get_max_index(); - for ( int i = min_i_index; i<= max_i_index; ++i) - { - // Patlak Linear regression is applied to the data in the format: - // C(t)/Cp(t)=Ki*\int{Cp(t)}/Cp(t)+Vb - // therefore our "y" value for the regression is C(t)/Cp(t). C(t) is the dynamic image value. - // (remember, these are integrals over the time frame, not single values at discrete t) - for ( frame_num = starting_frame; - frame_num<=num_frames ; ++frame_num ) - patlak_y[frame_num-1]=dyn_image[frame_num][k][j][i]/patlak_model_array[2][frame_num]; - // Apply the regression to this pixel - linear_regression(y_intersection, slope, - chi_square, - variance_of_y_intersection, - variance_of_slope, - covariance_of_y_intersection_with_slope, - patlak_y, - patlak_x, - weights); - par_image[k][j][i][2]=y_intersection; - par_image[k][j][i][1]=slope; - } - } - } + for (int k = min_k_index; k <= max_k_index; ++k) { + const int min_j_index = dyn_image[1][k].get_min_index(); + const int max_j_index = dyn_image[1][k].get_max_index(); + for (int j = min_j_index; j <= max_j_index; ++j) { + const int min_i_index = dyn_image[1][k][j].get_min_index(); + const int max_i_index = dyn_image[1][k][j].get_max_index(); + for (int i = min_i_index; i <= max_i_index; ++i) { + // Patlak Linear regression is applied to the data in the format: + // C(t)/Cp(t)=Ki*\int{Cp(t)}/Cp(t)+Vb + // therefore our "y" value for the regression is C(t)/Cp(t). C(t) is the dynamic image value. + // (remember, these are integrals over the time frame, not single values at discrete t) + for (frame_num = starting_frame; frame_num <= num_frames; ++frame_num) + patlak_y[frame_num - 1] = dyn_image[frame_num][k][j][i] / patlak_model_array[2][frame_num]; + // Apply the regression to this pixel + linear_regression(y_intersection, slope, chi_square, variance_of_y_intersection, variance_of_slope, + covariance_of_y_intersection_with_slope, patlak_y, patlak_x, weights); + par_image[k][j][i][2] = y_intersection; + par_image[k][j][i][1] = slope; + } + } + } } } void -PatlakPlot::multiply_dynamic_image_with_model_gradient(ParametricVoxelsOnCartesianGrid & par_image, - const DynamicDiscretisedDensity & dyn_image) const -{ - if (!this->_in_correct_scale) - { +PatlakPlot::multiply_dynamic_image_with_model_gradient(ParametricVoxelsOnCartesianGrid& par_image, + const DynamicDiscretisedDensity& dyn_image) const { + if (!this->_in_correct_scale) { #ifndef NDEBUG - this->_model_matrix.write_to_file("patlak_matrix_not_in_correct_scale.txt"); -#endif //NDEBUG - const DiscretisedDensityOnCartesianGrid <3,float>* image_cartesian_ptr = - dynamic_cast< DiscretisedDensityOnCartesianGrid<3,float>* > (((dyn_image.get_densities())[0]).get()); - const BasicCoordinate<3,float> this_grid_spacing = image_cartesian_ptr->get_grid_spacing(); - if (dyn_image.get_scanner_default_bin_size()<=0) - error("PatlakPlot: The dynamic image currently needs to know the Scanner's default_bin_size. Did you set the 'originating system'?"); - this->_model_matrix.scale_model_matrix(this_grid_spacing[2]/dyn_image.get_scanner_default_bin_size()); + this->_model_matrix.write_to_file("patlak_matrix_not_in_correct_scale.txt"); +#endif // NDEBUG + const DiscretisedDensityOnCartesianGrid<3, float>* image_cartesian_ptr = + dynamic_cast*>(((dyn_image.get_densities())[0]).get()); + const BasicCoordinate<3, float> this_grid_spacing = image_cartesian_ptr->get_grid_spacing(); + if (dyn_image.get_scanner_default_bin_size() <= 0) + error("PatlakPlot: The dynamic image currently needs to know the Scanner's default_bin_size. Did you set the 'originating " + "system'?"); + this->_model_matrix.scale_model_matrix(this_grid_spacing[2] / dyn_image.get_scanner_default_bin_size()); #ifndef NDEBUG - this->_model_matrix.write_to_file("patlak_matrix_in_correct_scale.txt"); -#endif //NDEBUG - } - this->_model_matrix.multiply_dynamic_image_with_model(par_image,dyn_image); + this->_model_matrix.write_to_file("patlak_matrix_in_correct_scale.txt"); +#endif // NDEBUG + } + this->_model_matrix.multiply_dynamic_image_with_model(par_image, dyn_image); } void -PatlakPlot::multiply_dynamic_image_with_model_gradient_and_add_to_input(ParametricVoxelsOnCartesianGrid & par_image, - const DynamicDiscretisedDensity & dyn_image) const -{ - if (!this->_in_correct_scale) - { +PatlakPlot::multiply_dynamic_image_with_model_gradient_and_add_to_input(ParametricVoxelsOnCartesianGrid& par_image, + const DynamicDiscretisedDensity& dyn_image) const { + if (!this->_in_correct_scale) { #ifndef NDEBUG - this->_model_matrix.write_to_file("patlak_matrix_not_in_correct_scale.txt"); -#endif //NDEBUG - const DiscretisedDensityOnCartesianGrid <3,float>* image_cartesian_ptr = - dynamic_cast< DiscretisedDensityOnCartesianGrid<3,float>* > (((dyn_image.get_densities())[0]).get()); - const BasicCoordinate<3,float> this_grid_spacing = image_cartesian_ptr->get_grid_spacing(); - if (dyn_image.get_scanner_default_bin_size()<=0) - error("PatlakPlot: The dynamic image currently needs to know the Scanner's default_bin_size. Did you set the 'originating system'?"); - this->_model_matrix.scale_model_matrix(this_grid_spacing[2]/dyn_image.get_scanner_default_bin_size()); + this->_model_matrix.write_to_file("patlak_matrix_not_in_correct_scale.txt"); +#endif // NDEBUG + const DiscretisedDensityOnCartesianGrid<3, float>* image_cartesian_ptr = + dynamic_cast*>(((dyn_image.get_densities())[0]).get()); + const BasicCoordinate<3, float> this_grid_spacing = image_cartesian_ptr->get_grid_spacing(); + if (dyn_image.get_scanner_default_bin_size() <= 0) + error("PatlakPlot: The dynamic image currently needs to know the Scanner's default_bin_size. Did you set the 'originating " + "system'?"); + this->_model_matrix.scale_model_matrix(this_grid_spacing[2] / dyn_image.get_scanner_default_bin_size()); #ifndef NDEBUG - this->_model_matrix.write_to_file("patlak_matrix_in_correct_scale.txt"); -#endif //NDEBUG - } - this->_model_matrix.multiply_dynamic_image_with_model_and_add_to_input(par_image,dyn_image); + this->_model_matrix.write_to_file("patlak_matrix_in_correct_scale.txt"); +#endif // NDEBUG + } + this->_model_matrix.multiply_dynamic_image_with_model_and_add_to_input(par_image, dyn_image); } // Should be a virtual function declared in the KineticModels or better to the LinearModels void -PatlakPlot::get_dynamic_image_from_parametric_image(DynamicDiscretisedDensity & dyn_image, - const ParametricVoxelsOnCartesianGrid & par_image) const -{ - if (!this->_in_correct_scale) - { +PatlakPlot::get_dynamic_image_from_parametric_image(DynamicDiscretisedDensity& dyn_image, + const ParametricVoxelsOnCartesianGrid& par_image) const { + if (!this->_in_correct_scale) { #ifndef NDEBUG - this->_model_matrix.write_to_file("patlak_matrix_not_in_correct_scale.txt"); -#endif //NDEBUG - const DiscretisedDensityOnCartesianGrid <3,float>* image_cartesian_ptr = - dynamic_cast< DiscretisedDensityOnCartesianGrid<3,float>* > (((dyn_image.get_densities())[0]).get()); - const BasicCoordinate<3,float> this_grid_spacing = image_cartesian_ptr->get_grid_spacing(); - if (dyn_image.get_scanner_default_bin_size()<=0) - error("PatlakPlot: The dynamic image currently needs to know the Scanner's default_bin_size. Did you set the 'originating system'?"); - this->_model_matrix.scale_model_matrix(this_grid_spacing[2]/dyn_image.get_scanner_default_bin_size()); + this->_model_matrix.write_to_file("patlak_matrix_not_in_correct_scale.txt"); +#endif // NDEBUG + const DiscretisedDensityOnCartesianGrid<3, float>* image_cartesian_ptr = + dynamic_cast*>(((dyn_image.get_densities())[0]).get()); + const BasicCoordinate<3, float> this_grid_spacing = image_cartesian_ptr->get_grid_spacing(); + if (dyn_image.get_scanner_default_bin_size() <= 0) + error("PatlakPlot: The dynamic image currently needs to know the Scanner's default_bin_size. Did you set the 'originating " + "system'?"); + this->_model_matrix.scale_model_matrix(this_grid_spacing[2] / dyn_image.get_scanner_default_bin_size()); #ifndef NDEBUG - this->_model_matrix.write_to_file("patlak_matrix_in_correct_scale.txt"); -#endif //NDEBUG - } + this->_model_matrix.write_to_file("patlak_matrix_in_correct_scale.txt"); +#endif // NDEBUG + } - this->_model_matrix.multiply_parametric_image_with_model(dyn_image,par_image); + this->_model_matrix.multiply_parametric_image_with_model(dyn_image, par_image); } unsigned int -PatlakPlot::get_starting_frame() const -{ +PatlakPlot::get_starting_frame() const { return this->_starting_frame; } -TimeFrameDefinitions -PatlakPlot::get_time_frame_definitions() const -{ +TimeFrameDefinitions +PatlakPlot::get_time_frame_definitions() const { return this->_frame_defs; } void -PatlakPlot:: -initialise_keymap() -{ +PatlakPlot::initialise_keymap() { base_type::initialise_keymap(); this->parser.add_start_key("Patlak Plot Parameters"); this->parser.add_key("Blood Data Filename", &this->_blood_data_filename); @@ -335,44 +311,38 @@ initialise_keymap() this->parser.add_key("Time Shift", &this->_time_shift); this->parser.add_key("In total counts", &this->_in_total_cnt); this->parser.add_key("In correct scale", &this->_in_correct_scale); - this->parser.add_key("Time Frame Definition Filename", &this->_time_frame_definition_filename); + this->parser.add_key("Time Frame Definition Filename", &this->_time_frame_definition_filename); this->parser.add_stop_key("end Patlak Plot Parameters"); } /*! \todo This currently hard-wired F-18 decay for the plasma data */ bool -PatlakPlot:: -post_processing() -{ +PatlakPlot::post_processing() { if (base_type::post_processing() == true) return true; - // read time frame def - if (this->_time_frame_definition_filename.size()!=0) - this->_frame_defs=TimeFrameDefinitions(this->_time_frame_definition_filename); - else - { - error("No Time Frames Definitions available!!!\n "); - return true; - } + // read time frame def + if (this->_time_frame_definition_filename.size() != 0) + this->_frame_defs = TimeFrameDefinitions(this->_time_frame_definition_filename); + else { + error("No Time Frames Definitions available!!!\n "); + return true; + } // Reading the input function - if(this->_blood_data_filename=="0") - { - warning("You need to specify a file for the input function."); - return true; - } - else - { - this->_if_cardiac=false; - PlasmaData plasma_data_temp; - plasma_data_temp.read_plasma_data(this->_blood_data_filename); // The implementation assumes three list file. - // TODO have parameter - warning("Assuming F-18 tracer for half-life!!!"); - plasma_data_temp.set_isotope_halflife(6586.2F); - plasma_data_temp.shift_time(this->_time_shift); - this->_plasma_frame_data=plasma_data_temp.get_sample_data_in_frames(this->_frame_defs); - } -return false; + if (this->_blood_data_filename == "0") { + warning("You need to specify a file for the input function."); + return true; + } else { + this->_if_cardiac = false; + PlasmaData plasma_data_temp; + plasma_data_temp.read_plasma_data(this->_blood_data_filename); // The implementation assumes three list file. + // TODO have parameter + warning("Assuming F-18 tracer for half-life!!!"); + plasma_data_temp.set_isotope_halflife(6586.2F); + plasma_data_temp.shift_time(this->_time_shift); + this->_plasma_frame_data = plasma_data_temp.get_sample_data_in_frames(this->_frame_defs); + } + return false; } //! Create model matrix from blood frame data diff --git a/src/modelling_buildblock/modelling_registries.cxx b/src/modelling_buildblock/modelling_registries.cxx index 26b4e4044b..1e44c64ba2 100644 --- a/src/modelling_buildblock/modelling_registries.cxx +++ b/src/modelling_buildblock/modelling_registries.cxx @@ -27,7 +27,7 @@ \brief File that registers all stir::RegisterObject children in modelling \author Charalampos Tsoumpas - + */ #include "stir/modelling/PatlakPlot.h" @@ -36,4 +36,3 @@ START_NAMESPACE_STIR static PatlakPlot::RegisterIt dummy113; END_NAMESPACE_STIR - diff --git a/src/modelling_utilities/apply_patlak_to_images.cxx b/src/modelling_utilities/apply_patlak_to_images.cxx index 8fbdeb9842..89e36931d9 100644 --- a/src/modelling_utilities/apply_patlak_to_images.cxx +++ b/src/modelling_utilities/apply_patlak_to_images.cxx @@ -24,20 +24,21 @@ \par Usage: - \code - apply_patlak_to_images output_parametric_image input_dynamic_image [par_file] + \code + apply_patlak_to_images output_parametric_image input_dynamic_image [par_file] \endcode - + \par - - The dynamic images will be calibrated only if the calibration factor is given. - - The \a if_total_cnt is set to true the Dynamic Image will have the total number of + - The dynamic images will be calibrated only if the calibration factor is given. + - The \a if_total_cnt is set to true the Dynamic Image will have the total number of counts while if set to false it will have the \a total_number_of_counts/get_duration(frame_num). - The dynamic images will always be in decaying counts. - The plasma data is assumed to be in decaying counts. - + \sa PatlakPlot.h for the \a par_file - \note This implementation does not use wighted least squares because for Patlak Plot only the last frames are used, which they usually have the same duration and similar number of counts. + \note This implementation does not use wighted least squares because for Patlak Plot only the last frames are used, which they + usually have the same duration and similar number of counts. \todo Reimplement the method for image-based input function. @@ -56,51 +57,48 @@ #include #include -int main(int argc, char *argv[]) -{ -USING_NAMESPACE_STIR +int +main(int argc, char* argv[]) { + USING_NAMESPACE_STIR PatlakPlot indirect_patlak; - if (argc==4) - { - if (indirect_patlak.parse(argv[3]) == false) - return EXIT_FAILURE; - } - if (argc!=3 && argc!=4) - { - std::cerr << "Usage:" << argv[0] << " output_parametric_image input_dynamic_image [par_file] \n"; + if (argc == 4) { + if (indirect_patlak.parse(argv[3]) == false) return EXIT_FAILURE; - } - if (argc==3) + } + if (argc != 3 && argc != 4) { + std::cerr << "Usage:" << argv[0] << " output_parametric_image input_dynamic_image [par_file] \n"; + return EXIT_FAILURE; + } + if (argc == 3) indirect_patlak.ask_parameters(); CPUTimer timer; timer.start(); - if (indirect_patlak.set_up()==Succeeded::no) - return EXIT_FAILURE ; - else - { - // Create dynamic images object from input file - shared_ptr dyn_image_sptr(read_from_file(argv[2])); - const DynamicDiscretisedDensity & dyn_image= *dyn_image_sptr; - // Create parametric images from input file - shared_ptr par_image_sptr; - par_image_sptr = MAKE_SHARED(dyn_image); - - //ToDo: Assertion for the dyn-par images, sizes I have to create from one to the other image, so then it should be OK... - assert(indirect_patlak.get_time_frame_definitions().get_num_frames()==dyn_image.get_time_frame_definitions().get_num_frames()); - indirect_patlak.apply_linear_regression(*par_image_sptr,dyn_image); - - // Writing image - std::cerr << "Writing parametric-image in '"<< argv[1] << "'\n"; - const Succeeded writing_succeeded=OutputFileFormat::default_sptr()-> - write_to_file(argv[1], *par_image_sptr); - std::cerr << "Total time for Image-Based Patlak in sec: " << timer.value() <<"\n"; - timer.stop(); - - if(writing_succeeded==Succeeded::yes) - return EXIT_SUCCESS ; - else - return EXIT_FAILURE ; - } -} + if (indirect_patlak.set_up() == Succeeded::no) + return EXIT_FAILURE; + else { + // Create dynamic images object from input file + shared_ptr dyn_image_sptr(read_from_file(argv[2])); + const DynamicDiscretisedDensity& dyn_image = *dyn_image_sptr; + // Create parametric images from input file + shared_ptr par_image_sptr; + par_image_sptr = MAKE_SHARED(dyn_image); + + // ToDo: Assertion for the dyn-par images, sizes I have to create from one to the other image, so then it should be OK... + assert(indirect_patlak.get_time_frame_definitions().get_num_frames() == + dyn_image.get_time_frame_definitions().get_num_frames()); + indirect_patlak.apply_linear_regression(*par_image_sptr, dyn_image); + // Writing image + std::cerr << "Writing parametric-image in '" << argv[1] << "'\n"; + const Succeeded writing_succeeded = + OutputFileFormat::default_sptr()->write_to_file(argv[1], *par_image_sptr); + std::cerr << "Total time for Image-Based Patlak in sec: " << timer.value() << "\n"; + timer.stop(); + + if (writing_succeeded == Succeeded::yes) + return EXIT_SUCCESS; + else + return EXIT_FAILURE; + } +} diff --git a/src/modelling_utilities/extract_single_images_from_parametric_image.cxx b/src/modelling_utilities/extract_single_images_from_parametric_image.cxx index 208b474135..c30f82f7e9 100644 --- a/src/modelling_utilities/extract_single_images_from_parametric_image.cxx +++ b/src/modelling_utilities/extract_single_images_from_parametric_image.cxx @@ -25,7 +25,7 @@ \author Kris Thielemans \par Usage: - \code + \code extract_single_images_from_parametric_image output_filename_pattern input_header_filename output_format_parameter_file The output filename should look something like this: param_im_%d_output.file_extension, @@ -52,79 +52,79 @@ #include "stir/IO/OutputFileFormat.h" #include "stir/Succeeded.h" -int main(int argc, char *argv[]) -{ - USING_NAMESPACE_STIR +int +main(int argc, char* argv[]) { + USING_NAMESPACE_STIR + + if (argc != 3 && argc != 4) { + std::cerr << "\nUsage: extract_single_images_from_parametric_image output_filename_pattern input_header_filename " + "[output_format_parameter_file]\n\n"; + return EXIT_FAILURE; + } + + try { + + // Read images + auto param_im_sptr(read_from_file(argv[2])); + + // Check + if (is_null_ptr(param_im_sptr)) + throw std::runtime_error("Failed to read dynamic image (" + std::string(argv[2]) + ")."); + + // Set up the output type + shared_ptr>> output_file_format_sptr; + if (argc == 3) + output_file_format_sptr = OutputFileFormat>::default_sptr(); + else { + KeyParser parser; + parser.add_start_key("OutputFileFormat Parameters"); + parser.add_parsing_key("output file format type", &output_file_format_sptr); + parser.add_stop_key("END"); + std::ifstream in(argv[3]); + if (!parser.parse(in) || is_null_ptr(output_file_format_sptr)) + throw std::runtime_error("Failed to parse output format file (" + std::string(argv[3]) + ")."); + } - if (argc != 3 && argc != 4) { - std::cerr << "\nUsage: extract_single_images_from_parametric_image output_filename_pattern input_header_filename [output_format_parameter_file]\n\n"; + // Loop over each image + for (unsigned i = 1; i <= param_im_sptr->get_num_params(); ++i) { + + auto disc = param_im_sptr->construct_single_density(i); + { + // Get the time frame definition (from start of first frame to end of last) + ExamInfo exam_info = disc.get_exam_info(); + TimeFrameDefinitions tdefs = exam_info.get_time_frame_definitions(); + const double start = tdefs.get_start_time(1); + const double end = tdefs.get_end_time(tdefs.get_num_frames()); + tdefs.set_num_time_frames(1); + tdefs.set_time_frame(1, start, end); + exam_info.set_time_frame_definitions(tdefs); + disc.set_exam_info(exam_info); + } + + std::string current_filename; + try { + current_filename = boost::str(boost::format(argv[1]) % i); + } catch (std::exception& e) { + error(boost::format("Error using 'output_filename' pattern (which is set to '%1%'). " + "Check syntax for boost::format. Error is:\n%2%") % + argv[1] % e.what()); return EXIT_FAILURE; + } + + // Write to file + const Succeeded success = output_file_format_sptr->write_to_file(current_filename, disc); + if (success == Succeeded::no) + throw std::runtime_error("Failed writing."); } - try { - - // Read images - auto param_im_sptr(read_from_file(argv[2])); - - // Check - if (is_null_ptr(param_im_sptr)) - throw std::runtime_error("Failed to read dynamic image (" + std::string(argv[2]) + ")."); - - // Set up the output type - shared_ptr > > output_file_format_sptr; - if (argc == 3) - output_file_format_sptr = OutputFileFormat >::default_sptr(); - else { - KeyParser parser; - parser.add_start_key("OutputFileFormat Parameters"); - parser.add_parsing_key("output file format type", &output_file_format_sptr); - parser.add_stop_key("END"); - std::ifstream in(argv[3]); - if (!parser.parse(in) || is_null_ptr(output_file_format_sptr)) - throw std::runtime_error("Failed to parse output format file (" + std::string(argv[3]) + ")."); - } - - // Loop over each image - for (unsigned i=1; i<=param_im_sptr->get_num_params(); ++i) { - - auto disc = param_im_sptr->construct_single_density(i); - { - // Get the time frame definition (from start of first frame to end of last) - ExamInfo exam_info = disc.get_exam_info(); - TimeFrameDefinitions tdefs = exam_info.get_time_frame_definitions(); - const double start = tdefs.get_start_time(1); - const double end = tdefs.get_end_time(tdefs.get_num_frames()); - tdefs.set_num_time_frames(1); - tdefs.set_time_frame(1,start,end); - exam_info.set_time_frame_definitions(tdefs); - disc.set_exam_info(exam_info); - } - - std::string current_filename; - try { - current_filename = boost::str(boost::format(argv[1]) % i); - } catch (std::exception& e) { - error(boost::format("Error using 'output_filename' pattern (which is set to '%1%'). " - "Check syntax for boost::format. Error is:\n%2%") % argv[1] % e.what()); - return EXIT_FAILURE; - } - - // Write to file - const Succeeded success = output_file_format_sptr->write_to_file(current_filename,disc); - if (success == Succeeded::no) - throw std::runtime_error("Failed writing."); - } - - // If all is good, exit - return EXIT_SUCCESS; + // If all is good, exit + return EXIT_SUCCESS; // If there was an error - } catch(const std::exception &error) { - std::cerr << "\nHere's the error:\n\t" << error.what() << "\n\n"; - return EXIT_FAILURE; - } catch(...) { - return EXIT_FAILURE; - } + } catch (const std::exception& error) { + std::cerr << "\nHere's the error:\n\t" << error.what() << "\n\n"; + return EXIT_FAILURE; + } catch (...) { + return EXIT_FAILURE; + } } - - diff --git a/src/modelling_utilities/get_dynamic_images_from_parametric_images.cxx b/src/modelling_utilities/get_dynamic_images_from_parametric_images.cxx index baed0afdd8..5a311b92c2 100644 --- a/src/modelling_utilities/get_dynamic_images_from_parametric_images.cxx +++ b/src/modelling_utilities/get_dynamic_images_from_parametric_images.cxx @@ -22,18 +22,18 @@ \file \ingroup utilities \brief Multiplies Parametric Images with the Model Matrix creating Dynamic Images - \author Charalampos Tsoumpas + \author Charalampos Tsoumpas \author Richard Brown \par Usage: - \code + \code get_dynamic_images_from_parametric_images output_parametric_image input_dynamic_image [par_file [output_format_par_file]] \endcode - + \par - - The dynamic images will be calibrated only if the calibration factor is given. + - The dynamic images will be calibrated only if the calibration factor is given. - The dynamic images will be in decaying counts if the plasma data are in decaying counts. - + An optional output file format parameter file can also be given. An example for this might be: output file format parameters := output file format type := Interfile @@ -42,7 +42,7 @@ number_of_bytes_per_pixel:=4 End Interfile Output File Format Parameters:= end := - + \sa PatlakPlot.h for the \a par_file \todo Add to the Doxygen documentation how exactly this utility works. @@ -63,110 +63,98 @@ USING_NAMESPACE_STIR -shared_ptr > set_up_output_format(int argc, char *argv[]) -{ - shared_ptr > output = - OutputFileFormat::default_sptr(); +shared_ptr> +set_up_output_format(int argc, char* argv[]) { + shared_ptr> output = OutputFileFormat::default_sptr(); - if (argc == 5) { + if (argc == 5) { - KeyParser parser; - parser.add_start_key("output file format parameters"); - parser.add_parsing_key("output file format type", &output); - parser.add_stop_key("END"); + KeyParser parser; + parser.add_start_key("output file format parameters"); + parser.add_parsing_key("output file format type", &output); + parser.add_stop_key("END"); - if (parser.parse(argv[4]) == false || is_null_ptr(output)) { - warning("Error parsing output file format. Using default format."); - output = OutputFileFormat::default_sptr(); - } + if (parser.parse(argv[4]) == false || is_null_ptr(output)) { + warning("Error parsing output file format. Using default format."); + output = OutputFileFormat::default_sptr(); } - return output; + } + return output; } -int main(int argc, char *argv[]) -{ -// Impelemented only for the linear Patlak Plot so far. -// In the future I should implement the KineticModels with the "linear" specification -// for patlak, logan etc... PatlakPlot patlak_plot; +int +main(int argc, char* argv[]) { + // Impelemented only for the linear Patlak Plot so far. + // In the future I should implement the KineticModels with the "linear" specification + // for patlak, logan etc... PatlakPlot patlak_plot; PatlakPlot patlak_plot; - if (argc>=4) - { - if (patlak_plot.parse(argv[3]) == false) - return EXIT_FAILURE; - } - if (argc!=3 && argc!=4 && argc!=5) - { - std::cerr << "Usage:" << argv[0] << " output_dynamic_image input_parametric_image [par_file [output_format_par_file]]\n"; + if (argc >= 4) { + if (patlak_plot.parse(argv[3]) == false) return EXIT_FAILURE; - } - if (argc==3) + } + if (argc != 3 && argc != 4 && argc != 5) { + std::cerr << "Usage:" << argv[0] << " output_dynamic_image input_parametric_image [par_file [output_format_par_file]]\n"; + return EXIT_FAILURE; + } + if (argc == 3) patlak_plot.ask_parameters(); - if (patlak_plot.set_up()==Succeeded::no) - return EXIT_FAILURE ; - else - { - shared_ptr - par_image_sptr(ParametricVoxelsOnCartesianGrid::read_from_file(argv[2])); - - DynamicDiscretisedDensity dyn_image; - - // If the output file already exists, use it as the template - std::ifstream file(argv[1]); - if (file) - { - std::cerr << "\nOutput image already exists, so using that as the template.\n"; + if (patlak_plot.set_up() == Succeeded::no) + return EXIT_FAILURE; + else { + shared_ptr par_image_sptr(ParametricVoxelsOnCartesianGrid::read_from_file(argv[2])); + + DynamicDiscretisedDensity dyn_image; + + // If the output file already exists, use it as the template + std::ifstream file(argv[1]); + if (file) { + std::cerr << "\nOutput image already exists, so using that as the template.\n"; #if 1 - shared_ptr - dyn_image_sptr(read_from_file(argv[1])); - dyn_image= *dyn_image_sptr; + shared_ptr dyn_image_sptr(read_from_file(argv[1])); + dyn_image = *dyn_image_sptr; #else // At the moment it is impossible to have the scanner information without extra prior information. - const shared_ptr > density_template_sptr((par_image_sptr->construct_single_density(1)).clone()); - DynamicDiscretisedDensity dyn_image=DynamicDiscretisedDensity(patlak_plot.get_time_frame_definitions(), scanner_sptr, density_template_sptr); + const shared_ptr> density_template_sptr((par_image_sptr->construct_single_density(1)).clone()); + DynamicDiscretisedDensity dyn_image = + DynamicDiscretisedDensity(patlak_plot.get_time_frame_definitions(), scanner_sptr, density_template_sptr); #endif - //ToDo: Assertion for the dyn-par images, sizes I have to create from one to the other image, so then it should be OK... - assert(patlak_plot.get_time_frame_definitions().get_num_frames()==dyn_image.get_time_frame_definitions().get_num_frames()); + // ToDo: Assertion for the dyn-par images, sizes I have to create from one to the other image, so then it should be OK... + assert(patlak_plot.get_time_frame_definitions().get_num_frames() == + dyn_image.get_time_frame_definitions().get_num_frames()); #ifndef NDEBUG - const DiscretisedDensityOnCartesianGrid <3,float>* cartesian_ptr = - dynamic_cast< DiscretisedDensityOnCartesianGrid<3,float>*> (&dyn_image[1]); - assert(par_image_sptr->get_voxel_size()==cartesian_ptr->get_grid_spacing()); + const DiscretisedDensityOnCartesianGrid<3, float>* cartesian_ptr = + dynamic_cast*>(&dyn_image[1]); + assert(par_image_sptr->get_voxel_size() == cartesian_ptr->get_grid_spacing()); #endif - } + } - // If the output file doesn't exist, get all the info we need - else - { - std::cerr << "\nOutput image does not exist, so getting relevant info from parametric image and Patlak plot.\n"; + // If the output file doesn't exist, get all the info we need + else { + std::cerr << "\nOutput image does not exist, so getting relevant info from parametric image and Patlak plot.\n"; - const ExamInfo exam_info = par_image_sptr->get_exam_info(); - const TimeFrameDefinitions tdefs = patlak_plot.get_time_frame_definitions(); - const double time_since_1970 = exam_info.start_time_in_secs_since_1970; - shared_ptr scanner_sptr(Scanner::get_scanner_from_name(par_image_sptr->get_exam_info().originating_system)); - shared_ptr > voxels_sptr(par_image_sptr->construct_single_density(1).clone()); + const ExamInfo exam_info = par_image_sptr->get_exam_info(); + const TimeFrameDefinitions tdefs = patlak_plot.get_time_frame_definitions(); + const double time_since_1970 = exam_info.start_time_in_secs_since_1970; + shared_ptr scanner_sptr(Scanner::get_scanner_from_name(par_image_sptr->get_exam_info().originating_system)); + shared_ptr> voxels_sptr(par_image_sptr->construct_single_density(1).clone()); - // Construct the dynamic image - dyn_image = DynamicDiscretisedDensity(tdefs, - time_since_1970, - scanner_sptr, - voxels_sptr); - } + // Construct the dynamic image + dyn_image = DynamicDiscretisedDensity(tdefs, time_since_1970, scanner_sptr, voxels_sptr); + } - patlak_plot.get_dynamic_image_from_parametric_image(dyn_image,*par_image_sptr); + patlak_plot.get_dynamic_image_from_parametric_image(dyn_image, *par_image_sptr); - // Writing image - std::cerr << "Writing dynamic-image in '"<< argv[1] << "'\n"; + // Writing image + std::cerr << "Writing dynamic-image in '" << argv[1] << "'\n"; - shared_ptr > output_file_format = - set_up_output_format(argc, argv); + shared_ptr> output_file_format = set_up_output_format(argc, argv); - Succeeded writing_succeeded = output_file_format->write_to_file(argv[1], dyn_image); + Succeeded writing_succeeded = output_file_format->write_to_file(argv[1], dyn_image); - if(writing_succeeded==Succeeded::yes) - return EXIT_SUCCESS ; - else - return EXIT_FAILURE ; - } + if (writing_succeeded == Succeeded::yes) + return EXIT_SUCCESS; + else + return EXIT_FAILURE; + } } - - diff --git a/src/modelling_utilities/make_parametric_image_from_components.cxx b/src/modelling_utilities/make_parametric_image_from_components.cxx index 32415411b0..1e8602e934 100644 --- a/src/modelling_utilities/make_parametric_image_from_components.cxx +++ b/src/modelling_utilities/make_parametric_image_from_components.cxx @@ -24,7 +24,7 @@ \author Richard Brown \par Usage: - \code + \code make_parametric_image_from_components output_parametric_image slope intercept \endcode @@ -42,73 +42,72 @@ #include "stir/ProjDataInfo.h" -int main(int argc, char *argv[]) -{ - USING_NAMESPACE_STIR +int +main(int argc, char* argv[]) { + USING_NAMESPACE_STIR - if (argc != 4) { - std::cerr << "\nUsage: make_parametric_image_from_components output_parametric_image param1 param2 param3...\n\n"; - std::cerr << "\tCurrently only implemented for 2 kinetic parameters. E.g., for Patlak, slope followed by intercept.\n"; - return EXIT_FAILURE; - } - - try { - - std::vector > params; + if (argc != 4) { + std::cerr << "\nUsage: make_parametric_image_from_components output_parametric_image param1 param2 param3...\n\n"; + std::cerr << "\tCurrently only implemented for 2 kinetic parameters. E.g., for Patlak, slope followed by intercept.\n"; + return EXIT_FAILURE; + } - // Loop over all parameters - for (int i=2; i<=argc; ++i) { + try { - // Read - shared_ptr > im(read_from_file >(argv[i])); - // Check - if (is_null_ptr(im)) throw std::runtime_error("Failed to read file: " + std::string(argv[i]) + "."); + std::vector> params; - // Convert to VoxelsOnCartesianGrid - if (is_null_ptr(dynamic_cast*>(im.get()))) - throw std::runtime_error("Failed to convert parameter to VoxelsOnCartesianGrid."); + // Loop over all parameters + for (int i = 2; i <= argc; ++i) { - VoxelsOnCartesianGrid *param = dynamic_cast*>(im.get()); + // Read + shared_ptr> im(read_from_file>(argv[i])); + // Check + if (is_null_ptr(im)) + throw std::runtime_error("Failed to read file: " + std::string(argv[i]) + "."); - params.push_back(*param); + // Convert to VoxelsOnCartesianGrid + if (is_null_ptr(dynamic_cast*>(im.get()))) + throw std::runtime_error("Failed to convert parameter to VoxelsOnCartesianGrid."); - // Check characteristics match (compare new with first) - std::string explanation; - if (!param->has_same_characteristics(params.at(0),explanation)) - throw std::runtime_error("Kinetic images do not have same characteristics (" + std::string(explanation) + ")."); - } + VoxelsOnCartesianGrid* param = dynamic_cast*>(im.get()); - // At the moment, only implemented for 2 parameters - if (params.size() == 2) { - // Construct the parametric image - ParametricVoxelsOnCartesianGridBaseType base_type(params[0].get_index_range(),params[0].get_origin(),params[0].get_grid_spacing()); - ParametricVoxelsOnCartesianGrid param_im(base_type); + params.push_back(*param); - // Set data - param_im.update_parametric_image(params[0],1); - param_im.update_parametric_image(params[1],2); - - // Write it to file - const Succeeded success = OutputFileFormat::default_sptr()->write_to_file(argv[1], param_im); - if (success == Succeeded::no) - throw std::runtime_error("Failed writing."); - } - else { - std::cerr << "\ncurrently only implemented for 2 kinetic parameters. Exiting...\n"; - return EXIT_FAILURE; - } + // Check characteristics match (compare new with first) + std::string explanation; + if (!param->has_same_characteristics(params.at(0), explanation)) + throw std::runtime_error("Kinetic images do not have same characteristics (" + std::string(explanation) + ")."); + } + // At the moment, only implemented for 2 parameters + if (params.size() == 2) { + // Construct the parametric image + ParametricVoxelsOnCartesianGridBaseType base_type(params[0].get_index_range(), params[0].get_origin(), + params[0].get_grid_spacing()); + ParametricVoxelsOnCartesianGrid param_im(base_type); + + // Set data + param_im.update_parametric_image(params[0], 1); + param_im.update_parametric_image(params[1], 2); + + // Write it to file + const Succeeded success = + OutputFileFormat::default_sptr()->write_to_file(argv[1], param_im); + if (success == Succeeded::no) + throw std::runtime_error("Failed writing."); + } else { + std::cerr << "\ncurrently only implemented for 2 kinetic parameters. Exiting...\n"; + return EXIT_FAILURE; + } - // If all is good, exit - return EXIT_SUCCESS; + // If all is good, exit + return EXIT_SUCCESS; // If there was an error - } catch(const std::exception &error) { - std::cerr << "\nHere's the error:\n\t" << error.what() << "\n\n"; - return EXIT_FAILURE; - } catch(...) { - return EXIT_FAILURE; - } + } catch (const std::exception& error) { + std::cerr << "\nHere's the error:\n\t" << error.what() << "\n\n"; + return EXIT_FAILURE; + } catch (...) { + return EXIT_FAILURE; + } } - - diff --git a/src/modelling_utilities/mult_image_parameters.cxx b/src/modelling_utilities/mult_image_parameters.cxx index 5465690693..472570e537 100644 --- a/src/modelling_utilities/mult_image_parameters.cxx +++ b/src/modelling_utilities/mult_image_parameters.cxx @@ -25,14 +25,14 @@ \par Usage: \code - mult_image_parameters -o output_filename -i input_filename + mult_image_parameters -o output_filename -i input_filename \endcode \attention Assumes that only two parameters exist. \todo Make a generic utility which will multiply all the parameters together and store them in a multiple image file. \todo It might be possible to integrate it into the stir_math.cxx, in the future. - \note It is useful to estimate the covariance between the two parameters of the parametric images. + \note It is useful to estimate the covariance between the two parameters of the parametric images. */ #include "stir/Succeeded.h" @@ -43,74 +43,65 @@ #include "stir/getopt.h" #include -int main(int argc, char * argv[]) -{ +int +main(int argc, char* argv[]) { USING_NAMESPACE_STIR; - const char * output_filename = 0; - const char * input_filename = 0; + const char* output_filename = 0; + const char* input_filename = 0; - const char * const usage = "mult_image_parameters -o output_filename -i input_filename\n"; + const char* const usage = "mult_image_parameters -o output_filename -i input_filename\n"; opterr = 0; { char c; - while ((c = getopt (argc, argv, "i:o:p")) != -1) - switch (c) - { + while ((c = getopt(argc, argv, "i:o:p")) != -1) + switch (c) { case 'i': - input_filename = optarg; - break; + input_filename = optarg; + break; case 'o': - output_filename = optarg; - break; + output_filename = optarg; + break; case '?': - std::cerr << usage; - return EXIT_FAILURE; + std::cerr << usage; + return EXIT_FAILURE; default: - if (isprint (optopt)) - fprintf (stderr, "Unknown option `-%c'.\n", optopt); - else - fprintf (stderr, - "Unknown option character `\\x%x'.\n", - optopt); - std::cerr << usage; - return EXIT_FAILURE; + if (isprint(optopt)) + fprintf(stderr, "Unknown option `-%c'.\n", optopt); + else + fprintf(stderr, "Unknown option character `\\x%x'.\n", optopt); + std::cerr << usage; + return EXIT_FAILURE; } } - if (output_filename==0 || input_filename==0) - { - std::cerr << usage; - return EXIT_FAILURE; - } + if (output_filename == 0 || input_filename == 0) { + std::cerr << usage; + return EXIT_FAILURE; + } - const shared_ptr - input_image_sptr(ParametricVoxelsOnCartesianGrid::read_from_file(input_filename)); - const ParametricVoxelsOnCartesianGrid & input_image = *input_image_sptr; + const shared_ptr input_image_sptr( + ParametricVoxelsOnCartesianGrid::read_from_file(input_filename)); + const ParametricVoxelsOnCartesianGrid& input_image = *input_image_sptr; - shared_ptr > - output_image_sptr((input_image_sptr->construct_single_density(1)).clone()); - DiscretisedDensity<3,float>& output_image = *output_image_sptr; + shared_ptr> output_image_sptr((input_image_sptr->construct_single_density(1)).clone()); + DiscretisedDensity<3, float>& output_image = *output_image_sptr; - const int min_k_index = output_image.get_min_index(); + const int min_k_index = output_image.get_min_index(); const int max_k_index = output_image.get_max_index(); - for ( int k = min_k_index; k<= max_k_index; ++k) - { - const int min_j_index = output_image[k].get_min_index(); - const int max_j_index = output_image[k].get_max_index(); - for ( int j = min_j_index; j<= max_j_index; ++j) - { - const int min_i_index = output_image[k][j].get_min_index(); - const int max_i_index = output_image[k][j].get_max_index(); - for ( int i = min_i_index; i<= max_i_index; ++i) - output_image[k][j][i]=input_image[k][j][i][1]*input_image[k][j][i][2]; - } + for (int k = min_k_index; k <= max_k_index; ++k) { + const int min_j_index = output_image[k].get_min_index(); + const int max_j_index = output_image[k].get_max_index(); + for (int j = min_j_index; j <= max_j_index; ++j) { + const int min_i_index = output_image[k][j].get_min_index(); + const int max_i_index = output_image[k][j].get_max_index(); + for (int i = min_i_index; i <= max_i_index; ++i) + output_image[k][j][i] = input_image[k][j][i][1] * input_image[k][j][i][2]; } + } Succeeded success = - OutputFileFormat >::default_sptr()-> - write_to_file(output_filename, *output_image_sptr); - - return success==Succeeded::yes ? EXIT_SUCCESS : EXIT_FAILURE; -} + OutputFileFormat>::default_sptr()->write_to_file(output_filename, *output_image_sptr); + return success == Succeeded::yes ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/src/modelling_utilities/mult_model_with_dyn_images.cxx b/src/modelling_utilities/mult_model_with_dyn_images.cxx index 36b77f7642..e522ae3896 100644 --- a/src/modelling_utilities/mult_model_with_dyn_images.cxx +++ b/src/modelling_utilities/mult_model_with_dyn_images.cxx @@ -24,14 +24,14 @@ \author Charalampos Tsoumpas \par Usage: - \code - mult_model_with_dyn_images output_parametric_image input_dynamic_image [par_file] + \code + mult_model_with_dyn_images output_parametric_image input_dynamic_image [par_file] \endcode - + \par - - The dynamic images will be calibrated only if the calibration factor is given. + - The dynamic images will be calibrated only if the calibration factor is given. - The dynamic images and the plasma data must be both either in decaying counts or in decay-corrected counts. - + \sa PatlakPlot.h for the \a par_file \todo Add to the Doxygen documentation how exactly this utility works. @@ -48,52 +48,47 @@ #include #include -int main(int argc, char *argv[]) -{ +int +main(int argc, char* argv[]) { USING_NAMESPACE_STIR -// Impelemented only for the linear Patlak Plot so far. -// In the future I should implement the KineticModels with the "linear" specification -// for patlak, logan etc... + // Impelemented only for the linear Patlak Plot so far. + // In the future I should implement the KineticModels with the "linear" specification + // for patlak, logan etc... PatlakPlot patlak_plot; - if (argc==4) - { - if (patlak_plot.parse(argv[3]) == false) - { - std::cerr << "Usage:" << argv[0] << " output_parametric_image input_dynamic_image [par_file] \n"; - return EXIT_FAILURE; - } + if (argc == 4) { + if (patlak_plot.parse(argv[3]) == false) { + std::cerr << "Usage:" << argv[0] << " output_parametric_image input_dynamic_image [par_file] \n"; + return EXIT_FAILURE; } - if (argc!=3 && argc!=4) + } + if (argc != 3 && argc != 4) return EXIT_FAILURE; - if (argc==3) + if (argc == 3) patlak_plot.ask_parameters(); - if (patlak_plot.set_up()==Succeeded::no) - return EXIT_FAILURE ; - else - { - shared_ptr - par_image_sptr(ParametricVoxelsOnCartesianGrid::read_from_file(argv[1])); - ParametricVoxelsOnCartesianGrid par_image = *par_image_sptr; - - shared_ptr - dyn_image_sptr(read_from_file(argv[2])); - const DynamicDiscretisedDensity & dyn_image= *dyn_image_sptr; - - //NotToDo: Assertion for the dyn-par images, sizes should not be ncessary ONLY WHEN I will create from dyn_image the par_image... - assert(patlak_plot.get_time_frame_definitions().get_num_frames()==dyn_image.get_time_frame_definitions().get_num_frames()); - patlak_plot.multiply_dynamic_image_with_model_gradient(par_image,dyn_image); - - // Writing image - std::cerr << "Writing parametric-image in '"<< argv[1] << "'\n"; - const Succeeded writing_succeeded=OutputFileFormat::default_sptr()-> - write_to_file(argv[1], par_image); - - if(writing_succeeded==Succeeded::yes) - return EXIT_SUCCESS ; - else - return EXIT_FAILURE ; - } + if (patlak_plot.set_up() == Succeeded::no) + return EXIT_FAILURE; + else { + shared_ptr par_image_sptr(ParametricVoxelsOnCartesianGrid::read_from_file(argv[1])); + ParametricVoxelsOnCartesianGrid par_image = *par_image_sptr; + + shared_ptr dyn_image_sptr(read_from_file(argv[2])); + const DynamicDiscretisedDensity& dyn_image = *dyn_image_sptr; + + // NotToDo: Assertion for the dyn-par images, sizes should not be ncessary ONLY WHEN I will create from dyn_image the + // par_image... + assert(patlak_plot.get_time_frame_definitions().get_num_frames() == dyn_image.get_time_frame_definitions().get_num_frames()); + patlak_plot.multiply_dynamic_image_with_model_gradient(par_image, dyn_image); + + // Writing image + std::cerr << "Writing parametric-image in '" << argv[1] << "'\n"; + const Succeeded writing_succeeded = + OutputFileFormat::default_sptr()->write_to_file(argv[1], par_image); + + if (writing_succeeded == Succeeded::yes) + return EXIT_SUCCESS; + else + return EXIT_FAILURE; + } } - diff --git a/src/modelling_utilities/write_patlak_matrix.cxx b/src/modelling_utilities/write_patlak_matrix.cxx index ff5210c0f5..2f332ad93c 100644 --- a/src/modelling_utilities/write_patlak_matrix.cxx +++ b/src/modelling_utilities/write_patlak_matrix.cxx @@ -25,8 +25,8 @@ \par Usage: - \code - write_patlak_matrix [par_file] + \code + write_patlak_matrix [par_file] \endcode \note It writes it always to the text file: "model_matrix.out" of the working directory @@ -40,37 +40,32 @@ #include #include -int main(int argc, char *argv[]) -{ +int +main(int argc, char* argv[]) { USING_NAMESPACE_STIR -// Impelemented only for the linear Patlak Plot so far. -// In the future I should implement the KineticModels with the "linear" specification -// for patlak, logan etc... + // Impelemented only for the linear Patlak Plot so far. + // In the future I should implement the KineticModels with the "linear" specification + // for patlak, logan etc... PatlakPlot patlak_plot; - if (argc==2) - { - if (patlak_plot.parse(argv[1]) == false) - return EXIT_FAILURE; - } - else + if (argc == 2) { + if (patlak_plot.parse(argv[1]) == false) + return EXIT_FAILURE; + } else patlak_plot.ask_parameters(); - if (patlak_plot.set_up()==Succeeded::no) - { - std::cerr << "Usage:" << argv[0] << " [par_file] \n"; - return EXIT_FAILURE ; - } - else - { - // Writing model matrix - std::cerr << "Writing Patlak Model Matrix in file 'model_matrix.out'" << "\n"; - Succeeded writing_succeeded=(patlak_plot.get_model_matrix().write_to_file("model_matrix.out")); + if (patlak_plot.set_up() == Succeeded::no) { + std::cerr << "Usage:" << argv[0] << " [par_file] \n"; + return EXIT_FAILURE; + } else { + // Writing model matrix + std::cerr << "Writing Patlak Model Matrix in file 'model_matrix.out'" + << "\n"; + Succeeded writing_succeeded = (patlak_plot.get_model_matrix().write_to_file("model_matrix.out")); - if(writing_succeeded==Succeeded::yes) - return EXIT_SUCCESS ; - else - return EXIT_FAILURE ; - } + if (writing_succeeded == Succeeded::yes) + return EXIT_SUCCESS; + else + return EXIT_FAILURE; + } } - diff --git a/src/numerics_buildblock/determinant.cxx b/src/numerics_buildblock/determinant.cxx index 7f3212754d..976d0e6969 100644 --- a/src/numerics_buildblock/determinant.cxx +++ b/src/numerics_buildblock/determinant.cxx @@ -19,9 +19,9 @@ /*! \file \ingroup numerics - + \brief Implementation of stir::determinant() function for matrices - + \author Kris Thielemans */ @@ -32,50 +32,38 @@ START_NAMESPACE_STIR -namespace detail -{ +namespace detail { - template - static elemT - determinant_size3(const Array<2,elemT>& m) - { - const int i0 = m.get_min_index(); - const int j0 = m[i0].get_min_index(); - return - m[i0+0][j0+0]*m[i0+1][j0+1]*m[i0+2][j0+2] + - m[i0+0][j0+1]*m[i0+1][j0+2]*m[i0+2][j0+0] + - m[i0+0][j0+2]*m[i0+1][j0+0]*m[i0+2][j0+1] - - m[i0+0][j0+2]*m[i0+1][j0+1]*m[i0+2][j0+0] - - m[i0+0][j0+1]*m[i0+1][j0+0]*m[i0+2][j0+2] - - m[i0+0][j0+0]*m[i0+1][j0+2]*m[i0+2][j0+1]; - } +template +static elemT +determinant_size3(const Array<2, elemT>& m) { + const int i0 = m.get_min_index(); + const int j0 = m[i0].get_min_index(); + return m[i0 + 0][j0 + 0] * m[i0 + 1][j0 + 1] * m[i0 + 2][j0 + 2] + m[i0 + 0][j0 + 1] * m[i0 + 1][j0 + 2] * m[i0 + 2][j0 + 0] + + m[i0 + 0][j0 + 2] * m[i0 + 1][j0 + 0] * m[i0 + 2][j0 + 1] - m[i0 + 0][j0 + 2] * m[i0 + 1][j0 + 1] * m[i0 + 2][j0 + 0] - + m[i0 + 0][j0 + 1] * m[i0 + 1][j0 + 0] * m[i0 + 2][j0 + 2] - m[i0 + 0][j0 + 0] * m[i0 + 1][j0 + 2] * m[i0 + 2][j0 + 1]; +} - template - static elemT - determinant_size2(const Array<2,elemT>& m) - { - const int i0 = m.get_min_index(); - const int j0 = m[i0].get_min_index(); - return - m[i0+0][j0+0]*m[i0+1][j0+1] - - m[i0+0][j0+2]*m[i0+1][j0+1]; - } +template +static elemT +determinant_size2(const Array<2, elemT>& m) { + const int i0 = m.get_min_index(); + const int j0 = m[i0].get_min_index(); + return m[i0 + 0][j0 + 0] * m[i0 + 1][j0 + 1] - m[i0 + 0][j0 + 2] * m[i0 + 1][j0 + 1]; +} - template - static elemT - determinant_size1(const Array<2,elemT>& m) - { - const int i0 = m.get_min_index(); - const int j0 = m[i0].get_min_index(); - return - m[i0][j0]; - } +template +static elemT +determinant_size1(const Array<2, elemT>& m) { + const int i0 = m.get_min_index(); + const int j0 = m[i0].get_min_index(); + return m[i0][j0]; } +} // namespace detail template elemT -determinant(const Array<2,elemT>& m) -{ +determinant(const Array<2, elemT>& m) { assert(m.is_regular()); if (m.size() == 1) return detail::determinant_size1(m); @@ -83,8 +71,7 @@ determinant(const Array<2,elemT>& m) return detail::determinant_size2(m); if (m.size() == 3) return detail::determinant_size3(m); - error("determinant called for size larger than 3. Code in file %s needs work", - __FILE__); + error("determinant called for size larger than 3. Code in file %s needs work", __FILE__); // return to avoid compiler warning return 0; } @@ -92,8 +79,8 @@ determinant(const Array<2,elemT>& m) // // instantiations // -template float determinant(const Array<2,float>&); -template double determinant(const Array<2,double>&); -template std::complex determinant(const Array<2,std::complex >&); -template std::complex determinant(const Array<2,std::complex >&); +template float determinant(const Array<2, float>&); +template double determinant(const Array<2, double>&); +template std::complex determinant(const Array<2, std::complex>&); +template std::complex determinant(const Array<2, std::complex>&); END_NAMESPACE_STIR diff --git a/src/numerics_buildblock/fourier.cxx b/src/numerics_buildblock/fourier.cxx index 4fe4e6927c..a5ce6b4b80 100644 --- a/src/numerics_buildblock/fourier.cxx +++ b/src/numerics_buildblock/fourier.cxx @@ -1,5 +1,5 @@ /*! - \file + \file \ingroup DFT \brief Functions for computing discrete fourier transforms @@ -28,17 +28,16 @@ #include "stir/array_index_functions.h" START_NAMESPACE_STIR - template -static void bitreversal(VectorWithOffset& data) -{ - const int n=data.get_length(); - int j=1; - for (int i=0;i i) { - std::swap(data[j/2],data[i]); +static void +bitreversal(VectorWithOffset& data) { + const int n = data.get_length(); + int j = 1; + for (int i = 0; i < n; ++i) { + if (j / 2 > i) { + std::swap(data[j / 2], data[i]); } - int m=n; + int m = n; while (m >= 2 && j > m) { j -= m; m >>= 1; @@ -51,38 +50,37 @@ static void bitreversal(VectorWithOffset& data) call of the Fourier functions, and then stored in static arrays. */ // exparray[k][i] = exp(i*_PI/pow(2,k)) -typedef VectorWithOffset > > exparray_t; -static exparray_t exparray; +typedef VectorWithOffset>> exparray_t; +static exparray_t exparray; -static void init_exparray(const int k, const int pow2k) -{ - if (exparray.get_max_index() >= k && exparray[k].size()>0) +static void +init_exparray(const int k, const int pow2k) { + if (exparray.get_max_index() >= k && exparray[k].size() > 0) return; - if (exparray.get_max_index() (0, static_cast((i*_PI)/pow2k))); + exparray[k].grow(0, pow2k - 1); + for (int i = 0; i < pow2k; ++i) + exparray[k][i] = std::exp(std::complex(0, static_cast((i * _PI) / pow2k))); } // expminarray[k][i] = exp(-i*_PI/pow(2,k)) // obviously just the complex conjugate of exparray -static exparray_t expminarray; +static exparray_t expminarray; -static void init_expminarray(const int k, const int pow2k) -{ - if (expminarray.get_max_index() >= k && expminarray[k].size()>0) +static void +init_expminarray(const int k, const int pow2k) { + if (expminarray.get_max_index() >= k && expminarray[k].size() > 0) return; - if (expminarray.get_max_index() (0, static_cast(-(i*_PI)/pow2k))); + expminarray[k].grow(0, pow2k - 1); + for (int i = 0; i < pow2k; ++i) + expminarray[k][i] = std::exp(std::complex(0, static_cast(-(i * _PI) / pow2k))); } - /* First we define 1D fourier transforms of vectors with almost arbitrary element types. This is almost a straightforward 1D FFT implementation. The only tricky bit @@ -91,47 +89,45 @@ static void init_expminarray(const int k, const int pow2k) */ template -void fourier_1d(T& c, const int sign) -{ - if (c.size()==0) return; - assert(c.get_min_index()==0); - assert(sign==1 || sign ==-1); +void +fourier_1d(T& c, const int sign) { + if (c.size() == 0) + return; + assert(c.get_min_index() == 0); + assert(sign == 1 || sign == -1); bitreversal(c); // find 'nn' which is such that length==2^nn - const int nn=round(log(static_cast(c.size()))/log(2.)); - if (c.get_length()!= round(pow(2.,nn))) - error ("fourier_1d called with array length %d which is not 2^%d\n", c.size(), nn); - - int k=0; - int pow2k = 1; // will be updated to be round(pow(2,k)) - const int pow2nn=c.get_length(); // ==round(pow(2,nn)); - for (; k(c.size())) / log(2.)); + if (c.get_length() != round(pow(2., nn))) + error("fourier_1d called with array length %d which is not 2^%d\n", c.size(), nn); + + int k = 0; + int pow2k = 1; // will be updated to be round(pow(2,k)) + const int pow2nn = c.get_length(); // ==round(pow(2,nn)); + for (; k < nn; ++k, pow2k *= 2) { + if (sign == 1) + init_exparray(k, pow2k); else - init_expminarray(k,pow2k); - const exparray_t& cur_exparray = - sign==1? exparray : expminarray; - for (int j=0; j< pow2nn;j+= pow2k*2) - for (int i=0; i< pow2k; ++i) - { - typename T::reference c1= c[i + j]; - typename T::reference c2= c[i + j + pow2k]; - - typename T::value_type const t1 = c1; - /* here is what we have to do: - typename T::value_type const t2 = + init_expminarray(k, pow2k); + const exparray_t& cur_exparray = sign == 1 ? exparray : expminarray; + for (int j = 0; j < pow2nn; j += pow2k * 2) + for (int i = 0; i < pow2k; ++i) { + typename T::reference c1 = c[i + j]; + typename T::reference c2 = c[i + j + pow2k]; + + typename T::value_type const t1 = c1; + /* here is what we have to do: + typename T::value_type const t2 = c2*cur_exparray[k][i]; c1 = t1+t2; c2 = t1-t2; - however, this would create an unnecessary copy of t2, which is - potentially large. - So, we rewrite it without t2, and using operations that also - work when using multi-dim arrays. - Note that for multi-dimensional arrays, this code involves 4 - loops over the same data. - Using expression templates would speed this up. - */ + however, this would create an unnecessary copy of t2, which is + potentially large. + So, we rewrite it without t2, and using operations that also + work when using multi-dim arrays. + Note that for multi-dimensional arrays, this code involves 4 + loops over the same data. + Using expression templates would speed this up. + */ c2 *= cur_exparray[k][i]; c1 += c2; c2 *= -1; @@ -148,16 +144,11 @@ namespace detail { more powerful than function overloading. */ template -struct fourier_auxiliary -{ - static void - do_fourier(VectorWithOffset& c, const int sign) - { +struct fourier_auxiliary { + static void do_fourier(VectorWithOffset& c, const int sign) { fourier_1d(c, sign); const typename VectorWithOffset::iterator iter_end = c.end(); - for (typename VectorWithOffset::iterator iter = c.begin(); - iter != iter_end; - ++iter) + for (typename VectorWithOffset::iterator iter = c.begin(); iter != iter_end; ++iter) fourier(*iter, sign); } }; @@ -166,35 +157,19 @@ struct fourier_auxiliary #ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION template -struct fourier_auxiliary > -{ - static void - do_fourier(VectorWithOffset >& c, const int sign) - { - fourier_1d(c, sign); - } +struct fourier_auxiliary> { + static void do_fourier(VectorWithOffset>& c, const int sign) { fourier_1d(c, sign); } }; - -#else //no partial template specialisation +#else // no partial template specialisation // we just list float and double explicitly -struct fourier_auxiliary > -{ - static void - do_fourier(VectorWithOffset >& c, const int sign) - { - fourier_1d(c, sign); - } +struct fourier_auxiliary> { + static void do_fourier(VectorWithOffset>& c, const int sign) { fourier_1d(c, sign); } }; -struct fourier_auxiliary > -{ - static void - do_fourier(VectorWithOffset >& c, const int sign) - { - fourier_1d(c, sign); - } +struct fourier_auxiliary> { + static void do_fourier(VectorWithOffset>& c, const int sign) { fourier_1d(c, sign); } }; #endif @@ -202,90 +177,82 @@ struct fourier_auxiliary > // now the fourier function is easy to define in terms of the class above template -void -fourier(T& c, const int sign) -{ -#if !defined(_MSC_VER) || _MSC_VER>1200 - detail::fourier_auxiliary::do_fourier(c,sign); +void +fourier(T& c, const int sign) { +#if !defined(_MSC_VER) || _MSC_VER > 1200 + detail::fourier_auxiliary::do_fourier(c, sign); #else - detail::fourier_auxiliary::do_fourier(c,sign); + detail::fourier_auxiliary::do_fourier(c, sign); #endif } - /****************************************************************** DFT of real data *****************************************************************/ - - template -Array<1,std::complex > -fourier_1d_for_real_data(const Array<1,T>& v, const int sign) -//Array<1,std::complex > -//fourier_1d_for_real_data(const T& v, const int sign) +Array<1, std::complex> +fourier_1d_for_real_data(const Array<1, T>& v, const int sign) +// Array<1,std::complex > +// fourier_1d_for_real_data(const T& v, const int sign) { - //typedef std::complex complex_t; + // typedef std::complex complex_t; typedef std::complex complex_t; - if (v.size()==0) return Array<1,complex_t>(); - assert(v.get_min_index()==0); - assert(sign==1 || sign ==-1); - if (v.size()%2!=0) + if (v.size() == 0) + return Array<1, complex_t>(); + assert(v.get_min_index() == 0); + assert(sign == 1 || sign == -1); + if (v.size() % 2 != 0) error("fourier_1d_of_real can only handle arrays of even length.\n"); - Array<1,complex_t> c; - const unsigned int n = static_cast(v.size()/2); - // we reserve a range of 0,n here, such that + Array<1, complex_t> c; + const unsigned int n = static_cast(v.size() / 2); + // we reserve a range of 0,n here, such that // resize(n) later doesn't reallocate and copy - c.reserve(n+1); + c.reserve(n + 1); c.resize(n); // fill in complex numbers. // note: we need to divide by 2 in the final result. To save // some time, we do that already here. - for (int i=0; i(sign*(i*_PI)/n-_PI/2)))* - (c[i]-std::conj(c[n-i])); - - c[i] = (t1 + t2); - c[n-i] = std::conj(t1-t2); - } + // cout << "C: " << c; + c.resize(n + 1); + for (unsigned int i = 1; i <= n / 2; ++i) { + const complex_t t1 = (c[i] + std::conj(c[n - i])); + // TODO could get exp() from static exparray + // the nice thing about this code that it works even when the length is not a power of 2 + // (but of course, the call to fourier_1d would currently abort in that case) + const complex_t t2 = std::exp(complex_t(0, static_cast(sign * (i * _PI) / n - _PI / 2))) * (c[i] - std::conj(c[n - i])); + + c[i] = (t1 + t2); + c[n - i] = std::conj(t1 - t2); + } { const complex_t c0_copy = c[0]; - c[0]=(c0_copy.real() + c0_copy.imag())*2; - c[n]=(c0_copy.real() - c0_copy.imag())*2; + c[0] = (c0_copy.real() + c0_copy.imag()) * 2; + c[n] = (c0_copy.real() - c0_copy.imag()) * 2; } return c; } - template -Array<1,T> -inverse_fourier_1d_for_real_data_corrupting_input(Array<1,std::complex >& c, const int sign) -{ +Array<1, T> +inverse_fourier_1d_for_real_data_corrupting_input(Array<1, std::complex>& c, const int sign) { typedef std::complex complex_t; - if (c.size()==0) return Array<1,T>(); - assert(c.get_min_index()==0); - assert(sign==1 || sign ==-1); - const int n = c.get_length()-1; - if (n%2!=0) + if (c.size() == 0) + return Array<1, T>(); + assert(c.get_min_index() == 0); + assert(sign == 1 || sign == -1); + const int n = c.get_length() - 1; + if (n % 2 != 0) error("inverse_fourier_1d_of_real_data can only handle arrays of even length.\n"); /* Problematic asserts to check that the imaginary part of c[0] and c[n] is 0 - Trouble is that it could be only approximately 0 (e.g. when calling + Trouble is that it could be only approximately 0 (e.g. when calling inverse_fourier_real_data on multi-dimensional arrays). The version below tries to circumvent this problem by comparing with the norm of c. That fails however when c is zero (up to numerical precision). @@ -293,48 +260,38 @@ inverse_fourier_1d_for_real_data_corrupting_input(Array<1,std::complex >& c, we don't have that one to our disposal in this function. So, I disabled the asserts. */ - //assert(fabs(c[0].imag())<=.001*norm(c.begin_all(),c.end_all())/sqrt(n+1.)); // note divide by n+1 to avoid division by 0 - //assert(fabs(c[n].imag())<=.001*norm(c.begin_all(),c.end_all())/sqrt(n+1.)); - for (int i=1; i<=n/2; ++i) - { - const complex_t t1 = (c[i]+std::conj(c[n-i])); - // TODO could get exp() from static exparray - const complex_t t2 = - std::exp(complex_t(0, static_cast(-sign*(i*_PI)/n+_PI/2)))* - (c[i]-std::conj(c[n-i])); - - c[i] = (t1 + t2); - c[n-i] = std::conj(t1-t2); - } - { - c[0]=complex_t((c[0].real() + c[n].real()), - (c[0].real() - c[n].real()) - ); + // assert(fabs(c[0].imag())<=.001*norm(c.begin_all(),c.end_all())/sqrt(n+1.)); // note divide by n+1 to avoid division by 0 + // assert(fabs(c[n].imag())<=.001*norm(c.begin_all(),c.end_all())/sqrt(n+1.)); + for (int i = 1; i <= n / 2; ++i) { + const complex_t t1 = (c[i] + std::conj(c[n - i])); + // TODO could get exp() from static exparray + const complex_t t2 = std::exp(complex_t(0, static_cast(-sign * (i * _PI) / n + _PI / 2))) * (c[i] - std::conj(c[n - i])); + + c[i] = (t1 + t2); + c[n - i] = std::conj(t1 - t2); } + { c[0] = complex_t((c[0].real() + c[n].real()), (c[0].real() - c[n].real())); } - // now get rid of c[n] + // now get rid of c[n] c.resize(n); - //cout << "\nC: " << c/4; + // cout << "\nC: " << c/4; inverse_fourier(c, sign); // extract real numbers. - Array<1,T> v(2*n); - for (int i=0; i v(2 * n); + for (int i = 0; i < n; ++i) { + v[2 * i] = c[i].real() / 2; + v[2 * i + 1] = c[i].imag() / 2; + } return v; } template -Array<1,T> -inverse_fourier_1d_for_real_data(const Array<1,std::complex >& c, const int sign) -{ - Array<1,std::complex > tmp(c); +Array<1, T> +inverse_fourier_1d_for_real_data(const Array<1, std::complex>& c, const int sign) { + Array<1, std::complex> tmp(c); return inverse_fourier_1d_for_real_data_corrupting_input(tmp, sign); } - // multi-dimensional case namespace detail { @@ -344,40 +301,37 @@ namespace detail { more powerful than function overloading. */ template -struct fourier_for_real_data_auxiliary -{ - static Array > - do_fourier_for_real_data(const Array& c, const int sign) - { +struct fourier_for_real_data_auxiliary { + static Array> do_fourier_for_real_data(const Array& c, + const int sign) { // complicated business to get index range which is as follows: // outer_dimension = outer_dimension of c // all other dimensions are as small as possible (to avoid reallocations) BasicCoordinate min_index, max_index; - for (int d=2; d<=num_dimensions; ++d) + for (int d = 2; d <= num_dimensions; ++d) min_index[d] = max_index[d] = 0; min_index[1] = c.get_min_index(); max_index[1] = c.get_max_index(); - Array > array(IndexRange(min_index, max_index)); - for (int i=c.get_min_index(); i<=c.get_max_index(); ++i) + Array> array(IndexRange(min_index, max_index)); + for (int i = c.get_min_index(); i <= c.get_max_index(); ++i) array[i] = fourier_for_real_data(c[i], sign); fourier_1d(array, sign); return array; } - static Array - do_inverse_fourier_for_real_data_corrupting_input(Array >& c, const int sign) - { + static Array + do_inverse_fourier_for_real_data_corrupting_input(Array>& c, const int sign) { inverse_fourier_1d(c, sign); // complicated business to get index range which is as follows: // outer_dimension = outer_dimension of c // all other dimensions are as small as possible (to avoid reallocations) BasicCoordinate min_index, max_index; - for (int d=2; d<=num_dimensions; ++d) + for (int d = 2; d <= num_dimensions; ++d) min_index[d] = max_index[d] = 0; min_index[1] = c.get_min_index(); max_index[1] = c.get_max_index(); Array array(IndexRange(min_index, max_index)); - for (int i=c.get_min_index(); i<=c.get_max_index(); ++i) + for (int i = c.get_min_index(); i <= c.get_max_index(); ++i) array[i] = inverse_fourier_for_real_data_corrupting_input(c[i], sign); return array; } @@ -387,44 +341,29 @@ struct fourier_for_real_data_auxiliary #ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION template -struct fourier_for_real_data_auxiliary<1,elemT> -{ - static Array<1,std::complex > - do_fourier_for_real_data(const Array<1,elemT>& c, const int sign) - { - return - fourier_1d_for_real_data(c, sign); +struct fourier_for_real_data_auxiliary<1, elemT> { + static Array<1, std::complex> do_fourier_for_real_data(const Array<1, elemT>& c, const int sign) { + return fourier_1d_for_real_data(c, sign); } - static Array<1,elemT> - do_inverse_fourier_for_real_data_corrupting_input(Array<1,std::complex >& c, const int sign) - { - return - inverse_fourier_1d_for_real_data_corrupting_input(c, sign); + static Array<1, elemT> do_inverse_fourier_for_real_data_corrupting_input(Array<1, std::complex>& c, const int sign) { + return inverse_fourier_1d_for_real_data_corrupting_input(c, sign); } }; - -#else //no partial template specialisation +#else // no partial template specialisation // we just list float explicitly -struct fourier_for_real_data_auxiliary<1,float> -{ - static Array<1,std::complex > - do_fourier_for_real_data(const Array<1,float>& c, const int sign) - { - return - fourier_1d_for_real_data(c, sign); +struct fourier_for_real_data_auxiliary<1, float> { + static Array<1, std::complex> do_fourier_for_real_data(const Array<1, float>& c, const int sign) { + return fourier_1d_for_real_data(c, sign); } - static Array<1,float> - do_inverse_fourier_for_real_data_corrupting_input(Array<1,std::complex >& c, const int sign) - { - return - inverse_fourier_1d_for_real_data_corrupting_input(c, sign); + static Array<1, float> do_inverse_fourier_for_real_data_corrupting_input(Array<1, std::complex>& c, const int sign) { + return inverse_fourier_1d_for_real_data_corrupting_input(c, sign); } }; -#if 0 +# if 0 /* Disabled double for now. If you want to use double, you will probably have to make sure that Array<1,std::complex > is instantiated. @@ -445,108 +384,77 @@ struct fourier_for_real_data_auxiliary<1,double> inverse_fourier_1d_for_real_data_corrupting_input(c, sign); } }; -#endif // end of double +# endif // end of double #endif // end of BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION } // end of namespace detail - // now the fourier_for_real_data function is easy to define in terms of the class above template -Array > -fourier_for_real_data(const Array& c, const int sign) -{ - return - detail::fourier_for_real_data_auxiliary:: - do_fourier_for_real_data(c,sign); +Array> +fourier_for_real_data(const Array& c, const int sign) { + return detail::fourier_for_real_data_auxiliary::do_fourier_for_real_data(c, sign); } - template -Array -inverse_fourier_for_real_data_corrupting_input(Array >& c, const int sign) -{ - return - detail::fourier_for_real_data_auxiliary:: - do_inverse_fourier_for_real_data_corrupting_input(c,sign); +Array +inverse_fourier_for_real_data_corrupting_input(Array>& c, const int sign) { + return detail::fourier_for_real_data_auxiliary::do_inverse_fourier_for_real_data_corrupting_input(c, sign); } template -Array -inverse_fourier_for_real_data(const Array >& c, const int sign) -{ - Array > tmp(c); +Array +inverse_fourier_for_real_data(const Array>& c, const int sign) { + Array> tmp(c); return inverse_fourier_for_real_data_corrupting_input(tmp, sign); } template -Array > -pos_frequencies_to_all(const Array >& c) -{ +Array> +pos_frequencies_to_all(const Array>& c) { assert(c.is_regular()); BasicCoordinate min_index, max_index; c.get_regular_range(min_index, max_index); // check min_indices are 0 - assert(min_index == (min_index*0)); - max_index[num_dimensions]=max_index[num_dimensions]*2-1; - Array > result(IndexRange(min_index, max_index)); - + assert(min_index == (min_index * 0)); + max_index[num_dimensions] = max_index[num_dimensions] * 2 - 1; + Array> result(IndexRange(min_index, max_index)); + BasicCoordinate index = min_index; - const BasicCoordinate sizes = max_index+1; - do - { - result[index] = c[index]; - if (index[num_dimensions]>0) - { - const BasicCoordinate related_index = - modulo(sizes-index, sizes); - result[related_index] = std::conj(c[index]); - } + const BasicCoordinate sizes = max_index + 1; + do { + result[index] = c[index]; + if (index[num_dimensions] > 0) { + const BasicCoordinate related_index = modulo(sizes - index, sizes); + result[related_index] = std::conj(c[index]); } - while(next(index, c)); + } while (next(index, c)); return result; } - /***************************************************************** * INSTANTIATIONS * add any you need ******************************************************************/ -template -void -fourier<>(Array<3,std::complex >& c, const int sign); +template void fourier<>(Array<3, std::complex>& c, const int sign); -template -void -fourier<>(Array<2,std::complex >& c, const int sign); +template void fourier<>(Array<2, std::complex>& c, const int sign); -template -void -fourier<>(Array<1,std::complex >& c, const int sign); - -template -void -fourier<>(VectorWithOffset >& c, const int sign); - -#define INSTANTIATE(d,type) \ - template \ - Array > \ - fourier_for_real_data<>(const Array& v, const int sign); \ - template \ - Array \ - inverse_fourier_for_real_data_corrupting_input<>(Array >& c, const int sign); \ - template \ - Array \ - inverse_fourier_for_real_data<>(const Array >& c, const int sign); \ - template \ - Array > \ - pos_frequencies_to_all<>(const Array >& c); - -INSTANTIATE(1,float); -INSTANTIATE(2,float); -INSTANTIATE(3,float); +template void fourier<>(Array<1, std::complex>& c, const int sign); + +template void fourier<>(VectorWithOffset>& c, const int sign); + +#define INSTANTIATE(d, type) \ + template Array> fourier_for_real_data<>(const Array& v, const int sign); \ + template Array inverse_fourier_for_real_data_corrupting_input<>(Array> & c, const int sign); \ + template Array inverse_fourier_for_real_data<>(const Array>& c, const int sign); \ + template Array> pos_frequencies_to_all<>(const Array>& c); + +INSTANTIATE(1, float); +INSTANTIATE(2, float); +INSTANTIATE(3, float); #undef INSTANTIATE END_NAMESPACE_STIR diff --git a/src/recon_buildblock/AnalyticReconstruction.cxx b/src/recon_buildblock/AnalyticReconstruction.cxx index a5d4e50594..264d89cdce 100644 --- a/src/recon_buildblock/AnalyticReconstruction.cxx +++ b/src/recon_buildblock/AnalyticReconstruction.cxx @@ -1,33 +1,33 @@ /* Copyright (C) 2000 PARAPET partners - Copyright (C) 2000 - 2006, Hammersmith Imanet Ltd + Copyright (C) 2000 - 2006, Hammersmith Imanet Ltd Copyright (C) 2016, 2018 - 2020 University College London - This file is part of STIR. - - This file is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - This file is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - + This file is part of STIR. + + This file is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + See STIR/LICENSE.txt for details */ /*! \file \ingroup recon_buildblock - - \brief implementation of the stir::AnalyticReconstruction class - + + \brief implementation of the stir::AnalyticReconstruction class + \author Kris Thielemans \author Matthew Jacobson \author Nikos Efthimiou \author PARAPET project - + */ #include "stir/recon_buildblock/AnalyticReconstruction.h" @@ -40,35 +40,29 @@ START_NAMESPACE_STIR - - // parameters -void -AnalyticReconstruction::set_defaults() -{ +void +AnalyticReconstruction::set_defaults() { base_type::set_defaults(); - input_filename=""; - max_segment_num_to_process=-1; - proj_data_ptr.reset(); + input_filename = ""; + max_segment_num_to_process = -1; + proj_data_ptr.reset(); target_parameter_parser.set_defaults(); } - -void -AnalyticReconstruction::initialise_keymap() -{ +void +AnalyticReconstruction::initialise_keymap() { base_type::initialise_keymap(); - parser.add_key("input file",&input_filename); + parser.add_key("input file", &input_filename); // KT 20/06/2001 disabled - //parser.add_key("mash x views", &num_views_to_add); + // parser.add_key("mash x views", &num_views_to_add); parser.add_key("maximum absolute segment number to process", &max_segment_num_to_process); this->target_parameter_parser.add_to_keymap(parser); -// parser.add_key("END", &KeyParser::stop_parsing); - + // parser.add_key("END", &KeyParser::stop_parsing); } #if 0 @@ -90,7 +84,7 @@ void AnalyticReconstruction::ask_parameters() max_segment_num_to_process= ask_num("Maximum absolute segment number to process: ", 0, proj_data_ptr->get_max_segment_num(), 0); -#if 0 +# if 0 // The angular compression consists of an average pairs of sinograms rows // in order to reduce the number of views by a factor of 2 // and therefore reduce the amount of data in a sinogram as well @@ -101,7 +95,7 @@ void AnalyticReconstruction::ask_parameters() // to have little effect near the center of the FOV. // However, it could cause loss of precision num_views_to_add= ask_num("Mashing views ? (1: No mashing, 2: By 2 , 4: By 4) : ",1,4,1); -#endif +# endif ask_filename_with_extension(output_filename_prefix_char,"Output filename prefix", ""); @@ -110,67 +104,54 @@ void AnalyticReconstruction::ask_parameters() } #endif // ask_parameters disabled - -bool AnalyticReconstruction::post_processing() -{ - if (base_type::post_processing()) - return true; - if (input_filename.length() == 0) - { warning("You need to specify an input file\n"); return true; } +bool +AnalyticReconstruction::post_processing() { + if (base_type::post_processing()) + return true; + if (input_filename.length() == 0) { + warning("You need to specify an input file\n"); + return true; + } // KT 20/06/2001 disabled as not functional yet #if 0 if (num_views_to_add!=1 && (num_views_to_add<=0 || num_views_to_add%2 != 0)) { warning("The 'mash x views' key has an invalid value (must be 1 or even number)\n"); return true; } #endif - - proj_data_ptr= ProjData::read_from_file(input_filename); + + proj_data_ptr = ProjData::read_from_file(input_filename); target_parameter_parser.check_values(); return false; } - //************* other functions ************* -DiscretisedDensity<3,float>* -AnalyticReconstruction:: -construct_target_image_ptr() const -{ - return - this->target_parameter_parser.create(this->get_input_data()); +DiscretisedDensity<3, float>* +AnalyticReconstruction::construct_target_image_ptr() const { + return this->target_parameter_parser.create(this->get_input_data()); } - - -Succeeded -AnalyticReconstruction:: -reconstruct() -{ +Succeeded +AnalyticReconstruction::reconstruct() { this->start_timers(); this->target_data_sptr.reset(this->construct_target_image_ptr()); - if (this->set_up(this->target_data_sptr) == Succeeded::no) - { - this->stop_timers(); - return Succeeded::no; - } + if (this->set_up(this->target_data_sptr) == Succeeded::no) { + this->stop_timers(); + return Succeeded::no; + } this->stop_timers(); const Succeeded success = this->reconstruct(this->target_data_sptr); - if (success == Succeeded::yes && !_disable_output) - { - this->output_file_format_ptr-> - write_to_file(this->output_filename_prefix, *this->target_data_sptr); + if (success == Succeeded::yes && !_disable_output) { + this->output_file_format_ptr->write_to_file(this->output_filename_prefix, *this->target_data_sptr); } return success; } - -Succeeded -AnalyticReconstruction:: -reconstruct(shared_ptr const& target_image_sptr) -{ +Succeeded +AnalyticReconstruction::reconstruct(shared_ptr const& target_image_sptr) { this->check(*target_data_sptr); #if 0 Succeeded success = this->set_up(target_image_sptr); @@ -179,32 +160,27 @@ reconstruct(shared_ptr const& target_image_sptr) #endif this->start_timers(); Succeeded success = this->actual_reconstruct(target_image_sptr); - if (success == Succeeded::yes) - { - if(!is_null_ptr(this->post_filter_sptr)) - { - info("Applying post-filter"); - this->post_filter_sptr->apply(*target_image_sptr); - - info(boost::format(" min and max after post-filtering %1% %2%") % target_image_sptr->find_min() % target_image_sptr->find_max()); - } + if (success == Succeeded::yes) { + if (!is_null_ptr(this->post_filter_sptr)) { + info("Applying post-filter"); + this->post_filter_sptr->apply(*target_image_sptr); + + info(boost::format(" min and max after post-filtering %1% %2%") % target_image_sptr->find_min() % + target_image_sptr->find_max()); + } } this->stop_timers(); return success; } void -AnalyticReconstruction:: -set_input_data(const shared_ptr &arg) -{ +AnalyticReconstruction::set_input_data(const shared_ptr& arg) { _already_set_up = false; - this->proj_data_ptr = dynamic_pointer_cast < ProjData >(arg); + this->proj_data_ptr = dynamic_pointer_cast(arg); } const ProjData& -AnalyticReconstruction:: -get_input_data() const -{ +AnalyticReconstruction::get_input_data() const { if (is_null_ptr(this->proj_data_ptr)) error("calling get_input_data but it hasn't been set yet"); return *this->proj_data_ptr; @@ -212,69 +188,58 @@ get_input_data() const // forwarding functions for ParseDiscretisedDensityParameters int -AnalyticReconstruction:: -get_output_image_size_xy() const -{ return target_parameter_parser.get_output_image_size_xy(); } +AnalyticReconstruction::get_output_image_size_xy() const { + return target_parameter_parser.get_output_image_size_xy(); +} void -AnalyticReconstruction:: -set_output_image_size_xy(int v) -{ +AnalyticReconstruction::set_output_image_size_xy(int v) { _already_set_up = false; target_parameter_parser.set_output_image_size_xy(v); } int -AnalyticReconstruction:: -get_output_image_size_z() const -{ return target_parameter_parser.get_output_image_size_z(); } +AnalyticReconstruction::get_output_image_size_z() const { + return target_parameter_parser.get_output_image_size_z(); +} void -AnalyticReconstruction:: -set_output_image_size_z(int v) -{ +AnalyticReconstruction::set_output_image_size_z(int v) { _already_set_up = false; target_parameter_parser.set_output_image_size_z(v); } float -AnalyticReconstruction:: -get_zoom_xy() const -{ return target_parameter_parser.get_zoom_xy(); } +AnalyticReconstruction::get_zoom_xy() const { + return target_parameter_parser.get_zoom_xy(); +} void -AnalyticReconstruction:: -set_zoom_xy(float v) -{ +AnalyticReconstruction::set_zoom_xy(float v) { _already_set_up = false; target_parameter_parser.set_zoom_xy(v); } float -AnalyticReconstruction:: -get_zoom_z() const -{ return target_parameter_parser.get_zoom_z(); } +AnalyticReconstruction::get_zoom_z() const { + return target_parameter_parser.get_zoom_z(); +} void -AnalyticReconstruction:: -set_zoom_z(float v) -{ +AnalyticReconstruction::set_zoom_z(float v) { _already_set_up = false; target_parameter_parser.set_zoom_z(v); } const CartesianCoordinate3D& -AnalyticReconstruction:: -get_offset() const -{ return target_parameter_parser.get_offset(); } +AnalyticReconstruction::get_offset() const { + return target_parameter_parser.get_offset(); +} void -AnalyticReconstruction:: -set_offset(const CartesianCoordinate3D& v) -{ +AnalyticReconstruction::set_offset(const CartesianCoordinate3D& v) { _already_set_up = false; target_parameter_parser.set_offset(v); } END_NAMESPACE_STIR - diff --git a/src/recon_buildblock/BackProjectorByBin.cxx b/src/recon_buildblock/BackProjectorByBin.cxx index 8f9867ed35..9fdc6fddd5 100644 --- a/src/recon_buildblock/BackProjectorByBin.cxx +++ b/src/recon_buildblock/BackProjectorByBin.cxx @@ -9,7 +9,7 @@ \author Kris Thielemans \author PARAPET project \author Richard Brown - + */ /* Copyright (C) 2000 PARAPET partners @@ -30,7 +30,6 @@ See STIR/LICENSE.txt for details */ - #include "stir/recon_buildblock/BackProjectorByBin.h" #include "stir/recon_buildblock/find_basic_vs_nums_in_subsets.h" #include "stir/RelatedViewgrams.h" @@ -41,120 +40,91 @@ #include "stir/DataProcessor.h" #include #ifdef STIR_OPENMP -#include "stir/is_null_ptr.h" -#include "stir/DiscretisedDensity.h" -#include +# include "stir/is_null_ptr.h" +# include "stir/DiscretisedDensity.h" +# include #endif #include START_NAMESPACE_STIR -BackProjectorByBin::BackProjectorByBin() - : _already_set_up(false) -{ - set_defaults(); -} +BackProjectorByBin::BackProjectorByBin() : _already_set_up(false) { set_defaults(); } -BackProjectorByBin::~BackProjectorByBin() -{ -} +BackProjectorByBin::~BackProjectorByBin() {} void -BackProjectorByBin:: -set_defaults() -{ +BackProjectorByBin::set_defaults() { _post_data_processor_sptr.reset(); } void -BackProjectorByBin:: -initialise_keymap() -{ +BackProjectorByBin::initialise_keymap() { parser.add_start_key("Back Projector Parameters"); parser.add_stop_key("End Back Projector Parameters"); parser.add_parsing_key("post data processor", &_post_data_processor_sptr); } void -BackProjectorByBin:: -set_up(const shared_ptr& proj_data_info_sptr, - const shared_ptr >& density_info_sptr) -{ +BackProjectorByBin::set_up(const shared_ptr& proj_data_info_sptr, + const shared_ptr>& density_info_sptr) { _already_set_up = true; _proj_data_info_sptr = proj_data_info_sptr->create_shared_clone(); _density_sptr.reset(density_info_sptr->clone()); #ifdef STIR_OPENMP -#pragma omp parallel - { -#pragma omp single - _local_output_image_sptrs.resize(omp_get_num_threads(), shared_ptr >()); - } - for (int i=0; i(_local_output_image_sptrs.size()); ++i) - if(!is_null_ptr(_local_output_image_sptrs[i])) // already created in previous run - if (!_local_output_image_sptrs[i]->has_same_characteristics(*density_info_sptr)) - { - // previous run was with different sizes, so reallocate - _local_output_image_sptrs[i].reset(density_info_sptr->get_empty_copy()); - } +# pragma omp parallel + { +# pragma omp single + _local_output_image_sptrs.resize(omp_get_num_threads(), shared_ptr>()); + } + for (int i = 0; i < static_cast(_local_output_image_sptrs.size()); ++i) + if (!is_null_ptr(_local_output_image_sptrs[i])) // already created in previous run + if (!_local_output_image_sptrs[i]->has_same_characteristics(*density_info_sptr)) { + // previous run was with different sizes, so reallocate + _local_output_image_sptrs[i].reset(density_info_sptr->get_empty_copy()); + } #endif } void -BackProjectorByBin:: -check(const ProjDataInfo& proj_data_info, const DiscretisedDensity<3,float>& density_info) const -{ +BackProjectorByBin::check(const ProjDataInfo& proj_data_info, const DiscretisedDensity<3, float>& density_info) const { if (!this->_already_set_up) error("BackProjectorByBin method called without calling set_up first."); if (!(*this->_proj_data_info_sptr >= proj_data_info)) - error(boost::format("BackProjectorByBin set-up with different geometry for projection data.\nSet_up was with\n%1%\nCalled with\n%2%") - % this->_proj_data_info_sptr->parameter_info() % proj_data_info.parameter_info()); - if (! this->_density_sptr->has_same_characteristics(density_info)) + error(boost::format( + "BackProjectorByBin set-up with different geometry for projection data.\nSet_up was with\n%1%\nCalled with\n%2%") % + this->_proj_data_info_sptr->parameter_info() % proj_data_info.parameter_info()); + if (!this->_density_sptr->has_same_characteristics(density_info)) error("BackProjectorByBin set-up with different geometry for density or volume data."); } void -BackProjectorByBin::back_project(DiscretisedDensity<3,float>& image, -const ProjData& proj_data, int subset_num, int num_subsets) -{ +BackProjectorByBin::back_project(DiscretisedDensity<3, float>& image, const ProjData& proj_data, int subset_num, + int num_subsets) { start_accumulating_in_new_target(); back_project(proj_data, subset_num, num_subsets); get_output(image); } #ifdef STIR_PROJECTORS_AS_V3 void -BackProjectorByBin::back_project( DiscretisedDensity<3,float>& image, - const RelatedViewgrams& viewgrams) -{ - back_project(image,viewgrams, - viewgrams.get_min_axial_pos_num(), - viewgrams.get_max_axial_pos_num(), - viewgrams.get_min_tangential_pos_num(), - viewgrams.get_max_tangential_pos_num()); +BackProjectorByBin::back_project(DiscretisedDensity<3, float>& image, const RelatedViewgrams& viewgrams) { + back_project(image, viewgrams, viewgrams.get_min_axial_pos_num(), viewgrams.get_max_axial_pos_num(), + viewgrams.get_min_tangential_pos_num(), viewgrams.get_max_tangential_pos_num()); } -void BackProjectorByBin::back_project - (DiscretisedDensity<3,float>& image, - const RelatedViewgrams& viewgrams, - const int min_axial_pos_num, - const int max_axial_pos_num) -{ - back_project(image,viewgrams, - min_axial_pos_num, - max_axial_pos_num, - viewgrams.get_min_tangential_pos_num(), - viewgrams.get_max_tangential_pos_num()); +void +BackProjectorByBin::back_project(DiscretisedDensity<3, float>& image, const RelatedViewgrams& viewgrams, + const int min_axial_pos_num, const int max_axial_pos_num) { + back_project(image, viewgrams, min_axial_pos_num, max_axial_pos_num, viewgrams.get_min_tangential_pos_num(), + viewgrams.get_max_tangential_pos_num()); } void -BackProjectorByBin:: -back_project(DiscretisedDensity<3,float>& density, - const RelatedViewgrams& viewgrams, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) -{ - if (viewgrams.get_num_viewgrams()==0) +BackProjectorByBin::back_project(DiscretisedDensity<3, float>& density, const RelatedViewgrams& viewgrams, + const int min_axial_pos_num, const int max_axial_pos_num, const int min_tangential_pos_num, + const int max_tangential_pos_num) { + if (viewgrams.get_num_viewgrams() == 0) return; check(*viewgrams.get_proj_data_info_sptr(), density); @@ -165,118 +135,92 @@ back_project(DiscretisedDensity<3,float>& density, { const ViewSegmentNumbers basic_vs = viewgrams.get_basic_view_segment_num(); - if (get_symmetries_used()->num_related_view_segment_numbers(basic_vs) != - viewgrams.get_num_viewgrams()) + if (get_symmetries_used()->num_related_view_segment_numbers(basic_vs) != viewgrams.get_num_viewgrams()) error("BackProjectorByBin::back_project called with incorrect related_viewgrams. Problem with symmetries!\n"); - for (RelatedViewgrams::const_iterator iter = viewgrams.begin(); - iter != viewgrams.end(); - ++iter) - { - ViewSegmentNumbers vs(iter->get_view_num(), iter->get_segment_num()); - get_symmetries_used()->find_basic_view_segment_numbers(vs); - if (vs != basic_vs) - error("BackProjectorByBin::back_project called with incorrect related_viewgrams. Problem with symmetries!\n"); + for (RelatedViewgrams::const_iterator iter = viewgrams.begin(); iter != viewgrams.end(); ++iter) { + ViewSegmentNumbers vs(iter->get_view_num(), iter->get_segment_num()); + get_symmetries_used()->find_basic_view_segment_numbers(vs); + if (vs != basic_vs) + error("BackProjectorByBin::back_project called with incorrect related_viewgrams. Problem with symmetries!\n"); } } - actual_back_project(density,viewgrams, - min_axial_pos_num, - max_axial_pos_num, - min_tangential_pos_num, - max_tangential_pos_num); + actual_back_project(density, viewgrams, min_axial_pos_num, max_axial_pos_num, min_tangential_pos_num, max_tangential_pos_num); stop_timers(); } #endif // -------------------------------------------------------------------------------------------------------------------- // -// The following are repetition of above, where the DiscretisedDensity has already been set with start_accumulating_in_new_target() +// The following are repetition of above, where the DiscretisedDensity has already been set with +// start_accumulating_in_new_target() // -------------------------------------------------------------------------------------------------------------------- // void -BackProjectorByBin::back_project(const ProjData& proj_data, int subset_num, int num_subsets) -{ +BackProjectorByBin::back_project(const ProjData& proj_data, int subset_num, int num_subsets) { #ifndef NDEBUG - assert(fabs(_density_sptr->sum()) < 1.e-7F); + assert(fabs(_density_sptr->sum()) < 1.e-7F); #endif check(*proj_data.get_proj_data_info_sptr(), *_density_sptr); - shared_ptr - symmetries_sptr(this->get_symmetries_used()->clone()); + shared_ptr symmetries_sptr(this->get_symmetries_used()->clone()); - const std::vector vs_nums_to_process = - detail::find_basic_vs_nums_in_subset(*proj_data.get_proj_data_info_sptr(), *symmetries_sptr, - proj_data.get_min_segment_num(), proj_data.get_max_segment_num(), - subset_num, num_subsets); + const std::vector vs_nums_to_process = detail::find_basic_vs_nums_in_subset( + *proj_data.get_proj_data_info_sptr(), *symmetries_sptr, proj_data.get_min_segment_num(), proj_data.get_max_segment_num(), + subset_num, num_subsets); #ifdef STIR_OPENMP -#pragma omp parallel shared(proj_data, symmetries_sptr) +# pragma omp parallel shared(proj_data, symmetries_sptr) #endif { #ifdef STIR_OPENMP -#pragma omp for schedule(runtime) +# pragma omp for schedule(runtime) #endif // note: older versions of openmp need an int as loop - for (int i=0; i(vs_nums_to_process.size()); ++i) - { - const ViewSegmentNumbers vs=vs_nums_to_process[i]; - - for (int k=proj_data.get_proj_data_info_sptr()->get_min_tof_pos_num(); - k<=proj_data.get_proj_data_info_sptr()->get_max_tof_pos_num(); - ++k) - { + for (int i = 0; i < static_cast(vs_nums_to_process.size()); ++i) { + const ViewSegmentNumbers vs = vs_nums_to_process[i]; + + for (int k = proj_data.get_proj_data_info_sptr()->get_min_tof_pos_num(); + k <= proj_data.get_proj_data_info_sptr()->get_max_tof_pos_num(); ++k) { #ifdef STIR_OPENMP RelatedViewgrams viewgrams; -#pragma omp critical (BACKPROJECTORBYBIN_GETVIEWGRAMS) +# pragma omp critical(BACKPROJECTORBYBIN_GETVIEWGRAMS) viewgrams = proj_data.get_related_viewgrams(vs, symmetries_sptr, false, k); #else - const RelatedViewgrams viewgrams = - proj_data.get_related_viewgrams(vs, symmetries_sptr, false, k); + const RelatedViewgrams viewgrams = proj_data.get_related_viewgrams(vs, symmetries_sptr, false, k); #endif info(boost::format("Processing view %1% of segment %2%") % vs.view_num() % vs.segment_num(), 2); back_project(viewgrams); } - } + } } #ifdef STIR_OPENMP // "reduce" data constructed by threads { - for (int i=0; i(_local_output_image_sptrs.size()); ++i) - if(!is_null_ptr(_local_output_image_sptrs[i])) // only accumulate if a thread filled something in + for (int i = 0; i < static_cast(_local_output_image_sptrs.size()); ++i) + if (!is_null_ptr(_local_output_image_sptrs[i])) // only accumulate if a thread filled something in (*_density_sptr) += *(_local_output_image_sptrs[i]); } #endif } - void -BackProjectorByBin::back_project(const RelatedViewgrams& viewgrams) -{ - back_project(viewgrams, - viewgrams.get_min_axial_pos_num(), - viewgrams.get_max_axial_pos_num(), - viewgrams.get_min_tangential_pos_num(), - viewgrams.get_max_tangential_pos_num()); +BackProjectorByBin::back_project(const RelatedViewgrams& viewgrams) { + back_project(viewgrams, viewgrams.get_min_axial_pos_num(), viewgrams.get_max_axial_pos_num(), + viewgrams.get_min_tangential_pos_num(), viewgrams.get_max_tangential_pos_num()); } -void BackProjectorByBin::back_project - (const RelatedViewgrams& viewgrams, - const int min_axial_pos_num, - const int max_axial_pos_num) -{ - back_project(viewgrams, - min_axial_pos_num, - max_axial_pos_num, - viewgrams.get_min_tangential_pos_num(), - viewgrams.get_max_tangential_pos_num()); +void +BackProjectorByBin::back_project(const RelatedViewgrams& viewgrams, const int min_axial_pos_num, + const int max_axial_pos_num) { + back_project(viewgrams, min_axial_pos_num, max_axial_pos_num, viewgrams.get_min_tangential_pos_num(), + viewgrams.get_max_tangential_pos_num()); } void -BackProjectorByBin:: -back_project(const RelatedViewgrams& viewgrams, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) -{ - if (viewgrams.get_num_viewgrams()==0) +BackProjectorByBin::back_project(const RelatedViewgrams& viewgrams, const int min_axial_pos_num, + const int max_axial_pos_num, const int min_tangential_pos_num, + const int max_tangential_pos_num) { + if (viewgrams.get_num_viewgrams() == 0) return; check(*viewgrams.get_proj_data_info_sptr(), *_density_sptr); @@ -284,8 +228,8 @@ back_project(const RelatedViewgrams& viewgrams, start_timers(); #ifdef STIR_OPENMP - const int thread_num=omp_get_thread_num(); - if(is_null_ptr(_local_output_image_sptrs[thread_num])) + const int thread_num = omp_get_thread_num(); + if (is_null_ptr(_local_output_image_sptrs[thread_num])) _local_output_image_sptrs[thread_num].reset(_density_sptr->get_empty_copy()); #endif @@ -293,112 +237,90 @@ back_project(const RelatedViewgrams& viewgrams, { const ViewSegmentNumbers basic_vs = viewgrams.get_basic_view_segment_num(); - if (get_symmetries_used()->num_related_view_segment_numbers(basic_vs) != - viewgrams.get_num_viewgrams()) + if (get_symmetries_used()->num_related_view_segment_numbers(basic_vs) != viewgrams.get_num_viewgrams()) error("BackProjectorByBin::back_project called with incorrect related_viewgrams. Problem with symmetries!\n"); - for (RelatedViewgrams::const_iterator iter = viewgrams.begin(); - iter != viewgrams.end(); - ++iter) - { - ViewSegmentNumbers vs(iter->get_view_num(), iter->get_segment_num()); - get_symmetries_used()->find_basic_view_segment_numbers(vs); - if (vs != basic_vs) - error("BackProjectorByBin::back_project called with incorrect related_viewgrams. Problem with symmetries!\n"); + for (RelatedViewgrams::const_iterator iter = viewgrams.begin(); iter != viewgrams.end(); ++iter) { + ViewSegmentNumbers vs(iter->get_view_num(), iter->get_segment_num()); + get_symmetries_used()->find_basic_view_segment_numbers(vs); + if (vs != basic_vs) + error("BackProjectorByBin::back_project called with incorrect related_viewgrams. Problem with symmetries!\n"); } } - actual_back_project( - viewgrams, - min_axial_pos_num, - max_axial_pos_num, - min_tangential_pos_num, - max_tangential_pos_num); + actual_back_project(viewgrams, min_axial_pos_num, max_axial_pos_num, min_tangential_pos_num, max_tangential_pos_num); stop_timers(); } void -BackProjectorByBin:: -start_accumulating_in_new_target() -{ +BackProjectorByBin::start_accumulating_in_new_target() { #ifdef STIR_OPENMP - if (omp_get_num_threads()!=1) - error("BackProjectorByBin::start_accumulating_in_new_target cannot be called inside a thread"); - - for (int i=0; i(_local_output_image_sptrs.size()); ++i) - if(!is_null_ptr(_local_output_image_sptrs[i])) // only reset to zero if a thread filled something in - { - if (!_local_output_image_sptrs.at(i)->has_same_characteristics(*_density_sptr)) - error("BackProjectorByBin implementation error: local images for openmp have wrong size"); - _local_output_image_sptrs.at(i)->fill(0.F); - } + if (omp_get_num_threads() != 1) + error("BackProjectorByBin::start_accumulating_in_new_target cannot be called inside a thread"); + + for (int i = 0; i < static_cast(_local_output_image_sptrs.size()); ++i) + if (!is_null_ptr(_local_output_image_sptrs[i])) // only reset to zero if a thread filled something in + { + if (!_local_output_image_sptrs.at(i)->has_same_characteristics(*_density_sptr)) + error("BackProjectorByBin implementation error: local images for openmp have wrong size"); + _local_output_image_sptrs.at(i)->fill(0.F); + } #endif - _density_sptr->fill(0.); + _density_sptr->fill(0.); } void -BackProjectorByBin:: -get_output(DiscretisedDensity<3,float> &density) const -{ - if (!density.has_same_characteristics(*_density_sptr)) - error("Images should have similar characteristics."); +BackProjectorByBin::get_output(DiscretisedDensity<3, float>& density) const { + if (!density.has_same_characteristics(*_density_sptr)) + error("Images should have similar characteristics."); #ifdef STIR_OPENMP - if (omp_get_num_threads()!=1) - error("BackProjectorByBin::get_output() cannot be called inside a thread"); + if (omp_get_num_threads() != 1) + error("BackProjectorByBin::get_output() cannot be called inside a thread"); // "reduce" data constructed by threads { density.fill(0.F); - for (int i=0; i(_local_output_image_sptrs.size()); ++i) { - if(!is_null_ptr(_local_output_image_sptrs[i]))// only accumulate if a thread filled something in - density += *(_local_output_image_sptrs[i]); + for (int i = 0; i < static_cast(_local_output_image_sptrs.size()); ++i) { + if (!is_null_ptr(_local_output_image_sptrs[i])) // only accumulate if a thread filled something in + density += *(_local_output_image_sptrs[i]); } } #else - std::copy(_density_sptr->begin_all(), _density_sptr->end_all(), density.begin_all()); + std::copy(_density_sptr->begin_all(), _density_sptr->end_all(), density.begin_all()); #endif - // If a post-back-projection data processor has been set, apply it. - if (!is_null_ptr(_post_data_processor_sptr)) { - Succeeded success = _post_data_processor_sptr->apply(density); - if (success != Succeeded::yes) - throw std::runtime_error("BackProjectorByBin::get_output(). Post-back-projection data processor failed."); - } + // If a post-back-projection data processor has been set, apply it. + if (!is_null_ptr(_post_data_processor_sptr)) { + Succeeded success = _post_data_processor_sptr->apply(density); + if (success != Succeeded::yes) + throw std::runtime_error("BackProjectorByBin::get_output(). Post-back-projection data processor failed."); + } } void -BackProjectorByBin:: -set_post_data_processor(shared_ptr > > post_data_processor_sptr) -{ - _post_data_processor_sptr = post_data_processor_sptr; +BackProjectorByBin::set_post_data_processor(shared_ptr>> post_data_processor_sptr) { + _post_data_processor_sptr = post_data_processor_sptr; } void -BackProjectorByBin:: -actual_back_project(DiscretisedDensity<3,float>&, - const RelatedViewgrams&, - const int, const int, - const int, const int) -{ - error("BackProjectorByBin::actual_forward_project() This is deprecated and should not be used."); +BackProjectorByBin::actual_back_project(DiscretisedDensity<3, float>&, const RelatedViewgrams&, const int, const int, + const int, const int) { + error("BackProjectorByBin::actual_forward_project() This is deprecated and should not be used."); } void -BackProjectorByBin:: -actual_back_project(const RelatedViewgrams& viewgrams, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) -{ - shared_ptr > density_sptr = _density_sptr; +BackProjectorByBin::actual_back_project(const RelatedViewgrams& viewgrams, const int min_axial_pos_num, + const int max_axial_pos_num, const int min_tangential_pos_num, + const int max_tangential_pos_num) { + shared_ptr> density_sptr = _density_sptr; #ifdef STIR_OPENMP - const int thread_num=omp_get_thread_num(); - density_sptr = _local_output_image_sptrs[thread_num]; + const int thread_num = omp_get_thread_num(); + density_sptr = _local_output_image_sptrs[thread_num]; #endif - actual_back_project(*density_sptr, viewgrams, - min_axial_pos_num, max_axial_pos_num, - min_tangential_pos_num, max_tangential_pos_num); + actual_back_project(*density_sptr, viewgrams, min_axial_pos_num, max_axial_pos_num, min_tangential_pos_num, + max_tangential_pos_num); } END_NAMESPACE_STIR diff --git a/src/recon_buildblock/BackProjectorByBinUsingInterpolation.cxx b/src/recon_buildblock/BackProjectorByBinUsingInterpolation.cxx index 50890b384a..af651552c8 100644 --- a/src/recon_buildblock/BackProjectorByBinUsingInterpolation.cxx +++ b/src/recon_buildblock/BackProjectorByBinUsingInterpolation.cxx @@ -27,7 +27,7 @@ \author Kris Thielemans \author Claire Labbe \author PARAPET project - + */ #include "stir/VoxelsOnCartesianGrid.h" @@ -49,26 +49,17 @@ using std::max; START_NAMESPACE_STIR -JacobianForIntBP:: -JacobianForIntBP(const shared_ptr proj_data_info_sptr, bool exact) - - : R2(square(proj_data_info_sptr->get_ring_radius())), - dxy2(square(proj_data_info_sptr->get_tangential_sampling())), - ring_spacing2 (square(proj_data_info_sptr->get_ring_spacing())), - backprojection_normalisation - (proj_data_info_sptr->get_ring_spacing()/2/proj_data_info_sptr->get_num_views()), - use_exact_Jacobian_now(exact) - {} +JacobianForIntBP::JacobianForIntBP(const shared_ptr proj_data_info_sptr, bool exact) -const char * const -BackProjectorByBinUsingInterpolation::registered_name = - "Interpolation"; + : R2(square(proj_data_info_sptr->get_ring_radius())), dxy2(square(proj_data_info_sptr->get_tangential_sampling())), + ring_spacing2(square(proj_data_info_sptr->get_ring_spacing())), + backprojection_normalisation(proj_data_info_sptr->get_ring_spacing() / 2 / proj_data_info_sptr->get_num_views()), + use_exact_Jacobian_now(exact) {} +const char* const BackProjectorByBinUsingInterpolation::registered_name = "Interpolation"; void -BackProjectorByBinUsingInterpolation:: -set_defaults() -{ +BackProjectorByBinUsingInterpolation::set_defaults() { use_piecewise_linear_interpolation_now = true; use_exact_Jacobian_now = true; @@ -80,17 +71,14 @@ set_defaults() // next 2 can be set to false but are ignored anyway do_symmetry_swap_s = true; do_symmetry_shift_z = true; - } void -BackProjectorByBinUsingInterpolation:: -initialise_keymap() -{ +BackProjectorByBinUsingInterpolation::initialise_keymap() { parser.add_start_key("Back Projector Using Interpolation Parameters"); parser.add_stop_key("End Back Projector Using Interpolation Parameters"); parser.add_key("use_piecewise_linear_interpolation", &use_piecewise_linear_interpolation_now); - parser.add_key("use_exact_Jacobian",&use_exact_Jacobian_now); + parser.add_key("use_exact_Jacobian", &use_exact_Jacobian_now); #ifdef STIR_DEVEL // see set_defaults() parser.add_key("do_symmetry_90degrees_min_phi", &do_symmetry_90degrees_min_phi); @@ -101,29 +89,24 @@ initialise_keymap() #endif } -const DataSymmetriesForViewSegmentNumbers * - BackProjectorByBinUsingInterpolation::get_symmetries_used() const -{ +const DataSymmetriesForViewSegmentNumbers* +BackProjectorByBinUsingInterpolation::get_symmetries_used() const { if (!this->_already_set_up) error("BackProjectorByBin method called without calling set_up first."); return symmetries_ptr.get(); } -BackProjectorByBinUsingInterpolation:: -BackProjectorByBinUsingInterpolation(const bool use_piecewise_linear_interpolation, - const bool use_exact_Jacobian) -{ +BackProjectorByBinUsingInterpolation::BackProjectorByBinUsingInterpolation(const bool use_piecewise_linear_interpolation, + const bool use_exact_Jacobian) { set_defaults(); use_piecewise_linear_interpolation_now = use_piecewise_linear_interpolation; use_exact_Jacobian_now = use_exact_Jacobian; } -BackProjectorByBinUsingInterpolation:: -BackProjectorByBinUsingInterpolation(shared_ptr const& proj_data_info_ptr, - shared_ptr > const& image_info_ptr, - const bool use_piecewise_linear_interpolation, - const bool use_exact_Jacobian) -{ +BackProjectorByBinUsingInterpolation::BackProjectorByBinUsingInterpolation( + shared_ptr const& proj_data_info_ptr, + shared_ptr> const& image_info_ptr, const bool use_piecewise_linear_interpolation, + const bool use_exact_Jacobian) { set_defaults(); use_piecewise_linear_interpolation_now = use_piecewise_linear_interpolation; use_exact_Jacobian_now = use_exact_Jacobian; @@ -132,21 +115,16 @@ BackProjectorByBinUsingInterpolation(shared_ptr const& proj_ void BackProjectorByBinUsingInterpolation::set_up(shared_ptr const& proj_data_info_ptr, - shared_ptr > const& image_info_ptr) -{ + shared_ptr> const& image_info_ptr) { BackProjectorByBin::set_up(proj_data_info_ptr, image_info_ptr); - this->symmetries_ptr. - reset(new DataSymmetriesForBins_PET_CartesianGrid(proj_data_info_ptr, image_info_ptr, - do_symmetry_90degrees_min_phi, - do_symmetry_180degrees_min_phi, - do_symmetry_swap_segment, - do_symmetry_swap_s, - do_symmetry_shift_z)); + this->symmetries_ptr.reset(new DataSymmetriesForBins_PET_CartesianGrid( + proj_data_info_ptr, image_info_ptr, do_symmetry_90degrees_min_phi, do_symmetry_180degrees_min_phi, do_symmetry_swap_segment, + do_symmetry_swap_s, do_symmetry_shift_z)); - // check if data are according to what we can handle + // check if data are according to what we can handle - const VoxelsOnCartesianGrid * vox_image_info_ptr = - dynamic_cast*> (image_info_ptr.get()); + const VoxelsOnCartesianGrid* vox_image_info_ptr = + dynamic_cast*>(image_info_ptr.get()); if (vox_image_info_ptr == NULL) error("BackProjectorByBinUsingInterpolation initialised with a wrong type of DiscretisedDensity\n"); @@ -154,8 +132,7 @@ BackProjectorByBinUsingInterpolation::set_up(shared_ptr cons const CartesianCoordinate3D voxel_size = vox_image_info_ptr->get_voxel_size(); // z_origin_in_planes should be an integer - const float z_origin_in_planes = - image_info_ptr->get_origin().z()/voxel_size.z(); + const float z_origin_in_planes = image_info_ptr->get_origin().z() / voxel_size.z(); if (fabs(round(z_origin_in_planes) - z_origin_in_planes) > 1.E-4) error("BackProjectorByBinUsingInterpolation: the shift in the " "z-direction of the origin (which is %g) should be a multiple of the plane " @@ -163,300 +140,222 @@ BackProjectorByBinUsingInterpolation::set_up(shared_ptr cons image_info_ptr->get_origin().z(), voxel_size.z()); // num_planes_per_axial_pos should currently be an integer - for (int segment_num = proj_data_info_ptr->get_min_segment_num(); - segment_num <= proj_data_info_ptr->get_max_segment_num(); - ++segment_num) - { - const float num_planes_per_axial_pos = - symmetries_ptr->get_num_planes_per_axial_pos(segment_num); + for (int segment_num = proj_data_info_ptr->get_min_segment_num(); segment_num <= proj_data_info_ptr->get_max_segment_num(); + ++segment_num) { + const float num_planes_per_axial_pos = symmetries_ptr->get_num_planes_per_axial_pos(segment_num); if (fabs(round(num_planes_per_axial_pos) - num_planes_per_axial_pos) > 1.E-4) error("BackProjectorByBinUsingInterpolation: the number of image planes " "per axial_pos (which is %g for segment %d) should be an integer\n", - num_planes_per_axial_pos, segment_num); + num_planes_per_axial_pos, segment_num); } - - } void -BackProjectorByBinUsingInterpolation:: -use_exact_Jacobian(const bool use_exact_Jacobian) -{ +BackProjectorByBinUsingInterpolation::use_exact_Jacobian(const bool use_exact_Jacobian) { use_exact_Jacobian_now = use_exact_Jacobian; } BackProjectorByBinUsingInterpolation* -BackProjectorByBinUsingInterpolation:: -clone() const -{ - return new BackProjectorByBinUsingInterpolation(*this); +BackProjectorByBinUsingInterpolation::clone() const { + return new BackProjectorByBinUsingInterpolation(*this); } void -BackProjectorByBinUsingInterpolation:: -use_piecewise_linear_interpolation(const bool use_piecewise_linear_interpolation) -{ +BackProjectorByBinUsingInterpolation::use_piecewise_linear_interpolation(const bool use_piecewise_linear_interpolation) { use_piecewise_linear_interpolation_now = use_piecewise_linear_interpolation; } -void BackProjectorByBinUsingInterpolation:: -actual_back_project(DiscretisedDensity<3,float>& density, - const RelatedViewgrams& viewgrams, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) +void +BackProjectorByBinUsingInterpolation::actual_back_project(DiscretisedDensity<3, float>& density, + const RelatedViewgrams& viewgrams, const int min_axial_pos_num, + const int max_axial_pos_num, const int min_tangential_pos_num, + const int max_tangential_pos_num) { const shared_ptr proj_data_info_cyl_sptr = - dynamic_pointer_cast (viewgrams.get_proj_data_info_sptr()); - + dynamic_pointer_cast(viewgrams.get_proj_data_info_sptr()); - if (is_null_ptr(proj_data_info_cyl_sptr)) - { + if (is_null_ptr(proj_data_info_cyl_sptr)) { error("\nBackProjectorByBinUsingInterpolation:\n" - "can only handle arc-corrected data (cast to ProjDataInfoCylindricalArcCorr)!\n"); + "can only handle arc-corrected data (cast to ProjDataInfoCylindricalArcCorr)!\n"); } // this will throw an exception when the cast does not work - VoxelsOnCartesianGrid& image = - dynamic_cast&>(density); + VoxelsOnCartesianGrid& image = dynamic_cast&>(density); // TODO somehow check symmetry object in RelatedViewgrams - const float zoom = - proj_data_info_cyl_sptr->get_tangential_sampling()/ - image.get_voxel_size().x(); + const float zoom = proj_data_info_cyl_sptr->get_tangential_sampling() / image.get_voxel_size().x(); // zoom the viewgrams if necessary // if zoom==1 there's no need for allocation of a new // RelatedViewgrams object, so we do some trickery with a pointer const RelatedViewgrams* zoomed_viewgrams_ptr = 0; // to make it exception-proof we need to use an unique_ptr or shared_ptr - shared_ptr > zoomed_viewgrams_sptr; + shared_ptr> zoomed_viewgrams_sptr; int zoomed_min_tangential_pos_num; int zoomed_max_tangential_pos_num; // warning: this criterion has to be the same as the error-check in x,y voxel size // (see lines around warning message 'must be equal to'... which occurs more than once) - if (fabs(zoom-1) > 1E-4) - { - zoomed_min_tangential_pos_num = - static_cast(ceil(min_tangential_pos_num*zoom)); - zoomed_max_tangential_pos_num = - static_cast(ceil(max_tangential_pos_num*zoom)); - // store it in a shared_ptr, such that it gets cleaned up correctly - zoomed_viewgrams_sptr.reset(new RelatedViewgrams(viewgrams)); - zoomed_viewgrams_ptr = zoomed_viewgrams_sptr.get(); - - zoom_viewgrams(*zoomed_viewgrams_sptr, zoom, - zoomed_min_tangential_pos_num, zoomed_max_tangential_pos_num); - } - else - { - zoomed_min_tangential_pos_num = - min_tangential_pos_num; - zoomed_max_tangential_pos_num = - max_tangential_pos_num; - // we cannot use the unique_ptr here, as that would try to free the - // viewgrams object - zoomed_viewgrams_ptr = &viewgrams; - } + if (fabs(zoom - 1) > 1E-4) { + zoomed_min_tangential_pos_num = static_cast(ceil(min_tangential_pos_num * zoom)); + zoomed_max_tangential_pos_num = static_cast(ceil(max_tangential_pos_num * zoom)); + // store it in a shared_ptr, such that it gets cleaned up correctly + zoomed_viewgrams_sptr.reset(new RelatedViewgrams(viewgrams)); + zoomed_viewgrams_ptr = zoomed_viewgrams_sptr.get(); + + zoom_viewgrams(*zoomed_viewgrams_sptr, zoom, zoomed_min_tangential_pos_num, zoomed_max_tangential_pos_num); + } else { + zoomed_min_tangential_pos_num = min_tangential_pos_num; + zoomed_max_tangential_pos_num = max_tangential_pos_num; + // we cannot use the unique_ptr here, as that would try to free the + // viewgrams object + zoomed_viewgrams_ptr = &viewgrams; + } const int num_views = viewgrams.get_proj_data_info_sptr()->get_num_views(); RelatedViewgrams::const_iterator r_viewgrams_iter = zoomed_viewgrams_ptr->begin(); - if (zoomed_viewgrams_ptr->get_basic_segment_num() == 0) - { - // no segment symmetry - const Viewgram & pos_view =*r_viewgrams_iter; - const Viewgram neg_view = pos_view.get_empty_copy(); - - if (zoomed_viewgrams_ptr->get_num_viewgrams() == 1) - { - const Viewgram pos_plus90 = pos_view.get_empty_copy(); - const Viewgram& neg_plus90 = pos_plus90; - back_project_view_plus_90_and_delta( - image, - pos_view, neg_view, pos_plus90, neg_plus90, - min_axial_pos_num, max_axial_pos_num, - zoomed_min_tangential_pos_num, zoomed_max_tangential_pos_num); - } - else - { - r_viewgrams_iter++; - if (zoomed_viewgrams_ptr->get_num_viewgrams() == 2) - { - if (r_viewgrams_iter->get_view_num() == pos_view.get_view_num() + num_views/2) - { - const Viewgram & pos_plus90 =*r_viewgrams_iter; - const Viewgram neg_plus90 = pos_plus90.get_empty_copy(); - assert(pos_plus90.get_view_num() == num_views / 2 + pos_view.get_view_num()); - back_project_view_plus_90_and_delta( - image, - pos_view, neg_view, pos_plus90, neg_plus90, - min_axial_pos_num, max_axial_pos_num, - zoomed_min_tangential_pos_num, zoomed_max_tangential_pos_num); - } - else if (r_viewgrams_iter->get_view_num() == num_views-pos_view.get_view_num()) - { - assert(zoomed_viewgrams_ptr->get_basic_view_num() != 0); - const Viewgram & pos_min180 =*r_viewgrams_iter; - const Viewgram neg_min180 = pos_min180.get_empty_copy(); - const Viewgram& pos_plus90 =neg_min180;// anything 0 really - const Viewgram& neg_plus90 = pos_plus90; - const Viewgram& pos_min90 =neg_min180;// anything 0 really - const Viewgram& neg_min90 = pos_min90; - - assert(pos_min180.get_view_num() == num_views - pos_view.get_view_num()); - - back_project_all_symmetries( - image, - pos_view, neg_view, pos_plus90, neg_plus90, - pos_min180, neg_min180, pos_min90, neg_min90, - min_axial_pos_num, max_axial_pos_num, - zoomed_min_tangential_pos_num, zoomed_max_tangential_pos_num); - } - else - { - error("BackProjectorByBinUsingInterpolation: back_project called with RelatedViewgrams with inconsistent views"); - } - } - else - { - assert(zoomed_viewgrams_ptr->get_basic_view_num() != 0); - assert(zoomed_viewgrams_ptr->get_basic_view_num() != num_views/4); - const Viewgram & pos_plus90 =*r_viewgrams_iter; - const Viewgram neg_plus90 = pos_plus90.get_empty_copy(); - r_viewgrams_iter++;//2 - const Viewgram & pos_min180 =*r_viewgrams_iter; - r_viewgrams_iter++;//3 - const Viewgram & pos_min90 =*r_viewgrams_iter; - const Viewgram& neg_min180 = neg_plus90;//pos_min180.get_empty_copy(); - const Viewgram& neg_min90 = neg_plus90;//pos_min90.get_empty_copy(); - - assert(pos_plus90.get_view_num() == num_views / 2 + pos_view.get_view_num()); - assert(pos_min90.get_view_num() == num_views / 2 - pos_view.get_view_num()); - assert(pos_min180.get_view_num() == num_views - pos_view.get_view_num()); - - back_project_all_symmetries( - image, - pos_view, neg_view, pos_plus90, neg_plus90, - pos_min180, neg_min180, pos_min90, neg_min90, - min_axial_pos_num, max_axial_pos_num, - zoomed_min_tangential_pos_num, zoomed_max_tangential_pos_num); - } - } + if (zoomed_viewgrams_ptr->get_basic_segment_num() == 0) { + // no segment symmetry + const Viewgram& pos_view = *r_viewgrams_iter; + const Viewgram neg_view = pos_view.get_empty_copy(); + + if (zoomed_viewgrams_ptr->get_num_viewgrams() == 1) { + const Viewgram pos_plus90 = pos_view.get_empty_copy(); + const Viewgram& neg_plus90 = pos_plus90; + back_project_view_plus_90_and_delta(image, pos_view, neg_view, pos_plus90, neg_plus90, min_axial_pos_num, max_axial_pos_num, + zoomed_min_tangential_pos_num, zoomed_max_tangential_pos_num); + } else { + r_viewgrams_iter++; + if (zoomed_viewgrams_ptr->get_num_viewgrams() == 2) { + if (r_viewgrams_iter->get_view_num() == pos_view.get_view_num() + num_views / 2) { + const Viewgram& pos_plus90 = *r_viewgrams_iter; + const Viewgram neg_plus90 = pos_plus90.get_empty_copy(); + assert(pos_plus90.get_view_num() == num_views / 2 + pos_view.get_view_num()); + back_project_view_plus_90_and_delta(image, pos_view, neg_view, pos_plus90, neg_plus90, min_axial_pos_num, + max_axial_pos_num, zoomed_min_tangential_pos_num, zoomed_max_tangential_pos_num); + } else if (r_viewgrams_iter->get_view_num() == num_views - pos_view.get_view_num()) { + assert(zoomed_viewgrams_ptr->get_basic_view_num() != 0); + const Viewgram& pos_min180 = *r_viewgrams_iter; + const Viewgram neg_min180 = pos_min180.get_empty_copy(); + const Viewgram& pos_plus90 = neg_min180; // anything 0 really + const Viewgram& neg_plus90 = pos_plus90; + const Viewgram& pos_min90 = neg_min180; // anything 0 really + const Viewgram& neg_min90 = pos_min90; + + assert(pos_min180.get_view_num() == num_views - pos_view.get_view_num()); + + back_project_all_symmetries(image, pos_view, neg_view, pos_plus90, neg_plus90, pos_min180, neg_min180, pos_min90, + neg_min90, min_axial_pos_num, max_axial_pos_num, zoomed_min_tangential_pos_num, + zoomed_max_tangential_pos_num); + } else { + error("BackProjectorByBinUsingInterpolation: back_project called with RelatedViewgrams with inconsistent views"); + } + } else { + assert(zoomed_viewgrams_ptr->get_basic_view_num() != 0); + assert(zoomed_viewgrams_ptr->get_basic_view_num() != num_views / 4); + const Viewgram& pos_plus90 = *r_viewgrams_iter; + const Viewgram neg_plus90 = pos_plus90.get_empty_copy(); + r_viewgrams_iter++; // 2 + const Viewgram& pos_min180 = *r_viewgrams_iter; + r_viewgrams_iter++; // 3 + const Viewgram& pos_min90 = *r_viewgrams_iter; + const Viewgram& neg_min180 = neg_plus90; // pos_min180.get_empty_copy(); + const Viewgram& neg_min90 = neg_plus90; // pos_min90.get_empty_copy(); + + assert(pos_plus90.get_view_num() == num_views / 2 + pos_view.get_view_num()); + assert(pos_min90.get_view_num() == num_views / 2 - pos_view.get_view_num()); + assert(pos_min180.get_view_num() == num_views - pos_view.get_view_num()); + + back_project_all_symmetries(image, pos_view, neg_view, pos_plus90, neg_plus90, pos_min180, neg_min180, pos_min90, + neg_min90, min_axial_pos_num, max_axial_pos_num, zoomed_min_tangential_pos_num, + zoomed_max_tangential_pos_num); + } } - else - { - // segment symmetry + } else { + // segment symmetry + + if (zoomed_viewgrams_ptr->get_num_viewgrams() == 1) + error("BackProjectorByBinUsingInterpolation: back_project called with RelatedViewgrams with unexpect number of related " + "viewgrams"); + + const Viewgram& pos_view = *r_viewgrams_iter; // 0 + r_viewgrams_iter++; + const Viewgram& neg_view = *r_viewgrams_iter; // 1 + assert(neg_view.get_view_num() == pos_view.get_view_num()); + + if (zoomed_viewgrams_ptr->get_num_viewgrams() == 2) { + const Viewgram pos_plus90 = pos_view.get_empty_copy(); + const Viewgram& neg_plus90 = pos_plus90; + back_project_view_plus_90_and_delta(image, pos_view, neg_view, pos_plus90, neg_plus90, min_axial_pos_num, max_axial_pos_num, + zoomed_min_tangential_pos_num, zoomed_max_tangential_pos_num); + } else if (zoomed_viewgrams_ptr->get_num_viewgrams() == 4) { + r_viewgrams_iter++; - if (zoomed_viewgrams_ptr->get_num_viewgrams() == 1) - error("BackProjectorByBinUsingInterpolation: back_project called with RelatedViewgrams with unexpect number of related viewgrams"); + if (r_viewgrams_iter->get_view_num() == pos_view.get_view_num() + num_views / 2) { + const Viewgram& pos_plus90 = *r_viewgrams_iter; // 2 + r_viewgrams_iter++; + const Viewgram& neg_plus90 = *r_viewgrams_iter; // 3 + + assert(pos_plus90.get_view_num() == num_views / 2 + pos_view.get_view_num()); + assert(neg_plus90.get_view_num() == num_views / 2 + pos_view.get_view_num()); + back_project_view_plus_90_and_delta(image, pos_view, neg_view, pos_plus90, neg_plus90, min_axial_pos_num, + max_axial_pos_num, zoomed_min_tangential_pos_num, zoomed_max_tangential_pos_num); + } else if (r_viewgrams_iter->get_view_num() == num_views - pos_view.get_view_num()) { + assert(zoomed_viewgrams_ptr->get_basic_view_num() != 0); + const Viewgram& pos_min180 = *r_viewgrams_iter; // 2 + r_viewgrams_iter++; + const Viewgram& neg_min180 = *r_viewgrams_iter; // 3 + const Viewgram& pos_plus90 = pos_view.get_empty_copy(); // anything 0 really + const Viewgram& neg_plus90 = pos_plus90; + const Viewgram& pos_min90 = pos_plus90; + const Viewgram& neg_min90 = pos_plus90; + + assert(pos_min180.get_view_num() == num_views - pos_view.get_view_num()); + assert(neg_min180.get_view_num() == num_views - pos_view.get_view_num()); + + back_project_all_symmetries(image, pos_view, neg_view, pos_plus90, neg_plus90, pos_min180, neg_min180, pos_min90, + neg_min90, min_axial_pos_num, max_axial_pos_num, zoomed_min_tangential_pos_num, + zoomed_max_tangential_pos_num); + } else { + error("BackProjectorByBinUsingInterpolation: back_project called with RelatedViewgrams with inconsistent views"); + } - const Viewgram & pos_view = *r_viewgrams_iter;//0 + } else if (zoomed_viewgrams_ptr->get_num_viewgrams() == 8) { + assert(zoomed_viewgrams_ptr->get_basic_view_num() != 0); + assert(zoomed_viewgrams_ptr->get_basic_view_num() != num_views / 4); r_viewgrams_iter++; - const Viewgram & neg_view = *r_viewgrams_iter;//1 + const Viewgram& pos_plus90 = *r_viewgrams_iter; // 2 + r_viewgrams_iter++; + const Viewgram& neg_plus90 = *r_viewgrams_iter; // 3 + r_viewgrams_iter++; // 4 + const Viewgram& pos_min180 = *r_viewgrams_iter; + r_viewgrams_iter++; // 5 + const Viewgram& neg_min180 = *r_viewgrams_iter; + r_viewgrams_iter++; // 6 + const Viewgram& pos_min90 = *r_viewgrams_iter; + r_viewgrams_iter++; // 7 + const Viewgram& neg_min90 = *r_viewgrams_iter; + + assert(pos_plus90.get_view_num() == num_views / 2 + pos_view.get_view_num()); + assert(pos_min90.get_view_num() == num_views / 2 - pos_view.get_view_num()); + assert(pos_min180.get_view_num() == num_views - pos_view.get_view_num()); assert(neg_view.get_view_num() == pos_view.get_view_num()); - - if (zoomed_viewgrams_ptr->get_num_viewgrams() == 2) - { - const Viewgram pos_plus90 = pos_view.get_empty_copy(); - const Viewgram& neg_plus90 = pos_plus90; - back_project_view_plus_90_and_delta( - image, - pos_view, neg_view, pos_plus90, neg_plus90, - min_axial_pos_num, max_axial_pos_num, - zoomed_min_tangential_pos_num, zoomed_max_tangential_pos_num); - } - else if (zoomed_viewgrams_ptr->get_num_viewgrams() == 4) - { - r_viewgrams_iter++; - - if (r_viewgrams_iter->get_view_num() == pos_view.get_view_num() + num_views/2) - { - const Viewgram & pos_plus90 =*r_viewgrams_iter;//2 - r_viewgrams_iter++; - const Viewgram & neg_plus90 =*r_viewgrams_iter;//3 - - assert(pos_plus90.get_view_num() == num_views / 2 + pos_view.get_view_num()); - assert(neg_plus90.get_view_num() == num_views / 2 + pos_view.get_view_num()); - back_project_view_plus_90_and_delta( - image, - pos_view, neg_view, pos_plus90, neg_plus90, - min_axial_pos_num, max_axial_pos_num, - zoomed_min_tangential_pos_num, zoomed_max_tangential_pos_num); - } - else if (r_viewgrams_iter->get_view_num() == num_views-pos_view.get_view_num()) - { - assert(zoomed_viewgrams_ptr->get_basic_view_num() != 0); - const Viewgram & pos_min180 =*r_viewgrams_iter; //2 - r_viewgrams_iter++; - const Viewgram & neg_min180 =*r_viewgrams_iter;//3 - const Viewgram& pos_plus90 =pos_view.get_empty_copy();// anything 0 really - const Viewgram& neg_plus90 = pos_plus90; - const Viewgram& pos_min90 = pos_plus90; - const Viewgram& neg_min90 = pos_plus90; - - assert(pos_min180.get_view_num() == num_views - pos_view.get_view_num()); - assert(neg_min180.get_view_num() == num_views - pos_view.get_view_num()); - - back_project_all_symmetries( - image, - pos_view, neg_view, pos_plus90, neg_plus90, - pos_min180, neg_min180, pos_min90, neg_min90, - min_axial_pos_num, max_axial_pos_num, - zoomed_min_tangential_pos_num, zoomed_max_tangential_pos_num); - } - else - { - error("BackProjectorByBinUsingInterpolation: back_project called with RelatedViewgrams with inconsistent views"); - } - - } - else if (zoomed_viewgrams_ptr->get_num_viewgrams() == 8) - { - assert(zoomed_viewgrams_ptr->get_basic_view_num() != 0); - assert(zoomed_viewgrams_ptr->get_basic_view_num() != num_views/4); - r_viewgrams_iter++; - const Viewgram & pos_plus90 =*r_viewgrams_iter;//2 - r_viewgrams_iter++; - const Viewgram & neg_plus90 =*r_viewgrams_iter;//3 - r_viewgrams_iter++;//4 - const Viewgram & pos_min180 =*r_viewgrams_iter; - r_viewgrams_iter++;//5 - const Viewgram & neg_min180 =*r_viewgrams_iter; - r_viewgrams_iter++;//6 - const Viewgram & pos_min90 =*r_viewgrams_iter; - r_viewgrams_iter++;//7 - const Viewgram & neg_min90 =*r_viewgrams_iter; - - assert(pos_plus90.get_view_num() == num_views / 2 + pos_view.get_view_num()); - assert(pos_min90.get_view_num() == num_views / 2 - pos_view.get_view_num()); - assert(pos_min180.get_view_num() == num_views - pos_view.get_view_num()); - assert(neg_view.get_view_num() == pos_view.get_view_num()); - assert(neg_plus90.get_view_num() == pos_plus90.get_view_num()); - assert(neg_min90.get_view_num() == pos_min90.get_view_num()); - assert(neg_min180.get_view_num() == pos_min180.get_view_num()); - - back_project_all_symmetries( - image, - pos_view, neg_view, pos_plus90, neg_plus90, - pos_min180, neg_min180, pos_min90, neg_min90, - min_axial_pos_num, max_axial_pos_num, - zoomed_min_tangential_pos_num, zoomed_max_tangential_pos_num); - - } - } + assert(neg_plus90.get_view_num() == pos_plus90.get_view_num()); + assert(neg_min90.get_view_num() == pos_min90.get_view_num()); + assert(neg_min180.get_view_num() == pos_min180.get_view_num()); + back_project_all_symmetries(image, pos_view, neg_view, pos_plus90, neg_plus90, pos_min180, neg_min180, pos_min90, neg_min90, + min_axial_pos_num, max_axial_pos_num, zoomed_min_tangential_pos_num, + zoomed_max_tangential_pos_num); + } + } } - void -BackProjectorByBinUsingInterpolation:: -actual_back_project(DiscretisedDensity<3,float>&, - const Bin&) -{ - error("BackProjectorByBinUsingInterpolation is not supported for list-mode reconstruction. Abort."); +BackProjectorByBinUsingInterpolation::actual_back_project(DiscretisedDensity<3, float>&, const Bin&) { + error("BackProjectorByBinUsingInterpolation is not supported for list-mode reconstruction. Abort."); } - #if 0 /****************************************************************************** 2D @@ -580,444 +479,363 @@ BackProjectorByBinUsingInterpolation:: #endif - - /**************************************************************************** real work ****************************************************************************/ - /* - The version which uses all possible symmetries. - Here 0<=view < num_views/4 (= 45 degrees) - */ +/* + The version which uses all possible symmetries. + Here 0<=view < num_views/4 (= 45 degrees) + */ -void +void BackProjectorByBinUsingInterpolation::back_project_all_symmetries( - VoxelsOnCartesianGrid& image, - const Viewgram & pos_view, - const Viewgram & neg_view, - const Viewgram & pos_plus90, - const Viewgram & neg_plus90, - const Viewgram & pos_min180, - const Viewgram & neg_min180, - const Viewgram & pos_min90, - const Viewgram & neg_min90, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) -{ + VoxelsOnCartesianGrid& image, const Viewgram& pos_view, const Viewgram& neg_view, + const Viewgram& pos_plus90, const Viewgram& neg_plus90, const Viewgram& pos_min180, + const Viewgram& neg_min180, const Viewgram& pos_min90, const Viewgram& neg_min90, + const int min_axial_pos_num, const int max_axial_pos_num, const int min_tangential_pos_num, + const int max_tangential_pos_num) { const shared_ptr proj_data_info_cyl_sptr = - dynamic_pointer_cast (pos_view.get_proj_data_info_sptr()); - + dynamic_pointer_cast(pos_view.get_proj_data_info_sptr()); - if (is_null_ptr(proj_data_info_cyl_sptr)) - { + if (is_null_ptr(proj_data_info_cyl_sptr)) { error("\nBackProjectorByBinUsingInterpolation:\n\ can only handle arc-corrected data (cast to ProjDataInfoCylindricalArcCorr)!\n"); } - assert(min_axial_pos_num >= pos_view. get_min_axial_pos_num()); - assert(max_axial_pos_num <= pos_view. get_max_axial_pos_num()); + assert(min_axial_pos_num >= pos_view.get_min_axial_pos_num()); + assert(max_axial_pos_num <= pos_view.get_max_axial_pos_num()); assert(min_tangential_pos_num >= pos_view.get_min_tangential_pos_num()); assert(max_tangential_pos_num <= pos_view.get_max_tangential_pos_num()); - //KTxxx not necessary anymore - //assert(min_tangential_pos_num == - max_tangential_pos_num); + // KTxxx not necessary anymore + // assert(min_tangential_pos_num == - max_tangential_pos_num); #ifndef NDEBUG - // This variable is only used in assert() at the moment, so avoid compiler + // This variable is only used in assert() at the moment, so avoid compiler // warning by defining it only when in debug mode const int segment_num = pos_view.get_segment_num(); #endif - - assert(proj_data_info_cyl_sptr ->get_average_ring_difference(segment_num) >= 0); + assert(proj_data_info_cyl_sptr->get_average_ring_difference(segment_num) >= 0); assert(pos_view.get_view_num() > 0); - assert(pos_view.get_view_num() < pos_view.get_proj_data_info_sptr()->get_num_views()/4 || - (!symmetries_ptr->using_symmetry_90degrees_min_phi() && - pos_view.get_view_num() < pos_view.get_proj_data_info_sptr()->get_num_views()/2 && - pos_plus90.find_max()==0 && neg_plus90.find_max()==0 && - pos_min90.find_max()==0 && neg_min90.find_max()==0) ); + assert(pos_view.get_view_num() < pos_view.get_proj_data_info_sptr()->get_num_views() / 4 || + (!symmetries_ptr->using_symmetry_90degrees_min_phi() && + pos_view.get_view_num() < pos_view.get_proj_data_info_sptr()->get_num_views() / 2 && pos_plus90.find_max() == 0 && + neg_plus90.find_max() == 0 && pos_min90.find_max() == 0 && neg_min90.find_max() == 0)); const int nviews = pos_view.get_proj_data_info_sptr()->get_num_views(); // warning: error check has to be the same as what is used for the criterion to do the zooming // (see lines concerning zoomed_viewgrams) - if(fabs(image.get_voxel_size().x()/proj_data_info_cyl_sptr->get_tangential_sampling() - 1) > 1E-4 - || fabs(image.get_voxel_size().y()/proj_data_info_cyl_sptr->get_tangential_sampling() - 1) > 1E-4) + if (fabs(image.get_voxel_size().x() / proj_data_info_cyl_sptr->get_tangential_sampling() - 1) > 1E-4 || + fabs(image.get_voxel_size().y() / proj_data_info_cyl_sptr->get_tangential_sampling() - 1) > 1E-4) error("BackProjectorByBinUsingInterpolation: x,y voxel size must be equal to bin size."); - + // KTxxx not necessary anymore - //assert(image.get_min_z() == 0); + // assert(image.get_min_z() == 0); - if (pos_view.get_view_num() == 0) - { - error("BackProjectorByBinUsingInterpolation: back_project_all_symmetries called with view 0 degrees.\n"); - } - if (symmetries_ptr->using_symmetry_90degrees_min_phi() && - pos_view.get_view_num() == nviews/4) - { - error("BackProjectorByBinUsingInterpolation: back_project_all_symmetries called with view 45 degrees.\n"); - } + if (pos_view.get_view_num() == 0) { + error("BackProjectorByBinUsingInterpolation: back_project_all_symmetries called with view 0 degrees.\n"); + } + if (symmetries_ptr->using_symmetry_90degrees_min_phi() && pos_view.get_view_num() == nviews / 4) { + error("BackProjectorByBinUsingInterpolation: back_project_all_symmetries called with view 45 degrees.\n"); + } // KT XXX - const float fovrad_in_mm = - min((min(image.get_max_x(), -image.get_min_x()))*image.get_voxel_size().x(), - (min(image.get_max_y(), -image.get_min_y()))*image.get_voxel_size().y()); - const int fovrad = round(fovrad_in_mm/image.get_voxel_size().x()); + const float fovrad_in_mm = min((min(image.get_max_x(), -image.get_min_x())) * image.get_voxel_size().x(), + (min(image.get_max_y(), -image.get_min_y())) * image.get_voxel_size().y()); + const int fovrad = round(fovrad_in_mm / image.get_voxel_size().x()); // TODO remove -2, it's there because otherwise find_start_values() goes crazy. - const int max_tang_pos_to_use = - min(max_tangential_pos_num, fovrad-2); - const int min_tang_pos_to_use = - max(min_tangential_pos_num, -(fovrad-2)); - - const int max_abs_tang_pos_to_use = - max(max_tang_pos_to_use, -min_tang_pos_to_use); - const int min_abs_tang_pos_to_use = - max_tang_pos_to_use<0 ? - -max_tang_pos_to_use - : (min_tang_pos_to_use>0 ? - min_tang_pos_to_use - : 0 ); + const int max_tang_pos_to_use = min(max_tangential_pos_num, fovrad - 2); + const int min_tang_pos_to_use = max(min_tangential_pos_num, -(fovrad - 2)); + const int max_abs_tang_pos_to_use = max(max_tang_pos_to_use, -min_tang_pos_to_use); + const int min_abs_tang_pos_to_use = + max_tang_pos_to_use < 0 ? -max_tang_pos_to_use : (min_tang_pos_to_use > 0 ? min_tang_pos_to_use : 0); start_timers(); const JacobianForIntBP jacobian(proj_data_info_cyl_sptr, use_exact_Jacobian_now); - Array<4, float > Proj2424(IndexRange4D(0, 1, 0, 3, 0, 1, 1, 4)); + Array<4, float> Proj2424(IndexRange4D(0, 1, 0, 3, 0, 1, 1, 4)); // a variable which will be used in the loops over s to get s_in_mm - Bin bin(pos_view.get_segment_num(), pos_view.get_view_num(),min_axial_pos_num,pos_view.get_timing_pos_num(),0); + Bin bin(pos_view.get_segment_num(), pos_view.get_view_num(), min_axial_pos_num, pos_view.get_timing_pos_num(), 0); - // KT 20/06/2001 rewrite using get_phi + // KT 20/06/2001 rewrite using get_phi const float cphi = cos(proj_data_info_cyl_sptr->get_phi(bin)); const float sphi = sin(proj_data_info_cyl_sptr->get_phi(bin)); - // Do a loop over all axial positions. However, because we use interpolation of // a 'beam', each step takes elements from ax_pos and ax_pos+1. So, data in // a ring influences beam ax_pos-1 and ax_pos. All this means that we // have to let ax_pos run from min_axial_pos_num-1 to max_axial_pos_num. - for (int ax_pos = min_axial_pos_num-1; ax_pos <= max_axial_pos_num; ax_pos++) - { - const int ax_pos_plus = ax_pos + 1; - - // We have to fill with 0, as not all elements are set in the lines below - if (ax_pos==min_axial_pos_num-1 || ax_pos==max_axial_pos_num) - Proj2424.fill(0); - - for (int s = min_abs_tang_pos_to_use; s <= max_abs_tang_pos_to_use; s++) { - const int splus = s + 1; - const int ms = -s; - const int msplus = -splus; - - // now I have to check if ax_pos is in allowable range - if (ax_pos >= min_axial_pos_num) - { - Proj2424[0][0][0][1] = s>max_tang_pos_to_use ? 0 : pos_view[ax_pos][s]; - Proj2424[0][0][0][2] = splus>max_tang_pos_to_use ? 0 : pos_view[ax_pos][splus]; - Proj2424[0][1][0][3] = s>max_tang_pos_to_use ? 0 : pos_min90[ax_pos][s]; - Proj2424[0][1][0][4] = splus>max_tang_pos_to_use ? 0 : pos_min90[ax_pos][splus]; - Proj2424[0][2][0][1] = s>max_tang_pos_to_use ? 0 : pos_plus90[ax_pos][s]; - Proj2424[0][2][0][2] = splus>max_tang_pos_to_use ? 0 : pos_plus90[ax_pos][splus]; - Proj2424[0][3][0][3] = s>max_tang_pos_to_use ? 0 : pos_min180[ax_pos][s]; - Proj2424[0][3][0][4] = splus>max_tang_pos_to_use ? 0 : pos_min180[ax_pos][splus]; - Proj2424[1][0][0][3] = s>max_tang_pos_to_use ? 0 : neg_view[ax_pos][s]; - Proj2424[1][0][0][4] = splus>max_tang_pos_to_use ? 0 : neg_view[ax_pos][splus]; - Proj2424[1][1][0][1] = s>max_tang_pos_to_use ? 0 : neg_min90[ax_pos][s]; - Proj2424[1][1][0][2] = splus>max_tang_pos_to_use ? 0 : neg_min90[ax_pos][splus]; - Proj2424[1][2][0][3] = s>max_tang_pos_to_use ? 0 : neg_plus90[ax_pos][s]; - Proj2424[1][2][0][4] = splus>max_tang_pos_to_use ? 0 : neg_plus90[ax_pos][splus]; - Proj2424[1][3][0][1] = s>max_tang_pos_to_use ? 0 : neg_min180[ax_pos][s]; - Proj2424[1][3][0][2] = splus>max_tang_pos_to_use ? 0 : neg_min180[ax_pos][splus]; - - Proj2424[0][0][1][3] = msmax_tang_pos_to_use ? 0 : pos_view[ax_pos_plus][s]; - Proj2424[0][0][0][4] = splus>max_tang_pos_to_use ? 0 : pos_view[ax_pos_plus][splus]; - Proj2424[0][1][0][1] = s>max_tang_pos_to_use ? 0 : pos_min90[ax_pos_plus][s]; - Proj2424[0][1][0][2] = splus>max_tang_pos_to_use ? 0 : pos_min90[ax_pos_plus][splus]; - Proj2424[0][2][0][3] = s>max_tang_pos_to_use ? 0 : pos_plus90[ax_pos_plus][s]; - Proj2424[0][2][0][4] = splus>max_tang_pos_to_use ? 0 : pos_plus90[ax_pos_plus][splus]; - Proj2424[0][3][0][1] = s>max_tang_pos_to_use ? 0 : pos_min180[ax_pos_plus][s]; - Proj2424[0][3][0][2] = splus>max_tang_pos_to_use ? 0 : pos_min180[ax_pos_plus][splus]; - Proj2424[1][0][0][1] = s>max_tang_pos_to_use ? 0 : neg_view[ax_pos_plus][s]; - Proj2424[1][0][0][2] = splus>max_tang_pos_to_use ? 0 : neg_view[ax_pos_plus][splus]; - Proj2424[1][1][0][3] = s>max_tang_pos_to_use ? 0 : neg_min90[ax_pos_plus][s]; - Proj2424[1][1][0][4] = splus>max_tang_pos_to_use ? 0 : neg_min90[ax_pos_plus][splus]; - Proj2424[1][2][0][1] = s>max_tang_pos_to_use ? 0 : neg_plus90[ax_pos_plus][s]; - Proj2424[1][2][0][2] = splus>max_tang_pos_to_use ? 0 : neg_plus90[ax_pos_plus][splus]; - Proj2424[1][3][0][3] = s>max_tang_pos_to_use ? 0 : neg_min180[ax_pos_plus][s]; - Proj2424[1][3][0][4] = splus>max_tang_pos_to_use ? 0 : neg_min180[ax_pos_plus][splus]; - - Proj2424[0][0][1][1] = msget_average_ring_difference(segment_num); - - // take s+.5 as average for the beam (it's slowly varying in s anyway) - Proj2424 *= jacobian(delta, s+ 0.5F); - - // find correspondence between ax_pos coordinates and image coordinates: - // z = num_planes_per_axial_pos * ring + axial_pos_to_z_offset - // KT 20/06/2001 rewrote using symmetries_ptr - const int num_planes_per_axial_pos = - round(symmetries_ptr->get_num_planes_per_axial_pos(segment_num)); - const float axial_pos_to_z_offset = - symmetries_ptr->get_axial_pos_to_z_offset(segment_num); - - if (use_piecewise_linear_interpolation_now && num_planes_per_axial_pos>1) - piecewise_linear_interpolation_backproj3D_Cho_view_viewplus90_180minview_90minview - (Proj2424, - image, - proj_data_info_cyl_sptr, - delta, - cphi, sphi, s, ax_pos, - num_planes_per_axial_pos, - axial_pos_to_z_offset); - else - linear_interpolation_backproj3D_Cho_view_viewplus90_180minview_90minview - (Proj2424, - image, - proj_data_info_cyl_sptr, - delta, - cphi, sphi, s, ax_pos, - num_planes_per_axial_pos, - axial_pos_to_z_offset); + for (int ax_pos = min_axial_pos_num - 1; ax_pos <= max_axial_pos_num; ax_pos++) { + const int ax_pos_plus = ax_pos + 1; + + // We have to fill with 0, as not all elements are set in the lines below + if (ax_pos == min_axial_pos_num - 1 || ax_pos == max_axial_pos_num) + Proj2424.fill(0); + + for (int s = min_abs_tang_pos_to_use; s <= max_abs_tang_pos_to_use; s++) { + const int splus = s + 1; + const int ms = -s; + const int msplus = -splus; + + // now I have to check if ax_pos is in allowable range + if (ax_pos >= min_axial_pos_num) { + Proj2424[0][0][0][1] = s > max_tang_pos_to_use ? 0 : pos_view[ax_pos][s]; + Proj2424[0][0][0][2] = splus > max_tang_pos_to_use ? 0 : pos_view[ax_pos][splus]; + Proj2424[0][1][0][3] = s > max_tang_pos_to_use ? 0 : pos_min90[ax_pos][s]; + Proj2424[0][1][0][4] = splus > max_tang_pos_to_use ? 0 : pos_min90[ax_pos][splus]; + Proj2424[0][2][0][1] = s > max_tang_pos_to_use ? 0 : pos_plus90[ax_pos][s]; + Proj2424[0][2][0][2] = splus > max_tang_pos_to_use ? 0 : pos_plus90[ax_pos][splus]; + Proj2424[0][3][0][3] = s > max_tang_pos_to_use ? 0 : pos_min180[ax_pos][s]; + Proj2424[0][3][0][4] = splus > max_tang_pos_to_use ? 0 : pos_min180[ax_pos][splus]; + Proj2424[1][0][0][3] = s > max_tang_pos_to_use ? 0 : neg_view[ax_pos][s]; + Proj2424[1][0][0][4] = splus > max_tang_pos_to_use ? 0 : neg_view[ax_pos][splus]; + Proj2424[1][1][0][1] = s > max_tang_pos_to_use ? 0 : neg_min90[ax_pos][s]; + Proj2424[1][1][0][2] = splus > max_tang_pos_to_use ? 0 : neg_min90[ax_pos][splus]; + Proj2424[1][2][0][3] = s > max_tang_pos_to_use ? 0 : neg_plus90[ax_pos][s]; + Proj2424[1][2][0][4] = splus > max_tang_pos_to_use ? 0 : neg_plus90[ax_pos][splus]; + Proj2424[1][3][0][1] = s > max_tang_pos_to_use ? 0 : neg_min180[ax_pos][s]; + Proj2424[1][3][0][2] = splus > max_tang_pos_to_use ? 0 : neg_min180[ax_pos][splus]; + + Proj2424[0][0][1][3] = ms < min_tang_pos_to_use ? 0 : pos_view[ax_pos][ms]; + Proj2424[0][0][1][4] = msplus < min_tang_pos_to_use ? 0 : pos_view[ax_pos][msplus]; + Proj2424[0][1][1][1] = ms < min_tang_pos_to_use ? 0 : pos_min90[ax_pos][ms]; + Proj2424[0][1][1][2] = msplus < min_tang_pos_to_use ? 0 : pos_min90[ax_pos][msplus]; + Proj2424[0][2][1][3] = ms < min_tang_pos_to_use ? 0 : pos_plus90[ax_pos][ms]; + Proj2424[0][2][1][4] = msplus < min_tang_pos_to_use ? 0 : pos_plus90[ax_pos][msplus]; + Proj2424[0][3][1][1] = ms < min_tang_pos_to_use ? 0 : pos_min180[ax_pos][ms]; + Proj2424[0][3][1][2] = msplus < min_tang_pos_to_use ? 0 : pos_min180[ax_pos][msplus]; + Proj2424[1][0][1][1] = ms < min_tang_pos_to_use ? 0 : neg_view[ax_pos][ms]; + Proj2424[1][0][1][2] = msplus < min_tang_pos_to_use ? 0 : neg_view[ax_pos][msplus]; + Proj2424[1][1][1][3] = ms < min_tang_pos_to_use ? 0 : neg_min90[ax_pos][ms]; + Proj2424[1][1][1][4] = msplus < min_tang_pos_to_use ? 0 : neg_min90[ax_pos][msplus]; + Proj2424[1][2][1][1] = ms < min_tang_pos_to_use ? 0 : neg_plus90[ax_pos][ms]; + Proj2424[1][2][1][2] = msplus < min_tang_pos_to_use ? 0 : neg_plus90[ax_pos][msplus]; + Proj2424[1][3][1][3] = ms < min_tang_pos_to_use ? 0 : neg_min180[ax_pos][ms]; + Proj2424[1][3][1][4] = msplus < min_tang_pos_to_use ? 0 : neg_min180[ax_pos][msplus]; } + + if (ax_pos_plus <= max_axial_pos_num) { + Proj2424[0][0][0][3] = s > max_tang_pos_to_use ? 0 : pos_view[ax_pos_plus][s]; + Proj2424[0][0][0][4] = splus > max_tang_pos_to_use ? 0 : pos_view[ax_pos_plus][splus]; + Proj2424[0][1][0][1] = s > max_tang_pos_to_use ? 0 : pos_min90[ax_pos_plus][s]; + Proj2424[0][1][0][2] = splus > max_tang_pos_to_use ? 0 : pos_min90[ax_pos_plus][splus]; + Proj2424[0][2][0][3] = s > max_tang_pos_to_use ? 0 : pos_plus90[ax_pos_plus][s]; + Proj2424[0][2][0][4] = splus > max_tang_pos_to_use ? 0 : pos_plus90[ax_pos_plus][splus]; + Proj2424[0][3][0][1] = s > max_tang_pos_to_use ? 0 : pos_min180[ax_pos_plus][s]; + Proj2424[0][3][0][2] = splus > max_tang_pos_to_use ? 0 : pos_min180[ax_pos_plus][splus]; + Proj2424[1][0][0][1] = s > max_tang_pos_to_use ? 0 : neg_view[ax_pos_plus][s]; + Proj2424[1][0][0][2] = splus > max_tang_pos_to_use ? 0 : neg_view[ax_pos_plus][splus]; + Proj2424[1][1][0][3] = s > max_tang_pos_to_use ? 0 : neg_min90[ax_pos_plus][s]; + Proj2424[1][1][0][4] = splus > max_tang_pos_to_use ? 0 : neg_min90[ax_pos_plus][splus]; + Proj2424[1][2][0][1] = s > max_tang_pos_to_use ? 0 : neg_plus90[ax_pos_plus][s]; + Proj2424[1][2][0][2] = splus > max_tang_pos_to_use ? 0 : neg_plus90[ax_pos_plus][splus]; + Proj2424[1][3][0][3] = s > max_tang_pos_to_use ? 0 : neg_min180[ax_pos_plus][s]; + Proj2424[1][3][0][4] = splus > max_tang_pos_to_use ? 0 : neg_min180[ax_pos_plus][splus]; + + Proj2424[0][0][1][1] = ms < min_tang_pos_to_use ? 0 : pos_view[ax_pos_plus][ms]; + Proj2424[0][0][1][2] = msplus < min_tang_pos_to_use ? 0 : pos_view[ax_pos_plus][msplus]; + Proj2424[0][1][1][3] = ms < min_tang_pos_to_use ? 0 : pos_min90[ax_pos_plus][ms]; + Proj2424[0][1][1][4] = msplus < min_tang_pos_to_use ? 0 : pos_min90[ax_pos_plus][msplus]; + Proj2424[0][2][1][1] = ms < min_tang_pos_to_use ? 0 : pos_plus90[ax_pos_plus][ms]; + Proj2424[0][2][1][2] = msplus < min_tang_pos_to_use ? 0 : pos_plus90[ax_pos_plus][msplus]; + Proj2424[0][3][1][3] = ms < min_tang_pos_to_use ? 0 : pos_min180[ax_pos_plus][ms]; + Proj2424[0][3][1][4] = msplus < min_tang_pos_to_use ? 0 : pos_min180[ax_pos_plus][msplus]; + Proj2424[1][0][1][3] = ms < min_tang_pos_to_use ? 0 : neg_view[ax_pos_plus][ms]; + Proj2424[1][0][1][4] = msplus < min_tang_pos_to_use ? 0 : neg_view[ax_pos_plus][msplus]; + Proj2424[1][1][1][1] = ms < min_tang_pos_to_use ? 0 : neg_min90[ax_pos_plus][ms]; + Proj2424[1][1][1][2] = msplus < min_tang_pos_to_use ? 0 : neg_min90[ax_pos_plus][msplus]; + Proj2424[1][2][1][3] = ms < min_tang_pos_to_use ? 0 : neg_plus90[ax_pos_plus][ms]; + Proj2424[1][2][1][4] = msplus < min_tang_pos_to_use ? 0 : neg_plus90[ax_pos_plus][msplus]; + Proj2424[1][3][1][1] = ms < min_tang_pos_to_use ? 0 : neg_min180[ax_pos_plus][ms]; + Proj2424[1][3][1][2] = msplus < min_tang_pos_to_use ? 0 : neg_min180[ax_pos_plus][msplus]; + } + const int segment_num = pos_view.get_segment_num(); + + const float delta = proj_data_info_cyl_sptr->get_average_ring_difference(segment_num); + + // take s+.5 as average for the beam (it's slowly varying in s anyway) + Proj2424 *= jacobian(delta, s + 0.5F); + + // find correspondence between ax_pos coordinates and image coordinates: + // z = num_planes_per_axial_pos * ring + axial_pos_to_z_offset + // KT 20/06/2001 rewrote using symmetries_ptr + const int num_planes_per_axial_pos = round(symmetries_ptr->get_num_planes_per_axial_pos(segment_num)); + const float axial_pos_to_z_offset = symmetries_ptr->get_axial_pos_to_z_offset(segment_num); + + if (use_piecewise_linear_interpolation_now && num_planes_per_axial_pos > 1) + piecewise_linear_interpolation_backproj3D_Cho_view_viewplus90_180minview_90minview( + Proj2424, image, proj_data_info_cyl_sptr, delta, cphi, sphi, s, ax_pos, num_planes_per_axial_pos, + axial_pos_to_z_offset); + else + linear_interpolation_backproj3D_Cho_view_viewplus90_180minview_90minview(Proj2424, image, proj_data_info_cyl_sptr, delta, + cphi, sphi, s, ax_pos, num_planes_per_axial_pos, + axial_pos_to_z_offset); } + } stop_timers(); } /* This function projects 4 viewgrams related by symmetry. -It will be used for view=0 or 45 degrees +It will be used for view=0 or 45 degrees (or others if the 90degrees_min_phi symmetry is not used). Here 0<=view < num_views/2 (= 90 degrees) */ -void -BackProjectorByBinUsingInterpolation:: -back_project_view_plus_90_and_delta( - VoxelsOnCartesianGrid& image, - const Viewgram & pos_view, - const Viewgram & neg_view, - const Viewgram & pos_plus90, - const Viewgram & neg_plus90, - const int min_axial_pos_num, - const int max_axial_pos_num, - const int min_tangential_pos_num, - const int max_tangential_pos_num) -{ +void +BackProjectorByBinUsingInterpolation::back_project_view_plus_90_and_delta( + VoxelsOnCartesianGrid& image, const Viewgram& pos_view, const Viewgram& neg_view, + const Viewgram& pos_plus90, const Viewgram& neg_plus90, const int min_axial_pos_num, + const int max_axial_pos_num, const int min_tangential_pos_num, const int max_tangential_pos_num) { const shared_ptr proj_data_info_cyl_sptr = - dynamic_pointer_cast (pos_view.get_proj_data_info_sptr()); + dynamic_pointer_cast(pos_view.get_proj_data_info_sptr()); - if (is_null_ptr(proj_data_info_cyl_sptr)) - { - error("\nBackProjectorByBinUsingInterpolation:,\n\ + if (is_null_ptr(proj_data_info_cyl_sptr)) { + error("\nBackProjectorByBinUsingInterpolation:,\n\ can only handle arc-corrected data (cast to ProjDataInfoCylindricalArcCorr)!\n"); - } + } - assert(min_axial_pos_num >= pos_view. get_min_axial_pos_num()); - assert(max_axial_pos_num <= pos_view. get_max_axial_pos_num()); + assert(min_axial_pos_num >= pos_view.get_min_axial_pos_num()); + assert(max_axial_pos_num <= pos_view.get_max_axial_pos_num()); assert(min_tangential_pos_num >= pos_view.get_min_tangential_pos_num()); assert(max_tangential_pos_num <= pos_view.get_max_tangential_pos_num()); // KTXXX not necessary anymore - //assert(min_tangential_pos_num == - max_tangential_pos_num); + // assert(min_tangential_pos_num == - max_tangential_pos_num); #ifndef NDEBUG - // These variables are only used in assert() at the moment, so avoid compiler + // These variables are only used in assert() at the moment, so avoid compiler // warning by defining it only when in debug mode const int segment_num = pos_view.get_segment_num(); #endif - assert(proj_data_info_cyl_sptr ->get_average_ring_difference(segment_num) >= 0); + assert(proj_data_info_cyl_sptr->get_average_ring_difference(segment_num) >= 0); - const int num_views = pos_view.get_proj_data_info_sptr()->get_num_views(); - - assert(pos_view.get_view_num()>=0); - assert(pos_view.get_view_num() get_num_views(); + assert(pos_view.get_view_num() >= 0); + assert(pos_view.get_view_num() < num_views / 2 || + (pos_view.get_view_num() < num_views && pos_plus90.find_max() == 0 && neg_plus90.find_max() == 0)); // warning: error check has to be the same as what is used for the criterion to do the zooming // (see lines concerning zoomed_viewgrams) - if(fabs(image.get_voxel_size().x()/proj_data_info_cyl_sptr->get_tangential_sampling() - 1) > 1E-4 - || fabs(image.get_voxel_size().y()/proj_data_info_cyl_sptr->get_tangential_sampling() - 1) > 1E-4) + if (fabs(image.get_voxel_size().x() / proj_data_info_cyl_sptr->get_tangential_sampling() - 1) > 1E-4 || + fabs(image.get_voxel_size().y() / proj_data_info_cyl_sptr->get_tangential_sampling() - 1) > 1E-4) error("BackProjectorByBinUsingInterpolation: x,y voxel size must be equal to bin size."); // KTXXX not necessary anymore - //assert(image.get_min_z() == 0); + // assert(image.get_min_z() == 0); start_timers(); const JacobianForIntBP jacobian(proj_data_info_cyl_sptr, use_exact_Jacobian_now); - Array<4, float > Proj2424(IndexRange4D(0, 1, 0, 3, 0, 1, 1, 4)); + Array<4, float> Proj2424(IndexRange4D(0, 1, 0, 3, 0, 1, 1, 4)); // a variable which will be used in the loops over s to get s_in_mm - Bin bin(pos_view.get_segment_num(), pos_view.get_view_num(),min_axial_pos_num,0,pos_view.get_timing_pos_num()); + Bin bin(pos_view.get_segment_num(), pos_view.get_view_num(), min_axial_pos_num, 0, pos_view.get_timing_pos_num()); // compute cos(phi) and sin(phi) /* KT included special cases for phi=0 and 90 degrees to try to avoid rounding problems This is because the current low-level code has problems with e.g. cos(phi) being a very small negative number.*/ - const float cphi = - bin.view_num()==0? 1 : - 2*bin.view_num()==num_views ? 0 : - cos(proj_data_info_cyl_sptr->get_phi(bin)); - const float sphi = - bin.view_num()==0? 0 : - 2*bin.view_num()==num_views ? 1 : - sin(proj_data_info_cyl_sptr->get_phi(bin)); - - assert(fabs(cphi-cos(proj_data_info_cyl_sptr->get_phi(bin)))<.001); - assert(fabs(sphi-sin(proj_data_info_cyl_sptr->get_phi(bin)))<.001); + const float cphi = bin.view_num() == 0 ? 1 : 2 * bin.view_num() == num_views ? 0 : cos(proj_data_info_cyl_sptr->get_phi(bin)); + const float sphi = bin.view_num() == 0 ? 0 : 2 * bin.view_num() == num_views ? 1 : sin(proj_data_info_cyl_sptr->get_phi(bin)); + + assert(fabs(cphi - cos(proj_data_info_cyl_sptr->get_phi(bin))) < .001); + assert(fabs(sphi - sin(proj_data_info_cyl_sptr->get_phi(bin))) < .001); // KT XXX - const float fovrad_in_mm = - min((min(image.get_max_x(), -image.get_min_x()))*image.get_voxel_size().x(), - (min(image.get_max_y(), -image.get_min_y()))*image.get_voxel_size().y()); - const int fovrad = round(fovrad_in_mm/image.get_voxel_size().x()); + const float fovrad_in_mm = min((min(image.get_max_x(), -image.get_min_x())) * image.get_voxel_size().x(), + (min(image.get_max_y(), -image.get_min_y())) * image.get_voxel_size().y()); + const int fovrad = round(fovrad_in_mm / image.get_voxel_size().x()); // TODO remove -2, it's there because otherwise find_start_values() goes crazy. - const int max_tang_pos_to_use = - min(max_tangential_pos_num, fovrad-2); - const int min_tang_pos_to_use = - max(min_tangential_pos_num, -(fovrad-2)); + const int max_tang_pos_to_use = min(max_tangential_pos_num, fovrad - 2); + const int min_tang_pos_to_use = max(min_tangential_pos_num, -(fovrad - 2)); - - const int max_abs_tang_pos_to_use = - max(max_tang_pos_to_use, -min_tang_pos_to_use); - const int min_abs_tang_pos_to_use = - max_tang_pos_to_use<0 ? - -max_tang_pos_to_use - : (min_tang_pos_to_use>0 ? - min_tang_pos_to_use - : 0 ); + const int max_abs_tang_pos_to_use = max(max_tang_pos_to_use, -min_tang_pos_to_use); + const int min_abs_tang_pos_to_use = + max_tang_pos_to_use < 0 ? -max_tang_pos_to_use : (min_tang_pos_to_use > 0 ? min_tang_pos_to_use : 0); // Do a loop over all axial positions. However, because we use interpolation of // a 'beam', each step takes elements from ax_pos and ax_pos+1. So, data at // ax_pos influences beam ax_pos-1 and ax_pos. All this means that we // have to let ax_pos run from min_axial_pos_num-1 to max_axial_pos_num. - for (int ax_pos = min_axial_pos_num-1; ax_pos <= max_axial_pos_num; ax_pos++) - { - const int ax_pos_plus = ax_pos + 1; - - // We have to fill with 0, as not all elements are set in the lines below - if (ax_pos==min_axial_pos_num-1 || ax_pos==max_axial_pos_num) - Proj2424.fill(0); - for (int s = min_abs_tang_pos_to_use; s <= max_abs_tang_pos_to_use; s++) { - const int splus = s + 1; - const int ms = -s; - const int msplus = -splus; - - // now I have to check if ax_pos is in allowable range - if (ax_pos >= min_axial_pos_num) - { - Proj2424[0][0][0][1] = s>max_tang_pos_to_use ? 0 : pos_view[ax_pos][s]; - Proj2424[0][0][0][2] = splus>max_tang_pos_to_use ? 0 : pos_view[ax_pos][splus]; - Proj2424[0][2][0][1] = s>max_tang_pos_to_use ? 0 : pos_plus90[ax_pos][s]; - Proj2424[0][2][0][2] = splus>max_tang_pos_to_use ? 0 : pos_plus90[ax_pos][splus]; - Proj2424[1][0][0][3] = s>max_tang_pos_to_use ? 0 : neg_view[ax_pos][s]; - Proj2424[1][0][0][4] = splus>max_tang_pos_to_use ? 0 : neg_view[ax_pos][splus]; - Proj2424[1][2][0][3] = s>max_tang_pos_to_use ? 0 : neg_plus90[ax_pos][s]; - Proj2424[1][2][0][4] = splus>max_tang_pos_to_use ? 0 : neg_plus90[ax_pos][splus]; - - Proj2424[0][0][1][3] = msmax_tang_pos_to_use ? 0 : pos_view[ax_pos_plus][s]; - Proj2424[0][0][0][4] = splus>max_tang_pos_to_use ? 0 : pos_view[ax_pos_plus][splus]; - Proj2424[0][2][0][3] = s>max_tang_pos_to_use ? 0 : pos_plus90[ax_pos_plus][s]; - Proj2424[0][2][0][4] = splus>max_tang_pos_to_use ? 0 : pos_plus90[ax_pos_plus][splus]; - Proj2424[1][0][0][1] = s>max_tang_pos_to_use ? 0 : neg_view[ax_pos_plus][s]; - Proj2424[1][0][0][2] = splus>max_tang_pos_to_use ? 0 : neg_view[ax_pos_plus][splus]; - Proj2424[1][2][0][1] = s>max_tang_pos_to_use ? 0 : neg_plus90[ax_pos_plus][s]; - Proj2424[1][2][0][2] = splus>max_tang_pos_to_use ? 0 : neg_plus90[ax_pos_plus][splus]; - - Proj2424[0][0][1][1] = msget_average_ring_difference(segment_num); - - // take s+.5 as average for the beam (it's slowly varying in s anyway) - Proj2424 *= jacobian(delta, s+ 0.5F); - - // find correspondence between ax_pos coordinates and image coordinates: - // z = num_planes_per_axial_pos * ring + axial_pos_to_z_offset - // KT 20/06/2001 rewrote using symmetries_ptr - const int num_planes_per_axial_pos = - round(symmetries_ptr->get_num_planes_per_axial_pos(segment_num)); - const float axial_pos_to_z_offset = - symmetries_ptr->get_axial_pos_to_z_offset(segment_num); - - if (use_piecewise_linear_interpolation_now && num_planes_per_axial_pos>1) - piecewise_linear_interpolation_backproj3D_Cho_view_viewplus90( Proj2424, image, - proj_data_info_cyl_sptr, - delta, - cphi, sphi, s, ax_pos, - num_planes_per_axial_pos, - axial_pos_to_z_offset); - else - linear_interpolation_backproj3D_Cho_view_viewplus90( Proj2424, image, - proj_data_info_cyl_sptr, - delta, - cphi, sphi, s, ax_pos, - num_planes_per_axial_pos, - axial_pos_to_z_offset); + for (int ax_pos = min_axial_pos_num - 1; ax_pos <= max_axial_pos_num; ax_pos++) { + const int ax_pos_plus = ax_pos + 1; + + // We have to fill with 0, as not all elements are set in the lines below + if (ax_pos == min_axial_pos_num - 1 || ax_pos == max_axial_pos_num) + Proj2424.fill(0); + for (int s = min_abs_tang_pos_to_use; s <= max_abs_tang_pos_to_use; s++) { + const int splus = s + 1; + const int ms = -s; + const int msplus = -splus; + + // now I have to check if ax_pos is in allowable range + if (ax_pos >= min_axial_pos_num) { + Proj2424[0][0][0][1] = s > max_tang_pos_to_use ? 0 : pos_view[ax_pos][s]; + Proj2424[0][0][0][2] = splus > max_tang_pos_to_use ? 0 : pos_view[ax_pos][splus]; + Proj2424[0][2][0][1] = s > max_tang_pos_to_use ? 0 : pos_plus90[ax_pos][s]; + Proj2424[0][2][0][2] = splus > max_tang_pos_to_use ? 0 : pos_plus90[ax_pos][splus]; + Proj2424[1][0][0][3] = s > max_tang_pos_to_use ? 0 : neg_view[ax_pos][s]; + Proj2424[1][0][0][4] = splus > max_tang_pos_to_use ? 0 : neg_view[ax_pos][splus]; + Proj2424[1][2][0][3] = s > max_tang_pos_to_use ? 0 : neg_plus90[ax_pos][s]; + Proj2424[1][2][0][4] = splus > max_tang_pos_to_use ? 0 : neg_plus90[ax_pos][splus]; + + Proj2424[0][0][1][3] = ms < min_tang_pos_to_use ? 0 : pos_view[ax_pos][ms]; + Proj2424[0][0][1][4] = msplus < min_tang_pos_to_use ? 0 : pos_view[ax_pos][msplus]; + Proj2424[0][2][1][3] = ms < min_tang_pos_to_use ? 0 : pos_plus90[ax_pos][ms]; + Proj2424[0][2][1][4] = msplus < min_tang_pos_to_use ? 0 : pos_plus90[ax_pos][msplus]; + Proj2424[1][0][1][1] = ms < min_tang_pos_to_use ? 0 : neg_view[ax_pos][ms]; + Proj2424[1][0][1][2] = msplus < min_tang_pos_to_use ? 0 : neg_view[ax_pos][msplus]; + Proj2424[1][2][1][1] = ms < min_tang_pos_to_use ? 0 : neg_plus90[ax_pos][ms]; + Proj2424[1][2][1][2] = msplus < min_tang_pos_to_use ? 0 : neg_plus90[ax_pos][msplus]; + } + + if (ax_pos_plus <= max_axial_pos_num) { + Proj2424[0][0][0][3] = s > max_tang_pos_to_use ? 0 : pos_view[ax_pos_plus][s]; + Proj2424[0][0][0][4] = splus > max_tang_pos_to_use ? 0 : pos_view[ax_pos_plus][splus]; + Proj2424[0][2][0][3] = s > max_tang_pos_to_use ? 0 : pos_plus90[ax_pos_plus][s]; + Proj2424[0][2][0][4] = splus > max_tang_pos_to_use ? 0 : pos_plus90[ax_pos_plus][splus]; + Proj2424[1][0][0][1] = s > max_tang_pos_to_use ? 0 : neg_view[ax_pos_plus][s]; + Proj2424[1][0][0][2] = splus > max_tang_pos_to_use ? 0 : neg_view[ax_pos_plus][splus]; + Proj2424[1][2][0][1] = s > max_tang_pos_to_use ? 0 : neg_plus90[ax_pos_plus][s]; + Proj2424[1][2][0][2] = splus > max_tang_pos_to_use ? 0 : neg_plus90[ax_pos_plus][splus]; + + Proj2424[0][0][1][1] = ms < min_tang_pos_to_use ? 0 : pos_view[ax_pos_plus][ms]; + Proj2424[0][0][1][2] = msplus < min_tang_pos_to_use ? 0 : pos_view[ax_pos_plus][msplus]; + Proj2424[0][2][1][1] = ms < min_tang_pos_to_use ? 0 : pos_plus90[ax_pos_plus][ms]; + Proj2424[0][2][1][2] = msplus < min_tang_pos_to_use ? 0 : pos_plus90[ax_pos_plus][msplus]; + Proj2424[1][0][1][3] = ms < min_tang_pos_to_use ? 0 : neg_view[ax_pos_plus][ms]; + Proj2424[1][0][1][4] = msplus < min_tang_pos_to_use ? 0 : neg_view[ax_pos_plus][msplus]; + Proj2424[1][2][1][3] = ms < min_tang_pos_to_use ? 0 : neg_plus90[ax_pos_plus][ms]; + Proj2424[1][2][1][4] = msplus < min_tang_pos_to_use ? 0 : neg_plus90[ax_pos_plus][msplus]; } + + const int segment_num = pos_view.get_segment_num(); + const float delta = proj_data_info_cyl_sptr->get_average_ring_difference(segment_num); + + // take s+.5 as average for the beam (it's slowly varying in s anyway) + Proj2424 *= jacobian(delta, s + 0.5F); + + // find correspondence between ax_pos coordinates and image coordinates: + // z = num_planes_per_axial_pos * ring + axial_pos_to_z_offset + // KT 20/06/2001 rewrote using symmetries_ptr + const int num_planes_per_axial_pos = round(symmetries_ptr->get_num_planes_per_axial_pos(segment_num)); + const float axial_pos_to_z_offset = symmetries_ptr->get_axial_pos_to_z_offset(segment_num); + + if (use_piecewise_linear_interpolation_now && num_planes_per_axial_pos > 1) + piecewise_linear_interpolation_backproj3D_Cho_view_viewplus90(Proj2424, image, proj_data_info_cyl_sptr, delta, cphi, sphi, + s, ax_pos, num_planes_per_axial_pos, axial_pos_to_z_offset); + else + linear_interpolation_backproj3D_Cho_view_viewplus90(Proj2424, image, proj_data_info_cyl_sptr, delta, cphi, sphi, s, + ax_pos, num_planes_per_axial_pos, axial_pos_to_z_offset); } + } stop_timers(); } - END_NAMESPACE_STIR diff --git a/src/recon_buildblock/BackProjectorByBinUsingInterpolation_3DCho.cxx b/src/recon_buildblock/BackProjectorByBinUsingInterpolation_3DCho.cxx index 53af34a3ad..503f05656c 100644 --- a/src/recon_buildblock/BackProjectorByBinUsingInterpolation_3DCho.cxx +++ b/src/recon_buildblock/BackProjectorByBinUsingInterpolation_3DCho.cxx @@ -25,7 +25,7 @@ \brief This file defines two private static functions from stir::BackProjectorByBinUsingInterpolation. - \warning This file is #included by BackProjectorByBinUsingInterpolation_linear.cxx + \warning This file is #included by BackProjectorByBinUsingInterpolation_linear.cxx and BackProjectorByBinUsingInterpolation_piecewise_linear.cxx. It should NOT be added to a Makefile (or project). @@ -40,16 +40,16 @@ // enable this variable if you need to handle very oblique LORs #define MOREZ //#define ALTERNATIVE -/* +/* - These functions use a 3D version of Cho's algorithm for backprojecting + These functions use a 3D version of Cho's algorithm for backprojecting incrementally. See M. Egger's thesis for details. They use the following symmetries: - opposite ring difference - views : view, view+90 degrees, 180 degrees - view, 90 degrees - view - - s,-s symmetry (while being careful when s==0 to avoid self-symmetric + - s,-s symmetry (while being careful when s==0 to avoid self-symmetric cases) For historical reasons, 'axial_pos_num' is here called 'ring'. @@ -59,10 +59,10 @@ n : 1 : negative delta; 0 : positive delta f : 0 : view; 1 : pi/2-view; 2 : pi/2+view; 3: pi-view ms : 1 : negative s; 0 : positive s - b : 1 : r1,s; 2: r2,s+1; 3: r2,s 4: r1,s+1 + b : 1 : r1,s; 2: r2,s+1; 3: r2,s 4: r1,s+1 where r1=ring0, r2=ring0+1 when - (n==ms && f==0||2) || (n!=ms && f==1||3) - and the role of r1,r2 are reversed in the other cases + (n==ms && f==0||2) || (n!=ms && f==1||3) + and the role of r1,r2 are reversed in the other cases Naming conventions for the incremental variables: n==0 && ms==0: A'f' @@ -77,7 +77,6 @@ delta->-delta changes dz->1-dz, ds->ds */ - #include "stir/ProjDataInfo.h" #include "stir/VoxelsOnCartesianGrid.h" #include "stir/ProjDataInfoCylindricalArcCorr.h" @@ -94,62 +93,62 @@ using std::max; KT 22/05/98 drastic revision cosmetic changes: - - removed lots of local variables which were not used (or are not used + - removed lots of local variables which were not used (or are not used anymore) - - moved most declarations of variables to the place where they are + - moved most declarations of variables to the place where they are initialised, and add const when possible - - added grouping {} to make variables local + - added grouping {} to make variables local - translated German and Afrikaans comments and added new comments - reordered the main loop to avoid repeating the 'voxel-updating' code - - moved the initialisation code (common to all symmetry cases) into the + - moved the initialisation code (common to all symmetry cases) into the find_start_values() function bugs removed: - handle self-symmetric case at s==0 properly, this was only correct in - the 45 degrees case. This removed most of the bright spot in the + the 45 degrees case. This removed most of the bright spot in the central pixel. - - changed the condition to break out of the loop to avoid early stopping, + - changed the condition to break out of the loop to avoid early stopping, which gave artifacts at the border - attempt to take into account that calculations are done with finite precision There used to be 'random' misplacements of data into neighboring voxels, this happened in the central pixel and at 45 and 135 degrees. - * use double to prevent rounding errors, necessary for incremental + * use double to prevent rounding errors, necessary for incremental quantities - * change all comparisons with 0 to comparisons with a small number + * change all comparisons with 0 to comparisons with a small number 'epsilon' increased precision: - use double for invariants as well - (otherwise computations of incremental constants would mean promoting + (otherwise computations of incremental constants would mean promoting floats to doubles all the time) speed-up - all these changes resulted in an (unexpected) speed-up of about a factor 2 - - KT&CL 22/12/99 + + KT&CL 22/12/99 span works now by introducing offsets - - KT&SM + + KT&SM introduced MOREZ to handle very oblique segments. This handles the case where Z needs to be incremented more than once to find the next voxel in the beam KT 25/09/2001 make sure things are ok for any image size. The main change is -to let find_start_values return Succeeded::no if there's no voxel in +to let find_start_values return Succeeded::no if there's no voxel in the FOV for this partiuclar tube. If so, we'll just exit. -TODO: +TODO: solve remaining issues of rounding errors (see backproj2D) if we never parallelise 'beyond' viewgram level (that is, always execute the calling back_project() and these functions at the same processor), there is probably no point in using the Projptr parameter, and we could -use the viewgrams directly. -Indeed, each element in Projptr is accessed 2 (sometimes 3) times only. The -compiler probably optimises this to 1 (sometimes 2) accesses only. +use the viewgrams directly. +Indeed, each element in Projptr is accessed 2 (sometimes 3) times only. The +compiler probably optimises this to 1 (sometimes 2) accesses only. So, it could be more expensive to construct the Projptr and fill it in from the viewgrams, compared to use the viewgrams directly. */ @@ -158,8 +157,8 @@ START_NAMESPACE_STIR static const double epsilon = 1e-10; -/* - This declares a local function that finds the first voxel in the beam, and +/* + This declares a local function that finds the first voxel in the beam, and associated ds,dz etc. It is implemented at the end of the file. @@ -170,52 +169,40 @@ static const double epsilon = 1e-10; */ // KT 25/09/2001 changed return value -static Succeeded find_start_values(const shared_ptr proj_data_info_sptr, - const float delta, const double cphi, const double sphi, - const int s, const int ring0, - const float image_rad, - const double d_sl, - int&X1, int&Y1, int& Z1, - double& ds, double& dz, double& dzhor, double& dzvert, - const float num_planes_per_axial_pos, - const float axial_pos_to_z_offset); - - -inline void -check_values(const shared_ptr proj_data_info_sptr, - const float delta, const double cphi, const double sphi, - const int s, const int ring0, - const int X1, const int Y1, const int Z1, - const double ds, const double dz, - const float num_planes_per_axial_pos, - const float axial_pos_to_z_offset) -{ +static Succeeded find_start_values(const shared_ptr proj_data_info_sptr, const float delta, + const double cphi, const double sphi, const int s, const int ring0, const float image_rad, + const double d_sl, int& X1, int& Y1, int& Z1, double& ds, double& dz, double& dzhor, + double& dzvert, const float num_planes_per_axial_pos, const float axial_pos_to_z_offset); + +inline void +check_values(const shared_ptr proj_data_info_sptr, const float delta, const double cphi, + const double sphi, const int s, const int ring0, const int X1, const int Y1, const int Z1, const double ds, + const double dz, const float num_planes_per_axial_pos, const float axial_pos_to_z_offset) { #ifdef BPINT_CHECK const double d_xy = proj_data_info_sptr->get_tangential_sampling(); - - const double R2 =square(proj_data_info_sptr->get_ring_radius()); + + const double R2 = square(proj_data_info_sptr->get_ring_radius()); /* Radius of scanner squared in Pixel^2 */ const double R2p = R2 / d_xy / d_xy; // TODO remove assumption const int num_planes_per_physical_ring = 2; - const double t = s + 0.5; /* In a beam, not on a ray */ - //t=X1*cphi+Y1*sphi; - //ds=t-s; + const double t = s + 0.5; /* In a beam, not on a ray */ + // t=X1*cphi+Y1*sphi; + // ds=t-s; - const double new_ds=X1*cphi+Y1*sphi-s;// Eq 6.13 in Egger thsis - const double root=sqrt(R2p-t*t);//CL 26/10/98 Put it back the original formula as before it was root=sqrt(R2p-s*s) + const double new_ds = X1 * cphi + Y1 * sphi - s; // Eq 6.13 in Egger thsis + const double root = sqrt(R2p - t * t); // CL 26/10/98 Put it back the original formula as before it was root=sqrt(R2p-s*s) // Eq 6.15 from Egger Thesis - const double z=( Z1-num_planes_per_physical_ring*delta/2*( (-X1*sphi+Y1*cphi)/root + 1 ) - - axial_pos_to_z_offset)/num_planes_per_axial_pos; - const double new_dz=z-ring0; + const double z = + (Z1 - num_planes_per_physical_ring * delta / 2 * ((-X1 * sphi + Y1 * cphi) / root + 1) - axial_pos_to_z_offset) / + num_planes_per_axial_pos; + const double new_dz = z - ring0; - if (fabs(ds-new_ds)>.005 || fabs(dz - new_dz)>.005) - { - warning("Difference ds (%g,%g) dz (%g,%g) at X=%d,Y=%d,Z=%d\n", - ds,new_ds,dz,new_dz,X1,Y1,Z1); - } -#endif //BPINT_CHECK + if (fabs(ds - new_ds) > .005 || fabs(dz - new_dz) > .005) { + warning("Difference ds (%g,%g) dz (%g,%g) at X=%d,Y=%d,Z=%d\n", ds, new_ds, dz, new_dz, X1, Y1, Z1); + } +#endif // BPINT_CHECK } /**************************************************************************** @@ -225,392 +212,374 @@ check_values(const shared_ptr proj_data_in *****************************************************************************/ -// KT 22/05/98 new, but extracted from backproj3D_ChoView45 +// KT 22/05/98 new, but extracted from backproj3D_ChoView45 // (which used to be called Backproj_Cho_Symstheta_Phiplus90s0_P) void BackProjectorByBinUsingInterpolation:: #if PIECEWISE_INTERPOLATION -piecewise_linear_interpolation_backproj3D_Cho_view_viewplus90 + piecewise_linear_interpolation_backproj3D_Cho_view_viewplus90 #else -linear_interpolation_backproj3D_Cho_view_viewplus90 + linear_interpolation_backproj3D_Cho_view_viewplus90 #endif -(Array<4, float > const &Projptr, - VoxelsOnCartesianGrid& image, - const shared_ptr proj_data_info_sptr, - float delta, - const double cphi, const double sphi, int s, int ring0, - const int num_planes_per_axial_pos, - const float axial_pos_to_z_offset) -{ + (Array<4, float> const& Projptr, VoxelsOnCartesianGrid& image, + const shared_ptr proj_data_info_sptr, float delta, const double cphi, + const double sphi, int s, int ring0, const int num_planes_per_axial_pos, const float axial_pos_to_z_offset) { // KT 04/05/2000 new check #if PIECEWISE_INTERPOLATION - if (num_planes_per_axial_pos==1 && delta !=0) + if (num_planes_per_axial_pos == 1 && delta != 0) error("This version of the backprojector cannot be used for span>1. \ -Recompile %s with PIECEWISE_INTERPOLATION=0", __FILE__); +Recompile %s with PIECEWISE_INTERPOLATION=0", + __FILE__); #else -#ifdef ALTERNATIVE +# ifdef ALTERNATIVE // TODO - if (num_planes_per_axial_pos==1 && delta !=0) + if (num_planes_per_axial_pos == 1 && delta != 0) error("This version of the backprojector cannot be used for span>1. \ -Recompile %s with ALTERNATIVE not #defined", __FILE__); -#endif +Recompile %s with ALTERNATIVE not #defined", + __FILE__); +# endif #endif #ifndef MOREZ - // below test was too conservative. We have to adjust it a bit -#error times sqrt(2)/iets met t + // below test was too conservative. We have to adjust it a bit +# error times sqrt(2)/iets met t /* Check if delta is not too large. If the condition below is violated, z might have to be incremented more than once to remain in the beam. To do this, the code would have to be modified with while() instead of if(). As this will slow it down, we just abort at the moment. */ - - - if (delta * proj_data_info_sptr_info_cyl_ptr->get_tangential_sampling() / proj_data_info_sptr_info_cyl_ptr->get_ring_radius() > 1) - { - error("This backprojector cannot handle such oblique segments: delta = %g. Sorry.\n",delta); + if (delta * proj_data_info_sptr_info_cyl_ptr->get_tangential_sampling() / proj_data_info_sptr_info_cyl_ptr->get_ring_radius() > + 1) { + error("This backprojector cannot handle such oblique segments: delta = %g. Sorry.\n", delta); } #endif // conditions on searching flow - assert(cphi>=0-.001); - assert(sphi>=0-.001); - assert(cphi+sphi>=1-.001); + assert(cphi >= 0 - .001); + assert(sphi >= 0 - .001); + assert(cphi + sphi >= 1 - .001); - double dzvert,dzhor,ds; - double dsdiag,dzdiag,dz; - int X,Y,Z,Q; + double dzvert, dzhor, ds; + double dsdiag, dzdiag, dz; + int X, Y, Z, Q; + + const float ring_unit = 1. / num_planes_per_axial_pos; - const float ring_unit = 1./num_planes_per_axial_pos; - // in our current coordinate system, the following constant is always 2 // TODO remove assumption const int num_planes_per_physical_ring = 2; - assert(fabs(image.get_voxel_size().z() * num_planes_per_physical_ring/ proj_data_info_sptr->get_ring_spacing() -1) < 10E-4); + assert(fabs(image.get_voxel_size().z() * num_planes_per_physical_ring / proj_data_info_sptr->get_ring_spacing() - 1) < 10E-4); /* FOV radius in voxel units */ // KT 20/06/2001 change calculation of FOV such that even sized image will work - const float fovrad_in_mm = - min((min(image.get_max_x(), -image.get_min_x()))*image.get_voxel_size().x(), - (min(image.get_max_y(), -image.get_min_y()))*image.get_voxel_size().y()); - const float image_rad = fovrad_in_mm/image.get_voxel_size().x() - 2; - //const int image_rad = (int)((image.get_x_size()-1)/2); - - //KT 20/06/2001 allow min_z!=0 in all comparisons below - const int minplane = image.get_min_z(); - const int maxplane = image.get_max_z(); - - - if (find_start_values(proj_data_info_sptr, - delta, cphi, sphi, s, ring0, - image_rad, image.get_voxel_size().z(), - X, Y, Z, - ds, dz, dzhor, dzvert, - num_planes_per_axial_pos, - axial_pos_to_z_offset) == Succeeded::no) - { - // no voxels in the beam - return; - } + const float fovrad_in_mm = min((min(image.get_max_x(), -image.get_min_x())) * image.get_voxel_size().x(), + (min(image.get_max_y(), -image.get_min_y())) * image.get_voxel_size().y()); + const float image_rad = fovrad_in_mm / image.get_voxel_size().x() - 2; + // const int image_rad = (int)((image.get_x_size()-1)/2); + + // KT 20/06/2001 allow min_z!=0 in all comparisons below + const int minplane = image.get_min_z(); + const int maxplane = image.get_max_z(); + + if (find_start_values(proj_data_info_sptr, delta, cphi, sphi, s, ring0, image_rad, image.get_voxel_size().z(), X, Y, Z, ds, dz, + dzhor, dzvert, num_planes_per_axial_pos, axial_pos_to_z_offset) == Succeeded::no) { + // no voxels in the beam + return; + } - /* - The formulas below give the values to update a pixel. - Things are then complicated by optimising this backprojection - by 'incrementalising' the formulas. - // linear (original) interpolation - double UpA0=Projptr[0][0][0][1]+ds*K1A0+dz*K2A0; - image[Z][Y][X]+= (UpA0+dsdz*K3A0); - - // new interpolation - double A0a0 = Projptr[0][0][0][1]+ds*K1A0; - double A0a1 = Projptr[0][0][0][3]+ds*(K1A0+K3A0); - double UpA0 = 2*(Projptr[0][0][0][1]+ds*K1A0+dz*K2A0) - (A0a0 + A0a1)/2; - image[Z][Y][X]+= (dz <= 0.25) ? A0a0 : UpA0+twodsdz*K3A0; - - incremental changes: - // original interpolation - UpA0inc=dsinc*K1A0+dzinc*K2A0; - // new interpolation - A0a0inc = dsinc*K1A0; - A0a1inc = dsinc*(K1A0+K3A0); - UpA0inc = A0a0inc - A0a1inc/2 +2*dzinc*K2A0 + /* + The formulas below give the values to update a pixel. + Things are then complicated by optimising this backprojection + by 'incrementalising' the formulas. + // linear (original) interpolation + double UpA0=Projptr[0][0][0][1]+ds*K1A0+dz*K2A0; + image[Z][Y][X]+= (UpA0+dsdz*K3A0); + + // new interpolation + double A0a0 = Projptr[0][0][0][1]+ds*K1A0; + double A0a1 = Projptr[0][0][0][3]+ds*(K1A0+K3A0); + double UpA0 = 2*(Projptr[0][0][0][1]+ds*K1A0+dz*K2A0) - (A0a0 + A0a1)/2; + image[Z][Y][X]+= (dz <= 0.25) ? A0a0 : UpA0+twodsdz*K3A0; + + incremental changes: + // original interpolation + UpA0inc=dsinc*K1A0+dzinc*K2A0; + // new interpolation + A0a0inc = dsinc*K1A0; + A0a1inc = dsinc*(K1A0+K3A0); + UpA0inc = A0a0inc - A0a1inc/2 +2*dzinc*K2A0 - */ +*/ // K1, K2, K3 invariants - const double K1A0=Projptr[0][0][0][2]-Projptr[0][0][0][1]; - const double K2A0=Projptr[0][0][0][3]-Projptr[0][0][0][1]; - const double K3A0=Projptr[0][0][0][4]-Projptr[0][0][0][2]-K2A0; - const double K1A2=Projptr[0][2][0][2]-Projptr[0][2][0][1]; - const double K2A2=Projptr[0][2][0][3]-Projptr[0][2][0][1]; - const double K3A2=Projptr[0][2][0][4]-Projptr[0][2][0][2]-K2A2; - - const double K1A0n=Projptr[1][0][0][2]-Projptr[1][0][0][1]; - const double K2A0n=Projptr[1][0][0][3]-Projptr[1][0][0][1]; - const double K3A0n=Projptr[1][0][0][4]-Projptr[1][0][0][2]-K2A0n; - const double K1A2n=Projptr[1][2][0][2]-Projptr[1][2][0][1]; - const double K2A2n=Projptr[1][2][0][3]-Projptr[1][2][0][1]; - const double K3A2n=Projptr[1][2][0][4]-Projptr[1][2][0][2]-K2A2n; - - const double K1P0=Projptr[0][0][1][2]-Projptr[0][0][1][1]; - const double K2P0=Projptr[0][0][1][3]-Projptr[0][0][1][1]; - const double K3P0=Projptr[0][0][1][4]-Projptr[0][0][1][2]-K2P0; - const double K1P2=Projptr[0][2][1][2]-Projptr[0][2][1][1]; - const double K2P2=Projptr[0][2][1][3]-Projptr[0][2][1][1]; - const double K3P2=Projptr[0][2][1][4]-Projptr[0][2][1][2]-K2P2; - - const double K1P0n=Projptr[1][0][1][2]-Projptr[1][0][1][1]; - const double K2P0n=Projptr[1][0][1][3]-Projptr[1][0][1][1]; - const double K3P0n=Projptr[1][0][1][4]-Projptr[1][0][1][2]-K2P0n; - const double K1P2n=Projptr[1][2][1][2]-Projptr[1][2][1][1]; - const double K2P2n=Projptr[1][2][1][3]-Projptr[1][2][1][1]; - const double K3P2n=Projptr[1][2][1][4]-Projptr[1][2][1][2]-K2P2n; - + const double K1A0 = Projptr[0][0][0][2] - Projptr[0][0][0][1]; + const double K2A0 = Projptr[0][0][0][3] - Projptr[0][0][0][1]; + const double K3A0 = Projptr[0][0][0][4] - Projptr[0][0][0][2] - K2A0; + const double K1A2 = Projptr[0][2][0][2] - Projptr[0][2][0][1]; + const double K2A2 = Projptr[0][2][0][3] - Projptr[0][2][0][1]; + const double K3A2 = Projptr[0][2][0][4] - Projptr[0][2][0][2] - K2A2; + + const double K1A0n = Projptr[1][0][0][2] - Projptr[1][0][0][1]; + const double K2A0n = Projptr[1][0][0][3] - Projptr[1][0][0][1]; + const double K3A0n = Projptr[1][0][0][4] - Projptr[1][0][0][2] - K2A0n; + const double K1A2n = Projptr[1][2][0][2] - Projptr[1][2][0][1]; + const double K2A2n = Projptr[1][2][0][3] - Projptr[1][2][0][1]; + const double K3A2n = Projptr[1][2][0][4] - Projptr[1][2][0][2] - K2A2n; + + const double K1P0 = Projptr[0][0][1][2] - Projptr[0][0][1][1]; + const double K2P0 = Projptr[0][0][1][3] - Projptr[0][0][1][1]; + const double K3P0 = Projptr[0][0][1][4] - Projptr[0][0][1][2] - K2P0; + const double K1P2 = Projptr[0][2][1][2] - Projptr[0][2][1][1]; + const double K2P2 = Projptr[0][2][1][3] - Projptr[0][2][1][1]; + const double K3P2 = Projptr[0][2][1][4] - Projptr[0][2][1][2] - K2P2; + + const double K1P0n = Projptr[1][0][1][2] - Projptr[1][0][1][1]; + const double K2P0n = Projptr[1][0][1][3] - Projptr[1][0][1][1]; + const double K3P0n = Projptr[1][0][1][4] - Projptr[1][0][1][2] - K2P0n; + const double K1P2n = Projptr[1][2][1][2] - Projptr[1][2][1][1]; + const double K2P2n = Projptr[1][2][1][3] - Projptr[1][2][1][1]; + const double K3P2n = Projptr[1][2][1][4] - Projptr[1][2][1][2] - K2P2n; #if !PIECEWISE_INTERPOLATION - const double ZplusKorrA0=ring_unit*K2A0; - const double ZplusKorrA2=ring_unit*K2A2; + const double ZplusKorrA0 = ring_unit * K2A0; + const double ZplusKorrA2 = ring_unit * K2A2; - const double ZplusKorrA0n=ring_unit*K2A0n; - const double ZplusKorrA2n=ring_unit*K2A2n; + const double ZplusKorrA0n = ring_unit * K2A0n; + const double ZplusKorrA2n = ring_unit * K2A2n; - const double ZplusKorrP0=ring_unit*K2P0; - const double ZplusKorrP2=ring_unit*K2P2; + const double ZplusKorrP0 = ring_unit * K2P0; + const double ZplusKorrP2 = ring_unit * K2P2; - const double ZplusKorrP0n=ring_unit*K2P0n; - const double ZplusKorrP2n=ring_unit*K2P2n; + const double ZplusKorrP0n = ring_unit * K2P0n; + const double ZplusKorrP2n = ring_unit * K2P2n; // V, H, D invariants - const double VA0=dzvert*K2A0+sphi*K1A0; - const double HA0=dzhor*K2A0-cphi*K1A0; - const double DA0=VA0+HA0; - const double VZA0=VA0+ring_unit*K2A0; - const double HZA0=HA0+ring_unit*K2A0; - const double DZA0=DA0+ring_unit*K2A0; - const double VA2=dzvert*K2A2+sphi*K1A2; - const double HA2=dzhor*K2A2-cphi*K1A2; - const double DA2=VA2+HA2; - const double VZA2=VA2+ring_unit*K2A2; - const double HZA2=HA2+ring_unit*K2A2; - const double DZA2=DA2+ring_unit*K2A2; - - const double VA0n=dzvert*K2A0n+sphi*K1A0n; - const double HA0n=dzhor*K2A0n-cphi*K1A0n; - const double DA0n=VA0n+HA0n; - const double VZA0n=VA0n+ring_unit*K2A0n; - const double HZA0n=HA0n+ring_unit*K2A0n; - const double DZA0n=DA0n+ring_unit*K2A0n; - const double VA2n=dzvert*K2A2n+sphi*K1A2n; - const double HA2n=dzhor*K2A2n-cphi*K1A2n; - const double DA2n=VA2n+HA2n; - const double VZA2n=VA2n+ring_unit*K2A2n; - const double HZA2n=HA2n+ring_unit*K2A2n; - const double DZA2n=DA2n+ring_unit*K2A2n; - - const double VP0=dzvert*K2P0+sphi*K1P0; - const double HP0=dzhor*K2P0-cphi*K1P0; - const double DP0=VP0+HP0; - const double VZP0=VP0+ring_unit*K2P0; - const double HZP0=HP0+ring_unit*K2P0; - const double DZP0=DP0+ring_unit*K2P0; - const double VP2=dzvert*K2P2+sphi*K1P2; - const double HP2=dzhor*K2P2-cphi*K1P2; - const double DP2=VP2+HP2; - const double VZP2=VP2+ring_unit*K2P2; - const double HZP2=HP2+ring_unit*K2P2; - const double DZP2=DP2+ring_unit*K2P2; - - const double VP0n=dzvert*K2P0n+sphi*K1P0n; - const double HP0n=dzhor*K2P0n-cphi*K1P0n; - const double DP0n=VP0n+HP0n; - const double VZP0n=VP0n+ring_unit*K2P0n; - const double HZP0n=HP0n+ring_unit*K2P0n; - const double DZP0n=DP0n+ring_unit*K2P0n; - const double VP2n=dzvert*K2P2n+sphi*K1P2n; - const double HP2n=dzhor*K2P2n-cphi*K1P2n; - const double DP2n=VP2n+HP2n; - const double VZP2n=VP2n+ring_unit*K2P2n; - const double HZP2n=HP2n+ring_unit*K2P2n; - const double DZP2n=DP2n+ring_unit*K2P2n; - + const double VA0 = dzvert * K2A0 + sphi * K1A0; + const double HA0 = dzhor * K2A0 - cphi * K1A0; + const double DA0 = VA0 + HA0; + const double VZA0 = VA0 + ring_unit * K2A0; + const double HZA0 = HA0 + ring_unit * K2A0; + const double DZA0 = DA0 + ring_unit * K2A0; + const double VA2 = dzvert * K2A2 + sphi * K1A2; + const double HA2 = dzhor * K2A2 - cphi * K1A2; + const double DA2 = VA2 + HA2; + const double VZA2 = VA2 + ring_unit * K2A2; + const double HZA2 = HA2 + ring_unit * K2A2; + const double DZA2 = DA2 + ring_unit * K2A2; + + const double VA0n = dzvert * K2A0n + sphi * K1A0n; + const double HA0n = dzhor * K2A0n - cphi * K1A0n; + const double DA0n = VA0n + HA0n; + const double VZA0n = VA0n + ring_unit * K2A0n; + const double HZA0n = HA0n + ring_unit * K2A0n; + const double DZA0n = DA0n + ring_unit * K2A0n; + const double VA2n = dzvert * K2A2n + sphi * K1A2n; + const double HA2n = dzhor * K2A2n - cphi * K1A2n; + const double DA2n = VA2n + HA2n; + const double VZA2n = VA2n + ring_unit * K2A2n; + const double HZA2n = HA2n + ring_unit * K2A2n; + const double DZA2n = DA2n + ring_unit * K2A2n; + + const double VP0 = dzvert * K2P0 + sphi * K1P0; + const double HP0 = dzhor * K2P0 - cphi * K1P0; + const double DP0 = VP0 + HP0; + const double VZP0 = VP0 + ring_unit * K2P0; + const double HZP0 = HP0 + ring_unit * K2P0; + const double DZP0 = DP0 + ring_unit * K2P0; + const double VP2 = dzvert * K2P2 + sphi * K1P2; + const double HP2 = dzhor * K2P2 - cphi * K1P2; + const double DP2 = VP2 + HP2; + const double VZP2 = VP2 + ring_unit * K2P2; + const double HZP2 = HP2 + ring_unit * K2P2; + const double DZP2 = DP2 + ring_unit * K2P2; + + const double VP0n = dzvert * K2P0n + sphi * K1P0n; + const double HP0n = dzhor * K2P0n - cphi * K1P0n; + const double DP0n = VP0n + HP0n; + const double VZP0n = VP0n + ring_unit * K2P0n; + const double HZP0n = HP0n + ring_unit * K2P0n; + const double DZP0n = DP0n + ring_unit * K2P0n; + const double VP2n = dzvert * K2P2n + sphi * K1P2n; + const double HP2n = dzhor * K2P2n - cphi * K1P2n; + const double DP2n = VP2n + HP2n; + const double VZP2n = VP2n + ring_unit * K2P2n; + const double HZP2n = HP2n + ring_unit * K2P2n; + const double DZP2n = DP2n + ring_unit * K2P2n; // Initial values of update values (Up) - double UpA0=Projptr[0][0][0][1]+ds*K1A0+dz*K2A0; - double UpA2=Projptr[0][2][0][1]+ds*K1A2+dz*K2A2; + double UpA0 = Projptr[0][0][0][1] + ds * K1A0 + dz * K2A0; + double UpA2 = Projptr[0][2][0][1] + ds * K1A2 + dz * K2A2; - double UpA0n=Projptr[1][0][0][1]+ds*K1A0n+dz*K2A0n; - double UpA2n=Projptr[1][2][0][1]+ds*K1A2n+dz*K2A2n; + double UpA0n = Projptr[1][0][0][1] + ds * K1A0n + dz * K2A0n; + double UpA2n = Projptr[1][2][0][1] + ds * K1A2n + dz * K2A2n; - double UpP0=Projptr[0][0][1][1]+ds*K1P0+dz*K2P0; - double UpP2=Projptr[0][2][1][1]+ds*K1P2+dz*K2P2; + double UpP0 = Projptr[0][0][1][1] + ds * K1P0 + dz * K2P0; + double UpP2 = Projptr[0][2][1][1] + ds * K1P2 + dz * K2P2; - double UpP0n=Projptr[1][0][1][1]+ds*K1P0n+dz*K2P0n; - double UpP2n=Projptr[1][2][1][1]+ds*K1P2n+dz*K2P2n; + double UpP0n = Projptr[1][0][1][1] + ds * K1P0n + dz * K2P0n; + double UpP2n = Projptr[1][2][1][1] + ds * K1P2n + dz * K2P2n; #else // PIECEWISE_INTERPOLATION - const double ZplusKorrA0=K2A0; - const double ZplusKorrA2=K2A2; + const double ZplusKorrA0 = K2A0; + const double ZplusKorrA2 = K2A2; - const double ZplusKorrA0n=K2A0n; - const double ZplusKorrA2n=K2A2n; + const double ZplusKorrA0n = K2A0n; + const double ZplusKorrA2n = K2A2n; - const double ZplusKorrP0=K2P0; - const double ZplusKorrP2=K2P2; + const double ZplusKorrP0 = K2P0; + const double ZplusKorrP2 = K2P2; - const double ZplusKorrP0n=K2P0n; - const double ZplusKorrP2n=K2P2n; + const double ZplusKorrP0n = K2P0n; + const double ZplusKorrP2n = K2P2n; // V, H, D invariants - const double VA0a0=+sphi*K1A0; - const double HA0a0=-cphi*K1A0; - const double DA0a0=VA0a0+HA0a0; - const double VA2a0=+sphi*K1A2; - const double HA2a0=-cphi*K1A2; - const double DA2a0=VA2a0+HA2a0; - - const double VA0na0=+sphi*K1A0n; - const double HA0na0=-cphi*K1A0n; - const double DA0na0=VA0na0+HA0na0; - const double VA2na0=+sphi*K1A2n; - const double HA2na0=-cphi*K1A2n; - const double DA2na0=VA2na0+HA2na0; - - const double VP0a0=+sphi*K1P0; - const double HP0a0=-cphi*K1P0; - const double DP0a0=VP0a0+HP0a0; - const double VP2a0=+sphi*K1P2; - const double HP2a0=-cphi*K1P2; - const double DP2a0=VP2a0+HP2a0; - - const double VP0na0=+sphi*K1P0n; - const double HP0na0=-cphi*K1P0n; - const double DP0na0=VP0na0+HP0na0; - const double VP2na0=+sphi*K1P2n; - const double HP2na0=-cphi*K1P2n; - const double DP2na0=VP2na0+HP2na0; - - - const double VA0a1=+sphi*(K1A0+K3A0); - const double HA0a1=-cphi*(K1A0+K3A0); - const double DA0a1=VA0a1+HA0a1; - const double VA2a1=+sphi*(K1A2+K3A2); - const double HA2a1=-cphi*(K1A2+K3A2); - const double DA2a1=VA2a1+HA2a1; - - const double VA0na1=+sphi*(K1A0n+K3A0n); - const double HA0na1=-cphi*(K1A0n+K3A0n); - const double DA0na1=VA0na1+HA0na1; - const double VA2na1=+sphi*(K1A2n+K3A2n); - const double HA2na1=-cphi*(K1A2n+K3A2n); - const double DA2na1=VA2na1+HA2na1; - - const double VP0a1=+sphi*(K1P0+K3P0); - const double HP0a1=-cphi*(K1P0+K3P0); - const double DP0a1=VP0a1+HP0a1; - const double VP2a1=+sphi*(K1P2+K3P2); - const double HP2a1=-cphi*(K1P2+K3P2); - const double DP2a1=VP2a1+HP2a1; - - const double VP0na1=+sphi*(K1P0n+K3P0n); - const double HP0na1=-cphi*(K1P0n+K3P0n); - const double DP0na1=VP0na1+HP0na1; - const double VP2na1=+sphi*(K1P2n+K3P2n); - const double HP2na1=-cphi*(K1P2n+K3P2n); - const double DP2na1=VP2na1+HP2na1; - - - const double VA0=VA0a0*1.5 - VA0a1/2 + 2*dzvert*K2A0; - const double HA0=HA0a0*1.5 - HA0a1/2 + 2*dzhor*K2A0; - const double DA0=VA0+HA0; - const double VZA0=VA0+K2A0; - const double HZA0=HA0+K2A0; - const double DZA0=DA0+K2A0; - const double VA2=VA2a0*1.5 - VA2a1/2 + 2*dzvert*K2A2; - const double HA2=HA2a0*1.5 - HA2a1/2 + 2*dzhor*K2A2; - const double DA2=VA2+HA2; - const double VZA2=VA2+K2A2; - const double HZA2=HA2+K2A2; - const double DZA2=DA2+K2A2; - - const double VA0n=VA0na0*1.5 - VA0na1/2 + 2*dzvert*K2A0n; - const double HA0n=HA0na0*1.5 - HA0na1/2 + 2*dzhor*K2A0n; - const double DA0n=VA0n+HA0n; - const double VZA0n=VA0n+K2A0n; - const double HZA0n=HA0n+K2A0n; - const double DZA0n=DA0n+K2A0n; - const double VA2n=VA2na0*1.5 - VA2na1/2 + 2*dzvert*K2A2n; - const double HA2n=HA2na0*1.5 - HA2na1/2 + 2*dzhor*K2A2n; - const double DA2n=VA2n+HA2n; - const double VZA2n=VA2n+K2A2n; - const double HZA2n=HA2n+K2A2n; - const double DZA2n=DA2n+K2A2n; - - const double VP0=VP0a0*1.5 - VP0a1/2 + 2*dzvert*K2P0; - const double HP0=HP0a0*1.5 - HP0a1/2 + 2*dzhor*K2P0; - const double DP0=VP0+HP0; - const double VZP0=VP0+K2P0; - const double HZP0=HP0+K2P0; - const double DZP0=DP0+K2P0; - const double VP2=VP2a0*1.5 - VP2a1/2 + 2*dzvert*K2P2; - const double HP2=HP2a0*1.5 - HP2a1/2 + 2*dzhor*K2P2; - const double DP2=VP2+HP2; - const double VZP2=VP2+K2P2; - const double HZP2=HP2+K2P2; - const double DZP2=DP2+K2P2; - - const double VP0n=VP0na0*1.5 - VP0na1/2 + 2*dzvert*K2P0n; - const double HP0n=HP0na0*1.5 - HP0na1/2 + 2*dzhor*K2P0n; - const double DP0n=VP0n+HP0n; - const double VZP0n=VP0n+K2P0n; - const double HZP0n=HP0n+K2P0n; - const double DZP0n=DP0n+K2P0n; - const double VP2n=VP2na0*1.5 - VP2na1/2 + 2*dzvert*K2P2n; - const double HP2n=HP2na0*1.5 - HP2na1/2 + 2*dzhor*K2P2n; - const double DP2n=VP2n+HP2n; - const double VZP2n=VP2n+K2P2n; - const double HZP2n=HP2n+K2P2n; - const double DZP2n=DP2n+K2P2n; + const double VA0a0 = +sphi * K1A0; + const double HA0a0 = -cphi * K1A0; + const double DA0a0 = VA0a0 + HA0a0; + const double VA2a0 = +sphi * K1A2; + const double HA2a0 = -cphi * K1A2; + const double DA2a0 = VA2a0 + HA2a0; + + const double VA0na0 = +sphi * K1A0n; + const double HA0na0 = -cphi * K1A0n; + const double DA0na0 = VA0na0 + HA0na0; + const double VA2na0 = +sphi * K1A2n; + const double HA2na0 = -cphi * K1A2n; + const double DA2na0 = VA2na0 + HA2na0; + + const double VP0a0 = +sphi * K1P0; + const double HP0a0 = -cphi * K1P0; + const double DP0a0 = VP0a0 + HP0a0; + const double VP2a0 = +sphi * K1P2; + const double HP2a0 = -cphi * K1P2; + const double DP2a0 = VP2a0 + HP2a0; + + const double VP0na0 = +sphi * K1P0n; + const double HP0na0 = -cphi * K1P0n; + const double DP0na0 = VP0na0 + HP0na0; + const double VP2na0 = +sphi * K1P2n; + const double HP2na0 = -cphi * K1P2n; + const double DP2na0 = VP2na0 + HP2na0; + + const double VA0a1 = +sphi * (K1A0 + K3A0); + const double HA0a1 = -cphi * (K1A0 + K3A0); + const double DA0a1 = VA0a1 + HA0a1; + const double VA2a1 = +sphi * (K1A2 + K3A2); + const double HA2a1 = -cphi * (K1A2 + K3A2); + const double DA2a1 = VA2a1 + HA2a1; + + const double VA0na1 = +sphi * (K1A0n + K3A0n); + const double HA0na1 = -cphi * (K1A0n + K3A0n); + const double DA0na1 = VA0na1 + HA0na1; + const double VA2na1 = +sphi * (K1A2n + K3A2n); + const double HA2na1 = -cphi * (K1A2n + K3A2n); + const double DA2na1 = VA2na1 + HA2na1; + + const double VP0a1 = +sphi * (K1P0 + K3P0); + const double HP0a1 = -cphi * (K1P0 + K3P0); + const double DP0a1 = VP0a1 + HP0a1; + const double VP2a1 = +sphi * (K1P2 + K3P2); + const double HP2a1 = -cphi * (K1P2 + K3P2); + const double DP2a1 = VP2a1 + HP2a1; + + const double VP0na1 = +sphi * (K1P0n + K3P0n); + const double HP0na1 = -cphi * (K1P0n + K3P0n); + const double DP0na1 = VP0na1 + HP0na1; + const double VP2na1 = +sphi * (K1P2n + K3P2n); + const double HP2na1 = -cphi * (K1P2n + K3P2n); + const double DP2na1 = VP2na1 + HP2na1; + + const double VA0 = VA0a0 * 1.5 - VA0a1 / 2 + 2 * dzvert * K2A0; + const double HA0 = HA0a0 * 1.5 - HA0a1 / 2 + 2 * dzhor * K2A0; + const double DA0 = VA0 + HA0; + const double VZA0 = VA0 + K2A0; + const double HZA0 = HA0 + K2A0; + const double DZA0 = DA0 + K2A0; + const double VA2 = VA2a0 * 1.5 - VA2a1 / 2 + 2 * dzvert * K2A2; + const double HA2 = HA2a0 * 1.5 - HA2a1 / 2 + 2 * dzhor * K2A2; + const double DA2 = VA2 + HA2; + const double VZA2 = VA2 + K2A2; + const double HZA2 = HA2 + K2A2; + const double DZA2 = DA2 + K2A2; + + const double VA0n = VA0na0 * 1.5 - VA0na1 / 2 + 2 * dzvert * K2A0n; + const double HA0n = HA0na0 * 1.5 - HA0na1 / 2 + 2 * dzhor * K2A0n; + const double DA0n = VA0n + HA0n; + const double VZA0n = VA0n + K2A0n; + const double HZA0n = HA0n + K2A0n; + const double DZA0n = DA0n + K2A0n; + const double VA2n = VA2na0 * 1.5 - VA2na1 / 2 + 2 * dzvert * K2A2n; + const double HA2n = HA2na0 * 1.5 - HA2na1 / 2 + 2 * dzhor * K2A2n; + const double DA2n = VA2n + HA2n; + const double VZA2n = VA2n + K2A2n; + const double HZA2n = HA2n + K2A2n; + const double DZA2n = DA2n + K2A2n; + + const double VP0 = VP0a0 * 1.5 - VP0a1 / 2 + 2 * dzvert * K2P0; + const double HP0 = HP0a0 * 1.5 - HP0a1 / 2 + 2 * dzhor * K2P0; + const double DP0 = VP0 + HP0; + const double VZP0 = VP0 + K2P0; + const double HZP0 = HP0 + K2P0; + const double DZP0 = DP0 + K2P0; + const double VP2 = VP2a0 * 1.5 - VP2a1 / 2 + 2 * dzvert * K2P2; + const double HP2 = HP2a0 * 1.5 - HP2a1 / 2 + 2 * dzhor * K2P2; + const double DP2 = VP2 + HP2; + const double VZP2 = VP2 + K2P2; + const double HZP2 = HP2 + K2P2; + const double DZP2 = DP2 + K2P2; + + const double VP0n = VP0na0 * 1.5 - VP0na1 / 2 + 2 * dzvert * K2P0n; + const double HP0n = HP0na0 * 1.5 - HP0na1 / 2 + 2 * dzhor * K2P0n; + const double DP0n = VP0n + HP0n; + const double VZP0n = VP0n + K2P0n; + const double HZP0n = HP0n + K2P0n; + const double DZP0n = DP0n + K2P0n; + const double VP2n = VP2na0 * 1.5 - VP2na1 / 2 + 2 * dzvert * K2P2n; + const double HP2n = HP2na0 * 1.5 - HP2na1 / 2 + 2 * dzhor * K2P2n; + const double DP2n = VP2n + HP2n; + const double VZP2n = VP2n + K2P2n; + const double HZP2n = HP2n + K2P2n; + const double DZP2n = DP2n + K2P2n; // Initial values of update values (Up) - double A0a0 = Projptr[0][0][0][1]+ds*K1A0; - double A0a1 = Projptr[0][0][0][3]+ds*(K1A0+K3A0); - double A2a0 = Projptr[0][2][0][1]+ds*K1A2; - double A2a1 = Projptr[0][2][0][3]+ds*(K1A2+K3A2); - - double P0na0 = Projptr[1][0][1][1]+ds*K1P0n; - double P0na1 = Projptr[1][0][1][3]+ds*(K1P0n+K3P0n); - double P2na0 = Projptr[1][2][1][1]+ds*K1P2n; - double P2na1 = Projptr[1][2][1][3]+ds*(K1P2n+K3P2n); + double A0a0 = Projptr[0][0][0][1] + ds * K1A0; + double A0a1 = Projptr[0][0][0][3] + ds * (K1A0 + K3A0); + double A2a0 = Projptr[0][2][0][1] + ds * K1A2; + double A2a1 = Projptr[0][2][0][3] + ds * (K1A2 + K3A2); - double A0na0 = Projptr[1][0][0][1]+ds*K1A0n; - double A0na1 = Projptr[1][0][0][3]+ds*(K1A0n+K3A0n); - double A2na0 = Projptr[1][2][0][1]+ds*K1A2n; - double A2na1 = Projptr[1][2][0][3]+ds*(K1A2n+K3A2n); + double P0na0 = Projptr[1][0][1][1] + ds * K1P0n; + double P0na1 = Projptr[1][0][1][3] + ds * (K1P0n + K3P0n); + double P2na0 = Projptr[1][2][1][1] + ds * K1P2n; + double P2na1 = Projptr[1][2][1][3] + ds * (K1P2n + K3P2n); - double P0a0 = Projptr[0][0][1][1]+ds*K1P0; - double P0a1 = Projptr[0][0][1][3]+ds*(K1P0+K3P0); - double P2a0 = Projptr[0][2][1][1]+ds*K1P2; - double P2a1 = Projptr[0][2][1][3]+ds*(K1P2+K3P2); + double A0na0 = Projptr[1][0][0][1] + ds * K1A0n; + double A0na1 = Projptr[1][0][0][3] + ds * (K1A0n + K3A0n); + double A2na0 = Projptr[1][2][0][1] + ds * K1A2n; + double A2na1 = Projptr[1][2][0][3] + ds * (K1A2n + K3A2n); + double P0a0 = Projptr[0][0][1][1] + ds * K1P0; + double P0a1 = Projptr[0][0][1][3] + ds * (K1P0 + K3P0); + double P2a0 = Projptr[0][2][1][1] + ds * K1P2; + double P2a1 = Projptr[0][2][1][3] + ds * (K1P2 + K3P2); - double UpA0 = 2*(Projptr[0][0][0][1]+ds*K1A0+dz*K2A0) - (A0a0 + A0a1)/2; - double UpA2 = 2*(Projptr[0][2][0][1]+ds*K1A2+dz*K2A2) - (A2a0 + A2a1)/2; + double UpA0 = 2 * (Projptr[0][0][0][1] + ds * K1A0 + dz * K2A0) - (A0a0 + A0a1) / 2; + double UpA2 = 2 * (Projptr[0][2][0][1] + ds * K1A2 + dz * K2A2) - (A2a0 + A2a1) / 2; - double UpA0n = 2*(Projptr[1][0][0][1]+ds*K1A0n+dz*K2A0n) - (A0na0 + A0na1)/2; - double UpA2n = 2*(Projptr[1][2][0][1]+ds*K1A2n+dz*K2A2n) - (A2na0 + A2na1)/2; + double UpA0n = 2 * (Projptr[1][0][0][1] + ds * K1A0n + dz * K2A0n) - (A0na0 + A0na1) / 2; + double UpA2n = 2 * (Projptr[1][2][0][1] + ds * K1A2n + dz * K2A2n) - (A2na0 + A2na1) / 2; - double UpP0 = 2*(Projptr[0][0][1][1]+ds*K1P0+dz*K2P0) - (P0a0 + P0a1)/2; - double UpP2 = 2*(Projptr[0][2][1][1]+ds*K1P2+dz*K2P2) - (P2a0 + P2a1)/2; + double UpP0 = 2 * (Projptr[0][0][1][1] + ds * K1P0 + dz * K2P0) - (P0a0 + P0a1) / 2; + double UpP2 = 2 * (Projptr[0][2][1][1] + ds * K1P2 + dz * K2P2) - (P2a0 + P2a1) / 2; - double UpP0n = 2*(Projptr[1][0][1][1]+ds*K1P0n+dz*K2P0n) - (P0na0 + P0na1)/2; - double UpP2n = 2*(Projptr[1][2][1][1]+ds*K1P2n+dz*K2P2n) - (P2na0 + P2na1)/2; + double UpP0n = 2 * (Projptr[1][0][1][1] + ds * K1P0n + dz * K2P0n) - (P0na0 + P0na1) / 2; + double UpP2n = 2 * (Projptr[1][2][1][1] + ds * K1P2n + dz * K2P2n) - (P2na0 + P2na1) / 2; #endif // PIECEWISE_INTERPOLATION @@ -623,20 +592,17 @@ Recompile %s with ALTERNATIVE not #defined", __FILE__); // CL&KT 21/12/99 added axial_pos_to_z_offset and num_planes_per_physical_ring { // first compute it as floating point (although it has to be an int really) - const float Qf = (2*num_planes_per_axial_pos*(ring0 + 0.5) - + num_planes_per_physical_ring*delta - + 2*axial_pos_to_z_offset - - Z); + const float Qf = + (2 * num_planes_per_axial_pos * (ring0 + 0.5) + num_planes_per_physical_ring * delta + 2 * axial_pos_to_z_offset - Z); // now use rounding to be safe Q = (int)floor(Qf + 0.5); - assert(fabs(Q-Qf) < 10E-4); + assert(fabs(Q - Qf) < 10E-4); } - dzdiag=dzvert+dzhor; - dsdiag=-cphi+sphi; - + dzdiag = dzvert + dzhor; + dsdiag = -cphi + sphi; - /* KT 13/05/98 changed loop condition, originally a combination of + /* KT 13/05/98 changed loop condition, originally a combination of while (X>X2||YY2||Z>Z2) @@ -647,428 +613,420 @@ Recompile %s with ALTERNATIVE not #defined", __FILE__); Now I break out of the while when the voxel goes out of the cylinder, which means I don't have to use X2,Y2,Z2 anymore */ - do - { - assert(ds >= 0); - assert(ds <= 1); - assert(dz >= 0); - assert(dz <= ring_unit+epsilon); - - // Update voxel values for this X,Y,Z - // For 1 given (X,Y)-position, there are always 2 voxels along the z-axis - // in this beam - const int Zplus=Z+1; - const int Qmin=Q-1; - -#if PIECEWISE_INTERPOLATION - //KT&MJ 07/08/98 new - - // KT 16/06/98 changed check ds!=0 to fabs(ds)>epsilon for better rounding control - const bool do_s_symmetry = (s!=0 || fabs(ds) > epsilon); - const double twodsdz=2*ds*dz; - const double twodsdz2=2*ds*(dz+0.5); - - if (Z>=minplane&&Z<=maxplane) { - // original image[Z][Y][X]+=UpA0+dsdz*K3A0 - image[Z][Y][X]+= (dz <= 0.25) ? A0a0 : UpA0+twodsdz*K3A0; - image[Z][X][-Y]+=(dz <= 0.25) ? A2a0 : UpA2 +twodsdz*K3A2; - if (do_s_symmetry) { - image[Z][-Y][-X]+= (dz <= 0.25) ? P0na0 : UpP0n +twodsdz*K3P0n; - image[Z][-X][Y]+=(dz <= 0.25) ? P2na0 : UpP2n +twodsdz*K3P2n; - } + do { + assert(ds >= 0); + assert(ds <= 1); + assert(dz >= 0); + assert(dz <= ring_unit + epsilon); + + // Update voxel values for this X,Y,Z + // For 1 given (X,Y)-position, there are always 2 voxels along the z-axis + // in this beam + const int Zplus = Z + 1; + const int Qmin = Q - 1; +#if PIECEWISE_INTERPOLATION + // KT&MJ 07/08/98 new + + // KT 16/06/98 changed check ds!=0 to fabs(ds)>epsilon for better rounding control + const bool do_s_symmetry = (s != 0 || fabs(ds) > epsilon); + const double twodsdz = 2 * ds * dz; + const double twodsdz2 = 2 * ds * (dz + 0.5); + + if (Z >= minplane && Z <= maxplane) { + // original image[Z][Y][X]+=UpA0+dsdz*K3A0 + image[Z][Y][X] += (dz <= 0.25) ? A0a0 : UpA0 + twodsdz * K3A0; + image[Z][X][-Y] += (dz <= 0.25) ? A2a0 : UpA2 + twodsdz * K3A2; + if (do_s_symmetry) { + image[Z][-Y][-X] += (dz <= 0.25) ? P0na0 : UpP0n + twodsdz * K3P0n; + image[Z][-X][Y] += (dz <= 0.25) ? P2na0 : UpP2n + twodsdz * K3P2n; } - if (Zplus>=minplane&&Zplus<=maxplane) { - image[Zplus][Y][X]+=(dz >= 0.25) ? A0a1 : UpA0+twodsdz2*K3A0 +ZplusKorrA0; - image[Zplus][X][-Y]+=(dz >= 0.25) ? A2a1 : UpA2+twodsdz2*K3A2 +ZplusKorrA2; - if (do_s_symmetry) { - image[Zplus][-Y][-X]+=(dz >= 0.25) ? P0na1 : UpP0n+twodsdz2*K3P0n +ZplusKorrP0n; - image[Zplus][-X][Y]+=(dz >= 0.25) ? P2na1 : UpP2n+twodsdz2*K3P2n +ZplusKorrP2n; - } - + } + if (Zplus >= minplane && Zplus <= maxplane) { + image[Zplus][Y][X] += (dz >= 0.25) ? A0a1 : UpA0 + twodsdz2 * K3A0 + ZplusKorrA0; + image[Zplus][X][-Y] += (dz >= 0.25) ? A2a1 : UpA2 + twodsdz2 * K3A2 + ZplusKorrA2; + if (do_s_symmetry) { + image[Zplus][-Y][-X] += (dz >= 0.25) ? P0na1 : UpP0n + twodsdz2 * K3P0n + ZplusKorrP0n; + image[Zplus][-X][Y] += (dz >= 0.25) ? P2na1 : UpP2n + twodsdz2 * K3P2n + ZplusKorrP2n; } - if (Q>=minplane&&Q<=maxplane) { - image[Q][Y][X]+=(dz <= 0.25) ? A0na0 : UpA0n +twodsdz*K3A0n; - image[Q][X][-Y]+=(dz <= 0.25) ? A2na0 : UpA2n +twodsdz*K3A2n; - if (do_s_symmetry) { - image[Q][-Y][-X]+=(dz <= 0.25) ? P0a0 : UpP0 +twodsdz*K3P0; - image[Q][-X][Y]+=(dz <= 0.25) ? P2a0 : UpP2 +twodsdz*K3P2; - } + } + if (Q >= minplane && Q <= maxplane) { + image[Q][Y][X] += (dz <= 0.25) ? A0na0 : UpA0n + twodsdz * K3A0n; + image[Q][X][-Y] += (dz <= 0.25) ? A2na0 : UpA2n + twodsdz * K3A2n; + if (do_s_symmetry) { + image[Q][-Y][-X] += (dz <= 0.25) ? P0a0 : UpP0 + twodsdz * K3P0; + image[Q][-X][Y] += (dz <= 0.25) ? P2a0 : UpP2 + twodsdz * K3P2; } - if (Qmin>=minplane&&Qmin<=maxplane) { - image[Qmin][Y][X]+=(dz >= 0.25) ? A0na1 : UpA0n+twodsdz2*K3A0n + ZplusKorrA0n; - image[Qmin][X][-Y]+=(dz >= 0.25) ? A2na1 : UpA2n+twodsdz2*K3A2n + ZplusKorrA2n; - if (do_s_symmetry) { - image[Qmin][-Y][-X]+=(dz >= 0.25) ? P0a1 : UpP0+twodsdz2*K3P0 + ZplusKorrP0; - image[Qmin][-X][Y]+=(dz >= 0.25) ? P2a1 : UpP2+twodsdz2*K3P2 + ZplusKorrP2; - } + } + if (Qmin >= minplane && Qmin <= maxplane) { + image[Qmin][Y][X] += (dz >= 0.25) ? A0na1 : UpA0n + twodsdz2 * K3A0n + ZplusKorrA0n; + image[Qmin][X][-Y] += (dz >= 0.25) ? A2na1 : UpA2n + twodsdz2 * K3A2n + ZplusKorrA2n; + if (do_s_symmetry) { + image[Qmin][-Y][-X] += (dz >= 0.25) ? P0a1 : UpP0 + twodsdz2 * K3P0 + ZplusKorrP0; + image[Qmin][-X][Y] += (dz >= 0.25) ? P2a1 : UpP2 + twodsdz2 * K3P2 + ZplusKorrP2; } + } #else // !PIECEWISE_INTERPOLATION -#ifdef ALTERNATIVE - // This code and the one after #else are equivalent. However, this version is - // shorter, while the other version looks 'more optimal'. - // TODO check which is faster. - // KT 16/06/98 changed check ds!=0 to fabs(ds)>epsilon for better rounding control - const bool do_s_symmetry = (s!=0 || fabs(ds) > epsilon); - const double dsdz=ds*dz; - const double dsdz2=ds*(dz+ring_unit); - - if (Z>=minplane&&Z<=maxplane) { - image[Z][Y][X]+=UpA0+dsdz*K3A0; - image[Z][X][-Y]+=UpA2+dsdz*K3A2; - if (do_s_symmetry) { - image[Z][-Y][-X]+=UpP0n+dsdz*K3P0n; - image[Z][-X][Y]+=UpP2n+dsdz*K3P2n; - } - +# ifdef ALTERNATIVE + // This code and the one after #else are equivalent. However, this version is + // shorter, while the other version looks 'more optimal'. + // TODO check which is faster. + // KT 16/06/98 changed check ds!=0 to fabs(ds)>epsilon for better rounding control + const bool do_s_symmetry = (s != 0 || fabs(ds) > epsilon); + const double dsdz = ds * dz; + const double dsdz2 = ds * (dz + ring_unit); + + if (Z >= minplane && Z <= maxplane) { + image[Z][Y][X] += UpA0 + dsdz * K3A0; + image[Z][X][-Y] += UpA2 + dsdz * K3A2; + if (do_s_symmetry) { + image[Z][-Y][-X] += UpP0n + dsdz * K3P0n; + image[Z][-X][Y] += UpP2n + dsdz * K3P2n; } - //CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data - //as there is only one voxel in the beam in slice unit - if (Zplus>=minplane&&Zplus<=maxplane && ring_unit==0.5) { - image[Zplus][Y][X]+=UpA0+dsdz2*K3A0+ZplusKorrA0; - image[Zplus][X][-Y]+=UpA2+dsdz2*K3A2+ZplusKorrA2; - if (do_s_symmetry) { - image[Zplus][-Y][-X]+=UpP0n+dsdz2*K3P0n+ZplusKorrP0n; - image[Zplus][-X][Y]+=UpP2n+dsdz2*K3P2n+ZplusKorrP2n; - } - + } + // CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data + // as there is only one voxel in the beam in slice unit + if (Zplus >= minplane && Zplus <= maxplane && ring_unit == 0.5) { + image[Zplus][Y][X] += UpA0 + dsdz2 * K3A0 + ZplusKorrA0; + image[Zplus][X][-Y] += UpA2 + dsdz2 * K3A2 + ZplusKorrA2; + if (do_s_symmetry) { + image[Zplus][-Y][-X] += UpP0n + dsdz2 * K3P0n + ZplusKorrP0n; + image[Zplus][-X][Y] += UpP2n + dsdz2 * K3P2n + ZplusKorrP2n; } - if (Q>=minplane&&Q<=maxplane) { - image[Q][Y][X]+=UpA0n+dsdz*K3A0n; - image[Q][X][-Y]+=UpA2n+dsdz*K3A2n; - if (do_s_symmetry) { - image[Q][-Y][-X]+=UpP0+dsdz*K3P0; - image[Q][-X][Y]+=UpP2+dsdz*K3P2; - } + } + if (Q >= minplane && Q <= maxplane) { + image[Q][Y][X] += UpA0n + dsdz * K3A0n; + image[Q][X][-Y] += UpA2n + dsdz * K3A2n; + if (do_s_symmetry) { + image[Q][-Y][-X] += UpP0 + dsdz * K3P0; + image[Q][-X][Y] += UpP2 + dsdz * K3P2; } - //CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data - //as there is only one voxel in the beam in slice unit - if (Qmin>=minplane&&Qmin<=maxplane && ring_unit==0.5) { - image[Qmin][Y][X]+=UpA0n+dsdz2*K3A0n+ZplusKorrA0n; - image[Qmin][X][-Y]+=UpA2n+dsdz2*K3A2n+ZplusKorrA2n; - if (do_s_symmetry) { - image[Qmin][-Y][-X]+=UpP0+dsdz2*K3P0+ZplusKorrP0; - image[Qmin][-X][Y]+=UpP2+dsdz2*K3P2+ZplusKorrP2; - } + } + // CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data + // as there is only one voxel in the beam in slice unit + if (Qmin >= minplane && Qmin <= maxplane && ring_unit == 0.5) { + image[Qmin][Y][X] += UpA0n + dsdz2 * K3A0n + ZplusKorrA0n; + image[Qmin][X][-Y] += UpA2n + dsdz2 * K3A2n + ZplusKorrA2n; + if (do_s_symmetry) { + image[Qmin][-Y][-X] += UpP0 + dsdz2 * K3P0 + ZplusKorrP0; + image[Qmin][-X][Y] += UpP2 + dsdz2 * K3P2 + ZplusKorrP2; } -#else // ALTERNATIVE - double TMP1,TMP2; + } +# else // ALTERNATIVE + double TMP1, TMP2; - // if(Z == maxplane || Q == maxplane || Zplus == maxplane || Qmin == maxplane) + // if(Z == maxplane || Q == maxplane || Zplus == maxplane || Qmin == maxplane) // cout << " " << ring0 ; - - TMP1=ds*K3A0; - TMP2=UpA0+dz*TMP1; - if (Z>=minplane&&Z<=maxplane) - image[Z][Y][X]+=TMP2; - //CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data - //as there is only one voxel in the beam in slice unit - if (Zplus>=minplane&&Zplus<=maxplane && ring_unit==0.5) - image[Zplus][Y][X]+=TMP2+ring_unit*TMP1+ZplusKorrA0; - TMP1=ds*K3A2; - TMP2=UpA2+dz*TMP1; - if (Z>=minplane&&Z<=maxplane) - image[Z][X][-Y]+=TMP2; - //CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data - //as there is only one voxel in the beam in slice unit - if (Zplus>=minplane&&Zplus<=maxplane && ring_unit==0.5) - image[Zplus][X][-Y]+=TMP2+ring_unit*TMP1+ZplusKorrA2; - - TMP1=ds*K3A0n; - TMP2=UpA0n+dz*TMP1; - if (Q>=minplane&&Q<=maxplane) - image[Q][Y][X]+=TMP2; - //CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data - //as there is only one voxel in the beam in slice unit - if (Qmin>=minplane&&Qmin<=maxplane && ring_unit==0.5) - image[Qmin][Y][X]+=TMP2+ring_unit*TMP1+ZplusKorrA0n; - TMP1=ds*K3A2n; - TMP2=UpA2n+dz*TMP1; - if (Q>=minplane&&Q<=maxplane) - image[Q][X][-Y]+=TMP2; - //CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data - //as there is only one voxel in the beam in slice unit - if (Qmin>=minplane&&Qmin<=maxplane && ring_unit==0.5) - image[Qmin][X][-Y]+=TMP2+ring_unit*TMP1+ZplusKorrA2n; - - // KT 14/05/98 changed X!=-Y to s!=0 || ds!=0 to make it work for view != view45 - // KT 16/06/98 changed check ds!=0 to fabs(ds)>epsilon for better rounding control - if (s!=0 || fabs(ds) > epsilon) { - TMP1=ds*K3P0; - TMP2=UpP0+dz*TMP1; - if (Q>=minplane&&Q<=maxplane) - image[Q][-Y][-X]+=TMP2; - //CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data - //as there is only one voxel in the beam in slice unit - if (Qmin>=minplane&&Qmin<=maxplane && ring_unit==0.5) - image[Qmin][-Y][-X]+=TMP2+ring_unit*TMP1+ZplusKorrP0; - TMP1=ds*K3P2; - TMP2=UpP2+dz*TMP1; - if (Q>=minplane&&Q<=maxplane) - image[Q][-X][Y]+=TMP2; - //CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data - //as there is only one voxel in the beam in slice unit - if (Qmin>=minplane&&Qmin<=maxplane && ring_unit==0.5) - image[Qmin][-X][Y]+=TMP2+ring_unit*TMP1+ZplusKorrP2; - - TMP1=ds*K3P0n; - TMP2=UpP0n+dz*TMP1; - if (Z>=minplane&&Z<=maxplane) - image[Z][-Y][-X]+=TMP2; - //CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data - //as there is only one voxel in the beam in slice unit - if (Zplus>=minplane&&Zplus<=maxplane && ring_unit==0.5) - image[Zplus][-Y][-X]+=TMP2+ring_unit*TMP1+ZplusKorrP0n; - TMP1=ds*K3P2n; - TMP2=UpP2n+dz*TMP1; - if (Z>=minplane&&Z<=maxplane) - image[Z][-X][Y]+=TMP2; - //CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data - //as there is only one voxel in the beam in slice unit - if (Zplus>=minplane&&Zplus<=maxplane && ring_unit==0.5) - image[Zplus][-X][Y]+=TMP2+ring_unit*TMP1+ZplusKorrP2n; - } -#endif // ALTERNATIVE -#endif // PIECEWISE_INTERPOLATION - // Search for next pixel in the beam - - if (ds>=cphi) { - /* horizontal*/ - X-=1; - ds-=cphi; - dz+=dzhor; - if (dz= minplane && Z <= maxplane) + image[Z][Y][X] += TMP2; + // CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data + // as there is only one voxel in the beam in slice unit + if (Zplus >= minplane && Zplus <= maxplane && ring_unit == 0.5) + image[Zplus][Y][X] += TMP2 + ring_unit * TMP1 + ZplusKorrA0; + TMP1 = ds * K3A2; + TMP2 = UpA2 + dz * TMP1; + if (Z >= minplane && Z <= maxplane) + image[Z][X][-Y] += TMP2; + // CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data + // as there is only one voxel in the beam in slice unit + if (Zplus >= minplane && Zplus <= maxplane && ring_unit == 0.5) + image[Zplus][X][-Y] += TMP2 + ring_unit * TMP1 + ZplusKorrA2; + + TMP1 = ds * K3A0n; + TMP2 = UpA0n + dz * TMP1; + if (Q >= minplane && Q <= maxplane) + image[Q][Y][X] += TMP2; + // CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data + // as there is only one voxel in the beam in slice unit + if (Qmin >= minplane && Qmin <= maxplane && ring_unit == 0.5) + image[Qmin][Y][X] += TMP2 + ring_unit * TMP1 + ZplusKorrA0n; + TMP1 = ds * K3A2n; + TMP2 = UpA2n + dz * TMP1; + if (Q >= minplane && Q <= maxplane) + image[Q][X][-Y] += TMP2; + // CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data + // as there is only one voxel in the beam in slice unit + if (Qmin >= minplane && Qmin <= maxplane && ring_unit == 0.5) + image[Qmin][X][-Y] += TMP2 + ring_unit * TMP1 + ZplusKorrA2n; + + // KT 14/05/98 changed X!=-Y to s!=0 || ds!=0 to make it work for view != view45 + // KT 16/06/98 changed check ds!=0 to fabs(ds)>epsilon for better rounding control + if (s != 0 || fabs(ds) > epsilon) { + TMP1 = ds * K3P0; + TMP2 = UpP0 + dz * TMP1; + if (Q >= minplane && Q <= maxplane) + image[Q][-Y][-X] += TMP2; + // CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data + // as there is only one voxel in the beam in slice unit + if (Qmin >= minplane && Qmin <= maxplane && ring_unit == 0.5) + image[Qmin][-Y][-X] += TMP2 + ring_unit * TMP1 + ZplusKorrP0; + TMP1 = ds * K3P2; + TMP2 = UpP2 + dz * TMP1; + if (Q >= minplane && Q <= maxplane) + image[Q][-X][Y] += TMP2; + // CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data + // as there is only one voxel in the beam in slice unit + if (Qmin >= minplane && Qmin <= maxplane && ring_unit == 0.5) + image[Qmin][-X][Y] += TMP2 + ring_unit * TMP1 + ZplusKorrP2; + + TMP1 = ds * K3P0n; + TMP2 = UpP0n + dz * TMP1; + if (Z >= minplane && Z <= maxplane) + image[Z][-Y][-X] += TMP2; + // CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data + // as there is only one voxel in the beam in slice unit + if (Zplus >= minplane && Zplus <= maxplane && ring_unit == 0.5) + image[Zplus][-Y][-X] += TMP2 + ring_unit * TMP1 + ZplusKorrP0n; + TMP1 = ds * K3P2n; + TMP2 = UpP2n + dz * TMP1; + if (Z >= minplane && Z <= maxplane) + image[Z][-X][Y] += TMP2; + // CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data + // as there is only one voxel in the beam in slice unit + if (Zplus >= minplane && Zplus <= maxplane && ring_unit == 0.5) + image[Zplus][-X][Y] += TMP2 + ring_unit * TMP1 + ZplusKorrP2n; + } +# endif // ALTERNATIVE +#endif // PIECEWISE_INTERPOLATION + + // Search for next pixel in the beam + + if (ds >= cphi) { + /* horizontal*/ + X -= 1; + ds -= cphi; + dz += dzhor; + if (dz < epsilon) { /* increment Z */ + Z++; + Q--; + dz += ring_unit; + UpA0 += HZA0; + UpA2 += HZA2; + UpA0n += HZA0n; + UpA2n += HZA2n; + UpP0 += HZP0; + UpP2 += HZP2; + UpP0n += HZP0n; + UpP2n += HZP2n; #ifdef MOREZ - while (dz=minplane)); - + check_values(proj_data_info_sptr, delta, cphi, sphi, s, ring0, X, Y, Z, ds, dz, num_planes_per_axial_pos, + axial_pos_to_z_offset); + } while ((X * X + Y * Y <= image_rad * image_rad) && (Z <= maxplane || Q >= minplane)); } /***************************************************************************** - backproj3D_Cho_view_viewplus90_180minview_90minview backprojects 8 beams + backproj3D_Cho_view_viewplus90_180minview_90minview backprojects 8 beams related by symmetry (see bckproj.h) The structure of this function is essentially the same as the previous one. Only we have more variables because we are handling 8 viewgrams here. *****************************************************************************/ - -// KT 22/05/98 new, but extracted from backproj3D_ChoViews + +// KT 22/05/98 new, but extracted from backproj3D_ChoViews // (which used to be called Backproj_Cho_Symsphitheta_P) -void +void BackProjectorByBinUsingInterpolation:: #if PIECEWISE_INTERPOLATION -piecewise_linear_interpolation_backproj3D_Cho_view_viewplus90_180minview_90minview + piecewise_linear_interpolation_backproj3D_Cho_view_viewplus90_180minview_90minview #else -linear_interpolation_backproj3D_Cho_view_viewplus90_180minview_90minview + linear_interpolation_backproj3D_Cho_view_viewplus90_180minview_90minview #endif - (Array<4, float > const& Projptr, - VoxelsOnCartesianGrid& image, - const shared_ptr proj_data_info_sptr, - float delta, - const double cphi, const double sphi, - int s, int ring0, - const int num_planes_per_axial_pos, - const float axial_pos_to_z_offset) -{ + (Array<4, float> const& Projptr, VoxelsOnCartesianGrid& image, + const shared_ptr proj_data_info_sptr, float delta, const double cphi, + const double sphi, int s, int ring0, const int num_planes_per_axial_pos, const float axial_pos_to_z_offset) { // KT 04/05/2000 new check #if PIECEWISE_INTERPOLATION - if (num_planes_per_axial_pos==1 && delta !=0) + if (num_planes_per_axial_pos == 1 && delta != 0) error("This version of the backprojector cannot be used for span>1. \ -Recompile %s with PIECEWISE_INTERPOLATION=0", __FILE__); +Recompile %s with PIECEWISE_INTERPOLATION=0", + __FILE__); #else -#ifdef ALTERNATIVE +# ifdef ALTERNATIVE // TODO - if (num_planes_per_axial_pos==1 && delta !=0) + if (num_planes_per_axial_pos == 1 && delta != 0) error("This version of the backprojector cannot be used for span>1. \ -Recompile %s with ALTERNATIVE not #defined", __FILE__); -#endif +Recompile %s with ALTERNATIVE not #defined", + __FILE__); +# endif #endif #ifndef MOREZ @@ -1079,548 +1037,532 @@ Recompile %s with ALTERNATIVE not #defined", __FILE__); As this will slow it down, we just abort at the moment. */ - - if (delta * proj_data_info_sptr_info_cyl_ptr->get_tangential_sampling() /proj_data_info_sptr_info_cyl_ptr->get_ring_radius() > 1) - { - error("This backprojector cannot handle such oblique segments: delta = %g. Sorry.\n",delta); + if (delta * proj_data_info_sptr_info_cyl_ptr->get_tangential_sampling() / proj_data_info_sptr_info_cyl_ptr->get_ring_radius() > + 1) { + error("This backprojector cannot handle such oblique segments: delta = %g. Sorry.\n", delta); } #endif // conditions on searching flow - assert(cphi>=0-.001); - assert(sphi>=0-.001); - assert(cphi+sphi>=1-.001); + assert(cphi >= 0 - .001); + assert(sphi >= 0 - .001); + assert(cphi + sphi >= 1 - .001); - const float ring_unit = 1./num_planes_per_axial_pos; + const float ring_unit = 1. / num_planes_per_axial_pos; // CL&KT 21/12/99 new // in our current coordinate system, the following constant is always 2 const int num_planes_per_physical_ring = 2; - assert(fabs(image.get_voxel_size().z() * num_planes_per_physical_ring/ proj_data_info_sptr->get_ring_spacing() -1) < 10E-4); + assert(fabs(image.get_voxel_size().z() * num_planes_per_physical_ring / proj_data_info_sptr->get_ring_spacing() - 1) < 10E-4); - double dzvert,dzhor,ds; - double dsdiag,dzdiag,dz; - int X,Y,Z,Q; + double dzvert, dzhor, ds; + double dsdiag, dzdiag, dz; + int X, Y, Z, Q; /* FOV radius in voxel units */ // KT 20/06/2001 change calculation of FOV such that even sized image will work - const float fovrad_in_mm = - min((min(image.get_max_x(), -image.get_min_x()))*image.get_voxel_size().x(), - (min(image.get_max_y(), -image.get_min_y()))*image.get_voxel_size().y()); - const float image_rad = fovrad_in_mm/image.get_voxel_size().x() - 2; - //const int image_rad = (int)((image.get_x_size()-1)/2); - //KT 20/06/2001 allow min_z!=0 in all comparisons below - const int minplane = image.get_min_z(); - const int maxplane = image.get_max_z(); - - - if(find_start_values(proj_data_info_sptr, - delta, cphi, sphi, s, ring0, - image_rad, image.get_voxel_size().z(), - X, Y, Z, - ds, dz, dzhor, dzvert, - num_planes_per_axial_pos, - axial_pos_to_z_offset) == Succeeded::no) - { - // no voxels in the beam - return; - } + const float fovrad_in_mm = min((min(image.get_max_x(), -image.get_min_x())) * image.get_voxel_size().x(), + (min(image.get_max_y(), -image.get_min_y())) * image.get_voxel_size().y()); + const float image_rad = fovrad_in_mm / image.get_voxel_size().x() - 2; + // const int image_rad = (int)((image.get_x_size()-1)/2); + // KT 20/06/2001 allow min_z!=0 in all comparisons below + const int minplane = image.get_min_z(); + const int maxplane = image.get_max_z(); + + if (find_start_values(proj_data_info_sptr, delta, cphi, sphi, s, ring0, image_rad, image.get_voxel_size().z(), X, Y, Z, ds, dz, + dzhor, dzvert, num_planes_per_axial_pos, axial_pos_to_z_offset) == Succeeded::no) { + // no voxels in the beam + return; + } // K1, K2, K3 invariants - const double K1A0=Projptr[0][0][0][2]-Projptr[0][0][0][1]; - const double K2A0=Projptr[0][0][0][3]-Projptr[0][0][0][1]; - const double K3A0=Projptr[0][0][0][4]-Projptr[0][0][0][2]-K2A0; - const double K1A1=Projptr[0][1][0][2]-Projptr[0][1][0][1]; - const double K2A1=Projptr[0][1][0][3]-Projptr[0][1][0][1]; - const double K3A1=Projptr[0][1][0][4]-Projptr[0][1][0][2]-K2A1; - const double K1A2=Projptr[0][2][0][2]-Projptr[0][2][0][1]; - const double K2A2=Projptr[0][2][0][3]-Projptr[0][2][0][1]; - const double K3A2=Projptr[0][2][0][4]-Projptr[0][2][0][2]-K2A2; - const double K1A3=Projptr[0][3][0][2]-Projptr[0][3][0][1]; - const double K2A3=Projptr[0][3][0][3]-Projptr[0][3][0][1]; - const double K3A3=Projptr[0][3][0][4]-Projptr[0][3][0][2]-K2A3; - - const double K1A0n=Projptr[1][0][0][2]-Projptr[1][0][0][1]; - const double K2A0n=Projptr[1][0][0][3]-Projptr[1][0][0][1]; - const double K3A0n=Projptr[1][0][0][4]-Projptr[1][0][0][2]-K2A0n; - const double K1A1n=Projptr[1][1][0][2]-Projptr[1][1][0][1]; - const double K2A1n=Projptr[1][1][0][3]-Projptr[1][1][0][1]; - const double K3A1n=Projptr[1][1][0][4]-Projptr[1][1][0][2]-K2A1n; - const double K1A2n=Projptr[1][2][0][2]-Projptr[1][2][0][1]; - const double K2A2n=Projptr[1][2][0][3]-Projptr[1][2][0][1]; - const double K3A2n=Projptr[1][2][0][4]-Projptr[1][2][0][2]-K2A2n; - const double K1A3n=Projptr[1][3][0][2]-Projptr[1][3][0][1]; - const double K2A3n=Projptr[1][3][0][3]-Projptr[1][3][0][1]; - const double K3A3n=Projptr[1][3][0][4]-Projptr[1][3][0][2]-K2A3n; - - const double K1P0=Projptr[0][0][1][2]-Projptr[0][0][1][1]; - const double K2P0=Projptr[0][0][1][3]-Projptr[0][0][1][1]; - const double K3P0=Projptr[0][0][1][4]-Projptr[0][0][1][2]-K2P0; - const double K1P1=Projptr[0][1][1][2]-Projptr[0][1][1][1]; - const double K2P1=Projptr[0][1][1][3]-Projptr[0][1][1][1]; - const double K3P1=Projptr[0][1][1][4]-Projptr[0][1][1][2]-K2P1; - const double K1P2=Projptr[0][2][1][2]-Projptr[0][2][1][1]; - const double K2P2=Projptr[0][2][1][3]-Projptr[0][2][1][1]; - const double K3P2=Projptr[0][2][1][4]-Projptr[0][2][1][2]-K2P2; - const double K1P3=Projptr[0][3][1][2]-Projptr[0][3][1][1]; - const double K2P3=Projptr[0][3][1][3]-Projptr[0][3][1][1]; - const double K3P3=Projptr[0][3][1][4]-Projptr[0][3][1][2]-K2P3; - - const double K1P0n=Projptr[1][0][1][2]-Projptr[1][0][1][1]; - const double K2P0n=Projptr[1][0][1][3]-Projptr[1][0][1][1]; - const double K3P0n=Projptr[1][0][1][4]-Projptr[1][0][1][2]-K2P0n; - const double K1P1n=Projptr[1][1][1][2]-Projptr[1][1][1][1]; - const double K2P1n=Projptr[1][1][1][3]-Projptr[1][1][1][1]; - const double K3P1n=Projptr[1][1][1][4]-Projptr[1][1][1][2]-K2P1n; - const double K1P2n=Projptr[1][2][1][2]-Projptr[1][2][1][1]; - const double K2P2n=Projptr[1][2][1][3]-Projptr[1][2][1][1]; - const double K3P2n=Projptr[1][2][1][4]-Projptr[1][2][1][2]-K2P2n; - const double K1P3n=Projptr[1][3][1][2]-Projptr[1][3][1][1]; - const double K2P3n=Projptr[1][3][1][3]-Projptr[1][3][1][1]; - const double K3P3n=Projptr[1][3][1][4]-Projptr[1][3][1][2]-K2P3n; + const double K1A0 = Projptr[0][0][0][2] - Projptr[0][0][0][1]; + const double K2A0 = Projptr[0][0][0][3] - Projptr[0][0][0][1]; + const double K3A0 = Projptr[0][0][0][4] - Projptr[0][0][0][2] - K2A0; + const double K1A1 = Projptr[0][1][0][2] - Projptr[0][1][0][1]; + const double K2A1 = Projptr[0][1][0][3] - Projptr[0][1][0][1]; + const double K3A1 = Projptr[0][1][0][4] - Projptr[0][1][0][2] - K2A1; + const double K1A2 = Projptr[0][2][0][2] - Projptr[0][2][0][1]; + const double K2A2 = Projptr[0][2][0][3] - Projptr[0][2][0][1]; + const double K3A2 = Projptr[0][2][0][4] - Projptr[0][2][0][2] - K2A2; + const double K1A3 = Projptr[0][3][0][2] - Projptr[0][3][0][1]; + const double K2A3 = Projptr[0][3][0][3] - Projptr[0][3][0][1]; + const double K3A3 = Projptr[0][3][0][4] - Projptr[0][3][0][2] - K2A3; + + const double K1A0n = Projptr[1][0][0][2] - Projptr[1][0][0][1]; + const double K2A0n = Projptr[1][0][0][3] - Projptr[1][0][0][1]; + const double K3A0n = Projptr[1][0][0][4] - Projptr[1][0][0][2] - K2A0n; + const double K1A1n = Projptr[1][1][0][2] - Projptr[1][1][0][1]; + const double K2A1n = Projptr[1][1][0][3] - Projptr[1][1][0][1]; + const double K3A1n = Projptr[1][1][0][4] - Projptr[1][1][0][2] - K2A1n; + const double K1A2n = Projptr[1][2][0][2] - Projptr[1][2][0][1]; + const double K2A2n = Projptr[1][2][0][3] - Projptr[1][2][0][1]; + const double K3A2n = Projptr[1][2][0][4] - Projptr[1][2][0][2] - K2A2n; + const double K1A3n = Projptr[1][3][0][2] - Projptr[1][3][0][1]; + const double K2A3n = Projptr[1][3][0][3] - Projptr[1][3][0][1]; + const double K3A3n = Projptr[1][3][0][4] - Projptr[1][3][0][2] - K2A3n; + + const double K1P0 = Projptr[0][0][1][2] - Projptr[0][0][1][1]; + const double K2P0 = Projptr[0][0][1][3] - Projptr[0][0][1][1]; + const double K3P0 = Projptr[0][0][1][4] - Projptr[0][0][1][2] - K2P0; + const double K1P1 = Projptr[0][1][1][2] - Projptr[0][1][1][1]; + const double K2P1 = Projptr[0][1][1][3] - Projptr[0][1][1][1]; + const double K3P1 = Projptr[0][1][1][4] - Projptr[0][1][1][2] - K2P1; + const double K1P2 = Projptr[0][2][1][2] - Projptr[0][2][1][1]; + const double K2P2 = Projptr[0][2][1][3] - Projptr[0][2][1][1]; + const double K3P2 = Projptr[0][2][1][4] - Projptr[0][2][1][2] - K2P2; + const double K1P3 = Projptr[0][3][1][2] - Projptr[0][3][1][1]; + const double K2P3 = Projptr[0][3][1][3] - Projptr[0][3][1][1]; + const double K3P3 = Projptr[0][3][1][4] - Projptr[0][3][1][2] - K2P3; + + const double K1P0n = Projptr[1][0][1][2] - Projptr[1][0][1][1]; + const double K2P0n = Projptr[1][0][1][3] - Projptr[1][0][1][1]; + const double K3P0n = Projptr[1][0][1][4] - Projptr[1][0][1][2] - K2P0n; + const double K1P1n = Projptr[1][1][1][2] - Projptr[1][1][1][1]; + const double K2P1n = Projptr[1][1][1][3] - Projptr[1][1][1][1]; + const double K3P1n = Projptr[1][1][1][4] - Projptr[1][1][1][2] - K2P1n; + const double K1P2n = Projptr[1][2][1][2] - Projptr[1][2][1][1]; + const double K2P2n = Projptr[1][2][1][3] - Projptr[1][2][1][1]; + const double K3P2n = Projptr[1][2][1][4] - Projptr[1][2][1][2] - K2P2n; + const double K1P3n = Projptr[1][3][1][2] - Projptr[1][3][1][1]; + const double K2P3n = Projptr[1][3][1][3] - Projptr[1][3][1][1]; + const double K3P3n = Projptr[1][3][1][4] - Projptr[1][3][1][2] - K2P3n; #if !PIECEWISE_INTERPOLATION + const double ZplusKorrA0 = ring_unit * K2A0; + const double ZplusKorrA1 = ring_unit * K2A1; + const double ZplusKorrA2 = ring_unit * K2A2; + const double ZplusKorrA3 = ring_unit * K2A3; - const double ZplusKorrA0=ring_unit*K2A0; - const double ZplusKorrA1=ring_unit*K2A1; - const double ZplusKorrA2=ring_unit*K2A2; - const double ZplusKorrA3=ring_unit*K2A3; + const double ZplusKorrA0n = ring_unit * K2A0n; + const double ZplusKorrA1n = ring_unit * K2A1n; + const double ZplusKorrA2n = ring_unit * K2A2n; + const double ZplusKorrA3n = ring_unit * K2A3n; - const double ZplusKorrA0n=ring_unit*K2A0n; - const double ZplusKorrA1n=ring_unit*K2A1n; - const double ZplusKorrA2n=ring_unit*K2A2n; - const double ZplusKorrA3n=ring_unit*K2A3n; + const double ZplusKorrP0 = ring_unit * K2P0; + const double ZplusKorrP1 = ring_unit * K2P1; + const double ZplusKorrP2 = ring_unit * K2P2; + const double ZplusKorrP3 = ring_unit * K2P3; - const double ZplusKorrP0=ring_unit*K2P0; - const double ZplusKorrP1=ring_unit*K2P1; - const double ZplusKorrP2=ring_unit*K2P2; - const double ZplusKorrP3=ring_unit*K2P3; - - const double ZplusKorrP0n=ring_unit*K2P0n; - const double ZplusKorrP1n=ring_unit*K2P1n; - const double ZplusKorrP2n=ring_unit*K2P2n; - const double ZplusKorrP3n=ring_unit*K2P3n; + const double ZplusKorrP0n = ring_unit * K2P0n; + const double ZplusKorrP1n = ring_unit * K2P1n; + const double ZplusKorrP2n = ring_unit * K2P2n; + const double ZplusKorrP3n = ring_unit * K2P3n; // V, H, D invariants - const double VA0=dzvert*K2A0+sphi*K1A0; - const double HA0=dzhor*K2A0-cphi*K1A0; - const double DA0=VA0+HA0; - const double VZA0=VA0+ring_unit*K2A0; - const double HZA0=HA0+ring_unit*K2A0; - const double DZA0=DA0+ring_unit*K2A0; - const double VA1=dzvert*K2A1+sphi*K1A1; - const double HA1=dzhor*K2A1-cphi*K1A1; - const double DA1=VA1+HA1; - const double VZA1=VA1+ring_unit*K2A1; - const double HZA1=HA1+ring_unit*K2A1; - const double DZA1=DA1+ring_unit*K2A1; - const double VA2=dzvert*K2A2+sphi*K1A2; - const double HA2=dzhor*K2A2-cphi*K1A2; - const double DA2=VA2+HA2; - const double VZA2=VA2+ring_unit*K2A2; - const double HZA2=HA2+ring_unit*K2A2; - const double DZA2=DA2+ring_unit*K2A2; - const double VA3=dzvert*K2A3+sphi*K1A3; - const double HA3=dzhor*K2A3-cphi*K1A3; - const double DA3=VA3+HA3; - const double VZA3=VA3+ring_unit*K2A3; - const double HZA3=HA3+ring_unit*K2A3; - const double DZA3=DA3+ring_unit*K2A3; - - const double VA0n=dzvert*K2A0n+sphi*K1A0n; - const double HA0n=dzhor*K2A0n-cphi*K1A0n; - const double DA0n=VA0n+HA0n; - const double VZA0n=VA0n+ring_unit*K2A0n; - const double HZA0n=HA0n+ring_unit*K2A0n; - const double DZA0n=DA0n+ring_unit*K2A0n; - const double VA1n=dzvert*K2A1n+sphi*K1A1n; - const double HA1n=dzhor*K2A1n-cphi*K1A1n; - const double DA1n=VA1n+HA1n; - const double VZA1n=VA1n+ring_unit*K2A1n; - const double HZA1n=HA1n+ring_unit*K2A1n; - const double DZA1n=DA1n+ring_unit*K2A1n; - const double VA2n=dzvert*K2A2n+sphi*K1A2n; - const double HA2n=dzhor*K2A2n-cphi*K1A2n; - const double DA2n=VA2n+HA2n; - const double VZA2n=VA2n+ring_unit*K2A2n; - const double HZA2n=HA2n+ring_unit*K2A2n; - const double DZA2n=DA2n+ring_unit*K2A2n; - const double VA3n=dzvert*K2A3n+sphi*K1A3n; - const double HA3n=dzhor*K2A3n-cphi*K1A3n; - const double DA3n=VA3n+HA3n; - const double VZA3n=VA3n+ring_unit*K2A3n; - const double HZA3n=HA3n+ring_unit*K2A3n; - const double DZA3n=DA3n+ring_unit*K2A3n; - - const double VP0=dzvert*K2P0+sphi*K1P0; - const double HP0=dzhor*K2P0-cphi*K1P0; - const double DP0=VP0+HP0; - const double VZP0=VP0+ring_unit*K2P0; - const double HZP0=HP0+ring_unit*K2P0; - const double DZP0=DP0+ring_unit*K2P0; - const double VP1=dzvert*K2P1+sphi*K1P1; - const double HP1=dzhor*K2P1-cphi*K1P1; - const double DP1=VP1+HP1; - const double VZP1=VP1+ring_unit*K2P1; - const double HZP1=HP1+ring_unit*K2P1; - const double DZP1=DP1+ring_unit*K2P1; - const double VP2=dzvert*K2P2+sphi*K1P2; - const double HP2=dzhor*K2P2-cphi*K1P2; - const double DP2=VP2+HP2; - const double VZP2=VP2+ring_unit*K2P2; - const double HZP2=HP2+ring_unit*K2P2; - const double DZP2=DP2+ring_unit*K2P2; - const double VP3=dzvert*K2P3+sphi*K1P3; - const double HP3=dzhor*K2P3-cphi*K1P3; - const double DP3=VP3+HP3; - const double VZP3=VP3+ring_unit*K2P3; - const double HZP3=HP3+ring_unit*K2P3; - const double DZP3=DP3+ring_unit*K2P3; - - const double VP0n=dzvert*K2P0n+sphi*K1P0n; - const double HP0n=dzhor*K2P0n-cphi*K1P0n; - const double DP0n=VP0n+HP0n; - const double VZP0n=VP0n+ring_unit*K2P0n; - const double HZP0n=HP0n+ring_unit*K2P0n; - const double DZP0n=DP0n+ring_unit*K2P0n; - const double VP1n=dzvert*K2P1n+sphi*K1P1n; - const double HP1n=dzhor*K2P1n-cphi*K1P1n; - const double DP1n=VP1n+HP1n; - const double VZP1n=VP1n+ring_unit*K2P1n; - const double HZP1n=HP1n+ring_unit*K2P1n; - const double DZP1n=DP1n+ring_unit*K2P1n; - const double VP2n=dzvert*K2P2n+sphi*K1P2n; - const double HP2n=dzhor*K2P2n-cphi*K1P2n; - const double DP2n=VP2n+HP2n; - const double VZP2n=VP2n+ring_unit*K2P2n; - const double HZP2n=HP2n+ring_unit*K2P2n; - const double DZP2n=DP2n+ring_unit*K2P2n; - const double VP3n=dzvert*K2P3n+sphi*K1P3n; - const double HP3n=dzhor*K2P3n-cphi*K1P3n; - const double DP3n=VP3n+HP3n; - const double VZP3n=VP3n+ring_unit*K2P3n; - const double HZP3n=HP3n+ring_unit*K2P3n; - const double DZP3n=DP3n+ring_unit*K2P3n; - - + const double VA0 = dzvert * K2A0 + sphi * K1A0; + const double HA0 = dzhor * K2A0 - cphi * K1A0; + const double DA0 = VA0 + HA0; + const double VZA0 = VA0 + ring_unit * K2A0; + const double HZA0 = HA0 + ring_unit * K2A0; + const double DZA0 = DA0 + ring_unit * K2A0; + const double VA1 = dzvert * K2A1 + sphi * K1A1; + const double HA1 = dzhor * K2A1 - cphi * K1A1; + const double DA1 = VA1 + HA1; + const double VZA1 = VA1 + ring_unit * K2A1; + const double HZA1 = HA1 + ring_unit * K2A1; + const double DZA1 = DA1 + ring_unit * K2A1; + const double VA2 = dzvert * K2A2 + sphi * K1A2; + const double HA2 = dzhor * K2A2 - cphi * K1A2; + const double DA2 = VA2 + HA2; + const double VZA2 = VA2 + ring_unit * K2A2; + const double HZA2 = HA2 + ring_unit * K2A2; + const double DZA2 = DA2 + ring_unit * K2A2; + const double VA3 = dzvert * K2A3 + sphi * K1A3; + const double HA3 = dzhor * K2A3 - cphi * K1A3; + const double DA3 = VA3 + HA3; + const double VZA3 = VA3 + ring_unit * K2A3; + const double HZA3 = HA3 + ring_unit * K2A3; + const double DZA3 = DA3 + ring_unit * K2A3; + + const double VA0n = dzvert * K2A0n + sphi * K1A0n; + const double HA0n = dzhor * K2A0n - cphi * K1A0n; + const double DA0n = VA0n + HA0n; + const double VZA0n = VA0n + ring_unit * K2A0n; + const double HZA0n = HA0n + ring_unit * K2A0n; + const double DZA0n = DA0n + ring_unit * K2A0n; + const double VA1n = dzvert * K2A1n + sphi * K1A1n; + const double HA1n = dzhor * K2A1n - cphi * K1A1n; + const double DA1n = VA1n + HA1n; + const double VZA1n = VA1n + ring_unit * K2A1n; + const double HZA1n = HA1n + ring_unit * K2A1n; + const double DZA1n = DA1n + ring_unit * K2A1n; + const double VA2n = dzvert * K2A2n + sphi * K1A2n; + const double HA2n = dzhor * K2A2n - cphi * K1A2n; + const double DA2n = VA2n + HA2n; + const double VZA2n = VA2n + ring_unit * K2A2n; + const double HZA2n = HA2n + ring_unit * K2A2n; + const double DZA2n = DA2n + ring_unit * K2A2n; + const double VA3n = dzvert * K2A3n + sphi * K1A3n; + const double HA3n = dzhor * K2A3n - cphi * K1A3n; + const double DA3n = VA3n + HA3n; + const double VZA3n = VA3n + ring_unit * K2A3n; + const double HZA3n = HA3n + ring_unit * K2A3n; + const double DZA3n = DA3n + ring_unit * K2A3n; + + const double VP0 = dzvert * K2P0 + sphi * K1P0; + const double HP0 = dzhor * K2P0 - cphi * K1P0; + const double DP0 = VP0 + HP0; + const double VZP0 = VP0 + ring_unit * K2P0; + const double HZP0 = HP0 + ring_unit * K2P0; + const double DZP0 = DP0 + ring_unit * K2P0; + const double VP1 = dzvert * K2P1 + sphi * K1P1; + const double HP1 = dzhor * K2P1 - cphi * K1P1; + const double DP1 = VP1 + HP1; + const double VZP1 = VP1 + ring_unit * K2P1; + const double HZP1 = HP1 + ring_unit * K2P1; + const double DZP1 = DP1 + ring_unit * K2P1; + const double VP2 = dzvert * K2P2 + sphi * K1P2; + const double HP2 = dzhor * K2P2 - cphi * K1P2; + const double DP2 = VP2 + HP2; + const double VZP2 = VP2 + ring_unit * K2P2; + const double HZP2 = HP2 + ring_unit * K2P2; + const double DZP2 = DP2 + ring_unit * K2P2; + const double VP3 = dzvert * K2P3 + sphi * K1P3; + const double HP3 = dzhor * K2P3 - cphi * K1P3; + const double DP3 = VP3 + HP3; + const double VZP3 = VP3 + ring_unit * K2P3; + const double HZP3 = HP3 + ring_unit * K2P3; + const double DZP3 = DP3 + ring_unit * K2P3; + + const double VP0n = dzvert * K2P0n + sphi * K1P0n; + const double HP0n = dzhor * K2P0n - cphi * K1P0n; + const double DP0n = VP0n + HP0n; + const double VZP0n = VP0n + ring_unit * K2P0n; + const double HZP0n = HP0n + ring_unit * K2P0n; + const double DZP0n = DP0n + ring_unit * K2P0n; + const double VP1n = dzvert * K2P1n + sphi * K1P1n; + const double HP1n = dzhor * K2P1n - cphi * K1P1n; + const double DP1n = VP1n + HP1n; + const double VZP1n = VP1n + ring_unit * K2P1n; + const double HZP1n = HP1n + ring_unit * K2P1n; + const double DZP1n = DP1n + ring_unit * K2P1n; + const double VP2n = dzvert * K2P2n + sphi * K1P2n; + const double HP2n = dzhor * K2P2n - cphi * K1P2n; + const double DP2n = VP2n + HP2n; + const double VZP2n = VP2n + ring_unit * K2P2n; + const double HZP2n = HP2n + ring_unit * K2P2n; + const double DZP2n = DP2n + ring_unit * K2P2n; + const double VP3n = dzvert * K2P3n + sphi * K1P3n; + const double HP3n = dzhor * K2P3n - cphi * K1P3n; + const double DP3n = VP3n + HP3n; + const double VZP3n = VP3n + ring_unit * K2P3n; + const double HZP3n = HP3n + ring_unit * K2P3n; + const double DZP3n = DP3n + ring_unit * K2P3n; + // Initial values of update values (Up) - double UpA0=Projptr[0][0][0][1]+ds*K1A0+dz*K2A0; - double UpA1=Projptr[0][1][0][1]+ds*K1A1+dz*K2A1; - double UpA2=Projptr[0][2][0][1]+ds*K1A2+dz*K2A2; - double UpA3=Projptr[0][3][0][1]+ds*K1A3+dz*K2A3; + double UpA0 = Projptr[0][0][0][1] + ds * K1A0 + dz * K2A0; + double UpA1 = Projptr[0][1][0][1] + ds * K1A1 + dz * K2A1; + double UpA2 = Projptr[0][2][0][1] + ds * K1A2 + dz * K2A2; + double UpA3 = Projptr[0][3][0][1] + ds * K1A3 + dz * K2A3; - double UpA0n=Projptr[1][0][0][1]+ds*K1A0n+dz*K2A0n; - double UpA1n=Projptr[1][1][0][1]+ds*K1A1n+dz*K2A1n; - double UpA2n=Projptr[1][2][0][1]+ds*K1A2n+dz*K2A2n; - double UpA3n=Projptr[1][3][0][1]+ds*K1A3n+dz*K2A3n; + double UpA0n = Projptr[1][0][0][1] + ds * K1A0n + dz * K2A0n; + double UpA1n = Projptr[1][1][0][1] + ds * K1A1n + dz * K2A1n; + double UpA2n = Projptr[1][2][0][1] + ds * K1A2n + dz * K2A2n; + double UpA3n = Projptr[1][3][0][1] + ds * K1A3n + dz * K2A3n; - double UpP0=Projptr[0][0][1][1]+ds*K1P0+dz*K2P0; - double UpP1=Projptr[0][1][1][1]+ds*K1P1+dz*K2P1; - double UpP2=Projptr[0][2][1][1]+ds*K1P2+dz*K2P2; - double UpP3=Projptr[0][3][1][1]+ds*K1P3+dz*K2P3; + double UpP0 = Projptr[0][0][1][1] + ds * K1P0 + dz * K2P0; + double UpP1 = Projptr[0][1][1][1] + ds * K1P1 + dz * K2P1; + double UpP2 = Projptr[0][2][1][1] + ds * K1P2 + dz * K2P2; + double UpP3 = Projptr[0][3][1][1] + ds * K1P3 + dz * K2P3; - double UpP0n=Projptr[1][0][1][1]+ds*K1P0n+dz*K2P0n; - double UpP1n=Projptr[1][1][1][1]+ds*K1P1n+dz*K2P1n; - double UpP2n=Projptr[1][2][1][1]+ds*K1P2n+dz*K2P2n; - double UpP3n=Projptr[1][3][1][1]+ds*K1P3n+dz*K2P3n; + double UpP0n = Projptr[1][0][1][1] + ds * K1P0n + dz * K2P0n; + double UpP1n = Projptr[1][1][1][1] + ds * K1P1n + dz * K2P1n; + double UpP2n = Projptr[1][2][1][1] + ds * K1P2n + dz * K2P2n; + double UpP3n = Projptr[1][3][1][1] + ds * K1P3n + dz * K2P3n; #else // PIECEWISE_INTERPOLATION + const double ZplusKorrA0 = K2A0; + const double ZplusKorrA1 = K2A1; + const double ZplusKorrA2 = K2A2; + const double ZplusKorrA3 = K2A3; - const double ZplusKorrA0=K2A0; - const double ZplusKorrA1=K2A1; - const double ZplusKorrA2=K2A2; - const double ZplusKorrA3=K2A3; + const double ZplusKorrA0n = K2A0n; + const double ZplusKorrA1n = K2A1n; + const double ZplusKorrA2n = K2A2n; + const double ZplusKorrA3n = K2A3n; - const double ZplusKorrA0n=K2A0n; - const double ZplusKorrA1n=K2A1n; - const double ZplusKorrA2n=K2A2n; - const double ZplusKorrA3n=K2A3n; + const double ZplusKorrP0 = K2P0; + const double ZplusKorrP1 = K2P1; + const double ZplusKorrP2 = K2P2; + const double ZplusKorrP3 = K2P3; - const double ZplusKorrP0=K2P0; - const double ZplusKorrP1=K2P1; - const double ZplusKorrP2=K2P2; - const double ZplusKorrP3=K2P3; - - const double ZplusKorrP0n=K2P0n; - const double ZplusKorrP1n=K2P1n; - const double ZplusKorrP2n=K2P2n; - const double ZplusKorrP3n=K2P3n; + const double ZplusKorrP0n = K2P0n; + const double ZplusKorrP1n = K2P1n; + const double ZplusKorrP2n = K2P2n; + const double ZplusKorrP3n = K2P3n; // V, H, D invariants - const double VA0a0=+sphi*K1A0; - const double HA0a0=-cphi*K1A0; - const double DA0a0=VA0a0+HA0a0; - const double VA1a0=+sphi*K1A1; - const double HA1a0=-cphi*K1A1; - const double DA1a0=VA1a0+HA1a0; - const double VA2a0=+sphi*K1A2; - const double HA2a0=-cphi*K1A2; - const double DA2a0=VA2a0+HA2a0; - const double VA3a0=+sphi*K1A3; - const double HA3a0=-cphi*K1A3; - const double DA3a0=VA3a0+HA3a0; - - const double VA0na0=+sphi*K1A0n; - const double HA0na0=-cphi*K1A0n; - const double DA0na0=VA0na0+HA0na0; - const double VA1na0=+sphi*K1A1n; - const double HA1na0=-cphi*K1A1n; - const double DA1na0=VA1na0+HA1na0; - const double VA2na0=+sphi*K1A2n; - const double HA2na0=-cphi*K1A2n; - const double DA2na0=VA2na0+HA2na0; - const double VA3na0=+sphi*K1A3n; - const double HA3na0=-cphi*K1A3n; - const double DA3na0=VA3na0+HA3na0; - - const double VP0a0=+sphi*K1P0; - const double HP0a0=-cphi*K1P0; - const double DP0a0=VP0a0+HP0a0; - const double VP1a0=+sphi*K1P1; - const double HP1a0=-cphi*K1P1; - const double DP1a0=VP1a0+HP1a0; - const double VP2a0=+sphi*K1P2; - const double HP2a0=-cphi*K1P2; - const double DP2a0=VP2a0+HP2a0; - const double VP3a0=+sphi*K1P3; - const double HP3a0=-cphi*K1P3; - const double DP3a0=VP3a0+HP3a0; - - const double VP0na0=+sphi*K1P0n; - const double HP0na0=-cphi*K1P0n; - const double DP0na0=VP0na0+HP0na0; - const double VP1na0=+sphi*K1P1n; - const double HP1na0=-cphi*K1P1n; - const double DP1na0=VP1na0+HP1na0; - const double VP2na0=+sphi*K1P2n; - const double HP2na0=-cphi*K1P2n; - const double DP2na0=VP2na0+HP2na0; - const double VP3na0=+sphi*K1P3n; - const double HP3na0=-cphi*K1P3n; - const double DP3na0=VP3na0+HP3na0; - - - - const double VA0a1=+sphi*(K1A0+K3A0); - const double HA0a1=-cphi*(K1A0+K3A0); - const double DA0a1=VA0a1+HA0a1; - const double VA1a1=+sphi*(K1A1+K3A1); - const double HA1a1=-cphi*(K1A1+K3A1); - const double DA1a1=VA1a1+HA1a1; - const double VA2a1=+sphi*(K1A2+K3A2); - const double HA2a1=-cphi*(K1A2+K3A2); - const double DA2a1=VA2a1+HA2a1; - const double VA3a1=+sphi*(K1A3+K3A3); - const double HA3a1=-cphi*(K1A3+K3A3); - const double DA3a1=VA3a1+HA3a1; - - const double VA0na1=+sphi*(K1A0n+K3A0n); - const double HA0na1=-cphi*(K1A0n+K3A0n); - const double DA0na1=VA0na1+HA0na1; - const double VA1na1=+sphi*(K1A1n+K3A1n); - const double HA1na1=-cphi*(K1A1n+K3A1n); - const double DA1na1=VA1na1+HA1na1; - const double VA2na1=+sphi*(K1A2n+K3A2n); - const double HA2na1=-cphi*(K1A2n+K3A2n); - const double DA2na1=VA2na1+HA2na1; - const double VA3na1=+sphi*(K1A3n+K3A3n); - const double HA3na1=-cphi*(K1A3n+K3A3n); - const double DA3na1=VA3na1+HA3na1; - - const double VP0a1=+sphi*(K1P0+K3P0); - const double HP0a1=-cphi*(K1P0+K3P0); - const double DP0a1=VP0a1+HP0a1; - const double VP1a1=+sphi*(K1P1+K3P1); - const double HP1a1=-cphi*(K1P1+K3P1); - const double DP1a1=VP1a1+HP1a1; - const double VP2a1=+sphi*(K1P2+K3P2); - const double HP2a1=-cphi*(K1P2+K3P2); - const double DP2a1=VP2a1+HP2a1; - const double VP3a1=+sphi*(K1P3+K3P3); - const double HP3a1=-cphi*(K1P3+K3P3); - const double DP3a1=VP3a1+HP3a1; - - const double VP0na1=+sphi*(K1P0n+K3P0n); - const double HP0na1=-cphi*(K1P0n+K3P0n); - const double DP0na1=VP0na1+HP0na1; - const double VP1na1=+sphi*(K1P1n+K3P1n); - const double HP1na1=-cphi*(K1P1n+K3P1n); - const double DP1na1=VP1na1+HP1na1; - const double VP2na1=+sphi*(K1P2n+K3P2n); - const double HP2na1=-cphi*(K1P2n+K3P2n); - const double DP2na1=VP2na1+HP2na1; - const double VP3na1=+sphi*(K1P3n+K3P3n); - const double HP3na1=-cphi*(K1P3n+K3P3n); - const double DP3na1=VP3na1+HP3na1; - - - const double VA0=VA0a0*1.5 - VA0a1/2 + 2*dzvert*K2A0; - const double HA0=HA0a0*1.5 - HA0a1/2 + 2*dzhor*K2A0; - const double DA0=VA0+HA0; - const double VZA0=VA0+K2A0; - const double HZA0=HA0+K2A0; - const double DZA0=DA0+K2A0; - const double VA1=VA1a0*1.5 - VA1a1/2 + 2*dzvert*K2A1; - const double HA1=HA1a0*1.5 - HA1a1/2 + 2*dzhor*K2A1; - const double DA1=VA1+HA1; - const double VZA1=VA1+K2A1; - const double HZA1=HA1+K2A1; - const double DZA1=DA1+K2A1; - const double VA2=VA2a0*1.5 - VA2a1/2 + 2*dzvert*K2A2; - const double HA2=HA2a0*1.5 - HA2a1/2 + 2*dzhor*K2A2; - const double DA2=VA2+HA2; - const double VZA2=VA2+K2A2; - const double HZA2=HA2+K2A2; - const double DZA2=DA2+K2A2; - const double VA3=VA3a0*1.5 - VA3a1/2 + 2*dzvert*K2A3; - const double HA3=HA3a0*1.5 - HA3a1/2 + 2*dzhor*K2A3; - const double DA3=VA3+HA3; - const double VZA3=VA3+K2A3; - const double HZA3=HA3+K2A3; - const double DZA3=DA3+K2A3; - - const double VA0n=VA0na0*1.5 - VA0na1/2 + 2*dzvert*K2A0n; - const double HA0n=HA0na0*1.5 - HA0na1/2 + 2*dzhor*K2A0n; - const double DA0n=VA0n+HA0n; - const double VZA0n=VA0n+K2A0n; - const double HZA0n=HA0n+K2A0n; - const double DZA0n=DA0n+K2A0n; - const double VA1n=VA1na0*1.5 - VA1na1/2 + 2*dzvert*K2A1n; - const double HA1n=HA1na0*1.5 - HA1na1/2 + 2*dzhor*K2A1n; - const double DA1n=VA1n+HA1n; - const double VZA1n=VA1n+K2A1n; - const double HZA1n=HA1n+K2A1n; - const double DZA1n=DA1n+K2A1n; - const double VA2n=VA2na0*1.5 - VA2na1/2 + 2*dzvert*K2A2n; - const double HA2n=HA2na0*1.5 - HA2na1/2 + 2*dzhor*K2A2n; - const double DA2n=VA2n+HA2n; - const double VZA2n=VA2n+K2A2n; - const double HZA2n=HA2n+K2A2n; - const double DZA2n=DA2n+K2A2n; - const double VA3n=VA3na0*1.5 - VA3na1/2 + 2*dzvert*K2A3n; - const double HA3n=HA3na0*1.5 - HA3na1/2 + 2*dzhor*K2A3n; - const double DA3n=VA3n+HA3n; - const double VZA3n=VA3n+K2A3n; - const double HZA3n=HA3n+K2A3n; - const double DZA3n=DA3n+K2A3n; - - const double VP0=VP0a0*1.5 - VP0a1/2 + 2*dzvert*K2P0; - const double HP0=HP0a0*1.5 - HP0a1/2 + 2*dzhor*K2P0; - const double DP0=VP0+HP0; - const double VZP0=VP0+K2P0; - const double HZP0=HP0+K2P0; - const double DZP0=DP0+K2P0; - const double VP1=VP1a0*1.5 - VP1a1/2 + 2*dzvert*K2P1; - const double HP1=HP1a0*1.5 - HP1a1/2 + 2*dzhor*K2P1; - const double DP1=VP1+HP1; - const double VZP1=VP1+K2P1; - const double HZP1=HP1+K2P1; - const double DZP1=DP1+K2P1; - const double VP2=VP2a0*1.5 - VP2a1/2 + 2*dzvert*K2P2; - const double HP2=HP2a0*1.5 - HP2a1/2 + 2*dzhor*K2P2; - const double DP2=VP2+HP2; - const double VZP2=VP2+K2P2; - const double HZP2=HP2+K2P2; - const double DZP2=DP2+K2P2; - const double VP3=VP3a0*1.5 - VP3a1/2 + 2*dzvert*K2P3; - const double HP3=HP3a0*1.5 - HP3a1/2 + 2*dzhor*K2P3; - const double DP3=VP3+HP3; - const double VZP3=VP3+K2P3; - const double HZP3=HP3+K2P3; - const double DZP3=DP3+K2P3; - - const double VP0n=VP0na0*1.5 - VP0na1/2 + 2*dzvert*K2P0n; - const double HP0n=HP0na0*1.5 - HP0na1/2 + 2*dzhor*K2P0n; - const double DP0n=VP0n+HP0n; - const double VZP0n=VP0n+K2P0n; - const double HZP0n=HP0n+K2P0n; - const double DZP0n=DP0n+K2P0n; - const double VP1n=VP1na0*1.5 - VP1na1/2 + 2*dzvert*K2P1n; - const double HP1n=HP1na0*1.5 - HP1na1/2 + 2*dzhor*K2P1n; - const double DP1n=VP1n+HP1n; - const double VZP1n=VP1n+K2P1n; - const double HZP1n=HP1n+K2P1n; - const double DZP1n=DP1n+K2P1n; - const double VP2n=VP2na0*1.5 - VP2na1/2 + 2*dzvert*K2P2n; - const double HP2n=HP2na0*1.5 - HP2na1/2 + 2*dzhor*K2P2n; - const double DP2n=VP2n+HP2n; - const double VZP2n=VP2n+K2P2n; - const double HZP2n=HP2n+K2P2n; - const double DZP2n=DP2n+K2P2n; - const double VP3n=VP3na0*1.5 - VP3na1/2 + 2*dzvert*K2P3n; - const double HP3n=HP3na0*1.5 - HP3na1/2 + 2*dzhor*K2P3n; - const double DP3n=VP3n+HP3n; - const double VZP3n=VP3n+K2P3n; - const double HZP3n=HP3n+K2P3n; - const double DZP3n=DP3n+K2P3n; - - + const double VA0a0 = +sphi * K1A0; + const double HA0a0 = -cphi * K1A0; + const double DA0a0 = VA0a0 + HA0a0; + const double VA1a0 = +sphi * K1A1; + const double HA1a0 = -cphi * K1A1; + const double DA1a0 = VA1a0 + HA1a0; + const double VA2a0 = +sphi * K1A2; + const double HA2a0 = -cphi * K1A2; + const double DA2a0 = VA2a0 + HA2a0; + const double VA3a0 = +sphi * K1A3; + const double HA3a0 = -cphi * K1A3; + const double DA3a0 = VA3a0 + HA3a0; + + const double VA0na0 = +sphi * K1A0n; + const double HA0na0 = -cphi * K1A0n; + const double DA0na0 = VA0na0 + HA0na0; + const double VA1na0 = +sphi * K1A1n; + const double HA1na0 = -cphi * K1A1n; + const double DA1na0 = VA1na0 + HA1na0; + const double VA2na0 = +sphi * K1A2n; + const double HA2na0 = -cphi * K1A2n; + const double DA2na0 = VA2na0 + HA2na0; + const double VA3na0 = +sphi * K1A3n; + const double HA3na0 = -cphi * K1A3n; + const double DA3na0 = VA3na0 + HA3na0; + + const double VP0a0 = +sphi * K1P0; + const double HP0a0 = -cphi * K1P0; + const double DP0a0 = VP0a0 + HP0a0; + const double VP1a0 = +sphi * K1P1; + const double HP1a0 = -cphi * K1P1; + const double DP1a0 = VP1a0 + HP1a0; + const double VP2a0 = +sphi * K1P2; + const double HP2a0 = -cphi * K1P2; + const double DP2a0 = VP2a0 + HP2a0; + const double VP3a0 = +sphi * K1P3; + const double HP3a0 = -cphi * K1P3; + const double DP3a0 = VP3a0 + HP3a0; + + const double VP0na0 = +sphi * K1P0n; + const double HP0na0 = -cphi * K1P0n; + const double DP0na0 = VP0na0 + HP0na0; + const double VP1na0 = +sphi * K1P1n; + const double HP1na0 = -cphi * K1P1n; + const double DP1na0 = VP1na0 + HP1na0; + const double VP2na0 = +sphi * K1P2n; + const double HP2na0 = -cphi * K1P2n; + const double DP2na0 = VP2na0 + HP2na0; + const double VP3na0 = +sphi * K1P3n; + const double HP3na0 = -cphi * K1P3n; + const double DP3na0 = VP3na0 + HP3na0; + + const double VA0a1 = +sphi * (K1A0 + K3A0); + const double HA0a1 = -cphi * (K1A0 + K3A0); + const double DA0a1 = VA0a1 + HA0a1; + const double VA1a1 = +sphi * (K1A1 + K3A1); + const double HA1a1 = -cphi * (K1A1 + K3A1); + const double DA1a1 = VA1a1 + HA1a1; + const double VA2a1 = +sphi * (K1A2 + K3A2); + const double HA2a1 = -cphi * (K1A2 + K3A2); + const double DA2a1 = VA2a1 + HA2a1; + const double VA3a1 = +sphi * (K1A3 + K3A3); + const double HA3a1 = -cphi * (K1A3 + K3A3); + const double DA3a1 = VA3a1 + HA3a1; + + const double VA0na1 = +sphi * (K1A0n + K3A0n); + const double HA0na1 = -cphi * (K1A0n + K3A0n); + const double DA0na1 = VA0na1 + HA0na1; + const double VA1na1 = +sphi * (K1A1n + K3A1n); + const double HA1na1 = -cphi * (K1A1n + K3A1n); + const double DA1na1 = VA1na1 + HA1na1; + const double VA2na1 = +sphi * (K1A2n + K3A2n); + const double HA2na1 = -cphi * (K1A2n + K3A2n); + const double DA2na1 = VA2na1 + HA2na1; + const double VA3na1 = +sphi * (K1A3n + K3A3n); + const double HA3na1 = -cphi * (K1A3n + K3A3n); + const double DA3na1 = VA3na1 + HA3na1; + + const double VP0a1 = +sphi * (K1P0 + K3P0); + const double HP0a1 = -cphi * (K1P0 + K3P0); + const double DP0a1 = VP0a1 + HP0a1; + const double VP1a1 = +sphi * (K1P1 + K3P1); + const double HP1a1 = -cphi * (K1P1 + K3P1); + const double DP1a1 = VP1a1 + HP1a1; + const double VP2a1 = +sphi * (K1P2 + K3P2); + const double HP2a1 = -cphi * (K1P2 + K3P2); + const double DP2a1 = VP2a1 + HP2a1; + const double VP3a1 = +sphi * (K1P3 + K3P3); + const double HP3a1 = -cphi * (K1P3 + K3P3); + const double DP3a1 = VP3a1 + HP3a1; + + const double VP0na1 = +sphi * (K1P0n + K3P0n); + const double HP0na1 = -cphi * (K1P0n + K3P0n); + const double DP0na1 = VP0na1 + HP0na1; + const double VP1na1 = +sphi * (K1P1n + K3P1n); + const double HP1na1 = -cphi * (K1P1n + K3P1n); + const double DP1na1 = VP1na1 + HP1na1; + const double VP2na1 = +sphi * (K1P2n + K3P2n); + const double HP2na1 = -cphi * (K1P2n + K3P2n); + const double DP2na1 = VP2na1 + HP2na1; + const double VP3na1 = +sphi * (K1P3n + K3P3n); + const double HP3na1 = -cphi * (K1P3n + K3P3n); + const double DP3na1 = VP3na1 + HP3na1; + + const double VA0 = VA0a0 * 1.5 - VA0a1 / 2 + 2 * dzvert * K2A0; + const double HA0 = HA0a0 * 1.5 - HA0a1 / 2 + 2 * dzhor * K2A0; + const double DA0 = VA0 + HA0; + const double VZA0 = VA0 + K2A0; + const double HZA0 = HA0 + K2A0; + const double DZA0 = DA0 + K2A0; + const double VA1 = VA1a0 * 1.5 - VA1a1 / 2 + 2 * dzvert * K2A1; + const double HA1 = HA1a0 * 1.5 - HA1a1 / 2 + 2 * dzhor * K2A1; + const double DA1 = VA1 + HA1; + const double VZA1 = VA1 + K2A1; + const double HZA1 = HA1 + K2A1; + const double DZA1 = DA1 + K2A1; + const double VA2 = VA2a0 * 1.5 - VA2a1 / 2 + 2 * dzvert * K2A2; + const double HA2 = HA2a0 * 1.5 - HA2a1 / 2 + 2 * dzhor * K2A2; + const double DA2 = VA2 + HA2; + const double VZA2 = VA2 + K2A2; + const double HZA2 = HA2 + K2A2; + const double DZA2 = DA2 + K2A2; + const double VA3 = VA3a0 * 1.5 - VA3a1 / 2 + 2 * dzvert * K2A3; + const double HA3 = HA3a0 * 1.5 - HA3a1 / 2 + 2 * dzhor * K2A3; + const double DA3 = VA3 + HA3; + const double VZA3 = VA3 + K2A3; + const double HZA3 = HA3 + K2A3; + const double DZA3 = DA3 + K2A3; + + const double VA0n = VA0na0 * 1.5 - VA0na1 / 2 + 2 * dzvert * K2A0n; + const double HA0n = HA0na0 * 1.5 - HA0na1 / 2 + 2 * dzhor * K2A0n; + const double DA0n = VA0n + HA0n; + const double VZA0n = VA0n + K2A0n; + const double HZA0n = HA0n + K2A0n; + const double DZA0n = DA0n + K2A0n; + const double VA1n = VA1na0 * 1.5 - VA1na1 / 2 + 2 * dzvert * K2A1n; + const double HA1n = HA1na0 * 1.5 - HA1na1 / 2 + 2 * dzhor * K2A1n; + const double DA1n = VA1n + HA1n; + const double VZA1n = VA1n + K2A1n; + const double HZA1n = HA1n + K2A1n; + const double DZA1n = DA1n + K2A1n; + const double VA2n = VA2na0 * 1.5 - VA2na1 / 2 + 2 * dzvert * K2A2n; + const double HA2n = HA2na0 * 1.5 - HA2na1 / 2 + 2 * dzhor * K2A2n; + const double DA2n = VA2n + HA2n; + const double VZA2n = VA2n + K2A2n; + const double HZA2n = HA2n + K2A2n; + const double DZA2n = DA2n + K2A2n; + const double VA3n = VA3na0 * 1.5 - VA3na1 / 2 + 2 * dzvert * K2A3n; + const double HA3n = HA3na0 * 1.5 - HA3na1 / 2 + 2 * dzhor * K2A3n; + const double DA3n = VA3n + HA3n; + const double VZA3n = VA3n + K2A3n; + const double HZA3n = HA3n + K2A3n; + const double DZA3n = DA3n + K2A3n; + + const double VP0 = VP0a0 * 1.5 - VP0a1 / 2 + 2 * dzvert * K2P0; + const double HP0 = HP0a0 * 1.5 - HP0a1 / 2 + 2 * dzhor * K2P0; + const double DP0 = VP0 + HP0; + const double VZP0 = VP0 + K2P0; + const double HZP0 = HP0 + K2P0; + const double DZP0 = DP0 + K2P0; + const double VP1 = VP1a0 * 1.5 - VP1a1 / 2 + 2 * dzvert * K2P1; + const double HP1 = HP1a0 * 1.5 - HP1a1 / 2 + 2 * dzhor * K2P1; + const double DP1 = VP1 + HP1; + const double VZP1 = VP1 + K2P1; + const double HZP1 = HP1 + K2P1; + const double DZP1 = DP1 + K2P1; + const double VP2 = VP2a0 * 1.5 - VP2a1 / 2 + 2 * dzvert * K2P2; + const double HP2 = HP2a0 * 1.5 - HP2a1 / 2 + 2 * dzhor * K2P2; + const double DP2 = VP2 + HP2; + const double VZP2 = VP2 + K2P2; + const double HZP2 = HP2 + K2P2; + const double DZP2 = DP2 + K2P2; + const double VP3 = VP3a0 * 1.5 - VP3a1 / 2 + 2 * dzvert * K2P3; + const double HP3 = HP3a0 * 1.5 - HP3a1 / 2 + 2 * dzhor * K2P3; + const double DP3 = VP3 + HP3; + const double VZP3 = VP3 + K2P3; + const double HZP3 = HP3 + K2P3; + const double DZP3 = DP3 + K2P3; + + const double VP0n = VP0na0 * 1.5 - VP0na1 / 2 + 2 * dzvert * K2P0n; + const double HP0n = HP0na0 * 1.5 - HP0na1 / 2 + 2 * dzhor * K2P0n; + const double DP0n = VP0n + HP0n; + const double VZP0n = VP0n + K2P0n; + const double HZP0n = HP0n + K2P0n; + const double DZP0n = DP0n + K2P0n; + const double VP1n = VP1na0 * 1.5 - VP1na1 / 2 + 2 * dzvert * K2P1n; + const double HP1n = HP1na0 * 1.5 - HP1na1 / 2 + 2 * dzhor * K2P1n; + const double DP1n = VP1n + HP1n; + const double VZP1n = VP1n + K2P1n; + const double HZP1n = HP1n + K2P1n; + const double DZP1n = DP1n + K2P1n; + const double VP2n = VP2na0 * 1.5 - VP2na1 / 2 + 2 * dzvert * K2P2n; + const double HP2n = HP2na0 * 1.5 - HP2na1 / 2 + 2 * dzhor * K2P2n; + const double DP2n = VP2n + HP2n; + const double VZP2n = VP2n + K2P2n; + const double HZP2n = HP2n + K2P2n; + const double DZP2n = DP2n + K2P2n; + const double VP3n = VP3na0 * 1.5 - VP3na1 / 2 + 2 * dzvert * K2P3n; + const double HP3n = HP3na0 * 1.5 - HP3na1 / 2 + 2 * dzhor * K2P3n; + const double DP3n = VP3n + HP3n; + const double VZP3n = VP3n + K2P3n; + const double HZP3n = HP3n + K2P3n; + const double DZP3n = DP3n + K2P3n; + // Initial values of update values (Up) - - double A0a0 = Projptr[0][0][0][1]+ds*K1A0; - double A0a1 = Projptr[0][0][0][3]+ds*(K1A0+K3A0); - double A2a0 = Projptr[0][2][0][1]+ds*K1A2; - double A2a1 = Projptr[0][2][0][3]+ds*(K1A2+K3A2); - - double P0na0 = Projptr[1][0][1][1]+ds*K1P0n; - double P0na1 = Projptr[1][0][1][3]+ds*(K1P0n+K3P0n); - double P2na0 = Projptr[1][2][1][1]+ds*K1P2n; - double P2na1 = Projptr[1][2][1][3]+ds*(K1P2n+K3P2n); - - double A0na0 = Projptr[1][0][0][1]+ds*K1A0n; - double A0na1 = Projptr[1][0][0][3]+ds*(K1A0n+K3A0n); - double A2na0 = Projptr[1][2][0][1]+ds*K1A2n; - double A2na1 = Projptr[1][2][0][3]+ds*(K1A2n+K3A2n); - - double P0a0 = Projptr[0][0][1][1]+ds*K1P0; - double P0a1 = Projptr[0][0][1][3]+ds*(K1P0+K3P0); - double P2a0 = Projptr[0][2][1][1]+ds*K1P2; - double P2a1 = Projptr[0][2][1][3]+ds*(K1P2+K3P2); - - double A1a0 = Projptr[0][1][0][1]+ds*K1A1; - double A1a1 = Projptr[0][1][0][3]+ds*(K1A1+K3A1); - double A3a0 = Projptr[0][3][0][1]+ds*K1A3; - double A3a1 = Projptr[0][3][0][3]+ds*(K1A3+K3A3); - - double P1na0 = Projptr[1][1][1][1]+ds*K1P1n; - double P1na1 = Projptr[1][1][1][3]+ds*(K1P1n+K3P1n); - double P3na0 = Projptr[1][3][1][1]+ds*K1P3n; - double P3na1 = Projptr[1][3][1][3]+ds*(K1P3n+K3P3n); - - double A1na0 = Projptr[1][1][0][1]+ds*K1A1n; - double A1na1 = Projptr[1][1][0][3]+ds*(K1A1n+K3A1n); - double A3na0 = Projptr[1][3][0][1]+ds*K1A3n; - double A3na1 = Projptr[1][3][0][3]+ds*(K1A3n+K3A3n); - - double P1a0 = Projptr[0][1][1][1]+ds*K1P1; - double P1a1 = Projptr[0][1][1][3]+ds*(K1P1+K3P1); - double P3a0 = Projptr[0][3][1][1]+ds*K1P3; - double P3a1 = Projptr[0][3][1][3]+ds*(K1P3+K3P3); - - double UpA0 = 2*(Projptr[0][0][0][1]+ds*K1A0+dz*K2A0) - (A0a0 + A0a1)/2; - double UpA1 = 2*(Projptr[0][1][0][1]+ds*K1A1+dz*K2A1) - (A1a0 + A1a1)/2; - double UpA2 = 2*(Projptr[0][2][0][1]+ds*K1A2+dz*K2A2) - (A2a0 + A2a1)/2; - double UpA3 = 2*(Projptr[0][3][0][1]+ds*K1A3+dz*K2A3) - (A3a0 + A3a1)/2; - - double UpA0n = 2*(Projptr[1][0][0][1]+ds*K1A0n+dz*K2A0n) - (A0na0 + A0na1)/2; - double UpA1n = 2*(Projptr[1][1][0][1]+ds*K1A1n+dz*K2A1n) - (A1na0 + A1na1)/2; - double UpA2n = 2*(Projptr[1][2][0][1]+ds*K1A2n+dz*K2A2n) - (A2na0 + A2na1)/2; - double UpA3n = 2*(Projptr[1][3][0][1]+ds*K1A3n+dz*K2A3n) - (A3na0 + A3na1)/2; - - double UpP0 = 2*(Projptr[0][0][1][1]+ds*K1P0+dz*K2P0) - (P0a0 + P0a1)/2; - double UpP1 = 2*(Projptr[0][1][1][1]+ds*K1P1+dz*K2P1) - (P1a0 + P1a1)/2; - double UpP2 = 2*(Projptr[0][2][1][1]+ds*K1P2+dz*K2P2) - (P2a0 + P2a1)/2; - double UpP3 = 2*(Projptr[0][3][1][1]+ds*K1P3+dz*K2P3) - (P3a0 + P3a1)/2; - - double UpP0n = 2*(Projptr[1][0][1][1]+ds*K1P0n+dz*K2P0n) - (P0na0 + P0na1)/2; - double UpP1n = 2*(Projptr[1][1][1][1]+ds*K1P1n+dz*K2P1n) - (P1na0 + P1na1)/2; - double UpP2n = 2*(Projptr[1][2][1][1]+ds*K1P2n+dz*K2P2n) - (P2na0 + P2na1)/2; - double UpP3n = 2*(Projptr[1][3][1][1]+ds*K1P3n+dz*K2P3n) - (P3na0 + P3na1)/2; + + double A0a0 = Projptr[0][0][0][1] + ds * K1A0; + double A0a1 = Projptr[0][0][0][3] + ds * (K1A0 + K3A0); + double A2a0 = Projptr[0][2][0][1] + ds * K1A2; + double A2a1 = Projptr[0][2][0][3] + ds * (K1A2 + K3A2); + + double P0na0 = Projptr[1][0][1][1] + ds * K1P0n; + double P0na1 = Projptr[1][0][1][3] + ds * (K1P0n + K3P0n); + double P2na0 = Projptr[1][2][1][1] + ds * K1P2n; + double P2na1 = Projptr[1][2][1][3] + ds * (K1P2n + K3P2n); + + double A0na0 = Projptr[1][0][0][1] + ds * K1A0n; + double A0na1 = Projptr[1][0][0][3] + ds * (K1A0n + K3A0n); + double A2na0 = Projptr[1][2][0][1] + ds * K1A2n; + double A2na1 = Projptr[1][2][0][3] + ds * (K1A2n + K3A2n); + + double P0a0 = Projptr[0][0][1][1] + ds * K1P0; + double P0a1 = Projptr[0][0][1][3] + ds * (K1P0 + K3P0); + double P2a0 = Projptr[0][2][1][1] + ds * K1P2; + double P2a1 = Projptr[0][2][1][3] + ds * (K1P2 + K3P2); + + double A1a0 = Projptr[0][1][0][1] + ds * K1A1; + double A1a1 = Projptr[0][1][0][3] + ds * (K1A1 + K3A1); + double A3a0 = Projptr[0][3][0][1] + ds * K1A3; + double A3a1 = Projptr[0][3][0][3] + ds * (K1A3 + K3A3); + + double P1na0 = Projptr[1][1][1][1] + ds * K1P1n; + double P1na1 = Projptr[1][1][1][3] + ds * (K1P1n + K3P1n); + double P3na0 = Projptr[1][3][1][1] + ds * K1P3n; + double P3na1 = Projptr[1][3][1][3] + ds * (K1P3n + K3P3n); + + double A1na0 = Projptr[1][1][0][1] + ds * K1A1n; + double A1na1 = Projptr[1][1][0][3] + ds * (K1A1n + K3A1n); + double A3na0 = Projptr[1][3][0][1] + ds * K1A3n; + double A3na1 = Projptr[1][3][0][3] + ds * (K1A3n + K3A3n); + + double P1a0 = Projptr[0][1][1][1] + ds * K1P1; + double P1a1 = Projptr[0][1][1][3] + ds * (K1P1 + K3P1); + double P3a0 = Projptr[0][3][1][1] + ds * K1P3; + double P3a1 = Projptr[0][3][1][3] + ds * (K1P3 + K3P3); + + double UpA0 = 2 * (Projptr[0][0][0][1] + ds * K1A0 + dz * K2A0) - (A0a0 + A0a1) / 2; + double UpA1 = 2 * (Projptr[0][1][0][1] + ds * K1A1 + dz * K2A1) - (A1a0 + A1a1) / 2; + double UpA2 = 2 * (Projptr[0][2][0][1] + ds * K1A2 + dz * K2A2) - (A2a0 + A2a1) / 2; + double UpA3 = 2 * (Projptr[0][3][0][1] + ds * K1A3 + dz * K2A3) - (A3a0 + A3a1) / 2; + + double UpA0n = 2 * (Projptr[1][0][0][1] + ds * K1A0n + dz * K2A0n) - (A0na0 + A0na1) / 2; + double UpA1n = 2 * (Projptr[1][1][0][1] + ds * K1A1n + dz * K2A1n) - (A1na0 + A1na1) / 2; + double UpA2n = 2 * (Projptr[1][2][0][1] + ds * K1A2n + dz * K2A2n) - (A2na0 + A2na1) / 2; + double UpA3n = 2 * (Projptr[1][3][0][1] + ds * K1A3n + dz * K2A3n) - (A3na0 + A3na1) / 2; + + double UpP0 = 2 * (Projptr[0][0][1][1] + ds * K1P0 + dz * K2P0) - (P0a0 + P0a1) / 2; + double UpP1 = 2 * (Projptr[0][1][1][1] + ds * K1P1 + dz * K2P1) - (P1a0 + P1a1) / 2; + double UpP2 = 2 * (Projptr[0][2][1][1] + ds * K1P2 + dz * K2P2) - (P2a0 + P2a1) / 2; + double UpP3 = 2 * (Projptr[0][3][1][1] + ds * K1P3 + dz * K2P3) - (P3a0 + P3a1) / 2; + + double UpP0n = 2 * (Projptr[1][0][1][1] + ds * K1P0n + dz * K2P0n) - (P0na0 + P0na1) / 2; + double UpP1n = 2 * (Projptr[1][1][1][1] + ds * K1P1n + dz * K2P1n) - (P1na0 + P1na1) / 2; + double UpP2n = 2 * (Projptr[1][2][1][1] + ds * K1P2n + dz * K2P2n) - (P2na0 + P2na1) / 2; + double UpP3n = 2 * (Projptr[1][3][1][1] + ds * K1P3n + dz * K2P3n) - (P3na0 + P3na1) / 2; #endif @@ -1633,20 +1575,17 @@ Recompile %s with ALTERNATIVE not #defined", __FILE__); // CL&KT 21/12/99 added axial_pos_to_z_offset and num_planes_per_physical_ring { // first compute it as floating point (although it has to be an int really) - const float Qf = (2*num_planes_per_axial_pos*(ring0 + 0.5) - + num_planes_per_physical_ring*delta - + 2*axial_pos_to_z_offset - - Z); + const float Qf = + (2 * num_planes_per_axial_pos * (ring0 + 0.5) + num_planes_per_physical_ring * delta + 2 * axial_pos_to_z_offset - Z); // now use rounding to be safe Q = (int)floor(Qf + 0.5); - assert(fabs(Q-Qf) < 10E-4); + assert(fabs(Q - Qf) < 10E-4); } - dzdiag=dzvert+dzhor; - dsdiag=-cphi+sphi; - + dzdiag = dzvert + dzhor; + dsdiag = -cphi + sphi; - /* KT 13/05/98 changed loop condition, originally a combination of + /* KT 13/05/98 changed loop condition, originally a combination of while (X>X2||YY2||Z>Z2) @@ -1657,694 +1596,669 @@ Recompile %s with ALTERNATIVE not #defined", __FILE__); Now I break out of the while when the voxel goes out of the cylinder, which means I don't have to use X2,Y2,Z2 anymore */ - do - { - assert(ds >= 0); - assert(ds <= 1); - assert(dz >= 0); - assert(dz <= ring_unit+epsilon); - - // Update voxel values for this X,Y,Z - // For 1 given (X,Y)-position, there are always 2 voxels along the z-axis - // in this beam. - const int Zplus=Z+1; - const int Qmin=Q-1; - - + do { + assert(ds >= 0); + assert(ds <= 1); + assert(dz >= 0); + assert(dz <= ring_unit + epsilon); + + // Update voxel values for this X,Y,Z + // For 1 given (X,Y)-position, there are always 2 voxels along the z-axis + // in this beam. + const int Zplus = Z + 1; + const int Qmin = Q - 1; + #if PIECEWISE_INTERPOLATION - - const double twodsdz=2*ds*dz; - const double twodsdz2=2*ds*(dz+0.5); - // KT 16/06/98 changed check ds!=0 to fabs(ds)>epsilon for better rounding control - const bool do_s_symmetry = (s!=0 || fabs(ds) > epsilon); - - - if (Z>=minplane&&Z<=maxplane) { - image[Z][Y][X]+=(dz <= 0.25) ? A0a0 : UpA0 +twodsdz*K3A0; - image[Z][X][-Y]+=(dz <= 0.25) ? A2a0 : UpA2 +twodsdz*K3A2; - image[Z][X][Y]+=(dz <= 0.25) ? A1na0 : UpA1n +twodsdz*K3A1n; - image[Z][Y][-X]+=(dz <= 0.25) ? A3na0 : UpA3n +twodsdz*K3A3n; - if (do_s_symmetry) - { - image[Z][-X][-Y]+=(dz <= 0.25) ? P1a0 : UpP1 +twodsdz*K3P1; - image[Z][-Y][X]+=(dz <= 0.25) ? P3a0 : UpP3 +twodsdz*K3P3; - image[Z][-Y][-X]+=(dz <= 0.25) ? P0na0 : UpP0n +twodsdz*K3P0n; - image[Z][-X][Y]+=(dz <= 0.25) ? P2na0 : UpP2n +twodsdz*K3P2n; - } + + const double twodsdz = 2 * ds * dz; + const double twodsdz2 = 2 * ds * (dz + 0.5); + // KT 16/06/98 changed check ds!=0 to fabs(ds)>epsilon for better rounding control + const bool do_s_symmetry = (s != 0 || fabs(ds) > epsilon); + + if (Z >= minplane && Z <= maxplane) { + image[Z][Y][X] += (dz <= 0.25) ? A0a0 : UpA0 + twodsdz * K3A0; + image[Z][X][-Y] += (dz <= 0.25) ? A2a0 : UpA2 + twodsdz * K3A2; + image[Z][X][Y] += (dz <= 0.25) ? A1na0 : UpA1n + twodsdz * K3A1n; + image[Z][Y][-X] += (dz <= 0.25) ? A3na0 : UpA3n + twodsdz * K3A3n; + if (do_s_symmetry) { + image[Z][-X][-Y] += (dz <= 0.25) ? P1a0 : UpP1 + twodsdz * K3P1; + image[Z][-Y][X] += (dz <= 0.25) ? P3a0 : UpP3 + twodsdz * K3P3; + image[Z][-Y][-X] += (dz <= 0.25) ? P0na0 : UpP0n + twodsdz * K3P0n; + image[Z][-X][Y] += (dz <= 0.25) ? P2na0 : UpP2n + twodsdz * K3P2n; } - if (Zplus>=minplane&&Zplus<=maxplane) { - image[Zplus][Y][X]+=(dz >= 0.25) ? A0a1 : UpA0 +twodsdz2*K3A0+ZplusKorrA0; - image[Zplus][X][-Y]+=(dz >= 0.25) ? A2a1 : UpA2 +twodsdz2*K3A2+ZplusKorrA2; - image[Zplus][X][Y]+=(dz >= 0.25) ? A1na1 : UpA1n +twodsdz2*K3A1n+ZplusKorrA1n; - image[Zplus][Y][-X]+=(dz >= 0.25) ? A3na1 : UpA3n +twodsdz2*K3A3n+ZplusKorrA3n; - if (do_s_symmetry) - { - image[Zplus][-X][-Y]+=(dz >= 0.25) ? P1a1 : UpP1 +twodsdz2*K3P1+ZplusKorrP1; - image[Zplus][-Y][X]+=(dz >= 0.25) ? P3a1 : UpP3 +twodsdz2*K3P3+ZplusKorrP3; - image[Zplus][-Y][-X]+=(dz >= 0.25) ? P0na1 : UpP0n +twodsdz2*K3P0n+ZplusKorrP0n; - image[Zplus][-X][Y]+=(dz >= 0.25) ? P2na1 : UpP2n +twodsdz2*K3P2n+ZplusKorrP2n; - } + } + if (Zplus >= minplane && Zplus <= maxplane) { + image[Zplus][Y][X] += (dz >= 0.25) ? A0a1 : UpA0 + twodsdz2 * K3A0 + ZplusKorrA0; + image[Zplus][X][-Y] += (dz >= 0.25) ? A2a1 : UpA2 + twodsdz2 * K3A2 + ZplusKorrA2; + image[Zplus][X][Y] += (dz >= 0.25) ? A1na1 : UpA1n + twodsdz2 * K3A1n + ZplusKorrA1n; + image[Zplus][Y][-X] += (dz >= 0.25) ? A3na1 : UpA3n + twodsdz2 * K3A3n + ZplusKorrA3n; + if (do_s_symmetry) { + image[Zplus][-X][-Y] += (dz >= 0.25) ? P1a1 : UpP1 + twodsdz2 * K3P1 + ZplusKorrP1; + image[Zplus][-Y][X] += (dz >= 0.25) ? P3a1 : UpP3 + twodsdz2 * K3P3 + ZplusKorrP3; + image[Zplus][-Y][-X] += (dz >= 0.25) ? P0na1 : UpP0n + twodsdz2 * K3P0n + ZplusKorrP0n; + image[Zplus][-X][Y] += (dz >= 0.25) ? P2na1 : UpP2n + twodsdz2 * K3P2n + ZplusKorrP2n; } - if (Q>=minplane&&Q<=maxplane) { - image[Q][Y][-X]+=(dz <= 0.25) ? A3a0 : UpA3 +twodsdz*K3A3; - image[Q][Y][X]+=(dz <= 0.25) ? A0na0 : UpA0n +twodsdz*K3A0n; - image[Q][X][-Y]+=(dz <= 0.25) ? A2na0 : UpA2n +twodsdz*K3A2n; - image[Q][X][Y]+=(dz <= 0.25) ? A1a0 : UpA1 +twodsdz*K3A1; - if (do_s_symmetry) - { - image[Q][-Y][-X]+=(dz <= 0.25) ? P0a0 : UpP0 +twodsdz*K3P0; - image[Q][-X][Y]+=(dz <= 0.25) ? P2a0 : UpP2 +twodsdz*K3P2; - image[Q][-X][-Y]+=(dz <= 0.25) ? P1na0 : UpP1n +twodsdz*K3P1n; - image[Q][-Y][X]+=(dz <= 0.25) ? P3na0 : UpP3n +twodsdz*K3P3n; - } + } + if (Q >= minplane && Q <= maxplane) { + image[Q][Y][-X] += (dz <= 0.25) ? A3a0 : UpA3 + twodsdz * K3A3; + image[Q][Y][X] += (dz <= 0.25) ? A0na0 : UpA0n + twodsdz * K3A0n; + image[Q][X][-Y] += (dz <= 0.25) ? A2na0 : UpA2n + twodsdz * K3A2n; + image[Q][X][Y] += (dz <= 0.25) ? A1a0 : UpA1 + twodsdz * K3A1; + if (do_s_symmetry) { + image[Q][-Y][-X] += (dz <= 0.25) ? P0a0 : UpP0 + twodsdz * K3P0; + image[Q][-X][Y] += (dz <= 0.25) ? P2a0 : UpP2 + twodsdz * K3P2; + image[Q][-X][-Y] += (dz <= 0.25) ? P1na0 : UpP1n + twodsdz * K3P1n; + image[Q][-Y][X] += (dz <= 0.25) ? P3na0 : UpP3n + twodsdz * K3P3n; } - if (Qmin>=minplane&&Qmin<=maxplane) { - image[Qmin][Y][-X]+=(dz >= 0.25) ? A3a1 : UpA3 +twodsdz2*K3A3+ZplusKorrA3; - image[Qmin][Y][X]+=(dz >= 0.25) ? A0na1 : UpA0n +twodsdz2*K3A0n+ZplusKorrA0n; - image[Qmin][X][-Y]+=(dz >= 0.25) ? A2na1 : UpA2n +twodsdz2*K3A2n+ZplusKorrA2n; - image[Qmin][X][Y]+=(dz >= 0.25) ? A1a1 : UpA1 +twodsdz2*K3A1+ZplusKorrA1; - if (do_s_symmetry) - { - image[Qmin][-Y][-X]+=(dz >= 0.25) ? P0a1 : UpP0 +twodsdz2*K3P0+ZplusKorrP0; - image[Qmin][-X][Y]+=(dz >= 0.25) ? P2a1 : UpP2 +twodsdz2*K3P2+ZplusKorrP2; - image[Qmin][-X][-Y]+=(dz >= 0.25) ? P1na1 : UpP1n +twodsdz2*K3P1n+ZplusKorrP1n; - image[Qmin][-Y][X]+=(dz >= 0.25) ? P3na1 : UpP3n +twodsdz2*K3P3n+ZplusKorrP3n; - } + } + if (Qmin >= minplane && Qmin <= maxplane) { + image[Qmin][Y][-X] += (dz >= 0.25) ? A3a1 : UpA3 + twodsdz2 * K3A3 + ZplusKorrA3; + image[Qmin][Y][X] += (dz >= 0.25) ? A0na1 : UpA0n + twodsdz2 * K3A0n + ZplusKorrA0n; + image[Qmin][X][-Y] += (dz >= 0.25) ? A2na1 : UpA2n + twodsdz2 * K3A2n + ZplusKorrA2n; + image[Qmin][X][Y] += (dz >= 0.25) ? A1a1 : UpA1 + twodsdz2 * K3A1 + ZplusKorrA1; + if (do_s_symmetry) { + image[Qmin][-Y][-X] += (dz >= 0.25) ? P0a1 : UpP0 + twodsdz2 * K3P0 + ZplusKorrP0; + image[Qmin][-X][Y] += (dz >= 0.25) ? P2a1 : UpP2 + twodsdz2 * K3P2 + ZplusKorrP2; + image[Qmin][-X][-Y] += (dz >= 0.25) ? P1na1 : UpP1n + twodsdz2 * K3P1n + ZplusKorrP1n; + image[Qmin][-Y][X] += (dz >= 0.25) ? P3na1 : UpP3n + twodsdz2 * K3P3n + ZplusKorrP3n; } + } #else // !PIECEWISE_INTERPOLATION -#ifdef ALTERNATIVE - const double dsdz=ds*dz; - const double dsdz2=ds*(dz+0.5); - // KT 16/06/98 changed check ds!=0 to fabs(ds)>epsilon for better rounding control - const bool do_s_symmetry = (s!=0 || fabs(ds) > epsilon); - - if (Z>=minplane&&Z<=maxplane) { - image[Z][Y][X]+=UpA0+dsdz*K3A0; - image[Z][X][-Y]+=UpA2+dsdz*K3A2; - image[Z][X][Y]+=UpA1n+dsdz*K3A1n; - image[Z][Y][-X]+=UpA3n+dsdz*K3A3n; - if (do_s_symmetry) - { - image[Z][-X][-Y]+=UpP1+dsdz*K3P1; - image[Z][-Y][X]+=UpP3+dsdz*K3P3; - image[Z][-Y][-X]+=UpP0n+dsdz*K3P0n; - image[Z][-X][Y]+=UpP2n+dsdz*K3P2n; - } +# ifdef ALTERNATIVE + const double dsdz = ds * dz; + const double dsdz2 = ds * (dz + 0.5); + // KT 16/06/98 changed check ds!=0 to fabs(ds)>epsilon for better rounding control + const bool do_s_symmetry = (s != 0 || fabs(ds) > epsilon); + + if (Z >= minplane && Z <= maxplane) { + image[Z][Y][X] += UpA0 + dsdz * K3A0; + image[Z][X][-Y] += UpA2 + dsdz * K3A2; + image[Z][X][Y] += UpA1n + dsdz * K3A1n; + image[Z][Y][-X] += UpA3n + dsdz * K3A3n; + if (do_s_symmetry) { + image[Z][-X][-Y] += UpP1 + dsdz * K3P1; + image[Z][-Y][X] += UpP3 + dsdz * K3P3; + image[Z][-Y][-X] += UpP0n + dsdz * K3P0n; + image[Z][-X][Y] += UpP2n + dsdz * K3P2n; } - if (Zplus>=minplane&&Zplus<=maxplane) { - image[Zplus][Y][X]+=UpA0+dsdz2*K3A0+ZplusKorrA0; - image[Zplus][X][-Y]+=UpA2+dsdz2*K3A2+ZplusKorrA2; - image[Zplus][X][Y]+=UpA1n+dsdz2*K3A1n+ZplusKorrA1n; - image[Zplus][Y][-X]+=UpA3n+dsdz2*K3A3n+ZplusKorrA3n; - if (do_s_symmetry) - { - image[Zplus][-X][-Y]+=UpP1+dsdz2*K3P1+ZplusKorrP1; - image[Zplus][-Y][X]+=UpP3+dsdz2*K3P3+ZplusKorrP3; - image[Zplus][-Y][-X]+=UpP0n+dsdz2*K3P0n+ZplusKorrP0n; - image[Zplus][-X][Y]+=UpP2n+dsdz2*K3P2n+ZplusKorrP2n; - } + } + if (Zplus >= minplane && Zplus <= maxplane) { + image[Zplus][Y][X] += UpA0 + dsdz2 * K3A0 + ZplusKorrA0; + image[Zplus][X][-Y] += UpA2 + dsdz2 * K3A2 + ZplusKorrA2; + image[Zplus][X][Y] += UpA1n + dsdz2 * K3A1n + ZplusKorrA1n; + image[Zplus][Y][-X] += UpA3n + dsdz2 * K3A3n + ZplusKorrA3n; + if (do_s_symmetry) { + image[Zplus][-X][-Y] += UpP1 + dsdz2 * K3P1 + ZplusKorrP1; + image[Zplus][-Y][X] += UpP3 + dsdz2 * K3P3 + ZplusKorrP3; + image[Zplus][-Y][-X] += UpP0n + dsdz2 * K3P0n + ZplusKorrP0n; + image[Zplus][-X][Y] += UpP2n + dsdz2 * K3P2n + ZplusKorrP2n; } - if (Q>=minplane&&Q<=maxplane) { - image[Q][Y][-X]+=UpA3+dsdz*K3A3; - image[Q][Y][X]+=UpA0n+dsdz*K3A0n; - image[Q][X][-Y]+=UpA2n+dsdz*K3A2n; - image[Q][X][Y]+=UpA1+dsdz*K3A1; - if (do_s_symmetry) - { - image[Q][-Y][-X]+=UpP0+dsdz*K3P0; - image[Q][-X][Y]+=UpP2+dsdz*K3P2; - image[Q][-X][-Y]+=UpP1n+dsdz*K3P1n; - image[Q][-Y][X]+=UpP3n+dsdz*K3P3n; - } + } + if (Q >= minplane && Q <= maxplane) { + image[Q][Y][-X] += UpA3 + dsdz * K3A3; + image[Q][Y][X] += UpA0n + dsdz * K3A0n; + image[Q][X][-Y] += UpA2n + dsdz * K3A2n; + image[Q][X][Y] += UpA1 + dsdz * K3A1; + if (do_s_symmetry) { + image[Q][-Y][-X] += UpP0 + dsdz * K3P0; + image[Q][-X][Y] += UpP2 + dsdz * K3P2; + image[Q][-X][-Y] += UpP1n + dsdz * K3P1n; + image[Q][-Y][X] += UpP3n + dsdz * K3P3n; } - if (Qmin>=minplane&&Qmin<=maxplane) { - image[Qmin][Y][-X]+=UpA3+dsdz2*K3A3+ZplusKorrA3; - image[Qmin][Y][X]+=UpA0n+dsdz2*K3A0n+ZplusKorrA0n; - image[Qmin][X][-Y]+=UpA2n+dsdz2*K3A2n+ZplusKorrA2n; - image[Qmin][X][Y]+=UpA1+dsdz2*K3A1+ZplusKorrA1; - if (do_s_symmetry) - { - image[Qmin][-Y][-X]+=UpP0+dsdz2*K3P0+ZplusKorrP0; - image[Qmin][-X][Y]+=UpP2+dsdz2*K3P2+ZplusKorrP2; - image[Qmin][-X][-Y]+=UpP1n+dsdz2*K3P1n+ZplusKorrP1n; - image[Qmin][-Y][X]+=UpP3n+dsdz2*K3P3n+ZplusKorrP3n; - } + } + if (Qmin >= minplane && Qmin <= maxplane) { + image[Qmin][Y][-X] += UpA3 + dsdz2 * K3A3 + ZplusKorrA3; + image[Qmin][Y][X] += UpA0n + dsdz2 * K3A0n + ZplusKorrA0n; + image[Qmin][X][-Y] += UpA2n + dsdz2 * K3A2n + ZplusKorrA2n; + image[Qmin][X][Y] += UpA1 + dsdz2 * K3A1 + ZplusKorrA1; + if (do_s_symmetry) { + image[Qmin][-Y][-X] += UpP0 + dsdz2 * K3P0 + ZplusKorrP0; + image[Qmin][-X][Y] += UpP2 + dsdz2 * K3P2 + ZplusKorrP2; + image[Qmin][-X][-Y] += UpP1n + dsdz2 * K3P1n + ZplusKorrP1n; + image[Qmin][-Y][X] += UpP3n + dsdz2 * K3P3n + ZplusKorrP3n; } - #else // ALTERNATIVE - double TMP1,TMP2; - TMP1=ds*K3A0; - TMP2=UpA0+dz*TMP1; - if (Z>=minplane&&Z<=maxplane) - image[Z][Y][X]+=TMP2; - - - //CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data - //as there is only one voxel in the beam in slice unit - if (Zplus>=minplane&&Zplus<=maxplane && ring_unit==0.5) - image[Zplus][Y][X]+=TMP2+ring_unit*TMP1+ZplusKorrA0; - - TMP1=ds*K3A1; - TMP2=UpA1+dz*TMP1; - if (Q>=minplane&&Q<=maxplane) - image[Q][X][Y]+=TMP2; - //CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data - //as there is only one voxel in the beam in slice unit - if (Qmin>=minplane&&Qmin<=maxplane && ring_unit==0.5) - image[Qmin][X][Y]+=TMP2+ring_unit*TMP1+ZplusKorrA1; - - - TMP1=ds*K3A2; - TMP2=UpA2+dz*TMP1; - if (Z>=minplane&&Z<=maxplane) - image[Z][X][-Y]+=TMP2; - //CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data - //as there is only one voxel in the beam in slice unit - if (Zplus>=minplane&&Zplus<=maxplane && ring_unit==0.5) - image[Zplus][X][-Y]+=TMP2+ring_unit*TMP1+ZplusKorrA2; - TMP1=ds*K3A3; - TMP2=UpA3+dz*TMP1; - if (Q>=minplane&&Q<=maxplane) - image[Q][Y][-X]+=TMP2; - //CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data - //as there is only one voxel in the beam in slice unit - if (Qmin>=minplane&&Qmin<=maxplane && ring_unit==0.5) - image[Qmin][Y][-X]+=TMP2+ring_unit*TMP1+ZplusKorrA3; - - TMP1=ds*K3A0n; - TMP2=UpA0n+dz*TMP1; - if (Q>=minplane&&Q<=maxplane) - image[Q][Y][X]+=TMP2; - //CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data - //as there is only one voxel in the beam in slice unit - if (Qmin>=minplane&&Qmin<=maxplane && ring_unit==0.5) - image[Qmin][Y][X]+=TMP2+ring_unit*TMP1+ZplusKorrA0n; - TMP1=ds*K3A1n; - TMP2=UpA1n+dz*TMP1; - if (Z>=minplane&&Z<=maxplane) - image[Z][X][Y]+=TMP2; - //CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data - //as there is only one voxel in the beam in slice unit - if (Zplus>=minplane&&Zplus<=maxplane && ring_unit==0.5) - image[Zplus][X][Y]+=TMP2+ring_unit*TMP1+ZplusKorrA1n; - TMP1=ds*K3A2n; - TMP2=UpA2n+dz*TMP1; - if (Q>=minplane&&Q<=maxplane) - image[Q][X][-Y]+=TMP2; - //CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data - //as there is only one voxel in the beam in slice unit - if (Qmin>=minplane&&Qmin<=maxplane && ring_unit==0.5) - image[Qmin][X][-Y]+=TMP2+ring_unit*TMP1+ZplusKorrA2n; - TMP1=ds*K3A3n; - TMP2=UpA3n+dz*TMP1; - if (Z>=minplane&&Z<=maxplane) - image[Z][Y][-X]+=TMP2; - //CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data - //as there is only one voxel in the beam in slice unit - if (Zplus>=minplane&&Zplus<=maxplane && ring_unit==0.5) - image[Zplus][Y][-X]+=TMP2+ring_unit*TMP1+ZplusKorrA3n; - - // KT 16/06/98 changed check ds!=0 to fabs(ds)>epsilon for better rounding control - if (s!=0 || fabs(ds) > epsilon) - { - TMP1=ds*K3P0; - TMP2=UpP0+dz*TMP1; - if (Q>=minplane&&Q<=maxplane) - image[Q][-Y][-X]+=TMP2; - //CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data - //as there is only one voxel in the beam in slice unit - if (Qmin>=minplane&&Qmin<=maxplane && ring_unit==0.5) - image[Qmin][-Y][-X]+=TMP2+ring_unit*TMP1+ZplusKorrP0; - TMP1=ds*K3P1; - TMP2=UpP1+dz*TMP1; - if (Z>=minplane&&Z<=maxplane) - image[Z][-X][-Y]+=TMP2; - //CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data - //as there is only one voxel in the beam in slice unit - if (Zplus>=minplane&&Zplus<=maxplane && ring_unit==0.5) - image[Zplus][-X][-Y]+=TMP2+ring_unit*TMP1+ZplusKorrP1; - TMP1=ds*K3P2; - TMP2=UpP2+dz*TMP1; - if (Q>=minplane&&Q<=maxplane) - image[Q][-X][Y]+=TMP2; - //CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data - //as there is only one voxel in the beam in slice unit - if (Qmin>=minplane&&Qmin<=maxplane && ring_unit==0.5) - image[Qmin][-X][Y]+=TMP2+ring_unit*TMP1+ZplusKorrP2; - TMP1=ds*K3P3; - TMP2=UpP3+dz*TMP1; - if (Z>=minplane&&Z<=maxplane) - image[Z][-Y][X]+=TMP2; - //CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data - //as there is only one voxel in the beam in slice unit - if (Zplus>=minplane&&Zplus<=maxplane && ring_unit==0.5) - image[Zplus][-Y][X]+=TMP2+ring_unit*TMP1+ZplusKorrP3; - - TMP1=ds*K3P0n; - TMP2=UpP0n+dz*TMP1; - if (Z>=minplane&&Z<=maxplane) - image[Z][-Y][-X]+=TMP2; - //CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data - //as there is only one voxel in the beam in slice unit - if (Zplus>=minplane&&Zplus<=maxplane && ring_unit==0.5) - image[Zplus][-Y][-X]+=TMP2+ring_unit*TMP1+ZplusKorrP0n; - TMP1=ds*K3P1n; - TMP2=UpP1n+dz*TMP1; - if (Q>=minplane&&Q<=maxplane) - image[Q][-X][-Y]+=TMP2; - //CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data - //as there is only one voxel in the beam in slice unit - if (Qmin>=minplane&&Qmin<=maxplane && ring_unit==0.5) - image[Qmin][-X][-Y]+=TMP2+ring_unit*TMP1+ZplusKorrP1n; - TMP1=ds*K3P2n; - TMP2=UpP2n+dz*TMP1; - if (Z>=minplane&&Z<=maxplane) - image[Z][-X][Y]+=TMP2; - //CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data - //as there is only one voxel in the beam in slice unit - if (Zplus>=minplane&&Zplus<=maxplane && ring_unit==0.5) - image[Zplus][-X][Y]+=TMP2+ring_unit*TMP1+ZplusKorrP2n; - TMP1=ds*K3P3n; - TMP2=UpP3n+dz*TMP1; - if (Q>=minplane&&Q<=maxplane) - image[Q][-Y][X]+=TMP2; - //CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data - //as there is only one voxel in the beam in slice unit - if (Qmin>=minplane&&Qmin<=maxplane && ring_unit==0.5) - image[Qmin][-Y][X]+=TMP2+ring_unit*TMP1+ZplusKorrP3n; - } - -#endif // ALTERNATIVE -#endif //PIECEWISE_INTERPOLATION - - // Search for next pixel in the beam - - if (ds>=cphi) { - /* horizontal*/ - X-=1; - ds-=cphi; - dz+=dzhor; - if (dz= minplane && Z <= maxplane) + image[Z][Y][X] += TMP2; + + // CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data + // as there is only one voxel in the beam in slice unit + if (Zplus >= minplane && Zplus <= maxplane && ring_unit == 0.5) + image[Zplus][Y][X] += TMP2 + ring_unit * TMP1 + ZplusKorrA0; + + TMP1 = ds * K3A1; + TMP2 = UpA1 + dz * TMP1; + if (Q >= minplane && Q <= maxplane) + image[Q][X][Y] += TMP2; + // CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data + // as there is only one voxel in the beam in slice unit + if (Qmin >= minplane && Qmin <= maxplane && ring_unit == 0.5) + image[Qmin][X][Y] += TMP2 + ring_unit * TMP1 + ZplusKorrA1; + + TMP1 = ds * K3A2; + TMP2 = UpA2 + dz * TMP1; + if (Z >= minplane && Z <= maxplane) + image[Z][X][-Y] += TMP2; + // CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data + // as there is only one voxel in the beam in slice unit + if (Zplus >= minplane && Zplus <= maxplane && ring_unit == 0.5) + image[Zplus][X][-Y] += TMP2 + ring_unit * TMP1 + ZplusKorrA2; + TMP1 = ds * K3A3; + TMP2 = UpA3 + dz * TMP1; + if (Q >= minplane && Q <= maxplane) + image[Q][Y][-X] += TMP2; + // CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data + // as there is only one voxel in the beam in slice unit + if (Qmin >= minplane && Qmin <= maxplane && ring_unit == 0.5) + image[Qmin][Y][-X] += TMP2 + ring_unit * TMP1 + ZplusKorrA3; + + TMP1 = ds * K3A0n; + TMP2 = UpA0n + dz * TMP1; + if (Q >= minplane && Q <= maxplane) + image[Q][Y][X] += TMP2; + // CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data + // as there is only one voxel in the beam in slice unit + if (Qmin >= minplane && Qmin <= maxplane && ring_unit == 0.5) + image[Qmin][Y][X] += TMP2 + ring_unit * TMP1 + ZplusKorrA0n; + TMP1 = ds * K3A1n; + TMP2 = UpA1n + dz * TMP1; + if (Z >= minplane && Z <= maxplane) + image[Z][X][Y] += TMP2; + // CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data + // as there is only one voxel in the beam in slice unit + if (Zplus >= minplane && Zplus <= maxplane && ring_unit == 0.5) + image[Zplus][X][Y] += TMP2 + ring_unit * TMP1 + ZplusKorrA1n; + TMP1 = ds * K3A2n; + TMP2 = UpA2n + dz * TMP1; + if (Q >= minplane && Q <= maxplane) + image[Q][X][-Y] += TMP2; + // CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data + // as there is only one voxel in the beam in slice unit + if (Qmin >= minplane && Qmin <= maxplane && ring_unit == 0.5) + image[Qmin][X][-Y] += TMP2 + ring_unit * TMP1 + ZplusKorrA2n; + TMP1 = ds * K3A3n; + TMP2 = UpA3n + dz * TMP1; + if (Z >= minplane && Z <= maxplane) + image[Z][Y][-X] += TMP2; + // CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data + // as there is only one voxel in the beam in slice unit + if (Zplus >= minplane && Zplus <= maxplane && ring_unit == 0.5) + image[Zplus][Y][-X] += TMP2 + ring_unit * TMP1 + ZplusKorrA3n; + + // KT 16/06/98 changed check ds!=0 to fabs(ds)>epsilon for better rounding control + if (s != 0 || fabs(ds) > epsilon) { + TMP1 = ds * K3P0; + TMP2 = UpP0 + dz * TMP1; + if (Q >= minplane && Q <= maxplane) + image[Q][-Y][-X] += TMP2; + // CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data + // as there is only one voxel in the beam in slice unit + if (Qmin >= minplane && Qmin <= maxplane && ring_unit == 0.5) + image[Qmin][-Y][-X] += TMP2 + ring_unit * TMP1 + ZplusKorrP0; + TMP1 = ds * K3P1; + TMP2 = UpP1 + dz * TMP1; + if (Z >= minplane && Z <= maxplane) + image[Z][-X][-Y] += TMP2; + // CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data + // as there is only one voxel in the beam in slice unit + if (Zplus >= minplane && Zplus <= maxplane && ring_unit == 0.5) + image[Zplus][-X][-Y] += TMP2 + ring_unit * TMP1 + ZplusKorrP1; + TMP1 = ds * K3P2; + TMP2 = UpP2 + dz * TMP1; + if (Q >= minplane && Q <= maxplane) + image[Q][-X][Y] += TMP2; + // CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data + // as there is only one voxel in the beam in slice unit + if (Qmin >= minplane && Qmin <= maxplane && ring_unit == 0.5) + image[Qmin][-X][Y] += TMP2 + ring_unit * TMP1 + ZplusKorrP2; + TMP1 = ds * K3P3; + TMP2 = UpP3 + dz * TMP1; + if (Z >= minplane && Z <= maxplane) + image[Z][-Y][X] += TMP2; + // CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data + // as there is only one voxel in the beam in slice unit + if (Zplus >= minplane && Zplus <= maxplane && ring_unit == 0.5) + image[Zplus][-Y][X] += TMP2 + ring_unit * TMP1 + ZplusKorrP3; + + TMP1 = ds * K3P0n; + TMP2 = UpP0n + dz * TMP1; + if (Z >= minplane && Z <= maxplane) + image[Z][-Y][-X] += TMP2; + // CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data + // as there is only one voxel in the beam in slice unit + if (Zplus >= minplane && Zplus <= maxplane && ring_unit == 0.5) + image[Zplus][-Y][-X] += TMP2 + ring_unit * TMP1 + ZplusKorrP0n; + TMP1 = ds * K3P1n; + TMP2 = UpP1n + dz * TMP1; + if (Q >= minplane && Q <= maxplane) + image[Q][-X][-Y] += TMP2; + // CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data + // as there is only one voxel in the beam in slice unit + if (Qmin >= minplane && Qmin <= maxplane && ring_unit == 0.5) + image[Qmin][-X][-Y] += TMP2 + ring_unit * TMP1 + ZplusKorrP1n; + TMP1 = ds * K3P2n; + TMP2 = UpP2n + dz * TMP1; + if (Z >= minplane && Z <= maxplane) + image[Z][-X][Y] += TMP2; + // CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data + // as there is only one voxel in the beam in slice unit + if (Zplus >= minplane && Zplus <= maxplane && ring_unit == 0.5) + image[Zplus][-X][Y] += TMP2 + ring_unit * TMP1 + ZplusKorrP2n; + TMP1 = ds * K3P3n; + TMP2 = UpP3n + dz * TMP1; + if (Q >= minplane && Q <= maxplane) + image[Q][-Y][X] += TMP2; + // CL SPAN 15/09/98 Add one more condition in order to skip the statement in case of span data + // as there is only one voxel in the beam in slice unit + if (Qmin >= minplane && Qmin <= maxplane && ring_unit == 0.5) + image[Qmin][-Y][X] += TMP2 + ring_unit * TMP1 + ZplusKorrP3n; + } + +# endif // ALTERNATIVE +#endif // PIECEWISE_INTERPOLATION + + // Search for next pixel in the beam + + if (ds >= cphi) { + /* horizontal*/ + X -= 1; + ds -= cphi; + dz += dzhor; + if (dz < epsilon) { /* increment Z */ + Z++; + Q--; + dz += ring_unit; + UpA0 += HZA0; + UpA1 += HZA1; + UpA2 += HZA2; + UpA3 += HZA3; + UpA0n += HZA0n; + UpA1n += HZA1n; + UpA2n += HZA2n; + UpA3n += HZA3n; + UpP0 += HZP0; + UpP1 += HZP1; + UpP2 += HZP2; + UpP3 += HZP3; + UpP0n += HZP0n; + UpP1n += HZP1n; + UpP2n += HZP2n; + UpP3n += HZP3n; #ifdef MOREZ - while (dz=minplane)); + check_values(proj_data_info_sptr, delta, cphi, sphi, s, ring0, X, Y, Z, ds, dz, num_planes_per_axial_pos, + axial_pos_to_z_offset); + } while ((X * X + Y * Y <= image_rad * image_rad) && (Z <= maxplane || Q >= minplane)); } - /****************************************************************************************/ - static Succeeded -find_start_values(const shared_ptr proj_data_info_sptr, - const float delta, const double cphi, const double sphi, - const int s, const int ring0, - const float image_rad, const double d_sl, - int&X1, int&Y1, int& Z1, - double& ds, double& dz, double& dzhor, double& dzvert, - const float num_planes_per_axial_pos, - const float axial_pos_to_z_offset) -{ +find_start_values(const shared_ptr proj_data_info_sptr, const float delta, + const double cphi, const double sphi, const int s, const int ring0, const float image_rad, const double d_sl, + int& X1, int& Y1, int& Z1, double& ds, double& dz, double& dzhor, double& dzvert, + const float num_planes_per_axial_pos, const float axial_pos_to_z_offset) { // use notations from Egger's thesis - const double d_p = proj_data_info_sptr->get_tangential_sampling(); - // d_xy = image.get_voxel_size().x, but we can use the bin_size here as + // d_xy = image.get_voxel_size().x, but we can use the bin_size here as // the routines.work only when these 2 are equal const double d_xy = proj_data_info_sptr->get_tangential_sampling(); - - const double R2 =square(proj_data_info_sptr->get_ring_radius()); + + const double R2 = square(proj_data_info_sptr->get_ring_radius()); /* Radius of scanner squared in Pixel^2 */ const double R2p = R2 / d_xy / d_xy; - + // TODO REMOVE ASSUMPTION const int num_planes_per_physical_ring = 2; - assert(fabs(d_sl * num_planes_per_physical_ring/proj_data_info_sptr->get_ring_spacing() -1) < 10E-4); - - /* KT 16/06/98 + assert(fabs(d_sl * num_planes_per_physical_ring / proj_data_info_sptr->get_ring_spacing() - 1) < 10E-4); + + /* KT 16/06/98 This code should select a pixel inside the FOV. I tried to set rpix=image_rad, and X1 = (int)(X1f<0 ? ceil(X1f) : floor(X1f)); Y1 = (int)(Y1f<0 ? ceil(Y1f) : floor(Y1f)); - Up to this point it is fine. However, it would also require (messy?) + Up to this point it is fine. However, it would also require (messy?) modifications of the 'push back into beam' code, so I gave up. - + So, now we simply take rpix=image_rad-1. This means that sometimes a pixel close to the border is not selected. Not that anyone cares... */ - int rpix = round(image_rad-1); /* Radius of target image in voxel units */ + int rpix = round(image_rad - 1); /* Radius of target image in voxel units */ const double r2 = rpix * rpix * d_xy * d_xy; /* Radius squared of target image in mm^2 */ - - -//Formula in Eq 6.12 of Egger Matthias PhD + + // Formula in Eq 6.12 of Egger Matthias PhD // First compute X1, Y1 // KT 16/06/98 added const - const double t = s + 0.5; /* In a beam, not on a ray */ + const double t = s + 0.5; /* In a beam, not on a ray */ // KT 25/09/2001 replace assert() with return value // check if there is any pixel in the beam if (t > rpix) return Succeeded::no; - { - const double root = sqrt(rpix * rpix - t * t);// Eq 6.12 in EGger Thesis - const double X1f = t * cphi + sphi * root; /* Only valid if d_xy = d_p from Eq 6.12 in EGger Thesis */ - //const double X2f = t * cphi - sphi * root; - const double Y1f = t * sphi - cphi * root;// Eq 6.12 in EGger Thesis - //const double Y2f = t * sphi + cphi * root; + { + const double root = sqrt(rpix * rpix - t * t); // Eq 6.12 in EGger Thesis + const double X1f = t * cphi + sphi * root; /* Only valid if d_xy = d_p from Eq 6.12 in EGger Thesis */ + // const double X2f = t * cphi - sphi * root; + const double Y1f = t * sphi - cphi * root; // Eq 6.12 in EGger Thesis + // const double Y2f = t * sphi + cphi * root; // KTTODO when using even number of bins, this should be floor(X1f) + .5 - X1 = (int) floor(X1f + 0.5); - //X2 = (int) floor(X2f + 0.5); - Y1 = (int) floor(Y1f + 0.5); - //Y2 = (int) floor(Y2f + 0.5); + X1 = (int)floor(X1f + 0.5); + // X2 = (int) floor(X2f + 0.5); + Y1 = (int)floor(Y1f + 0.5); + // Y2 = (int) floor(Y2f + 0.5); } - // Compute ds = difference between s and s-projection of selected voxel // KT 22/05/98 we don't need t later on - //t=X1*cphi+Y1*sphi; - //ds=t-s; - ds=X1*cphi+Y1*sphi-s;// Eq 6.13 in Egger thsis + // t=X1*cphi+Y1*sphi; + // ds=t-s; + ds = X1 * cphi + Y1 * sphi - s; // Eq 6.13 in Egger thsis // Note that X1f*cphi+Y1f*sphi == t == s + 0.5, so // ds == (X1-X1f)*cphi + (Y1-Y1f)*sphi + 0.5, hence // -(cphi + sphi)/2 + 0.5 <= ds <= (cphi + sphi)/2 + 0.5 /* Push voxel back into beam */ - // KT 16/06/98 I now always use the cos(phi) case + // KT 16/06/98 I now always use the cos(phi) case // For example, when ds<0, adding sin(phi) is not guaranteed to make it positive // (where the most obvious example was phi==0, although I did treat that correctly). // On the other hand ds + cphi >= (cphi-sphi)/2 + 0.5, which is >=0.5 as // 0<=phi<=45 (in fact, it is still >=0 if 45epsilon) { @@ -2355,15 +2269,14 @@ find_start_values(const shared_ptr proj_da } else #endif - { - X1++; - //X2--; - //t+=cphi; - ds+=cphi; - // Now 0.5 <= (cphi-sphi)/2 + 0.5 <= ds < cphi+epsilon - } - } else if (ds>=1.) - { + { + X1++; + // X2--; + // t+=cphi; + ds += cphi; + // Now 0.5 <= (cphi-sphi)/2 + 0.5 <= ds < cphi+epsilon + } + } else if (ds >= 1.) { #if 0 if(sphi>epsilon) { @@ -2374,53 +2287,55 @@ find_start_values(const shared_ptr proj_da } else #endif - { - X1--; - //X2++; - //t-=cphi; - ds-=cphi; - // Now 0 <= 1-cphi<= ds <= -(cphi-sphi)/2 + 0.5 <= 0.5 - } - } + { + X1--; + // X2++; + // t-=cphi; + ds -= cphi; + // Now 0 <= 1-cphi<= ds <= -(cphi-sphi)/2 + 0.5 <= 0.5 + } + } // At the end of all this, 0 <= ds < 1 // Now find Z1 // Note that t=s+.5 { - const double t2dp2 = t * t * d_p * d_p;//Eq 6.12 in EGger thesis - //const double ttheta =(delta * d_sl / sqrt(R2 - t2dp2));//Equivalent to tan(theta) see Eq 6.10 in Egger thesis except that d_r/2 has been replaced to d_sl - + const double t2dp2 = t * t * d_p * d_p; // Eq 6.12 in EGger thesis + // const double ttheta =(delta * d_sl / sqrt(R2 - t2dp2));//Equivalent to tan(theta) see Eq 6.10 in Egger thesis except that + // d_r/2 has been replaced to d_sl + if (t2dp2 >= R2 || t2dp2 > r2) return Succeeded::no; const double root1 = sqrt(R2 - t2dp2); - const double root2 = sqrt(r2 - t2dp2); + const double root2 = sqrt(r2 - t2dp2); // Eq 6.12 in Egger's thesis - // const double Z1f = 2 * ring0 + 1 + (ttheta * (root1 - root2))/d_sl: + // const double Z1f = 2 * ring0 + 1 + (ttheta * (root1 - root2))/d_sl: // equivalent formula: // Z1f = 2 * ring0 + 1 + delta * (1 - root2/root1) - // We convert this to 'general' sizes of planes w.r.t. rings + // We convert this to 'general' sizes of planes w.r.t. rings // KT&CL 22/12/99 inserted offset - const double Z1f = - num_planes_per_axial_pos*(ring0 + 0.5) + axial_pos_to_z_offset - + num_planes_per_physical_ring*delta/2 * (1 - root2/root1); - //const double Z2f = (ring0 + 0.5)/ring_unit + (ttheta * (root1 + root2))/d_sl; + const double Z1f = num_planes_per_axial_pos * (ring0 + 0.5) + axial_pos_to_z_offset + + num_planes_per_physical_ring * delta / 2 * (1 - root2 / root1); + // const double Z2f = (ring0 + 0.5)/ring_unit + (ttheta * (root1 + root2))/d_sl; // TODO possible problem for negative Z1f, use floor() instead - Z1 = (int) floor(Z1f); + Z1 = (int)floor(Z1f); - //Z2 = (int) Z2f; + // Z2 = (int) Z2f; } { // Compute 'z' (formula 6.15 in Egger's thesis) which is the ring coordinate of the // projection of the voxel X1,Y1,Z1 - const double root=sqrt(R2p-t*t);//CL 26/10/98 Put it back the original formula as before it was root=sqrt(R2p-s*s) - //const double z=ring_unit*( Z1-delta*( (-X1*sphi+Y1*cphi)/root + 1 ) );// Eq 6.15 from Egger Thesis//CL SPAN 14/09/98 2*delta + const double root = sqrt(R2p - t * t); // CL 26/10/98 Put it back the original formula as before it was root=sqrt(R2p-s*s) + // const double z=ring_unit*( Z1-delta*( (-X1*sphi+Y1*cphi)/root + 1 ) );// Eq 6.15 from Egger Thesis//CL SPAN 14/09/98 + // 2*delta // KT&CL 22/12/99 inserted offset - const double z=( Z1-num_planes_per_physical_ring*delta/2*( (-X1*sphi+Y1*cphi)/root + 1 ) - - axial_pos_to_z_offset)/num_planes_per_axial_pos; - dz=z-ring0; + const double z = + (Z1 - num_planes_per_physical_ring * delta / 2 * ((-X1 * sphi + Y1 * cphi) / root + 1) - axial_pos_to_z_offset) / + num_planes_per_axial_pos; + dz = z - ring0; // Using the same formula (z=...) for X1f, Y1f, Z1f gives zf = ring0+ 0.5 // As the difference between X1f, Y1f, Z1f and X1,Y1,Y2 is at most 1 in every coordinate, // -1/2 - delta/root/2 <= z - zf <= delta/root/2, so @@ -2429,39 +2344,34 @@ find_start_values(const shared_ptr proj_da // For some scanners though (e.g. HiDAC) this is not true, so we keep on checking if // dz is in the appropriate range - /* Push voxel back into beam */ // KT 01/06/98 added 'else' here for the case when dz=1/num_planes_per_axial_pos, the first step puts it to 0, - // the 2nd step shouldn't do anything (dz<0), but because we compare with epsilon, + // the 2nd step shouldn't do anything (dz<0), but because we compare with epsilon, // it got back to .5 - + // MOREZ: if ->while - if (dz>=1./num_planes_per_axial_pos) + if (dz >= 1. / num_planes_per_axial_pos) { + while (dz >= 1. / num_planes_per_axial_pos) { + Z1--; + // z-=1/num_planes_per_axial_pos; + dz -= 1. / num_planes_per_axial_pos; + } + } else // if { - while (dz>=1./num_planes_per_axial_pos) - { - Z1--; - //z-=1/num_planes_per_axial_pos; - dz-=1./num_planes_per_axial_pos; - } - } - else //if - { - while (dzproj_matrix_ptr.reset(); BackProjectorByBin::set_defaults(); } void -BackProjectorByBinUsingProjMatrixByBin:: -initialise_keymap() -{ +BackProjectorByBinUsingProjMatrixByBin::initialise_keymap() { parser.add_start_key("Back Projector Using Matrix Parameters"); parser.add_stop_key("End Back Projector Using Matrix Parameters"); parser.add_parsing_key("matrix type", &proj_matrix_ptr); BackProjectorByBin::initialise_keymap(); } - bool -BackProjectorByBinUsingProjMatrixByBin:: -post_processing() -{ - //if (BackProjectorByBin::post_processing() == true) +BackProjectorByBinUsingProjMatrixByBin::post_processing() { + // if (BackProjectorByBin::post_processing() == true) // return true; - if (is_null_ptr(proj_matrix_ptr)) - { + if (is_null_ptr(proj_matrix_ptr)) { warning("BackProjectorByBinUsingProjMatrixByBin: matrix not set.\n"); return true; } return false; } -BackProjectorByBinUsingProjMatrixByBin:: -BackProjectorByBinUsingProjMatrixByBin() -{ - set_defaults(); -} +BackProjectorByBinUsingProjMatrixByBin::BackProjectorByBinUsingProjMatrixByBin() { set_defaults(); } -BackProjectorByBinUsingProjMatrixByBin:: -BackProjectorByBinUsingProjMatrixByBin( - const shared_ptr& proj_matrix_ptr - ) - : proj_matrix_ptr(proj_matrix_ptr) -{ +BackProjectorByBinUsingProjMatrixByBin::BackProjectorByBinUsingProjMatrixByBin(const shared_ptr& proj_matrix_ptr) + : proj_matrix_ptr(proj_matrix_ptr) { if (is_null_ptr(proj_matrix_ptr)) error("BackProjector initialised with zero projection matrix ptr"); } void -BackProjectorByBinUsingProjMatrixByBin:: -set_up(const shared_ptr& proj_data_info_ptr, - const shared_ptr >& image_info_ptr) +BackProjectorByBinUsingProjMatrixByBin::set_up(const shared_ptr& proj_data_info_ptr, + const shared_ptr>& image_info_ptr) { BackProjectorByBin::set_up(proj_data_info_ptr, image_info_ptr); proj_matrix_ptr->set_up(proj_data_info_ptr, image_info_ptr); } -const DataSymmetriesForViewSegmentNumbers * -BackProjectorByBinUsingProjMatrixByBin::get_symmetries_used() const -{ +const DataSymmetriesForViewSegmentNumbers* +BackProjectorByBinUsingProjMatrixByBin::get_symmetries_used() const { if (!this->_already_set_up) error("BackProjectorByBin method called without calling set_up first."); return proj_matrix_ptr->get_symmetries_ptr(); } -void -BackProjectorByBinUsingProjMatrixByBin:: -actual_back_project(DiscretisedDensity<3,float>& image, - const RelatedViewgrams& viewgrams, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) -{ +void +BackProjectorByBinUsingProjMatrixByBin::actual_back_project(DiscretisedDensity<3, float>& image, + const RelatedViewgrams& viewgrams, const int min_axial_pos_num, + const int max_axial_pos_num, const int min_tangential_pos_num, + const int max_tangential_pos_num) { if (proj_matrix_ptr->is_cache_enabled()/* && !proj_matrix_ptr->does_cache_store_only_basic_bins()*/) { - // straightforward version which relies on ProjMatrixByBin to sort out all - // symmetries - // would be slow if there's no caching at all, but is very fast if everything is cached - - ProjMatrixElemsForOneBin proj_matrix_row; - - RelatedViewgrams::const_iterator r_viewgrams_iter = viewgrams.begin(); - - while( r_viewgrams_iter!=viewgrams.end()) - { - const Viewgram& viewgram = *r_viewgrams_iter; - const int view_num = viewgram.get_view_num(); - const int segment_num = viewgram.get_segment_num(); - const int timing_num = viewgram.get_timing_pos_num(); - - for ( int tang_pos = min_tangential_pos_num ;tang_pos <= max_tangential_pos_num ;++tang_pos) - for ( int ax_pos = min_axial_pos_num; ax_pos <= max_axial_pos_num ;++ax_pos) - { - // KT 21/02/2002 added check on 0 - if (viewgram[ax_pos][tang_pos] == 0) - continue; - Bin bin(segment_num, view_num, ax_pos, tang_pos, timing_num, viewgram[ax_pos][tang_pos]); - proj_matrix_ptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, bin); - proj_matrix_row.back_project(image, bin); - } - ++r_viewgrams_iter; - } - } - else - { - // complicated version which handles the symmetries explicitly - // faster when no caching is performed, about just as fast when there is caching - ProjMatrixElemsForOneBin proj_matrix_row; - ProjMatrixElemsForOneBin proj_matrix_row_copy; - const DataSymmetriesForBins* symmetries = proj_matrix_ptr->get_symmetries_ptr(); - - Array<2,int> - already_processed(IndexRange2D(min_axial_pos_num, max_axial_pos_num, - min_tangential_pos_num, max_tangential_pos_num)); - - vector related_ax_tang_poss; - for ( int tang_pos = min_tangential_pos_num ;tang_pos <= max_tangential_pos_num ;++tang_pos) - for ( int ax_pos = min_axial_pos_num; ax_pos <= max_axial_pos_num ;++ax_pos) - { - if (already_processed[ax_pos][tang_pos]) - continue; - - Bin basic_bin(viewgrams.get_basic_segment_num(), - viewgrams.get_basic_view_num(), - ax_pos, - tang_pos, - viewgrams.get_basic_timing_pos_num()); - symmetries->find_basic_bin(basic_bin); - - proj_matrix_ptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, basic_bin); - - related_ax_tang_poss.resize(0); - symmetries->get_related_bins_factorised(related_ax_tang_poss,basic_bin, - min_axial_pos_num, max_axial_pos_num, - min_tangential_pos_num, max_tangential_pos_num); - - for ( + // straightforward version which relies on ProjMatrixByBin to sort out all + // symmetries + // would be slow if there's no caching at all, but is very fast if everything is cached + + ProjMatrixElemsForOneBin proj_matrix_row; + + RelatedViewgrams::const_iterator r_viewgrams_iter = viewgrams.begin(); + + while (r_viewgrams_iter != viewgrams.end()) { + const Viewgram& viewgram = *r_viewgrams_iter; + const int view_num = viewgram.get_view_num(); + const int segment_num = viewgram.get_segment_num(); + const int timing_num = viewgram.get_timing_pos_num(); + + for (int tang_pos = min_tangential_pos_num; tang_pos <= max_tangential_pos_num; ++tang_pos) + for (int ax_pos = min_axial_pos_num; ax_pos <= max_axial_pos_num; ++ax_pos) { + // KT 21/02/2002 added check on 0 + if (viewgram[ax_pos][tang_pos] == 0) + continue; + Bin bin(segment_num, view_num, ax_pos, tang_pos, timing_num, viewgram[ax_pos][tang_pos]); + proj_matrix_ptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, bin); + proj_matrix_row.back_project(image, bin); + } + ++r_viewgrams_iter; + } + } else { + // complicated version which handles the symmetries explicitly + // faster when no caching is performed, about just as fast when there is caching + ProjMatrixElemsForOneBin proj_matrix_row; + ProjMatrixElemsForOneBin proj_matrix_row_copy; + const DataSymmetriesForBins* symmetries = proj_matrix_ptr->get_symmetries_ptr(); + + Array<2, int> already_processed( + IndexRange2D(min_axial_pos_num, max_axial_pos_num, min_tangential_pos_num, max_tangential_pos_num)); + + vector related_ax_tang_poss; + for (int tang_pos = min_tangential_pos_num; tang_pos <= max_tangential_pos_num; ++tang_pos) + for (int ax_pos = min_axial_pos_num; ax_pos <= max_axial_pos_num; ++ax_pos) { + if (already_processed[ax_pos][tang_pos]) + continue; + + Bin basic_bin(viewgrams.get_basic_segment_num(), viewgrams.get_basic_view_num(), ax_pos, tang_pos, + viewgrams.get_basic_timing_pos_num()); + symmetries->find_basic_bin(basic_bin); + + proj_matrix_ptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, basic_bin); + + related_ax_tang_poss.resize(0); + symmetries->get_related_bins_factorised(related_ax_tang_poss, basic_bin, min_axial_pos_num, max_axial_pos_num, + min_tangential_pos_num, max_tangential_pos_num); + + for ( #ifndef STIR_NO_NAMESPACES - std:: + std:: #endif - vector::const_iterator r_ax_tang_poss_iter = related_ax_tang_poss.begin(); - r_ax_tang_poss_iter != related_ax_tang_poss.end(); - ++r_ax_tang_poss_iter) - { - const int axial_pos_tmp = (*r_ax_tang_poss_iter)[1]; - const int tang_pos_tmp = (*r_ax_tang_poss_iter)[2]; - - // symmetries might take the ranges out of what the user wants - if ( !(min_axial_pos_num <= axial_pos_tmp && axial_pos_tmp <= max_axial_pos_num && - min_tangential_pos_num <=tang_pos_tmp && tang_pos_tmp <= max_tangential_pos_num)) - continue; - - already_processed[axial_pos_tmp][tang_pos_tmp] = 1; - - - for (RelatedViewgrams::const_iterator viewgram_iter = viewgrams.begin(); - viewgram_iter != viewgrams.end(); - ++viewgram_iter) - { - // KT 21/02/2002 added check on 0 - if ((*viewgram_iter)[axial_pos_tmp][tang_pos_tmp] == 0) - continue; - proj_matrix_row_copy = proj_matrix_row; - Bin bin(viewgram_iter->get_segment_num(), - viewgram_iter->get_view_num(), - axial_pos_tmp, - tang_pos_tmp, - viewgram_iter->get_timing_pos_num(), - (*viewgram_iter)[axial_pos_tmp][tang_pos_tmp]); - - unique_ptr symm_op_ptr = - symmetries->find_symmetry_operation_from_basic_bin(bin); - // TODO replace with Bin::compare_coordinates or so - assert(bin.segment_num() == basic_bin.segment_num()); - assert(bin.view_num() == basic_bin.view_num()); - assert(bin.axial_pos_num() == basic_bin.axial_pos_num()); - assert(bin.tangential_pos_num() == basic_bin.tangential_pos_num()); - assert(bin.timing_pos_num() == basic_bin.timing_pos_num()); - - symm_op_ptr->transform_proj_matrix_elems_for_one_bin(proj_matrix_row_copy); - proj_matrix_row_copy.back_project(image, bin); - } - } - } - assert(already_processed.sum() - == ( - (max_axial_pos_num - min_axial_pos_num + 1) * - (max_tangential_pos_num - min_tangential_pos_num + 1))); - } + vector::const_iterator r_ax_tang_poss_iter = related_ax_tang_poss.begin(); + r_ax_tang_poss_iter != related_ax_tang_poss.end(); ++r_ax_tang_poss_iter) { + const int axial_pos_tmp = (*r_ax_tang_poss_iter)[1]; + const int tang_pos_tmp = (*r_ax_tang_poss_iter)[2]; + + // symmetries might take the ranges out of what the user wants + if (!(min_axial_pos_num <= axial_pos_tmp && axial_pos_tmp <= max_axial_pos_num && + min_tangential_pos_num <= tang_pos_tmp && tang_pos_tmp <= max_tangential_pos_num)) + continue; + + already_processed[axial_pos_tmp][tang_pos_tmp] = 1; + + for (RelatedViewgrams::const_iterator viewgram_iter = viewgrams.begin(); viewgram_iter != viewgrams.end(); + ++viewgram_iter) { + // KT 21/02/2002 added check on 0 + if ((*viewgram_iter)[axial_pos_tmp][tang_pos_tmp] == 0) + continue; + proj_matrix_row_copy = proj_matrix_row; + Bin bin(viewgram_iter->get_segment_num(), viewgram_iter->get_view_num(), axial_pos_tmp, tang_pos_tmp, + viewgram_iter->get_timing_pos_num(), (*viewgram_iter)[axial_pos_tmp][tang_pos_tmp]); + + unique_ptr symm_op_ptr = symmetries->find_symmetry_operation_from_basic_bin(bin); + // TODO replace with Bin::compare_coordinates or so + assert(bin.segment_num() == basic_bin.segment_num()); + assert(bin.view_num() == basic_bin.view_num()); + assert(bin.axial_pos_num() == basic_bin.axial_pos_num()); + assert(bin.tangential_pos_num() == basic_bin.tangential_pos_num()); + assert(bin.timing_pos_num() == basic_bin.timing_pos_num()); + + symm_op_ptr->transform_proj_matrix_elems_for_one_bin(proj_matrix_row_copy); + proj_matrix_row_copy.back_project(image, bin); + } + } + } + assert(already_processed.sum() == + ((max_axial_pos_num - min_axial_pos_num + 1) * (max_tangential_pos_num - min_tangential_pos_num + 1))); + } } - void -BackProjectorByBinUsingProjMatrixByBin:: -actual_back_project(DiscretisedDensity<3,float>& image, - const Bin& bin) -{ - if (proj_matrix_ptr->is_cache_enabled() /*&& !tof_enabled*/) - { - ProjMatrixElemsForOneBin proj_matrix_row; - proj_matrix_ptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, bin); - proj_matrix_row.back_project(image, bin); - } - else if (proj_matrix_ptr->is_cache_enabled()/* && tof_enabled*/) - { - tof_row->back_project(image, bin); - } - else - error("BackProjectorByBinUsingProjMatrixByBin: Symmetries should be handled by ProjMatrix. Abort. "); - +BackProjectorByBinUsingProjMatrixByBin::actual_back_project(DiscretisedDensity<3, float>& image, const Bin& bin) { + if (proj_matrix_ptr->is_cache_enabled() /*&& !tof_enabled*/) { + ProjMatrixElemsForOneBin proj_matrix_row; + proj_matrix_ptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, bin); + proj_matrix_row.back_project(image, bin); + } else if (proj_matrix_ptr->is_cache_enabled() /* && tof_enabled*/) { + tof_row->back_project(image, bin); + } else + error("BackProjectorByBinUsingProjMatrixByBin: Symmetries should be handled by ProjMatrix. Abort. "); } void -BackProjectorByBinUsingProjMatrixByBin:: -enable_tof(ProjMatrixElemsForOneBin * for_row) -{ - tof_row = for_row; -// tof_enabled = true; +BackProjectorByBinUsingProjMatrixByBin::enable_tof(ProjMatrixElemsForOneBin* for_row) { + tof_row = for_row; + // tof_enabled = true; } BackProjectorByBinUsingProjMatrixByBin* -BackProjectorByBinUsingProjMatrixByBin:: -clone() const -{ - BackProjectorByBinUsingProjMatrixByBin* sptr(new BackProjectorByBinUsingProjMatrixByBin(*this)); - sptr->proj_matrix_ptr.reset(this->proj_matrix_ptr->clone()); - return sptr; +BackProjectorByBinUsingProjMatrixByBin::clone() const { + BackProjectorByBinUsingProjMatrixByBin* sptr(new BackProjectorByBinUsingProjMatrixByBin(*this)); + sptr->proj_matrix_ptr.reset(this->proj_matrix_ptr->clone()); + return sptr; } END_NAMESPACE_STIR diff --git a/src/recon_buildblock/BackProjectorByBinUsingSquareProjMatrixByBin.cxx b/src/recon_buildblock/BackProjectorByBinUsingSquareProjMatrixByBin.cxx index 9a8f25eada..28ad70a436 100644 --- a/src/recon_buildblock/BackProjectorByBinUsingSquareProjMatrixByBin.cxx +++ b/src/recon_buildblock/BackProjectorByBinUsingSquareProjMatrixByBin.cxx @@ -8,10 +8,10 @@ \ingroup recon_buildblock \brief non-inline implementations for stir::BackProjectorByBinUsingSquareProjMatrixByBin - + \author Sanida Mustafovic \author Kris Thielemans - + */ /* Copyright (C) 2000- 2011, Hammersmith Imanet Ltd @@ -30,7 +30,6 @@ See STIR/LICENSE.txt for details */ - #include "stir/recon_buildblock/BackProjectorByBinUsingSquareProjMatrixByBin.h" #include "stir/Viewgram.h" #include "stir/RelatedViewgrams.h" @@ -40,108 +39,79 @@ START_NAMESPACE_STIR -const char * const -BackProjectorByBinUsingSquareProjMatrixByBin::registered_name = - "Matrix Square"; - +const char* const BackProjectorByBinUsingSquareProjMatrixByBin::registered_name = "Matrix Square"; void -BackProjectorByBinUsingSquareProjMatrixByBin:: -set_defaults() -{ +BackProjectorByBinUsingSquareProjMatrixByBin::set_defaults() { this->proj_matrix_ptr.reset(); } +BackProjectorByBinUsingSquareProjMatrixByBin::BackProjectorByBinUsingSquareProjMatrixByBin( + const shared_ptr& proj_matrix_ptr) + : proj_matrix_ptr(proj_matrix_ptr) { + assert(!is_null_ptr(proj_matrix_ptr)); +} -BackProjectorByBinUsingSquareProjMatrixByBin:: -BackProjectorByBinUsingSquareProjMatrixByBin( - const shared_ptr& proj_matrix_ptr - ) - : proj_matrix_ptr(proj_matrix_ptr) - { - assert(!is_null_ptr(proj_matrix_ptr)); - - } - -const DataSymmetriesForViewSegmentNumbers * -BackProjectorByBinUsingSquareProjMatrixByBin::get_symmetries_used() const -{ +const DataSymmetriesForViewSegmentNumbers* +BackProjectorByBinUsingSquareProjMatrixByBin::get_symmetries_used() const { if (!this->_already_set_up) error("BackProjectorByBin method called without calling set_up first."); return proj_matrix_ptr->get_symmetries_ptr(); } -void -BackProjectorByBinUsingSquareProjMatrixByBin:: -actual_back_project(DiscretisedDensity<3,float>& image, - const RelatedViewgrams& viewgrams, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) -{ +void +BackProjectorByBinUsingSquareProjMatrixByBin::actual_back_project(DiscretisedDensity<3, float>& image, + const RelatedViewgrams& viewgrams, + const int min_axial_pos_num, const int max_axial_pos_num, + const int min_tangential_pos_num, + const int max_tangential_pos_num) { ProjMatrixElemsForOneBin proj_matrix_row; - - + RelatedViewgrams::const_iterator r_viewgrams_iter = viewgrams.begin(); - - while( r_viewgrams_iter!=viewgrams.end()) - { + while (r_viewgrams_iter != viewgrams.end()) { const Viewgram& viewgram = *r_viewgrams_iter; const int view_num = viewgram.get_view_num(); const int segment_num = viewgram.get_segment_num(); const int timing_pos_num = viewgram.get_timing_pos_num(); - - for ( int tang_pos = min_tangential_pos_num ;tang_pos <= max_tangential_pos_num ;++tang_pos) - for ( int ax_pos = min_axial_pos_num; ax_pos <= max_axial_pos_num ;++ax_pos) - { - - Bin bin(segment_num, view_num, ax_pos, tang_pos, timing_pos_num, viewgram[ax_pos][tang_pos]); - proj_matrix_ptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, bin); - ProjMatrixElemsForOneBin::iterator element_ptr = - proj_matrix_row.begin(); - - // square matrix elements - while (element_ptr != proj_matrix_row.end()) - { - const float val=element_ptr->get_value(); - *element_ptr *=val; - element_ptr++; - } - - proj_matrix_row.back_project(image, bin); + + for (int tang_pos = min_tangential_pos_num; tang_pos <= max_tangential_pos_num; ++tang_pos) + for (int ax_pos = min_axial_pos_num; ax_pos <= max_axial_pos_num; ++ax_pos) { + + Bin bin(segment_num, view_num, ax_pos, tang_pos, timing_pos_num, viewgram[ax_pos][tang_pos]); + proj_matrix_ptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, bin); + ProjMatrixElemsForOneBin::iterator element_ptr = proj_matrix_row.begin(); + + // square matrix elements + while (element_ptr != proj_matrix_row.end()) { + const float val = element_ptr->get_value(); + *element_ptr *= val; + element_ptr++; + } + + proj_matrix_row.back_project(image, bin); } ++r_viewgrams_iter; } } void -BackProjectorByBinUsingSquareProjMatrixByBin:: -actual_back_project(DiscretisedDensity<3, float> &image, - const Bin& bin) -{ - error("BackProjectorByBinUsingSquareProjMatrixByBin is not supported for list-mode reconstruction. Abort."); +BackProjectorByBinUsingSquareProjMatrixByBin::actual_back_project(DiscretisedDensity<3, float>& image, const Bin& bin) { + error("BackProjectorByBinUsingSquareProjMatrixByBin is not supported for list-mode reconstruction. Abort."); } void -BackProjectorByBinUsingSquareProjMatrixByBin:: -initialise_keymap() -{ +BackProjectorByBinUsingSquareProjMatrixByBin::initialise_keymap() { parser.add_start_key("Back Projector ( Square) Using Matrix Parameters"); parser.add_stop_key("End Back Projector ( Square) Using Matrix Parameters"); parser.add_parsing_key("matrix type", &proj_matrix_ptr); } -BackProjectorByBinUsingSquareProjMatrixByBin:: -BackProjectorByBinUsingSquareProjMatrixByBin() -{ - set_defaults(); -} - +BackProjectorByBinUsingSquareProjMatrixByBin::BackProjectorByBinUsingSquareProjMatrixByBin() { set_defaults(); } void -BackProjectorByBinUsingSquareProjMatrixByBin:: -set_up(const shared_ptr& proj_data_info_ptr, - const shared_ptr >& image_info_ptr) +BackProjectorByBinUsingSquareProjMatrixByBin::set_up(const shared_ptr& proj_data_info_ptr, + const shared_ptr>& image_info_ptr) { BackProjectorByBin::set_up(proj_data_info_ptr, image_info_ptr); diff --git a/src/recon_buildblock/BinNormalisation.cxx b/src/recon_buildblock/BinNormalisation.cxx index 8298cb5af0..6b338a3610 100644 --- a/src/recon_buildblock/BinNormalisation.cxx +++ b/src/recon_buildblock/BinNormalisation.cxx @@ -26,7 +26,6 @@ \author Kris Thielemans */ - #include "stir/recon_buildblock/BinNormalisation.h" #include "stir/recon_buildblock/TrivialDataSymmetriesForBins.h" #include "stir/recon_buildblock/find_basic_vs_nums_in_subsets.h" @@ -40,201 +39,152 @@ START_NAMESPACE_STIR -BinNormalisation:: -BinNormalisation() - : _already_set_up(false) -{ -} +BinNormalisation::BinNormalisation() : _already_set_up(false) {} -BinNormalisation:: -~BinNormalisation() -{} +BinNormalisation::~BinNormalisation() {} -void BinNormalisation:: -set_exam_info_sptr(const shared_ptr _exam_info_sptr) -{ - this->exam_info_sptr=_exam_info_sptr; +void +BinNormalisation::set_exam_info_sptr(const shared_ptr _exam_info_sptr) { + this->exam_info_sptr = _exam_info_sptr; } -shared_ptr BinNormalisation:: -get_exam_info_sptr() const -{ - return this->exam_info_sptr; +shared_ptr +BinNormalisation::get_exam_info_sptr() const { + return this->exam_info_sptr; } Succeeded -BinNormalisation:: -set_up(const shared_ptr& exam_info_sptr, const shared_ptr& proj_data_info_sptr ) -{ +BinNormalisation::set_up(const shared_ptr& exam_info_sptr, + const shared_ptr& proj_data_info_sptr) { _already_set_up = true; _proj_data_info_sptr = proj_data_info_sptr->create_shared_clone(); - this->exam_info_sptr=exam_info_sptr; - return Succeeded::yes; + this->exam_info_sptr = exam_info_sptr; + return Succeeded::yes; } void -BinNormalisation:: -check(const ProjDataInfo& proj_data_info) const -{ +BinNormalisation::check(const ProjDataInfo& proj_data_info) const { if (!this->_already_set_up) error("BinNormalisation method called without calling set_up first."); if (!(*this->_proj_data_info_sptr >= proj_data_info)) - error(boost::format("BinNormalisation set-up with different geometry for projection data.\nSet_up was with\n%1%\nCalled with\n%2%") - % this->_proj_data_info_sptr->parameter_info() % proj_data_info.parameter_info()); + error(boost::format( + "BinNormalisation set-up with different geometry for projection data.\nSet_up was with\n%1%\nCalled with\n%2%") % + this->_proj_data_info_sptr->parameter_info() % proj_data_info.parameter_info()); } void -BinNormalisation:: -check(const ExamInfo &exam_info) const -{ - if (!(*this->exam_info_sptr==exam_info)) - error(boost::format("BinNormalisation set-up with different ExamInfo.\n Set_up was with\n%1%\nCalled with\n%2%") - % this->exam_info_sptr->parameter_info() % exam_info.parameter_info()); +BinNormalisation::check(const ExamInfo& exam_info) const { + if (!(*this->exam_info_sptr == exam_info)) + error(boost::format("BinNormalisation set-up with different ExamInfo.\n Set_up was with\n%1%\nCalled with\n%2%") % + this->exam_info_sptr->parameter_info() % exam_info.parameter_info()); } - + // TODO remove duplication between apply and undo by just having 1 functino that does the loops -void -BinNormalisation::apply(RelatedViewgrams& viewgrams, - const double start_time, const double end_time) const -{ +void +BinNormalisation::apply(RelatedViewgrams& viewgrams, const double start_time, const double end_time) const { this->check(*viewgrams.get_proj_data_info_sptr()); - for (RelatedViewgrams::iterator iter = viewgrams.begin(); iter != viewgrams.end(); ++iter) - { - Bin bin(iter->get_segment_num(),iter->get_view_num(), 0,0,iter->get_timing_pos_num()); - for (bin.axial_pos_num()= iter->get_min_axial_pos_num(); - bin.axial_pos_num()<=iter->get_max_axial_pos_num(); - ++bin.axial_pos_num()) - for (bin.tangential_pos_num()= iter->get_min_tangential_pos_num(); - bin.tangential_pos_num()<=iter->get_max_tangential_pos_num(); - ++bin.tangential_pos_num()) - (*iter)[bin.axial_pos_num()][bin.tangential_pos_num()] /= - std::max(1.E-20F, get_bin_efficiency(bin, start_time, end_time)); + for (RelatedViewgrams::iterator iter = viewgrams.begin(); iter != viewgrams.end(); ++iter) { + Bin bin(iter->get_segment_num(), iter->get_view_num(), 0, 0, iter->get_timing_pos_num()); + for (bin.axial_pos_num() = iter->get_min_axial_pos_num(); bin.axial_pos_num() <= iter->get_max_axial_pos_num(); + ++bin.axial_pos_num()) + for (bin.tangential_pos_num() = iter->get_min_tangential_pos_num(); + bin.tangential_pos_num() <= iter->get_max_tangential_pos_num(); ++bin.tangential_pos_num()) + (*iter)[bin.axial_pos_num()][bin.tangential_pos_num()] /= + std::max(1.E-20F, get_bin_efficiency(bin, start_time, end_time)); } } -void -BinNormalisation:: -undo(RelatedViewgrams& viewgrams,const double start_time, const double end_time) const -{ +void +BinNormalisation::undo(RelatedViewgrams& viewgrams, const double start_time, const double end_time) const { this->check(*viewgrams.get_proj_data_info_sptr()); - for (RelatedViewgrams::iterator iter = viewgrams.begin(); iter != viewgrams.end(); ++iter) - { - Bin bin(iter->get_segment_num(),iter->get_view_num(), 0,0,iter->get_timing_pos_num()); - for (bin.axial_pos_num()= iter->get_min_axial_pos_num(); - bin.axial_pos_num()<=iter->get_max_axial_pos_num(); - ++bin.axial_pos_num()) - for (bin.tangential_pos_num()= iter->get_min_tangential_pos_num(); - bin.tangential_pos_num()<=iter->get_max_tangential_pos_num(); - ++bin.tangential_pos_num()) - (*iter)[bin.axial_pos_num()][bin.tangential_pos_num()] *= - this->get_bin_efficiency(bin,start_time, end_time); + for (RelatedViewgrams::iterator iter = viewgrams.begin(); iter != viewgrams.end(); ++iter) { + Bin bin(iter->get_segment_num(), iter->get_view_num(), 0, 0, iter->get_timing_pos_num()); + for (bin.axial_pos_num() = iter->get_min_axial_pos_num(); bin.axial_pos_num() <= iter->get_max_axial_pos_num(); + ++bin.axial_pos_num()) + for (bin.tangential_pos_num() = iter->get_min_tangential_pos_num(); + bin.tangential_pos_num() <= iter->get_max_tangential_pos_num(); ++bin.tangential_pos_num()) + (*iter)[bin.axial_pos_num()][bin.tangential_pos_num()] *= this->get_bin_efficiency(bin, start_time, end_time); } - } -void -BinNormalisation:: -apply(ProjData& proj_data, - shared_ptr symmetries_sptr) const -{ - const float start_time=exam_info_sptr->get_time_frame_definitions().get_start_time(); - const float end_time=exam_info_sptr->get_time_frame_definitions().get_end_time(); +void +BinNormalisation::apply(ProjData& proj_data, shared_ptr symmetries_sptr) const { + const float start_time = exam_info_sptr->get_time_frame_definitions().get_start_time(); + const float end_time = exam_info_sptr->get_time_frame_definitions().get_end_time(); this->check(*proj_data.get_proj_data_info_sptr()); this->check(proj_data.get_exam_info()); if (is_null_ptr(symmetries_sptr)) symmetries_sptr.reset(new TrivialDataSymmetriesForBins(proj_data.get_proj_data_info_sptr()->create_shared_clone())); - const std::vector vs_nums_to_process = - detail::find_basic_vs_nums_in_subset(*proj_data.get_proj_data_info_sptr(), *symmetries_sptr, - proj_data.get_min_segment_num(), proj_data.get_max_segment_num(), - 0, 1/*subset_num, num_subsets*/); + const std::vector vs_nums_to_process = detail::find_basic_vs_nums_in_subset( + *proj_data.get_proj_data_info_sptr(), *symmetries_sptr, proj_data.get_min_segment_num(), proj_data.get_max_segment_num(), 0, + 1 /*subset_num, num_subsets*/); #ifdef STIR_OPENMP -#pragma omp parallel for shared(proj_data, symmetries_sptr) schedule(runtime) +# pragma omp parallel for shared(proj_data, symmetries_sptr) schedule(runtime) #endif - // note: older versions of openmp need an int as loop - for (int i=0; i(vs_nums_to_process.size()); ++i) - { - const ViewSegmentNumbers vs=vs_nums_to_process[i]; - - for (int k=proj_data.get_proj_data_info_sptr()->get_min_tof_pos_num(); - k<=proj_data.get_proj_data_info_sptr()->get_max_tof_pos_num(); - ++k) - { - - RelatedViewgrams viewgrams; + // note: older versions of openmp need an int as loop + for (int i = 0; i < static_cast(vs_nums_to_process.size()); ++i) { + const ViewSegmentNumbers vs = vs_nums_to_process[i]; + + for (int k = proj_data.get_proj_data_info_sptr()->get_min_tof_pos_num(); + k <= proj_data.get_proj_data_info_sptr()->get_max_tof_pos_num(); ++k) { + + RelatedViewgrams viewgrams; #ifdef STIR_OPENMP - // reading/writing to streams is not safe in multi-threaded code - // so protect with a critical section - // note that the name of the section has to be same for the get/set - // function as they're reading from/writing to the same stream -#pragma omp critical (BINNORMALISATION_APPLY__VIEWGRAMS) + // reading/writing to streams is not safe in multi-threaded code + // so protect with a critical section + // note that the name of the section has to be same for the get/set + // function as they're reading from/writing to the same stream +# pragma omp critical(BINNORMALISATION_APPLY__VIEWGRAMS) #endif - { - viewgrams = - proj_data.get_related_viewgrams(vs, symmetries_sptr, false, k); - } + { viewgrams = proj_data.get_related_viewgrams(vs, symmetries_sptr, false, k); } - this->apply(viewgrams, start_time, end_time); + this->apply(viewgrams, start_time, end_time); #ifdef STIR_OPENMP -#pragma omp critical (BINNORMALISATION_APPLY__VIEWGRAMS) +# pragma omp critical(BINNORMALISATION_APPLY__VIEWGRAMS) #endif - { - proj_data.set_related_viewgrams(viewgrams); - } - } + { proj_data.set_related_viewgrams(viewgrams); } } + } } -void -BinNormalisation:: -undo(ProjData& proj_data,const double start_time, const double end_time, - shared_ptr symmetries_sptr) const -{ +void +BinNormalisation::undo(ProjData& proj_data, const double start_time, const double end_time, + shared_ptr symmetries_sptr) const { this->check(*proj_data.get_proj_data_info_sptr()); if (is_null_ptr(symmetries_sptr)) symmetries_sptr.reset(new TrivialDataSymmetriesForBins(proj_data.get_proj_data_info_sptr()->create_shared_clone())); - const std::vector vs_nums_to_process = - detail::find_basic_vs_nums_in_subset(*proj_data.get_proj_data_info_sptr(), *symmetries_sptr, - proj_data.get_min_segment_num(), proj_data.get_max_segment_num(), - 0, 1/*subset_num, num_subsets*/); + const std::vector vs_nums_to_process = detail::find_basic_vs_nums_in_subset( + *proj_data.get_proj_data_info_sptr(), *symmetries_sptr, proj_data.get_min_segment_num(), proj_data.get_max_segment_num(), 0, + 1 /*subset_num, num_subsets*/); #ifdef STIR_OPENMP -#pragma omp parallel for shared(proj_data, symmetries_sptr) schedule(runtime) +# pragma omp parallel for shared(proj_data, symmetries_sptr) schedule(runtime) #endif - // note: older versions of openmp need an int as loop - for (int i=0; i(vs_nums_to_process.size()); ++i) - { - const ViewSegmentNumbers vs=vs_nums_to_process[i]; - - for (int k=proj_data.get_proj_data_info_sptr()->get_min_tof_pos_num(); - k<=proj_data.get_proj_data_info_sptr()->get_max_tof_pos_num(); - ++k) - { - RelatedViewgrams viewgrams; + // note: older versions of openmp need an int as loop + for (int i = 0; i < static_cast(vs_nums_to_process.size()); ++i) { + const ViewSegmentNumbers vs = vs_nums_to_process[i]; + + for (int k = proj_data.get_proj_data_info_sptr()->get_min_tof_pos_num(); + k <= proj_data.get_proj_data_info_sptr()->get_max_tof_pos_num(); ++k) { + RelatedViewgrams viewgrams; #ifdef STIR_OPENMP -#pragma omp critical (BINNORMALISATION_UNDO__VIEWGRAMS) +# pragma omp critical(BINNORMALISATION_UNDO__VIEWGRAMS) #endif - { - viewgrams = - proj_data.get_related_viewgrams(vs, symmetries_sptr,false,k); - } + { viewgrams = proj_data.get_related_viewgrams(vs, symmetries_sptr, false, k); } - this->undo(viewgrams, start_time, end_time); + this->undo(viewgrams, start_time, end_time); #ifdef STIR_OPENMP -#pragma omp critical (BINNORMALISATION_UNDO__VIEWGRAMS) +# pragma omp critical(BINNORMALISATION_UNDO__VIEWGRAMS) #endif - { - proj_data.set_related_viewgrams(viewgrams); - } - } + { proj_data.set_related_viewgrams(viewgrams); } } + } } - END_NAMESPACE_STIR - diff --git a/src/recon_buildblock/BinNormalisationFromAttenuationImage.cxx b/src/recon_buildblock/BinNormalisationFromAttenuationImage.cxx index 0903c3cf08..af0c49e835 100644 --- a/src/recon_buildblock/BinNormalisationFromAttenuationImage.cxx +++ b/src/recon_buildblock/BinNormalisationFromAttenuationImage.cxx @@ -37,158 +37,127 @@ START_NAMESPACE_STIR -const char * const -BinNormalisationFromAttenuationImage::registered_name = - "From Attenuation Image"; +const char* const BinNormalisationFromAttenuationImage::registered_name = "From Attenuation Image"; - -void -BinNormalisationFromAttenuationImage::set_defaults() -{ +void +BinNormalisationFromAttenuationImage::set_defaults() { attenuation_image_ptr.reset(); forward_projector_ptr.reset(); attenuation_image_filename = ""; } -void -BinNormalisationFromAttenuationImage:: -initialise_keymap() -{ +void +BinNormalisationFromAttenuationImage::initialise_keymap() { parser.add_start_key("Bin Normalisation From Attenuation Image"); parser.add_key("attenuation_image_filename", &attenuation_image_filename); parser.add_parsing_key("forward projector type", &forward_projector_ptr); parser.add_stop_key("End Bin Normalisation From Attenuation Image"); } -bool -BinNormalisationFromAttenuationImage:: -post_processing() -{ +bool +BinNormalisationFromAttenuationImage::post_processing() { // read attenuation_image // we do this only when it isn't initialised yet, as this function can be called from a constructor if (is_null_ptr(attenuation_image_ptr)) - attenuation_image_ptr = read_from_file >(attenuation_image_filename); - if (is_null_ptr(attenuation_image_ptr)) - { - warning("BinNormalisationFromAttenuationImage could not read attenuation image %s\n", - attenuation_image_filename.c_str()); + attenuation_image_ptr = read_from_file>(attenuation_image_filename); + if (is_null_ptr(attenuation_image_ptr)) { + warning("BinNormalisationFromAttenuationImage could not read attenuation image %s\n", attenuation_image_filename.c_str()); return true; } if (is_null_ptr(forward_projector_ptr)) forward_projector_ptr.reset(new ForwardProjectorByBinUsingRayTracing()); - + { const float amax = attenuation_image_ptr->find_max(); if ((amax < .08F) || (amax > .2F)) warning(boost::format("BinNormalisationFromAttenuationImage:\n" "\tattenuation image data are supposed to be in units cm^-1\n" - "\tReference: water has mu .096 cm^-1\n" + "\tReference: water has mu .096 cm^-1\n" "\tMax in attenuation image: %1%\n" - "\tContinuing as you might know what you are doing.") % amax); + "\tContinuing as you might know what you are doing.") % + amax); } #ifndef NEWSCALE - /* - cerr << "WARNING: multiplying attenuation image by x-voxel size " - << " to correct for scale factor in forward projectors...\n"; - */ - // projectors work in pixel units, so convert attenuation data + /* + cerr << "WARNING: multiplying attenuation image by x-voxel size " + << " to correct for scale factor in forward projectors...\n"; +*/ + // projectors work in pixel units, so convert attenuation data // from cm^-1 to pixel_units^-1 - const float rescale = - dynamic_cast const &>(*attenuation_image_ptr). - get_grid_spacing()[3]/10; + const float rescale = + dynamic_cast const&>(*attenuation_image_ptr).get_grid_spacing()[3] / 10; #else - const float rescale = - 0.1F; + const float rescale = 0.1F; #endif - shared_ptr > new_sptr(attenuation_image_ptr->clone()); + shared_ptr> new_sptr(attenuation_image_ptr->clone()); *new_sptr *= rescale; attenuation_image_ptr = new_sptr; return false; } +BinNormalisationFromAttenuationImage::BinNormalisationFromAttenuationImage() { set_defaults(); } -BinNormalisationFromAttenuationImage:: -BinNormalisationFromAttenuationImage() -{ - set_defaults(); -} - -BinNormalisationFromAttenuationImage:: -BinNormalisationFromAttenuationImage(const std::string& filename, - shared_ptr const& forward_projector_ptr) - : forward_projector_ptr(forward_projector_ptr), - attenuation_image_filename(filename) -{ +BinNormalisationFromAttenuationImage::BinNormalisationFromAttenuationImage( + const std::string& filename, shared_ptr const& forward_projector_ptr) + : forward_projector_ptr(forward_projector_ptr), attenuation_image_filename(filename) { attenuation_image_ptr.reset(); post_processing(); } -BinNormalisationFromAttenuationImage:: -BinNormalisationFromAttenuationImage(shared_ptr > const& attenuation_image_ptr_v, - shared_ptr const& forward_projector_ptr) - : attenuation_image_ptr(attenuation_image_ptr_v->clone()), // need a clone as it guarantees we won't be affected by the caller, and vice versa - forward_projector_ptr(forward_projector_ptr) -{ +BinNormalisationFromAttenuationImage::BinNormalisationFromAttenuationImage( + shared_ptr> const& attenuation_image_ptr_v, + shared_ptr const& forward_projector_ptr) + : attenuation_image_ptr( + attenuation_image_ptr_v->clone()), // need a clone as it guarantees we won't be affected by the caller, and vice versa + forward_projector_ptr(forward_projector_ptr) { post_processing(); } -Succeeded -BinNormalisationFromAttenuationImage:: -set_up(const shared_ptr &exam_info_sptr, const shared_ptr& proj_data_info_ptr) -{ +Succeeded +BinNormalisationFromAttenuationImage::set_up(const shared_ptr& exam_info_sptr, + const shared_ptr& proj_data_info_ptr) { BinNormalisation::set_up(exam_info_sptr, proj_data_info_ptr); forward_projector_ptr->set_up(proj_data_info_ptr, attenuation_image_ptr); forward_projector_ptr->set_input(*attenuation_image_ptr); return Succeeded::yes; } - -void -BinNormalisationFromAttenuationImage::apply(RelatedViewgrams& viewgrams,const double start_time, const double end_time) const -{ +void +BinNormalisationFromAttenuationImage::apply(RelatedViewgrams& viewgrams, const double start_time, + const double end_time) const { this->check(*viewgrams.get_proj_data_info_sptr()); RelatedViewgrams attenuation_viewgrams = viewgrams.get_empty_copy(); forward_projector_ptr->forward_project(attenuation_viewgrams); - + // TODO cannot use std::transform ? - for (RelatedViewgrams::iterator viewgrams_iter = - attenuation_viewgrams.begin(); - viewgrams_iter != attenuation_viewgrams.end(); - ++viewgrams_iter) - { + for (RelatedViewgrams::iterator viewgrams_iter = attenuation_viewgrams.begin(); + viewgrams_iter != attenuation_viewgrams.end(); ++viewgrams_iter) { in_place_exp(*viewgrams_iter); } viewgrams *= attenuation_viewgrams; } -void -BinNormalisationFromAttenuationImage:: -undo(RelatedViewgrams& viewgrams,const double start_time, const double end_time) const -{ +void +BinNormalisationFromAttenuationImage::undo(RelatedViewgrams& viewgrams, const double start_time, + const double end_time) const { this->check(*viewgrams.get_proj_data_info_sptr()); RelatedViewgrams attenuation_viewgrams = viewgrams.get_empty_copy(); forward_projector_ptr->forward_project(attenuation_viewgrams); - + // TODO cannot use std::transform ? - for (RelatedViewgrams::iterator viewgrams_iter = - attenuation_viewgrams.begin(); - viewgrams_iter != attenuation_viewgrams.end(); - ++viewgrams_iter) - { + for (RelatedViewgrams::iterator viewgrams_iter = attenuation_viewgrams.begin(); + viewgrams_iter != attenuation_viewgrams.end(); ++viewgrams_iter) { in_place_exp(*viewgrams_iter); } viewgrams /= attenuation_viewgrams; } -float -BinNormalisationFromAttenuationImage::get_bin_efficiency(const Bin& bin,const double start_time, const double end_time) const -{ - //TODO +float +BinNormalisationFromAttenuationImage::get_bin_efficiency(const Bin& bin, const double start_time, const double end_time) const { + // TODO error("BinNormalisationFromAttenuationImage::get_bin_efficiency is not implemented"); return 1; } - END_NAMESPACE_STIR - diff --git a/src/recon_buildblock/BinNormalisationFromECAT7.cxx b/src/recon_buildblock/BinNormalisationFromECAT7.cxx index 28cf93c81e..ca5efd1bc0 100644 --- a/src/recon_buildblock/BinNormalisationFromECAT7.cxx +++ b/src/recon_buildblock/BinNormalisationFromECAT7.cxx @@ -61,126 +61,92 @@ START_NAMESPACE_STIR START_NAMESPACE_ECAT START_NAMESPACE_ECAT7 - +const char* const BinNormalisationFromECAT7::registered_name = "From ECAT7"; -const char * const -BinNormalisationFromECAT7::registered_name = "From ECAT7"; - - -namespace detail -{ +namespace detail { // // helper functions used in this class. // -static -float +static float calc_geo_z_correction(const Bin& bin, int span) { - const int rtmp = abs(bin.segment_num() * span) ; - return( 1.0F + ( ( 0.007F - ( 0.000164F * rtmp ) ) * rtmp ) ); + const int rtmp = abs(bin.segment_num() * span); + return (1.0F + ((0.007F - (0.000164F * rtmp)) * rtmp)); } - - - -static -int -calc_ring1_plus_ring2(const Bin& bin, - const ProjDataInfoCylindricalNoArcCorr *proj_data_cyl) { +static int +calc_ring1_plus_ring2(const Bin& bin, const ProjDataInfoCylindricalNoArcCorr* proj_data_cyl) { int segment_num = bin.segment_num(); - + const int min_ring_diff = proj_data_cyl->get_min_ring_difference(segment_num); const int max_ring_diff = proj_data_cyl->get_max_ring_difference(segment_num); const int num_rings = proj_data_cyl->get_scanner_ptr()->get_num_rings(); - return( (2 * bin.axial_pos_num() - - (proj_data_cyl->get_min_axial_pos_num(segment_num) + - proj_data_cyl->get_max_axial_pos_num(segment_num)) - ) / (min_ring_diff != max_ring_diff ? 2 : 1) - + num_rings - 1 ); - + return ((2 * bin.axial_pos_num() - + (proj_data_cyl->get_min_axial_pos_num(segment_num) + proj_data_cyl->get_max_axial_pos_num(segment_num))) / + (min_ring_diff != max_ring_diff ? 2 : 1) + + num_rings - 1); } - - -static -void -set_detection_tangential_coords(shared_ptr proj_data_cyl_uncomp, - const Bin& uncomp_bin, +static void +set_detection_tangential_coords(shared_ptr proj_data_cyl_uncomp, const Bin& uncomp_bin, DetectionPositionPair<>& detection_position_pair) { - int det1_num=0; - int det2_num=0; - - proj_data_cyl_uncomp->get_det_num_pair_for_view_tangential_pos_num(det1_num, det2_num, - uncomp_bin.view_num(), + int det1_num = 0; + int det2_num = 0; + + proj_data_cyl_uncomp->get_det_num_pair_for_view_tangential_pos_num(det1_num, det2_num, uncomp_bin.view_num(), uncomp_bin.tangential_pos_num()); detection_position_pair.pos1().tangential_coord() = det1_num; detection_position_pair.pos2().tangential_coord() = det2_num; - } - - // Returns the sum of the two axial coordinates. Or -1 if the ring positions are // out of range. // sets axial_coord of detection_position_pair -static -int -set_detection_axial_coords(const ProjDataInfoCylindricalNoArcCorr *proj_data_info_cyl, - int ring1_plus_ring2, const Bin& uncomp_bin, - DetectionPositionPair<>& detection_position_pair) { - +static int +set_detection_axial_coords(const ProjDataInfoCylindricalNoArcCorr* proj_data_info_cyl, int ring1_plus_ring2, + const Bin& uncomp_bin, DetectionPositionPair<>& detection_position_pair) { + const int num_rings = proj_data_info_cyl->get_scanner_ptr()->get_num_rings(); const int ring_diff = uncomp_bin.segment_num(); - const int ring1 = (ring1_plus_ring2 - ring_diff)/2; - const int ring2 = (ring1_plus_ring2 + ring_diff)/2; - - if (ring1<0 || ring2 < 0 || ring1>=num_rings || ring2 >= num_rings) { - return(-1); + const int ring1 = (ring1_plus_ring2 - ring_diff) / 2; + const int ring2 = (ring1_plus_ring2 + ring_diff) / 2; + + if (ring1 < 0 || ring2 < 0 || ring1 >= num_rings || ring2 >= num_rings) { + return (-1); } - - assert((ring1_plus_ring2 + ring_diff)%2 == 0); - assert((ring1_plus_ring2 - ring_diff)%2 == 0); - + + assert((ring1_plus_ring2 + ring_diff) % 2 == 0); + assert((ring1_plus_ring2 - ring_diff) % 2 == 0); + detection_position_pair.pos1().axial_coord() = ring1; detection_position_pair.pos2().axial_coord() = ring2; - - return(ring1 + ring2); + return (ring1 + ring2); } - - } // end of namespace detail - - - // // Member functions // - - -void -BinNormalisationFromECAT7::set_defaults() -{ +void +BinNormalisationFromECAT7::set_defaults() { this->normalisation_ECAT7_filename = ""; this->_use_detector_efficiencies = true; this->_use_dead_time = true; this->_use_geometric_factors = true; - this->_use_crystal_interference_factors = true; + this->_use_crystal_interference_factors = true; } -void -BinNormalisationFromECAT7:: -initialise_keymap() -{ +void +BinNormalisationFromECAT7::initialise_keymap() { this->parser.add_start_key("Bin Normalisation From ECAT7"); // todo remove obsolete keyword this->parser.add_key("normalisation_ECAT7_filename", &this->normalisation_ECAT7_filename); @@ -193,197 +159,151 @@ initialise_keymap() this->parser.add_stop_key("End Bin Normalisation From ECAT7"); } -bool -BinNormalisationFromECAT7:: -post_processing() -{ +bool +BinNormalisationFromECAT7::post_processing() { read_norm_data(normalisation_ECAT7_filename); this->set_calibration_factor(1); return false; } +BinNormalisationFromECAT7::BinNormalisationFromECAT7() { set_defaults(); } -BinNormalisationFromECAT7:: -BinNormalisationFromECAT7() -{ - set_defaults(); -} - -BinNormalisationFromECAT7:: -BinNormalisationFromECAT7(const std::string& filename) -{ - read_norm_data(filename); -} +BinNormalisationFromECAT7::BinNormalisationFromECAT7(const std::string& filename) { read_norm_data(filename); } Succeeded -BinNormalisationFromECAT7:: -set_up(const shared_ptr& proj_data_info_ptr_v) -{ +BinNormalisationFromECAT7::set_up(const shared_ptr& proj_data_info_ptr_v) { BinNormalisation::set_up(proj_data_info_ptr_v); proj_data_info_ptr = proj_data_info_ptr_v; - proj_data_info_cyl_ptr = - dynamic_cast(proj_data_info_ptr.get()); - if (proj_data_info_cyl_ptr==0) - { + proj_data_info_cyl_ptr = dynamic_cast(proj_data_info_ptr.get()); + if (proj_data_info_cyl_ptr == 0) { warning("BinNormalisationFromECAT7 can only be used on non-arccorrected data\n"); return Succeeded::no; } - if (*proj_data_info_ptr->get_scanner_ptr() != *scanner_ptr) - { + if (*proj_data_info_ptr->get_scanner_ptr() != *scanner_ptr) { warning("BinNormalisationFromECAT7: scanner object from proj data is different from the one " - "from the normalisation file\n"); + "from the normalisation file\n"); return Succeeded::no; } - span = - proj_data_info_cyl_ptr->get_max_ring_difference(0) - - proj_data_info_cyl_ptr->get_min_ring_difference(0) + 1; + span = proj_data_info_cyl_ptr->get_max_ring_difference(0) - proj_data_info_cyl_ptr->get_min_ring_difference(0) + 1; // TODO insert check all other segments are the same - mash = scanner_ptr->get_num_detectors_per_ring()/2/proj_data_info_ptr->get_num_views(); + mash = scanner_ptr->get_num_detectors_per_ring() / 2 / proj_data_info_ptr->get_num_views(); return Succeeded::yes; } void -BinNormalisationFromECAT7:: -read_norm_data(const std::string& filename) -{ - - MatrixFile* mptr = matrix_open(filename.c_str(), MAT_READ_ONLY, Norm3d); +BinNormalisationFromECAT7::read_norm_data(const std::string& filename) { + + MatrixFile* mptr = matrix_open(filename.c_str(), MAT_READ_ONLY, Norm3d); if (mptr == 0) error("BinNormalisationFromECAT7: error opening %s\n", filename.c_str()); - scanner_ptr.reset( - find_scanner_from_ECAT_system_type(mptr->mhptr->system_type)); - - MatrixData* matrix = matrix_read( mptr, mat_numcod (1, 1, 1, 0, 0), - Norm3d /*= read data as well */); + scanner_ptr.reset(find_scanner_from_ECAT_system_type(mptr->mhptr->system_type)); + + MatrixData* matrix = matrix_read(mptr, mat_numcod(1, 1, 1, 0, 0), Norm3d /*= read data as well */); if (matrix == 0) error("BinNormalisationFromECAT7: error reading data in %s\n", filename.c_str()); - Norm3D_subheader * nrm_subheader_ptr = - reinterpret_cast(matrix->shptr); - - num_transaxial_crystals_per_block = nrm_subheader_ptr->num_transaxial_crystals ; + Norm3D_subheader* nrm_subheader_ptr = reinterpret_cast(matrix->shptr); + num_transaxial_crystals_per_block = nrm_subheader_ptr->num_transaxial_crystals; - // Calculate the number of axial blocks per singles unit and + // Calculate the number of axial blocks per singles unit and // total number of blocks per singles unit. - int axial_crystals_per_singles_unit = - scanner_ptr->get_num_axial_crystals_per_singles_unit(); - - int transaxial_crystals_per_singles_unit = - scanner_ptr->get_num_transaxial_crystals_per_singles_unit(); - - int axial_crystals_per_block = - scanner_ptr->get_num_axial_crystals_per_block(); - - int transaxial_crystals_per_block = - scanner_ptr->get_num_transaxial_crystals_per_block(); - + int axial_crystals_per_singles_unit = scanner_ptr->get_num_axial_crystals_per_singles_unit(); + + int transaxial_crystals_per_singles_unit = scanner_ptr->get_num_transaxial_crystals_per_singles_unit(); + + int axial_crystals_per_block = scanner_ptr->get_num_axial_crystals_per_block(); + + int transaxial_crystals_per_block = scanner_ptr->get_num_transaxial_crystals_per_block(); + // Axial blocks. - num_axial_blocks_per_singles_unit = - axial_crystals_per_singles_unit / axial_crystals_per_block; - - int transaxial_blocks_per_singles_unit = - transaxial_crystals_per_singles_unit / transaxial_crystals_per_block; - - // Total blocks. - num_blocks_per_singles_unit = - num_axial_blocks_per_singles_unit * transaxial_blocks_per_singles_unit; - + num_axial_blocks_per_singles_unit = axial_crystals_per_singles_unit / axial_crystals_per_block; + + int transaxial_blocks_per_singles_unit = transaxial_crystals_per_singles_unit / transaxial_crystals_per_block; + // Total blocks. + num_blocks_per_singles_unit = num_axial_blocks_per_singles_unit * transaxial_blocks_per_singles_unit; if (scanner_ptr->get_num_rings() != nrm_subheader_ptr->num_crystal_rings) error("BinNormalisationFromECAT7: " "number of rings determined from subheader is %d, while the scanner object says it is %d\n", - nrm_subheader_ptr->num_crystal_rings, scanner_ptr->get_num_rings()); + nrm_subheader_ptr->num_crystal_rings, scanner_ptr->get_num_rings()); if (scanner_ptr->get_num_detectors_per_ring() != nrm_subheader_ptr->crystals_per_ring) error("BinNormalisationFromECAT7: " "number of detectors per ring determined from subheader is %d, while the scanner object says it is %d\n", - nrm_subheader_ptr->crystals_per_ring, scanner_ptr->get_num_detectors_per_ring()); - - proj_data_info_cyl_uncompressed_ptr.reset( - dynamic_cast( - ProjDataInfo::ProjDataInfoCTI(scanner_ptr, - /*span=*/1, scanner_ptr->get_num_rings()-1, - /*num_views,=*/scanner_ptr->get_num_detectors_per_ring()/2, - /*num_tangential_poss=*/nrm_subheader_ptr->num_r_elements, - /*arc_corrected =*/false) - )); - + nrm_subheader_ptr->crystals_per_ring, scanner_ptr->get_num_detectors_per_ring()); + + proj_data_info_cyl_uncompressed_ptr.reset(dynamic_cast( + ProjDataInfo::ProjDataInfoCTI(scanner_ptr, + /*span=*/1, scanner_ptr->get_num_rings() - 1, + /*num_views,=*/scanner_ptr->get_num_detectors_per_ring() / 2, + /*num_tangential_poss=*/nrm_subheader_ptr->num_r_elements, + /*arc_corrected =*/false))); + /* Extract geometrical & crystal interference, and crystal efficiencies from the - normalisation data. + normalisation data. */ // SM 13/02/2003 corrected number of min_tang_pos_num and max_tang_pos_num used get_max_num_non_arccorrected_bins() - // instead of get_num_detectors_per_ring() - //const int min_tang_pos_num = -(scanner_ptr->get_num_detectors_per_ring()/2); - //const int max_tang_pos_num = min_tang_pos_num + scanner_ptr->get_num_detectors_per_ring() - 1; + // instead of get_num_detectors_per_ring() + // const int min_tang_pos_num = -(scanner_ptr->get_num_detectors_per_ring()/2); + // const int max_tang_pos_num = min_tang_pos_num + scanner_ptr->get_num_detectors_per_ring() - 1; - const int min_tang_pos_num = -(scanner_ptr->get_max_num_non_arccorrected_bins())/2; - const int max_tang_pos_num = min_tang_pos_num +scanner_ptr->get_max_num_non_arccorrected_bins()- 1; + const int min_tang_pos_num = -(scanner_ptr->get_max_num_non_arccorrected_bins()) / 2; + const int max_tang_pos_num = min_tang_pos_num + scanner_ptr->get_max_num_non_arccorrected_bins() - 1; - /* The order of coefficients is as follows: + /* The order of coefficients is as follows: 1. geometric_factors (= number_of_corr_planes * number_of_bins) 2. crystal_interference_factors (num_transaxial_crystals_per_block * number_of_bins) 3. efficiency_factors (number_of_rings*number_of_crystals ) */ - geometric_factors = - Array<2,float>(IndexRange2D(0,nrm_subheader_ptr->num_geo_corr_planes-1, - min_tang_pos_num, max_tang_pos_num)); + geometric_factors = + Array<2, float>(IndexRange2D(0, nrm_subheader_ptr->num_geo_corr_planes - 1, min_tang_pos_num, max_tang_pos_num)); crystal_interference_factors = - Array<2,float>(IndexRange2D(min_tang_pos_num, max_tang_pos_num, - 0, num_transaxial_crystals_per_block-1)); - // SM 13/02/2003 + Array<2, float>(IndexRange2D(min_tang_pos_num, max_tang_pos_num, 0, num_transaxial_crystals_per_block - 1)); + // SM 13/02/2003 efficiency_factors = - Array<2,float>(IndexRange2D(0,scanner_ptr->get_num_rings()-1, - 0, scanner_ptr->get_num_detectors_per_ring()-1)); - + Array<2, float>(IndexRange2D(0, scanner_ptr->get_num_rings() - 1, 0, scanner_ptr->get_num_detectors_per_ring() - 1)); #if 0 int geom_test = nrm_subheader_ptr->num_geo_corr_planes * (max_tang_pos_num-min_tang_pos_num +1); int cry_inter = num_transaxial_crystals_per_block * (max_tang_pos_num-min_tang_pos_num +1); int eff_test = scanner_ptr->get_num_detectors_per_ring() * scanner_ptr->get_num_rings(); #endif - + { - float const* data_ptr = reinterpret_cast(matrix->data_ptr); - for (Array<2,float>::full_iterator iter = geometric_factors.begin_all(); - iter != geometric_factors.end_all(); - ) + float const* data_ptr = reinterpret_cast(matrix->data_ptr); + for (Array<2, float>::full_iterator iter = geometric_factors.begin_all(); iter != geometric_factors.end_all();) *iter++ = *data_ptr++; - for (Array<2,float>::full_iterator iter = crystal_interference_factors.begin_all(); - iter != crystal_interference_factors.end_all(); - ) + for (Array<2, float>::full_iterator iter = crystal_interference_factors.begin_all(); + iter != crystal_interference_factors.end_all();) *iter++ = *data_ptr++; - for (Array<2,float>::full_iterator iter = efficiency_factors.begin_all(); - iter != efficiency_factors.end_all(); - ) + for (Array<2, float>::full_iterator iter = efficiency_factors.begin_all(); iter != efficiency_factors.end_all();) *iter++ = *data_ptr++; } // TODO mvoe dead-time stuff to a separate function /* Set up equation parameters for dead_time correction */ - float *axial_t1 = nrm_subheader_ptr->ring_dtcor1 ; /* 'Paralyzing dead_times' for each axial Xstal */ - float *axial_t2 = nrm_subheader_ptr->ring_dtcor2 ; /* 'Non-paralyzing dead_times' for each axial Xstal */ + float* axial_t1 = nrm_subheader_ptr->ring_dtcor1; /* 'Paralyzing dead_times' for each axial Xstal */ + float* axial_t2 = nrm_subheader_ptr->ring_dtcor2; /* 'Non-paralyzing dead_times' for each axial Xstal */ /* for 966 24 entries for axial_t1 & axial_t2 Each entry accounts for 2 crystal rings 0 <= iRing <= 23 for ring 0 --> 47 */ - axial_t1_array = Array<1,float>(0,scanner_ptr->get_num_rings()/num_axial_blocks_per_singles_unit-1); - axial_t2_array = Array<1,float>(0,scanner_ptr->get_num_rings()/num_axial_blocks_per_singles_unit-1); + axial_t1_array = Array<1, float>(0, scanner_ptr->get_num_rings() / num_axial_blocks_per_singles_unit - 1); + axial_t2_array = Array<1, float>(0, scanner_ptr->get_num_rings() / num_axial_blocks_per_singles_unit - 1); - for (Array<1,float>::full_iterator iter = axial_t1_array.begin_all(); - iter != axial_t1_array.end_all();) - *iter++ = *axial_t1++; + for (Array<1, float>::full_iterator iter = axial_t1_array.begin_all(); iter != axial_t1_array.end_all();) + *iter++ = *axial_t1++; - for (Array<1,float>::full_iterator iter = axial_t2_array.begin_all(); - iter != axial_t2_array.end_all();) - *iter++ = *axial_t2++; + for (Array<1, float>::full_iterator iter = axial_t2_array.begin_all(); iter != axial_t2_array.end_all();) + *iter++ = *axial_t2++; #if 0 // this is currently not used by CTI and hence not by get_dead_time_efficiency float *trans_t1 = nrm_subheader_ptr->crystal_dtcor ; /* 'Non-paralyzing dead_times' for each transaxial Xstal in block */ @@ -393,7 +313,6 @@ read_norm_data(const std::string& filename) *iter++ = *trans_t1++; #endif - free_matrix_data(matrix); matrix_close(mptr); #if 0 @@ -444,39 +363,29 @@ read_norm_data(const std::string& filename) #endif } -bool -BinNormalisationFromECAT7:: -use_detector_efficiencies() const -{ +bool +BinNormalisationFromECAT7::use_detector_efficiencies() const { return this->_use_detector_efficiencies; } -bool -BinNormalisationFromECAT7:: -use_dead_time() const -{ +bool +BinNormalisationFromECAT7::use_dead_time() const { return this->_use_dead_time; } -bool -BinNormalisationFromECAT7:: -use_geometric_factors() const -{ +bool +BinNormalisationFromECAT7::use_geometric_factors() const { return this->_use_geometric_factors; } -bool -BinNormalisationFromECAT7:: -use_crystal_interference_factors() const -{ +bool +BinNormalisationFromECAT7::use_crystal_interference_factors() const { return this->_use_crystal_interference_factors; } #if 1 -float -BinNormalisationFromECAT7:: -get_bin_efficiency(const Bin& bin, const double start_time, const double end_time) const { - +float +BinNormalisationFromECAT7::get_bin_efficiency(const Bin& bin, const double start_time, const double end_time) const { // TODO disable when not HR+ or HR++ /* @@ -492,47 +401,37 @@ get_bin_efficiency(const Bin& bin, const double start_time, const double end_tim */ const float geo_Z_corr = detail::calc_geo_z_correction(bin, span); - - float total_efficiency = 0 ; - + float total_efficiency = 0; + /* Correct dead time */ - const int start_view = bin.view_num() * mash ; - //SM removed bin.view_num() + mash ; - const int end_view = start_view + mash ; - //start_view +mash; + const int start_view = bin.view_num() * mash; + // SM removed bin.view_num() + mash ; + const int end_view = start_view + mash; + // start_view +mash; const int min_ring_diff = proj_data_info_cyl_ptr->get_min_ring_difference(bin.segment_num()); const int max_ring_diff = proj_data_info_cyl_ptr->get_max_ring_difference(bin.segment_num()); - - /* - ring1_plus_ring2 is the same for any ring pair that contributes to + /* + ring1_plus_ring2 is the same for any ring pair that contributes to this particular bin.segment_num(), bin.axial_pos_num(). We determine it first here. See ProjDataInfoCylindrical for the relevant formulas */ - const int ring1_plus_ring2 = detail::calc_ring1_plus_ring2(bin, proj_data_info_cyl_ptr); - - + const int ring1_plus_ring2 = detail::calc_ring1_plus_ring2(bin, proj_data_info_cyl_ptr); DetectionPositionPair<> detection_position_pair; - Bin uncompressed_bin(0,0,0,bin.tangential_pos_num()); + Bin uncompressed_bin(0, 0, 0, bin.tangential_pos_num()); { float view_efficiency = 0.; + for (uncompressed_bin.view_num() = start_view; uncompressed_bin.view_num() < end_view; ++uncompressed_bin.view_num()) { - for(uncompressed_bin.view_num() = start_view; - uncompressed_bin.view_num() < end_view; - ++uncompressed_bin.view_num() ) { + detail::set_detection_tangential_coords(proj_data_info_cyl_uncompressed_ptr, uncompressed_bin, detection_position_pair); - detail::set_detection_tangential_coords(proj_data_info_cyl_uncompressed_ptr, - uncompressed_bin, detection_position_pair); + float lor_efficiency = 0.; - - - float lor_efficiency= 0.; - /* loop over ring differences that contribute to bin.segment_num() at the current bin.axial_pos_num(). @@ -547,118 +446,90 @@ get_bin_efficiency(const Bin& bin, const double start_time, const double end_tim == (2*min_ring_diff+ring1_plus_ring2)%2 == ring1_plus_ring2%2 */ - for(uncompressed_bin.segment_num() = min_ring_diff + (min_ring_diff+ring1_plus_ring2)%2; - uncompressed_bin.segment_num() <= max_ring_diff; - uncompressed_bin.segment_num()+=2 ) { - - - int geo_plane_num = - detail::set_detection_axial_coords(proj_data_info_cyl_ptr, - ring1_plus_ring2, uncompressed_bin, - detection_position_pair); - if ( geo_plane_num < 0 ) { + for (uncompressed_bin.segment_num() = min_ring_diff + (min_ring_diff + ring1_plus_ring2) % 2; + uncompressed_bin.segment_num() <= max_ring_diff; uncompressed_bin.segment_num() += 2) { + + int geo_plane_num = detail::set_detection_axial_coords(proj_data_info_cyl_ptr, ring1_plus_ring2, uncompressed_bin, + detection_position_pair); + if (geo_plane_num < 0) { // Ring numbers out of range. continue; } - -#ifndef NDEBUG +# ifndef NDEBUG Bin check_bin; check_bin.set_bin_value(bin.get_bin_value()); - assert(proj_data_info_cyl_ptr->get_bin_for_det_pos_pair(check_bin, - detection_position_pair) == - Succeeded::yes); + assert(proj_data_info_cyl_ptr->get_bin_for_det_pos_pair(check_bin, detection_position_pair) == Succeeded::yes); assert(check_bin == bin); -#endif - - const DetectionPosition<>& pos1 = detection_position_pair.pos1(); +# endif + + const DetectionPosition<>& pos1 = detection_position_pair.pos1(); const DetectionPosition<>& pos2 = detection_position_pair.pos2(); - float lor_efficiency_this_pair = 1.F; - if (this->use_detector_efficiencies()) - { - lor_efficiency_this_pair = - efficiency_factors[pos1.axial_coord()][pos1.tangential_coord()] * - efficiency_factors[pos2.axial_coord()][pos2.tangential_coord()]; - } - if (this->use_dead_time()) - { - lor_efficiency_this_pair *= - get_dead_time_efficiency(pos1, start_time, end_time) * - get_dead_time_efficiency(pos2, start_time, end_time); - } - if (this->use_geometric_factors()) - { - lor_efficiency_this_pair *= -#ifdef SAME_AS_PETER + float lor_efficiency_this_pair = 1.F; + if (this->use_detector_efficiencies()) { + lor_efficiency_this_pair = efficiency_factors[pos1.axial_coord()][pos1.tangential_coord()] * + efficiency_factors[pos2.axial_coord()][pos2.tangential_coord()]; + } + if (this->use_dead_time()) { + lor_efficiency_this_pair *= + get_dead_time_efficiency(pos1, start_time, end_time) * get_dead_time_efficiency(pos2, start_time, end_time); + } + if (this->use_geometric_factors()) { + lor_efficiency_this_pair *= +# ifdef SAME_AS_PETER 1.F; -#else // this is 3dbkproj (at the moment) - geometric_factors[geo_plane_num][uncompressed_bin.tangential_pos_num()]; -#endif - } - lor_efficiency += lor_efficiency_this_pair; +# else // this is 3dbkproj (at the moment) + geometric_factors[geo_plane_num][uncompressed_bin.tangential_pos_num()]; +# endif + } + lor_efficiency += lor_efficiency_this_pair; } - if (this->use_crystal_interference_factors()) - { - view_efficiency += lor_efficiency * - crystal_interference_factors[uncompressed_bin.tangential_pos_num()][uncompressed_bin.view_num()%num_transaxial_crystals_per_block] ; - } - else - { - view_efficiency += lor_efficiency; - } - } - - if (this->use_geometric_factors()) - { - /* z==bin.get_axial_pos_num() only when min_axial_pos_num()==0*/ - // for oblique plaanes use the single radial profile from segment 0 - -#ifdef SAME_AS_PETER - const int geo_plane_num = 0; - - total_efficiency += view_efficiency * - geometric_factors[geo_plane_num][uncompressed_bin.tangential_pos_num()] * - geo_Z_corr; -#else - total_efficiency += view_efficiency * geo_Z_corr; -#endif - } - else - { - total_efficiency += view_efficiency; + if (this->use_crystal_interference_factors()) { + view_efficiency += lor_efficiency * + crystal_interference_factors[uncompressed_bin.tangential_pos_num()] + [uncompressed_bin.view_num() % num_transaxial_crystals_per_block]; + } else { + view_efficiency += lor_efficiency; } + } + + if (this->use_geometric_factors()) { + /* z==bin.get_axial_pos_num() only when min_axial_pos_num()==0*/ + // for oblique plaanes use the single radial profile from segment 0 + +# ifdef SAME_AS_PETER + const int geo_plane_num = 0; + + total_efficiency += view_efficiency * geometric_factors[geo_plane_num][uncompressed_bin.tangential_pos_num()] * geo_Z_corr; +# else + total_efficiency += view_efficiency * geo_Z_corr; +# endif + } else { + total_efficiency += view_efficiency; + } } return total_efficiency; } #endif - -float -BinNormalisationFromECAT7::get_dead_time_efficiency (const DetectionPosition<>& det_pos, - const double start_time, - const double end_time) const -{ +float +BinNormalisationFromECAT7::get_dead_time_efficiency(const DetectionPosition<>& det_pos, const double start_time, + const double end_time) const { if (is_null_ptr(singles_rates_ptr)) { return 1; } // Get singles rate per block (rate per singles unit / blocks per singles unit). - const float rate = singles_rates_ptr->get_singles_rate(det_pos, start_time, end_time) / - num_blocks_per_singles_unit; - - return - ( 1.0F + axial_t1_array[ det_pos.axial_coord()/num_axial_blocks_per_singles_unit] * rate + - axial_t2_array[ det_pos.axial_coord()/num_axial_blocks_per_singles_unit] * rate * rate ); - - //* ( 1. + ( trans_t1_array[ det_pos.tangential_coord() % num_transaxial_crystals_per_block ] * rate ) ) ; - -} + const float rate = singles_rates_ptr->get_singles_rate(det_pos, start_time, end_time) / num_blocks_per_singles_unit; + return (1.0F + axial_t1_array[det_pos.axial_coord() / num_axial_blocks_per_singles_unit] * rate + + axial_t2_array[det_pos.axial_coord() / num_axial_blocks_per_singles_unit] * rate * rate); + //* ( 1. + ( trans_t1_array[ det_pos.tangential_coord() % num_transaxial_crystals_per_block ] * rate ) ) ; +} END_NAMESPACE_ECAT7 -END_NAMESPACE_ECAT +END_NAMESPACE_ECAT END_NAMESPACE_STIR - diff --git a/src/recon_buildblock/BinNormalisationFromECAT8.cxx b/src/recon_buildblock/BinNormalisationFromECAT8.cxx index bd2960fde0..006336c81b 100644 --- a/src/recon_buildblock/BinNormalisationFromECAT8.cxx +++ b/src/recon_buildblock/BinNormalisationFromECAT8.cxx @@ -32,7 +32,6 @@ \author Sanida Mustafovic */ - #include "stir/recon_buildblock/BinNormalisationFromECAT8.h" #include "stir/DetectionPosition.h" #include "stir/DetectionPositionPair.h" @@ -60,118 +59,87 @@ using std::ios; START_NAMESPACE_STIR START_NAMESPACE_ECAT - - -const char * const -BinNormalisationFromECAT8::registered_name = "From ECAT8"; +const char* const BinNormalisationFromECAT8::registered_name = "From ECAT8"; - -namespace detail -{ +namespace detail { // // helper functions used in this class. // - -static -int -calc_ring1_plus_ring2(const Bin& bin, - const ProjDataInfoCylindricalNoArcCorr *proj_data_cyl) { +static int +calc_ring1_plus_ring2(const Bin& bin, const ProjDataInfoCylindricalNoArcCorr* proj_data_cyl) { int segment_num = bin.segment_num(); - + const int min_ring_diff = proj_data_cyl->get_min_ring_difference(segment_num); const int max_ring_diff = proj_data_cyl->get_max_ring_difference(segment_num); const int num_rings = proj_data_cyl->get_scanner_ptr()->get_num_rings(); - return( (2 * bin.axial_pos_num() - - (proj_data_cyl->get_min_axial_pos_num(segment_num) + - proj_data_cyl->get_max_axial_pos_num(segment_num)) - ) / (min_ring_diff != max_ring_diff ? 2 : 1) - + num_rings - 1 ); - + return ((2 * bin.axial_pos_num() - + (proj_data_cyl->get_min_axial_pos_num(segment_num) + proj_data_cyl->get_max_axial_pos_num(segment_num))) / + (min_ring_diff != max_ring_diff ? 2 : 1) + + num_rings - 1); } - - -static -void -set_detection_tangential_coords(shared_ptr proj_data_cyl_uncomp, - const Bin& uncomp_bin, +static void +set_detection_tangential_coords(shared_ptr proj_data_cyl_uncomp, const Bin& uncomp_bin, DetectionPositionPair<>& detection_position_pair) { - int det1_num=0; - int det2_num=0; - - proj_data_cyl_uncomp->get_det_num_pair_for_view_tangential_pos_num(det1_num, det2_num, - uncomp_bin.view_num(), + int det1_num = 0; + int det2_num = 0; + + proj_data_cyl_uncomp->get_det_num_pair_for_view_tangential_pos_num(det1_num, det2_num, uncomp_bin.view_num(), uncomp_bin.tangential_pos_num()); detection_position_pair.pos1().tangential_coord() = det1_num; detection_position_pair.pos2().tangential_coord() = det2_num; - } - - // Returns the sum of the two axial coordinates. Or -1 if the ring positions are // out of range. // sets axial_coord of detection_position_pair -static -int -set_detection_axial_coords(const ProjDataInfoCylindricalNoArcCorr *proj_data_info_cyl, - int ring1_plus_ring2, const Bin& uncomp_bin, - DetectionPositionPair<>& detection_position_pair) { - +static int +set_detection_axial_coords(const ProjDataInfoCylindricalNoArcCorr* proj_data_info_cyl, int ring1_plus_ring2, + const Bin& uncomp_bin, DetectionPositionPair<>& detection_position_pair) { + const int num_rings = proj_data_info_cyl->get_scanner_ptr()->get_num_rings(); const int ring_diff = uncomp_bin.segment_num(); - const int ring1 = (ring1_plus_ring2 - ring_diff)/2; - const int ring2 = (ring1_plus_ring2 + ring_diff)/2; - - if (ring1<0 || ring2 < 0 || ring1>=num_rings || ring2 >= num_rings) { - return(-1); + const int ring1 = (ring1_plus_ring2 - ring_diff) / 2; + const int ring2 = (ring1_plus_ring2 + ring_diff) / 2; + + if (ring1 < 0 || ring2 < 0 || ring1 >= num_rings || ring2 >= num_rings) { + return (-1); } - - assert((ring1_plus_ring2 + ring_diff)%2 == 0); - assert((ring1_plus_ring2 - ring_diff)%2 == 0); - + + assert((ring1_plus_ring2 + ring_diff) % 2 == 0); + assert((ring1_plus_ring2 - ring_diff) % 2 == 0); + detection_position_pair.pos1().axial_coord() = ring1; detection_position_pair.pos2().axial_coord() = ring2; - - return(ring1 + ring2); + return (ring1 + ring2); } - - } // end of namespace detail - - - // // Member functions // - - -void -BinNormalisationFromECAT8::set_defaults() -{ +void +BinNormalisationFromECAT8::set_defaults() { this->normalisation_ECAT8_filename = ""; this->_use_gaps = true; this->_use_detector_efficiencies = true; this->_use_dead_time = false; this->_use_geometric_factors = true; - this->_use_crystal_interference_factors = true; + this->_use_crystal_interference_factors = true; } -void -BinNormalisationFromECAT8:: -initialise_keymap() -{ +void +BinNormalisationFromECAT8::initialise_keymap() { this->parser.add_start_key("Bin Normalisation From ECAT8"); // todo remove obsolete keyword this->parser.add_key("normalisation_ECAT8_filename", &this->normalisation_ECAT8_filename); @@ -179,73 +147,57 @@ initialise_keymap() this->parser.add_parsing_key("singles rates", &this->singles_rates_ptr); this->parser.add_key("use_gaps", &this->_use_gaps); this->parser.add_key("use_detector_efficiencies", &this->_use_detector_efficiencies); - //this->parser.add_key("use_dead_time", &this->_use_dead_time); + // this->parser.add_key("use_dead_time", &this->_use_dead_time); this->parser.add_key("use_geometric_factors", &this->_use_geometric_factors); this->parser.add_key("use_crystal_interference_factors", &this->_use_crystal_interference_factors); this->parser.add_stop_key("End Bin Normalisation From ECAT8"); } -bool -BinNormalisationFromECAT8:: -post_processing() -{ +bool +BinNormalisationFromECAT8::post_processing() { read_norm_data(normalisation_ECAT8_filename); -// this->set_calibration_factor(cross_calib_factor*calib_factor); TODO understand if we need to use cross calib factor. Let's set 1 for now + // this->set_calibration_factor(cross_calib_factor*calib_factor); TODO understand if we need to use cross calib factor. Let's + // set 1 for now this->set_calibration_factor(1); return false; } +BinNormalisationFromECAT8::BinNormalisationFromECAT8() { set_defaults(); } -BinNormalisationFromECAT8:: -BinNormalisationFromECAT8() -{ - set_defaults(); -} - -BinNormalisationFromECAT8:: -BinNormalisationFromECAT8(const string& filename) -{ +BinNormalisationFromECAT8::BinNormalisationFromECAT8(const string& filename) { set_defaults(); read_norm_data(filename); } Succeeded -BinNormalisationFromECAT8:: -set_up(const shared_ptr &exam_info_sptr_v, const shared_ptr& proj_data_info_ptr_v) -{ +BinNormalisationFromECAT8::set_up(const shared_ptr& exam_info_sptr_v, + const shared_ptr& proj_data_info_ptr_v) { BinNormalisation::set_up(exam_info_sptr_v, proj_data_info_ptr_v); set_exam_info_sptr(exam_info_sptr_v); proj_data_info_ptr = proj_data_info_ptr_v; - proj_data_info_cyl_ptr = - dynamic_cast(proj_data_info_ptr.get()); - if (proj_data_info_cyl_ptr==0) - { + proj_data_info_cyl_ptr = dynamic_cast(proj_data_info_ptr.get()); + if (proj_data_info_cyl_ptr == 0) { warning("BinNormalisationFromECAT8 can only be used on non-arccorrected data\n"); return Succeeded::no; } - if (*proj_data_info_ptr->get_scanner_ptr() != *scanner_ptr) - { + if (*proj_data_info_ptr->get_scanner_ptr() != *scanner_ptr) { warning("BinNormalisationFromECAT8: scanner object from proj data is different from the one " - "from the normalisation file\n"); + "from the normalisation file\n"); return Succeeded::no; } - span = - proj_data_info_cyl_ptr->get_max_ring_difference(0) - - proj_data_info_cyl_ptr->get_min_ring_difference(0) + 1; + span = proj_data_info_cyl_ptr->get_max_ring_difference(0) - proj_data_info_cyl_ptr->get_min_ring_difference(0) + 1; // TODO insert check all other segments are the same - mash = scanner_ptr->get_num_detectors_per_ring()/2/proj_data_info_ptr->get_num_views(); + mash = scanner_ptr->get_num_detectors_per_ring() / 2 / proj_data_info_ptr->get_num_views(); return Succeeded::yes; } void -BinNormalisationFromECAT8:: -read_norm_data(const string& filename) -{ - +BinNormalisationFromECAT8::read_norm_data(const string& filename) { + #if 0 MatrixFile* mptr = matrix_open(filename.c_str(), MAT_READ_ONLY, Norm3d); if (mptr == 0) @@ -275,62 +227,54 @@ MatrixFile* mptr = matrix_open(filename.c_str(), MAT_READ_ONLY, Norm3d); parser.add_key("originating_system", &originating_system); parser.add_key("name_of_data_file", &data_file_name); parser.add_key("%number of buckets", &num_buckets); - parser.add_key("%scanner quantification factor (Bq*s/ECAT counts)",& calib_factor); - parser.add_key("%cross calibration factor",& cross_calib_factor); + parser.add_key("%scanner quantification factor (Bq*s/ECAT counts)", &calib_factor); + parser.add_key("%cross calibration factor", &cross_calib_factor); parser.parse(filename.c_str()); } #endif // remove trailing \r - std::string s=/*interfile_parser.*/originating_system; - s.erase( std::remove_if( s.begin(), s.end(), isspace ), s.end() ); - /*interfile_parser.*/originating_system=s; - s=/*interfile_parser.*/data_file_name; - s.erase( std::remove_if( s.begin(), s.end(), isspace ), s.end() ); - /*interfile_parser.*/data_file_name=s; - - this->scanner_ptr.reset(Scanner::get_scanner_from_name(/*interfile_parser.*/originating_system)); - switch(this->scanner_ptr->get_type()) - { - //case Scanner::E1080: - case Scanner::Siemens_mCT: - case Scanner::Siemens_mMR: - break; - default: - error(boost::format("Unknown originating_system '%s', when parsing file '%s'") % /*interfile_parser.*/originating_system % filename ); - } + std::string s = /*interfile_parser.*/ originating_system; + s.erase(std::remove_if(s.begin(), s.end(), isspace), s.end()); + /*interfile_parser.*/ originating_system = s; + s = /*interfile_parser.*/ data_file_name; + s.erase(std::remove_if(s.begin(), s.end(), isspace), s.end()); + /*interfile_parser.*/ data_file_name = s; + + this->scanner_ptr.reset(Scanner::get_scanner_from_name(/*interfile_parser.*/ originating_system)); + switch (this->scanner_ptr->get_type()) { + // case Scanner::E1080: + case Scanner::Siemens_mCT: + case Scanner::Siemens_mMR: + break; + default: + error(boost::format("Unknown originating_system '%s', when parsing file '%s'") % /*interfile_parser.*/ originating_system % + filename); + } - char directory_name[max_filename_length]; - get_directory_name(directory_name, filename.c_str()); - char full_data_file_name[max_filename_length]; - strcpy(full_data_file_name, data_file_name.c_str()); - prepend_directory_name(full_data_file_name, directory_name); + char directory_name[max_filename_length]; + get_directory_name(directory_name, filename.c_str()); + char full_data_file_name[max_filename_length]; + strcpy(full_data_file_name, data_file_name.c_str()); + prepend_directory_name(full_data_file_name, directory_name); num_transaxial_crystals_per_block = scanner_ptr->get_num_transaxial_crystals_per_block(); - // Calculate the number of axial blocks per singles unit and + // Calculate the number of axial blocks per singles unit and // total number of blocks per singles unit. - int axial_crystals_per_singles_unit = - scanner_ptr->get_num_axial_crystals_per_singles_unit(); - - int transaxial_crystals_per_singles_unit = - scanner_ptr->get_num_transaxial_crystals_per_singles_unit(); - - int axial_crystals_per_block = - scanner_ptr->get_num_axial_crystals_per_block(); + int axial_crystals_per_singles_unit = scanner_ptr->get_num_axial_crystals_per_singles_unit(); + + int transaxial_crystals_per_singles_unit = scanner_ptr->get_num_transaxial_crystals_per_singles_unit(); + + int axial_crystals_per_block = scanner_ptr->get_num_axial_crystals_per_block(); + + int transaxial_crystals_per_block = scanner_ptr->get_num_transaxial_crystals_per_block(); - int transaxial_crystals_per_block = - scanner_ptr->get_num_transaxial_crystals_per_block(); - // Axial blocks. - num_axial_blocks_per_singles_unit = - axial_crystals_per_singles_unit / axial_crystals_per_block; - - int transaxial_blocks_per_singles_unit = - transaxial_crystals_per_singles_unit / transaxial_crystals_per_block; - + num_axial_blocks_per_singles_unit = axial_crystals_per_singles_unit / axial_crystals_per_block; + + int transaxial_blocks_per_singles_unit = transaxial_crystals_per_singles_unit / transaxial_crystals_per_block; + // Total blocks. - num_blocks_per_singles_unit = - num_axial_blocks_per_singles_unit * transaxial_blocks_per_singles_unit; - + num_blocks_per_singles_unit = num_axial_blocks_per_singles_unit * transaxial_blocks_per_singles_unit; #if 0 if (scanner_ptr->get_num_rings() != nrm_subheader_ptr->num_crystal_rings) @@ -342,44 +286,39 @@ MatrixFile* mptr = matrix_open(filename.c_str(), MAT_READ_ONLY, Norm3d); "number of detectors per ring determined from subheader is %d, while the scanner object says it is %d\n", nrm_subheader_ptr->crystals_per_ring, scanner_ptr->get_num_detectors_per_ring()); #endif - proj_data_info_cyl_uncompressed_ptr.reset( - dynamic_cast( - ProjDataInfo::ProjDataInfoCTI(scanner_ptr, - /*span=*/1, scanner_ptr->get_num_rings()-1, - /*num_views,=*/scanner_ptr->get_num_detectors_per_ring()/2, - /*num_tangential_poss=*/scanner_ptr->get_max_num_non_arccorrected_bins(), //XXXnrm_subheader_ptr->num_r_elements, - /*arc_corrected =*/false) - )); - + proj_data_info_cyl_uncompressed_ptr.reset(dynamic_cast(ProjDataInfo::ProjDataInfoCTI( + scanner_ptr, + /*span=*/1, scanner_ptr->get_num_rings() - 1, + /*num_views,=*/scanner_ptr->get_num_detectors_per_ring() / 2, + /*num_tangential_poss=*/scanner_ptr->get_max_num_non_arccorrected_bins(), // XXXnrm_subheader_ptr->num_r_elements, + /*arc_corrected =*/false))); + /* Extract geometrical & crystal interference, and crystal efficiencies from the - normalisation data. + normalisation data. */ // SM 13/02/2003 corrected number of min_tang_pos_num and max_tang_pos_num used get_max_num_non_arccorrected_bins() - // instead of get_num_detectors_per_ring() - //const int min_tang_pos_num = -(scanner_ptr->get_num_detectors_per_ring()/2); - //const int max_tang_pos_num = min_tang_pos_num + scanner_ptr->get_num_detectors_per_ring() - 1; + // instead of get_num_detectors_per_ring() + // const int min_tang_pos_num = -(scanner_ptr->get_num_detectors_per_ring()/2); + // const int max_tang_pos_num = min_tang_pos_num + scanner_ptr->get_num_detectors_per_ring() - 1; - const int min_tang_pos_num = -(scanner_ptr->get_max_num_non_arccorrected_bins())/2; - const int max_tang_pos_num = min_tang_pos_num +scanner_ptr->get_max_num_non_arccorrected_bins()- 1; + const int min_tang_pos_num = -(scanner_ptr->get_max_num_non_arccorrected_bins()) / 2; + const int max_tang_pos_num = min_tang_pos_num + scanner_ptr->get_max_num_non_arccorrected_bins() - 1; - /* The order of coefficients is as follows: + /* The order of coefficients is as follows: 1. geometric_factors (= number_of_corr_planes * number_of_bins) 2. crystal_interference_factors (num_transaxial_crystals_per_block * number_of_bins) 3. efficiency_factors (number_of_rings*number_of_crystals ) */ - geometric_factors = - Array<2,float>(IndexRange2D(0,scanner_ptr->get_num_rings()*2-1-1, //XXXXnrm_subheader_ptr->num_geo_corr_planes-1, - min_tang_pos_num, max_tang_pos_num)); + geometric_factors = + Array<2, float>(IndexRange2D(0, scanner_ptr->get_num_rings() * 2 - 1 - 1, // XXXXnrm_subheader_ptr->num_geo_corr_planes-1, + min_tang_pos_num, max_tang_pos_num)); crystal_interference_factors = - Array<2,float>(IndexRange2D(min_tang_pos_num, max_tang_pos_num, - 0, num_transaxial_crystals_per_block-1)); - // SM 13/02/2003 + Array<2, float>(IndexRange2D(min_tang_pos_num, max_tang_pos_num, 0, num_transaxial_crystals_per_block - 1)); + // SM 13/02/2003 efficiency_factors = - Array<2,float>(IndexRange2D(0,scanner_ptr->get_num_rings()-1, - 0, scanner_ptr->get_num_detectors_per_ring()-1)); - + Array<2, float>(IndexRange2D(0, scanner_ptr->get_num_rings() - 1, 0, scanner_ptr->get_num_detectors_per_ring() - 1)); #if 0 int geom_test = nrm_subheader_ptr->num_geo_corr_planes * (max_tang_pos_num-min_tang_pos_num +1); @@ -395,49 +334,39 @@ MatrixFile* mptr = matrix_open(filename.c_str(), MAT_READ_ONLY, Norm3d); if (read_data(binary_data, efficiency_factors, ByteOrder::little_endian) != Succeeded::yes) error("failed reading efficiency_factors from '%s'", full_data_file_name); - if (scanner_ptr->get_type() == Scanner::Siemens_mMR) - { + if (scanner_ptr->get_type() == Scanner::Siemens_mMR) { // for mMR, we need to shift the efficiencies for 1 crystal. This is probably because of where the gap is inserted // TODO we have no idea if this is necessary for other ECAT8 scanners. // The code below works for the mMR ONLY - for (int r=0; rget_num_rings(); ++r) - { - int c=scanner_ptr->get_num_detectors_per_ring()-1; - const float save_last_eff = efficiency_factors[r][c]; - for (; c>0; --c) - { - efficiency_factors[r][c]= efficiency_factors[r][c-1]; - } - efficiency_factors[r][c]= save_last_eff; + for (int r = 0; r < scanner_ptr->get_num_rings(); ++r) { + int c = scanner_ptr->get_num_detectors_per_ring() - 1; + const float save_last_eff = efficiency_factors[r][c]; + for (; c > 0; --c) { + efficiency_factors[r][c] = efficiency_factors[r][c - 1]; } + efficiency_factors[r][c] = save_last_eff; + } } - if (this->_use_gaps) - { - // TODO we really have no idea where the gaps are for every ECAT8 scanner. - // The code below works for the mMR - if (scanner_ptr->get_num_virtual_transaxial_crystals_per_block()>0) - { - for (int r=0; rget_num_rings(); ++r) - for (int c=0; cget_num_detectors_per_ring(); - c+=scanner_ptr->get_num_transaxial_crystals_per_block()) - for (int inc=0; incget_num_virtual_transaxial_crystals_per_block(); ++inc) - { - efficiency_factors[r][c+inc]=0.F; - } - } - if (scanner_ptr->get_num_virtual_axial_crystals_per_block()>0) - { - // axial gaps for mCT etc - for (int r=scanner_ptr->get_num_axial_crystals_per_block()-scanner_ptr->get_num_virtual_axial_crystals_per_block(); - rget_num_rings(); - r+=scanner_ptr->get_num_axial_crystals_per_block()) - for (int inc=0; incget_num_virtual_axial_crystals_per_block(); ++inc) - { - efficiency_factors[r+inc].fill(0.F); - } + if (this->_use_gaps) { + // TODO we really have no idea where the gaps are for every ECAT8 scanner. + // The code below works for the mMR + if (scanner_ptr->get_num_virtual_transaxial_crystals_per_block() > 0) { + for (int r = 0; r < scanner_ptr->get_num_rings(); ++r) + for (int c = 0; c < scanner_ptr->get_num_detectors_per_ring(); c += scanner_ptr->get_num_transaxial_crystals_per_block()) + for (int inc = 0; inc < scanner_ptr->get_num_virtual_transaxial_crystals_per_block(); ++inc) { + efficiency_factors[r][c + inc] = 0.F; + } + } + if (scanner_ptr->get_num_virtual_axial_crystals_per_block() > 0) { + // axial gaps for mCT etc + for (int r = scanner_ptr->get_num_axial_crystals_per_block() - scanner_ptr->get_num_virtual_axial_crystals_per_block(); + r < scanner_ptr->get_num_rings(); r += scanner_ptr->get_num_axial_crystals_per_block()) + for (int inc = 0; inc < scanner_ptr->get_num_virtual_axial_crystals_per_block(); ++inc) { + efficiency_factors[r + inc].fill(0.F); } } + } // TODO mvoe dead-time stuff to a separate function #if 0 @@ -459,53 +388,45 @@ MatrixFile* mptr = matrix_open(filename.c_str(), MAT_READ_ONLY, Norm3d); for (Array<1,float>::full_iterator iter = axial_t2_array.begin_all(); iter != axial_t2_array.end_all();) *iter++ = *axial_t2++; -#if 0 +# if 0 // this is currently not used by CTI and hence not by get_dead_time_efficiency float *trans_t1 = nrm_subheader_ptr->crystal_dtcor ; /* 'Non-paralyzing dead_times' for each transaxial Xstal in block */ trans_t1_array = Array<1,float>(0,num_transaxial_crystals_per_block-1); for (Array<1,float>::full_iterator iter = trans_t1_array.begin_all(); iter != trans_t1_array.end_all();) *iter++ = *trans_t1++; -#endif +# endif #endif - #if 1 - // to test pipe the obtained values into file - ofstream out_geom; - ofstream out_inter; - ofstream out_eff; - out_geom.open("geom_out.txt",ios::out); - out_inter.open("inter_out.txt",ios::out); - out_eff.open("eff_out.txt",ios::out); - - for ( int i = geometric_factors.get_min_index(); i<=geometric_factors.get_max_index();i++) - { - for ( int j =geometric_factors[i].get_min_index(); j <=geometric_factors[i].get_max_index(); j++) - { - out_geom << geometric_factors[i][j] << " " ; - } - out_geom << std::endl; + // to test pipe the obtained values into file + ofstream out_geom; + ofstream out_inter; + ofstream out_eff; + out_geom.open("geom_out.txt", ios::out); + out_inter.open("inter_out.txt", ios::out); + out_eff.open("eff_out.txt", ios::out); + + for (int i = geometric_factors.get_min_index(); i <= geometric_factors.get_max_index(); i++) { + for (int j = geometric_factors[i].get_min_index(); j <= geometric_factors[i].get_max_index(); j++) { + out_geom << geometric_factors[i][j] << " "; } + out_geom << std::endl; + } + for (int i = crystal_interference_factors.get_min_index(); i <= crystal_interference_factors.get_max_index(); i++) { + for (int j = crystal_interference_factors[i].get_min_index(); j <= crystal_interference_factors[i].get_max_index(); j++) { + out_inter << crystal_interference_factors[i][j] << " "; + } + out_inter << std::endl; + } - for ( int i = crystal_interference_factors.get_min_index(); i<=crystal_interference_factors.get_max_index();i++) - { - for ( int j =crystal_interference_factors[i].get_min_index(); j <=crystal_interference_factors[i].get_max_index(); j++) - { - out_inter << crystal_interference_factors[i][j] << " " ; - } - out_inter << std::endl; - } - - for ( int i = efficiency_factors.get_min_index(); i<=efficiency_factors.get_max_index();i++) - { - for ( int j =efficiency_factors[i].get_min_index(); j <=efficiency_factors[i].get_max_index(); j++) - { - out_eff << efficiency_factors[i][j] << " " ; - } - out_eff << std::endl<< std::endl; - } + for (int i = efficiency_factors.get_min_index(); i <= efficiency_factors.get_max_index(); i++) { + for (int j = efficiency_factors[i].get_min_index(); j <= efficiency_factors[i].get_max_index(); j++) { + out_eff << efficiency_factors[i][j] << " "; + } + out_eff << std::endl << std::endl; + } #endif @@ -516,39 +437,29 @@ MatrixFile* mptr = matrix_open(filename.c_str(), MAT_READ_ONLY, Norm3d); #endif } -bool -BinNormalisationFromECAT8:: -use_detector_efficiencies() const -{ +bool +BinNormalisationFromECAT8::use_detector_efficiencies() const { return this->_use_detector_efficiencies; } -bool -BinNormalisationFromECAT8:: -use_dead_time() const -{ +bool +BinNormalisationFromECAT8::use_dead_time() const { return this->_use_dead_time; } -bool -BinNormalisationFromECAT8:: -use_geometric_factors() const -{ +bool +BinNormalisationFromECAT8::use_geometric_factors() const { return this->_use_geometric_factors; } -bool -BinNormalisationFromECAT8:: -use_crystal_interference_factors() const -{ +bool +BinNormalisationFromECAT8::use_crystal_interference_factors() const { return this->_use_crystal_interference_factors; } #if 1 -float -BinNormalisationFromECAT8:: -get_uncalibrated_bin_efficiency(const Bin& bin, const double start_time, const double end_time) const { - +float +BinNormalisationFromECAT8::get_uncalibrated_bin_efficiency(const Bin& bin, const double start_time, const double end_time) const { // TODO disable when not HR+ or HR++ /* @@ -564,47 +475,37 @@ get_uncalibrated_bin_efficiency(const Bin& bin, const double start_time, const d */ const float geo_Z_corr = 1; - - float total_efficiency = 0 ; - + float total_efficiency = 0; + /* Correct dead time */ - const int start_view = bin.view_num() * mash ; - //SM removed bin.view_num() + mash ; - const int end_view = start_view + mash ; - //start_view +mash; + const int start_view = bin.view_num() * mash; + // SM removed bin.view_num() + mash ; + const int end_view = start_view + mash; + // start_view +mash; const int min_ring_diff = proj_data_info_cyl_ptr->get_min_ring_difference(bin.segment_num()); const int max_ring_diff = proj_data_info_cyl_ptr->get_max_ring_difference(bin.segment_num()); - - /* - ring1_plus_ring2 is the same for any ring pair that contributes to + /* + ring1_plus_ring2 is the same for any ring pair that contributes to this particular bin.segment_num(), bin.axial_pos_num(). We determine it first here. See ProjDataInfoCylindrical for the relevant formulas */ - const int ring1_plus_ring2 = detail::calc_ring1_plus_ring2(bin, proj_data_info_cyl_ptr); - - + const int ring1_plus_ring2 = detail::calc_ring1_plus_ring2(bin, proj_data_info_cyl_ptr); DetectionPositionPair<> detection_position_pair; - Bin uncompressed_bin(0,0,0,bin.tangential_pos_num()); + Bin uncompressed_bin(0, 0, 0, bin.tangential_pos_num()); { float view_efficiency = 0.; + for (uncompressed_bin.view_num() = start_view; uncompressed_bin.view_num() < end_view; ++uncompressed_bin.view_num()) { - for(uncompressed_bin.view_num() = start_view; - uncompressed_bin.view_num() < end_view; - ++uncompressed_bin.view_num() ) { + detail::set_detection_tangential_coords(proj_data_info_cyl_uncompressed_ptr, uncompressed_bin, detection_position_pair); - detail::set_detection_tangential_coords(proj_data_info_cyl_uncompressed_ptr, - uncompressed_bin, detection_position_pair); + float lor_efficiency = 0.; - - - float lor_efficiency= 0.; - /* loop over ring differences that contribute to bin.segment_num() at the current bin.axial_pos_num(). @@ -619,117 +520,89 @@ get_uncalibrated_bin_efficiency(const Bin& bin, const double start_time, const d == (2*min_ring_diff+ring1_plus_ring2)%2 == ring1_plus_ring2%2 */ - for(uncompressed_bin.segment_num() = min_ring_diff + (min_ring_diff+ring1_plus_ring2)%2; - uncompressed_bin.segment_num() <= max_ring_diff; - uncompressed_bin.segment_num()+=2 ) { - - - int geo_plane_num = - detail::set_detection_axial_coords(proj_data_info_cyl_ptr, - ring1_plus_ring2, uncompressed_bin, - detection_position_pair); - if ( geo_plane_num < 0 ) { + for (uncompressed_bin.segment_num() = min_ring_diff + (min_ring_diff + ring1_plus_ring2) % 2; + uncompressed_bin.segment_num() <= max_ring_diff; uncompressed_bin.segment_num() += 2) { + + int geo_plane_num = detail::set_detection_axial_coords(proj_data_info_cyl_ptr, ring1_plus_ring2, uncompressed_bin, + detection_position_pair); + if (geo_plane_num < 0) { // Ring numbers out of range. continue; } - -#ifndef NDEBUG +# ifndef NDEBUG Bin check_bin; check_bin.set_bin_value(bin.get_bin_value()); - assert(proj_data_info_cyl_ptr->get_bin_for_det_pos_pair(check_bin, - detection_position_pair) == - Succeeded::yes); + assert(proj_data_info_cyl_ptr->get_bin_for_det_pos_pair(check_bin, detection_position_pair) == Succeeded::yes); assert(check_bin == bin); -#endif - - const DetectionPosition<>& pos1 = detection_position_pair.pos1(); +# endif + + const DetectionPosition<>& pos1 = detection_position_pair.pos1(); const DetectionPosition<>& pos2 = detection_position_pair.pos2(); - float lor_efficiency_this_pair = 1.F; - if (this->use_detector_efficiencies()) - { - lor_efficiency_this_pair = - efficiency_factors[pos1.axial_coord()][pos1.tangential_coord()] * - efficiency_factors[pos2.axial_coord()][pos2.tangential_coord()]; - } - if (this->use_dead_time()) - { - lor_efficiency_this_pair *= - get_dead_time_efficiency(pos1, start_time, end_time) * - get_dead_time_efficiency(pos2, start_time, end_time); - } - if (this->use_geometric_factors()) - { - lor_efficiency_this_pair *= -#ifdef SAME_AS_PETER + float lor_efficiency_this_pair = 1.F; + if (this->use_detector_efficiencies()) { + lor_efficiency_this_pair = efficiency_factors[pos1.axial_coord()][pos1.tangential_coord()] * + efficiency_factors[pos2.axial_coord()][pos2.tangential_coord()]; + } + if (this->use_dead_time()) { + lor_efficiency_this_pair *= + get_dead_time_efficiency(pos1, start_time, end_time) * get_dead_time_efficiency(pos2, start_time, end_time); + } + if (this->use_geometric_factors()) { + lor_efficiency_this_pair *= +# ifdef SAME_AS_PETER 1.F; -#else // this is 3dbkproj (at the moment) - geometric_factors[geo_plane_num][uncompressed_bin.tangential_pos_num()]; -#endif - } - lor_efficiency += lor_efficiency_this_pair; +# else // this is 3dbkproj (at the moment) + geometric_factors[geo_plane_num][uncompressed_bin.tangential_pos_num()]; +# endif + } + lor_efficiency += lor_efficiency_this_pair; } - if (this->use_crystal_interference_factors()) - { - view_efficiency += lor_efficiency * - crystal_interference_factors[uncompressed_bin.tangential_pos_num()][uncompressed_bin.view_num()%num_transaxial_crystals_per_block] ; - } - else - { - view_efficiency += lor_efficiency; - } - } - - if (this->use_geometric_factors()) - { - /* z==bin.get_axial_pos_num() only when min_axial_pos_num()==0*/ - // for oblique plaanes use the single radial profile from segment 0 - -#ifdef SAME_AS_PETER - const int geo_plane_num = 0; - - total_efficiency += view_efficiency * - geometric_factors[geo_plane_num][uncompressed_bin.tangential_pos_num()] * - geo_Z_corr; -#else - total_efficiency += view_efficiency * geo_Z_corr; -#endif - } - else - { - total_efficiency += view_efficiency; + if (this->use_crystal_interference_factors()) { + view_efficiency += lor_efficiency * + crystal_interference_factors[uncompressed_bin.tangential_pos_num()] + [uncompressed_bin.view_num() % num_transaxial_crystals_per_block]; + } else { + view_efficiency += lor_efficiency; } + } + + if (this->use_geometric_factors()) { + /* z==bin.get_axial_pos_num() only when min_axial_pos_num()==0*/ + // for oblique plaanes use the single radial profile from segment 0 + +# ifdef SAME_AS_PETER + const int geo_plane_num = 0; + + total_efficiency += view_efficiency * geometric_factors[geo_plane_num][uncompressed_bin.tangential_pos_num()] * geo_Z_corr; +# else + total_efficiency += view_efficiency * geo_Z_corr; +# endif + } else { + total_efficiency += view_efficiency; + } } return total_efficiency; } #endif - -float -BinNormalisationFromECAT8::get_dead_time_efficiency (const DetectionPosition<>& det_pos, - const double start_time, - const double end_time) const -{ +float +BinNormalisationFromECAT8::get_dead_time_efficiency(const DetectionPosition<>& det_pos, const double start_time, + const double end_time) const { if (is_null_ptr(singles_rates_ptr)) { return 1; } // Get singles rate per block (rate per singles unit / blocks per singles unit). - const float rate = singles_rates_ptr->get_singles_rate(det_pos, start_time, end_time) / - num_blocks_per_singles_unit; - - return - ( 1.0F + axial_t1_array[ det_pos.axial_coord()/num_axial_blocks_per_singles_unit] * rate + - axial_t2_array[ det_pos.axial_coord()/num_axial_blocks_per_singles_unit] * rate * rate ); - - //* ( 1. + ( trans_t1_array[ det_pos.tangential_coord() % num_transaxial_crystals_per_block ] * rate ) ) ; - -} + const float rate = singles_rates_ptr->get_singles_rate(det_pos, start_time, end_time) / num_blocks_per_singles_unit; + return (1.0F + axial_t1_array[det_pos.axial_coord() / num_axial_blocks_per_singles_unit] * rate + + axial_t2_array[det_pos.axial_coord() / num_axial_blocks_per_singles_unit] * rate * rate); + //* ( 1. + ( trans_t1_array[ det_pos.tangential_coord() % num_transaxial_crystals_per_block ] * rate ) ) ; +} -END_NAMESPACE_ECAT +END_NAMESPACE_ECAT END_NAMESPACE_STIR - diff --git a/src/recon_buildblock/BinNormalisationFromGEHDF5.cxx b/src/recon_buildblock/BinNormalisationFromGEHDF5.cxx index dad62a913e..40b6dc8c54 100644 --- a/src/recon_buildblock/BinNormalisationFromGEHDF5.cxx +++ b/src/recon_buildblock/BinNormalisationFromGEHDF5.cxx @@ -33,7 +33,6 @@ \author Palak Wadhwa */ - #include "stir/recon_buildblock/BinNormalisationFromGEHDF5.h" #include "stir/DetectionPosition.h" #include "stir/DetectionPositionPair.h" @@ -65,121 +64,93 @@ START_NAMESPACE_STIR namespace GE { namespace RDF_HDF5 { - - -const char * const -BinNormalisationFromGEHDF5::registered_name = "From GE HDF5"; - +const char* const BinNormalisationFromGEHDF5::registered_name = "From GE HDF5"; -namespace detail -{ +namespace detail { // // helper functions used in this class. // - -static -int -calc_ring1_plus_ring2(const Bin& bin, - const ProjDataInfoCylindricalNoArcCorr *proj_data_cyl) { +static int +calc_ring1_plus_ring2(const Bin& bin, const ProjDataInfoCylindricalNoArcCorr* proj_data_cyl) { int segment_num = bin.segment_num(); - + const int min_ring_diff = proj_data_cyl->get_min_ring_difference(segment_num); const int max_ring_diff = proj_data_cyl->get_max_ring_difference(segment_num); const int num_rings = proj_data_cyl->get_scanner_ptr()->get_num_rings(); - return( (2 * bin.axial_pos_num() - - (proj_data_cyl->get_min_axial_pos_num(segment_num) + - proj_data_cyl->get_max_axial_pos_num(segment_num)) - ) / (min_ring_diff != max_ring_diff ? 2 : 1) - + num_rings - 1 ); - + return ((2 * bin.axial_pos_num() - + (proj_data_cyl->get_min_axial_pos_num(segment_num) + proj_data_cyl->get_max_axial_pos_num(segment_num))) / + (min_ring_diff != max_ring_diff ? 2 : 1) + + num_rings - 1); } - - -static -void -set_detection_tangential_coords(shared_ptr proj_data_cyl_uncomp, - const Bin& uncomp_bin, +static void +set_detection_tangential_coords(shared_ptr proj_data_cyl_uncomp, const Bin& uncomp_bin, DetectionPositionPair<>& detection_position_pair) { - int det1_num=0; - int det2_num=0; - - proj_data_cyl_uncomp->get_det_num_pair_for_view_tangential_pos_num(det1_num, det2_num, - uncomp_bin.view_num(), + int det1_num = 0; + int det2_num = 0; + + proj_data_cyl_uncomp->get_det_num_pair_for_view_tangential_pos_num(det1_num, det2_num, uncomp_bin.view_num(), uncomp_bin.tangential_pos_num()); detection_position_pair.pos1().tangential_coord() = det1_num; detection_position_pair.pos2().tangential_coord() = det2_num; - } - - // Returns the sum of the two axial coordinates. Or -1 if the ring positions are // out of range. // sets axial_coord of detection_position_pair -static -int -set_detection_axial_coords(const ProjDataInfoCylindricalNoArcCorr *proj_data_info_cyl, - int ring1_plus_ring2, const Bin& uncomp_bin, - DetectionPositionPair<>& detection_position_pair) { - +static int +set_detection_axial_coords(const ProjDataInfoCylindricalNoArcCorr* proj_data_info_cyl, int ring1_plus_ring2, + const Bin& uncomp_bin, DetectionPositionPair<>& detection_position_pair) { + const int num_rings = proj_data_info_cyl->get_scanner_ptr()->get_num_rings(); const int ring_diff = uncomp_bin.segment_num(); - const int ring1 = (ring1_plus_ring2 - ring_diff)/2; - const int ring2 = (ring1_plus_ring2 + ring_diff)/2; - - if (ring1<0 || ring2 < 0 || ring1>=num_rings || ring2 >= num_rings) { - return(-1); + const int ring1 = (ring1_plus_ring2 - ring_diff) / 2; + const int ring2 = (ring1_plus_ring2 + ring_diff) / 2; + + if (ring1 < 0 || ring2 < 0 || ring1 >= num_rings || ring2 >= num_rings) { + return (-1); } - - assert((ring1_plus_ring2 + ring_diff)%2 == 0); - assert((ring1_plus_ring2 - ring_diff)%2 == 0); - + + assert((ring1_plus_ring2 + ring_diff) % 2 == 0); + assert((ring1_plus_ring2 - ring_diff) % 2 == 0); + detection_position_pair.pos1().axial_coord() = ring1; detection_position_pair.pos2().axial_coord() = ring2; - - return(ring1 + ring2); + return (ring1 + ring2); } // Returns a vetor with the segment sequence, as 0, 1, -1, 2, -2,... -static -std::vector // Prefered since C++11 -create_segment_sequence(shared_ptr const& proj_data_info_ptr) -{ +static std::vector // Prefered since C++11 +create_segment_sequence(shared_ptr const& proj_data_info_ptr) { std::vector segment_sequence; - segment_sequence.resize(2*proj_data_info_ptr->get_max_segment_num()+1); + segment_sequence.resize(2 * proj_data_info_ptr->get_max_segment_num() + 1); segment_sequence[0] = 0; // PW Flipped the segments, segment sequence is now as: 0,1,-1 and so on. - for (int segment_num = 1; segment_num<=proj_data_info_ptr->get_max_segment_num(); ++segment_num) - { - segment_sequence[2*segment_num-1] = segment_num; - segment_sequence[2*segment_num] = -segment_num; + for (int segment_num = 1; segment_num <= proj_data_info_ptr->get_max_segment_num(); ++segment_num) { + segment_sequence[2 * segment_num - 1] = segment_num; + segment_sequence[2 * segment_num] = -segment_num; } return segment_sequence; } // Returns the index in the segment sequence for a given segment number (e.g -1 returns 2) -static -unsigned int -find_segment_index_in_sequence(std::vector& segment_sequence, const int segment_num) -{ +static unsigned int +find_segment_index_in_sequence(std::vector& segment_sequence, const int segment_num) { std::vector::const_iterator iter = std::find(segment_sequence.begin(), segment_sequence.end(), segment_num); - assert(iter != segment_sequence.end()); + assert(iter != segment_sequence.end()); return static_cast(iter - segment_sequence.begin()); } -// Creates a vector that has the axial position offset for each segment. -static -std::vector // Prefered since C++11 -create_ax_pos_offset(shared_ptr const& proj_data_info_ptr, std::vector& segment_sequence) -{ +// Creates a vector that has the axial position offset for each segment. +static std::vector // Prefered since C++11 +create_ax_pos_offset(shared_ptr const& proj_data_info_ptr, std::vector& segment_sequence) { std::vector seg_ax_offset; seg_ax_offset.resize(proj_data_info_ptr->get_num_segments()); @@ -187,236 +158,205 @@ create_ax_pos_offset(shared_ptr const& proj_data_info_ptr, std::ve unsigned int previous_value = 0; - for (int i_seg = 1; i_seg < proj_data_info_ptr->get_num_segments(); ++i_seg) - { - const int segment_num = segment_sequence[i_seg-1]; + for (int i_seg = 1; i_seg < proj_data_info_ptr->get_num_segments(); ++i_seg) { + const int segment_num = segment_sequence[i_seg - 1]; - seg_ax_offset[i_seg] = static_cast(proj_data_info_ptr->get_num_axial_poss(segment_num)) + - previous_value; - previous_value = seg_ax_offset[i_seg]; + seg_ax_offset[i_seg] = static_cast(proj_data_info_ptr->get_num_axial_poss(segment_num)) + previous_value; + previous_value = seg_ax_offset[i_seg]; } return seg_ax_offset; } } // end of namespace detail - - - // // Member functions // - - -void -BinNormalisationFromGEHDF5::set_defaults() -{ +void +BinNormalisationFromGEHDF5::set_defaults() { this->normalisation_GEHDF5_filename = ""; - //this->_use_gaps = false; + // this->_use_gaps = false; this->_use_detector_efficiencies = true; this->_use_dead_time = false; this->_use_geometric_factors = true; } -void -BinNormalisationFromGEHDF5:: -initialise_keymap() -{ +void +BinNormalisationFromGEHDF5::initialise_keymap() { this->parser.add_start_key("Bin Normalisation From GE HDF5"); this->parser.add_key("normalisation_filename", &this->normalisation_GEHDF5_filename); - //this->parser.add_parsing_key("singles rates", &this->singles_rates_ptr); - //this->parser.add_key("use_gaps", &this->_use_gaps); + // this->parser.add_parsing_key("singles rates", &this->singles_rates_ptr); + // this->parser.add_key("use_gaps", &this->_use_gaps); this->parser.add_key("use_detector_efficiencies", &this->_use_detector_efficiencies); - //this->parser.add_key("use_dead_time", &this->_use_dead_time); + // this->parser.add_key("use_dead_time", &this->_use_dead_time); this->parser.add_key("use_geometric_factors", &this->_use_geometric_factors); this->parser.add_stop_key("End Bin Normalisation From GE HDF5"); } -bool -BinNormalisationFromGEHDF5:: -post_processing() -{ +bool +BinNormalisationFromGEHDF5::post_processing() { read_norm_data(normalisation_GEHDF5_filename); - this->set_calibration_factor(1); //TODO: read actual factor somewhere + this->set_calibration_factor(1); // TODO: read actual factor somewhere return false; } +BinNormalisationFromGEHDF5::BinNormalisationFromGEHDF5() { set_defaults(); } -BinNormalisationFromGEHDF5:: -BinNormalisationFromGEHDF5() -{ - set_defaults(); -} - -BinNormalisationFromGEHDF5:: -BinNormalisationFromGEHDF5(const string& filename) -{ - read_norm_data(filename); -} +BinNormalisationFromGEHDF5::BinNormalisationFromGEHDF5(const string& filename) { read_norm_data(filename); } Succeeded -BinNormalisationFromGEHDF5:: -set_up(const shared_ptr &exam_info_sptr, const shared_ptr& proj_data_info_ptr_v) -{ +BinNormalisationFromGEHDF5::set_up(const shared_ptr& exam_info_sptr, + const shared_ptr& proj_data_info_ptr_v) { BinNormalisation::set_up(exam_info_sptr, proj_data_info_ptr_v); proj_data_info_ptr = proj_data_info_ptr_v; - proj_data_info_cyl_ptr = - dynamic_cast(proj_data_info_ptr.get()); - if (proj_data_info_cyl_ptr==0) - { + proj_data_info_cyl_ptr = dynamic_cast(proj_data_info_ptr.get()); + if (proj_data_info_cyl_ptr == 0) { warning("BinNormalisationFromGEHDF5 can only be used on non-arccorrected data\n"); return Succeeded::no; } - if (*proj_data_info_ptr->get_scanner_ptr() != *scanner_ptr) - { + if (*proj_data_info_ptr->get_scanner_ptr() != *scanner_ptr) { warning("BinNormalisationFromGEHDF5: scanner object from proj data is different from the one " - "from the normalisation file\n"); + "from the normalisation file\n"); return Succeeded::no; } - mash = scanner_ptr->get_num_detectors_per_ring()/2/proj_data_info_ptr->get_num_views(); + mash = scanner_ptr->get_num_detectors_per_ring() / 2 / proj_data_info_ptr->get_num_views(); return Succeeded::yes; } // Load all data that is needed for corrections -// This function will take a filename, that can be either a GE norm file, or a GE geo file. If you wan to do geo and norm corrections -// then you want the norm file as input, and if you only want geo files, then just the geo file is enough. The function will read from these files and -// fill the atributes with a full sinogram of efficiency factors and geometry factors. +// This function will take a filename, that can be either a GE norm file, or a GE geo file. If you wan to do geo and norm +// corrections then you want the norm file as input, and if you only want geo files, then just the geo file is enough. The +// function will read from these files and fill the atributes with a full sinogram of efficiency factors and geometry factors. void -BinNormalisationFromGEHDF5:: -read_norm_data(const string& filename) -{ +BinNormalisationFromGEHDF5::read_norm_data(const string& filename) { // If we actually do not want any correction, forget loading the data - if(!this->use_detector_efficiencies() && !this->use_geometric_factors()) + if (!this->use_detector_efficiencies() && !this->use_geometric_factors()) return; - // Build the HDF5 wrapper. This opens the file and makes sure its the correct type, plus loads all information about the scanner. + // Build the HDF5 wrapper. This opens the file and makes sure its the correct type, plus loads all information about the + // scanner. m_input_hdf5_sptr.reset(new GEHDF5Wrapper(filename)); - // We need the norm file to correct for geometry and efficiecies (the geometric correcction is contained inside the norm file too!) - // But if we are not correcting for efficiencies, then we dont require the file to be a norm file, it can be geo. - if(this->use_detector_efficiencies() && !m_input_hdf5_sptr->is_norm_file()) + // We need the norm file to correct for geometry and efficiecies (the geometric correcction is contained inside the norm file + // too!) But if we are not correcting for efficiencies, then we dont require the file to be a norm file, it can be geo. + if (this->use_detector_efficiencies() && !m_input_hdf5_sptr->is_norm_file()) error("Norm file required, another one given (possibly geo file). Aborting"); this->scanner_ptr = m_input_hdf5_sptr->get_scanner_sptr(); - // Generate a Projection data Info from the uncompressed scan, - proj_data_info_cyl_uncompressed_ptr.reset( - dynamic_cast( + // Generate a Projection data Info from the uncompressed scan, + proj_data_info_cyl_uncompressed_ptr.reset(dynamic_cast( ProjDataInfo::ProjDataInfoCTI(scanner_ptr, - /*span=*/1, - /*max_delta*/scanner_ptr->get_num_rings()-1, - /*num_views,=*/scanner_ptr->get_num_detectors_per_ring()/2, - /*num_tangential_poss=*/scanner_ptr->get_max_num_non_arccorrected_bins(), - /*arc_corrected =*/false) ) ); + /*span=*/1, + /*max_delta*/ scanner_ptr->get_num_rings() - 1, + /*num_views,=*/scanner_ptr->get_num_detectors_per_ring() / 2, + /*num_tangential_poss=*/scanner_ptr->get_max_num_non_arccorrected_bins(), + /*arc_corrected =*/false))); // // Read efficiency data from file // - if(this->use_detector_efficiencies()) - { + if (this->use_detector_efficiencies()) { // Allocate efficiency factor data from an "uncompressed scanner" (i.e. span = 1, all bins are physical bins in the scanner). efficiency_factors = - Array<2,float>(IndexRange2D(0,scanner_ptr->get_num_rings()-1, - 0, scanner_ptr->get_num_detectors_per_ring()-1)); - // Initialize the data reading. This internally checks the file and loads required variables fo further reading. + Array<2, float>(IndexRange2D(0, scanner_ptr->get_num_rings() - 1, 0, scanner_ptr->get_num_detectors_per_ring() - 1)); + // Initialize the data reading. This internally checks the file and loads required variables fo further reading. m_input_hdf5_sptr->initialise_efficiency_factors(); // Do the reading using a buffer. - unsigned int total_size = (scanner_ptr->get_num_rings()-1)*(scanner_ptr->get_num_detectors_per_ring()-1); - stir::Array<1, float> buffer(0, total_size-1); + unsigned int total_size = (scanner_ptr->get_num_rings() - 1) * (scanner_ptr->get_num_detectors_per_ring() - 1); + stir::Array<1, float> buffer(0, total_size - 1); m_input_hdf5_sptr->read_efficiency_factors(buffer); - // Aparently GE stores the normalization factor and not the "efficiency factor", so we just need to invert it. - // Lambda function, this just applies 1/buffer and stores it in efficiency_factors - std::transform(buffer.begin(), buffer.end(),efficiency_factors.begin_all(), [](const float f) { return 1/f;} ); + // Aparently GE stores the normalization factor and not the "efficiency factor", so we just need to invert it. + // Lambda function, this just applies 1/buffer and stores it in efficiency_factors + std::transform(buffer.begin(), buffer.end(), efficiency_factors.begin_all(), [](const float f) { return 1 / f; }); } // // Read geo data from file // - if(this->use_geometric_factors()) - { - // Construct a proper ProjDataInfo to initialize geometry factors array and use it to know the boudns of the iteratios to load it. - shared_ptr projInfo = ProjDataInfo::construct_proj_data_info(scanner_ptr, - /*span*/ 2, - /* max_delta*/ scanner_ptr->get_num_rings()-1, - /* num_views */ scanner_ptr->get_num_detectors_per_ring()/2, - /* num_tangential_poss */ scanner_ptr->get_max_num_non_arccorrected_bins(), - /* arc_corrected */ false - ); - geo_eff_factors_sptr.reset(new ProjDataInMemory(m_input_hdf5_sptr->get_exam_info_sptr(), - projInfo, - true)); // Initialize with zeroes (always true internally...) - - // TODO: remove all these loops and "duplication", and load the entire geometric factors file. Then modify the function get_geometric_factors() - // so that when accessed, re-indexes the bin number to the correct geometric factor. - // Doing this would save lots of RAM, as there are lots of symetries that are exploited in the geo file, but we are here undoing all that and duplicating data. - + if (this->use_geometric_factors()) { + // Construct a proper ProjDataInfo to initialize geometry factors array and use it to know the boudns of the iteratios to load + // it. + shared_ptr projInfo = + ProjDataInfo::construct_proj_data_info(scanner_ptr, + /*span*/ 2, + /* max_delta*/ scanner_ptr->get_num_rings() - 1, + /* num_views */ scanner_ptr->get_num_detectors_per_ring() / 2, + /* num_tangential_poss */ scanner_ptr->get_max_num_non_arccorrected_bins(), + /* arc_corrected */ false); + geo_eff_factors_sptr.reset(new ProjDataInMemory(m_input_hdf5_sptr->get_exam_info_sptr(), projInfo, + true)); // Initialize with zeroes (always true internally...) + + // TODO: remove all these loops and "duplication", and load the entire geometric factors file. Then modify the function + // get_geometric_factors() so that when accessed, re-indexes the bin number to the correct geometric factor. Doing this would + // save lots of RAM, as there are lots of symetries that are exploited in the geo file, but we are here undoing all that and + // duplicating data. + // These arrays will help us index the data to read. Just auxiliary variables. - std::vector segment_sequence = detail::create_segment_sequence(projInfo); - std::vector segment_axial_position_offset = detail::create_ax_pos_offset (projInfo, segment_sequence); - - int num_crystals_per_bucket=scanner_ptr->get_num_transaxial_crystals_per_bucket(); - // Geometric factors are related to geometry (ovbiously). This means that as the scanner has several geometric symetries itself, there is no need to - // store all of them in a big file. This is what GE does in RDF9 files. - // The following loops undo that. They go selecting different data pieces in the initialise_geo_factors() and reading different parts of - // it in read_geo_factors(), all to create a complete sinogram with all the geo factors loaded. - for (int i_seg = projInfo->get_min_segment_num(); i_seg <= projInfo->get_max_segment_num(); ++i_seg) - { - for(int i_view = 0; i_view < scanner_ptr->get_max_num_views(); ++i_view) - { - // Auxiliary single viewgram as a buffer - Viewgram viewgram = projInfo->get_empty_viewgram(projInfo->get_num_views()-1-i_view, i_seg); - // AB TODO This allocates the memory. I wish I knew how to do this without continous reallocation (by reusing) - viewgram.fill(0.0); - switch (m_input_hdf5_sptr->get_geo_dims()) - { - case 3: - { - m_input_hdf5_sptr->initialise_geo_factors_data(modulo(i_view,num_crystals_per_bucket)+1); - - // Define which chunk of the data we are reading from. - std::array offset = {segment_axial_position_offset[detail::find_segment_index_in_sequence(segment_sequence,i_seg)], 0}; - std::array count = {static_cast(projInfo->get_num_axial_poss(i_seg)), - static_cast(projInfo->get_num_tangential_poss())}; - // Initialize buffer to store temp variables - stir::Array<1, unsigned int> buffer(0, count[0]*count[1]-1); - // read geo chunk - m_input_hdf5_sptr->read_geometric_factors(buffer, offset, count); - // copy data back - // AB TODO: Hardcoded magic number, remove somehow (when magic is discovered) - std::transform(buffer.begin(), buffer.end(),viewgram.begin_all(), [](const float f) { return 1/(f*2.2110049e-4);} ); - break; - } - case 2: - { - m_input_hdf5_sptr->initialise_geo_factors_data(1); - - std::array offset = {static_cast(modulo(i_view,num_crystals_per_bucket)), 0}; - std::array count = {1, static_cast(projInfo->get_num_tangential_poss())}; - // Initialize buffer to store temp variables - stir::Array<1, unsigned int> buffer(0, count[1]-1); - // read geo chunk - m_input_hdf5_sptr->read_geometric_factors(buffer, offset, count); - std::vector repeat_buffer; - repeat_buffer.reserve(projInfo->get_num_axial_poss(i_seg)*count[1]-1); - // repeat the values - for (unsigned int i=0; iget_num_axial_poss(i_seg);i++) - repeat_buffer.insert(repeat_buffer.end(),buffer.begin(),buffer.end()); - // copy data back - // AB TODO: Hardcoded magic number, remove somehow (when magic is discovered) - std::transform(repeat_buffer.begin(), repeat_buffer.end(),viewgram.begin_all(), [](const float f) { return 1/(f*2.2110049e-4);} ); - break; - } - default: - error("BinNormalisationFromGEHDF5: Unexpected geometry type"); - } - - geo_eff_factors_sptr->set_viewgram(viewgram); - - }// end view for - }// end segment for -#if 0 // Use this to store loaded geo result in an interfile format. Useful for debugging purposes. + std::vector segment_sequence = detail::create_segment_sequence(projInfo); + std::vector segment_axial_position_offset = detail::create_ax_pos_offset(projInfo, segment_sequence); + + int num_crystals_per_bucket = scanner_ptr->get_num_transaxial_crystals_per_bucket(); + // Geometric factors are related to geometry (ovbiously). This means that as the scanner has several geometric symetries + // itself, there is no need to store all of them in a big file. This is what GE does in RDF9 files. The following loops undo + // that. They go selecting different data pieces in the initialise_geo_factors() and reading different parts of it in + // read_geo_factors(), all to create a complete sinogram with all the geo factors loaded. + for (int i_seg = projInfo->get_min_segment_num(); i_seg <= projInfo->get_max_segment_num(); ++i_seg) { + for (int i_view = 0; i_view < scanner_ptr->get_max_num_views(); ++i_view) { + // Auxiliary single viewgram as a buffer + Viewgram viewgram = projInfo->get_empty_viewgram(projInfo->get_num_views() - 1 - i_view, i_seg); + // AB TODO This allocates the memory. I wish I knew how to do this without continous reallocation (by reusing) + viewgram.fill(0.0); + switch (m_input_hdf5_sptr->get_geo_dims()) { + case 3: { + m_input_hdf5_sptr->initialise_geo_factors_data(modulo(i_view, num_crystals_per_bucket) + 1); + + // Define which chunk of the data we are reading from. + std::array offset = { + segment_axial_position_offset[detail::find_segment_index_in_sequence(segment_sequence, i_seg)], 0}; + std::array count = {static_cast(projInfo->get_num_axial_poss(i_seg)), + static_cast(projInfo->get_num_tangential_poss())}; + // Initialize buffer to store temp variables + stir::Array<1, unsigned int> buffer(0, count[0] * count[1] - 1); + // read geo chunk + m_input_hdf5_sptr->read_geometric_factors(buffer, offset, count); + // copy data back + // AB TODO: Hardcoded magic number, remove somehow (when magic is discovered) + std::transform(buffer.begin(), buffer.end(), viewgram.begin_all(), + [](const float f) { return 1 / (f * 2.2110049e-4); }); + break; + } + case 2: { + m_input_hdf5_sptr->initialise_geo_factors_data(1); + + std::array offset = {static_cast(modulo(i_view, num_crystals_per_bucket)), 0}; + std::array count = {1, static_cast(projInfo->get_num_tangential_poss())}; + // Initialize buffer to store temp variables + stir::Array<1, unsigned int> buffer(0, count[1] - 1); + // read geo chunk + m_input_hdf5_sptr->read_geometric_factors(buffer, offset, count); + std::vector repeat_buffer; + repeat_buffer.reserve(projInfo->get_num_axial_poss(i_seg) * count[1] - 1); + // repeat the values + for (unsigned int i = 0; i < projInfo->get_num_axial_poss(i_seg); i++) + repeat_buffer.insert(repeat_buffer.end(), buffer.begin(), buffer.end()); + // copy data back + // AB TODO: Hardcoded magic number, remove somehow (when magic is discovered) + std::transform(repeat_buffer.begin(), repeat_buffer.end(), viewgram.begin_all(), + [](const float f) { return 1 / (f * 2.2110049e-4); }); + break; + } + default: + error("BinNormalisationFromGEHDF5: Unexpected geometry type"); + } + + geo_eff_factors_sptr->set_viewgram(viewgram); + + } // end view for + } // end segment for +#if 0 // Use this to store loaded geo result in an interfile format. Useful for debugging purposes. shared_ptr output_projdata_ptr; const string filename="geo_debug.hs"; output_projdata_ptr.reset(new ProjDataInterfile(m_input_hdf5_sptr->get_exam_info_sptr(),projInfo,filename)); @@ -426,39 +366,32 @@ read_norm_data(const string& filename) output_projdata_ptr->set_viewgram(geo_eff_factors_sptr->get_viewgram(i_view,i_seg)); } #endif - }// end loading of geo factors + } // end loading of geo factors } -bool -BinNormalisationFromGEHDF5:: -use_detector_efficiencies() const -{ +bool +BinNormalisationFromGEHDF5::use_detector_efficiencies() const { return this->_use_detector_efficiencies; } -bool -BinNormalisationFromGEHDF5:: -use_dead_time() const -{ +bool +BinNormalisationFromGEHDF5::use_dead_time() const { return this->_use_dead_time; } -bool -BinNormalisationFromGEHDF5:: -use_geometric_factors() const -{ +bool +BinNormalisationFromGEHDF5::use_geometric_factors() const { return this->_use_geometric_factors; } -float -BinNormalisationFromGEHDF5:: -get_uncalibrated_bin_efficiency(const Bin& bin, const double start_time, const double end_time) const -{ - float total_efficiency = 0 ; +float +BinNormalisationFromGEHDF5::get_uncalibrated_bin_efficiency(const Bin& bin, const double start_time, + const double end_time) const { + float total_efficiency = 0; /* TODO this loop does some complicated stuff with rings etc - It should be possible to replace this with + It should be possible to replace this with std::vector > det_pos_pairs; proj_data_info_cyl_ptr->get_all_det_pos_pairs_for_bin(det_pos_pairs, bin); @@ -467,79 +400,66 @@ get_uncalibrated_bin_efficiency(const Bin& bin, const double start_time, const d ... } */ - + /* Correct dead time */ - const int start_view = bin.view_num() * mash ; - const int end_view = start_view + mash ; + const int start_view = bin.view_num() * mash; + const int end_view = start_view + mash; const int min_ring_diff = proj_data_info_cyl_ptr->get_min_ring_difference(bin.segment_num()); const int max_ring_diff = proj_data_info_cyl_ptr->get_max_ring_difference(bin.segment_num()); - - /* + /* ring1_plus_ring2 is the same for any ring pair that contributes to this particular bin.segment_num(), bin.axial_pos_num(). We determine it first here. See ProjDataInfoCylindrical for the relevant formulas */ - const int ring1_plus_ring2 = detail::calc_ring1_plus_ring2(bin, proj_data_info_cyl_ptr); - + const int ring1_plus_ring2 = detail::calc_ring1_plus_ring2(bin, proj_data_info_cyl_ptr); + DetectionPositionPair<> detection_position_pair; - Bin uncompressed_bin(0,0,0,bin.tangential_pos_num()); + Bin uncompressed_bin(0, 0, 0, bin.tangential_pos_num()); - float view_efficiency = 0.; - for(uncompressed_bin.view_num() = start_view; - uncompressed_bin.view_num() < end_view; - ++uncompressed_bin.view_num() ) - { - - detail::set_detection_tangential_coords(proj_data_info_cyl_uncompressed_ptr, - uncompressed_bin, detection_position_pair); - - float lor_efficiency= 0.; - + for (uncompressed_bin.view_num() = start_view; uncompressed_bin.view_num() < end_view; ++uncompressed_bin.view_num()) { + + detail::set_detection_tangential_coords(proj_data_info_cyl_uncompressed_ptr, uncompressed_bin, detection_position_pair); + + float lor_efficiency = 0.; + /* Loop over ring differences that contribute to bin.segment_num() at the current bin.axial_pos_num(). - The ring_difference increments with 2 as the other ring differences do not give a ring pair with this axial_position. - This is because: ring1_plus_ring2%2 == ring_diff%2 + The ring_difference increments with 2 as the other ring differences do not give a ring pair with this axial_position. + This is because: ring1_plus_ring2%2 == ring_diff%2 (which easily follows by plugging in ring1+ring2 and ring1-ring2). - The starting ring_diff is determined such that the above condition is satisfied. + The starting ring_diff is determined such that the above condition is satisfied. You can check it by noting that the start_ring_diff%2 == (min_ring_diff + (min_ring_diff+ring1_plus_ring2)%2)%2 == (2*min_ring_diff+ring1_plus_ring2)%2 == ring1_plus_ring2%2 */ - for(uncompressed_bin.segment_num() = min_ring_diff + (min_ring_diff+ring1_plus_ring2)%2; - uncompressed_bin.segment_num() <= max_ring_diff; - uncompressed_bin.segment_num()+=2 ) - { - - // Make sure we are within the range. Just some error checking. - int geo_plane_num = detail::set_detection_axial_coords(proj_data_info_cyl_ptr, - ring1_plus_ring2, uncompressed_bin, - detection_position_pair); - if ( geo_plane_num < 0 ) - { + for (uncompressed_bin.segment_num() = min_ring_diff + (min_ring_diff + ring1_plus_ring2) % 2; + uncompressed_bin.segment_num() <= max_ring_diff; uncompressed_bin.segment_num() += 2) { + + // Make sure we are within the range. Just some error checking. + int geo_plane_num = + detail::set_detection_axial_coords(proj_data_info_cyl_ptr, ring1_plus_ring2, uncompressed_bin, detection_position_pair); + if (geo_plane_num < 0) { // Ring numbers out of range. continue; } - - // Here is where the normalization is applied. Apply each of them if required. + + // Here is where the normalization is applied. Apply each of them if required. float lor_efficiency_this_pair = 1.F; - if (this->use_detector_efficiencies()) - { + if (this->use_detector_efficiencies()) { lor_efficiency_this_pair *= get_efficiency_factors(detection_position_pair); } - if (this->use_dead_time()) - { + if (this->use_dead_time()) { lor_efficiency_this_pair *= get_dead_time_efficiency(detection_position_pair, start_time, end_time); } - if (this->use_geometric_factors()) - { + if (this->use_geometric_factors()) { lor_efficiency_this_pair *= get_geometric_efficiency_factors(detection_position_pair); } lor_efficiency += lor_efficiency_this_pair; - }//endfor + } // endfor view_efficiency += lor_efficiency; total_efficiency += view_efficiency; @@ -548,42 +468,37 @@ get_uncalibrated_bin_efficiency(const Bin& bin, const double start_time, const d return total_efficiency; } -float -BinNormalisationFromGEHDF5::get_dead_time_efficiency (const DetectionPositionPair<>& detection_position_pair, - const double start_time, - const double end_time) const -{ +float +BinNormalisationFromGEHDF5::get_dead_time_efficiency(const DetectionPositionPair<>& detection_position_pair, + const double start_time, const double end_time) const { if (is_null_ptr(singles_rates_ptr)) { return 1; } - return 1; + return 1; } -float -BinNormalisationFromGEHDF5::get_geometric_efficiency_factors (const DetectionPositionPair<>& detection_position_pair) const -{ +float +BinNormalisationFromGEHDF5::get_geometric_efficiency_factors(const DetectionPositionPair<>& detection_position_pair) const { if (is_null_ptr(geo_eff_factors_sptr)) return 1.F; Bin bin; - if (this->proj_data_info_cyl_ptr->get_bin_for_det_pos_pair(bin,detection_position_pair) == Succeeded::no) + if (this->proj_data_info_cyl_ptr->get_bin_for_det_pos_pair(bin, detection_position_pair) == Succeeded::no) error("BinNormalisationFromGEHDF5 internal error"); return this->geo_eff_factors_sptr->get_bin_value(bin); } -float -BinNormalisationFromGEHDF5::get_efficiency_factors (const DetectionPositionPair<>& detection_position_pair) const -{ - const DetectionPosition<>& pos1=detection_position_pair.pos1(); - const DetectionPosition<>& pos2=detection_position_pair.pos2(); +float +BinNormalisationFromGEHDF5::get_efficiency_factors(const DetectionPositionPair<>& detection_position_pair) const { + const DetectionPosition<>& pos1 = detection_position_pair.pos1(); + const DetectionPosition<>& pos2 = detection_position_pair.pos2(); return (this->efficiency_factors[pos1.axial_coord()][pos1.tangential_coord()] * - this->efficiency_factors[pos2.axial_coord()][pos2.tangential_coord()]); + this->efficiency_factors[pos2.axial_coord()][pos2.tangential_coord()]); } -} // namespace -} +} // namespace RDF_HDF5 +} // namespace GE END_NAMESPACE_STIR - diff --git a/src/recon_buildblock/BinNormalisationFromProjData.cxx b/src/recon_buildblock/BinNormalisationFromProjData.cxx index f8b0a9b1f7..62d5103513 100644 --- a/src/recon_buildblock/BinNormalisationFromProjData.cxx +++ b/src/recon_buildblock/BinNormalisationFromProjData.cxx @@ -37,144 +37,107 @@ START_NAMESPACE_STIR -const char * const -BinNormalisationFromProjData::registered_name = "From ProjData"; +const char* const BinNormalisationFromProjData::registered_name = "From ProjData"; - -void -BinNormalisationFromProjData::set_defaults() -{ +void +BinNormalisationFromProjData::set_defaults() { normalisation_projdata_filename = ""; } -void -BinNormalisationFromProjData:: -initialise_keymap() -{ +void +BinNormalisationFromProjData::initialise_keymap() { parser.add_start_key("Bin Normalisation From ProjData"); parser.add_key("normalisation_projdata_filename", &normalisation_projdata_filename); parser.add_stop_key("End Bin Normalisation From ProjData"); } -bool -BinNormalisationFromProjData:: -post_processing() -{ +bool +BinNormalisationFromProjData::post_processing() { norm_proj_data_ptr = ProjData::read_from_file(normalisation_projdata_filename); return false; } -BinNormalisationFromProjData:: -BinNormalisationFromProjData() -{ - set_defaults(); -} +BinNormalisationFromProjData::BinNormalisationFromProjData() { set_defaults(); } -BinNormalisationFromProjData:: -BinNormalisationFromProjData(const std::string& filename) - : norm_proj_data_ptr(ProjData::read_from_file(filename)) - {} +BinNormalisationFromProjData::BinNormalisationFromProjData(const std::string& filename) + : norm_proj_data_ptr(ProjData::read_from_file(filename)) {} -BinNormalisationFromProjData:: -BinNormalisationFromProjData(const shared_ptr& norm_proj_data_ptr) - : norm_proj_data_ptr(norm_proj_data_ptr) - {} +BinNormalisationFromProjData::BinNormalisationFromProjData(const shared_ptr& norm_proj_data_ptr) + : norm_proj_data_ptr(norm_proj_data_ptr) {} -Succeeded -BinNormalisationFromProjData:: -set_up(const shared_ptr& exam_info_sptr, const shared_ptr& proj_data_info_ptr) -{ +Succeeded +BinNormalisationFromProjData::set_up(const shared_ptr& exam_info_sptr, + const shared_ptr& proj_data_info_ptr) { BinNormalisation::set_up(exam_info_sptr, proj_data_info_ptr); if (*(norm_proj_data_ptr->get_proj_data_info_sptr()) == *proj_data_info_ptr) return Succeeded::yes; - else - { + else { const ProjDataInfo& norm_proj = *(norm_proj_data_ptr->get_proj_data_info_sptr()); const ProjDataInfo& proj = *proj_data_info_ptr; - bool ok = - (norm_proj >= proj) && - (norm_proj.get_min_tangential_pos_num() ==proj.get_min_tangential_pos_num())&& - (norm_proj.get_max_tangential_pos_num() ==proj.get_max_tangential_pos_num()); + bool ok = (norm_proj >= proj) && (norm_proj.get_min_tangential_pos_num() == proj.get_min_tangential_pos_num()) && + (norm_proj.get_max_tangential_pos_num() == proj.get_max_tangential_pos_num()); if (ok) return Succeeded::yes; - else - { - warning(boost::format("BinNormalisationFromProjData: incompatible projection data:\nNorm projdata info:\n%s\nEmission projdata info:\n%s\n--- (end of incompatible projection data info)---\n") - % norm_proj.parameter_info() - % proj.parameter_info()); - return Succeeded::no; - } + else { + warning(boost::format("BinNormalisationFromProjData: incompatible projection data:\nNorm projdata info:\n%s\nEmission " + "projdata info:\n%s\n--- (end of incompatible projection data info)---\n") % + norm_proj.parameter_info() % proj.parameter_info()); + return Succeeded::no; + } } } -bool -BinNormalisationFromProjData:: -is_trivial() const -{ - for (int tof_pos = this->norm_proj_data_ptr->get_min_tof_pos_num(); - tof_pos <= this->norm_proj_data_ptr->get_max_tof_pos_num(); - ++tof_pos) - { - // check if all data is 1 (up to a tolerance of 1e-4) - for (int segment_num = this->norm_proj_data_ptr->get_min_segment_num(); - segment_num <= this->norm_proj_data_ptr->get_max_segment_num(); - ++segment_num) - { - for (int view_num = this->norm_proj_data_ptr->get_min_view_num(); - view_num <= this->norm_proj_data_ptr->get_max_view_num(); - ++view_num) - { - const Viewgram viewgram = - this->norm_proj_data_ptr->get_viewgram(view_num, segment_num, tof_pos); - if (fabs(viewgram.find_min()-1)>.0001 || fabs(viewgram.find_max()-1)>.0001) - return false; // return from function as we know not all data is 1 - } - } +bool +BinNormalisationFromProjData::is_trivial() const { + for (int tof_pos = this->norm_proj_data_ptr->get_min_tof_pos_num(); tof_pos <= this->norm_proj_data_ptr->get_max_tof_pos_num(); + ++tof_pos) { + // check if all data is 1 (up to a tolerance of 1e-4) + for (int segment_num = this->norm_proj_data_ptr->get_min_segment_num(); + segment_num <= this->norm_proj_data_ptr->get_max_segment_num(); ++segment_num) { + for (int view_num = this->norm_proj_data_ptr->get_min_view_num(); view_num <= this->norm_proj_data_ptr->get_max_view_num(); + ++view_num) { + const Viewgram viewgram = this->norm_proj_data_ptr->get_viewgram(view_num, segment_num, tof_pos); + if (fabs(viewgram.find_min() - 1) > .0001 || fabs(viewgram.find_max() - 1) > .0001) + return false; // return from function as we know not all data is 1 + } } + } // if we get here. they were all 1 return true; } -void -BinNormalisationFromProjData::apply(RelatedViewgrams& viewgrams,const double start_time, const double end_time) const - { - this->check(*viewgrams.get_proj_data_info_sptr()); - const ViewSegmentNumbers vs_num=viewgrams.get_basic_view_segment_num(); - const int timing_pos_num = norm_proj_data_ptr->get_proj_data_info_sptr()->is_tof_data() ? viewgrams.get_basic_timing_pos_num() : 0; - shared_ptr symmetries_sptr(viewgrams.get_symmetries_ptr()->clone()); - viewgrams *= - norm_proj_data_ptr->get_related_viewgrams(vs_num,symmetries_sptr, false,timing_pos_num); - } - -void -BinNormalisationFromProjData:: -undo(RelatedViewgrams& viewgrams,const double start_time, const double end_time) const - { - this->check(*viewgrams.get_proj_data_info_sptr()); - const ViewSegmentNumbers vs_num=viewgrams.get_basic_view_segment_num(); - const int timing_pos_num = norm_proj_data_ptr->get_proj_data_info_sptr()->is_tof_data() ? viewgrams.get_basic_timing_pos_num() : 0; - shared_ptr symmetries_sptr(viewgrams.get_symmetries_ptr()->clone()); - viewgrams /= - norm_proj_data_ptr->get_related_viewgrams(vs_num,symmetries_sptr, false, timing_pos_num); +void +BinNormalisationFromProjData::apply(RelatedViewgrams& viewgrams, const double start_time, const double end_time) const { + this->check(*viewgrams.get_proj_data_info_sptr()); + const ViewSegmentNumbers vs_num = viewgrams.get_basic_view_segment_num(); + const int timing_pos_num = + norm_proj_data_ptr->get_proj_data_info_sptr()->is_tof_data() ? viewgrams.get_basic_timing_pos_num() : 0; + shared_ptr symmetries_sptr(viewgrams.get_symmetries_ptr()->clone()); + viewgrams *= norm_proj_data_ptr->get_related_viewgrams(vs_num, symmetries_sptr, false, timing_pos_num); +} - } +void +BinNormalisationFromProjData::undo(RelatedViewgrams& viewgrams, const double start_time, const double end_time) const { + this->check(*viewgrams.get_proj_data_info_sptr()); + const ViewSegmentNumbers vs_num = viewgrams.get_basic_view_segment_num(); + const int timing_pos_num = + norm_proj_data_ptr->get_proj_data_info_sptr()->is_tof_data() ? viewgrams.get_basic_timing_pos_num() : 0; + shared_ptr symmetries_sptr(viewgrams.get_symmetries_ptr()->clone()); + viewgrams /= norm_proj_data_ptr->get_related_viewgrams(vs_num, symmetries_sptr, false, timing_pos_num); +} -float -BinNormalisationFromProjData::get_bin_efficiency(const Bin& bin,const double start_time, const double end_time) const -{ - //TODO +float +BinNormalisationFromProjData::get_bin_efficiency(const Bin& bin, const double start_time, const double end_time) const { + // TODO error("BinNormalisationFromProjData::get_bin_efficiency is not implemented"); return 1; - } shared_ptr -BinNormalisationFromProjData::get_norm_proj_data_sptr() const -{ +BinNormalisationFromProjData::get_norm_proj_data_sptr() const { return this->norm_proj_data_ptr; } - -END_NAMESPACE_STIR +END_NAMESPACE_STIR diff --git a/src/recon_buildblock/BinNormalisationSPECT.cxx b/src/recon_buildblock/BinNormalisationSPECT.cxx index 2bf0c31d2b..b45f071119 100644 --- a/src/recon_buildblock/BinNormalisationSPECT.cxx +++ b/src/recon_buildblock/BinNormalisationSPECT.cxx @@ -51,41 +51,33 @@ using std::fstream; START_NAMESPACE_STIR - -const char * const -BinNormalisationSPECT::registered_name = "SPECT"; +const char* const BinNormalisationSPECT::registered_name = "SPECT"; // // helper functions used in this class. // - - -void -BinNormalisationSPECT::set_defaults() -{ +void +BinNormalisationSPECT::set_defaults() { this->uniformity_filename = ""; this->_use_detector_efficiencies = false; this->_use_dead_time = false; this->_use_uniformity_factors = false; this->num_detector_heads = 3; - this->half_life = 6*60*60; //seconds - this->resampled=0; - this->measured_calibration_factor=-1.F; - + this->half_life = 6 * 60 * 60; // seconds + this->resampled = 0; + this->measured_calibration_factor = -1.F; } -void -BinNormalisationSPECT:: -initialise_keymap() -{ +void +BinNormalisationSPECT::initialise_keymap() { this->parser.add_start_key("Bin Normalisation SPECT"); this->parser.add_key("uniformity_filename", &this->uniformity_filename); this->parser.add_key("use_detector_efficiencies", &this->_use_detector_efficiencies); this->parser.add_key("use_uniformity_factors", &this->_use_uniformity_factors); this->parser.add_key("folder_prefix", &this->folder_prefix); this->parser.add_key("rel_angle", &this->rel_angle); - this->parser.add_key("half_life", &this->half_life); //TODO read this from the database according to isotope name + this->parser.add_key("half_life", &this->half_life); // TODO read this from the database according to isotope name this->parser.add_key("view_time_interval", &this->view_time_interval); this->parser.add_key("num detector heads", &this->num_detector_heads); this->parser.add_key("projdata filename", &this->projdata_filename); @@ -95,306 +87,259 @@ initialise_keymap() this->parser.add_stop_key("End Bin Normalisation SPECT"); } -bool -BinNormalisationSPECT:: -post_processing() -{ - if(use_uniformity_factors()){ - uniformity.resize(IndexRange3D(0,2,0,1023,0,1023)); - read_uniformity_table(uniformity);} - - norm_proj_data_info_ptr=ProjData::read_from_file(projdata_filename); - max_tang=norm_proj_data_info_ptr->get_max_tangential_pos_num(); - -// allow to set your own calibration factor - if(measured_calibration_factor>0) - set_calibration_factor(measured_calibration_factor); - else - set_calibration_factor(get_exam_info_sptr()->get_calibration_factor()); - -// read_norm_data(normalisation_spect_filename); - return false; -} +bool +BinNormalisationSPECT::post_processing() { + if (use_uniformity_factors()) { + uniformity.resize(IndexRange3D(0, 2, 0, 1023, 0, 1023)); + read_uniformity_table(uniformity); + } + + norm_proj_data_info_ptr = ProjData::read_from_file(projdata_filename); + max_tang = norm_proj_data_info_ptr->get_max_tangential_pos_num(); + // allow to set your own calibration factor + if (measured_calibration_factor > 0) + set_calibration_factor(measured_calibration_factor); + else + set_calibration_factor(get_exam_info_sptr()->get_calibration_factor()); -BinNormalisationSPECT:: -BinNormalisationSPECT() -{ - set_defaults(); + // read_norm_data(normalisation_spect_filename); + return false; } +BinNormalisationSPECT::BinNormalisationSPECT() { set_defaults(); } + Succeeded -BinNormalisationSPECT:: -set_up(const shared_ptr &exam_info_sptr, const shared_ptr& proj_data_info_ptr_v) -{ +BinNormalisationSPECT::set_up(const shared_ptr& exam_info_sptr, + const shared_ptr& proj_data_info_ptr_v) { return BinNormalisation::set_up(exam_info_sptr, proj_data_info_ptr_v); } -BinNormalisationSPECT:: -BinNormalisationSPECT(const std::string& filename) -{ - read_norm_data(filename); -} +BinNormalisationSPECT::BinNormalisationSPECT(const std::string& filename) { read_norm_data(filename); } void -BinNormalisationSPECT:: -read_norm_data(const std::string& filename) -{// to think about this - } +BinNormalisationSPECT::read_norm_data(const std::string& filename) { // to think about this +} -float BinNormalisationSPECT::get_uncalibrated_bin_efficiency(const Bin& bin,const double start_time, const double end_time) const { - int zoom=1024/(2*(max_tang+1)); - double normalisation=1; +float +BinNormalisationSPECT::get_uncalibrated_bin_efficiency(const Bin& bin, const double start_time, const double end_time) const { + int zoom = 1024 / (2 * (max_tang + 1)); + double normalisation = 1; - if(zoom!=1 && !resampled && use_uniformity_factors()){ + if (zoom != 1 && !resampled && use_uniformity_factors()) { - resample_uniformity(//down_sampled_uniformity, - uniformity, - max_tang, - zoom); - } + resample_uniformity( // down_sampled_uniformity, + uniformity, max_tang, zoom); + } - if(bin.view_num()==0) + if (bin.view_num() == 0) set_num_views(norm_proj_data_info_ptr->get_num_views()); - int head_num=(int)bin.view_num()/(num_views/num_detector_heads); - double rel_time; - rel_time=(this->view_time_interval)* - (bin.view_num()+1-head_num* - (num_views/num_detector_heads)); - /*#################################################################################################### - *#################################### uniformity factors #########################################*/ - - if (use_uniformity_factors()){ - if(uniformity_filename=="") - error("You need to define the uniformity filename and the folder prefix"); - if(zoom!=1) - normalisation= - normalisation*down_sampled_uniformity[head_num][bin.axial_pos_num()][bin.tangential_pos_num()+max_tang+1]; - else{ - normalisation= - normalisation*uniformity[head_num][bin.axial_pos_num()][bin.tangential_pos_num()+max_tang+1];} - } - /*#################################################################################################### - *#################################### decay factors #########################################*/ - - if (use_decay_correction_factors()){ - normalisation= - normalisation/decay_correction_factor(half_life, rel_time); - } -//std::cout<<"value"<view_time_interval) * (bin.view_num() + 1 - head_num * (num_views / num_detector_heads)); + /*#################################################################################################### + *#################################### uniformity factors #########################################*/ + + if (use_uniformity_factors()) { + if (uniformity_filename == "") + error("You need to define the uniformity filename and the folder prefix"); + if (zoom != 1) + normalisation = + normalisation * down_sampled_uniformity[head_num][bin.axial_pos_num()][bin.tangential_pos_num() + max_tang + 1]; + else { + normalisation = normalisation * uniformity[head_num][bin.axial_pos_num()][bin.tangential_pos_num() + max_tang + 1]; + } + } + /*#################################################################################################### + *#################################### decay factors #########################################*/ + + if (use_decay_correction_factors()) { + normalisation = normalisation / decay_correction_factor(half_life, rel_time); + } + // std::cout<<"value"<& viewgrams,const double start_time, const double end_time) const{ +void +BinNormalisationSPECT::apply(RelatedViewgrams& viewgrams, const double start_time, const double end_time) const { - this->check(*viewgrams.get_proj_data_info_sptr()); - int view_num=viewgrams.get_basic_view_num(); - int max_tang=viewgrams.get_max_tangential_pos_num(); - int zoom=1024/(2*(max_tang+1)); - double normalisation=1; + this->check(*viewgrams.get_proj_data_info_sptr()); + int view_num = viewgrams.get_basic_view_num(); + int max_tang = viewgrams.get_max_tangential_pos_num(); + int zoom = 1024 / (2 * (max_tang + 1)); + double normalisation = 1; - if(zoom!=1 && !resampled && use_uniformity_factors()){ + if (zoom != 1 && !resampled && use_uniformity_factors()) { - resample_uniformity(//down_sampled_uniformity, - uniformity, - max_tang, - zoom); - } + resample_uniformity( // down_sampled_uniformity, + uniformity, max_tang, zoom); + } - if(view_num==0) + if (view_num == 0) set_num_views(viewgrams.get_proj_data_info_sptr()->get_num_views()); - int head_num=(int)view_num/(num_views/num_detector_heads); - - double rel_time; - rel_time=(this->view_time_interval)* - (view_num+1-head_num* - (num_views/num_detector_heads)); - - for (RelatedViewgrams::iterator iter = viewgrams.begin(); iter != viewgrams.end(); ++iter) - { - Bin bin(iter->get_segment_num(),iter->get_view_num(), 0,0); - for (bin.axial_pos_num()= iter->get_min_axial_pos_num(); - bin.axial_pos_num()<=iter->get_max_axial_pos_num(); - ++bin.axial_pos_num()) - for (bin.tangential_pos_num()= iter->get_min_tangential_pos_num(); - bin.tangential_pos_num()<=iter->get_max_tangential_pos_num(); - ++bin.tangential_pos_num()){ - - /*#################################################################################################### - *#################################### uniformity factors #########################################*/ - - if (use_uniformity_factors()){ - if(uniformity_filename=="") - error("You need to define the uniformity filename and the folder prefix"); - if(zoom!=1) - normalisation=normalisation*down_sampled_uniformity[head_num][bin.axial_pos_num()][bin.tangential_pos_num()+max_tang+1]; - else - normalisation=normalisation*uniformity[head_num][bin.axial_pos_num()][bin.tangential_pos_num()+max_tang+1]; - } - /*#################################################################################################### - *#################################### decay factors #########################################*/ - - if (use_decay_correction_factors()){ - normalisation= - normalisation/decay_correction_factor(half_life, rel_time); - } - (*iter)[bin.axial_pos_num()][bin.tangential_pos_num()] /= - (std::max(1.E-20F, get_uncalibrated_bin_efficiency(bin, start_time, end_time))* - normalisation); - normalisation=1; + int head_num = (int)view_num / (num_views / num_detector_heads); + + double rel_time; + rel_time = (this->view_time_interval) * (view_num + 1 - head_num * (num_views / num_detector_heads)); + + for (RelatedViewgrams::iterator iter = viewgrams.begin(); iter != viewgrams.end(); ++iter) { + Bin bin(iter->get_segment_num(), iter->get_view_num(), 0, 0); + for (bin.axial_pos_num() = iter->get_min_axial_pos_num(); bin.axial_pos_num() <= iter->get_max_axial_pos_num(); + ++bin.axial_pos_num()) + for (bin.tangential_pos_num() = iter->get_min_tangential_pos_num(); + bin.tangential_pos_num() <= iter->get_max_tangential_pos_num(); ++bin.tangential_pos_num()) { + + /*#################################################################################################### + *#################################### uniformity factors #########################################*/ + + if (use_uniformity_factors()) { + if (uniformity_filename == "") + error("You need to define the uniformity filename and the folder prefix"); + if (zoom != 1) + normalisation = + normalisation * down_sampled_uniformity[head_num][bin.axial_pos_num()][bin.tangential_pos_num() + max_tang + 1]; + else + normalisation = normalisation * uniformity[head_num][bin.axial_pos_num()][bin.tangential_pos_num() + max_tang + 1]; } - } + /*#################################################################################################### + *#################################### decay factors #########################################*/ + + if (use_decay_correction_factors()) { + normalisation = normalisation / decay_correction_factor(half_life, rel_time); + } + (*iter)[bin.axial_pos_num()][bin.tangential_pos_num()] /= + (std::max(1.E-20F, get_uncalibrated_bin_efficiency(bin, start_time, end_time)) * normalisation); + normalisation = 1; + } + } } -void BinNormalisationSPECT::undo(RelatedViewgrams& viewgrams,const double start_time, const double end_time) const{ +void +BinNormalisationSPECT::undo(RelatedViewgrams& viewgrams, const double start_time, const double end_time) const { - this->check(*viewgrams.get_proj_data_info_sptr()); - int view_num=viewgrams.get_basic_view_num(); - int max_tang=viewgrams.get_max_tangential_pos_num(); - int zoom=1024/(2*(max_tang+1)); - double normalisation=1; + this->check(*viewgrams.get_proj_data_info_sptr()); + int view_num = viewgrams.get_basic_view_num(); + int max_tang = viewgrams.get_max_tangential_pos_num(); + int zoom = 1024 / (2 * (max_tang + 1)); + double normalisation = 1; -if(zoom!=1 && !resampled && use_uniformity_factors()){ + if (zoom != 1 && !resampled && use_uniformity_factors()) { - resample_uniformity(//down_sampled_uniformity, - uniformity, - max_tang, - zoom); -} + resample_uniformity( // down_sampled_uniformity, + uniformity, max_tang, zoom); + } - if(view_num==0) + if (view_num == 0) set_num_views(viewgrams.get_proj_data_info_sptr()->get_num_views()); - int head_num=(int)view_num/(num_views/num_detector_heads); - - double rel_time; - rel_time=(this->view_time_interval)* - (view_num+1-head_num* - (num_views/num_detector_heads)); - - for (RelatedViewgrams::iterator iter = viewgrams.begin(); iter != viewgrams.end(); ++iter) - { - Bin bin(iter->get_segment_num(),iter->get_view_num(), 0,0); - for (bin.axial_pos_num()= iter->get_min_axial_pos_num(); - bin.axial_pos_num()<=iter->get_max_axial_pos_num(); - ++bin.axial_pos_num()) - for (bin.tangential_pos_num()= iter->get_min_tangential_pos_num(); - bin.tangential_pos_num()<=iter->get_max_tangential_pos_num(); - ++bin.tangential_pos_num()){ - -/*#################################################################################################### - *#################################### uniformity factors #########################################*/ - - if (use_uniformity_factors()){ - if(uniformity_filename=="") - error("You need to define the uniformity filename and the folder prefix"); - if(zoom!=1) - normalisation=normalisation*down_sampled_uniformity[head_num][bin.axial_pos_num()][bin.tangential_pos_num()+max_tang+1]; - else - normalisation=normalisation*uniformity[head_num][bin.axial_pos_num()][bin.tangential_pos_num()+max_tang+1]; - } -/*#################################################################################################### - *#################################### decay factors #########################################*/ - - if (use_decay_correction_factors()){ - normalisation= - normalisation/decay_correction_factor(half_life, rel_time); - } - - - (*iter)[bin.axial_pos_num()][bin.tangential_pos_num()]*= - (this->get_uncalibrated_bin_efficiency(bin,start_time, end_time)*normalisation); - normalisation=1; + int head_num = (int)view_num / (num_views / num_detector_heads); + + double rel_time; + rel_time = (this->view_time_interval) * (view_num + 1 - head_num * (num_views / num_detector_heads)); + + for (RelatedViewgrams::iterator iter = viewgrams.begin(); iter != viewgrams.end(); ++iter) { + Bin bin(iter->get_segment_num(), iter->get_view_num(), 0, 0); + for (bin.axial_pos_num() = iter->get_min_axial_pos_num(); bin.axial_pos_num() <= iter->get_max_axial_pos_num(); + ++bin.axial_pos_num()) + for (bin.tangential_pos_num() = iter->get_min_tangential_pos_num(); + bin.tangential_pos_num() <= iter->get_max_tangential_pos_num(); ++bin.tangential_pos_num()) { + + /*#################################################################################################### + *#################################### uniformity factors #########################################*/ + + if (use_uniformity_factors()) { + if (uniformity_filename == "") + error("You need to define the uniformity filename and the folder prefix"); + if (zoom != 1) + normalisation = + normalisation * down_sampled_uniformity[head_num][bin.axial_pos_num()][bin.tangential_pos_num() + max_tang + 1]; + else + normalisation = normalisation * uniformity[head_num][bin.axial_pos_num()][bin.tangential_pos_num() + max_tang + 1]; + } + /*#################################################################################################### + *#################################### decay factors #########################################*/ + if (use_decay_correction_factors()) { + normalisation = normalisation / decay_correction_factor(half_life, rel_time); } - } + + (*iter)[bin.axial_pos_num()][bin.tangential_pos_num()] *= + (this->get_uncalibrated_bin_efficiency(bin, start_time, end_time) * normalisation); + normalisation = 1; + } + } } void -BinNormalisationSPECT:: -read_uniformity_table(Array<3,float>& uniformity) const -{//std::ofstream unif_table("uniformity.dat",std::ios::out); - for(int n=1; n<=num_detector_heads; n++ ){ - - const std::string n_string = boost::lexical_cast(n); - const std::string filename(this->folder_prefix+n_string+"/"+uniformity_filename); - - std::ifstream input(filename.c_str()); - - if (!input) - error("Could not open Uniformity correction table!"); - input.read(const_cast(reinterpret_cast(&map)), sizeof(map)); - input.close(); - for(int j=1;j<=1023;j++) - for(int i=1;i<=1023;i++){ - uniformity[n-1][j][i]=map[j+i*1024]; -// std::cout<<"value"<& uniformity) const { // std::ofstream unif_table("uniformity.dat",std::ios::out); + for (int n = 1; n <= num_detector_heads; n++) { + + const std::string n_string = boost::lexical_cast(n); + const std::string filename(this->folder_prefix + n_string + "/" + uniformity_filename); + + std::ifstream input(filename.c_str()); + + if (!input) + error("Could not open Uniformity correction table!"); + input.read(const_cast(reinterpret_cast(&map)), sizeof(map)); + input.close(); + for (int j = 1; j <= 1023; j++) + for (int i = 1; i <= 1023; i++) { + uniformity[n - 1][j][i] = map[j + i * 1024]; + // std::cout<<"value"<& down_sampled_uniformity, - Array<3,float> uniformity, - const int max_tang, - const int zoom) const -{ -down_sampled_uniformity.resize(IndexRange3D(0, 2, 0, 2*max_tang+1, 0, 2*max_tang+1)); -for(int n=0;n<=2;n++){ - for(int i=0;i<=2*max_tang+1;i++){ - for(int j=0;j<=2*max_tang+1;j++){ - for(int l=0;l<=zoom-1;l++){ - for(int k=0;k<=zoom-1;k++){// maybe resize uniformity - - down_sampled_uniformity[n][i][j]=down_sampled_uniformity[n][i][j] + - uniformity[n][zoom*i+l][zoom*j+k]/square(zoom); -// std::cout<<"uni"<& down_sampled_uniformity, + Array<3, float> uniformity, const int max_tang, const int zoom) const { + down_sampled_uniformity.resize(IndexRange3D(0, 2, 0, 2 * max_tang + 1, 0, 2 * max_tang + 1)); + for (int n = 0; n <= 2; n++) { + for (int i = 0; i <= 2 * max_tang + 1; i++) { + for (int j = 0; j <= 2 * max_tang + 1; j++) { + for (int l = 0; l <= zoom - 1; l++) { + for (int k = 0; k <= zoom - 1; k++) { // maybe resize uniformity + + down_sampled_uniformity[n][i][j] = + down_sampled_uniformity[n][i][j] + uniformity[n][zoom * i + l][zoom * j + k] / square(zoom); + // std::cout<<"uni"<_use_detector_efficiencies; } bool -BinNormalisationSPECT:: -use_decay_correction_factors() const -{ +BinNormalisationSPECT::use_decay_correction_factors() const { return this->_use_decay_correction; } -bool -BinNormalisationSPECT:: -use_dead_time() const -{ +bool +BinNormalisationSPECT::use_dead_time() const { return this->_use_dead_time; } -bool -BinNormalisationSPECT:: -use_uniformity_factors() const -{ +bool +BinNormalisationSPECT::use_uniformity_factors() const { return this->_use_uniformity_factors; } double -BinNormalisationSPECT:: -get_half_life() const -{ +BinNormalisationSPECT::get_half_life() const { return this->half_life; } diff --git a/src/recon_buildblock/BinNormalisationWithCalibration.cxx b/src/recon_buildblock/BinNormalisationWithCalibration.cxx index ad1eaa2c8d..7f2ebae31f 100644 --- a/src/recon_buildblock/BinNormalisationWithCalibration.cxx +++ b/src/recon_buildblock/BinNormalisationWithCalibration.cxx @@ -27,78 +27,60 @@ \author Kris Thielemans */ - #include "stir/recon_buildblock/BinNormalisationWithCalibration.h" START_NAMESPACE_STIR -void -BinNormalisationWithCalibration::set_defaults() -{ +void +BinNormalisationWithCalibration::set_defaults() { base_type::set_defaults(); - + this->calibration_factor = 1; - this->branching_ratio=1; + this->branching_ratio = 1; } -void -BinNormalisationWithCalibration:: -initialise_keymap() -{ - base_type::initialise_keymap();/* - this->parser.add_key("calibration_factor", &this->calibration_factor); - this->parser.add_key("branching_ratio", &this->branching_ratio);*/ +void +BinNormalisationWithCalibration::initialise_keymap() { + base_type::initialise_keymap(); /* + this->parser.add_key("calibration_factor", &this->calibration_factor); + this->parser.add_key("branching_ratio", &this->branching_ratio);*/ } -bool -BinNormalisationWithCalibration:: -post_processing() -{ +bool +BinNormalisationWithCalibration::post_processing() { return base_type::post_processing(); } +BinNormalisationWithCalibration::BinNormalisationWithCalibration() { set_defaults(); } -BinNormalisationWithCalibration:: -BinNormalisationWithCalibration() -{ - set_defaults(); -} - -float -BinNormalisationWithCalibration:: -get_calib_decay_branching_ratio_factor(const Bin&) const{ - return this->calibration_factor* this->branching_ratio; //TODO: multiply by branching factor and decay +float +BinNormalisationWithCalibration::get_calib_decay_branching_ratio_factor(const Bin&) const { + return this->calibration_factor * this->branching_ratio; // TODO: multiply by branching factor and decay } float -BinNormalisationWithCalibration:: -get_calibration_factor() const { - return this->calibration_factor; +BinNormalisationWithCalibration::get_calibration_factor() const { + return this->calibration_factor; } void -BinNormalisationWithCalibration:: -set_calibration_factor(const float calib){ - this->calibration_factor=calib; +BinNormalisationWithCalibration::set_calibration_factor(const float calib) { + this->calibration_factor = calib; } float -BinNormalisationWithCalibration:: -get_branching_ratio() const { - return this->branching_ratio; +BinNormalisationWithCalibration::get_branching_ratio() const { + return this->branching_ratio; } void -BinNormalisationWithCalibration:: -set_branching_ratio(const float br){ - this->branching_ratio=br; +BinNormalisationWithCalibration::set_branching_ratio(const float br) { + this->branching_ratio = br; } void -BinNormalisationWithCalibration:: -set_radionuclide(const std::string& rnuclide){ - this->radionuclide=rnuclide; +BinNormalisationWithCalibration::set_radionuclide(const std::string& rnuclide) { + this->radionuclide = rnuclide; } - END_NAMESPACE_STIR diff --git a/src/recon_buildblock/ChainedBinNormalisation.cxx b/src/recon_buildblock/ChainedBinNormalisation.cxx index 6bb60041e4..12bba0c9ca 100644 --- a/src/recon_buildblock/ChainedBinNormalisation.cxx +++ b/src/recon_buildblock/ChainedBinNormalisation.cxx @@ -25,84 +25,66 @@ \author Kris Thielemans */ - #include "stir/recon_buildblock/ChainedBinNormalisation.h" #include "stir/is_null_ptr.h" #include "stir/Succeeded.h" START_NAMESPACE_STIR -const char * const -ChainedBinNormalisation::registered_name = "Chained"; +const char* const ChainedBinNormalisation::registered_name = "Chained"; -void -ChainedBinNormalisation::set_defaults() -{ +void +ChainedBinNormalisation::set_defaults() { apply_first.reset(); apply_second.reset(); } -void -ChainedBinNormalisation:: -initialise_keymap() -{ +void +ChainedBinNormalisation::initialise_keymap() { parser.add_start_key("Chained Bin Normalisation Parameters"); parser.add_parsing_key("Bin Normalisation to apply first", &apply_first); parser.add_parsing_key("Bin Normalisation to apply second", &apply_second); - parser.add_stop_key("END Chained Bin Normalisation Parameters");} + parser.add_stop_key("END Chained Bin Normalisation Parameters"); +} -bool -ChainedBinNormalisation:: -post_processing() -{ - if ((apply_first->get_calibration_factor()>0.F) && (apply_second->get_calibration_factor()>0.F)) +bool +ChainedBinNormalisation::post_processing() { + if ((apply_first->get_calibration_factor() > 0.F) && (apply_second->get_calibration_factor() > 0.F)) error("ChainedBinNormalisation: both first and second have a calibration factor. The factor would be applied twice"); return false; } +ChainedBinNormalisation::ChainedBinNormalisation() { set_defaults(); } -ChainedBinNormalisation:: -ChainedBinNormalisation() -{ - set_defaults(); -} - -ChainedBinNormalisation:: -ChainedBinNormalisation(shared_ptr const& apply_first_v, - shared_ptr const& apply_second_v) - : apply_first(apply_first_v), - apply_second(apply_second_v) -{ +ChainedBinNormalisation::ChainedBinNormalisation(shared_ptr const& apply_first_v, + shared_ptr const& apply_second_v) + : apply_first(apply_first_v), apply_second(apply_second_v) { post_processing(); } Succeeded -ChainedBinNormalisation:: -set_up(const shared_ptr& exam_info_sptr, const shared_ptr& proj_data_info_ptr) -{ - BinNormalisation::set_up( exam_info_sptr,proj_data_info_ptr); +ChainedBinNormalisation::set_up(const shared_ptr& exam_info_sptr, + const shared_ptr& proj_data_info_ptr) { + BinNormalisation::set_up(exam_info_sptr, proj_data_info_ptr); if (!is_null_ptr(apply_first)) - if (apply_first->set_up(exam_info_sptr,proj_data_info_ptr ) == Succeeded::no) - return Succeeded::no; + if (apply_first->set_up(exam_info_sptr, proj_data_info_ptr) == Succeeded::no) + return Succeeded::no; if (!is_null_ptr(apply_second)) - return apply_second->set_up(exam_info_sptr,proj_data_info_ptr); + return apply_second->set_up(exam_info_sptr, proj_data_info_ptr); else - return Succeeded::yes; + return Succeeded::yes; } - -void -ChainedBinNormalisation::apply(RelatedViewgrams& viewgrams,const double start_time, const double end_time) const -{ +void +ChainedBinNormalisation::apply(RelatedViewgrams& viewgrams, const double start_time, const double end_time) const { if (!is_null_ptr(apply_first)) - apply_first->apply(viewgrams,start_time,end_time); + apply_first->apply(viewgrams, start_time, end_time); if (!is_null_ptr(apply_second)) - apply_second->apply(viewgrams,start_time,end_time); + apply_second->apply(viewgrams, start_time, end_time); } void -ChainedBinNormalisation::apply(ProjData& proj_data) const -{ +ChainedBinNormalisation::apply(ProjData& proj_data) const { if (!is_null_ptr(apply_first)) apply_first->apply(proj_data); if (!is_null_ptr(apply_second)) @@ -110,129 +92,105 @@ ChainedBinNormalisation::apply(ProjData& proj_data) const } void -ChainedBinNormalisation::apply_only_first(RelatedViewgrams& viewgrams,const double start_time, const double end_time) const -{ +ChainedBinNormalisation::apply_only_first(RelatedViewgrams& viewgrams, const double start_time, + const double end_time) const { if (!is_null_ptr(apply_first)) - apply_first->apply(viewgrams,start_time,end_time); + apply_first->apply(viewgrams, start_time, end_time); } void -ChainedBinNormalisation::apply_only_first(ProjData& proj_data,const double start_time, const double end_time) const -{ +ChainedBinNormalisation::apply_only_first(ProjData& proj_data, const double start_time, const double end_time) const { if (!is_null_ptr(apply_first)) apply_first->apply(proj_data); } void -ChainedBinNormalisation::apply_only_second(RelatedViewgrams& viewgrams,const double start_time, const double end_time) const -{ +ChainedBinNormalisation::apply_only_second(RelatedViewgrams& viewgrams, const double start_time, + const double end_time) const { if (!is_null_ptr(apply_second)) - apply_second->apply(viewgrams,start_time,end_time); + apply_second->apply(viewgrams, start_time, end_time); } void -ChainedBinNormalisation::apply_only_second(ProjData& proj_data,const double start_time, const double end_time) const -{ +ChainedBinNormalisation::apply_only_second(ProjData& proj_data, const double start_time, const double end_time) const { if (!is_null_ptr(apply_second)) apply_second->apply(proj_data); } -void -ChainedBinNormalisation:: -undo(RelatedViewgrams& viewgrams,const double start_time, const double end_time) const -{ +void +ChainedBinNormalisation::undo(RelatedViewgrams& viewgrams, const double start_time, const double end_time) const { if (!is_null_ptr(apply_first)) - apply_first->undo(viewgrams,start_time,end_time); + apply_first->undo(viewgrams, start_time, end_time); if (!is_null_ptr(apply_second)) - apply_second->undo(viewgrams,start_time,end_time); + apply_second->undo(viewgrams, start_time, end_time); } void -ChainedBinNormalisation:: -undo(ProjData& proj_data,const double start_time, const double end_time) const -{ +ChainedBinNormalisation::undo(ProjData& proj_data, const double start_time, const double end_time) const { if (!is_null_ptr(apply_first)) - apply_first->undo(proj_data,start_time,end_time); + apply_first->undo(proj_data, start_time, end_time); if (!is_null_ptr(apply_second)) - apply_second->undo(proj_data,start_time,end_time); + apply_second->undo(proj_data, start_time, end_time); } void -ChainedBinNormalisation:: -undo_only_first(RelatedViewgrams& viewgrams,const double start_time, const double end_time) const -{ +ChainedBinNormalisation::undo_only_first(RelatedViewgrams& viewgrams, const double start_time, + const double end_time) const { if (!is_null_ptr(apply_first)) - apply_first->undo(viewgrams,start_time,end_time); + apply_first->undo(viewgrams, start_time, end_time); } void -ChainedBinNormalisation:: -undo_only_first(ProjData& proj_data,const double start_time, const double end_time) const -{ +ChainedBinNormalisation::undo_only_first(ProjData& proj_data, const double start_time, const double end_time) const { if (!is_null_ptr(apply_first)) - apply_first->undo(proj_data,start_time,end_time); + apply_first->undo(proj_data, start_time, end_time); } void -ChainedBinNormalisation:: -undo_only_second(RelatedViewgrams& viewgrams,const double start_time, const double end_time) const -{ +ChainedBinNormalisation::undo_only_second(RelatedViewgrams& viewgrams, const double start_time, + const double end_time) const { if (!is_null_ptr(apply_second)) - apply_second->undo(viewgrams,start_time,end_time); + apply_second->undo(viewgrams, start_time, end_time); } void -ChainedBinNormalisation:: -undo_only_second(ProjData& proj_data,const double start_time, const double end_time) const -{ +ChainedBinNormalisation::undo_only_second(ProjData& proj_data, const double start_time, const double end_time) const { if (!is_null_ptr(apply_second)) - apply_second->undo(proj_data,start_time,end_time); + apply_second->undo(proj_data, start_time, end_time); } float -ChainedBinNormalisation:: get_bin_efficiency(const Bin& bin,const double start_time, const double end_time) const -{ - return - (!is_null_ptr(apply_first) - ? apply_first->get_bin_efficiency(bin,start_time,end_time) - : 1) - * - (!is_null_ptr(apply_second) - ? apply_second->get_bin_efficiency(bin,start_time,end_time) - : 1); -} - +ChainedBinNormalisation::get_bin_efficiency(const Bin& bin, const double start_time, const double end_time) const { + return (!is_null_ptr(apply_first) ? apply_first->get_bin_efficiency(bin, start_time, end_time) : 1) * + (!is_null_ptr(apply_second) ? apply_second->get_bin_efficiency(bin, start_time, end_time) : 1); +} + bool -ChainedBinNormalisation::is_first_trivial() const -{ - if (is_null_ptr(apply_first)) - error("First Normalisation object has not been set."); - return apply_first->is_trivial(); +ChainedBinNormalisation::is_first_trivial() const { + if (is_null_ptr(apply_first)) + error("First Normalisation object has not been set."); + return apply_first->is_trivial(); } bool -ChainedBinNormalisation::is_second_trivial() const -{ - if (is_null_ptr(apply_second)) - error("Second Normalisation object has not been set."); - return apply_second->is_trivial(); +ChainedBinNormalisation::is_second_trivial() const { + if (is_null_ptr(apply_second)) + error("Second Normalisation object has not been set."); + return apply_second->is_trivial(); } shared_ptr -ChainedBinNormalisation::get_first_norm() const -{ - if (is_null_ptr(apply_first)) - error("First Normalisation object has not been set."); - return apply_first; +ChainedBinNormalisation::get_first_norm() const { + if (is_null_ptr(apply_first)) + error("First Normalisation object has not been set."); + return apply_first; } shared_ptr -ChainedBinNormalisation::get_second_norm() const -{ - if (is_null_ptr(apply_second)) - error("Second Normalisation object has not been set."); - return apply_second; +ChainedBinNormalisation::get_second_norm() const { + if (is_null_ptr(apply_second)) + error("Second Normalisation object has not been set."); + return apply_second; } - -END_NAMESPACE_STIR +END_NAMESPACE_STIR diff --git a/src/recon_buildblock/DataSymmetriesForBins.cxx b/src/recon_buildblock/DataSymmetriesForBins.cxx index 50370513b6..ff1391d0d0 100644 --- a/src/recon_buildblock/DataSymmetriesForBins.cxx +++ b/src/recon_buildblock/DataSymmetriesForBins.cxx @@ -39,62 +39,46 @@ using std::vector; START_NAMESPACE_STIR -DataSymmetriesForBins:: -~DataSymmetriesForBins() -{} +DataSymmetriesForBins::~DataSymmetriesForBins() {} -DataSymmetriesForBins:: -DataSymmetriesForBins(const shared_ptr& proj_data_info_ptr) -: proj_data_info_ptr(proj_data_info_ptr) -{} +DataSymmetriesForBins::DataSymmetriesForBins(const shared_ptr& proj_data_info_ptr) + : proj_data_info_ptr(proj_data_info_ptr) {} bool -DataSymmetriesForBins:: -blindly_equals(const root_type * const sym_ptr) const -{ +DataSymmetriesForBins::blindly_equals(const root_type* const sym_ptr) const { if (!base_type::blindly_equals(sym_ptr)) return false; - return - *this->proj_data_info_ptr == - *static_cast(*sym_ptr).proj_data_info_ptr; + return *this->proj_data_info_ptr == *static_cast(*sym_ptr).proj_data_info_ptr; } - /*! default implementation in terms of get_related_bins, will be slow of course */ int -DataSymmetriesForBins::num_related_bins(const Bin& b) const -{ +DataSymmetriesForBins::num_related_bins(const Bin& b) const { vector rel_b; get_related_bins(rel_b, b); return static_cast(rel_b.size()); } /*! default implementation in terms of find_symmetry_operation_from_basic_bin */ -bool DataSymmetriesForBins::find_basic_bin(Bin& b) const -{ - unique_ptr sym_op = - find_symmetry_operation_from_basic_bin(b); +bool +DataSymmetriesForBins::find_basic_bin(Bin& b) const { + unique_ptr sym_op = find_symmetry_operation_from_basic_bin(b); return sym_op->is_trivial(); } - bool -DataSymmetriesForBins:: -is_basic(const Bin& b) const -{ +DataSymmetriesForBins::is_basic(const Bin& b) const { Bin copy = b; return !find_basic_bin(copy); } /*! default implementation in terms of get_related_bins_factorised */ void -DataSymmetriesForBins:: -get_related_bins(vector& rel_b, const Bin& b, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num, - const int min_timing_pos_num, const int max_timing_pos_num) const -{ +DataSymmetriesForBins::get_related_bins(vector& rel_b, const Bin& b, const int min_axial_pos_num, + const int max_axial_pos_num, const int min_tangential_pos_num, + const int max_tangential_pos_num, const int min_timing_pos_num, + const int max_timing_pos_num) const { #ifndef NDEBUG Bin bin_copy = b; assert(!find_basic_bin(bin_copy)); @@ -103,9 +87,8 @@ get_related_bins(vector& rel_b, const Bin& b, vector vs; vector ax_tang_poss; - get_related_bins_factorised(ax_tang_poss, b, - min_axial_pos_num, max_axial_pos_num, - min_tangential_pos_num, max_tangential_pos_num); + get_related_bins_factorised(ax_tang_poss, b, min_axial_pos_num, max_axial_pos_num, min_tangential_pos_num, + max_tangential_pos_num); get_related_view_segment_numbers(vs, ViewSegmentNumbers(b.view_num(), b.segment_num())); @@ -114,42 +97,34 @@ get_related_bins(vector& rel_b, const Bin& b, for ( #ifdef _MSC_VER - // VC bug work-around... - std:: + // VC bug work-around... + std:: #endif - vector::const_iterator view_seg_ptr = vs.begin(); - view_seg_ptr != vs.end(); - ++view_seg_ptr) - { + vector::const_iterator view_seg_ptr = vs.begin(); + view_seg_ptr != vs.end(); ++view_seg_ptr) { for ( #ifdef _MSC_VER - // VC bug work-around... - std:: + // VC bug work-around... + std:: #endif - vector::const_iterator ax_tang_pos_ptr = ax_tang_poss.begin(); - ax_tang_pos_ptr != ax_tang_poss.end(); - ++ax_tang_pos_ptr) - { - for (int k=min_timing_pos_num;k<=max_timing_pos_num;++k) - { - rel_b.push_back(Bin(view_seg_ptr->segment_num(), view_seg_ptr->view_num(), - (*ax_tang_pos_ptr)[1], (*ax_tang_pos_ptr)[2],k)); - } + vector::const_iterator ax_tang_pos_ptr = ax_tang_poss.begin(); + ax_tang_pos_ptr != ax_tang_poss.end(); ++ax_tang_pos_ptr) { + for (int k = min_timing_pos_num; k <= max_timing_pos_num; ++k) { + rel_b.push_back( + Bin(view_seg_ptr->segment_num(), view_seg_ptr->view_num(), (*ax_tang_pos_ptr)[1], (*ax_tang_pos_ptr)[2], k)); + } } } } unique_ptr -DataSymmetriesForBins:: -find_symmetry_operation_from_basic_view_segment_numbers(ViewSegmentNumbers& vs) const -{ - Bin bin(vs.segment_num(), vs.view_num(),0,0); +DataSymmetriesForBins::find_symmetry_operation_from_basic_view_segment_numbers(ViewSegmentNumbers& vs) const { + Bin bin(vs.segment_num(), vs.view_num(), 0, 0); #ifndef NDEBUG Bin bin_copy = bin; #endif - unique_ptr sym_op = - find_symmetry_operation_from_basic_bin(bin); + unique_ptr sym_op = find_symmetry_operation_from_basic_bin(bin); vs.segment_num() = bin.segment_num(); vs.view_num() = bin.view_num(); diff --git a/src/recon_buildblock/DataSymmetriesForBins_PET_CartesianGrid.cxx b/src/recon_buildblock/DataSymmetriesForBins_PET_CartesianGrid.cxx index c3bc24c18a..a6b7c22097 100644 --- a/src/recon_buildblock/DataSymmetriesForBins_PET_CartesianGrid.cxx +++ b/src/recon_buildblock/DataSymmetriesForBins_PET_CartesianGrid.cxx @@ -41,16 +41,15 @@ START_NAMESPACE_STIR //! find correspondence between axial_pos_num and image coordinates /*! z = num_planes_per_axial_pos * axial_pos_num + axial_pos_to_z_offset - compute the offset by matching up the centre of the scanner + compute the offset by matching up the centre of the scanner in the 2 coordinate systems */ -static void -find_relation_between_coordinate_systems(int& num_planes_per_scanner_ring, - VectorWithOffset& num_planes_per_axial_pos, +static void +find_relation_between_coordinate_systems(int& num_planes_per_scanner_ring, VectorWithOffset& num_planes_per_axial_pos, VectorWithOffset& axial_pos_to_z_offset, const ProjDataInfoCylindrical* proj_data_info_cyl_ptr, - const DiscretisedDensityOnCartesianGrid<3,float> * cartesian_grid_info_ptr) - + const DiscretisedDensityOnCartesianGrid<3, float>* cartesian_grid_info_ptr) + { const int min_segment_num = proj_data_info_cyl_ptr->get_min_segment_num(); @@ -59,102 +58,85 @@ find_relation_between_coordinate_systems(int& num_planes_per_scanner_ring, num_planes_per_axial_pos = VectorWithOffset(min_segment_num, max_segment_num); axial_pos_to_z_offset = VectorWithOffset(min_segment_num, max_segment_num); - // TODO and WARNING: get_grid_spacing()[1] is z() + // TODO and WARNING: get_grid_spacing()[1] is z() const float image_plane_spacing = cartesian_grid_info_ptr->get_grid_spacing()[1]; - - { - const float num_planes_per_scanner_ring_float = - proj_data_info_cyl_ptr->get_ring_spacing() / image_plane_spacing; - + + { + const float num_planes_per_scanner_ring_float = proj_data_info_cyl_ptr->get_ring_spacing() / image_plane_spacing; + num_planes_per_scanner_ring = round(num_planes_per_scanner_ring_float); - + if (fabs(num_planes_per_scanner_ring_float - num_planes_per_scanner_ring) > 1.E-2) - error(boost::format("DataSymmetriesForBins_PET_CartesianGrid can currently only support z-grid spacing " - "equal to the ring spacing of the scanner divided by an integer. Sorry. " - "(Image z-spacing is %1% and ring spacing is %2%)") % image_plane_spacing % proj_data_info_cyl_ptr->get_ring_spacing()); + error(boost::format("DataSymmetriesForBins_PET_CartesianGrid can currently only support z-grid spacing " + "equal to the ring spacing of the scanner divided by an integer. Sorry. " + "(Image z-spacing is %1% and ring spacing is %2%)") % + image_plane_spacing % proj_data_info_cyl_ptr->get_ring_spacing()); } - + /* disabled as we support this now if (fabs( cartesian_grid_info_ptr->get_origin().x()) > 1.E-2) error("DataSymmetriesForBins_PET_CartesianGrid can currently only support x-origin = 0 " - "Sorry\n"); + "Sorry\n"); if (fabs( cartesian_grid_info_ptr->get_origin().y()) > 1.E-2) error("DataSymmetriesForBins_PET_CartesianGrid can currently only support y-origin = 0 " - "Sorry\n"); + "Sorry\n"); */ - - for (int segment_num=min_segment_num; segment_num<=max_segment_num; ++segment_num) - { - { - const float - num_planes_per_axial_pos_float = - proj_data_info_cyl_ptr->get_axial_sampling(segment_num)/image_plane_spacing; - + + for (int segment_num = min_segment_num; segment_num <= max_segment_num; ++segment_num) { + { + const float num_planes_per_axial_pos_float = proj_data_info_cyl_ptr->get_axial_sampling(segment_num) / image_plane_spacing; + num_planes_per_axial_pos[segment_num] = round(num_planes_per_axial_pos_float); - + if (fabs(num_planes_per_axial_pos_float - num_planes_per_axial_pos[segment_num]) > 1.E-2) - error(boost::format("DataSymmetriesForBins_PET_CartesianGrid can currently only support z-grid spacing " - "equal to the sinogram spacing of the scanner divided by an integer. Sorry. " - "(Image z-spacing is %1% and axial sinogram spacing is %2% at segment %3%") - % image_plane_spacing % proj_data_info_cyl_ptr->get_axial_sampling(segment_num) % segment_num); - - } - + error(boost::format("DataSymmetriesForBins_PET_CartesianGrid can currently only support z-grid spacing " + "equal to the sinogram spacing of the scanner divided by an integer. Sorry. " + "(Image z-spacing is %1% and axial sinogram spacing is %2% at segment %3%") % + image_plane_spacing % proj_data_info_cyl_ptr->get_axial_sampling(segment_num) % segment_num); + } + const float delta = proj_data_info_cyl_ptr->get_average_ring_difference(segment_num); - + // KT 20/06/2001 take origin.z() into account - axial_pos_to_z_offset[segment_num] = - (cartesian_grid_info_ptr->get_max_index() + cartesian_grid_info_ptr->get_min_index())/2.F - - cartesian_grid_info_ptr->get_origin().z()/image_plane_spacing - - - (num_planes_per_axial_pos[segment_num] - *(proj_data_info_cyl_ptr->get_max_axial_pos_num(segment_num) - + proj_data_info_cyl_ptr->get_min_axial_pos_num(segment_num)) - + num_planes_per_scanner_ring*delta)/2; + axial_pos_to_z_offset[segment_num] = + (cartesian_grid_info_ptr->get_max_index() + cartesian_grid_info_ptr->get_min_index()) / 2.F - + cartesian_grid_info_ptr->get_origin().z() / image_plane_spacing - + (num_planes_per_axial_pos[segment_num] * (proj_data_info_cyl_ptr->get_max_axial_pos_num(segment_num) + + proj_data_info_cyl_ptr->get_min_axial_pos_num(segment_num)) + + num_planes_per_scanner_ring * delta) / + 2; } } -/*! The DiscretisedDensity pointer has to point to an object of +/*! The DiscretisedDensity pointer has to point to an object of type DiscretisedDensityOnCartesianGrid (or a derived type). - + We really need only the geometrical info from the image. At the moment we have to use the data itself as well. */ -DataSymmetriesForBins_PET_CartesianGrid:: -DataSymmetriesForBins_PET_CartesianGrid -( - const shared_ptr& proj_data_info_ptr, - const shared_ptr >& image_info_ptr, - const bool do_symmetry_90degrees_min_phi_v, - const bool do_symmetry_180degrees_min_phi_v, - const bool do_symmetry_swap_segment_v, - const bool do_symmetry_swap_s_v, - const bool do_symmetry_shift_z -) - : DataSymmetriesForBins(proj_data_info_ptr), - do_symmetry_90degrees_min_phi(do_symmetry_90degrees_min_phi_v), - do_symmetry_180degrees_min_phi(do_symmetry_90degrees_min_phi_v || do_symmetry_180degrees_min_phi_v), - do_symmetry_swap_segment(do_symmetry_swap_segment_v), - do_symmetry_swap_s(do_symmetry_swap_s_v), - do_symmetry_shift_z(do_symmetry_shift_z) -{ - if(is_null_ptr(dynamic_cast(proj_data_info_ptr.get()))) +DataSymmetriesForBins_PET_CartesianGrid::DataSymmetriesForBins_PET_CartesianGrid( + const shared_ptr& proj_data_info_ptr, + const shared_ptr>& image_info_ptr, const bool do_symmetry_90degrees_min_phi_v, + const bool do_symmetry_180degrees_min_phi_v, const bool do_symmetry_swap_segment_v, const bool do_symmetry_swap_s_v, + const bool do_symmetry_shift_z) + : DataSymmetriesForBins(proj_data_info_ptr), do_symmetry_90degrees_min_phi(do_symmetry_90degrees_min_phi_v), + do_symmetry_180degrees_min_phi(do_symmetry_90degrees_min_phi_v || do_symmetry_180degrees_min_phi_v), + do_symmetry_swap_segment(do_symmetry_swap_segment_v), do_symmetry_swap_s(do_symmetry_swap_s_v), + do_symmetry_shift_z(do_symmetry_shift_z) { + if (is_null_ptr(dynamic_cast(proj_data_info_ptr.get()))) error("DataSymmetriesForBins_PET_CartesianGrid constructed with wrong type of ProjDataInfo: %s\n" "(can only handle projection data corresponding to a cylinder)\n", - typeid(*proj_data_info_ptr).name()); + typeid(*proj_data_info_ptr).name()); + + const DiscretisedDensityOnCartesianGrid<3, float>* cartesian_grid_info_ptr = + dynamic_cast*>(image_info_ptr.get()); - const DiscretisedDensityOnCartesianGrid<3,float> * - cartesian_grid_info_ptr = - dynamic_cast *> - (image_info_ptr.get()); - if (is_null_ptr(cartesian_grid_info_ptr)) error("DataSymmetriesForBins_PET_CartesianGrid constructed with wrong type of image info: %s\n", - typeid(*image_info_ptr).name()); + typeid(*image_info_ptr).name()); // WARNING get_grid_spacing()[1] == z - const float z_origin_in_planes = - image_info_ptr->get_origin().z()/cartesian_grid_info_ptr->get_grid_spacing()[1]; + const float z_origin_in_planes = image_info_ptr->get_origin().z() / cartesian_grid_info_ptr->get_grid_spacing()[1]; // z_origin_in_planes should be an integer if (fabs(round(z_origin_in_planes) - z_origin_in_planes) > 1.E-3F) error("DataSymmetriesForBins_PET_CartesianGrid: the shift in the " @@ -162,104 +144,79 @@ DataSymmetriesForBins_PET_CartesianGrid "separation (%g)\n", image_info_ptr->get_origin().z(), cartesian_grid_info_ptr->get_grid_spacing()[1]); - // check if unequal voxel size in x,y, if so, use less symmetry - if (fabs(cartesian_grid_info_ptr->get_grid_spacing()[2]- - cartesian_grid_info_ptr->get_grid_spacing()[3])>2.E-3F) + if (fabs(cartesian_grid_info_ptr->get_grid_spacing()[2] - cartesian_grid_info_ptr->get_grid_spacing()[3]) > 2.E-3F) do_symmetry_90degrees_min_phi = false; - num_views= proj_data_info_ptr->get_num_views(); + num_views = proj_data_info_ptr->get_num_views(); - if (num_views%4!=0) + if (num_views % 4 != 0) do_symmetry_90degrees_min_phi = false; - if (num_views%2!=0) + if (num_views % 2 != 0) do_symmetry_180degrees_min_phi = false; // check on segment symmetry - if (fabs(proj_data_info_ptr->get_tantheta(Bin(0,0,0,0)))> 1.E-4F) + if (fabs(proj_data_info_ptr->get_tantheta(Bin(0, 0, 0, 0))) > 1.E-4F) error("DataSymmetriesForBins_PET_CartesianGrid can only handle projection data " - "with segment 0 corresponding to direct planes (i.e. theta==0)\n"); - - for (int segment_num=1; - segment_num<= min(proj_data_info_ptr->get_max_segment_num(), - -proj_data_info_ptr->get_min_segment_num()); - ++segment_num) - if (fabs(proj_data_info_ptr->get_tantheta(Bin(segment_num,0,0,0)) + - proj_data_info_ptr->get_tantheta(Bin(-segment_num,0,0,0))) > 1.E-4F) + "with segment 0 corresponding to direct planes (i.e. theta==0)\n"); + + for (int segment_num = 1; + segment_num <= min(proj_data_info_ptr->get_max_segment_num(), -proj_data_info_ptr->get_min_segment_num()); ++segment_num) + if (fabs(proj_data_info_ptr->get_tantheta(Bin(segment_num, 0, 0, 0)) + + proj_data_info_ptr->get_tantheta(Bin(-segment_num, 0, 0, 0))) > 1.E-4F) + error("DataSymmetriesForBins_PET_CartesianGrid can only handle projection data " + "with negative segment numbers corresponding to -theta of the positive segments. " + "This is not true for segment pair %d.\n", + segment_num); + + // feable check on s-symmetry + if (fabs(proj_data_info_ptr->get_s(Bin(0, 0, 0, 1)) + proj_data_info_ptr->get_s(Bin(0, 0, 0, -1))) > 1.E-4F) error("DataSymmetriesForBins_PET_CartesianGrid can only handle projection data " - "with negative segment numbers corresponding to -theta of the positive segments. " - "This is not true for segment pair %d.\n", - segment_num); - - //feable check on s-symmetry - if (fabs(proj_data_info_ptr->get_s(Bin(0,0,0,1)) + - proj_data_info_ptr->get_s(Bin(0,0,0,-1))) > 1.E-4F) - error("DataSymmetriesForBins_PET_CartesianGrid can only handle projection data " - "with tangential_pos_num s.t. get_s(...,tang_pos_num)==-get_s(...,-tang_pos_num)\n"); - - if (fabs(image_info_ptr->get_origin().x())>.01F || fabs(image_info_ptr->get_origin().y())>.01F) - { - // disable symmetries with shifted images - if (this->do_symmetry_90degrees_min_phi|| - this->do_symmetry_180degrees_min_phi|| - this->do_symmetry_swap_segment|| - this->do_symmetry_swap_s) - { - warning("Disabling symmetries in transaxial plane as image is shifted"); - this->do_symmetry_90degrees_min_phi = - this->do_symmetry_180degrees_min_phi = - this->do_symmetry_swap_segment = - this->do_symmetry_swap_s = false; - } + "with tangential_pos_num s.t. get_s(...,tang_pos_num)==-get_s(...,-tang_pos_num)\n"); + + if (fabs(image_info_ptr->get_origin().x()) > .01F || fabs(image_info_ptr->get_origin().y()) > .01F) { + // disable symmetries with shifted images + if (this->do_symmetry_90degrees_min_phi || this->do_symmetry_180degrees_min_phi || this->do_symmetry_swap_segment || + this->do_symmetry_swap_s) { + warning("Disabling symmetries in transaxial plane as image is shifted"); + this->do_symmetry_90degrees_min_phi = this->do_symmetry_180degrees_min_phi = this->do_symmetry_swap_segment = + this->do_symmetry_swap_s = false; } - - find_relation_between_coordinate_systems(num_planes_per_scanner_ring, - num_planes_per_axial_pos, - axial_pos_to_z_offset, - static_cast(proj_data_info_ptr.get()), - cartesian_grid_info_ptr); -} + } + find_relation_between_coordinate_systems(num_planes_per_scanner_ring, num_planes_per_axial_pos, axial_pos_to_z_offset, + static_cast(proj_data_info_ptr.get()), + cartesian_grid_info_ptr); +} #ifndef STIR_NO_COVARIANT_RETURN_TYPES - DataSymmetriesForBins_PET_CartesianGrid * +DataSymmetriesForBins_PET_CartesianGrid* #else - DataSymmetriesForViewSegmentNumbers * +DataSymmetriesForViewSegmentNumbers* #endif -DataSymmetriesForBins_PET_CartesianGrid:: -clone() const -{ +DataSymmetriesForBins_PET_CartesianGrid::clone() const { return new DataSymmetriesForBins_PET_CartesianGrid(*this); } - -bool -DataSymmetriesForBins_PET_CartesianGrid:: -operator==(const DataSymmetriesForBins_PET_CartesianGrid& sym) const -{ +bool +DataSymmetriesForBins_PET_CartesianGrid::operator==(const DataSymmetriesForBins_PET_CartesianGrid& sym) const { if (!base_type::operator==(sym)) return false; - return - this->do_symmetry_90degrees_min_phi == sym.do_symmetry_90degrees_min_phi && - this->do_symmetry_180degrees_min_phi == sym.do_symmetry_180degrees_min_phi && - this->do_symmetry_swap_segment == sym.do_symmetry_swap_segment && - this->do_symmetry_swap_s == sym.do_symmetry_swap_s && - this->do_symmetry_shift_z == sym.do_symmetry_shift_z && - this->num_views == sym.num_views && - this->num_planes_per_scanner_ring == sym.num_planes_per_scanner_ring && - this->num_planes_per_axial_pos == sym.num_planes_per_axial_pos && - this->axial_pos_to_z_offset == sym.axial_pos_to_z_offset; + return this->do_symmetry_90degrees_min_phi == sym.do_symmetry_90degrees_min_phi && + this->do_symmetry_180degrees_min_phi == sym.do_symmetry_180degrees_min_phi && + this->do_symmetry_swap_segment == sym.do_symmetry_swap_segment && this->do_symmetry_swap_s == sym.do_symmetry_swap_s && + this->do_symmetry_shift_z == sym.do_symmetry_shift_z && this->num_views == sym.num_views && + this->num_planes_per_scanner_ring == sym.num_planes_per_scanner_ring && + this->num_planes_per_axial_pos == sym.num_planes_per_axial_pos && + this->axial_pos_to_z_offset == sym.axial_pos_to_z_offset; } -bool -DataSymmetriesForBins_PET_CartesianGrid:: -blindly_equals(const root_type * const that_ptr) const -{ - assert(dynamic_cast(that_ptr) != 0); - return - this->operator==(static_cast(*that_ptr)); +bool +DataSymmetriesForBins_PET_CartesianGrid::blindly_equals(const root_type* const that_ptr) const { + assert(dynamic_cast(that_ptr) != 0); + return this->operator==(static_cast(*that_ptr)); } END_NAMESPACE_STIR diff --git a/src/recon_buildblock/DataSymmetriesForDensels.cxx b/src/recon_buildblock/DataSymmetriesForDensels.cxx index a7111eee83..74f19baa67 100644 --- a/src/recon_buildblock/DataSymmetriesForDensels.cxx +++ b/src/recon_buildblock/DataSymmetriesForDensels.cxx @@ -39,51 +39,38 @@ using std::vector; START_NAMESPACE_STIR -DataSymmetriesForDensels:: -DataSymmetriesForDensels() -{} +DataSymmetriesForDensels::DataSymmetriesForDensels() {} /*! Default implementation always returns \c true. Needs to be overloaded. */ bool -DataSymmetriesForDensels:: -blindly_equals(const root_type * const) const -{ +DataSymmetriesForDensels::blindly_equals(const root_type* const) const { return true; } bool -DataSymmetriesForDensels:: -operator ==(const root_type& that) const -{ - return - typeid(*this) == typeid(that) && - this->blindly_equals(&that); +DataSymmetriesForDensels::operator==(const root_type& that) const { + return typeid(*this) == typeid(that) && this->blindly_equals(&that); } bool -DataSymmetriesForDensels:: -operator !=(const root_type& that) const -{ +DataSymmetriesForDensels::operator!=(const root_type& that) const { return !((*this) == that); } /*! default implementation in terms of get_related_densels, will be slow of course */ int -DataSymmetriesForDensels::num_related_densels(const Densel& b) const -{ +DataSymmetriesForDensels::num_related_densels(const Densel& b) const { vector rel_b; get_related_densels(rel_b, b); return static_cast(rel_b.size()); } /*! default implementation in terms of find_symmetry_operation_from_basic_densel */ -bool DataSymmetriesForDensels::find_basic_densel(Densel& b) const -{ - unique_ptr sym_op = - find_symmetry_operation_from_basic_densel(b); +bool +DataSymmetriesForDensels::find_basic_densel(Densel& b) const { + unique_ptr sym_op = find_symmetry_operation_from_basic_densel(b); return sym_op->is_trivial(); } - END_NAMESPACE_STIR diff --git a/src/recon_buildblock/DistributedCachingInformation.cxx b/src/recon_buildblock/DistributedCachingInformation.cxx index f899d9bfbc..055104d064 100644 --- a/src/recon_buildblock/DistributedCachingInformation.cxx +++ b/src/recon_buildblock/DistributedCachingInformation.cxx @@ -23,162 +23,146 @@ \brief Implementation of class stir::DistributedCachingInformation - \author Tobias Beisel + \author Tobias Beisel \author Kris Thielemans */ - #include "stir/recon_buildblock/DistributedCachingInformation.h" #include "stir/recon_buildblock/distributed_functions.h" START_NAMESPACE_STIR -DistributedCachingInformation::DistributedCachingInformation(const int num_workers_v) - : num_workers(num_workers_v) -{ +DistributedCachingInformation::DistributedCachingInformation(const int num_workers_v) : num_workers(num_workers_v) { initialise(); } +DistributedCachingInformation::~DistributedCachingInformation() {} -DistributedCachingInformation::~DistributedCachingInformation() -{ -} - -void DistributedCachingInformation::initialise() -{ - //initialize vector sizes +void +DistributedCachingInformation::initialise() { + // initialize vector sizes this->proc_vs_nums.resize(this->num_workers); - for (int i=0; inum_workers; i++) + for (int i = 0; i < this->num_workers; i++) this->proc_vs_nums[i].resize(0); - + this->vs_nums_to_process.resize(0); this->initialise_new_subiteration(this->vs_nums_to_process); } -void DistributedCachingInformation::initialise_new_subiteration(const std::vector& vs_nums_to_process_v) -{ - this->vs_nums_to_process= vs_nums_to_process_v; +void +DistributedCachingInformation::initialise_new_subiteration(const std::vector& vs_nums_to_process_v) { + this->vs_nums_to_process = vs_nums_to_process_v; this->set_all_vs_num_unprocessed(); } -void DistributedCachingInformation::set_all_vs_num_unprocessed() -{ +void +DistributedCachingInformation::set_all_vs_num_unprocessed() { this->still_to_process.resize(this->vs_nums_to_process.size()); std::fill(this->still_to_process.begin(), this->still_to_process.end(), true); } -int DistributedCachingInformation::find_vs_num_position_in_list_to_process(const ViewSegmentNumbers& vs_num) const -{ - std::vector::const_iterator iter = - std::find(this->vs_nums_to_process.begin(), this->vs_nums_to_process.end(), vs_num); - if (iter== this->vs_nums_to_process.end()) +int +DistributedCachingInformation::find_vs_num_position_in_list_to_process(const ViewSegmentNumbers& vs_num) const { + std::vector::const_iterator iter = + std::find(this->vs_nums_to_process.begin(), this->vs_nums_to_process.end(), vs_num); + if (iter == this->vs_nums_to_process.end()) error("Internal error: asked for vs_num that is not in the list"); return iter - this->vs_nums_to_process.begin(); } -int DistributedCachingInformation::find_position_of_first_unprocessed() const -{ - std::vector::const_iterator iter = - std::find(this->still_to_process.begin(), this->still_to_process.end(), true); - if (iter== this->still_to_process.end()) +int +DistributedCachingInformation::find_position_of_first_unprocessed() const { + std::vector::const_iterator iter = std::find(this->still_to_process.begin(), this->still_to_process.end(), true); + if (iter == this->still_to_process.end()) error("Internal error: asked for unprocessed, but all done"); return iter - this->still_to_process.begin(); } -void DistributedCachingInformation::set_processed(const ViewSegmentNumbers& vs_num) -{ - this->still_to_process[this->find_vs_num_position_in_list_to_process(vs_num)]=false; +void +DistributedCachingInformation::set_processed(const ViewSegmentNumbers& vs_num) { + this->still_to_process[this->find_vs_num_position_in_list_to_process(vs_num)] = false; } -bool DistributedCachingInformation::is_still_to_be_processed(const ViewSegmentNumbers& vs_num) const -{ - std::vector::const_iterator iter = - std::find(this->vs_nums_to_process.begin(), this->vs_nums_to_process.end(), vs_num); - if (iter== this->vs_nums_to_process.end()) +bool +DistributedCachingInformation::is_still_to_be_processed(const ViewSegmentNumbers& vs_num) const { + std::vector::const_iterator iter = + std::find(this->vs_nums_to_process.begin(), this->vs_nums_to_process.end(), vs_num); + if (iter == this->vs_nums_to_process.end()) return false; else return this->still_to_process[iter - this->vs_nums_to_process.begin()]; } -void DistributedCachingInformation::add_vs_num_to_proc(int proc, const ViewSegmentNumbers& vs_num) -{ +void +DistributedCachingInformation::add_vs_num_to_proc(int proc, const ViewSegmentNumbers& vs_num) { this->proc_vs_nums[proc].push_back(vs_num); this->set_processed(vs_num); } -int DistributedCachingInformation::get_num_remaining_cached_data_to_process(int proc) const -{ +int +DistributedCachingInformation::get_num_remaining_cached_data_to_process(int proc) const { int cnt = 0; - for (std::vector::const_iterator iter=this->proc_vs_nums[proc].begin(); - iter!=this->proc_vs_nums[proc].end(); ++ iter) - { - if (this->is_still_to_be_processed(*iter)) - cnt++; - } + for (std::vector::const_iterator iter = this->proc_vs_nums[proc].begin(); + iter != this->proc_vs_nums[proc].end(); ++iter) { + if (this->is_still_to_be_processed(*iter)) + cnt++; + } return cnt; } -bool DistributedCachingInformation::get_oldest_unprocessed_vs_num(ViewSegmentNumbers& vs, int proc) const -{ - //find unprocessed vs_num - for (std::vector::const_iterator iter=this->proc_vs_nums[proc].begin(); - iter!=this->proc_vs_nums[proc].end(); ++ iter) - { - if (this->is_still_to_be_processed(*iter)) - { - vs=*iter; - return true; - } +bool +DistributedCachingInformation::get_oldest_unprocessed_vs_num(ViewSegmentNumbers& vs, int proc) const { + // find unprocessed vs_num + for (std::vector::const_iterator iter = this->proc_vs_nums[proc].begin(); + iter != this->proc_vs_nums[proc].end(); ++iter) { + if (this->is_still_to_be_processed(*iter)) { + vs = *iter; + return true; } - - return false; + } + + return false; } -bool DistributedCachingInformation::get_unprocessed_vs_num(ViewSegmentNumbers& vs, int proc) -{ +bool +DistributedCachingInformation::get_unprocessed_vs_num(ViewSegmentNumbers& vs, int proc) { const bool in_cache = this->get_oldest_unprocessed_vs_num(vs, proc); - if (!in_cache) - { - // no cached unprocessed data found - // get work from worker with most work left - vs = this->get_vs_num_of_proc_with_most_work_left(proc); - } - + if (!in_cache) { + // no cached unprocessed data found + // get work from worker with most work left + vs = this->get_vs_num_of_proc_with_most_work_left(proc); + } + this->add_vs_num_to_proc(proc, vs); - return !in_cache; + return !in_cache; } -ViewSegmentNumbers DistributedCachingInformation::get_vs_num_of_proc_with_most_work_left(int proc) const -{ - int proc_with_max_work=0; - int max_work=0; - - //find processor with most work left - for (int i=0; inum_workers;i++) - { - if (i==proc) continue; - const int cnt = this->get_num_remaining_cached_data_to_process(i); - if (cnt>max_work) - { - max_work=cnt; - proc_with_max_work = i; - } +ViewSegmentNumbers +DistributedCachingInformation::get_vs_num_of_proc_with_most_work_left(int proc) const { + int proc_with_max_work = 0; + int max_work = 0; + + // find processor with most work left + for (int i = 0; i < this->num_workers; i++) { + if (i == proc) + continue; + const int cnt = this->get_num_remaining_cached_data_to_process(i); + if (cnt > max_work) { + max_work = cnt; + proc_with_max_work = i; } + } ViewSegmentNumbers vs; - //get vs_number of the processor with most work left - if (max_work>0) - { - this->get_oldest_unprocessed_vs_num(vs, proc_with_max_work); - } - else - { - // just return first unprocessed - vs = this->vs_nums_to_process[this->find_position_of_first_unprocessed()]; - } - + // get vs_number of the processor with most work left + if (max_work > 0) { + this->get_oldest_unprocessed_vs_num(vs, proc_with_max_work); + } else { + // just return first unprocessed + vs = this->vs_nums_to_process[this->find_position_of_first_unprocessed()]; + } + return vs; } - END_NAMESPACE_STIR diff --git a/src/recon_buildblock/DistributedWorker.cxx b/src/recon_buildblock/DistributedWorker.cxx index f227f67876..2358f3dd1e 100644 --- a/src/recon_buildblock/DistributedWorker.cxx +++ b/src/recon_buildblock/DistributedWorker.cxx @@ -22,7 +22,7 @@ \brief Implementation of stir::DistributedWorker() - \author Tobias Beisel + \author Tobias Beisel \author Kris Thielemans \author Alexey Zverovich (idea of using main() function) */ @@ -48,184 +48,161 @@ #include "stir/recon_buildblock/distributable_main.h" -int main(int argc, char **argv) -{ +int +main(int argc, char** argv) { int return_value = EXIT_FAILURE; - try - { -#ifndef STIR_MPI - return stir::distributable_main(argc, argv); + try { +#ifndef STIR_MPI + return stir::distributable_main(argc, argv); #else - //processor-id within parallel Communicator - int my_rank; - - //saves the name of a processor - char processor_name[MPI_MAX_PROCESSOR_NAME]; - //length of the processor-name - int namelength; - - MPI_Init(&argc, &argv) ; /*Initializes the start up for MPI*/ - MPI_Comm_rank(MPI_COMM_WORLD, &my_rank) ; /*Gets the rank of the Processor*/ - MPI_Comm_size(MPI_COMM_WORLD, &distributed::num_processors) ; /*Finds the number of processes being used*/ - MPI_Get_processor_name(processor_name, &namelength); - - stir::info(boost::format("Process %d of %d on %s") - % my_rank % distributed::num_processors % processor_name); - - //master - if (my_rank==0) - { - if (distributed::num_processors<2) - { - stir::error("STIR MPI processing needs more than 1 processor"); - return_value = EXIT_FAILURE; - } - else - { - return_value=stir::distributable_main(argc, argv); - if (distributed::total_rpc_time_slaves!=0) - stir::info(boost::format("Total time used for RPC-processing: %1%") % - distributed::total_rpc_time_slaves); - } - } - else //slaves - { - //create Slave Object - stir::DistributedWorker > worker; - - //start Slave Process: - worker.start(); - return_value = EXIT_SUCCESS; - } -#endif + // processor-id within parallel Communicator + int my_rank; - } - catch (std::string& error_string) - { - // don't print yet, as error() already does that at the moment - // std::cerr << error_string << std::endl; - return_value = EXIT_FAILURE; - } - catch (std::exception& e) + // saves the name of a processor + char processor_name[MPI_MAX_PROCESSOR_NAME]; + // length of the processor-name + int namelength; + + MPI_Init(&argc, &argv); /*Initializes the start up for MPI*/ + MPI_Comm_rank(MPI_COMM_WORLD, &my_rank); /*Gets the rank of the Processor*/ + MPI_Comm_size(MPI_COMM_WORLD, &distributed::num_processors); /*Finds the number of processes being used*/ + MPI_Get_processor_name(processor_name, &namelength); + + stir::info(boost::format("Process %d of %d on %s") % my_rank % distributed::num_processors % processor_name); + + // master + if (my_rank == 0) { + if (distributed::num_processors < 2) { + stir::error("STIR MPI processing needs more than 1 processor"); + return_value = EXIT_FAILURE; + } else { + return_value = stir::distributable_main(argc, argv); + if (distributed::total_rpc_time_slaves != 0) + stir::info(boost::format("Total time used for RPC-processing: %1%") % distributed::total_rpc_time_slaves); + } + } else // slaves { - stir::warning(e.what()); - return_value = EXIT_FAILURE; + // create Slave Object + stir::DistributedWorker> worker; + + // start Slave Process: + worker.start(); + return_value = EXIT_SUCCESS; } +#endif + + } catch (std::string& error_string) { + // don't print yet, as error() already does that at the moment + // std::cerr << error_string << std::endl; + return_value = EXIT_FAILURE; + } catch (std::exception& e) { + stir::warning(e.what()); + return_value = EXIT_FAILURE; + } #ifdef STIR_MPI MPI_Finalize(); #endif return return_value; } - -namespace stir -{ - - template - DistributedWorker::DistributedWorker() - { - this->set_defaults(); - MPI_Comm_rank(MPI_COMM_WORLD, &this->my_rank) ; /*Gets the rank of the Processor*/ - } - - template - DistributedWorker::~DistributedWorker() - { - } +namespace stir { - template - void DistributedWorker::set_defaults() - { - log_likelihood_ptr=NULL; - zero_seg0_end_planes=false; - cache_enabled=false; - } - - template - void DistributedWorker::start() - { - - // keep-on waiting for new tasks - while (true) - { - //Receive task broadcasted by Master - const int task_id = distributed::receive_int_value(-1); - - switch(task_id) - { - case task_stop_processing: // done with processing - { - return; - } - - case task_setup_distributable_computation: - { - this->setup_distributable_computation(); - break; - } - - case task_do_distributable_gradient_computation: - { - this->distributable_computation(RPC_process_related_viewgrams_gradient); - break; - } - case task_do_distributable_loglikelihood_computation: - { - this->distributable_computation(RPC_process_related_viewgrams_accumulate_loglikelihood); - break; - } - case task_do_distributable_sensitivity_computation: - { - this->distributable_computation(RPC_process_related_viewgrams_sensitivity_computation); - break; - } +template +DistributedWorker::DistributedWorker() { + this->set_defaults(); + MPI_Comm_rank(MPI_COMM_WORLD, &this->my_rank); /*Gets the rank of the Processor*/ +} + +template +DistributedWorker::~DistributedWorker() {} + +template +void +DistributedWorker::set_defaults() { + log_likelihood_ptr = NULL; + zero_seg0_end_planes = false; + cache_enabled = false; +} + +template +void +DistributedWorker::start() { + + // keep-on waiting for new tasks + while (true) { + // Receive task broadcasted by Master + const int task_id = distributed::receive_int_value(-1); + + switch (task_id) { + case task_stop_processing: // done with processing + { + return; + } + + case task_setup_distributable_computation: { + this->setup_distributable_computation(); + break; + } + + case task_do_distributable_gradient_computation: { + this->distributable_computation(RPC_process_related_viewgrams_gradient); + break; + } + case task_do_distributable_loglikelihood_computation: { + this->distributable_computation(RPC_process_related_viewgrams_accumulate_loglikelihood); + break; + } + case task_do_distributable_sensitivity_computation: { + this->distributable_computation(RPC_process_related_viewgrams_sensitivity_computation); + break; + } /* - case task_do_distributable_sensitivity_computation;break; + case task_do_distributable_sensitivity_computation;break; */ - default: - { - error("Internal error: Slave %d received unknown task-id %d", this->my_rank, task_id); - } - } // end switch task_id - } // infinite loop - } - - template - void DistributedWorker::setup_distributable_computation() - { - //Receive zero_seg_end_planes - this->zero_seg0_end_planes = distributed::receive_bool_value(-1,-1); - - //receive target image pointer - distributed::receive_and_set_image_parameters(this->target_sptr, image_buffer_size, -1, 0); - - //Receive input_image values - MPI_Status status; - status = distributed::receive_image_values_and_fill_image_ptr(this->target_sptr, this->image_buffer_size, 0); - //construct exam_info_ptr and projection_data_info_ptr - distributed::receive_and_construct_exam_and_proj_data_info_ptr(this->exam_info_sptr, this->proj_data_info_sptr, 0); - - //initialize projectors and call set_up - distributed::receive_and_initialize_projectors(this->proj_pair_sptr, 0); - - proj_pair_sptr->set_up(this->proj_data_info_sptr, this->target_sptr); - - //some values to configure tests - int configurations[4]; - status=distributed::receive_int_values(configurations, 4, distributed::STIR_MPI_CONF_TAG); - - status=distributed::receive_double_values(&distributed::min_threshold, 1, distributed::STIR_MPI_CONF_TAG); - - (configurations[0]==1)?distributed::test=true:distributed::test=false; - (configurations[1]==1)?distributed::test_send_receive_times=true:distributed::test_send_receive_times=false; - (configurations[2]==1)?distributed::rpc_time=true:distributed::rpc_time=false; - (configurations[3]==1)?cache_enabled=true:cache_enabled=false; - + default: { + error("Internal error: Slave %d received unknown task-id %d", this->my_rank, task_id); + } + } // end switch task_id + } // infinite loop +} + +template +void +DistributedWorker::setup_distributable_computation() { + // Receive zero_seg_end_planes + this->zero_seg0_end_planes = distributed::receive_bool_value(-1, -1); + + // receive target image pointer + distributed::receive_and_set_image_parameters(this->target_sptr, image_buffer_size, -1, 0); + + // Receive input_image values + MPI_Status status; + status = distributed::receive_image_values_and_fill_image_ptr(this->target_sptr, this->image_buffer_size, 0); + // construct exam_info_ptr and projection_data_info_ptr + distributed::receive_and_construct_exam_and_proj_data_info_ptr(this->exam_info_sptr, this->proj_data_info_sptr, 0); + + // initialize projectors and call set_up + distributed::receive_and_initialize_projectors(this->proj_pair_sptr, 0); + + proj_pair_sptr->set_up(this->proj_data_info_sptr, this->target_sptr); + + // some values to configure tests + int configurations[4]; + status = distributed::receive_int_values(configurations, 4, distributed::STIR_MPI_CONF_TAG); + + status = distributed::receive_double_values(&distributed::min_threshold, 1, distributed::STIR_MPI_CONF_TAG); + + (configurations[0] == 1) ? distributed::test = true : distributed::test = false; + (configurations[1] == 1) ? distributed::test_send_receive_times = true : distributed::test_send_receive_times = false; + (configurations[2] == 1) ? distributed::rpc_time = true : distributed::rpc_time = false; + (configurations[3] == 1) ? cache_enabled = true : cache_enabled = false; + #ifndef NDEBUG - if (distributed::test && my_rank==1) distributed::test_parameter_info_slave(proj_pair_sptr->stir::ParsingObject::parameter_info()); + if (distributed::test && my_rank == 1) + distributed::test_parameter_info_slave(proj_pair_sptr->stir::ParsingObject::parameter_info()); #endif #if 0 @@ -237,208 +214,192 @@ namespace stir objective_function_ptr->set_zero_seg0_end_planes(zero_seg0_end_planes); objective_function_ptr->set_projector_pair_sptr(proj_pair_sptr); #endif - - // reset cache-stores, they will be initialised if we need them - this->proj_data_ptr.reset(); - this->binwise_correction.reset(); - this->mult_proj_data_sptr.reset(); - } // set_up - - template - void DistributedWorker:: - distributable_computation(RPC_process_related_viewgrams_type * RPC_process_related_viewgrams) - { - shared_ptr input_image_ptr = this->target_sptr; // use the target_sptr member as we don't need its values anyway - - shared_ptr - symmetries_sptr(this->proj_pair_sptr->get_symmetries_used()->clone()); - + + // reset cache-stores, they will be initialised if we need them + this->proj_data_ptr.reset(); + this->binwise_correction.reset(); + this->mult_proj_data_sptr.reset(); +} // set_up + +template +void +DistributedWorker::distributable_computation(RPC_process_related_viewgrams_type* RPC_process_related_viewgrams) { + shared_ptr input_image_ptr = this->target_sptr; // use the target_sptr member as we don't need its values anyway + + shared_ptr symmetries_sptr(this->proj_pair_sptr->get_symmetries_used()->clone()); + #ifndef NDEBUG - if (distributed::test && my_rank==1) + if (distributed::test && my_rank == 1) { + distributed::test_image_estimate_slave(); + distributed::test_parameter_info_slave(proj_data_info_sptr->parameter_info()); + distributed::test_bool_value_slave(); + distributed::test_int_value_slave(); + distributed::test_int_values_slave(); + distributed::test_viewgram_slave(proj_data_info_sptr); + } +#endif + + HighResWallClockTimer t; + + { + if (distributed::receive_bool_value(USE_DOUBLE_ARG_TAG, -1)) { + this->log_likelihood_ptr = new double; + *this->log_likelihood_ptr = 0.0; + } else { + this->log_likelihood_ptr = 0; + } + + // Receive input_image values + MPI_Status status = distributed::receive_image_values_and_fill_image_ptr(input_image_ptr, this->image_buffer_size, 0); + + shared_ptr output_image_ptr; + if (distributed::receive_bool_value(USE_OUTPUT_IMAGE_ARG_TAG, -1)) { + output_image_ptr.reset(this->target_sptr->get_empty_copy()); + // already set to zero + // output_image_ptr->fill(0.F); + } + + proj_pair_sptr->get_forward_projector_sptr()->set_input(*this->target_sptr); + if (!is_null_ptr(output_image_ptr)) + proj_pair_sptr->get_back_projector_sptr()->start_accumulating_in_new_target(); + + // loop to receive viewgrams until received END_ITERATION_TAG + while (true) { + // TODO, get rid of pointers somehow + RelatedViewgrams* viewgrams = NULL; + RelatedViewgrams* additive_binwise_correction_viewgrams = NULL; + RelatedViewgrams* mult_viewgrams_ptr = NULL; + int count = 0, count2 = 0; + + // receive vs_num values + ViewSegmentNumbers vs; + status = distributed::receive_view_segment_numbers(vs, MPI_ANY_TAG); + + /*check whether to + * - use a viewgram already received in previous iteration + * - receive a new viewgram + * - end the iteration + */ + if (status.MPI_TAG == REUSE_VIEWGRAM_TAG) // use a viewgram already available { - distributed::test_image_estimate_slave(); - distributed::test_parameter_info_slave(proj_data_info_sptr->parameter_info()); - distributed::test_bool_value_slave(); - distributed::test_int_value_slave(); - distributed::test_int_values_slave(); - distributed::test_viewgram_slave(proj_data_info_sptr); - } -#endif - - HighResWallClockTimer t; - + viewgrams = new RelatedViewgrams(proj_data_ptr->get_related_viewgrams(vs, symmetries_sptr)); + if (!is_null_ptr(binwise_correction)) + additive_binwise_correction_viewgrams = + new RelatedViewgrams(binwise_correction->get_related_viewgrams(vs, symmetries_sptr)); + if (!is_null_ptr(mult_proj_data_sptr)) + mult_viewgrams_ptr = new RelatedViewgrams(mult_proj_data_sptr->get_related_viewgrams(vs, symmetries_sptr)); + } else if (status.MPI_TAG == NEW_VIEWGRAM_TAG) // receive a message with a new viewgram { - if (distributed::receive_bool_value(USE_DOUBLE_ARG_TAG,-1)) - { - this->log_likelihood_ptr = new double; - *this->log_likelihood_ptr=0.0; - } - else - { - this->log_likelihood_ptr = 0; - } - - //Receive input_image values - MPI_Status status = distributed::receive_image_values_and_fill_image_ptr(input_image_ptr, this->image_buffer_size, 0); - - shared_ptr output_image_ptr; - if (distributed::receive_bool_value(USE_OUTPUT_IMAGE_ARG_TAG,-1)) - { - output_image_ptr.reset(this->target_sptr->get_empty_copy()); - // already set to zero - //output_image_ptr->fill(0.F); - } - - proj_pair_sptr->get_forward_projector_sptr()->set_input(*this->target_sptr); - if (!is_null_ptr(output_image_ptr)) - proj_pair_sptr->get_back_projector_sptr()->start_accumulating_in_new_target(); - - //loop to receive viewgrams until received END_ITERATION_TAG - while (true) - { - // TODO, get rid of pointers somehow - RelatedViewgrams* viewgrams = NULL; - RelatedViewgrams* additive_binwise_correction_viewgrams = NULL; - RelatedViewgrams* mult_viewgrams_ptr = NULL; - int count=0, count2=0; - - //receive vs_num values - ViewSegmentNumbers vs; - status = distributed::receive_view_segment_numbers(vs, MPI_ANY_TAG); - - /*check whether to - * - use a viewgram already received in previous iteration - * - receive a new viewgram - * - end the iteration - */ - if (status.MPI_TAG==REUSE_VIEWGRAM_TAG) //use a viewgram already available - { - viewgrams = new RelatedViewgrams(proj_data_ptr->get_related_viewgrams(vs, symmetries_sptr)); - if (!is_null_ptr(binwise_correction)) - additive_binwise_correction_viewgrams = - new RelatedViewgrams(binwise_correction->get_related_viewgrams(vs, symmetries_sptr)); - if (!is_null_ptr(mult_proj_data_sptr)) - mult_viewgrams_ptr = - new RelatedViewgrams(mult_proj_data_sptr->get_related_viewgrams(vs, symmetries_sptr)); - } - else if (status.MPI_TAG==NEW_VIEWGRAM_TAG) //receive a message with a new viewgram - { #ifndef NDEBUG - //run test for related viewgrams - if (distributed::test && my_rank==1 && distributed::first_iteration==true) distributed::test_related_viewgrams_slave(proj_data_info_sptr, symmetries_sptr); -#endif - //receive info if additive_binwise_correction_viewgrams are NULL - const bool add_bin_corr_viewgrams=distributed::receive_bool_value(BINWISE_CORRECTION_TAG, 0); - if (add_bin_corr_viewgrams) - { - distributed::receive_and_construct_related_viewgrams(additive_binwise_correction_viewgrams, proj_data_info_sptr, symmetries_sptr, 0); - } - - //receive info if mult_viewgrams_ptr are NULL - const bool mult_viewgrams=distributed::receive_bool_value(BINWISE_MULT_TAG, 0); - if (mult_viewgrams) - { - distributed::receive_and_construct_related_viewgrams(mult_viewgrams_ptr, proj_data_info_sptr, symmetries_sptr, 0); - } - - // measured viewgrams - distributed::receive_and_construct_related_viewgrams(viewgrams, proj_data_info_sptr, symmetries_sptr, 0); - - //save Viewgrams to ProjDataInMemory object - if(cache_enabled) - { - if (is_null_ptr(this->proj_data_ptr)) - this->proj_data_ptr.reset(new ProjDataInMemory(this->exam_info_sptr,this->proj_data_info_sptr, /*init_with_0*/ false)); - - if (proj_data_ptr->set_related_viewgrams(*viewgrams)==Succeeded::no) - error("Slave %i: Storing viewgrams failed!\n", my_rank); - - if (add_bin_corr_viewgrams) - { - if (is_null_ptr(binwise_correction)) - binwise_correction.reset(new ProjDataInMemory(this->exam_info_sptr,this->proj_data_info_sptr, /*init_with_0*/ false)); - - if (binwise_correction->set_related_viewgrams(*additive_binwise_correction_viewgrams)==Succeeded::no) - error("Slave %i: Storing additive_binwise_correction_viewgrams failed!\n", my_rank); - } - - if (mult_viewgrams) - { - if (is_null_ptr(mult_proj_data_sptr)) - mult_proj_data_sptr.reset(new ProjDataInMemory(this->exam_info_sptr,this->proj_data_info_sptr, /*init_with_0*/ false)); - - if (mult_proj_data_sptr->set_related_viewgrams(*mult_viewgrams_ptr)==Succeeded::no) - error("Slave %i: Storing mult_viewgrams_ptr failed!\n", my_rank); - } - } - } - else if (status.MPI_TAG==END_ITERATION_TAG) //the iteration is completed --> send results - { - //make reduction over computed output_images - distributed::first_iteration=false; - if (!is_null_ptr(output_image_ptr)) - { - proj_pair_sptr->get_back_projector_sptr()->get_output(*output_image_ptr); - distributed::reduce_output_image(output_image_ptr, image_buffer_size, my_rank, 0); - } - // and log_likelihood - if (!is_null_ptr(log_likelihood_ptr)) - { - double buffer = 0.0; - MPI_Reduce(log_likelihood_ptr, &buffer, /*size*/1, MPI_DOUBLE, MPI_SUM, /*destination*/ 0, MPI_COMM_WORLD); - delete log_likelihood_ptr; - } - - if(distributed::rpc_time) - { - double send = distributed::total_rpc_time; - double receive; - MPI_Reduce(&send, &receive, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); - distributed::total_rpc_time = 0.0; - } - // get out of infinite while loop - break; - } - else - error("Slave received unknown tag"); - - //measure time used for parallelized part - if (distributed::rpc_time) {t.reset(); t.start();} - - //call the actual calculation - RPC_process_related_viewgrams( - this->proj_pair_sptr->get_forward_projector_sptr(), - this->proj_pair_sptr->get_back_projector_sptr(), - viewgrams, - count, count2, - log_likelihood_ptr, - additive_binwise_correction_viewgrams, - mult_viewgrams_ptr); - - if (distributed::rpc_time) - { - t.stop(); - distributed::total_rpc_time=distributed::total_rpc_time + t.value(); - distributed::total_rpc_time_2=distributed::total_rpc_time_2 + t.value(); - } - - int int_values[2]; - int_values[0]=count; - int_values[1]=count2; - //send count,count2 and ask for new work - distributed::send_int_values(int_values, 2, AVAILABLE_NOTIFICATION_TAG, 0); - - if (viewgrams!=NULL) delete viewgrams; - if (additive_binwise_correction_viewgrams!=NULL) delete additive_binwise_correction_viewgrams; - if (mult_viewgrams_ptr!=NULL) delete mult_viewgrams_ptr; + // run test for related viewgrams + if (distributed::test && my_rank == 1 && distributed::first_iteration == true) + distributed::test_related_viewgrams_slave(proj_data_info_sptr, symmetries_sptr); +#endif + // receive info if additive_binwise_correction_viewgrams are NULL + const bool add_bin_corr_viewgrams = distributed::receive_bool_value(BINWISE_CORRECTION_TAG, 0); + if (add_bin_corr_viewgrams) { + distributed::receive_and_construct_related_viewgrams(additive_binwise_correction_viewgrams, proj_data_info_sptr, + symmetries_sptr, 0); + } + + // receive info if mult_viewgrams_ptr are NULL + const bool mult_viewgrams = distributed::receive_bool_value(BINWISE_MULT_TAG, 0); + if (mult_viewgrams) { + distributed::receive_and_construct_related_viewgrams(mult_viewgrams_ptr, proj_data_info_sptr, symmetries_sptr, 0); + } + + // measured viewgrams + distributed::receive_and_construct_related_viewgrams(viewgrams, proj_data_info_sptr, symmetries_sptr, 0); + + // save Viewgrams to ProjDataInMemory object + if (cache_enabled) { + if (is_null_ptr(this->proj_data_ptr)) + this->proj_data_ptr.reset( + new ProjDataInMemory(this->exam_info_sptr, this->proj_data_info_sptr, /*init_with_0*/ false)); + + if (proj_data_ptr->set_related_viewgrams(*viewgrams) == Succeeded::no) + error("Slave %i: Storing viewgrams failed!\n", my_rank); + + if (add_bin_corr_viewgrams) { + if (is_null_ptr(binwise_correction)) + binwise_correction.reset( + new ProjDataInMemory(this->exam_info_sptr, this->proj_data_info_sptr, /*init_with_0*/ false)); + + if (binwise_correction->set_related_viewgrams(*additive_binwise_correction_viewgrams) == Succeeded::no) + error("Slave %i: Storing additive_binwise_correction_viewgrams failed!\n", my_rank); } - if (distributed::rpc_time) - stir::info(boost::format("Slave %1% used %2% seconds for PRC-processing.") - % my_rank % distributed::total_rpc_time_2); - } + + if (mult_viewgrams) { + if (is_null_ptr(mult_proj_data_sptr)) + mult_proj_data_sptr.reset( + new ProjDataInMemory(this->exam_info_sptr, this->proj_data_info_sptr, /*init_with_0*/ false)); + + if (mult_proj_data_sptr->set_related_viewgrams(*mult_viewgrams_ptr) == Succeeded::no) + error("Slave %i: Storing mult_viewgrams_ptr failed!\n", my_rank); + } + } + } else if (status.MPI_TAG == END_ITERATION_TAG) // the iteration is completed --> send results + { + // make reduction over computed output_images + distributed::first_iteration = false; + if (!is_null_ptr(output_image_ptr)) { + proj_pair_sptr->get_back_projector_sptr()->get_output(*output_image_ptr); + distributed::reduce_output_image(output_image_ptr, image_buffer_size, my_rank, 0); + } + // and log_likelihood + if (!is_null_ptr(log_likelihood_ptr)) { + double buffer = 0.0; + MPI_Reduce(log_likelihood_ptr, &buffer, /*size*/ 1, MPI_DOUBLE, MPI_SUM, /*destination*/ 0, MPI_COMM_WORLD); + delete log_likelihood_ptr; + } + + if (distributed::rpc_time) { + double send = distributed::total_rpc_time; + double receive; + MPI_Reduce(&send, &receive, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); + distributed::total_rpc_time = 0.0; + } + // get out of infinite while loop + break; + } else + error("Slave received unknown tag"); + + // measure time used for parallelized part + if (distributed::rpc_time) { + t.reset(); + t.start(); + } + + // call the actual calculation + RPC_process_related_viewgrams(this->proj_pair_sptr->get_forward_projector_sptr(), + this->proj_pair_sptr->get_back_projector_sptr(), viewgrams, count, count2, log_likelihood_ptr, + additive_binwise_correction_viewgrams, mult_viewgrams_ptr); + + if (distributed::rpc_time) { + t.stop(); + distributed::total_rpc_time = distributed::total_rpc_time + t.value(); + distributed::total_rpc_time_2 = distributed::total_rpc_time_2 + t.value(); + } + + int int_values[2]; + int_values[0] = count; + int_values[1] = count2; + // send count,count2 and ask for new work + distributed::send_int_values(int_values, 2, AVAILABLE_NOTIFICATION_TAG, 0); + + if (viewgrams != NULL) + delete viewgrams; + if (additive_binwise_correction_viewgrams != NULL) + delete additive_binwise_correction_viewgrams; + if (mult_viewgrams_ptr != NULL) + delete mult_viewgrams_ptr; + } + if (distributed::rpc_time) + stir::info(boost::format("Slave %1% used %2% seconds for PRC-processing.") % my_rank % distributed::total_rpc_time_2); } +} + +// instantiation +template class DistributedWorker>; - // instantiation - template class DistributedWorker >; - } // namespace stir diff --git a/src/recon_buildblock/FilterRootPrior.cxx b/src/recon_buildblock/FilterRootPrior.cxx index 97bb26499d..ee4dd1f5ba 100644 --- a/src/recon_buildblock/FilterRootPrior.cxx +++ b/src/recon_buildblock/FilterRootPrior.cxx @@ -19,10 +19,10 @@ /*! \file \ingroup priors - \brief implementation of the stir::FilterRootPrior class - + \brief implementation of the stir::FilterRootPrior class + \author Kris Thielemans - \author Sanida Mustafovic + \author Sanida Mustafovic */ #include "stir/recon_buildblock/FilterRootPrior.h" @@ -35,81 +35,66 @@ START_NAMESPACE_STIR template -FilterRootPrior::FilterRootPrior() -{ +FilterRootPrior::FilterRootPrior() { set_defaults(); } - template -FilterRootPrior:: -FilterRootPrior(shared_ptr >const& filter_sptr, float penalisation_factor_v) -: filter_ptr(filter_sptr) -{ +FilterRootPrior::FilterRootPrior(shared_ptr> const& filter_sptr, float penalisation_factor_v) + : filter_ptr(filter_sptr) { this->penalisation_factor = penalisation_factor_v; } - -template < class T> +template static inline int -sign (const T& x) -{ return x>=0 ? 1: -1;} +sign(const T& x) { + return x >= 0 ? 1 : -1; +} -template < class T> -static inline T // can't call this abs() as it overlaps with std::abs -my_abs(const T& x) -{ return x>=0 ? x: -x;} +template +static inline T // can't call this abs() as it overlaps with std::abs +my_abs(const T& x) { + return x >= 0 ? x : -x; +} /* A function that divides 2 floats while avoiding division by 0 by imposing an upper threshold - It essentially returns + It essentially returns sign(numerator)*sign(denominator)* min(my_abs(numerator/denominator), max) */ -template < class T> +template static inline T -quotient_with_max(const T numerator, const T denominator, const T max) -{ - assert(max>0); - return - my_abs(numerator)< max*my_abs(denominator) ? - numerator/denominator : - max * sign(numerator)*sign(denominator); +quotient_with_max(const T numerator, const T denominator, const T max) { + assert(max > 0); + return my_abs(numerator) < max * my_abs(denominator) ? numerator / denominator : max * sign(numerator) * sign(denominator); } template double -FilterRootPrior:: -compute_value(const DataT ¤t_estimate) -{ - static bool first_time=true; - if (first_time) - { - warning("FilterRootPrior:compute_value does not make sense. Just returning 0."); - first_time=false; - } +FilterRootPrior::compute_value(const DataT& current_estimate) { + static bool first_time = true; + if (first_time) { + warning("FilterRootPrior:compute_value does not make sense. Just returning 0."); + first_time = false; + } return 0.; } template -void -FilterRootPrior:: -compute_gradient(DataT& prior_gradient, - const DataT ¤t_image_estimate) -{ - assert( prior_gradient.get_index_range() == current_image_estimate.get_index_range()); - if (this->penalisation_factor==0 || is_null_ptr(filter_ptr)) - { +void +FilterRootPrior::compute_gradient(DataT& prior_gradient, const DataT& current_image_estimate) { + assert(prior_gradient.get_index_range() == current_image_estimate.get_index_range()); + if (this->penalisation_factor == 0 || is_null_ptr(filter_ptr)) { std::fill(prior_gradient.begin_all(), prior_gradient.end_all(), 0); return; } this->check(current_image_estimate); - // first store filtered image in prior_gradient - filter_ptr->apply(prior_gradient,current_image_estimate); + filter_ptr->apply(prior_gradient, current_image_estimate); - /* now set + /* now set prior_gradient = current_image_estimate/filtered_image - 1 = current_image_estimate/prior_gradient - 1 However, we need to avoid division by 0, as it might cause a NaN or an 'infinity'. @@ -118,79 +103,64 @@ compute_gradient(DataT& prior_gradient, So, instead we do prior_gradient = quotient_with_max(current_image_estimate,prior_gradient,1000) - 1 - The code below does this by using a full_iterator loop as we're missing expression templates + The code below does this by using a full_iterator loop as we're missing expression templates at the moment and I did not feel like making a function object just for this ... */ - typename DataT::full_iterator iter_through_prior_gradient = - prior_gradient.begin_all(); - typename DataT::const_full_iterator iter_through_current_image_estimate = - current_image_estimate.begin_all(); - while (iter_through_current_image_estimate!= current_image_estimate.end_all()) - { - *iter_through_prior_gradient= - this->penalisation_factor * - (quotient_with_max(*iter_through_current_image_estimate, - *iter_through_prior_gradient, - static_cast(1000)) - - 1); + typename DataT::full_iterator iter_through_prior_gradient = prior_gradient.begin_all(); + typename DataT::const_full_iterator iter_through_current_image_estimate = current_image_estimate.begin_all(); + while (iter_through_current_image_estimate != current_image_estimate.end_all()) { + *iter_through_prior_gradient = + this->penalisation_factor * (quotient_with_max(*iter_through_current_image_estimate, *iter_through_prior_gradient, + static_cast(1000)) - + 1); ++iter_through_prior_gradient; ++iter_through_current_image_estimate; } assert(iter_through_prior_gradient == prior_gradient.end_all()); } - - template -void -FilterRootPrior::initialise_keymap() -{ +void +FilterRootPrior::initialise_keymap() { base_type::initialise_keymap(); this->parser.add_start_key("FilterRootPrior Parameters"); - this->parser.add_parsing_key("Filter type", &filter_ptr); + this->parser.add_parsing_key("Filter type", &filter_ptr); this->parser.add_stop_key("END FilterRootPrior Parameters"); } - template void -FilterRootPrior::set_defaults() -{ +FilterRootPrior::set_defaults() { base_type::set_defaults(); filter_ptr.reset(); } template Succeeded -FilterRootPrior::set_up (shared_ptr const& target_sptr) -{ +FilterRootPrior::set_up(shared_ptr const& target_sptr) { base_type::set_up(target_sptr); return Succeeded::yes; } template -void FilterRootPrior::check(DataT const& current_image_estimate) const -{ +void +FilterRootPrior::check(DataT const& current_image_estimate) const { // Do base-class check base_type::check(current_image_estimate); } template -const char * const -FilterRootPrior:: -registered_name = - "FilterRootPrior"; +const char* const FilterRootPrior::registered_name = "FilterRootPrior"; -# ifdef _MSC_VER -// prevent warning message on reinstantiation, +#ifdef _MSC_VER +// prevent warning message on reinstantiation, // note that we get a linking error if we don't have the explicit instantiation below -# pragma warning(disable:4660) -# endif - +# pragma warning(disable : 4660) +#endif -template class FilterRootPrior >; -template class FilterRootPrior; +template class FilterRootPrior>; +template class FilterRootPrior; END_NAMESPACE_STIR diff --git a/src/recon_buildblock/ForwardProjectorByBin.cxx b/src/recon_buildblock/ForwardProjectorByBin.cxx index 0e8776154c..7202177453 100644 --- a/src/recon_buildblock/ForwardProjectorByBin.cxx +++ b/src/recon_buildblock/ForwardProjectorByBin.cxx @@ -49,97 +49,68 @@ START_NAMESPACE_STIR +ForwardProjectorByBin::ForwardProjectorByBin() : _already_set_up(false) { set_defaults(); } -ForwardProjectorByBin::ForwardProjectorByBin() - : _already_set_up(false) -{ - set_defaults(); -} - -ForwardProjectorByBin::~ForwardProjectorByBin() -{ -} +ForwardProjectorByBin::~ForwardProjectorByBin() {} void -ForwardProjectorByBin:: -set_defaults() -{ +ForwardProjectorByBin::set_defaults() { _pre_data_processor_sptr.reset(); } void -ForwardProjectorByBin:: -initialise_keymap() -{ +ForwardProjectorByBin::initialise_keymap() { parser.add_start_key("Forward Projector Parameters"); parser.add_stop_key("End Forward Projector Parameters"); parser.add_parsing_key("pre data processor", &_pre_data_processor_sptr); } void -ForwardProjectorByBin:: -set_up(const shared_ptr& proj_data_info_sptr, - const shared_ptr >& density_info_sptr) -{ +ForwardProjectorByBin::set_up(const shared_ptr& proj_data_info_sptr, + const shared_ptr>& density_info_sptr) { _already_set_up = true; _proj_data_info_sptr = proj_data_info_sptr->create_shared_clone(); _density_sptr.reset(density_info_sptr->clone()); } void -ForwardProjectorByBin:: -check(const ProjDataInfo& proj_data_info, const DiscretisedDensity<3,float>& density_info) const -{ +ForwardProjectorByBin::check(const ProjDataInfo& proj_data_info, const DiscretisedDensity<3, float>& density_info) const { if (!this->_already_set_up) error("ForwardProjectorByBin method called without calling set_up first."); if (!(*this->_proj_data_info_sptr >= proj_data_info)) - error(boost::format("ForwardProjectorByBin set-up with different geometry for projection data.\nSet_up was with\n%1%\nCalled with\n%2%") - % this->_proj_data_info_sptr->parameter_info() % proj_data_info.parameter_info()); - if (! this->_density_sptr->has_same_characteristics(density_info)) + error( + boost::format( + "ForwardProjectorByBin set-up with different geometry for projection data.\nSet_up was with\n%1%\nCalled with\n%2%") % + this->_proj_data_info_sptr->parameter_info() % proj_data_info.parameter_info()); + if (!this->_density_sptr->has_same_characteristics(density_info)) error("ForwardProjectorByBin set-up with different geometry for density or volume data."); } void -ForwardProjectorByBin::forward_project(ProjData& proj_data, - const DiscretisedDensity<3,float>& image, - int subset_num, int num_subsets, bool zero) -{ +ForwardProjectorByBin::forward_project(ProjData& proj_data, const DiscretisedDensity<3, float>& image, int subset_num, + int num_subsets, bool zero) { set_input(image); - forward_project(proj_data,subset_num,num_subsets,zero); + forward_project(proj_data, subset_num, num_subsets, zero); } #ifdef STIR_PROJECTORS_AS_V3 void -ForwardProjectorByBin::forward_project(RelatedViewgrams& viewgrams, - const DiscretisedDensity<3,float>& image) -{ - forward_project(viewgrams, image, - viewgrams.get_min_axial_pos_num(), - viewgrams.get_max_axial_pos_num(), - viewgrams.get_min_tangential_pos_num(), - viewgrams.get_max_tangential_pos_num()); +ForwardProjectorByBin::forward_project(RelatedViewgrams& viewgrams, const DiscretisedDensity<3, float>& image) { + forward_project(viewgrams, image, viewgrams.get_min_axial_pos_num(), viewgrams.get_max_axial_pos_num(), + viewgrams.get_min_tangential_pos_num(), viewgrams.get_max_tangential_pos_num()); } -void ForwardProjectorByBin::forward_project - (RelatedViewgrams& viewgrams, - const DiscretisedDensity<3,float>& image, - const int min_axial_pos_num, - const int max_axial_pos_num) -{ - forward_project(viewgrams, image, - min_axial_pos_num, - max_axial_pos_num, - viewgrams.get_min_tangential_pos_num(), - viewgrams.get_max_tangential_pos_num()); +void +ForwardProjectorByBin::forward_project(RelatedViewgrams& viewgrams, const DiscretisedDensity<3, float>& image, + const int min_axial_pos_num, const int max_axial_pos_num) { + forward_project(viewgrams, image, min_axial_pos_num, max_axial_pos_num, viewgrams.get_min_tangential_pos_num(), + viewgrams.get_max_tangential_pos_num()); } void -ForwardProjectorByBin:: -forward_project(RelatedViewgrams& viewgrams, - const DiscretisedDensity<3,float>& density, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) -{ - if (viewgrams.get_num_viewgrams()==0) +ForwardProjectorByBin::forward_project(RelatedViewgrams& viewgrams, const DiscretisedDensity<3, float>& density, + const int min_axial_pos_num, const int max_axial_pos_num, const int min_tangential_pos_num, + const int max_tangential_pos_num) { + if (viewgrams.get_num_viewgrams() == 0) return; check(*viewgrams.get_proj_data_info_sptr(), density); start_timers(); @@ -148,25 +119,18 @@ forward_project(RelatedViewgrams& viewgrams, { const ViewSegmentNumbers basic_vs = viewgrams.get_basic_view_segment_num(); - if (get_symmetries_used()->num_related_view_segment_numbers(basic_vs) != - viewgrams.get_num_viewgrams()) + if (get_symmetries_used()->num_related_view_segment_numbers(basic_vs) != viewgrams.get_num_viewgrams()) error("ForwardProjectByBin: forward_project called with incorrect related_viewgrams. Problem with symmetries!\n"); - for (RelatedViewgrams::const_iterator iter = viewgrams.begin(); - iter != viewgrams.end(); - ++iter) - { - ViewSegmentNumbers vs(iter->get_view_num(), iter->get_segment_num()); - get_symmetries_used()->find_basic_view_segment_numbers(vs); - if (vs != basic_vs) - error("ForwardProjectByBin: forward_project called with incorrect related_viewgrams. Problem with symmetries!\n"); + for (RelatedViewgrams::const_iterator iter = viewgrams.begin(); iter != viewgrams.end(); ++iter) { + ViewSegmentNumbers vs(iter->get_view_num(), iter->get_segment_num()); + get_symmetries_used()->find_basic_view_segment_numbers(vs); + if (vs != basic_vs) + error("ForwardProjectByBin: forward_project called with incorrect related_viewgrams. Problem with symmetries!\n"); } } - actual_forward_project(viewgrams, density, - min_axial_pos_num, - max_axial_pos_num, - min_tangential_pos_num, - max_tangential_pos_num); + actual_forward_project(viewgrams, density, min_axial_pos_num, max_axial_pos_num, min_tangential_pos_num, + max_tangential_pos_num); stop_timers(); } #endif @@ -174,93 +138,71 @@ forward_project(RelatedViewgrams& viewgrams, // The following are repetition of above, where the DiscretisedDensity has already been set with set_input() // -------------------------------------------------------------------------------------------------------------------- // void -ForwardProjectorByBin::forward_project(ProjData& proj_data, - int subset_num, int num_subsets, bool zero) -{ - if (_density_sptr->get_exam_info().imaging_modality.is_unknown() - || proj_data.get_exam_info().imaging_modality.is_unknown()) - warning("forward_project. Imaging modality unknown for either the image or the projection data or both.\n" - "Going ahead anyway."); - else if (_density_sptr->get_exam_info().imaging_modality != - proj_data.get_exam_info().imaging_modality) - error("forward_project: Imaging modality should be the same for the image and the projection data"); - if (subset_num < 0) - error(boost::format("forward_project: wrong subset number %1%") % subset_num); - if (subset_num > num_subsets - 1) - error(boost::format("forward_project: wrong subset number %1% (must be less than the number of subsets %2%)") - % subset_num % num_subsets); - if (zero && num_subsets > 1) - proj_data.fill(0.0); - // this->set_up(proj_data_ptr->get_proj_data_info_sptr()->clone(), -// image_sptr); +ForwardProjectorByBin::forward_project(ProjData& proj_data, int subset_num, int num_subsets, bool zero) { + if (_density_sptr->get_exam_info().imaging_modality.is_unknown() || proj_data.get_exam_info().imaging_modality.is_unknown()) + warning("forward_project. Imaging modality unknown for either the image or the projection data or both.\n" + "Going ahead anyway."); + else if (_density_sptr->get_exam_info().imaging_modality != proj_data.get_exam_info().imaging_modality) + error("forward_project: Imaging modality should be the same for the image and the projection data"); + if (subset_num < 0) + error(boost::format("forward_project: wrong subset number %1%") % subset_num); + if (subset_num > num_subsets - 1) + error(boost::format("forward_project: wrong subset number %1% (must be less than the number of subsets %2%)") % subset_num % + num_subsets); + if (zero && num_subsets > 1) + proj_data.fill(0.0); + // this->set_up(proj_data_ptr->get_proj_data_info_sptr()->clone(), + // image_sptr); check(*proj_data.get_proj_data_info_sptr(), *_density_sptr); - shared_ptr - symmetries_sptr(this->get_symmetries_used()->clone()); + shared_ptr symmetries_sptr(this->get_symmetries_used()->clone()); - const std::vector vs_nums_to_process = - detail::find_basic_vs_nums_in_subset(*proj_data.get_proj_data_info_sptr(), *symmetries_sptr, - proj_data.get_min_segment_num(), proj_data.get_max_segment_num(), - subset_num, num_subsets); + const std::vector vs_nums_to_process = detail::find_basic_vs_nums_in_subset( + *proj_data.get_proj_data_info_sptr(), *symmetries_sptr, proj_data.get_min_segment_num(), proj_data.get_max_segment_num(), + subset_num, num_subsets); #ifdef STIR_OPENMP -#pragma omp parallel for shared(proj_data, symmetries_sptr) schedule(runtime) +# pragma omp parallel for shared(proj_data, symmetries_sptr) schedule(runtime) #endif - // note: older versions of openmp need an int as loop - for (int i=0; i(vs_nums_to_process.size()); ++i) - { - const ViewSegmentNumbers vs=vs_nums_to_process[i]; - for (int k=proj_data.get_proj_data_info_sptr()->get_min_tof_pos_num(); - k<=proj_data.get_proj_data_info_sptr()->get_max_tof_pos_num(); - ++k) - { - if (proj_data.get_proj_data_info_sptr()->is_tof_data()) - info(boost::format("Processing view %1% of segment %2% of TOF bin %3%") % vs.view_num() % vs.segment_num() % k); - else - info(boost::format("Processing view %1% of segment %2%") % vs.view_num() % vs.segment_num()); - RelatedViewgrams viewgrams = - proj_data.get_empty_related_viewgrams(vs, symmetries_sptr, false, k); - forward_project(viewgrams); + // note: older versions of openmp need an int as loop + for (int i = 0; i < static_cast(vs_nums_to_process.size()); ++i) { + const ViewSegmentNumbers vs = vs_nums_to_process[i]; + for (int k = proj_data.get_proj_data_info_sptr()->get_min_tof_pos_num(); + k <= proj_data.get_proj_data_info_sptr()->get_max_tof_pos_num(); ++k) { + if (proj_data.get_proj_data_info_sptr()->is_tof_data()) + info(boost::format("Processing view %1% of segment %2% of TOF bin %3%") % vs.view_num() % vs.segment_num() % k); + else + info(boost::format("Processing view %1% of segment %2%") % vs.view_num() % vs.segment_num()); + RelatedViewgrams viewgrams = proj_data.get_empty_related_viewgrams(vs, symmetries_sptr, false, k); + forward_project(viewgrams); #ifdef STIR_OPENMP -#pragma omp critical (FORWARDPROJ_SETVIEWGRAMS) +# pragma omp critical(FORWARDPROJ_SETVIEWGRAMS) #endif - { - if (!(proj_data.set_related_viewgrams(viewgrams) == Succeeded::yes)) - error("Error set_related_viewgrams in forward projecting"); - } - } + { + if (!(proj_data.set_related_viewgrams(viewgrams) == Succeeded::yes)) + error("Error set_related_viewgrams in forward projecting"); + } } - + } } void -ForwardProjectorByBin::forward_project(RelatedViewgrams& viewgrams) -{ - forward_project(viewgrams, - viewgrams.get_min_axial_pos_num(), - viewgrams.get_max_axial_pos_num(), - viewgrams.get_min_tangential_pos_num(), - viewgrams.get_max_tangential_pos_num()); +ForwardProjectorByBin::forward_project(RelatedViewgrams& viewgrams) { + forward_project(viewgrams, viewgrams.get_min_axial_pos_num(), viewgrams.get_max_axial_pos_num(), + viewgrams.get_min_tangential_pos_num(), viewgrams.get_max_tangential_pos_num()); } -void ForwardProjectorByBin::forward_project - (RelatedViewgrams& viewgrams, - const int min_axial_pos_num, - const int max_axial_pos_num) -{ - forward_project(viewgrams, - min_axial_pos_num, - max_axial_pos_num, - viewgrams.get_min_tangential_pos_num(), - viewgrams.get_max_tangential_pos_num()); +void +ForwardProjectorByBin::forward_project(RelatedViewgrams& viewgrams, const int min_axial_pos_num, + const int max_axial_pos_num) { + forward_project(viewgrams, min_axial_pos_num, max_axial_pos_num, viewgrams.get_min_tangential_pos_num(), + viewgrams.get_max_tangential_pos_num()); } void -ForwardProjectorByBin:: -forward_project(RelatedViewgrams& viewgrams, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) -{ - if (viewgrams.get_num_viewgrams()==0) +ForwardProjectorByBin::forward_project(RelatedViewgrams& viewgrams, const int min_axial_pos_num, + const int max_axial_pos_num, const int min_tangential_pos_num, + const int max_tangential_pos_num) { + if (viewgrams.get_num_viewgrams() == 0) return; check(*viewgrams.get_proj_data_info_sptr(), *_density_sptr); start_timers(); @@ -269,69 +211,49 @@ forward_project(RelatedViewgrams& viewgrams, { const ViewSegmentNumbers basic_vs = viewgrams.get_basic_view_segment_num(); - if (get_symmetries_used()->num_related_view_segment_numbers(basic_vs) != - viewgrams.get_num_viewgrams()) + if (get_symmetries_used()->num_related_view_segment_numbers(basic_vs) != viewgrams.get_num_viewgrams()) error("ForwardProjectByBin: forward_project called with incorrect related_viewgrams. Problem with symmetries!\n"); - for (RelatedViewgrams::const_iterator iter = viewgrams.begin(); - iter != viewgrams.end(); - ++iter) - { - ViewSegmentNumbers vs(iter->get_view_num(), iter->get_segment_num()); - get_symmetries_used()->find_basic_view_segment_numbers(vs); - if (vs != basic_vs) - error("ForwardProjectByBin: forward_project called with incorrect related_viewgrams. Problem with symmetries!\n"); + for (RelatedViewgrams::const_iterator iter = viewgrams.begin(); iter != viewgrams.end(); ++iter) { + ViewSegmentNumbers vs(iter->get_view_num(), iter->get_segment_num()); + get_symmetries_used()->find_basic_view_segment_numbers(vs); + if (vs != basic_vs) + error("ForwardProjectByBin: forward_project called with incorrect related_viewgrams. Problem with symmetries!\n"); } } - actual_forward_project(viewgrams, - min_axial_pos_num, - max_axial_pos_num, - min_tangential_pos_num, - max_tangential_pos_num); + actual_forward_project(viewgrams, min_axial_pos_num, max_axial_pos_num, min_tangential_pos_num, max_tangential_pos_num); stop_timers(); } void -ForwardProjectorByBin:: -actual_forward_project(RelatedViewgrams&, - const DiscretisedDensity<3,float>&, - const int, const int, - const int, const int) -{ - error("ForwardProjectorByBin::actual_forward_project() This is deprecated and should not be used."); +ForwardProjectorByBin::actual_forward_project(RelatedViewgrams&, const DiscretisedDensity<3, float>&, const int, const int, + const int, const int) { + error("ForwardProjectorByBin::actual_forward_project() This is deprecated and should not be used."); } void -ForwardProjectorByBin:: -actual_forward_project(RelatedViewgrams& viewgrams, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) -{ - actual_forward_project(viewgrams,*_density_sptr, - min_axial_pos_num, max_axial_pos_num, - min_tangential_pos_num, max_tangential_pos_num); +ForwardProjectorByBin::actual_forward_project(RelatedViewgrams& viewgrams, const int min_axial_pos_num, + const int max_axial_pos_num, const int min_tangential_pos_num, + const int max_tangential_pos_num) { + actual_forward_project(viewgrams, *_density_sptr, min_axial_pos_num, max_axial_pos_num, min_tangential_pos_num, + max_tangential_pos_num); } - void -ForwardProjectorByBin:: -set_input(const DiscretisedDensity<3,float> & density) -{ - _density_sptr.reset(density.clone()); - - // If a pre-forward-projection data processor has been set, apply it. - if (!is_null_ptr(_pre_data_processor_sptr)) { - Succeeded success = _pre_data_processor_sptr->apply(*_density_sptr); - if (success != Succeeded::yes) - throw std::runtime_error("ForwardProjectorByBin::set_input(). Pre-forward-projection data processor failed."); - } +ForwardProjectorByBin::set_input(const DiscretisedDensity<3, float>& density) { + _density_sptr.reset(density.clone()); + + // If a pre-forward-projection data processor has been set, apply it. + if (!is_null_ptr(_pre_data_processor_sptr)) { + Succeeded success = _pre_data_processor_sptr->apply(*_density_sptr); + if (success != Succeeded::yes) + throw std::runtime_error("ForwardProjectorByBin::set_input(). Pre-forward-projection data processor failed."); + } } void -ForwardProjectorByBin:: -set_pre_data_processor(shared_ptr > > pre_data_processor_sptr) -{ - _pre_data_processor_sptr = pre_data_processor_sptr; +ForwardProjectorByBin::set_pre_data_processor(shared_ptr>> pre_data_processor_sptr) { + _pre_data_processor_sptr = pre_data_processor_sptr; } END_NAMESPACE_STIR diff --git a/src/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.cxx b/src/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.cxx index c72241ed32..be9b04dcd2 100644 --- a/src/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.cxx +++ b/src/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.cxx @@ -5,8 +5,8 @@ \file \ingroup projection - \brief implementations for stir::ForwardProjectorByBinUsingProjMatrixByBin - + \brief implementations for stir::ForwardProjectorByBinUsingProjMatrixByBin + \author Kris Thielemans \author PARAPET project @@ -29,7 +29,6 @@ See STIR/LICENSE.txt for details */ - #include "stir/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.h" #include "stir/Viewgram.h" #include "stir/RelatedViewgrams.h" @@ -48,23 +47,16 @@ using std::list; START_NAMESPACE_STIR ////////////////////////////////////////////////////////// -const char * const -ForwardProjectorByBinUsingProjMatrixByBin::registered_name = - "Matrix"; - +const char* const ForwardProjectorByBinUsingProjMatrixByBin::registered_name = "Matrix"; void -ForwardProjectorByBinUsingProjMatrixByBin:: -set_defaults() -{ +ForwardProjectorByBinUsingProjMatrixByBin::set_defaults() { this->proj_matrix_ptr.reset(); ForwardProjectorByBin::set_defaults(); } void -ForwardProjectorByBinUsingProjMatrixByBin:: -initialise_keymap() -{ +ForwardProjectorByBinUsingProjMatrixByBin::initialise_keymap() { parser.add_start_key("Forward Projector Using Matrix Parameters"); parser.add_stop_key("End Forward Projector Using Matrix Parameters"); parser.add_parsing_key("matrix type", &proj_matrix_ptr); @@ -72,164 +64,133 @@ initialise_keymap() } bool -ForwardProjectorByBinUsingProjMatrixByBin:: -post_processing() -{ - //if (ForwardProjectorByBin::post_processing() == true) +ForwardProjectorByBinUsingProjMatrixByBin::post_processing() { + // if (ForwardProjectorByBin::post_processing() == true) // return true; - if (is_null_ptr(proj_matrix_ptr)) - { + if (is_null_ptr(proj_matrix_ptr)) { warning("ForwardProjectorByBinUsingProjMatrixByBin: matrix not set.\n"); return true; } return false; } -ForwardProjectorByBinUsingProjMatrixByBin:: -ForwardProjectorByBinUsingProjMatrixByBin() -{ - set_defaults(); -} +ForwardProjectorByBinUsingProjMatrixByBin::ForwardProjectorByBinUsingProjMatrixByBin() { set_defaults(); } -ForwardProjectorByBinUsingProjMatrixByBin:: -ForwardProjectorByBinUsingProjMatrixByBin( - const shared_ptr& proj_matrix_ptr - ) - : proj_matrix_ptr(proj_matrix_ptr) -{ +ForwardProjectorByBinUsingProjMatrixByBin::ForwardProjectorByBinUsingProjMatrixByBin( + const shared_ptr& proj_matrix_ptr) + : proj_matrix_ptr(proj_matrix_ptr) { assert(!is_null_ptr(proj_matrix_ptr)); } void -ForwardProjectorByBinUsingProjMatrixByBin:: -set_up(const shared_ptr& proj_data_info_ptr, - const shared_ptr >& image_info_ptr) -{ +ForwardProjectorByBinUsingProjMatrixByBin::set_up(const shared_ptr& proj_data_info_ptr, + const shared_ptr>& image_info_ptr) { ForwardProjectorByBin::set_up(proj_data_info_ptr, image_info_ptr); proj_matrix_ptr->set_up(proj_data_info_ptr, image_info_ptr); } -const DataSymmetriesForViewSegmentNumbers * -ForwardProjectorByBinUsingProjMatrixByBin::get_symmetries_used() const -{ +const DataSymmetriesForViewSegmentNumbers* +ForwardProjectorByBinUsingProjMatrixByBin::get_symmetries_used() const { if (!this->_already_set_up) error("ForwardProjectorByBin method called without calling set_up first."); return proj_matrix_ptr->get_symmetries_ptr(); } -void -ForwardProjectorByBinUsingProjMatrixByBin:: - actual_forward_project(RelatedViewgrams& viewgrams, - const DiscretisedDensity<3,float>& image, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) -{ +void +ForwardProjectorByBinUsingProjMatrixByBin::actual_forward_project(RelatedViewgrams& viewgrams, + const DiscretisedDensity<3, float>& image, + const int min_axial_pos_num, const int max_axial_pos_num, + const int min_tangential_pos_num, + const int max_tangential_pos_num) { if (proj_matrix_ptr->is_cache_enabled()/* && !proj_matrix_ptr->does_cache_store_only_basic_bins()*/) { - // straightforward version which relies on ProjMatrixByBin to sort out all + // straightforward version which relies on ProjMatrixByBin to sort out all // symmetries // would be slow if there's no caching at all, but is very fast if everything is cached ProjMatrixElemsForOneBin proj_matrix_row; RelatedViewgrams::iterator r_viewgrams_iter = viewgrams.begin(); - - while( r_viewgrams_iter!=viewgrams.end()) - { + + while (r_viewgrams_iter != viewgrams.end()) { Viewgram& viewgram = *r_viewgrams_iter; const int view_num = viewgram.get_view_num(); const int segment_num = viewgram.get_segment_num(); const int timing_num = viewgram.get_timing_pos_num(); - - for ( int tang_pos = min_tangential_pos_num ;tang_pos <= max_tangential_pos_num ;++tang_pos) - for ( int ax_pos = min_axial_pos_num; ax_pos <= max_axial_pos_num ;++ax_pos) - { + + for (int tang_pos = min_tangential_pos_num; tang_pos <= max_tangential_pos_num; ++tang_pos) + for (int ax_pos = min_axial_pos_num; ax_pos <= max_axial_pos_num; ++ax_pos) { Bin bin(segment_num, view_num, ax_pos, tang_pos, timing_num, 0.f); proj_matrix_ptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, bin); - proj_matrix_row.forward_project(bin,image); + proj_matrix_row.forward_project(bin, image); viewgram[ax_pos][tang_pos] = bin.get_bin_value(); } - ++r_viewgrams_iter; - } - } - else - { - error("Need to do TOF stuff here"); + ++r_viewgrams_iter; + } + } else { + error("Need to do TOF stuff here"); // Complicated version which handles the symmetries explicitly. - // Faster when no caching is performed, about just as fast when there is caching, + // Faster when no caching is performed, about just as fast when there is caching, // but of only basic bins. - + ProjMatrixElemsForOneBin proj_matrix_row; ProjMatrixElemsForOneBin proj_matrix_row_copy; - const DataSymmetriesForBins* symmetries = proj_matrix_ptr->get_symmetries_ptr(); - - Array<2,int> - already_processed(IndexRange2D(min_axial_pos_num, max_axial_pos_num, - min_tangential_pos_num, max_tangential_pos_num)); - - for ( int tang_pos = min_tangential_pos_num ;tang_pos <= max_tangential_pos_num ;++tang_pos) - for ( int ax_pos = min_axial_pos_num; ax_pos <= max_axial_pos_num ;++ax_pos) - { + const DataSymmetriesForBins* symmetries = proj_matrix_ptr->get_symmetries_ptr(); + + Array<2, int> already_processed( + IndexRange2D(min_axial_pos_num, max_axial_pos_num, min_tangential_pos_num, max_tangential_pos_num)); + + for (int tang_pos = min_tangential_pos_num; tang_pos <= max_tangential_pos_num; ++tang_pos) + for (int ax_pos = min_axial_pos_num; ax_pos <= max_axial_pos_num; ++ax_pos) { if (already_processed[ax_pos][tang_pos]) - continue; - - Bin basic_bin(viewgrams.get_basic_segment_num(),viewgrams.get_basic_view_num(),ax_pos,tang_pos, - viewgrams.get_basic_timing_pos_num()); + continue; + + Bin basic_bin(viewgrams.get_basic_segment_num(), viewgrams.get_basic_view_num(), ax_pos, tang_pos, + viewgrams.get_basic_timing_pos_num()); symmetries->find_basic_bin(basic_bin); proj_matrix_ptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, basic_bin); - + vector r_ax_poss; - symmetries->get_related_bins_factorised(r_ax_poss,basic_bin, - min_axial_pos_num, max_axial_pos_num, + symmetries->get_related_bins_factorised(r_ax_poss, basic_bin, min_axial_pos_num, max_axial_pos_num, min_tangential_pos_num, max_tangential_pos_num); - + for ( #ifndef STIR_NO_NAMESPACES - std:: + std:: #endif - vector::iterator r_ax_poss_iter = r_ax_poss.begin(); - r_ax_poss_iter != r_ax_poss.end(); - ++r_ax_poss_iter) - { + vector::iterator r_ax_poss_iter = r_ax_poss.begin(); + r_ax_poss_iter != r_ax_poss.end(); ++r_ax_poss_iter) { const int axial_pos_tmp = (*r_ax_poss_iter)[1]; const int tang_pos_tmp = (*r_ax_poss_iter)[2]; - + // symmetries might take the ranges out of what the user wants - if ( !(min_axial_pos_num <= axial_pos_tmp && axial_pos_tmp <= max_axial_pos_num && - min_tangential_pos_num <=tang_pos_tmp && tang_pos_tmp <= max_tangential_pos_num)) + if (!(min_axial_pos_num <= axial_pos_tmp && axial_pos_tmp <= max_axial_pos_num && + min_tangential_pos_num <= tang_pos_tmp && tang_pos_tmp <= max_tangential_pos_num)) continue; - + already_processed[axial_pos_tmp][tang_pos_tmp] = 1; - - - for (RelatedViewgrams::iterator viewgram_iter = viewgrams.begin(); - viewgram_iter != viewgrams.end(); - ++viewgram_iter) - { + + for (RelatedViewgrams::iterator viewgram_iter = viewgrams.begin(); viewgram_iter != viewgrams.end(); + ++viewgram_iter) { Viewgram& viewgram = *viewgram_iter; proj_matrix_row_copy = proj_matrix_row; - Bin bin(viewgram_iter->get_segment_num(), - viewgram_iter->get_view_num(), - axial_pos_tmp, - tang_pos_tmp, - viewgram_iter->get_timing_pos_num()); - - unique_ptr symm_op_ptr = - symmetries->find_symmetry_operation_from_basic_bin(bin); + Bin bin(viewgram_iter->get_segment_num(), viewgram_iter->get_view_num(), axial_pos_tmp, tang_pos_tmp, + viewgram_iter->get_timing_pos_num()); + + unique_ptr symm_op_ptr = symmetries->find_symmetry_operation_from_basic_bin(bin); assert(bin == basic_bin); - + symm_op_ptr->transform_proj_matrix_elems_for_one_bin(proj_matrix_row_copy); - proj_matrix_row_copy.forward_project(bin,image); - + proj_matrix_row_copy.forward_project(bin, image); + viewgram[axial_pos_tmp][tang_pos_tmp] = bin.get_bin_value(); } - } - } - assert(already_processed.sum() == ( - (max_axial_pos_num - min_axial_pos_num + 1) * - (max_tangential_pos_num - min_tangential_pos_num + 1))); + } + } + assert(already_processed.sum() == + ((max_axial_pos_num - min_axial_pos_num + 1) * (max_tangential_pos_num - min_tangential_pos_num + 1))); } } diff --git a/src/recon_buildblock/ForwardProjectorByBinUsingRayTracing.cxx b/src/recon_buildblock/ForwardProjectorByBinUsingRayTracing.cxx index 9e1991090f..ff615d95b0 100644 --- a/src/recon_buildblock/ForwardProjectorByBinUsingRayTracing.cxx +++ b/src/recon_buildblock/ForwardProjectorByBinUsingRayTracing.cxx @@ -30,9 +30,9 @@ See STIR/LICENSE.txt for details */ -/* +/* History: - * Matthias Egger: C version + * Matthias Egger: C version * PARAPET project : C++ version @@ -76,73 +76,55 @@ using std::max; START_NAMESPACE_STIR -const char * const -ForwardProjectorByBinUsingRayTracing::registered_name = - "Ray Tracing"; - +const char* const ForwardProjectorByBinUsingRayTracing::registered_name = "Ray Tracing"; void -ForwardProjectorByBinUsingRayTracing:: -set_defaults() -{ +ForwardProjectorByBinUsingRayTracing::set_defaults() { restrict_to_cylindrical_FOV = true; } void -ForwardProjectorByBinUsingRayTracing:: -initialise_keymap() -{ +ForwardProjectorByBinUsingRayTracing::initialise_keymap() { parser.add_start_key("Forward Projector Using Ray Tracing Parameters"); parser.add_key("restrict to cylindrical FOV", &restrict_to_cylindrical_FOV); parser.add_stop_key("End Forward Projector Using Ray Tracing Parameters"); } -ForwardProjectorByBinUsingRayTracing:: - ForwardProjectorByBinUsingRayTracing() -{ - set_defaults(); -} +ForwardProjectorByBinUsingRayTracing::ForwardProjectorByBinUsingRayTracing() { set_defaults(); } -ForwardProjectorByBinUsingRayTracing:: - ForwardProjectorByBinUsingRayTracing( - const shared_ptr& proj_data_info_sptr, - const shared_ptr >& image_info_ptr) -{ +ForwardProjectorByBinUsingRayTracing::ForwardProjectorByBinUsingRayTracing( + const shared_ptr& proj_data_info_sptr, + const shared_ptr>& image_info_ptr) { set_defaults(); set_up(proj_data_info_sptr, image_info_ptr); } void -ForwardProjectorByBinUsingRayTracing:: -set_up(const shared_ptr& proj_data_info_sptr, - const shared_ptr >& image_info_ptr) -{ +ForwardProjectorByBinUsingRayTracing::set_up(const shared_ptr& proj_data_info_sptr, + const shared_ptr>& image_info_ptr) { ForwardProjectorByBin::set_up(proj_data_info_sptr, image_info_ptr); - - if (proj_data_info_sptr->get_num_views()%2 != 0) - { - error("The on-the-fly Ray tracing forward projector cannot handle data with odd number of views. Use another projector. Sorry."); - } + + if (proj_data_info_sptr->get_num_views() % 2 != 0) { + error("The on-the-fly Ray tracing forward projector cannot handle data with odd number of views. Use another projector. " + "Sorry."); + } symmetries_ptr.reset(new DataSymmetriesForBins_PET_CartesianGrid(proj_data_info_sptr, image_info_ptr)); // check if data are according to what we can handle - const VoxelsOnCartesianGrid * vox_image_info_ptr = - dynamic_cast*> (image_info_ptr.get()); + const VoxelsOnCartesianGrid* vox_image_info_ptr = + dynamic_cast*>(image_info_ptr.get()); if (vox_image_info_ptr == NULL) error("ForwardProjectorByBinUsingRayTracing initialised with a wrong type of DiscretisedDensity\n"); const CartesianCoordinate3D voxel_size = vox_image_info_ptr->get_voxel_size(); - const float sampling_distance_of_adjacent_LORs_xy = - proj_data_info_sptr->get_sampling_in_s(Bin(0,0,0,0)); - + const float sampling_distance_of_adjacent_LORs_xy = proj_data_info_sptr->get_sampling_in_s(Bin(0, 0, 0, 0)); // z_origin_in_planes should be an integer - const float z_origin_in_planes = - image_info_ptr->get_origin().z()/voxel_size.z(); + const float z_origin_in_planes = image_info_ptr->get_origin().z() / voxel_size.z(); if (fabs(round(z_origin_in_planes) - z_origin_in_planes) > 1.E-4) error("ForwardProjectorByBinUsingRayTracing: the shift in the " "z-direction of the origin (which is %g) should be a multiple of the plane " @@ -150,616 +132,462 @@ set_up(const shared_ptr& proj_data_info_sptr, image_info_ptr->get_origin().z(), voxel_size.z()); // num_planes_per_axial_pos should currently be an integer - for (int segment_num = proj_data_info_sptr->get_min_segment_num(); - segment_num <= proj_data_info_sptr->get_max_segment_num(); - ++segment_num) - { - const float num_planes_per_axial_pos = - symmetries_ptr->get_num_planes_per_axial_pos(segment_num); - if (fabs(round(num_planes_per_axial_pos) - num_planes_per_axial_pos) > 1.E-4) - error("ForwardProjectorByBinUsingRayTracing: the number of image planes " - "per axial_pos (which is %g for segment %d) should be an integer\n", - num_planes_per_axial_pos, segment_num); + for (int segment_num = proj_data_info_sptr->get_min_segment_num(); segment_num <= proj_data_info_sptr->get_max_segment_num(); + ++segment_num) { + const float num_planes_per_axial_pos = symmetries_ptr->get_num_planes_per_axial_pos(segment_num); + if (fabs(round(num_planes_per_axial_pos) - num_planes_per_axial_pos) > 1.E-4) + error("ForwardProjectorByBinUsingRayTracing: the number of image planes " + "per axial_pos (which is %g for segment %d) should be an integer\n", + num_planes_per_axial_pos, segment_num); } - // KT 20/06/2001 converted from assert to a warning - if(sampling_distance_of_adjacent_LORs_xy > voxel_size.x() + 1.E-3 || - sampling_distance_of_adjacent_LORs_xy > voxel_size.y() + 1.E-3) - warning("ForwardProjectorByBinUsingRayTracing assumes that pixel size (in x,y) " - "is greater than or equal to the bin size.\n" - "As this is NOT the case with the current data, the projector will " - "completely miss some voxels for some (or all) views."); + if (sampling_distance_of_adjacent_LORs_xy > voxel_size.x() + 1.E-3 || + sampling_distance_of_adjacent_LORs_xy > voxel_size.y() + 1.E-3) + warning("ForwardProjectorByBinUsingRayTracing assumes that pixel size (in x,y) " + "is greater than or equal to the bin size.\n" + "As this is NOT the case with the current data, the projector will " + "completely miss some voxels for some (or all) views."); } - -const DataSymmetriesForViewSegmentNumbers * -ForwardProjectorByBinUsingRayTracing::get_symmetries_used() const -{ +const DataSymmetriesForViewSegmentNumbers* +ForwardProjectorByBinUsingRayTracing::get_symmetries_used() const { if (!this->_already_set_up) error("ForwardProjectorByBin method called without calling set_up first."); - return symmetries_ptr.get(); + return symmetries_ptr.get(); } -void -ForwardProjectorByBinUsingRayTracing:: -actual_forward_project(RelatedViewgrams& viewgrams, - const DiscretisedDensity<3,float>& density, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) +void +ForwardProjectorByBinUsingRayTracing::actual_forward_project(RelatedViewgrams& viewgrams, + const DiscretisedDensity<3, float>& density, + const int min_axial_pos_num, const int max_axial_pos_num, + const int min_tangential_pos_num, const int max_tangential_pos_num) { // this will throw an exception when the cast does not work - const VoxelsOnCartesianGrid& image = - dynamic_cast&>(density); + const VoxelsOnCartesianGrid& image = dynamic_cast&>(density); const int num_views = viewgrams.get_proj_data_info_sptr()->get_num_views(); - if (viewgrams.get_basic_segment_num() == 0) - { - if (viewgrams.get_num_viewgrams() == 1) - { - Viewgram & pos_view = *viewgrams.begin(); - forward_project_view_2D( - pos_view, - image, - min_axial_pos_num, max_axial_pos_num, - min_tangential_pos_num, max_tangential_pos_num); + if (viewgrams.get_basic_segment_num() == 0) { + if (viewgrams.get_num_viewgrams() == 1) { + Viewgram& pos_view = *viewgrams.begin(); + forward_project_view_2D(pos_view, image, min_axial_pos_num, max_axial_pos_num, min_tangential_pos_num, + max_tangential_pos_num); - } - else - if (viewgrams.get_num_viewgrams() == 2) - { + } else if (viewgrams.get_num_viewgrams() == 2) { assert(viewgrams.get_basic_view_num() >= 0); - assert(viewgrams.get_basic_view_num() < num_views/2); - Viewgram & pos_view = *viewgrams.begin(); - if ((viewgrams.begin()+1)->get_view_num() == pos_view.get_view_num() + num_views/2) - { - Viewgram & pos_plus90 =*(viewgrams.begin()+1); - if (pos_plus90.get_view_num() != pos_view.get_view_num() + num_views/2) - error("ForwardProjectorUsingRayTracing: error in symmetries. Check 2D case with 2 viewgrams\n"); - - forward_project_view_plus_90_2D( - pos_view, pos_plus90, - image, - min_axial_pos_num, max_axial_pos_num, - min_tangential_pos_num, max_tangential_pos_num); - } - else - { - Viewgram & pos_min180 =*(viewgrams.begin()+1); - if (pos_min180.get_view_num() != num_views - pos_view.get_view_num()) - error("ForwardProjectorUsingRayTracing: error in symmetries. Check 2D case with 2 viewgrams\n"); - - forward_project_view_min_180_2D( - pos_view, pos_min180, - image, - min_axial_pos_num, max_axial_pos_num, - min_tangential_pos_num, max_tangential_pos_num); - } - } - else - { + assert(viewgrams.get_basic_view_num() < num_views / 2); + Viewgram& pos_view = *viewgrams.begin(); + if ((viewgrams.begin() + 1)->get_view_num() == pos_view.get_view_num() + num_views / 2) { + Viewgram& pos_plus90 = *(viewgrams.begin() + 1); + if (pos_plus90.get_view_num() != pos_view.get_view_num() + num_views / 2) + error("ForwardProjectorUsingRayTracing: error in symmetries. Check 2D case with 2 viewgrams\n"); + + forward_project_view_plus_90_2D(pos_view, pos_plus90, image, min_axial_pos_num, max_axial_pos_num, min_tangential_pos_num, + max_tangential_pos_num); + } else { + Viewgram& pos_min180 = *(viewgrams.begin() + 1); + if (pos_min180.get_view_num() != num_views - pos_view.get_view_num()) + error("ForwardProjectorUsingRayTracing: error in symmetries. Check 2D case with 2 viewgrams\n"); + + forward_project_view_min_180_2D(pos_view, pos_min180, image, min_axial_pos_num, max_axial_pos_num, min_tangential_pos_num, + max_tangential_pos_num); + } + } else { assert(viewgrams.get_basic_view_num() > 0); - assert(viewgrams.get_basic_view_num() < num_views/4); - Viewgram & pos_view = *(viewgrams.begin()); - Viewgram & pos_plus90 =*(viewgrams.begin()+1); - Viewgram & pos_min180 =*(viewgrams.begin()+2); - Viewgram & pos_min90 =*(viewgrams.begin()+3); - - if (pos_plus90.get_view_num() != pos_view.get_view_num() + num_views/2) - error("ForwardProjectorUsingRayTracing: error in symmetries. Check 2D case with 4 viewgrams\n"); + assert(viewgrams.get_basic_view_num() < num_views / 4); + Viewgram& pos_view = *(viewgrams.begin()); + Viewgram& pos_plus90 = *(viewgrams.begin() + 1); + Viewgram& pos_min180 = *(viewgrams.begin() + 2); + Viewgram& pos_min90 = *(viewgrams.begin() + 3); + + if (pos_plus90.get_view_num() != pos_view.get_view_num() + num_views / 2) + error("ForwardProjectorUsingRayTracing: error in symmetries. Check 2D case with 4 viewgrams\n"); if (pos_min180.get_view_num() != num_views - pos_view.get_view_num()) - error("ForwardProjectorUsingRayTracing: error in symmetries. Check 2D case with 4 viewgrams\n"); - if (pos_min90.get_view_num() != num_views/2 - pos_view.get_view_num()) - error("ForwardProjectorUsingRayTracing: error in symmetries. Check 2D case with 4 viewgrams\n"); - - forward_project_all_symmetries_2D( - pos_view, pos_plus90, - pos_min180, pos_min90, - image, - min_axial_pos_num, max_axial_pos_num, - min_tangential_pos_num, max_tangential_pos_num); + error("ForwardProjectorUsingRayTracing: error in symmetries. Check 2D case with 4 viewgrams\n"); + if (pos_min90.get_view_num() != num_views / 2 - pos_view.get_view_num()) + error("ForwardProjectorUsingRayTracing: error in symmetries. Check 2D case with 4 viewgrams\n"); + forward_project_all_symmetries_2D(pos_view, pos_plus90, pos_min180, pos_min90, image, min_axial_pos_num, max_axial_pos_num, + min_tangential_pos_num, max_tangential_pos_num); } - } - else - { + } else { // segment symmetry - if (viewgrams.get_num_viewgrams() == 2) - { - Viewgram & pos_view = *(viewgrams.begin()+0); - Viewgram & neg_view =*(viewgrams.begin()+1); - - if (pos_view.get_view_num() != neg_view.get_view_num()) - error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 2 viewgrams\n"); - if (neg_view.get_segment_num() != - pos_view.get_segment_num()) - error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 2 viewgrams\n"); - - forward_project_delta( - pos_view, neg_view, - image, - min_axial_pos_num, max_axial_pos_num, - min_tangential_pos_num, max_tangential_pos_num); - } - else if (viewgrams.get_num_viewgrams() == 4) - { - Viewgram & pos_view = *(viewgrams.begin()+0); - Viewgram & neg_view =*(viewgrams.begin()+1); - if (neg_view.get_view_num() != pos_view.get_view_num()) - error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 2 viewgrams\n"); - if (neg_view.get_segment_num() != - pos_view.get_segment_num()) - error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 4 viewgrams\n"); - - if ((viewgrams.begin()+2)->get_view_num() == pos_view.get_view_num() + num_views/2) - { - Viewgram & pos_plus90 =*(viewgrams.begin()+2); - Viewgram & neg_plus90 =*(viewgrams.begin()+3); - if (pos_plus90.get_view_num() != pos_view.get_view_num() + num_views/2) - error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 4 viewgrams\n"); - if (neg_plus90.get_view_num() != neg_view.get_view_num() + num_views/2) - error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 4 viewgrams\n"); - if (pos_plus90.get_segment_num() != pos_view.get_segment_num()) - error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 4 viewgrams\n"); - if (neg_plus90.get_segment_num() != - pos_view.get_segment_num()) - error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 4 viewgrams\n"); - forward_project_view_plus_90_and_delta( - pos_view, neg_view, pos_plus90, neg_plus90, - image, - min_axial_pos_num, max_axial_pos_num, - min_tangential_pos_num, max_tangential_pos_num); - } - else - { - Viewgram & pos_min180 =*(viewgrams.begin()+2); - Viewgram & neg_min180 =*(viewgrams.begin()+3); - if (pos_min180.get_view_num() != num_views - pos_view.get_view_num()) - error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 4 viewgrams\n"); - if (neg_min180.get_view_num() != num_views - neg_view.get_view_num()) - error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 4 viewgrams\n"); - if (pos_min180.get_segment_num() != pos_view.get_segment_num()) - error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 4 viewgrams\n"); - if (neg_min180.get_segment_num() != - pos_view.get_segment_num()) - error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 4 viewgrams\n"); - - forward_project_view_min_180_and_delta( - pos_view, neg_view, pos_min180, neg_min180, - image, - min_axial_pos_num, max_axial_pos_num, - min_tangential_pos_num, max_tangential_pos_num); - } - } - else if (viewgrams.get_num_viewgrams() == 8) - { - assert(viewgrams.get_basic_view_num() > 0); - assert(viewgrams.get_basic_view_num() < num_views/4); - Viewgram & pos_view = *(viewgrams.begin()+0); - Viewgram & neg_view =*(viewgrams.begin()+1); - Viewgram & pos_plus90 =*(viewgrams.begin()+2); - Viewgram & neg_plus90 =*(viewgrams.begin()+3); - Viewgram & pos_min180 =*(viewgrams.begin()+4); - Viewgram & neg_min180=*(viewgrams.begin()+5); - Viewgram & pos_min90=*(viewgrams.begin()+6); - Viewgram & neg_min90 =*(viewgrams.begin()+7); - - if (pos_plus90.get_view_num() != pos_view.get_view_num() + num_views/2) - error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 8 viewgrams\n"); - if (pos_min180.get_view_num() != num_views - pos_view.get_view_num()) - error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 8 viewgrams\n"); - if (pos_min90.get_view_num() != num_views/2 - pos_view.get_view_num()) - error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 8 viewgrams\n"); - - if (neg_view.get_view_num() != pos_view.get_view_num()) - error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 8 viewgrams\n"); - if (neg_min90.get_view_num() != num_views/2 - neg_view.get_view_num()) - error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 8 viewgrams\n"); - if (neg_plus90.get_view_num() != neg_view.get_view_num() + num_views/2) - error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 8 viewgrams\n"); - if (neg_min180.get_view_num() != num_views - neg_view.get_view_num()) - error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 8 viewgrams\n"); - - if (pos_plus90.get_segment_num() != pos_view.get_segment_num()) - error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 8 viewgrams\n"); - if (pos_min90.get_segment_num() != pos_view.get_segment_num()) - error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 8 viewgrams\n"); - if (pos_min180.get_segment_num() != pos_view.get_segment_num()) - error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 8 viewgrams\n"); - - if (neg_view.get_segment_num() != - pos_view.get_segment_num()) - error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 8 viewgrams\n"); - if (neg_plus90.get_segment_num() != - pos_view.get_segment_num()) - error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 8 viewgrams\n"); - if (neg_min90.get_segment_num() != - pos_view.get_segment_num()) - error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 8 viewgrams\n"); - if (neg_min180.get_segment_num() != - pos_view.get_segment_num()) - error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 8 viewgrams\n"); - - forward_project_all_symmetries( - pos_view, neg_view, pos_plus90, neg_plus90, - pos_min180, neg_min180, pos_min90, neg_min90, - image, - min_axial_pos_num, max_axial_pos_num, - min_tangential_pos_num, max_tangential_pos_num); - - } - else // other number of viewgrams - { - error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case\n"); + if (viewgrams.get_num_viewgrams() == 2) { + Viewgram& pos_view = *(viewgrams.begin() + 0); + Viewgram& neg_view = *(viewgrams.begin() + 1); + + if (pos_view.get_view_num() != neg_view.get_view_num()) + error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 2 viewgrams\n"); + if (neg_view.get_segment_num() != -pos_view.get_segment_num()) + error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 2 viewgrams\n"); + + forward_project_delta(pos_view, neg_view, image, min_axial_pos_num, max_axial_pos_num, min_tangential_pos_num, + max_tangential_pos_num); + } else if (viewgrams.get_num_viewgrams() == 4) { + Viewgram& pos_view = *(viewgrams.begin() + 0); + Viewgram& neg_view = *(viewgrams.begin() + 1); + if (neg_view.get_view_num() != pos_view.get_view_num()) + error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 2 viewgrams\n"); + if (neg_view.get_segment_num() != -pos_view.get_segment_num()) + error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 4 viewgrams\n"); + + if ((viewgrams.begin() + 2)->get_view_num() == pos_view.get_view_num() + num_views / 2) { + Viewgram& pos_plus90 = *(viewgrams.begin() + 2); + Viewgram& neg_plus90 = *(viewgrams.begin() + 3); + if (pos_plus90.get_view_num() != pos_view.get_view_num() + num_views / 2) + error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 4 viewgrams\n"); + if (neg_plus90.get_view_num() != neg_view.get_view_num() + num_views / 2) + error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 4 viewgrams\n"); + if (pos_plus90.get_segment_num() != pos_view.get_segment_num()) + error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 4 viewgrams\n"); + if (neg_plus90.get_segment_num() != -pos_view.get_segment_num()) + error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 4 viewgrams\n"); + forward_project_view_plus_90_and_delta(pos_view, neg_view, pos_plus90, neg_plus90, image, min_axial_pos_num, + max_axial_pos_num, min_tangential_pos_num, max_tangential_pos_num); + } else { + Viewgram& pos_min180 = *(viewgrams.begin() + 2); + Viewgram& neg_min180 = *(viewgrams.begin() + 3); + if (pos_min180.get_view_num() != num_views - pos_view.get_view_num()) + error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 4 viewgrams\n"); + if (neg_min180.get_view_num() != num_views - neg_view.get_view_num()) + error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 4 viewgrams\n"); + if (pos_min180.get_segment_num() != pos_view.get_segment_num()) + error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 4 viewgrams\n"); + if (neg_min180.get_segment_num() != -pos_view.get_segment_num()) + error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 4 viewgrams\n"); + + forward_project_view_min_180_and_delta(pos_view, neg_view, pos_min180, neg_min180, image, min_axial_pos_num, + max_axial_pos_num, min_tangential_pos_num, max_tangential_pos_num); } + } else if (viewgrams.get_num_viewgrams() == 8) { + assert(viewgrams.get_basic_view_num() > 0); + assert(viewgrams.get_basic_view_num() < num_views / 4); + Viewgram& pos_view = *(viewgrams.begin() + 0); + Viewgram& neg_view = *(viewgrams.begin() + 1); + Viewgram& pos_plus90 = *(viewgrams.begin() + 2); + Viewgram& neg_plus90 = *(viewgrams.begin() + 3); + Viewgram& pos_min180 = *(viewgrams.begin() + 4); + Viewgram& neg_min180 = *(viewgrams.begin() + 5); + Viewgram& pos_min90 = *(viewgrams.begin() + 6); + Viewgram& neg_min90 = *(viewgrams.begin() + 7); + + if (pos_plus90.get_view_num() != pos_view.get_view_num() + num_views / 2) + error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 8 viewgrams\n"); + if (pos_min180.get_view_num() != num_views - pos_view.get_view_num()) + error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 8 viewgrams\n"); + if (pos_min90.get_view_num() != num_views / 2 - pos_view.get_view_num()) + error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 8 viewgrams\n"); + + if (neg_view.get_view_num() != pos_view.get_view_num()) + error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 8 viewgrams\n"); + if (neg_min90.get_view_num() != num_views / 2 - neg_view.get_view_num()) + error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 8 viewgrams\n"); + if (neg_plus90.get_view_num() != neg_view.get_view_num() + num_views / 2) + error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 8 viewgrams\n"); + if (neg_min180.get_view_num() != num_views - neg_view.get_view_num()) + error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 8 viewgrams\n"); + + if (pos_plus90.get_segment_num() != pos_view.get_segment_num()) + error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 8 viewgrams\n"); + if (pos_min90.get_segment_num() != pos_view.get_segment_num()) + error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 8 viewgrams\n"); + if (pos_min180.get_segment_num() != pos_view.get_segment_num()) + error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 8 viewgrams\n"); + + if (neg_view.get_segment_num() != -pos_view.get_segment_num()) + error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 8 viewgrams\n"); + if (neg_plus90.get_segment_num() != -pos_view.get_segment_num()) + error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 8 viewgrams\n"); + if (neg_min90.get_segment_num() != -pos_view.get_segment_num()) + error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 8 viewgrams\n"); + if (neg_min180.get_segment_num() != -pos_view.get_segment_num()) + error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case with 8 viewgrams\n"); + + forward_project_all_symmetries(pos_view, neg_view, pos_plus90, neg_plus90, pos_min180, neg_min180, pos_min90, neg_min90, + image, min_axial_pos_num, max_axial_pos_num, min_tangential_pos_num, max_tangential_pos_num); + + } else // other number of viewgrams + { + error("ForwardProjectorUsingRayTracing: error in symmetries. Check 3D case\n"); + } } // oblique case - - } - - /* The version which uses all possible symmetries. Here 0<=view < num_views/4 (= 45 degrees) */ -void -ForwardProjectorByBinUsingRayTracing:: -forward_project_all_symmetries( - Viewgram & pos_view, - Viewgram & neg_view, - Viewgram & pos_plus90, - Viewgram & neg_plus90, - Viewgram & pos_min180, - Viewgram & neg_min180, - Viewgram & pos_min90, - Viewgram & neg_min90, - const VoxelsOnCartesianGrid& image, - const int min_ax_pos_num, const int max_ax_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) const -{ +void +ForwardProjectorByBinUsingRayTracing::forward_project_all_symmetries( + Viewgram& pos_view, Viewgram& neg_view, Viewgram& pos_plus90, Viewgram& neg_plus90, + Viewgram& pos_min180, Viewgram& neg_min180, Viewgram& pos_min90, Viewgram& neg_min90, + const VoxelsOnCartesianGrid& image, const int min_ax_pos_num, const int max_ax_pos_num, + const int min_tangential_pos_num, const int max_tangential_pos_num) const { // KT 20/06/2001 should now work for non-arccorrected data as well const shared_ptr proj_data_info_sptr = - dynamic_pointer_cast - (pos_view.get_proj_data_info_sptr()); + dynamic_pointer_cast(pos_view.get_proj_data_info_sptr()); if (is_null_ptr(proj_data_info_sptr)) error("ForwardProjectorByBinUsingRayTracing::forward_project called with wrong type of ProjDataInfo\n"); - - const int nviews = pos_view.get_proj_data_info_sptr()->get_num_views(); - + + const int nviews = pos_view.get_proj_data_info_sptr()->get_num_views(); + const int segment_num = pos_view.get_segment_num(); - //const int timing_pos_num = pos_view.get_timing_pos_num(); + // const int timing_pos_num = pos_view.get_timing_pos_num(); const float delta = proj_data_info_sptr->get_average_ring_difference(segment_num); const int view = pos_view.get_view_num(); assert(delta > 0); assert(view >= 0); - /* removed assertions which would break the temporary 2,4 parameter forward_project + /* removed assertions which would break the temporary 2,4 parameter forward_project assert(view <= view90); - + assert(pos_plus90.get_view_num() == nviews / 2 + pos_view.get_view_num()); assert(pos_min90.get_view_num() == nviews / 2 - pos_view.get_view_num()); assert(pos_min180.get_view_num() == nviews - pos_view.get_view_num()); - + assert(neg_view.get_view_num() == pos_view.get_view_num()); assert(neg_plus90.get_view_num() == pos_plus90.get_view_num()); assert(neg_min90.get_view_num() == pos_min90.get_view_num()); assert(neg_min180.get_view_num() == pos_min180.get_view_num()); */ - //assert(image.get_min_z() == 0); + // assert(image.get_min_z() == 0); + + assert(delta == -proj_data_info_sptr->get_average_ring_difference(neg_view.get_segment_num())); - assert(delta == - -proj_data_info_sptr->get_average_ring_difference(neg_view.get_segment_num())); - // KT 21/05/98 added const where possible // TODO C value depends whether you are in Double or not, // If double C==2 => do 2*ax_pos0 and 2*ax_pos0+1 - const int C=1; - - int D, tang_pos_num; + const int C = 1; + + int D, tang_pos_num; int ax_pos0, my_ax_pos0; const float R = proj_data_info_sptr->get_ring_radius(); - + // a variable which will be used in the loops over tang_pos_num to get s_in_mm - Bin bin(pos_view.get_segment_num(), pos_view.get_view_num(),min_ax_pos_num,0,pos_view.get_timing_pos_num()); + Bin bin(pos_view.get_segment_num(), pos_view.get_view_num(), min_ax_pos_num, 0, pos_view.get_timing_pos_num()); - // KT 20/06/2001 rewrote using get_phi + // KT 20/06/2001 rewrote using get_phi const float cphi = cos(proj_data_info_sptr->get_phi(bin)); const float sphi = sin(proj_data_info_sptr->get_phi(bin)); // KT 20/06/2001 write using symmetries member // find correspondence between ring coordinates and image coordinates: // z = num_planes_per_axial_pos * ring + axial_pos_to_z_offset - const int num_planes_per_axial_pos = - round(symmetries_ptr->get_num_planes_per_axial_pos(segment_num)); - const float axial_pos_to_z_offset = - symmetries_ptr->get_axial_pos_to_z_offset(segment_num); - + const int num_planes_per_axial_pos = round(symmetries_ptr->get_num_planes_per_axial_pos(segment_num)); + const float axial_pos_to_z_offset = symmetries_ptr->get_axial_pos_to_z_offset(segment_num); + // KT 20/06/2001 parameters to find 'basic' range of tang_pos_num - const int max_abs_tangential_pos_num = - max(max_tangential_pos_num, -min_tangential_pos_num); - const int min_abs_tangential_pos_num = - max_tangential_pos_num<0 ? - -max_tangential_pos_num - : (min_tangential_pos_num>0 ? - min_tangential_pos_num - : 0 ); + const int max_abs_tangential_pos_num = max(max_tangential_pos_num, -min_tangential_pos_num); + const int min_abs_tangential_pos_num = + max_tangential_pos_num < 0 ? -max_tangential_pos_num : (min_tangential_pos_num > 0 ? min_tangential_pos_num : 0); // in the loop, the case tang_pos_num==0 will be treated separately (because it's self-symmetric) - const int min_tang_pos_num_in_loop = - min_abs_tangential_pos_num==0 ? 1 : min_abs_tangential_pos_num; - + const int min_tang_pos_num_in_loop = min_abs_tangential_pos_num == 0 ? 1 : min_abs_tangential_pos_num; + start_timers(); - - Array <4,float> Projall(IndexRange4D(min_ax_pos_num, max_ax_pos_num, 0, 1, 0, 1, 0, 3)); - // KT 21/05/98 removed as now automatically zero - // Projall.fill(0); + Array<4, float> Projall(IndexRange4D(min_ax_pos_num, max_ax_pos_num, 0, 1, 0, 1, 0, 3)); + // KT 21/05/98 removed as now automatically zero + // Projall.fill(0); - // In the case that axial sampling for the projection data = 2*voxel_size.z() - // we draw 2 LORs, at -1/4 and +1/4 of the centre of the bin - // If we don't do this, we will miss voxels in the forward projection step. + // In the case that axial sampling for the projection data = 2*voxel_size.z() + // we draw 2 LORs, at -1/4 and +1/4 of the centre of the bin + // If we don't do this, we will miss voxels in the forward projection step. - // When the axial sampling is the same as the voxel size, we take just - // 1 LOR. - float offset_start = -.25F; - float offset_incr = .5F; + // When the axial sampling is the same as the voxel size, we take just + // 1 LOR. + float offset_start = -.25F; + float offset_incr = .5F; - int num_lors_per_virtual_ring = 2; - - if (num_planes_per_axial_pos == 1) - { - offset_start = 0; - offset_incr=1; - num_lors_per_virtual_ring = 1; - } + int num_lors_per_virtual_ring = 2; + if (num_planes_per_axial_pos == 1) { + offset_start = 0; + offset_incr = 1; + num_lors_per_virtual_ring = 1; + } - for (float offset = offset_start; offset < 0.3; offset += offset_incr) - { - if (view == 0 || 4*view == nviews ) { /* phi=0 or 45 */ - for (D = 0; D < C; D++) { - if (min_abs_tangential_pos_num==0) - { - /* Here tang_pos_num=0 and phi=0 or 45*/ + for (float offset = offset_start; offset < 0.3; offset += offset_incr) { + if (view == 0 || 4 * view == nviews) { /* phi=0 or 45 */ + for (D = 0; D < C; D++) { + if (min_abs_tangential_pos_num == 0) { + /* Here tang_pos_num=0 and phi=0 or 45*/ - if (proj_Siddon + if (proj_Siddon #ifndef STIR_SIDDON_NO_TEMPLATE - < 2>( + <2>( #else - (2, + (2, #endif - Projall, image, proj_data_info_sptr, cphi, sphi, - delta + D, 0, R,min_ax_pos_num, max_ax_pos_num, - offset, num_planes_per_axial_pos, axial_pos_to_z_offset, - 1.F / num_lors_per_virtual_ring, - restrict_to_cylindrical_FOV)) - for (ax_pos0 = min_ax_pos_num; ax_pos0 <= max_ax_pos_num; ax_pos0++) { - my_ax_pos0 = C * ax_pos0 + D; - - pos_view[my_ax_pos0][0] += Projall[ax_pos0][0][0][0]; - pos_plus90[my_ax_pos0][0] += Projall[ax_pos0][0][0][2]; - neg_view[my_ax_pos0][0] += Projall[ax_pos0][1][0][0]; - neg_plus90[my_ax_pos0][0] += Projall[ax_pos0][1][0][2]; - } - } - /* Now tang_pos_num!=0 and phi=0 or 45 */ - for (tang_pos_num = min_tang_pos_num_in_loop; tang_pos_num <= max_abs_tangential_pos_num; tang_pos_num++) - { - bin.tangential_pos_num() = tang_pos_num; - const float s_in_mm = proj_data_info_sptr->get_s(bin); - if (proj_Siddon + Projall, image, proj_data_info_sptr, cphi, sphi, delta + D, 0, R, min_ax_pos_num, max_ax_pos_num, offset, + num_planes_per_axial_pos, axial_pos_to_z_offset, 1.F / num_lors_per_virtual_ring, restrict_to_cylindrical_FOV)) + for (ax_pos0 = min_ax_pos_num; ax_pos0 <= max_ax_pos_num; ax_pos0++) { + my_ax_pos0 = C * ax_pos0 + D; + + pos_view[my_ax_pos0][0] += Projall[ax_pos0][0][0][0]; + pos_plus90[my_ax_pos0][0] += Projall[ax_pos0][0][0][2]; + neg_view[my_ax_pos0][0] += Projall[ax_pos0][1][0][0]; + neg_plus90[my_ax_pos0][0] += Projall[ax_pos0][1][0][2]; + } + } + /* Now tang_pos_num!=0 and phi=0 or 45 */ + for (tang_pos_num = min_tang_pos_num_in_loop; tang_pos_num <= max_abs_tangential_pos_num; tang_pos_num++) { + bin.tangential_pos_num() = tang_pos_num; + const float s_in_mm = proj_data_info_sptr->get_s(bin); + if (proj_Siddon #ifndef STIR_SIDDON_NO_TEMPLATE - <1>( + <1>( #else - (1, + (1, #endif - Projall, image, proj_data_info_sptr, cphi, sphi, - delta + D, s_in_mm, R,min_ax_pos_num, max_ax_pos_num, - offset, num_planes_per_axial_pos, axial_pos_to_z_offset, - 1.F/num_lors_per_virtual_ring, - restrict_to_cylindrical_FOV)) - for (ax_pos0 = min_ax_pos_num; ax_pos0 <= max_ax_pos_num; ax_pos0++) { - my_ax_pos0 = C * ax_pos0 + D; - if (tang_pos_num<=max_tangential_pos_num) - { - pos_view[my_ax_pos0][tang_pos_num] += Projall[ax_pos0][0][0][0]; - pos_plus90[my_ax_pos0][tang_pos_num] += Projall[ax_pos0][0][0][2]; - neg_view[my_ax_pos0][tang_pos_num] += Projall[ax_pos0][1][0][0]; - neg_plus90[my_ax_pos0][tang_pos_num] += Projall[ax_pos0][1][0][2]; - } - if (-tang_pos_num>=min_tangential_pos_num) - { - pos_view[my_ax_pos0][-tang_pos_num] += Projall[ax_pos0][0][1][0]; - pos_plus90[my_ax_pos0][-tang_pos_num] += Projall[ax_pos0][0][1][2]; - neg_view[my_ax_pos0][-tang_pos_num] += Projall[ax_pos0][1][1][0]; - neg_plus90[my_ax_pos0][-tang_pos_num] += Projall[ax_pos0][1][1][2]; - } - } - } + Projall, image, proj_data_info_sptr, cphi, sphi, delta + D, s_in_mm, R, min_ax_pos_num, max_ax_pos_num, offset, + num_planes_per_axial_pos, axial_pos_to_z_offset, 1.F / num_lors_per_virtual_ring, restrict_to_cylindrical_FOV)) + for (ax_pos0 = min_ax_pos_num; ax_pos0 <= max_ax_pos_num; ax_pos0++) { + my_ax_pos0 = C * ax_pos0 + D; + if (tang_pos_num <= max_tangential_pos_num) { + pos_view[my_ax_pos0][tang_pos_num] += Projall[ax_pos0][0][0][0]; + pos_plus90[my_ax_pos0][tang_pos_num] += Projall[ax_pos0][0][0][2]; + neg_view[my_ax_pos0][tang_pos_num] += Projall[ax_pos0][1][0][0]; + neg_plus90[my_ax_pos0][tang_pos_num] += Projall[ax_pos0][1][0][2]; + } + if (-tang_pos_num >= min_tangential_pos_num) { + pos_view[my_ax_pos0][-tang_pos_num] += Projall[ax_pos0][0][1][0]; + pos_plus90[my_ax_pos0][-tang_pos_num] += Projall[ax_pos0][0][1][2]; + neg_view[my_ax_pos0][-tang_pos_num] += Projall[ax_pos0][1][1][0]; + neg_plus90[my_ax_pos0][-tang_pos_num] += Projall[ax_pos0][1][1][2]; + } } - } else { - - - for (D = 0; D < C; D++) { - if (min_abs_tangential_pos_num==0) - { - /* Here tang_pos_num==0 and phi!=k*45 */ - if (proj_Siddon + } + } + } else { + + for (D = 0; D < C; D++) { + if (min_abs_tangential_pos_num == 0) { + /* Here tang_pos_num==0 and phi!=k*45 */ + if (proj_Siddon #ifndef STIR_SIDDON_NO_TEMPLATE - <4>( + <4>( #else - (4, + (4, #endif - Projall, image, proj_data_info_sptr, cphi, sphi, - delta + D, 0, R,min_ax_pos_num, max_ax_pos_num, - offset, num_planes_per_axial_pos, axial_pos_to_z_offset , - 1.F/num_lors_per_virtual_ring, - restrict_to_cylindrical_FOV)) - for (ax_pos0 = min_ax_pos_num; ax_pos0 <= max_ax_pos_num; ax_pos0++) { - my_ax_pos0 = C * ax_pos0 + D; - pos_view[my_ax_pos0][0] += Projall[ax_pos0][0][0][0]; - pos_min90[my_ax_pos0][0] += Projall[ax_pos0][0][0][1]; - pos_plus90[my_ax_pos0][0] += Projall[ax_pos0][0][0][2]; - pos_min180[my_ax_pos0][0] += Projall[ax_pos0][0][0][3]; - neg_view[my_ax_pos0][0] += Projall[ax_pos0][1][0][0]; - neg_min90[my_ax_pos0][0] += Projall[ax_pos0][1][0][1]; - neg_plus90[my_ax_pos0][0] += Projall[ax_pos0][1][0][2]; - neg_min180[my_ax_pos0][0] += Projall[ax_pos0][1][0][3]; - } - } - - - /* Here tang_pos_num!=0 and phi!=k*45. */ - for (tang_pos_num = min_tang_pos_num_in_loop; tang_pos_num <= max_abs_tangential_pos_num; tang_pos_num++) { - bin.tangential_pos_num() = tang_pos_num; - const float s_in_mm = proj_data_info_sptr->get_s(bin); - - if (proj_Siddon + Projall, image, proj_data_info_sptr, cphi, sphi, delta + D, 0, R, min_ax_pos_num, max_ax_pos_num, offset, + num_planes_per_axial_pos, axial_pos_to_z_offset, 1.F / num_lors_per_virtual_ring, restrict_to_cylindrical_FOV)) + for (ax_pos0 = min_ax_pos_num; ax_pos0 <= max_ax_pos_num; ax_pos0++) { + my_ax_pos0 = C * ax_pos0 + D; + pos_view[my_ax_pos0][0] += Projall[ax_pos0][0][0][0]; + pos_min90[my_ax_pos0][0] += Projall[ax_pos0][0][0][1]; + pos_plus90[my_ax_pos0][0] += Projall[ax_pos0][0][0][2]; + pos_min180[my_ax_pos0][0] += Projall[ax_pos0][0][0][3]; + neg_view[my_ax_pos0][0] += Projall[ax_pos0][1][0][0]; + neg_min90[my_ax_pos0][0] += Projall[ax_pos0][1][0][1]; + neg_plus90[my_ax_pos0][0] += Projall[ax_pos0][1][0][2]; + neg_min180[my_ax_pos0][0] += Projall[ax_pos0][1][0][3]; + } + } + + /* Here tang_pos_num!=0 and phi!=k*45. */ + for (tang_pos_num = min_tang_pos_num_in_loop; tang_pos_num <= max_abs_tangential_pos_num; tang_pos_num++) { + bin.tangential_pos_num() = tang_pos_num; + const float s_in_mm = proj_data_info_sptr->get_s(bin); + + if (proj_Siddon #ifndef STIR_SIDDON_NO_TEMPLATE - <3>( + <3>( #else - (3, + (3, #endif - Projall, image, proj_data_info_sptr, cphi, sphi, - delta + D, s_in_mm, R,min_ax_pos_num, max_ax_pos_num, - offset, num_planes_per_axial_pos, axial_pos_to_z_offset , - 1.F/num_lors_per_virtual_ring, - restrict_to_cylindrical_FOV)) - for (ax_pos0 = min_ax_pos_num; ax_pos0 <= max_ax_pos_num; ax_pos0++) - { - my_ax_pos0 = C * ax_pos0 + D; - if (tang_pos_num<=max_tangential_pos_num) - { - pos_view[my_ax_pos0][tang_pos_num] += Projall[ax_pos0][0][0][0]; - pos_min90[my_ax_pos0][tang_pos_num] += Projall[ax_pos0][0][0][1]; - pos_plus90[my_ax_pos0][tang_pos_num] += Projall[ax_pos0][0][0][2]; - pos_min180[my_ax_pos0][tang_pos_num] += Projall[ax_pos0][0][0][3]; - neg_view[my_ax_pos0][tang_pos_num] += Projall[ax_pos0][1][0][0]; - neg_min90[my_ax_pos0][tang_pos_num] += Projall[ax_pos0][1][0][1]; - neg_plus90[my_ax_pos0][tang_pos_num] += Projall[ax_pos0][1][0][2]; - neg_min180[my_ax_pos0][tang_pos_num] += Projall[ax_pos0][1][0][3]; - } - if (-tang_pos_num>=min_tangential_pos_num) - { - pos_view[my_ax_pos0][-tang_pos_num] += Projall[ax_pos0][0][1][0]; - pos_min90[my_ax_pos0][-tang_pos_num] += Projall[ax_pos0][0][1][1]; - pos_plus90[my_ax_pos0][-tang_pos_num] += Projall[ax_pos0][0][1][2]; - pos_min180[my_ax_pos0][-tang_pos_num] += Projall[ax_pos0][0][1][3]; - neg_view[my_ax_pos0][-tang_pos_num] += Projall[ax_pos0][1][1][0]; - neg_min90[my_ax_pos0][-tang_pos_num] += Projall[ax_pos0][1][1][1]; - neg_plus90[my_ax_pos0][-tang_pos_num] += Projall[ax_pos0][1][1][2]; - neg_min180[my_ax_pos0][-tang_pos_num] += Projall[ax_pos0][1][1][3]; - } - } - } + Projall, image, proj_data_info_sptr, cphi, sphi, delta + D, s_in_mm, R, min_ax_pos_num, max_ax_pos_num, offset, + num_planes_per_axial_pos, axial_pos_to_z_offset, 1.F / num_lors_per_virtual_ring, restrict_to_cylindrical_FOV)) + for (ax_pos0 = min_ax_pos_num; ax_pos0 <= max_ax_pos_num; ax_pos0++) { + my_ax_pos0 = C * ax_pos0 + D; + if (tang_pos_num <= max_tangential_pos_num) { + pos_view[my_ax_pos0][tang_pos_num] += Projall[ax_pos0][0][0][0]; + pos_min90[my_ax_pos0][tang_pos_num] += Projall[ax_pos0][0][0][1]; + pos_plus90[my_ax_pos0][tang_pos_num] += Projall[ax_pos0][0][0][2]; + pos_min180[my_ax_pos0][tang_pos_num] += Projall[ax_pos0][0][0][3]; + neg_view[my_ax_pos0][tang_pos_num] += Projall[ax_pos0][1][0][0]; + neg_min90[my_ax_pos0][tang_pos_num] += Projall[ax_pos0][1][0][1]; + neg_plus90[my_ax_pos0][tang_pos_num] += Projall[ax_pos0][1][0][2]; + neg_min180[my_ax_pos0][tang_pos_num] += Projall[ax_pos0][1][0][3]; + } + if (-tang_pos_num >= min_tangential_pos_num) { + pos_view[my_ax_pos0][-tang_pos_num] += Projall[ax_pos0][0][1][0]; + pos_min90[my_ax_pos0][-tang_pos_num] += Projall[ax_pos0][0][1][1]; + pos_plus90[my_ax_pos0][-tang_pos_num] += Projall[ax_pos0][0][1][2]; + pos_min180[my_ax_pos0][-tang_pos_num] += Projall[ax_pos0][0][1][3]; + neg_view[my_ax_pos0][-tang_pos_num] += Projall[ax_pos0][1][1][0]; + neg_min90[my_ax_pos0][-tang_pos_num] += Projall[ax_pos0][1][1][1]; + neg_plus90[my_ax_pos0][-tang_pos_num] += Projall[ax_pos0][1][1][2]; + neg_min180[my_ax_pos0][-tang_pos_num] += Projall[ax_pos0][1][1][3]; + } } + } + } + } // end of } else { + } // end of test for offset loop - }// end of } else { - }// end of test for offset loop - - stop_timers(); - + stop_timers(); } - /* This function projects 2 viewgrams related by segment symmetry. */ -void -ForwardProjectorByBinUsingRayTracing:: - forward_project_delta(Viewgram & pos_view, - Viewgram & neg_view, - const VoxelsOnCartesianGrid & image, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) const -{ +void +ForwardProjectorByBinUsingRayTracing::forward_project_delta(Viewgram& pos_view, Viewgram& neg_view, + const VoxelsOnCartesianGrid& image, + const int min_axial_pos_num, const int max_axial_pos_num, + const int min_tangential_pos_num, + const int max_tangential_pos_num) const { assert(pos_view.get_segment_num() > 0); assert(pos_view.get_view_num() >= 0); assert(pos_view.get_view_num() < pos_view.get_proj_data_info_sptr()->get_num_views()); Viewgram dummy = pos_view; - - forward_project_all_symmetries( - pos_view, - neg_view, - dummy, - dummy, - dummy, - dummy, - dummy, - dummy, - image, - min_axial_pos_num, max_axial_pos_num, - min_tangential_pos_num, max_tangential_pos_num); + forward_project_all_symmetries(pos_view, neg_view, dummy, dummy, dummy, dummy, dummy, dummy, image, min_axial_pos_num, + max_axial_pos_num, min_tangential_pos_num, max_tangential_pos_num); } - /* This function projects 4 viewgrams related by symmetry. Here 0<=view < num_views/2 (= 90 degrees) */ -void -ForwardProjectorByBinUsingRayTracing:: - forward_project_view_plus_90_and_delta(Viewgram & pos_view, - Viewgram & neg_view, - Viewgram & pos_plus90, - Viewgram & neg_plus90, - const VoxelsOnCartesianGrid & image, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) const -{ +void +ForwardProjectorByBinUsingRayTracing::forward_project_view_plus_90_and_delta( + Viewgram& pos_view, Viewgram& neg_view, Viewgram& pos_plus90, Viewgram& neg_plus90, + const VoxelsOnCartesianGrid& image, const int min_axial_pos_num, const int max_axial_pos_num, + const int min_tangential_pos_num, const int max_tangential_pos_num) const { assert(pos_view.get_segment_num() > 0); assert(pos_view.get_view_num() >= 0); - assert(pos_view.get_view_num() < pos_view.get_proj_data_info_sptr()->get_num_views()/2); + assert(pos_view.get_view_num() < pos_view.get_proj_data_info_sptr()->get_num_views() / 2); Viewgram dummy = pos_view; - - forward_project_all_symmetries( - pos_view, - neg_view, - pos_plus90, - neg_plus90, - dummy, - dummy, - dummy, - dummy, - image, - min_axial_pos_num, max_axial_pos_num, - min_tangential_pos_num, max_tangential_pos_num); + forward_project_all_symmetries(pos_view, neg_view, pos_plus90, neg_plus90, dummy, dummy, dummy, dummy, image, min_axial_pos_num, + max_axial_pos_num, min_tangential_pos_num, max_tangential_pos_num); } - -void -ForwardProjectorByBinUsingRayTracing:: - forward_project_view_min_180_and_delta(Viewgram & pos_view, - Viewgram & neg_view, - Viewgram & pos_min180, - Viewgram & neg_min180, - const VoxelsOnCartesianGrid & image, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) const -{ +void +ForwardProjectorByBinUsingRayTracing::forward_project_view_min_180_and_delta( + Viewgram& pos_view, Viewgram& neg_view, Viewgram& pos_min180, Viewgram& neg_min180, + const VoxelsOnCartesianGrid& image, const int min_axial_pos_num, const int max_axial_pos_num, + const int min_tangential_pos_num, const int max_tangential_pos_num) const { assert(pos_view.get_segment_num() > 0); assert(pos_view.get_view_num() >= 0); - assert(pos_view.get_view_num() < pos_view.get_proj_data_info_sptr()->get_num_views()/2); + assert(pos_view.get_view_num() < pos_view.get_proj_data_info_sptr()->get_num_views() / 2); Viewgram dummy = pos_view; - forward_project_all_symmetries( - pos_view, - neg_view, - dummy, - dummy, - pos_min180, - neg_min180, - dummy, - dummy, - image, - min_axial_pos_num, max_axial_pos_num, - min_tangential_pos_num, max_tangential_pos_num); + forward_project_all_symmetries(pos_view, neg_view, dummy, dummy, pos_min180, neg_min180, dummy, dummy, image, min_axial_pos_num, + max_axial_pos_num, min_tangential_pos_num, max_tangential_pos_num); } #if 0 @@ -934,11 +762,11 @@ void ForwardProjectorByBinUsingRayTracing::forward_project_2D(Sinogram &s /* Here tang_pos_num=0 and phi=0 or 45*/ if (proj_Siddon -#ifndef STIR_SIDDON_NO_TEMPLATE +# ifndef STIR_SIDDON_NO_TEMPLATE <2>( -#else +# else (2, -#endif +# endif Projall,image,proj_data_cyl_ptr, cphi, sphi, delta + D, 0, R,min_ax_pos, max_ax_pos, offset, num_planes_per_axial_pos, 0, @@ -952,11 +780,11 @@ void ForwardProjectorByBinUsingRayTracing::forward_project_2D(Sinogram &s /* Now tang_pos_num!=0 and phi=0 or 45 */ for (tang_pos_num = min_tang_pos_num_in_loop; tang_pos_num <= max_abs_tangential_pos_num; tang_pos_num++) { if (proj_Siddon -#ifndef STIR_SIDDON_NO_TEMPLATE +# ifndef STIR_SIDDON_NO_TEMPLATE <1>( -#else +# else (1, -#endif +# endif Projall,image,proj_data_cyl_ptr, cphi, sphi, delta + D, tang_pos_num, R,min_ax_pos,max_ax_pos, offset, num_planes_per_axial_pos, 0, @@ -977,11 +805,11 @@ void ForwardProjectorByBinUsingRayTracing::forward_project_2D(Sinogram &s for (D = 0; D < C; D++) { /* Here tang_pos_num==0 and phi!=k*45 */ if (proj_Siddon -#ifndef STIR_SIDDON_NO_TEMPLATE +# ifndef STIR_SIDDON_NO_TEMPLATE <4>( -#else +# else (4, -#endif +# endif Projall,image,proj_data_cyl_ptr, cphi, sphi, delta + D, 0, R,min_ax_pos,max_ax_pos, offset, num_planes_per_axial_pos, 0, @@ -998,11 +826,11 @@ void ForwardProjectorByBinUsingRayTracing::forward_project_2D(Sinogram &s /* Here tang_pos_num!=0 and phi!=k*45. */ for (tang_pos_num = min_tang_pos_num_in_loop; tang_pos_num <= max_abs_tangential_pos_num; tang_pos_num++) { if (proj_Siddon -#ifndef STIR_SIDDON_NO_TEMPLATE +# ifndef STIR_SIDDON_NO_TEMPLATE <3>( -#else +# else (3, -#endif +# endif Projall,image,proj_data_cyl_ptr, cphi, sphi, delta + D, tang_pos_num, R,min_ax_pos, max_ax_pos, offset, num_planes_per_axial_pos, 0, @@ -1032,422 +860,328 @@ void ForwardProjectorByBinUsingRayTracing::forward_project_2D(Sinogram &s #endif // old 2D versions -void -ForwardProjectorByBinUsingRayTracing:: -forward_project_view_2D(Viewgram & pos_view, - const VoxelsOnCartesianGrid & image, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) const -{ +void +ForwardProjectorByBinUsingRayTracing::forward_project_view_2D(Viewgram& pos_view, + const VoxelsOnCartesianGrid& image, + const int min_axial_pos_num, const int max_axial_pos_num, + const int min_tangential_pos_num, + const int max_tangential_pos_num) const { assert(pos_view.get_segment_num() == 0); assert(pos_view.get_view_num() >= 0); assert(pos_view.get_view_num() < pos_view.get_proj_data_info_sptr()->get_num_views()); Viewgram dummy = pos_view; - forward_project_all_symmetries_2D( - pos_view, - dummy, - dummy, - dummy, - image, - min_axial_pos_num, max_axial_pos_num, - min_tangential_pos_num, max_tangential_pos_num); - + forward_project_all_symmetries_2D(pos_view, dummy, dummy, dummy, image, min_axial_pos_num, max_axial_pos_num, + min_tangential_pos_num, max_tangential_pos_num); } -void -ForwardProjectorByBinUsingRayTracing:: -forward_project_view_plus_90_2D(Viewgram & pos_view, - Viewgram & pos_plus90, - const VoxelsOnCartesianGrid & image, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) const -{ +void +ForwardProjectorByBinUsingRayTracing::forward_project_view_plus_90_2D(Viewgram& pos_view, Viewgram& pos_plus90, + const VoxelsOnCartesianGrid& image, + const int min_axial_pos_num, const int max_axial_pos_num, + const int min_tangential_pos_num, + const int max_tangential_pos_num) const { assert(pos_view.get_segment_num() == 0); assert(pos_view.get_view_num() >= 0); - assert(pos_view.get_view_num() < pos_view.get_proj_data_info_sptr()->get_num_views()/2); + assert(pos_view.get_view_num() < pos_view.get_proj_data_info_sptr()->get_num_views() / 2); Viewgram dummy = pos_view; - forward_project_all_symmetries_2D( - pos_view, - pos_plus90, - dummy, - dummy, - image, - min_axial_pos_num, max_axial_pos_num, - min_tangential_pos_num, max_tangential_pos_num); + forward_project_all_symmetries_2D(pos_view, pos_plus90, dummy, dummy, image, min_axial_pos_num, max_axial_pos_num, + min_tangential_pos_num, max_tangential_pos_num); } - -void -ForwardProjectorByBinUsingRayTracing:: -forward_project_view_min_180_2D(Viewgram & pos_view, - Viewgram & pos_min180, - const VoxelsOnCartesianGrid & image, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) const -{ +void +ForwardProjectorByBinUsingRayTracing::forward_project_view_min_180_2D(Viewgram& pos_view, Viewgram& pos_min180, + const VoxelsOnCartesianGrid& image, + const int min_axial_pos_num, const int max_axial_pos_num, + const int min_tangential_pos_num, + const int max_tangential_pos_num) const { assert(pos_view.get_segment_num() == 0); assert(pos_view.get_view_num() >= 0); - assert(pos_view.get_view_num() < pos_view.get_proj_data_info_sptr()->get_num_views()/2); + assert(pos_view.get_view_num() < pos_view.get_proj_data_info_sptr()->get_num_views() / 2); Viewgram dummy = pos_view; - forward_project_all_symmetries_2D( - pos_view, - dummy, - pos_min180, - dummy, - image, - min_axial_pos_num, max_axial_pos_num, - min_tangential_pos_num, max_tangential_pos_num); + forward_project_all_symmetries_2D(pos_view, dummy, pos_min180, dummy, image, min_axial_pos_num, max_axial_pos_num, + min_tangential_pos_num, max_tangential_pos_num); } - - -void -ForwardProjectorByBinUsingRayTracing:: -forward_project_all_symmetries_2D(Viewgram & pos_view, - Viewgram & pos_plus90, - Viewgram & pos_min180, - Viewgram & pos_min90, - const VoxelsOnCartesianGrid& image, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) const -{ +void +ForwardProjectorByBinUsingRayTracing::forward_project_all_symmetries_2D(Viewgram& pos_view, Viewgram& pos_plus90, + Viewgram& pos_min180, Viewgram& pos_min90, + const VoxelsOnCartesianGrid& image, + const int min_axial_pos_num, const int max_axial_pos_num, + const int min_tangential_pos_num, + const int max_tangential_pos_num) const { start_timers(); // KT 20/06/2001 should now work for non-arccorrected data as well const shared_ptr proj_data_info_sptr = - dynamic_pointer_cast - (pos_view.get_proj_data_info_sptr()); + dynamic_pointer_cast(pos_view.get_proj_data_info_sptr()); if (is_null_ptr(proj_data_info_sptr)) error("ForwardProjectorByBinUsingRayTracing::forward_project called with wrong type of ProjDataInfo\n"); - - const int nviews = pos_view.get_proj_data_info_sptr()->get_num_views(); - + + const int nviews = pos_view.get_proj_data_info_sptr()->get_num_views(); + const int segment_num = pos_view.get_segment_num(); - //const int timing_pos_num = pos_view.get_timing_pos_num(); + // const int timing_pos_num = pos_view.get_timing_pos_num(); const float delta = proj_data_info_sptr->get_average_ring_difference(segment_num); const int view = pos_view.get_view_num(); assert(delta == 0); assert(view >= 0); - /* remove assertions which would break the temporary 1,2 parameter forward_project. - Now checked before calling + Now checked before calling assert(pos_plus90.get_view_num() == nviews / 2 + pos_view.get_view_num()); assert(pos_min90.get_view_num() == nviews / 2 - pos_view.get_view_num()); assert(pos_min180.get_view_num() == nviews - pos_view.get_view_num()); */ - //assert(neg_view.get_view_num() == pos_view.get_view_num()); - //assert(neg_plus90.get_view_num() == pos_plus90.get_view_num()); - //assert(neg_min90.get_view_num() == pos_min90.get_view_num()); - //assert(neg_min180.get_view_num() == pos_min180.get_view_num()); - + // assert(neg_view.get_view_num() == pos_view.get_view_num()); + // assert(neg_plus90.get_view_num() == pos_plus90.get_view_num()); + // assert(neg_min90.get_view_num() == pos_min90.get_view_num()); + // assert(neg_min180.get_view_num() == pos_min180.get_view_num()); + // KT 21/05/98 added const where possible // TODO C value depends whether you are in Double or not, // If double C==2 => do 2*ax_pos0 and 2*ax_pos0+1 - const int C=1; - - int D, tang_pos_num; + const int C = 1; + + int D, tang_pos_num; int my_ax_pos0; const float R = proj_data_info_sptr->get_ring_radius(); // a variable which will be used in the loops over tang_pos_num to get s_in_mm - Bin bin(pos_view.get_segment_num(), pos_view.get_view_num(),min_axial_pos_num,0,pos_view.get_timing_pos_num()); - - // KT 20/06/2001 rewrote using get_phi + Bin bin(pos_view.get_segment_num(), pos_view.get_view_num(), min_axial_pos_num, 0, pos_view.get_timing_pos_num()); + + // KT 20/06/2001 rewrote using get_phi const float cphi = cos(proj_data_info_sptr->get_phi(bin)); const float sphi = sin(proj_data_info_sptr->get_phi(bin)); // find correspondence between ring coordinates and image coordinates: // z = num_planes_per_axial_pos * ax_pos_num + axial_pos_to_z_offset // KT 20/06/2001 write using symmetries member - const int num_planes_per_axial_pos = - round(symmetries_ptr->get_num_planes_per_axial_pos(segment_num)); - const float axial_pos_to_z_offset = - symmetries_ptr->get_axial_pos_to_z_offset(segment_num); - - const int max_abs_tangential_pos_num = - max(max_tangential_pos_num, -min_tangential_pos_num); - - const int min_abs_tangential_pos_num = - max_tangential_pos_num<0 ? - -max_tangential_pos_num - : (min_tangential_pos_num>0 ? - min_tangential_pos_num - : 0 ); - const int min_tang_pos_num_in_loop = - min_abs_tangential_pos_num==0 ? 1 : min_abs_tangential_pos_num; - + const int num_planes_per_axial_pos = round(symmetries_ptr->get_num_planes_per_axial_pos(segment_num)); + const float axial_pos_to_z_offset = symmetries_ptr->get_axial_pos_to_z_offset(segment_num); + + const int max_abs_tangential_pos_num = max(max_tangential_pos_num, -min_tangential_pos_num); + + const int min_abs_tangential_pos_num = + max_tangential_pos_num < 0 ? -max_tangential_pos_num : (min_tangential_pos_num > 0 ? min_tangential_pos_num : 0); + const int min_tang_pos_num_in_loop = min_abs_tangential_pos_num == 0 ? 1 : min_abs_tangential_pos_num; + + Array<4, float> Projall(IndexRange4D(min_axial_pos_num, max_axial_pos_num, 0, 1, 0, 1, 0, 3)); + Array<4, float> Projall2(IndexRange4D(min_axial_pos_num, max_axial_pos_num + 1, 0, 1, 0, 1, 0, 3)); - - Array <4,float> Projall(IndexRange4D(min_axial_pos_num, max_axial_pos_num, 0, 1, 0, 1, 0, 3)); - Array <4,float> Projall2(IndexRange4D(min_axial_pos_num, max_axial_pos_num+1, 0, 1, 0, 1, 0, 3)); - // What to do when num_planes_per_axial_pos==2 ? - // In the 2D case, the approach followed in 3D is ill-defined, as we would be + // In the 2D case, the approach followed in 3D is ill-defined, as we would be // forward projecting right along the edges of the voxels. - // Instead, we take for the contribution to an axial_pos_num, + // Instead, we take for the contribution to an axial_pos_num, // 1/2 left_voxel + centre_voxel + 1/2 right_voxel - + int num_lors_per_virtual_ring = 2; - - if (num_planes_per_axial_pos == 1) - { + + if (num_planes_per_axial_pos == 1) { num_lors_per_virtual_ring = 1; } - - - - if (view == 0 || 4*view == nviews ) - { /* phi=0 or 45 */ - for (D = 0; D < C; D++) - { - if (min_abs_tangential_pos_num==0) - { - /* Here tang_pos_num=0 and phi=0 or 45*/ - { - if (proj_Siddon + + if (view == 0 || 4 * view == nviews) { /* phi=0 or 45 */ + for (D = 0; D < C; D++) { + if (min_abs_tangential_pos_num == 0) { + /* Here tang_pos_num=0 and phi=0 or 45*/ + { + if (proj_Siddon #ifndef STIR_SIDDON_NO_TEMPLATE - <2>( + <2>( #else - (2, + (2, #endif - Projall, image, proj_data_info_sptr, cphi, sphi, - delta + D, 0, R,min_axial_pos_num, max_axial_pos_num, - 0.F/*==offset*/, num_planes_per_axial_pos, axial_pos_to_z_offset , - 1.F/num_lors_per_virtual_ring, - restrict_to_cylindrical_FOV)) - for (int ax_pos0 = min_axial_pos_num; ax_pos0 <= max_axial_pos_num; ax_pos0++) - { - my_ax_pos0 = C * ax_pos0 + D; - - pos_view[my_ax_pos0][0] += Projall[ax_pos0][0][0][0]; - pos_plus90[my_ax_pos0][0] +=Projall[ax_pos0][0][0][2]; - } - } - - if (num_planes_per_axial_pos == 2) - { - if (proj_Siddon + Projall, image, proj_data_info_sptr, cphi, sphi, delta + D, 0, R, min_axial_pos_num, max_axial_pos_num, + 0.F /*==offset*/, num_planes_per_axial_pos, axial_pos_to_z_offset, 1.F / num_lors_per_virtual_ring, + restrict_to_cylindrical_FOV)) + for (int ax_pos0 = min_axial_pos_num; ax_pos0 <= max_axial_pos_num; ax_pos0++) { + my_ax_pos0 = C * ax_pos0 + D; + + pos_view[my_ax_pos0][0] += Projall[ax_pos0][0][0][0]; + pos_plus90[my_ax_pos0][0] += Projall[ax_pos0][0][0][2]; + } + } + + if (num_planes_per_axial_pos == 2) { + if (proj_Siddon #ifndef STIR_SIDDON_NO_TEMPLATE - <2>( + <2>( #else - (2, + (2, #endif - Projall2, image, proj_data_info_sptr, cphi, sphi, - delta + D, 0, R, min_axial_pos_num, max_axial_pos_num+1, - -0.5F/*==offset*/, num_planes_per_axial_pos, axial_pos_to_z_offset , - 1.F/4, - restrict_to_cylindrical_FOV)) - for (int ax_pos0 = min_axial_pos_num; ax_pos0 <= max_axial_pos_num; ax_pos0++) - { - my_ax_pos0 = C * ax_pos0 + D; - pos_view[my_ax_pos0][0] += (Projall2[ax_pos0+1][0][0][0]+ Projall2[ax_pos0][0][0][0]); - pos_plus90[my_ax_pos0][0] += (Projall2[ax_pos0+1][0][0][2]+ Projall2[ax_pos0][0][0][2]); - } - } - } - + Projall2, image, proj_data_info_sptr, cphi, sphi, delta + D, 0, R, min_axial_pos_num, max_axial_pos_num + 1, + -0.5F /*==offset*/, num_planes_per_axial_pos, axial_pos_to_z_offset, 1.F / 4, restrict_to_cylindrical_FOV)) + for (int ax_pos0 = min_axial_pos_num; ax_pos0 <= max_axial_pos_num; ax_pos0++) { + my_ax_pos0 = C * ax_pos0 + D; + pos_view[my_ax_pos0][0] += (Projall2[ax_pos0 + 1][0][0][0] + Projall2[ax_pos0][0][0][0]); + pos_plus90[my_ax_pos0][0] += (Projall2[ax_pos0 + 1][0][0][2] + Projall2[ax_pos0][0][0][2]); + } + } + } + /* Now tang_pos_num!=0 and phi=0 or 45 */ - for (tang_pos_num = min_tang_pos_num_in_loop; tang_pos_num <= max_abs_tangential_pos_num; tang_pos_num++) - { - bin.tangential_pos_num() = tang_pos_num; - const float s_in_mm = proj_data_info_sptr->get_s(bin); - + for (tang_pos_num = min_tang_pos_num_in_loop; tang_pos_num <= max_abs_tangential_pos_num; tang_pos_num++) { + bin.tangential_pos_num() = tang_pos_num; + const float s_in_mm = proj_data_info_sptr->get_s(bin); - { + { if (proj_Siddon #ifndef STIR_SIDDON_NO_TEMPLATE - <1>( + <1>( #else - (1, + (1, #endif - Projall, image, proj_data_info_sptr, cphi, sphi, - delta + D, s_in_mm, R,min_axial_pos_num, max_axial_pos_num, - 0.F, num_planes_per_axial_pos, axial_pos_to_z_offset, - 1.F/num_lors_per_virtual_ring, - restrict_to_cylindrical_FOV)) - for (int ax_pos0 = min_axial_pos_num; ax_pos0 <= max_axial_pos_num; ax_pos0++) - { - my_ax_pos0 = C * ax_pos0 + D; - if (tang_pos_num<=max_tangential_pos_num) - { - pos_view[my_ax_pos0][tang_pos_num] += Projall[ax_pos0][0][0][0]; - pos_plus90[my_ax_pos0][tang_pos_num] += Projall[ax_pos0][0][0][2]; - } - if (-tang_pos_num>=min_tangential_pos_num) - { - pos_view[my_ax_pos0][-tang_pos_num] += Projall[ax_pos0][0][1][0]; - pos_plus90[my_ax_pos0][-tang_pos_num] += Projall[ax_pos0][0][1][2]; - } - } + Projall, image, proj_data_info_sptr, cphi, sphi, delta + D, s_in_mm, R, min_axial_pos_num, max_axial_pos_num, + 0.F, num_planes_per_axial_pos, axial_pos_to_z_offset, 1.F / num_lors_per_virtual_ring, + restrict_to_cylindrical_FOV)) + for (int ax_pos0 = min_axial_pos_num; ax_pos0 <= max_axial_pos_num; ax_pos0++) { + my_ax_pos0 = C * ax_pos0 + D; + if (tang_pos_num <= max_tangential_pos_num) { + pos_view[my_ax_pos0][tang_pos_num] += Projall[ax_pos0][0][0][0]; + pos_plus90[my_ax_pos0][tang_pos_num] += Projall[ax_pos0][0][0][2]; + } + if (-tang_pos_num >= min_tangential_pos_num) { + pos_view[my_ax_pos0][-tang_pos_num] += Projall[ax_pos0][0][1][0]; + pos_plus90[my_ax_pos0][-tang_pos_num] += Projall[ax_pos0][0][1][2]; + } + } } - if (num_planes_per_axial_pos == 2) - { + if (num_planes_per_axial_pos == 2) { if (proj_Siddon #ifndef STIR_SIDDON_NO_TEMPLATE - <1>( + <1>( #else - (1, + (1, #endif - Projall2, image, proj_data_info_sptr, cphi, sphi, - delta + D, s_in_mm, R,min_axial_pos_num, max_axial_pos_num+1, - -0.5F, num_planes_per_axial_pos, axial_pos_to_z_offset, - 1.F/4, - restrict_to_cylindrical_FOV)) - for (int ax_pos0 =min_axial_pos_num; ax_pos0 <=max_axial_pos_num; ax_pos0++) - { - my_ax_pos0 = C * ax_pos0 + D; - if (tang_pos_num<=max_tangential_pos_num) - { - pos_view[my_ax_pos0][tang_pos_num] +=(Projall2[ax_pos0][0][0][0]+Projall2[ax_pos0+1][0][0][0]); - pos_plus90[my_ax_pos0][tang_pos_num] += (Projall2[ax_pos0][0][0][2]+Projall2[ax_pos0+1][0][0][2]); - } - if (-tang_pos_num>=min_tangential_pos_num) - { - pos_view[my_ax_pos0][-tang_pos_num] +=(Projall2[ax_pos0][0][1][0]+Projall2[ax_pos0+1][0][1][0]); - pos_plus90[my_ax_pos0][-tang_pos_num] +=(Projall2[ax_pos0][0][1][2]+Projall2[ax_pos0+1][0][1][2]); - } - } + Projall2, image, proj_data_info_sptr, cphi, sphi, delta + D, s_in_mm, R, min_axial_pos_num, + max_axial_pos_num + 1, -0.5F, num_planes_per_axial_pos, axial_pos_to_z_offset, 1.F / 4, + restrict_to_cylindrical_FOV)) + for (int ax_pos0 = min_axial_pos_num; ax_pos0 <= max_axial_pos_num; ax_pos0++) { + my_ax_pos0 = C * ax_pos0 + D; + if (tang_pos_num <= max_tangential_pos_num) { + pos_view[my_ax_pos0][tang_pos_num] += (Projall2[ax_pos0][0][0][0] + Projall2[ax_pos0 + 1][0][0][0]); + pos_plus90[my_ax_pos0][tang_pos_num] += (Projall2[ax_pos0][0][0][2] + Projall2[ax_pos0 + 1][0][0][2]); + } + if (-tang_pos_num >= min_tangential_pos_num) { + pos_view[my_ax_pos0][-tang_pos_num] += (Projall2[ax_pos0][0][1][0] + Projall2[ax_pos0 + 1][0][1][0]); + pos_plus90[my_ax_pos0][-tang_pos_num] += (Projall2[ax_pos0][0][1][2] + Projall2[ax_pos0 + 1][0][1][2]); + } + } } - } // Loop over tang_pos_num - } // Loop over D - } - else - { - // general phi - for (D = 0; D < C; D++) - { - if (min_abs_tangential_pos_num==0) - { - /* Here tang_pos_num==0 and phi!=k*45 */ - { - if (proj_Siddon + } // Loop over tang_pos_num + } // Loop over D + } else { + // general phi + for (D = 0; D < C; D++) { + if (min_abs_tangential_pos_num == 0) { + /* Here tang_pos_num==0 and phi!=k*45 */ + { + if (proj_Siddon #ifndef STIR_SIDDON_NO_TEMPLATE - <4>( + <4>( #else - (4, + (4, #endif - Projall, image, proj_data_info_sptr, cphi, sphi, - delta + D, 0, R,min_axial_pos_num, max_axial_pos_num, - 0.F, num_planes_per_axial_pos, axial_pos_to_z_offset , - 1.F/num_lors_per_virtual_ring, - restrict_to_cylindrical_FOV)) - for (int ax_pos0 = min_axial_pos_num; ax_pos0 <= max_axial_pos_num; ax_pos0++) - { - my_ax_pos0 = C * ax_pos0 + D; - pos_view[my_ax_pos0][0] += Projall[ax_pos0][0][0][0]; - pos_min90[my_ax_pos0][0] += Projall[ax_pos0][0][0][1]; - pos_plus90[my_ax_pos0][0] += Projall[ax_pos0][0][0][2]; - pos_min180[my_ax_pos0][0] += Projall[ax_pos0][0][0][3]; - } - } - - if (num_planes_per_axial_pos == 2) - { - if (proj_Siddon + Projall, image, proj_data_info_sptr, cphi, sphi, delta + D, 0, R, min_axial_pos_num, max_axial_pos_num, 0.F, + num_planes_per_axial_pos, axial_pos_to_z_offset, 1.F / num_lors_per_virtual_ring, restrict_to_cylindrical_FOV)) + for (int ax_pos0 = min_axial_pos_num; ax_pos0 <= max_axial_pos_num; ax_pos0++) { + my_ax_pos0 = C * ax_pos0 + D; + pos_view[my_ax_pos0][0] += Projall[ax_pos0][0][0][0]; + pos_min90[my_ax_pos0][0] += Projall[ax_pos0][0][0][1]; + pos_plus90[my_ax_pos0][0] += Projall[ax_pos0][0][0][2]; + pos_min180[my_ax_pos0][0] += Projall[ax_pos0][0][0][3]; + } + } + + if (num_planes_per_axial_pos == 2) { + if (proj_Siddon #ifndef STIR_SIDDON_NO_TEMPLATE - <4>( + <4>( #else - (4, + (4, #endif - Projall2, image, proj_data_info_sptr, cphi, sphi, - delta + D, 0, R,min_axial_pos_num, max_axial_pos_num, - -0.5F, num_planes_per_axial_pos, axial_pos_to_z_offset , - 1.F/4, - restrict_to_cylindrical_FOV)) - for (int ax_pos0 = min_axial_pos_num; ax_pos0 <=max_axial_pos_num; ax_pos0++) - { - my_ax_pos0 = C * ax_pos0 + D; - pos_view[my_ax_pos0][0] += (Projall2[ax_pos0][0][0][0]+Projall2[ax_pos0+1][0][0][0]); - pos_min90[my_ax_pos0][0] += (Projall2[ax_pos0][0][0][1]+Projall2[ax_pos0+1][0][0][1]); - pos_plus90[my_ax_pos0][0] +=(Projall2[ax_pos0][0][0][2]+Projall2[ax_pos0+1][0][0][2]); - pos_min180[my_ax_pos0][0] +=(Projall2[ax_pos0][0][0][3]+Projall2[ax_pos0+1][0][0][3]); - } - } - } - + Projall2, image, proj_data_info_sptr, cphi, sphi, delta + D, 0, R, min_axial_pos_num, max_axial_pos_num, -0.5F, + num_planes_per_axial_pos, axial_pos_to_z_offset, 1.F / 4, restrict_to_cylindrical_FOV)) + for (int ax_pos0 = min_axial_pos_num; ax_pos0 <= max_axial_pos_num; ax_pos0++) { + my_ax_pos0 = C * ax_pos0 + D; + pos_view[my_ax_pos0][0] += (Projall2[ax_pos0][0][0][0] + Projall2[ax_pos0 + 1][0][0][0]); + pos_min90[my_ax_pos0][0] += (Projall2[ax_pos0][0][0][1] + Projall2[ax_pos0 + 1][0][0][1]); + pos_plus90[my_ax_pos0][0] += (Projall2[ax_pos0][0][0][2] + Projall2[ax_pos0 + 1][0][0][2]); + pos_min180[my_ax_pos0][0] += (Projall2[ax_pos0][0][0][3] + Projall2[ax_pos0 + 1][0][0][3]); + } + } + } + /* Here tang_pos_num!=0 and phi!=k*45. */ - for (tang_pos_num = min_tang_pos_num_in_loop; tang_pos_num <= max_abs_tangential_pos_num; tang_pos_num++) - { - bin.tangential_pos_num() = tang_pos_num; - const float s_in_mm = proj_data_info_sptr->get_s(bin); + for (tang_pos_num = min_tang_pos_num_in_loop; tang_pos_num <= max_abs_tangential_pos_num; tang_pos_num++) { + bin.tangential_pos_num() = tang_pos_num; + const float s_in_mm = proj_data_info_sptr->get_s(bin); - { + { if (proj_Siddon #ifndef STIR_SIDDON_NO_TEMPLATE - <3>( + <3>( #else - (3, + (3, #endif - Projall, image, proj_data_info_sptr, cphi, sphi, - delta + D, s_in_mm, R,min_axial_pos_num, max_axial_pos_num, - 0.F, num_planes_per_axial_pos, axial_pos_to_z_offset , - 1.F/num_lors_per_virtual_ring, - restrict_to_cylindrical_FOV)) - for (int ax_pos0 = min_axial_pos_num; ax_pos0<= max_axial_pos_num; ax_pos0++) - { - my_ax_pos0 = C * ax_pos0 + D; - if (tang_pos_num<=max_tangential_pos_num) - { - pos_view[my_ax_pos0][tang_pos_num] += Projall[ax_pos0][0][0][0]; - pos_min90[my_ax_pos0][tang_pos_num] += Projall[ax_pos0][0][0][1]; - pos_plus90[my_ax_pos0][tang_pos_num] += Projall[ax_pos0][0][0][2]; - pos_min180[my_ax_pos0][tang_pos_num] += Projall[ax_pos0][0][0][3]; - } - if (-tang_pos_num>=min_tangential_pos_num) - { - pos_view[my_ax_pos0][-tang_pos_num] += Projall[ax_pos0][0][1][0]; - pos_min90[my_ax_pos0][-tang_pos_num] += Projall[ax_pos0][0][1][1]; - pos_plus90[my_ax_pos0][-tang_pos_num] += Projall[ax_pos0][0][1][2]; - pos_min180[my_ax_pos0][-tang_pos_num] += Projall[ax_pos0][0][1][3]; - } - } - } - if (num_planes_per_axial_pos == 2) - { - if (proj_Siddon + Projall, image, proj_data_info_sptr, cphi, sphi, delta + D, s_in_mm, R, min_axial_pos_num, max_axial_pos_num, + 0.F, num_planes_per_axial_pos, axial_pos_to_z_offset, 1.F / num_lors_per_virtual_ring, + restrict_to_cylindrical_FOV)) + for (int ax_pos0 = min_axial_pos_num; ax_pos0 <= max_axial_pos_num; ax_pos0++) { + my_ax_pos0 = C * ax_pos0 + D; + if (tang_pos_num <= max_tangential_pos_num) { + pos_view[my_ax_pos0][tang_pos_num] += Projall[ax_pos0][0][0][0]; + pos_min90[my_ax_pos0][tang_pos_num] += Projall[ax_pos0][0][0][1]; + pos_plus90[my_ax_pos0][tang_pos_num] += Projall[ax_pos0][0][0][2]; + pos_min180[my_ax_pos0][tang_pos_num] += Projall[ax_pos0][0][0][3]; + } + if (-tang_pos_num >= min_tangential_pos_num) { + pos_view[my_ax_pos0][-tang_pos_num] += Projall[ax_pos0][0][1][0]; + pos_min90[my_ax_pos0][-tang_pos_num] += Projall[ax_pos0][0][1][1]; + pos_plus90[my_ax_pos0][-tang_pos_num] += Projall[ax_pos0][0][1][2]; + pos_min180[my_ax_pos0][-tang_pos_num] += Projall[ax_pos0][0][1][3]; + } + } + } + if (num_planes_per_axial_pos == 2) { + if (proj_Siddon #ifndef STIR_SIDDON_NO_TEMPLATE - <3>( + <3>( #else - (3, + (3, #endif - Projall2, image, proj_data_info_sptr, cphi, sphi, - delta + D, s_in_mm, R,min_axial_pos_num, max_axial_pos_num+1, - -0.5F, num_planes_per_axial_pos, axial_pos_to_z_offset , - 1.F/4, - restrict_to_cylindrical_FOV)) - for (int ax_pos0 = min_axial_pos_num; ax_pos0 <= max_axial_pos_num; ax_pos0++) - { - my_ax_pos0 = C * ax_pos0 + D; - if (tang_pos_num<=max_tangential_pos_num) - { - pos_view[ my_ax_pos0][tang_pos_num] +=(Projall2[ax_pos0][0][0][0]+Projall2[ax_pos0+1][0][0][0]); - pos_min90[my_ax_pos0][tang_pos_num] += (Projall2[ax_pos0][0][0][1]+Projall2[ax_pos0+1][0][0][1]); - pos_plus90[ my_ax_pos0][tang_pos_num] +=(Projall2[ax_pos0][0][0][2]+Projall2[ax_pos0+1][0][0][2]); - pos_min180[ my_ax_pos0][tang_pos_num] += (Projall2[ax_pos0][0][0][3]+Projall2[ax_pos0+1][0][0][3]); - } - if (-tang_pos_num>=min_tangential_pos_num) - { - pos_view[ my_ax_pos0][-tang_pos_num] += (Projall2[ax_pos0][0][1][0] +Projall2[ax_pos0+1][0][1][0]); - pos_min90[ my_ax_pos0][-tang_pos_num] +=(Projall2[ax_pos0][0][1][1]+Projall2[ax_pos0+1][0][1][1]); - pos_plus90[ my_ax_pos0][-tang_pos_num] += (Projall2[ax_pos0][0][1][2]+ Projall2[ax_pos0+1][0][1][2]); - pos_min180[ my_ax_pos0][-tang_pos_num] += ( Projall2[ax_pos0][0][1][3]+ Projall2[ax_pos0+1][0][1][3]); - } - } - } - - }// end of loop over tang_pos_num - - }// end loop over D - }// end of else - + Projall2, image, proj_data_info_sptr, cphi, sphi, delta + D, s_in_mm, R, min_axial_pos_num, + max_axial_pos_num + 1, -0.5F, num_planes_per_axial_pos, axial_pos_to_z_offset, 1.F / 4, + restrict_to_cylindrical_FOV)) + for (int ax_pos0 = min_axial_pos_num; ax_pos0 <= max_axial_pos_num; ax_pos0++) { + my_ax_pos0 = C * ax_pos0 + D; + if (tang_pos_num <= max_tangential_pos_num) { + pos_view[my_ax_pos0][tang_pos_num] += (Projall2[ax_pos0][0][0][0] + Projall2[ax_pos0 + 1][0][0][0]); + pos_min90[my_ax_pos0][tang_pos_num] += (Projall2[ax_pos0][0][0][1] + Projall2[ax_pos0 + 1][0][0][1]); + pos_plus90[my_ax_pos0][tang_pos_num] += (Projall2[ax_pos0][0][0][2] + Projall2[ax_pos0 + 1][0][0][2]); + pos_min180[my_ax_pos0][tang_pos_num] += (Projall2[ax_pos0][0][0][3] + Projall2[ax_pos0 + 1][0][0][3]); + } + if (-tang_pos_num >= min_tangential_pos_num) { + pos_view[my_ax_pos0][-tang_pos_num] += (Projall2[ax_pos0][0][1][0] + Projall2[ax_pos0 + 1][0][1][0]); + pos_min90[my_ax_pos0][-tang_pos_num] += (Projall2[ax_pos0][0][1][1] + Projall2[ax_pos0 + 1][0][1][1]); + pos_plus90[my_ax_pos0][-tang_pos_num] += (Projall2[ax_pos0][0][1][2] + Projall2[ax_pos0 + 1][0][1][2]); + pos_min180[my_ax_pos0][-tang_pos_num] += (Projall2[ax_pos0][0][1][3] + Projall2[ax_pos0 + 1][0][1][3]); + } + } + } + + } // end of loop over tang_pos_num + + } // end loop over D + } // end of else + stop_timers(); } diff --git a/src/recon_buildblock/ForwardProjectorByBinUsingRayTracing_Siddon.cxx b/src/recon_buildblock/ForwardProjectorByBinUsingRayTracing_Siddon.cxx index 59d461673b..4e08164546 100644 --- a/src/recon_buildblock/ForwardProjectorByBinUsingRayTracing_Siddon.cxx +++ b/src/recon_buildblock/ForwardProjectorByBinUsingRayTracing_Siddon.cxx @@ -67,15 +67,15 @@ using std::max; START_NAMESPACE_STIR template -static inline int sign(const T& t) -{ - return t<0 ? -1 : 1; +static inline int +sign(const T& t) { + return t < 0 ? -1 : 1; } /*! This function uses a 3D version of Siddon's algorithm for forward projecting. See M. Egger's thesis for details. - In addition to the symmetries is segment and view, it also uses s,-s symmetry + In addition to the symmetries is segment and view, it also uses s,-s symmetry (while being careful when s=0 to avoid self-symmetric cases) For historical reasons, 'axial_pos_num' is here called 'ring'. rmin,rmax are @@ -84,31 +84,23 @@ static inline int sign(const T& t) See RayTraceVoxelsOnCartesianGrid for a shorter and clearer version of the Siddon algorithm. */ - // KT 20/06/2001 should now work for non-arccorrected data as well, pass s_in_mm #ifndef STIR_SIDDON_NO_TEMPLATE template #endif bool -ForwardProjectorByBinUsingRayTracing:: -proj_Siddon( +ForwardProjectorByBinUsingRayTracing::proj_Siddon( #ifdef STIR_SIDDON_NO_TEMPLATE - int Siddon, + int Siddon, #endif - Array <4,float> & Projptr, const VoxelsOnCartesianGrid &Bild, - const shared_ptr proj_data_info_sptr, - const float cphi, const float sphi, const float delta, const - float s_in_mm, - const float R, const int rmin, const int rmax, const float offset, - const int num_planes_per_axial_pos, - const float axial_pos_to_z_offset, - const float norm_factor, - const bool restrict_to_cylindrical_FOV) -{ + Array<4, float>& Projptr, const VoxelsOnCartesianGrid& Bild, + const shared_ptr proj_data_info_sptr, const float cphi, const float sphi, const float delta, + const float s_in_mm, const float R, const int rmin, const int rmax, const float offset, const int num_planes_per_axial_pos, + const float axial_pos_to_z_offset, const float norm_factor, const bool restrict_to_cylindrical_FOV) { /* - * Siddon == 1 => Phiplus90_r0ab + * Siddon == 1 => Phiplus90_r0ab * Siddon == 2 => Phiplus90s0_r0ab - * Siddon == 3 => Symall_r0ab + * Siddon == 3 => Symall_r0ab * Siddon == 4 => s0_r0ab */ @@ -122,15 +114,13 @@ proj_Siddon( having been applied first, so the end_point-start_point coordinates always have particular signs. In the notation of RayTraceVoxelsOnCartesianGrid(): */ - const int sign_x=-1; - const int sign_y=+1; - const int sign_z=+1; + const int sign_x = -1; + const int sign_y = +1; + const int sign_z = +1; // in our current coordinate system, the following constant is always 2 const int num_planes_per_physical_ring = 2; - assert(fabs(Bild.get_voxel_size().z() * num_planes_per_physical_ring/ proj_data_info_sptr->get_ring_spacing() -1) < 10E-4); - - + assert(fabs(Bild.get_voxel_size().z() * num_planes_per_physical_ring / proj_data_info_sptr->get_ring_spacing() - 1) < 10E-4); #ifdef NEWSCALE /* KT 16/02/98 removed division by voxel_size.x() to get line integrals in mm @@ -143,112 +133,102 @@ proj_Siddon( // KT 1/12/2003 no longer subtract -1 as determination of first and last voxel is now // no longer sensitive to rounding error - const float fovrad_in_mm = - min((min(Bild.get_max_x(), -Bild.get_min_x()))*Bild.get_voxel_size().x(), - (min(Bild.get_max_y(), -Bild.get_min_y()))*Bild.get_voxel_size().y()); + const float fovrad_in_mm = min((min(Bild.get_max_x(), -Bild.get_min_x())) * Bild.get_voxel_size().x(), + (min(Bild.get_max_y(), -Bild.get_min_y())) * Bild.get_voxel_size().y()); const CartesianCoordinate3D& voxel_size = Bild.get_voxel_size(); - CartesianCoordinate3D start_point; + CartesianCoordinate3D start_point; CartesianCoordinate3D stop_point; { /* parametrisation of LOR is - X= s*cphi + a*sphi, - Y= s*sphi - a*cphi, + X= s*cphi + a*sphi, + Y= s*sphi - a*cphi, // Z= t/costheta+offset_in_z - a*tantheta Z= num_planes_per_axial_pos * (rmin + offset) + axial_pos_to_z_offset + + num_planes_per_physical_ring * delta/2 * (1 - a / TMP); - with TMP = sqrt(R*R - square(s_in_mm)); - The Z parametrisation can be understood by noting that at a=TMP, the LOR - intersects the detector cylinder with radius R. + with TMP = sqrt(R*R - square(s_in_mm)); + The Z parametrisation can be understood by noting that at a=TMP, the LOR + intersects the detector cylinder with radius R. - find now min_a, max_a such that end-points intersect border of FOV + find now min_a, max_a such that end-points intersect border of FOV */ float max_a; float min_a; - - if (restrict_to_cylindrical_FOV) - { - if (fabs(s_in_mm) >= fovrad_in_mm) - return false; - // a has to be such that X^2+Y^2 == fovrad^2 + + if (restrict_to_cylindrical_FOV) { + if (fabs(s_in_mm) >= fovrad_in_mm) + return false; + // a has to be such that X^2+Y^2 == fovrad^2 max_a = sqrt(square(fovrad_in_mm) - square(s_in_mm)); min_a = -max_a; } // restrict_to_cylindrical_FOV - else - { + else { // use FOV which is square. // note that we use square and not rectangular as otherwise symmetries // would take us out of the FOV. TODO /* - a has to be such that + a has to be such that |X| <= fovrad_in_mm && |Y| <= fovrad_in_mm */ - if (fabs(cphi) < 1.E-3 || fabs(sphi) < 1.E-3) - { + if (fabs(cphi) < 1.E-3 || fabs(sphi) < 1.E-3) { if (fovrad_in_mm < fabs(s_in_mm)) return false; max_a = fovrad_in_mm; min_a = -fovrad_in_mm; - } - else - { - max_a = min((fovrad_in_mm*sign(sphi) - s_in_mm*cphi)/sphi, - (fovrad_in_mm*sign(cphi) + s_in_mm*sphi)/cphi); - min_a = max((-fovrad_in_mm*sign(sphi) - s_in_mm*cphi)/sphi, - (-fovrad_in_mm*sign(cphi) + s_in_mm*sphi)/cphi); - if (min_a > max_a - 1.E-3*voxel_size.x()) + } else { + max_a = min((fovrad_in_mm * sign(sphi) - s_in_mm * cphi) / sphi, (fovrad_in_mm * sign(cphi) + s_in_mm * sphi) / cphi); + min_a = max((-fovrad_in_mm * sign(sphi) - s_in_mm * cphi) / sphi, (-fovrad_in_mm * sign(cphi) + s_in_mm * sphi) / cphi); + if (min_a > max_a - 1.E-3 * voxel_size.x()) return false; } - - } //!restrict_to_cylindrical_FOV - const float TMP = sqrt(R*R - square(s_in_mm)); - - start_point.x() = (s_in_mm*cphi + max_a*sphi)/voxel_size.x(); - start_point.y() = (s_in_mm*sphi - max_a*cphi)/voxel_size.y(); - //start_point.z() = (t_in_mm/costheta+offset_in_z - max_a*tantheta)/voxel_size.z(); + + } //! restrict_to_cylindrical_FOV + const float TMP = sqrt(R * R - square(s_in_mm)); + + start_point.x() = (s_in_mm * cphi + max_a * sphi) / voxel_size.x(); + start_point.y() = (s_in_mm * sphi - max_a * cphi) / voxel_size.y(); + // start_point.z() = (t_in_mm/costheta+offset_in_z - max_a*tantheta)/voxel_size.z(); start_point.z() = num_planes_per_axial_pos * (rmin + offset) + axial_pos_to_z_offset + - + num_planes_per_physical_ring * delta/2 * (1 - max_a / TMP); + +num_planes_per_physical_ring * delta / 2 * (1 - max_a / TMP); - stop_point.x() = (s_in_mm*cphi + min_a*sphi)/voxel_size.x(); - stop_point.y() = (s_in_mm*sphi - min_a*cphi)/voxel_size.y(); - //stop_point.z() = (t_in_mm/costheta+offset_in_z - min_a*tantheta)/voxel_size.z(); - stop_point.z() = num_planes_per_axial_pos * (rmin + offset) + axial_pos_to_z_offset + - + num_planes_per_physical_ring * delta/2 * (1 - min_a / TMP); + stop_point.x() = (s_in_mm * cphi + min_a * sphi) / voxel_size.x(); + stop_point.y() = (s_in_mm * sphi - min_a * cphi) / voxel_size.y(); + // stop_point.z() = (t_in_mm/costheta+offset_in_z - min_a*tantheta)/voxel_size.z(); + stop_point.z() = num_planes_per_axial_pos * (rmin + offset) + axial_pos_to_z_offset + + +num_planes_per_physical_ring * delta / 2 * (1 - min_a / TMP); } // find voxel which contains the start_point, and go to its 'left' edge - const float xmin = round(start_point.x()) - sign_x*0.5F; - const float ymin = round(start_point.y()) - sign_y*0.5F; - const float zmin = round(start_point.z()) - sign_z*0.5F; + const float xmin = round(start_point.x()) - sign_x * 0.5F; + const float ymin = round(start_point.y()) - sign_y * 0.5F; + const float zmin = round(start_point.z()) - sign_z * 0.5F; // find voxel which contains the end_point, and go to its 'right' edge - const float xmax = round(stop_point.x()) + sign_x*0.5F; - const float ymax = round(stop_point.y()) + sign_y*0.5F; - const float zmax = round(stop_point.z()) + sign_z*0.5F; + const float xmax = round(stop_point.x()) + sign_x * 0.5F; + const float ymax = round(stop_point.y()) + sign_y * 0.5F; + const float zmax = round(stop_point.z()) + sign_z * 0.5F; - const CartesianCoordinate3D difference = stop_point-start_point; + const CartesianCoordinate3D difference = stop_point - start_point; assert(difference.x() <= .00001F); assert(difference.y() >= -.00001F); assert(difference.z() >= -.00001F); const float small_difference = 1.E-5F; - const bool zero_diff_in_x = fabs(difference.x())<=small_difference; - const bool zero_diff_in_y = fabs(difference.y())<=small_difference; - const bool zero_diff_in_z = fabs(difference.z())<=small_difference; + const bool zero_diff_in_x = fabs(difference.x()) <= small_difference; + const bool zero_diff_in_y = fabs(difference.y()) <= small_difference; + const bool zero_diff_in_z = fabs(difference.z()) <= small_difference; // d12 is distance between the 2 points (times normalisation_const) - const float d12 = - static_cast(norm(difference*Bild.get_voxel_size()) * normalisation_constant); - // KT 16/02/98 multiply with d12 to get normalisation right from the start - const float inc_x = (zero_diff_in_x) ? 1000000.F*d12 : d12 / (sign_x*difference.x()); - const float inc_y = (zero_diff_in_y) ? 1000000.F*d12 : d12 / (sign_y*difference.y()); - const float inc_z = (zero_diff_in_z) ? 1000000.F*d12 : d12 / (sign_z*difference.z()); - + const float d12 = static_cast(norm(difference * Bild.get_voxel_size()) * normalisation_constant); + // KT 16/02/98 multiply with d12 to get normalisation right from the start + const float inc_x = (zero_diff_in_x) ? 1000000.F * d12 : d12 / (sign_x * difference.x()); + const float inc_y = (zero_diff_in_y) ? 1000000.F * d12 : d12 / (sign_y * difference.y()); + const float inc_z = (zero_diff_in_z) ? 1000000.F * d12 : d12 / (sign_z * difference.z()); /* Find the a? values of the intersection points of the LOR with the planes between voxels. Note on special handling of rays parallel to one of the planes: - + The corresponding a? value would be -infinity. We just set it to - a value low enough such that the start value of 'a' is not compromised + a value low enough such that the start value of 'a' is not compromised further on. Normally a? = (?min-start_point.?) * inc_? * sign_? @@ -269,7 +249,7 @@ proj_Siddon( /* The smallest a? value, gives the end of the 'a'-row */ /* (Note: doc is copied from RayTraceVoxelsOnCartesianGrid. Would need to be adapted a bit) - Find a?end for the last intersections with the coordinate planes. + Find a?end for the last intersections with the coordinate planes. amax will then be the smallest of all these a?end. If the LOR is parallel to a plane, take care that its a?end is larger than all the others. @@ -278,37 +258,35 @@ proj_Siddon( In fact, we will take a?end slightly smaller than the actual last value (i.e. we multiply with a factor .9999). This is to avoid rounding errors in the loop below. In this loop, we try to detect the end of the LOR by comparing a (which is either ax,ay or az) with - aend. With exact arithmetic, a? would have been incremented exactly to - a?_end_actual = a?start + (?max-?end)*inc_?*sign_?, + aend. With exact arithmetic, a? would have been incremented exactly to + a?_end_actual = a?start + (?max-?end)*inc_?*sign_?, so we could loop until a==aend_actual. However, because of numerical precision, - a? might turn out be a tiny bit smaller then a?_end_actual. So, we set aend a + a? might turn out be a tiny bit smaller then a?_end_actual. So, we set aend a (somewhat less tiny) bit smaller than aend_actual, and loop while (a= 0 && Zdup <= maxplane) { - Projptr[ring0][0][0][0] += d * Bild[Zdup][Y][X]; - Projptr[ring0][0][0][2] += d * Bild[Zdup][X][-Y]; - if ((Siddon == 4) || (Siddon == 3)) { - Projptr[ring0][1][0][1] += d * Bild[Zdup][X][Y]; - Projptr[ring0][1][0][3] += d * Bild[Zdup][Y][-X]; - } - if ((Siddon == 1) || (Siddon == 3)) { - Projptr[ring0][1][1][0] += d * Bild[Zdup][-Y][-X]; - Projptr[ring0][1][1][2] += d * Bild[Zdup][-X][Y]; - } - if (Siddon == 3) { - Projptr[ring0][0][1][1] += d * Bild[Zdup][-X][-Y]; - Projptr[ring0][0][1][3] += d * Bild[Zdup][-Y][X]; - } - } - if (Qdup >= 0 && Qdup <= maxplane) { - if ((Siddon == 4) || (Siddon == 3)) { - Projptr[ring0][0][0][1] += d * Bild[Qdup][X][Y]; - Projptr[ring0][0][0][3] += d * Bild[Qdup][Y][-X]; - } - if ((Siddon == 1) || (Siddon == 3)) { - Projptr[ring0][0][1][0] += d * Bild[Qdup][-Y][-X]; - Projptr[ring0][0][1][2] += d * Bild[Qdup][-X][Y]; - } - if (Siddon == 3) { - Projptr[ring0][1][1][1] += d * Bild[Qdup][-X][-Y]; - Projptr[ring0][1][1][3] += d * Bild[Qdup][-Y][X]; - } - Projptr[ring0][1][0][0] += d * Bild[Qdup][Y][X]; - Projptr[ring0][1][0][2] += d * Bild[Qdup][X][-Y]; - } - - } - a = ax ; ax += inc_x; - X--; - } else { /* LOR leaves voxel through xy-plane */ - const float d = az - a; - int Zdup = Z; - int Qdup = Q; - for (int ring0 = rmin; - ring0 <= rmax; - ring0++, Zdup += num_planes_per_axial_pos, Qdup +=num_planes_per_axial_pos ) - { - /* all symmetries except in 's'*/ - if (Zdup >= 0 && Zdup <= maxplane) { - Projptr[ring0][0][0][0] += d * Bild[Zdup][Y][X]; - Projptr[ring0][0][0][2] += d * Bild[Zdup][X][-Y]; - if ((Siddon == 4) || (Siddon == 3)) { - Projptr[ring0][1][0][1] += d * Bild[Zdup][X][Y]; - Projptr[ring0][1][0][3] += d * Bild[Zdup][Y][-X]; - } - if ((Siddon == 1) || (Siddon == 3)) { - Projptr[ring0][1][1][0] += d * Bild[Zdup][-Y][-X]; - Projptr[ring0][1][1][2] += d * Bild[Zdup][-X][Y]; - } - if (Siddon == 3) { - Projptr[ring0][0][1][1] += d * Bild[Zdup][-X][-Y]; - Projptr[ring0][0][1][3] += d * Bild[Zdup][-Y][X]; - - } - } - if (Qdup >= 0 && Qdup <= maxplane) { - if ((Siddon == 4) || (Siddon == 3)) { - Projptr[ring0][0][0][1] += d * Bild[Qdup][X][Y]; - Projptr[ring0][0][0][3] += d * Bild[Qdup][Y][-X]; - } - if ((Siddon == 1) || (Siddon == 3)) { - Projptr[ring0][0][1][0] += d * Bild[Qdup][-Y][-X]; - Projptr[ring0][0][1][2] += d * Bild[Qdup][-X][Y]; - } - if (Siddon == 3) { - Projptr[ring0][1][1][1] += d * Bild[Qdup][-X][-Y]; - Projptr[ring0][1][1][3] += d * Bild[Qdup][-Y][X]; - } - Projptr[ring0][1][0][0] += d * Bild[Qdup][Y][X]; - Projptr[ring0][1][0][2] += d * Bild[Qdup][X][-Y]; - } - - } - a = az ; az += inc_z; - Z++; - Q--; + if (ax < az) { /* LOR leaves voxel through yz-plane */ + const float d = ax - a; + int Zdup = Z; + int Qdup = Q; + for (int ring0 = rmin; ring0 <= rmax; ring0++, Zdup += num_planes_per_axial_pos, Qdup += num_planes_per_axial_pos) { + /* Alle Symetrien ausser s */ + if (Zdup >= 0 && Zdup <= maxplane) { + Projptr[ring0][0][0][0] += d * Bild[Zdup][Y][X]; + Projptr[ring0][0][0][2] += d * Bild[Zdup][X][-Y]; + if ((Siddon == 4) || (Siddon == 3)) { + Projptr[ring0][1][0][1] += d * Bild[Zdup][X][Y]; + Projptr[ring0][1][0][3] += d * Bild[Zdup][Y][-X]; + } + if ((Siddon == 1) || (Siddon == 3)) { + Projptr[ring0][1][1][0] += d * Bild[Zdup][-Y][-X]; + Projptr[ring0][1][1][2] += d * Bild[Zdup][-X][Y]; + } + if (Siddon == 3) { + Projptr[ring0][0][1][1] += d * Bild[Zdup][-X][-Y]; + Projptr[ring0][0][1][3] += d * Bild[Zdup][-Y][X]; + } + } + if (Qdup >= 0 && Qdup <= maxplane) { + if ((Siddon == 4) || (Siddon == 3)) { + Projptr[ring0][0][0][1] += d * Bild[Qdup][X][Y]; + Projptr[ring0][0][0][3] += d * Bild[Qdup][Y][-X]; + } + if ((Siddon == 1) || (Siddon == 3)) { + Projptr[ring0][0][1][0] += d * Bild[Qdup][-Y][-X]; + Projptr[ring0][0][1][2] += d * Bild[Qdup][-X][Y]; + } + if (Siddon == 3) { + Projptr[ring0][1][1][1] += d * Bild[Qdup][-X][-Y]; + Projptr[ring0][1][1][3] += d * Bild[Qdup][-Y][X]; + } + Projptr[ring0][1][0][0] += d * Bild[Qdup][Y][X]; + Projptr[ring0][1][0][2] += d * Bild[Qdup][X][-Y]; + } + } + a = ax; + ax += inc_x; + X--; + } else { /* LOR leaves voxel through xy-plane */ + const float d = az - a; + int Zdup = Z; + int Qdup = Q; + for (int ring0 = rmin; ring0 <= rmax; ring0++, Zdup += num_planes_per_axial_pos, Qdup += num_planes_per_axial_pos) { + /* all symmetries except in 's'*/ + if (Zdup >= 0 && Zdup <= maxplane) { + Projptr[ring0][0][0][0] += d * Bild[Zdup][Y][X]; + Projptr[ring0][0][0][2] += d * Bild[Zdup][X][-Y]; + if ((Siddon == 4) || (Siddon == 3)) { + Projptr[ring0][1][0][1] += d * Bild[Zdup][X][Y]; + Projptr[ring0][1][0][3] += d * Bild[Zdup][Y][-X]; + } + if ((Siddon == 1) || (Siddon == 3)) { + Projptr[ring0][1][1][0] += d * Bild[Zdup][-Y][-X]; + Projptr[ring0][1][1][2] += d * Bild[Zdup][-X][Y]; + } + if (Siddon == 3) { + Projptr[ring0][0][1][1] += d * Bild[Zdup][-X][-Y]; + Projptr[ring0][0][1][3] += d * Bild[Zdup][-Y][X]; + } + } + if (Qdup >= 0 && Qdup <= maxplane) { + if ((Siddon == 4) || (Siddon == 3)) { + Projptr[ring0][0][0][1] += d * Bild[Qdup][X][Y]; + Projptr[ring0][0][0][3] += d * Bild[Qdup][Y][-X]; + } + if ((Siddon == 1) || (Siddon == 3)) { + Projptr[ring0][0][1][0] += d * Bild[Qdup][-Y][-X]; + Projptr[ring0][0][1][2] += d * Bild[Qdup][-X][Y]; + } + if (Siddon == 3) { + Projptr[ring0][1][1][1] += d * Bild[Qdup][-X][-Y]; + Projptr[ring0][1][1][3] += d * Bild[Qdup][-Y][X]; + } + Projptr[ring0][1][0][0] += d * Bild[Qdup][Y][X]; + Projptr[ring0][1][0][2] += d * Bild[Qdup][X][-Y]; + } + } + a = az; + az += inc_z; + Z++; + Q--; } - else if (ay < az) { /* LOR leaves voxel through xz-plane */ + else if (ay < az) { /* LOR leaves voxel through xz-plane */ const float d = ay - a; int Zdup = Z; int Qdup = Q; - for (int ring0 = rmin; - ring0 <= rmax; - ring0++, Zdup += num_planes_per_axial_pos, Qdup +=num_planes_per_axial_pos ) - { - /* all symmetries except in 's' */ - if (Zdup >= 0 && Zdup <= maxplane) { - Projptr[ring0][0][0][0] += d * Bild[Zdup][Y][X]; - Projptr[ring0][0][0][2] += d * Bild[Zdup][X][-Y]; - if ((Siddon == 4) || (Siddon == 3)) { - Projptr[ring0][1][0][1] += d * Bild[Zdup][X][Y]; - Projptr[ring0][1][0][3] += d * Bild[Zdup][Y][-X]; - } - if ((Siddon == 1) || (Siddon == 3)) { - Projptr[ring0][1][1][0] += d * Bild[Zdup][-Y][-X]; - Projptr[ring0][1][1][2] += d * Bild[Zdup][-X][Y]; - } - if (Siddon == 3) { - Projptr[ring0][0][1][1] += d * Bild[Zdup][-X][-Y]; - Projptr[ring0][0][1][3] += d * Bild[Zdup][-Y][X]; - } - } - if (Qdup >= 0 && Qdup <= maxplane) { - if ((Siddon == 4) || (Siddon == 3)) { - Projptr[ring0][0][0][1] += d * Bild[Qdup][X][Y]; - Projptr[ring0][0][0][3] += d * Bild[Qdup][Y][-X]; - } - if ((Siddon == 1) || (Siddon == 3)) { - Projptr[ring0][0][1][0] += d * Bild[Qdup][-Y][-X]; - Projptr[ring0][0][1][2] += d * Bild[Qdup][-X][Y]; - } - if (Siddon == 3) { - Projptr[ring0][1][1][1] += d * Bild[Qdup][-X][-Y]; - Projptr[ring0][1][1][3] += d * Bild[Qdup][-Y][X]; - } - Projptr[ring0][1][0][0] += d * Bild[Qdup][Y][X]; - Projptr[ring0][1][0][2] += d * Bild[Qdup][X][-Y]; - } - + for (int ring0 = rmin; ring0 <= rmax; ring0++, Zdup += num_planes_per_axial_pos, Qdup += num_planes_per_axial_pos) { + /* all symmetries except in 's' */ + if (Zdup >= 0 && Zdup <= maxplane) { + Projptr[ring0][0][0][0] += d * Bild[Zdup][Y][X]; + Projptr[ring0][0][0][2] += d * Bild[Zdup][X][-Y]; + if ((Siddon == 4) || (Siddon == 3)) { + Projptr[ring0][1][0][1] += d * Bild[Zdup][X][Y]; + Projptr[ring0][1][0][3] += d * Bild[Zdup][Y][-X]; + } + if ((Siddon == 1) || (Siddon == 3)) { + Projptr[ring0][1][1][0] += d * Bild[Zdup][-Y][-X]; + Projptr[ring0][1][1][2] += d * Bild[Zdup][-X][Y]; + } + if (Siddon == 3) { + Projptr[ring0][0][1][1] += d * Bild[Zdup][-X][-Y]; + Projptr[ring0][0][1][3] += d * Bild[Zdup][-Y][X]; + } + } + if (Qdup >= 0 && Qdup <= maxplane) { + if ((Siddon == 4) || (Siddon == 3)) { + Projptr[ring0][0][0][1] += d * Bild[Qdup][X][Y]; + Projptr[ring0][0][0][3] += d * Bild[Qdup][Y][-X]; + } + if ((Siddon == 1) || (Siddon == 3)) { + Projptr[ring0][0][1][0] += d * Bild[Qdup][-Y][-X]; + Projptr[ring0][0][1][2] += d * Bild[Qdup][-X][Y]; + } + if (Siddon == 3) { + Projptr[ring0][1][1][1] += d * Bild[Qdup][-X][-Y]; + Projptr[ring0][1][1][3] += d * Bild[Qdup][-Y][X]; + } + Projptr[ring0][1][0][0] += d * Bild[Qdup][Y][X]; + Projptr[ring0][1][0][2] += d * Bild[Qdup][X][-Y]; + } } - a = ay; ay += inc_y; + a = ay; + ay += inc_y; Y++; - } else {/* LOR leaves voxel through xy-plane */ + } else { /* LOR leaves voxel through xy-plane */ const float d = az - a; int Zdup = Z; int Qdup = Q; - for (int ring0 = rmin; - ring0 <= rmax; - ring0++, Zdup += num_planes_per_axial_pos, Qdup +=num_planes_per_axial_pos ) - { - /* all symmetries except in 's' */ - if (Zdup >= 0 && Zdup <= maxplane) { - Projptr[ring0][0][0][0] += d * Bild[Zdup][Y][X]; - Projptr[ring0][0][0][2] += d * Bild[Zdup][X][-Y]; - if ((Siddon == 4) || (Siddon == 3)) { - Projptr[ring0][1][0][1] += d * Bild[Zdup][X][Y]; - Projptr[ring0][1][0][3] += d * Bild[Zdup][Y][-X]; - } - if ((Siddon == 1) || (Siddon == 3)) { - Projptr[ring0][1][1][0] += d * Bild[Zdup][-Y][-X]; - Projptr[ring0][1][1][2] += d * Bild[Zdup][-X][Y]; - } - if (Siddon == 3) { - Projptr[ring0][0][1][1] += d * Bild[Zdup][-X][-Y]; - Projptr[ring0][0][1][3] += d * Bild[Zdup][-Y][X]; - } - } - if (Qdup >= 0 && Qdup <= maxplane) { - if ((Siddon == 4) || (Siddon == 3)) { - Projptr[ring0][0][0][1] += d * Bild[Qdup][X][Y]; - Projptr[ring0][0][0][3] += d * Bild[Qdup][Y][-X]; - } - if ((Siddon == 1) || (Siddon == 3)) { - Projptr[ring0][0][1][0] += d * Bild[Qdup][-Y][-X]; - Projptr[ring0][0][1][2] += d * Bild[Qdup][-X][Y]; - } - if (Siddon == 3) { - Projptr[ring0][1][1][1] += d * Bild[Qdup][-X][-Y]; - Projptr[ring0][1][1][3] += d * Bild[Qdup][-Y][X]; - } - Projptr[ring0][1][0][0] += d * Bild[Qdup][Y][X]; - Projptr[ring0][1][0][2] += d * Bild[Qdup][X][-Y]; - } - + for (int ring0 = rmin; ring0 <= rmax; ring0++, Zdup += num_planes_per_axial_pos, Qdup += num_planes_per_axial_pos) { + /* all symmetries except in 's' */ + if (Zdup >= 0 && Zdup <= maxplane) { + Projptr[ring0][0][0][0] += d * Bild[Zdup][Y][X]; + Projptr[ring0][0][0][2] += d * Bild[Zdup][X][-Y]; + if ((Siddon == 4) || (Siddon == 3)) { + Projptr[ring0][1][0][1] += d * Bild[Zdup][X][Y]; + Projptr[ring0][1][0][3] += d * Bild[Zdup][Y][-X]; + } + if ((Siddon == 1) || (Siddon == 3)) { + Projptr[ring0][1][1][0] += d * Bild[Zdup][-Y][-X]; + Projptr[ring0][1][1][2] += d * Bild[Zdup][-X][Y]; + } + if (Siddon == 3) { + Projptr[ring0][0][1][1] += d * Bild[Zdup][-X][-Y]; + Projptr[ring0][0][1][3] += d * Bild[Zdup][-Y][X]; + } + } + if (Qdup >= 0 && Qdup <= maxplane) { + if ((Siddon == 4) || (Siddon == 3)) { + Projptr[ring0][0][0][1] += d * Bild[Qdup][X][Y]; + Projptr[ring0][0][0][3] += d * Bild[Qdup][Y][-X]; + } + if ((Siddon == 1) || (Siddon == 3)) { + Projptr[ring0][0][1][0] += d * Bild[Qdup][-Y][-X]; + Projptr[ring0][0][1][2] += d * Bild[Qdup][-X][Y]; + } + if (Siddon == 3) { + Projptr[ring0][1][1][1] += d * Bild[Qdup][-X][-Y]; + Projptr[ring0][1][1][3] += d * Bild[Qdup][-Y][X]; + } + Projptr[ring0][1][0][0] += d * Bild[Qdup][Y][X]; + Projptr[ring0][1][0][2] += d * Bild[Qdup][X][-Y]; + } } - a = az ; az += inc_z; + a = az; + az += inc_z; Z++; Q--; } - } /* Ende while (a(Array<4,float> &Projptr, const VoxelsOnCartesianGrid &, - const shared_ptr proj_data_info_sptr, - const float cphi, const float sphi, const float delta, - const float s_in_mm, - const float R, const int min_ax_pos_num, const int max_ax_pos_num, const float offset, - const int num_planes_per_axial_pos, - const float axial_pos_to_z_offset, - const float norm_factor, - const bool restrict_to_cylindrical_FOV); - - -template -bool -ForwardProjectorByBinUsingRayTracing:: -proj_Siddon<2>(Array<4,float> &Projptr, const VoxelsOnCartesianGrid &, - const shared_ptr proj_data_info_sptr, - const float cphi, const float sphi, const float delta, - const float s_in_mm, - const float R, const int min_ax_pos_num, const int max_ax_pos_num, const float offset, - const int num_planes_per_axial_pos, - const float axial_pos_to_z_offset, - const float norm_factor, - const bool restrict_to_cylindrical_FOV); - - -template -bool -ForwardProjectorByBinUsingRayTracing:: -proj_Siddon<3>(Array<4,float> &Projptr, const VoxelsOnCartesianGrid &, - const shared_ptr proj_data_info_sptr, - const float cphi, const float sphi, const float delta, - const float s_in_mm, - const float R, const int min_ax_pos_num, const int max_ax_pos_num, const float offset, - const int num_planes_per_axial_pos, - const float axial_pos_to_z_offset, - const float norm_factor, - const bool restrict_to_cylindrical_FOV); - - -template -bool -ForwardProjectorByBinUsingRayTracing:: -proj_Siddon<4>(Array<4,float> &Projptr, const VoxelsOnCartesianGrid &, - const shared_ptr proj_data_info_sptr, - const float cphi, const float sphi, const float delta, - const float s_in_mm, - const float R, const int min_ax_pos_num, const int max_ax_pos_num, const float offset, - const int num_planes_per_axial_pos, - const float axial_pos_to_z_offset, - const float norm_factor, - const bool restrict_to_cylindrical_FOV); - -#endif +template bool ForwardProjectorByBinUsingRayTracing::proj_Siddon<1>( + Array<4, float>& Projptr, const VoxelsOnCartesianGrid&, + const shared_ptr proj_data_info_sptr, const float cphi, const float sphi, const float delta, + const float s_in_mm, const float R, const int min_ax_pos_num, const int max_ax_pos_num, const float offset, + const int num_planes_per_axial_pos, const float axial_pos_to_z_offset, const float norm_factor, + const bool restrict_to_cylindrical_FOV); + +template bool ForwardProjectorByBinUsingRayTracing::proj_Siddon<2>( + Array<4, float>& Projptr, const VoxelsOnCartesianGrid&, + const shared_ptr proj_data_info_sptr, const float cphi, const float sphi, const float delta, + const float s_in_mm, const float R, const int min_ax_pos_num, const int max_ax_pos_num, const float offset, + const int num_planes_per_axial_pos, const float axial_pos_to_z_offset, const float norm_factor, + const bool restrict_to_cylindrical_FOV); + +template bool ForwardProjectorByBinUsingRayTracing::proj_Siddon<3>( + Array<4, float>& Projptr, const VoxelsOnCartesianGrid&, + const shared_ptr proj_data_info_sptr, const float cphi, const float sphi, const float delta, + const float s_in_mm, const float R, const int min_ax_pos_num, const int max_ax_pos_num, const float offset, + const int num_planes_per_axial_pos, const float axial_pos_to_z_offset, const float norm_factor, + const bool restrict_to_cylindrical_FOV); + +template bool ForwardProjectorByBinUsingRayTracing::proj_Siddon<4>( + Array<4, float>& Projptr, const VoxelsOnCartesianGrid&, + const shared_ptr proj_data_info_sptr, const float cphi, const float sphi, const float delta, + const float s_in_mm, const float R, const int min_ax_pos_num, const int max_ax_pos_num, const float offset, + const int num_planes_per_axial_pos, const float axial_pos_to_z_offset, const float norm_factor, + const bool restrict_to_cylindrical_FOV); + +#endif END_NAMESPACE_STIR diff --git a/src/recon_buildblock/FourierRebinning.cxx b/src/recon_buildblock/FourierRebinning.cxx index f81f8294c2..582de858ce 100644 --- a/src/recon_buildblock/FourierRebinning.cxx +++ b/src/recon_buildblock/FourierRebinning.cxx @@ -1,6 +1,6 @@ -/*! - \file - \brief FORE kernel +/*! + \file + \brief FORE kernel \ingroup recon_buildblock \author Matthias Egger \author Claire LABBE @@ -30,13 +30,12 @@ See STIR/LICENSE.txt for details */ - #include "stir/recon_buildblock/FourierRebinning.h" #include "stir/Scanner.h" #include "stir/ProjDataInfoCylindrical.h" #include "stir/ProjDataInterfile.h" #include "stir/SegmentBySinogram.h" -#include "stir/Bin.h" +#include "stir/Bin.h" #include "stir/IndexRange3D.h" #include "stir/IndexRange2D.h" #include "stir/Succeeded.h" @@ -61,47 +60,36 @@ using std::ios; using std::ofstream; #endif - START_NAMESPACE_STIR +const char* const FourierRebinning::registered_name = "FORE"; -const char * const -FourierRebinning::registered_name = "FORE"; - void -FourierRebinning:: -initialise_keymap() -{ +FourierRebinning::initialise_keymap() { base_type::initialise_keymap(); parser.add_start_key("FORE Parameters"); parser.add_stop_key("End FORE Parameters"); parser.add_key("Smallest angular frequency", &kmin); - parser.add_key("Smallest transaxial frequency",&wmin); + parser.add_key("Smallest transaxial frequency", &wmin); parser.add_key("Delta max for small omega", &deltamin); parser.add_key("Index for consistency", &kc); parser.add_key("FORE debug level", &fore_debug_level); - } - -bool -FourierRebinning:: -post_processing() -{ +bool +FourierRebinning::post_processing() { if (base_type::post_processing() == true) return true; // TODO check other parameterssegment return false; -} +} void -FourierRebinning:: -set_defaults() -{ +FourierRebinning::set_defaults() { base_type::set_defaults(); -//CON There is probably nothing like a set of FORE parameters which make sense for all scanners. -//CON Therefore default it to illegal values such that the application will terminate if the user -//CON does not set them to values which make sense via the parameter file or the set functions. + // CON There is probably nothing like a set of FORE parameters which make sense for all scanners. + // CON Therefore default it to illegal values such that the application will terminate if the user + // CON does not set them to values which make sense via the parameter file or the set functions. kmin = -1; wmin = -1; deltamin = -1; @@ -109,584 +97,534 @@ set_defaults() fore_debug_level = 0; } -FourierRebinning:: -FourierRebinning() -{ - set_defaults(); -} - +FourierRebinning::FourierRebinning() { set_defaults(); } Succeeded -FourierRebinning:: -rebin() -{ +FourierRebinning::rebin() { - if (proj_data_sptr->get_proj_data_info_sptr()->is_tof_data()) - { - error("FORE Rebinning :: Not supported for TOF data. Aborted"); - return Succeeded::no; - } + if (proj_data_sptr->get_proj_data_info_sptr()->is_tof_data()) { + error("FORE Rebinning :: Not supported for TOF data. Aborted"); + return Succeeded::no; + } start_timers(); CPUTimer timer; timer.start(); - - //CON return value + + // CON return value Succeeded success = Succeeded::yes; - - //CL Find the number of views and tangential positions power of two + + // CL Find the number of views and tangential positions power of two int num_views_pow2; - for ( num_views_pow2 = 1; num_views_pow2 < 2*proj_data_sptr->get_num_views() && num_views_pow2 < (1<<15); num_views_pow2*=2); + for (num_views_pow2 = 1; num_views_pow2 < 2 * proj_data_sptr->get_num_views() && num_views_pow2 < (1 << 15); + num_views_pow2 *= 2) + ; int num_tang_poss_pow2; - for ( num_tang_poss_pow2 = 1; num_tang_poss_pow2 < proj_data_sptr->get_num_tangential_poss() && num_tang_poss_pow2 < (1<<15); num_tang_poss_pow2*=2); - - //CL Initialise the 2D Fourier transform of all rebinned sinograms P(w,k)=0 - const int num_planes = proj_data_sptr->get_proj_data_info_sptr()->get_scanner_ptr()->get_num_rings()*2-1; + for (num_tang_poss_pow2 = 1; num_tang_poss_pow2 < proj_data_sptr->get_num_tangential_poss() && num_tang_poss_pow2 < (1 << 15); + num_tang_poss_pow2 *= 2) + ; + + // CL Initialise the 2D Fourier transform of all rebinned sinograms P(w,k)=0 + const int num_planes = proj_data_sptr->get_proj_data_info_sptr()->get_scanner_ptr()->get_num_rings() * 2 - 1; - Array<3,std::complex > FT_rebinned_data(IndexRange3D(0, num_planes-1, 0, num_views_pow2-1, 0, num_tang_poss_pow2-1)); - Array<3,float> Weights_for_FT_rebinned_data(IndexRange3D(0, num_planes-1, 0,num_views_pow2-1, 0,num_tang_poss_pow2-1)); - //CON some statistics - PETCount_rebinned num_rebinned(0,0,0); + Array<3, std::complex> FT_rebinned_data( + IndexRange3D(0, num_planes - 1, 0, num_views_pow2 - 1, 0, num_tang_poss_pow2 - 1)); + Array<3, float> Weights_for_FT_rebinned_data(IndexRange3D(0, num_planes - 1, 0, num_views_pow2 - 1, 0, num_tang_poss_pow2 - 1)); + // CON some statistics + PETCount_rebinned num_rebinned(0, 0, 0); - //CON Create the output (rebinned projection data) data structure and set the properties of the rebinned sinograms + // CON Create the output (rebinned projection data) data structure and set the properties of the rebinned sinograms shared_ptr rebinned_proj_data_sptr; - //CON initialise the new projection data properties by copying the properties from the input projection data. - shared_ptr rebinned_proj_data_info_sptr - ( proj_data_sptr->get_proj_data_info_sptr()->clone()); - //CON Adapt the properties that will be modified by the rebinning. - rebinned_proj_data_info_sptr->set_num_views(num_views_pow2/2); - //CON After rebinning we have of course only "direct" sinograms left e.q only segment 0 exists - rebinned_proj_data_info_sptr->reduce_segment_range(0,0); - //CON maximal ring difference a LOR in the largest segment that is going to be rebinned - const int max_delta = dynamic_cast - (*proj_data_sptr->get_proj_data_info_sptr()).get_max_ring_difference(max_segment_num_to_process); - //CON The maximum/minimum ring difference covered by LORs written to the rebinned sinogram changed to the maximum ring - //CON difference covered by the largest segment that has been rebinned. + // CON initialise the new projection data properties by copying the properties from the input projection data. + shared_ptr rebinned_proj_data_info_sptr(proj_data_sptr->get_proj_data_info_sptr()->clone()); + // CON Adapt the properties that will be modified by the rebinning. + rebinned_proj_data_info_sptr->set_num_views(num_views_pow2 / 2); + // CON After rebinning we have of course only "direct" sinograms left e.q only segment 0 exists + rebinned_proj_data_info_sptr->reduce_segment_range(0, 0); + // CON maximal ring difference a LOR in the largest segment that is going to be rebinned + const int max_delta = dynamic_cast(*proj_data_sptr->get_proj_data_info_sptr()) + .get_max_ring_difference(max_segment_num_to_process); + // CON The maximum/minimum ring difference covered by LORs written to the rebinned sinogram changed to the maximum ring + // CON difference covered by the largest segment that has been rebinned. dynamic_cast(*rebinned_proj_data_info_sptr).set_min_ring_difference(-max_delta, 0); dynamic_cast(*rebinned_proj_data_info_sptr).set_max_ring_difference(max_delta, 0); - //CON minimal and maximal axial position number. As usual we start with axial position 0 in segment 0 + // CON minimal and maximal axial position number. As usual we start with axial position 0 in segment 0 rebinned_proj_data_info_sptr->set_min_axial_pos_num(0, 0); - rebinned_proj_data_info_sptr->set_max_axial_pos_num(num_planes-1,0); - //CON create the output (interfile) file to where the rebinned data will be written. - rebinned_proj_data_sptr.reset(new ProjDataInterfile (proj_data_sptr->get_exam_info_sptr(), - rebinned_proj_data_info_sptr,output_filename_prefix)); - //CON get scanner related parameters needed for the rebinning kernel. - //CON create a scanner object. The scanner type is identified from the projection data info. + rebinned_proj_data_info_sptr->set_max_axial_pos_num(num_planes - 1, 0); + // CON create the output (interfile) file to where the rebinned data will be written. + rebinned_proj_data_sptr.reset( + new ProjDataInterfile(proj_data_sptr->get_exam_info_sptr(), rebinned_proj_data_info_sptr, output_filename_prefix)); + // CON get scanner related parameters needed for the rebinning kernel. + // CON create a scanner object. The scanner type is identified from the projection data info. const Scanner* scanner = rebinned_proj_data_sptr->get_proj_data_info_sptr()->get_scanner_ptr(); - const float half_distance_between_rings = scanner->get_ring_spacing()/2.F; - const float sampling_distance_in_s = rebinned_proj_data_info_sptr->get_sampling_in_s(Bin(0,0,0,0)); - const float radial_sampling_freq_w = float(2.*_PI)/sampling_distance_in_s/num_tang_poss_pow2; - //CON D = #bins * binsize, R = D / 2 - const float R_field_of_view_mm = ((int) (rebinned_proj_data_info_sptr->get_num_tangential_poss() / 2) - 1)*sampling_distance_in_s; + const float half_distance_between_rings = scanner->get_ring_spacing() / 2.F; + const float sampling_distance_in_s = rebinned_proj_data_info_sptr->get_sampling_in_s(Bin(0, 0, 0, 0)); + const float radial_sampling_freq_w = float(2. * _PI) / sampling_distance_in_s / num_tang_poss_pow2; + // CON D = #bins * binsize, R = D / 2 + const float R_field_of_view_mm = + ((int)(rebinned_proj_data_info_sptr->get_num_tangential_poss() / 2) - 1) * sampling_distance_in_s; const float scanner_space_between_rings = scanner->get_ring_spacing(); const float scanner_ring_radius = scanner->get_effective_ring_radius(); const float ratio_ring_spacing_to_ring_radius = scanner_space_between_rings / scanner_ring_radius; - //CON Check that the user defineable FORE parameters are inside a possible range of values - if(fore_check_parameters(num_tang_poss_pow2,num_views_pow2,max_segment_num_to_process) != Succeeded::yes){ - error("FORE Rebinning :: Setup failed "); - }; - - //CON Loop over all positive segments. Negative segments (those with negative (opposite) ring differences - //CON will be merged with the positive segment 180 degree sinograms to form a 360 degree segment. - for (int seg_num=0; seg_num <=max_segment_num_to_process ; seg_num++){ - + // CON Check that the user defineable FORE parameters are inside a possible range of values + if (fore_check_parameters(num_tang_poss_pow2, num_views_pow2, max_segment_num_to_process) != Succeeded::yes) { + error("FORE Rebinning :: Setup failed "); + }; + + // CON Loop over all positive segments. Negative segments (those with negative (opposite) ring differences + // CON will be merged with the positive segment 180 degree sinograms to form a 360 degree segment. + for (int seg_num = 0; seg_num <= max_segment_num_to_process; seg_num++) { + info(boost::format("FORE Rebinning :: Processing segment No %1% *") % seg_num); - // TODO at present, the processing is done by segment. However, it's fairly easy to - // change this to by sinogram (the rebinning call below will do everything - // as a loop over axial_pos anyway). - // This would save some memory overhead, and increase potential for parallelisation later. - // (The parallelised version of FORE in PARAPET was organised this way). - - //CON get one (positive) segment - SegmentBySinogram segment = proj_data_sptr->get_segment_by_sinogram(seg_num); - - //CON Retrieve some segment dependent properties needed for the rebinning kernel - const ProjDataInfoCylindrical& proj_data_info_cylindrical = dynamic_cast(*segment.get_proj_data_info_sptr()); - const float average_ring_difference_in_segment = proj_data_info_cylindrical.get_average_ring_difference(segment.get_segment_num()); - - - //CL Form a 360 degree sinogram by merging two 180 degree segments with opposite ring difference - //CL to get a new segment sampled over 2*pi (where 0 < view < pi) - //CON See DeFrise paper (exact and approximate rebinning algorithms for 3D PET data), Sec IV,C (p153) - //KT TODO this is currently not a good idea, as all ProjDataInfo classes assume that - //KT views go from 0 to Pi. - //CON Get the corresponding (negative) segment with the same absolute but opposite obliqueness - const SegmentBySinogram segment_neg = proj_data_sptr->get_segment_by_sinogram(-seg_num); - //CON Expand the (positive) segment such that the two segments can be merged - segment.grow(IndexRange3D(segment.get_min_axial_pos_num(), segment.get_max_axial_pos_num(), - 0,2*segment.get_num_views()-1, segment.get_min_tangential_pos_num(), segment.get_max_tangential_pos_num() - )); - - - //CON merge the two segments to form "360 degrees" sinograms - const int min_tangential_pos_num = std::max(segment_neg.get_min_tangential_pos_num(), - -segment.get_max_tangential_pos_num()); - - - const int max_tangential_pos_num = std::min(segment_neg.get_max_tangential_pos_num(), - -segment.get_min_tangential_pos_num()); - - - for (int ring = segment.get_min_axial_pos_num(); ring <= segment.get_max_axial_pos_num(); ring++) - for (int view = segment_neg.get_min_view_num(); view <= segment_neg.get_max_view_num(); view++) - for( int tangential_pos_num = min_tangential_pos_num; tangential_pos_num<=max_tangential_pos_num; tangential_pos_num++) - segment[ring][view+segment_neg.get_num_views()][tangential_pos_num] = segment_neg[ring][view][-tangential_pos_num]; - - // CON in debug mode visualize the segment - if(fore_debug_level>=2) - { - char s[100]; - sprintf(s, "(extended) segment by sinogram %d",segment.get_segment_num()); - display(segment, segment.find_max(), s); - } - - //CON the sinogramm dimensions need to have a dimension which is a power of 2 (required by the FFT algorithm) - //CON for s (radial coordinate) pad the sinogramm with zeros to form a larger array. - //CON the phi (azimuthal cordinate (view)) coordinate is periodic. The samples need to be interpolated to the - //CON to the new matrix size. Do this by linear interpolation. - //CON -> DeFrise p. 153 Sec IV.C + // TODO at present, the processing is done by segment. However, it's fairly easy to + // change this to by sinogram (the rebinning call below will do everything + // as a loop over axial_pos anyway). + // This would save some memory overhead, and increase potential for parallelisation later. + // (The parallelised version of FORE in PARAPET was organised this way). + + // CON get one (positive) segment + SegmentBySinogram segment = proj_data_sptr->get_segment_by_sinogram(seg_num); + + // CON Retrieve some segment dependent properties needed for the rebinning kernel + const ProjDataInfoCylindrical& proj_data_info_cylindrical = + dynamic_cast(*segment.get_proj_data_info_sptr()); + const float average_ring_difference_in_segment = + proj_data_info_cylindrical.get_average_ring_difference(segment.get_segment_num()); + + // CL Form a 360 degree sinogram by merging two 180 degree segments with opposite ring difference + // CL to get a new segment sampled over 2*pi (where 0 < view < pi) + // CON See DeFrise paper (exact and approximate rebinning algorithms for 3D PET data), Sec IV,C (p153) + // KT TODO this is currently not a good idea, as all ProjDataInfo classes assume that + // KT views go from 0 to Pi. + // CON Get the corresponding (negative) segment with the same absolute but opposite obliqueness + const SegmentBySinogram segment_neg = proj_data_sptr->get_segment_by_sinogram(-seg_num); + // CON Expand the (positive) segment such that the two segments can be merged + segment.grow(IndexRange3D(segment.get_min_axial_pos_num(), segment.get_max_axial_pos_num(), 0, + 2 * segment.get_num_views() - 1, segment.get_min_tangential_pos_num(), + segment.get_max_tangential_pos_num())); + + // CON merge the two segments to form "360 degrees" sinograms + const int min_tangential_pos_num = std::max(segment_neg.get_min_tangential_pos_num(), -segment.get_max_tangential_pos_num()); + + const int max_tangential_pos_num = std::min(segment_neg.get_max_tangential_pos_num(), -segment.get_min_tangential_pos_num()); + + for (int ring = segment.get_min_axial_pos_num(); ring <= segment.get_max_axial_pos_num(); ring++) + for (int view = segment_neg.get_min_view_num(); view <= segment_neg.get_max_view_num(); view++) + for (int tangential_pos_num = min_tangential_pos_num; tangential_pos_num <= max_tangential_pos_num; tangential_pos_num++) + segment[ring][view + segment_neg.get_num_views()][tangential_pos_num] = segment_neg[ring][view][-tangential_pos_num]; + + // CON in debug mode visualize the segment + if (fore_debug_level >= 2) { + char s[100]; + sprintf(s, "(extended) segment by sinogram %d", segment.get_segment_num()); + display(segment, segment.find_max(), s); + } + + // CON the sinogramm dimensions need to have a dimension which is a power of 2 (required by the FFT algorithm) + // CON for s (radial coordinate) pad the sinogramm with zeros to form a larger array. + // CON the phi (azimuthal cordinate (view)) coordinate is periodic. The samples need to be interpolated to the + // CON to the new matrix size. Do this by linear interpolation. + // CON -> DeFrise p. 153 Sec IV.C do_adjust_nb_views_to_pow2(segment); - - //CON The sinogramm data is now in the required format and ready for rebinning. - //CON The rebinned data is stored in a 3 dimensional array of complex numbers (FT_rebinned_data). - //CON FT_rebinned_data[plane][w(FT of s)][k(FT of phi)] - //CON Weight has the same dimensions. It stores normalisation factors (floats) - //CON to take into account the variable number of contributions to each frequency. - do_rebinning(FT_rebinned_data, Weights_for_FT_rebinned_data, num_rebinned, segment, - num_tang_poss_pow2, num_views_pow2, num_planes, average_ring_difference_in_segment, - half_distance_between_rings, sampling_distance_in_s, radial_sampling_freq_w, R_field_of_view_mm, - ratio_ring_spacing_to_ring_radius); - - } //CON end loop over segments. - - - //CON Some statistics + // CON The sinogramm data is now in the required format and ready for rebinning. + // CON The rebinned data is stored in a 3 dimensional array of complex numbers (FT_rebinned_data). + // CON FT_rebinned_data[plane][w(FT of s)][k(FT of phi)] + // CON Weight has the same dimensions. It stores normalisation factors (floats) + // CON to take into account the variable number of contributions to each frequency. + do_rebinning(FT_rebinned_data, Weights_for_FT_rebinned_data, num_rebinned, segment, num_tang_poss_pow2, num_views_pow2, + num_planes, average_ring_difference_in_segment, half_distance_between_rings, sampling_distance_in_s, + radial_sampling_freq_w, R_field_of_view_mm, ratio_ring_spacing_to_ring_radius); + + } // CON end loop over segments. + + // CON Some statistics std::cout << "\nFORE Rebinning :: Total rebinning count: \n"; do_display_count(num_rebinned); - info("FORE Rebinning :: Inverse FFT the rebinned sinograms " ); - //CL now finally fill in the new sinogram s + info("FORE Rebinning :: Inverse FFT the rebinned sinograms "); + // CL now finally fill in the new sinogram s SegmentBySinogram sino2D_rebinned = rebinned_proj_data_sptr->get_empty_segment_by_sinogram(0); - - for (int plane=FT_rebinned_data.get_min_index();plane <= FT_rebinned_data.get_max_index(); plane++){ - - if(plane%10==0) info(boost::format("FORE Rebinning :: Inv FFT rebinned z-position (slice) = %1%") % plane); - - //CON Create a temporary 2D array of complex numbers to store the rebinned and summed fourier coefficients for one slice. - //CON This data is then inverse FFTd and copied to a sinogram data structure. - //CON Strictly seen this temporary data structure is no longer necessary because the inv. FFT could now be - //CON be done on FT_rebinned_data itself. Since one has to anyway access the full FTdata matrix to apply - //CON the rebinning weights - //CON before the inv. FFT can be applied this is not much overhead and it can be left like it was done when still - //CON using the numerical receipies FFT code. - Array<2, std::complex > FT_rebinned_sinogram(IndexRange2D(0,num_tang_poss_pow2-1,0,num_views_pow2/2)); - //CON fourier_for_real_data will resize the array to its appropriate dimensions - Array<2,float> rebinned_sinogram(IndexRange2D(0,1,0,1)); - - //CON Normalise the rebinned sinograms by applying the weight factors - //CON See DeFrise IV.D p154. - for (int j = 0; j < num_tang_poss_pow2; j++) { - for (int i = 0; i <= num_views_pow2/2; i++) { - const float Actual_Weight = (Weights_for_FT_rebinned_data[plane][i][j] == 0) ? 0 : - 1.F/(Weights_for_FT_rebinned_data[plane][i][j]); - FT_rebinned_sinogram[j][i] = FT_rebinned_data[plane][i][j]* Actual_Weight; - - } - } - - if(fore_debug_level>=3) - { + for (int plane = FT_rebinned_data.get_min_index(); plane <= FT_rebinned_data.get_max_index(); plane++) { + + if (plane % 10 == 0) + info(boost::format("FORE Rebinning :: Inv FFT rebinned z-position (slice) = %1%") % plane); + + // CON Create a temporary 2D array of complex numbers to store the rebinned and summed fourier coefficients for one slice. + // CON This data is then inverse FFTd and copied to a sinogram data structure. + // CON Strictly seen this temporary data structure is no longer necessary because the inv. FFT could now be + // CON be done on FT_rebinned_data itself. Since one has to anyway access the full FTdata matrix to apply + // CON the rebinning weights + // CON before the inv. FFT can be applied this is not much overhead and it can be left like it was done when still + // CON using the numerical receipies FFT code. + Array<2, std::complex> FT_rebinned_sinogram(IndexRange2D(0, num_tang_poss_pow2 - 1, 0, num_views_pow2 / 2)); + // CON fourier_for_real_data will resize the array to its appropriate dimensions + Array<2, float> rebinned_sinogram(IndexRange2D(0, 1, 0, 1)); + + // CON Normalise the rebinned sinograms by applying the weight factors + // CON See DeFrise IV.D p154. + for (int j = 0; j < num_tang_poss_pow2; j++) { + for (int i = 0; i <= num_views_pow2 / 2; i++) { + const float Actual_Weight = + (Weights_for_FT_rebinned_data[plane][i][j] == 0) ? 0 : 1.F / (Weights_for_FT_rebinned_data[plane][i][j]); + FT_rebinned_sinogram[j][i] = FT_rebinned_data[plane][i][j] * Actual_Weight; + } + } + + if (fore_debug_level >= 3) { char s[100]; - Array<2,float> real(FT_rebinned_sinogram.get_index_range()); - for (int i = 0; i < num_views_pow2; i++) - for (int j = 0; j <= num_tang_poss_pow2/2; j++) + Array<2, float> real(FT_rebinned_sinogram.get_index_range()); + for (int i = 0; i < num_views_pow2; i++) + for (int j = 0; j <= num_tang_poss_pow2 / 2; j++) real[i][j] = FT_rebinned_sinogram[i][j].real(); - sprintf(s, "real part of FT of rebinned (extended) sinogram %d",plane); + sprintf(s, "real part of FT of rebinned (extended) sinogram %d", plane); display(real, s, real.find_max()); - for (int i = 0; i < num_views_pow2; i++) - for (int j = 0; j <= num_tang_poss_pow2/2; j++) + for (int i = 0; i < num_views_pow2; i++) + for (int j = 0; j <= num_tang_poss_pow2 / 2; j++) real[i][j] = FT_rebinned_sinogram[i][j].imag(); - sprintf(s, "imag part of FT of rebinned (extended) sinogram %d",plane); + sprintf(s, "imag part of FT of rebinned (extended) sinogram %d", plane); display(real, s, real.find_max()); } - - //CON inverse FFT the rebinned sinograms - rebinned_sinogram = inverse_fourier_for_real_data(FT_rebinned_sinogram); - - //CL Keep only one half of data [o.._PI] - for (int i=0;i<(int)(num_views_pow2/2);i++) - for (int j=0;j Min = %1%, Max= %2%, Sum = %3%") - % sino2D_rebinned.find_min() - % sino2D_rebinned.find_max() - % sino2D_rebinned.sum()); - - //CON finally write the rebinned sinograms to file - const Succeeded success_this_sino = - rebinned_proj_data_sptr->set_segment(sino2D_rebinned); - - - if (success == Succeeded::yes && success_this_sino == Succeeded::no) - success = Succeeded::no; - stop_timers(); -//CON presently not very useful. Maybe one could define a vriable fore_debug_level and -//CON only write in case of debugging - if(fore_debug_level>0) - do_log_file(); - - return success; -} + // CON inverse FFT the rebinned sinograms + rebinned_sinogram = inverse_fourier_for_real_data(FT_rebinned_sinogram); + // CL Keep only one half of data [o.._PI] + for (int i = 0; i < (int)(num_views_pow2 / 2); i++) + for (int j = 0; j < num_tang_poss_pow2; j++) + if ((j + sino2D_rebinned.get_min_tangential_pos_num()) <= sino2D_rebinned.get_max_tangential_pos_num()) + sino2D_rebinned[plane][i][j + sino2D_rebinned.get_min_tangential_pos_num()] = rebinned_sinogram[j][i]; + } // CON end loop over planes -void -FourierRebinning:: -do_rebinning(Array<3,std::complex > &FT_rebinned_data, Array<3,float> &Weights_for_FT_rebinned_data, - PETCount_rebinned &count_rebinned, - const SegmentBySinogram &segment, const int num_tang_poss_pow2, - const int num_views_pow2, const int num_planes, const float average_ring_difference_in_segment, - const float half_distance_between_rings, const float sampling_distance_in_s, - const float radial_sampling_freq_w, const float R_field_of_view_mm, - const float ratio_ring_spacing_to_ring_radius) - - - { - const int local_rebinned = count_rebinned.total; - const int local_miss= count_rebinned.miss; - const int local_ssrb= count_rebinned.ssrb; - -//CON Loop over all slices, FFT the sinograms and call the actual rebinning kernel. - for (int axial_pos_num = segment.get_min_axial_pos_num(); axial_pos_num <= segment.get_max_axial_pos_num() ;axial_pos_num++) - { - - if(axial_pos_num%10 == 0) info(boost::format("FORE Rebinning z (slice) = %1%") % axial_pos_num); - Array<2,float> current_sinogram(IndexRange2D(0,num_tang_poss_pow2-1,0,num_views_pow2-1)); - - //CL Calculate the 2D FFT of P(w,k) of the merged segment - //CON copy the sinogram data of slice axial_pos_num from the segment array to slicedata - //CON the sinogram is flipped. This will taken account for in the rebinning, where the assignment of the FFT - //CON coefficients are assigned opposite. - for (int j = 0; j < segment.get_num_tangential_poss(); j++) - for (int i = 0; i < num_views_pow2; i++) - current_sinogram[j][i] = segment[axial_pos_num][i][j + segment.get_min_tangential_pos_num()]; - - //CON FFT slicedata - const Array<2,std::complex > FT_current_sinogram = fourier_for_real_data(current_sinogram); + info(boost::format("FORE Rebinning :: 2D Rebinned sinograms => Min = %1%, Max= %2%, Sum = %3%") % sino2D_rebinned.find_min() % + sino2D_rebinned.find_max() % sino2D_rebinned.sum()); - //CON determine the axial position of the middle of the LOR in mm relative to Bin(segment=0,view=0,axial_pos=0,tang_pos=0) - const ProjDataInfo& proj_data_info = *segment.get_proj_data_info_sptr(); - const float z_in_mm = proj_data_info.get_m(Bin(segment.get_segment_num(),0,axial_pos_num,0)) - proj_data_info.get_m(Bin(0,0,0,0)); - - //CON Call the rebinning kernel. - rebinning(FT_rebinned_data,Weights_for_FT_rebinned_data,count_rebinned,FT_current_sinogram, - z_in_mm, average_ring_difference_in_segment, num_views_pow2, - num_tang_poss_pow2,half_distance_between_rings,sampling_distance_in_s,radial_sampling_freq_w, - R_field_of_view_mm,ratio_ring_spacing_to_ring_radius); - - }//CL End of loop of axial_pos_num - - if(fore_debug_level > 0){ - info(boost::format("Total rebinned: %1%\n" - "Total missed: %2%\n" - "Total rebinned SSRB: %3%") - % (count_rebinned.total - local_rebinned) % (count_rebinned.miss - local_miss) % (count_rebinned.ssrb - local_ssrb) ); - } + // CON finally write the rebinned sinograms to file + const Succeeded success_this_sino = rebinned_proj_data_sptr->set_segment(sino2D_rebinned); + + if (success == Succeeded::yes && success_this_sino == Succeeded::no) + success = Succeeded::no; + stop_timers(); + // CON presently not very useful. Maybe one could define a vriable fore_debug_level and + // CON only write in case of debugging + if (fore_debug_level > 0) + do_log_file(); + return success; } +void +FourierRebinning::do_rebinning(Array<3, std::complex>& FT_rebinned_data, Array<3, float>& Weights_for_FT_rebinned_data, + PETCount_rebinned& count_rebinned, const SegmentBySinogram& segment, + const int num_tang_poss_pow2, const int num_views_pow2, const int num_planes, + const float average_ring_difference_in_segment, const float half_distance_between_rings, + const float sampling_distance_in_s, const float radial_sampling_freq_w, + const float R_field_of_view_mm, const float ratio_ring_spacing_to_ring_radius) +{ + const int local_rebinned = count_rebinned.total; + const int local_miss = count_rebinned.miss; + const int local_ssrb = count_rebinned.ssrb; + + // CON Loop over all slices, FFT the sinograms and call the actual rebinning kernel. + for (int axial_pos_num = segment.get_min_axial_pos_num(); axial_pos_num <= segment.get_max_axial_pos_num(); axial_pos_num++) { + + if (axial_pos_num % 10 == 0) + info(boost::format("FORE Rebinning z (slice) = %1%") % axial_pos_num); + Array<2, float> current_sinogram(IndexRange2D(0, num_tang_poss_pow2 - 1, 0, num_views_pow2 - 1)); + + // CL Calculate the 2D FFT of P(w,k) of the merged segment + // CON copy the sinogram data of slice axial_pos_num from the segment array to slicedata + // CON the sinogram is flipped. This will taken account for in the rebinning, where the assignment of the FFT + // CON coefficients are assigned opposite. + for (int j = 0; j < segment.get_num_tangential_poss(); j++) + for (int i = 0; i < num_views_pow2; i++) + current_sinogram[j][i] = segment[axial_pos_num][i][j + segment.get_min_tangential_pos_num()]; + // CON FFT slicedata + const Array<2, std::complex> FT_current_sinogram = fourier_for_real_data(current_sinogram); -void -FourierRebinning:: -rebinning(Array<3,std::complex > &FT_rebinned_data, Array<3,float> &Weights_for_FT_rebinned_data, - PETCount_rebinned &num_rebinned, const Array<2,std::complex > &FT_current_sinogram, - const float z_in_mm, const float delta, - const int num_views_pow2, const int num_tang_poss_pow2, const float half_distance_between_rings, - const float sampling_distance_in_s, const float radial_sampling_freq_w, const float R_field_of_view_mm, - const float ratio_ring_spacing_to_ring_radius) -{ + // CON determine the axial position of the middle of the LOR in mm relative to Bin(segment=0,view=0,axial_pos=0,tang_pos=0) + const ProjDataInfo& proj_data_info = *segment.get_proj_data_info_sptr(); + const float z_in_mm = + proj_data_info.get_m(Bin(segment.get_segment_num(), 0, axial_pos_num, 0)) - proj_data_info.get_m(Bin(0, 0, 0, 0)); + + // CON Call the rebinning kernel. + rebinning(FT_rebinned_data, Weights_for_FT_rebinned_data, count_rebinned, FT_current_sinogram, z_in_mm, + average_ring_difference_in_segment, num_views_pow2, num_tang_poss_pow2, half_distance_between_rings, + sampling_distance_in_s, radial_sampling_freq_w, R_field_of_view_mm, ratio_ring_spacing_to_ring_radius); + + } // CL End of loop of axial_pos_num + + if (fore_debug_level > 0) { + info(boost::format("Total rebinned: %1%\n" + "Total missed: %2%\n" + "Total rebinned SSRB: %3%") % + (count_rebinned.total - local_rebinned) % (count_rebinned.miss - local_miss) % (count_rebinned.ssrb - local_ssrb)); + } +} - - //CON prevent rebinning to non existing z-positions (sinograms) +void +FourierRebinning::rebinning(Array<3, std::complex>& FT_rebinned_data, Array<3, float>& Weights_for_FT_rebinned_data, + PETCount_rebinned& num_rebinned, const Array<2, std::complex>& FT_current_sinogram, + const float z_in_mm, const float delta, const int num_views_pow2, const int num_tang_poss_pow2, + const float half_distance_between_rings, const float sampling_distance_in_s, + const float radial_sampling_freq_w, const float R_field_of_view_mm, + const float ratio_ring_spacing_to_ring_radius) { + + // CON prevent rebinning to non existing z-positions (sinograms) const int maxplane = FT_rebinned_data.get_max_index(); - //CON determine z position (sino identifier) - const int z = round(z_in_mm/half_distance_between_rings); + // CON determine z position (sino identifier) + const int z = round(z_in_mm / half_distance_between_rings); // TODO replace call to error() by warning() and returning Succeeded::no - if(fabs(static_cast(z_in_mm/half_distance_between_rings - z)) > .0001) - error("FORE rebinning :: rebinning kernel expected integer z coordinate but found a non integer value %g\n", z_in_mm); - - //CL t is the tangent of the angle theta between the LOR and the transaxial plane - const float t = delta * ratio_ring_spacing_to_ring_radius / 2.F; - - - //CON The continuous frequency "w" corresponds the radial coordinate "s" - //CON The integer Fourier index "k" corresponds to the azimuthal angle "view" - - //CON FORE regime (rebinning) - //CON Iterate over all frequency tuples (w,k) starting from wmin,kmin up to num_tang_poss_pow2/2,num_views_pow2/2 - - for (int j = wmin; j <= num_tang_poss_pow2/2;j++) { - for (int i = kmin; i <= num_views_pow2/2; i++) { - - float w = static_cast(j) * radial_sampling_freq_w; - float k = static_cast(i); - - //CON FORE consistency criteria . FORE is a good approximation only if w and k are large - //CON (FORE is a high frequency approximation). - //CON For small w and k second order corrections become important. - //CON FORE is a good approximation if: abs(w/k) > Romega, k>klim || w>wlim. - //CON see DeFrise III.C, p150. - //CON i>kc is an additional consistency criteria. The components of a 2D FFT sinogram are - //CON negligible when |-k/w| is larger than the - //CON the radius of the scanners FOV.These contributions can be forced to zero therefore avoiding - //CON large z-shifts which results in z-positions outside the axial range of the scanner. - if ((k > w * R_field_of_view_mm) && (i > kc)){ - continue; - } - - //CON FORE establishes a relation of the 2D FT of an oblique sinogram to the 2D FT of a direct sinogram - //CON shifted in z by a frequency dependent value (zshift). - //CON see DeFrise, III.B Formula 28 - float zshift; - if(w==0) zshift = 0.; - else zshift = t * k / w; //CL in mm - - //CON first evaluate the positive frequency for a given bin (w,k). The FFT is stored in data such, that in the first - //CON two dimensions the positive frequencies are stored. Starting with the zero frequency, followed by the smallest - //CON positive frequency and so on. The smallest negative frequency is in the last index value. - //CON Due to the ordering of positive and negative frequencies and the fact that this is the FFT of real data - //CON the assignment of the positive and negative frequency - //CON contributions to the rebinned data matrix in F-space can be done here in one pass. - for(int shift_direction=POSITIVE_Z_SHIFT; shift_direction<=NEGATIVE_Z_SHIFT; shift_direction+=CHANGE_Z_SHIFT){ - - int jj = j; - - if(shift_direction==NEGATIVE_Z_SHIFT && j > 0) jj = num_tang_poss_pow2 - j; - - //CON new_z_sl is the z-coordinate of the shifted z-position this contribution is assigned to. - const float new_z_sl = static_cast(z) + shift_direction * zshift/half_distance_between_rings; - //CON find the nearest "real" direct sinogram located at z < new_z_sl - const int small_z = static_cast(floor(new_z_sl)); - //CON distance between the direct sinogram and the z-position this F-space contribution was assigned to. - const float m = new_z_sl - small_z; - //CON Assign the F-space contributions from the given oblique sinogram to the neighbouring direct sinogram. - //CON The contribution is weighted by the - //CON z-"distance" of the calculated shifted z-position to the neighbouring direct sinogram with z < zshift. - if (small_z >= 0 && small_z <= maxplane) { - const float OneMinusM = 1.F - m; - FT_rebinned_data[small_z][i][jj] += (FT_current_sinogram[jj][i] * OneMinusM); - Weights_for_FT_rebinned_data[small_z][i][jj] += OneMinusM; - num_rebinned.total += 1; - } else { - num_rebinned.miss += 1; - } - //CON same for z > zshift - if (small_z >= -1 && small_z < maxplane) { - FT_rebinned_data[small_z + 1][i][jj] += (FT_current_sinogram[jj][i] * m); - Weights_for_FT_rebinned_data[small_z + 1][i][jj] += m; - } - - }//CON shift_direction - }//CON end j - }//CON end i - -//CL Particular cases for small frequencies i.e Small w -//CON Due to this they will only contribute to one direct sinogram. -//CON This is SSRB where each oblique sinogramm is an estimate of the direct sinogram -//CON P(w,k,z,d) = P(w,k,z,0) -//CON Only sinograms with an obliqueness smaller than deltamin (set by parameter file) are accepted - const int small_z = z; + if (fabs(static_cast(z_in_mm / half_distance_between_rings - z)) > .0001) + error("FORE rebinning :: rebinning kernel expected integer z coordinate but found a non integer value %g\n", z_in_mm); + + // CL t is the tangent of the angle theta between the LOR and the transaxial plane + const float t = delta * ratio_ring_spacing_to_ring_radius / 2.F; + + // CON The continuous frequency "w" corresponds the radial coordinate "s" + // CON The integer Fourier index "k" corresponds to the azimuthal angle "view" + + // CON FORE regime (rebinning) + // CON Iterate over all frequency tuples (w,k) starting from wmin,kmin up to num_tang_poss_pow2/2,num_views_pow2/2 + + for (int j = wmin; j <= num_tang_poss_pow2 / 2; j++) { + for (int i = kmin; i <= num_views_pow2 / 2; i++) { + + float w = static_cast(j) * radial_sampling_freq_w; + float k = static_cast(i); + + // CON FORE consistency criteria . FORE is a good approximation only if w and k are large + // CON (FORE is a high frequency approximation). + // CON For small w and k second order corrections become important. + // CON FORE is a good approximation if: abs(w/k) > Romega, k>klim || w>wlim. + // CON see DeFrise III.C, p150. + // CON i>kc is an additional consistency criteria. The components of a 2D FFT sinogram are + // CON negligible when |-k/w| is larger than the + // CON the radius of the scanners FOV.These contributions can be forced to zero therefore avoiding + // CON large z-shifts which results in z-positions outside the axial range of the scanner. + if ((k > w * R_field_of_view_mm) && (i > kc)) { + continue; + } + + // CON FORE establishes a relation of the 2D FT of an oblique sinogram to the 2D FT of a direct sinogram + // CON shifted in z by a frequency dependent value (zshift). + // CON see DeFrise, III.B Formula 28 + float zshift; + if (w == 0) + zshift = 0.; + else + zshift = t * k / w; // CL in mm + + // CON first evaluate the positive frequency for a given bin (w,k). The FFT is stored in data such, that in the first + // CON two dimensions the positive frequencies are stored. Starting with the zero frequency, followed by the smallest + // CON positive frequency and so on. The smallest negative frequency is in the last index value. + // CON Due to the ordering of positive and negative frequencies and the fact that this is the FFT of real data + // CON the assignment of the positive and negative frequency + // CON contributions to the rebinned data matrix in F-space can be done here in one pass. + for (int shift_direction = POSITIVE_Z_SHIFT; shift_direction <= NEGATIVE_Z_SHIFT; shift_direction += CHANGE_Z_SHIFT) { + + int jj = j; + + if (shift_direction == NEGATIVE_Z_SHIFT && j > 0) + jj = num_tang_poss_pow2 - j; + + // CON new_z_sl is the z-coordinate of the shifted z-position this contribution is assigned to. + const float new_z_sl = static_cast(z) + shift_direction * zshift / half_distance_between_rings; + // CON find the nearest "real" direct sinogram located at z < new_z_sl + const int small_z = static_cast(floor(new_z_sl)); + // CON distance between the direct sinogram and the z-position this F-space contribution was assigned to. + const float m = new_z_sl - small_z; + // CON Assign the F-space contributions from the given oblique sinogram to the neighbouring direct sinogram. + // CON The contribution is weighted by the + // CON z-"distance" of the calculated shifted z-position to the neighbouring direct sinogram with z < zshift. + if (small_z >= 0 && small_z <= maxplane) { + const float OneMinusM = 1.F - m; + FT_rebinned_data[small_z][i][jj] += (FT_current_sinogram[jj][i] * OneMinusM); + Weights_for_FT_rebinned_data[small_z][i][jj] += OneMinusM; + num_rebinned.total += 1; + } else { + num_rebinned.miss += 1; + } + // CON same for z > zshift + if (small_z >= -1 && small_z < maxplane) { + FT_rebinned_data[small_z + 1][i][jj] += (FT_current_sinogram[jj][i] * m); + Weights_for_FT_rebinned_data[small_z + 1][i][jj] += m; + } + + } // CON shift_direction + } // CON end j + } // CON end i + + // CL Particular cases for small frequencies i.e Small w + // CON Due to this they will only contribute to one direct sinogram. + // CON This is SSRB where each oblique sinogramm is an estimate of the direct sinogram + // CON P(w,k,z,d) = P(w,k,z,0) + // CON Only sinograms with an obliqueness smaller than deltamin (set by parameter file) are accepted + const int small_z = z; if (delta <= deltamin) { - //CL First, treat small w's then k=1..kNyq-1 : - //CON Same logic as in FORE, except that no zshift position is calculated. It is assumed that the obliqueness is small - //CON and therefore there will be only contributions to one direct sinogram and the weights are therefore always 1. - - for (int j = 0; j < wmin; j++){ - for (int i = 0; i <= num_views_pow2/2; i++) { - - for(int shift_direction=POSITIVE_Z_SHIFT;shift_direction<=NEGATIVE_Z_SHIFT;shift_direction+=CHANGE_Z_SHIFT){ - - int jj=j; - - // Take reverse ordering of tangential position in the negative segment into account (?) - if(shift_direction==NEGATIVE_Z_SHIFT && j > 0) - jj=num_tang_poss_pow2 - j; - - - if (small_z >= 0 && small_z <= maxplane ) { - - FT_rebinned_data[small_z][i][jj] += FT_current_sinogram[jj][i]; - Weights_for_FT_rebinned_data[small_z][i][jj] += 1.; - if(j==1) num_rebinned.ssrb += 1; - } - - } // end shift_direction - } // end for j - } // end for i - - -//CL Small k : -//CL Next treat small k's and w=wNyq=(num_tang_poss_pow2 / 2)+1, k=1..klim : - for (int j = wmin; j <= num_tang_poss_pow2/2; j++) { - for (int i = 0; i <= kmin; i++) { - - for(int shift_direction=POSITIVE_Z_SHIFT;shift_direction<=NEGATIVE_Z_SHIFT;shift_direction+=CHANGE_Z_SHIFT){ - - int jj=j; - - // Take reverse ordering of tangential position in the negative segment into account (?) - if(shift_direction==NEGATIVE_Z_SHIFT && j > 0) - jj=num_tang_poss_pow2 - j; - - - if (small_z >= 0 && small_z <= maxplane ) { - FT_rebinned_data[small_z][i][jj] += FT_current_sinogram[jj][i]; - Weights_for_FT_rebinned_data[small_z][i][jj] += 1.; - num_rebinned.ssrb += 1; - } - - } // shift_direction - } // end for j - } // end for i - - } // end delta < deltamin - - } - - - -void -FourierRebinning:: -do_log_file() -{//CL Saving time details and write them to log file - char file[200]; - sprintf(file,"%s.log",output_filename_prefix.c_str()); - - std::ofstream logfile(file); - - if (logfile.fail() || logfile.bad()) { - warning("FORE Rebinning :: Error opening log file\n"); - } + // CL First, treat small w's then k=1..kNyq-1 : + // CON Same logic as in FORE, except that no zshift position is calculated. It is assumed that the obliqueness is small + // CON and therefore there will be only contributions to one direct sinogram and the weights are therefore always 1. - std::time_t now = std::time(NULL); - logfile << "FORE Rebinning :: Date of the FORE image reconstruction : " << std::asctime(std::localtime(&now)) - << parameter_info() - << "\n\n CPU Time :\n" << get_CPU_timer_value() << '\n'; -} + for (int j = 0; j < wmin; j++) { + for (int i = 0; i <= num_views_pow2 / 2; i++) { + for (int shift_direction = POSITIVE_Z_SHIFT; shift_direction <= NEGATIVE_Z_SHIFT; shift_direction += CHANGE_Z_SHIFT) { -void -FourierRebinning:: -do_display_count(PETCount_rebinned &count) -{// Display rebinning statistics - std::cout << "FORE Rebinning :: Total rebinned: " << count.total << '\n' - << " Total missed: " << count.miss << '\n'; - - if (count.miss != 0) - std::cout << " (" << 100. * count.miss / (count.miss + count.ssrb) - << " percent)\n"; - std::cout << "FORE Rebinning :: Total rebinned SSRB: " << count.ssrb << std::endl; -} + int jj = j; + // Take reverse ordering of tangential position in the negative segment into account (?) + if (shift_direction == NEGATIVE_Z_SHIFT && j > 0) + jj = num_tang_poss_pow2 - j; -void -FourierRebinning:: -do_adjust_nb_views_to_pow2(SegmentBySinogram &segment) -{ -// Adjustment of the number of views to a power of two -//CON Use the STIR overlap_interpolate method and remove the simlar private implementation (adjust_pow2) here. - const int num_views_pow2 = stir::round(pow(2.,((int)ceil(log ((float)segment.get_num_views())/log(2.))))); - const float offset_for_overlap_interpolate = 0.F; - - if (num_views_pow2 == segment.get_num_views()) - return; - - //CON Create the projection data info ptr for the resized segment - shared_ptr out_proj_data_info_sptr(segment.get_proj_data_info_sptr()->clone()); - out_proj_data_info_sptr->set_num_views(num_views_pow2); - //CON the re-dimensioned segment - SegmentBySinogram out_segment = - out_proj_data_info_sptr->get_empty_segment_by_sinogram(segment.get_segment_num()); - - for (int axial_pos_num = segment.get_min_axial_pos_num(); axial_pos_num <= segment.get_max_axial_pos_num(); - axial_pos_num++) - { - const Sinogram sino2D = segment.get_sinogram(axial_pos_num); - Sinogram out_sino2D = out_segment.get_sinogram(axial_pos_num); - - float extension_factor = static_cast(out_sino2D.get_num_views())/ static_cast(sino2D.get_num_views()); - overlap_interpolate(out_sino2D, sino2D,extension_factor,offset_for_overlap_interpolate,true); - - out_segment.set_sinogram(out_sino2D); - } - segment = out_segment; -} + if (small_z >= 0 && small_z <= maxplane) { -Succeeded FourierRebinning:: -fore_check_parameters(int num_tang_poss_pow2, int num_views_pow2, int max_segment_num_to_process){ + FT_rebinned_data[small_z][i][jj] += FT_current_sinogram[jj][i]; + Weights_for_FT_rebinned_data[small_z][i][jj] += 1.; + if (j == 1) + num_rebinned.ssrb += 1; + } -//CON Check if the parameters given make sense. + } // end shift_direction + } // end for j + } // end for i - if(deltamin<0) { - warning("FORE initialisation :: The deltamin parameter must be an integer value >= 0"); - return Succeeded::no; + // CL Small k : + // CL Next treat small k's and w=wNyq=(num_tang_poss_pow2 / 2)+1, k=1..klim : + for (int j = wmin; j <= num_tang_poss_pow2 / 2; j++) { + for (int i = 0; i <= kmin; i++) { - } + for (int shift_direction = POSITIVE_Z_SHIFT; shift_direction <= NEGATIVE_Z_SHIFT; shift_direction += CHANGE_Z_SHIFT) { + int jj = j; - if(wmin < 0 || kmin < 0){ - warning("FORE initialisation :: The parameters wmin and kmin must be >=0\n" - " Negative frequencies are not allowed. Due to symmetry reasons they are implicitly defined by the positive wmin/kmin cut off values"); - return Succeeded::no; - } + // Take reverse ordering of tangential position in the negative segment into account (?) + if (shift_direction == NEGATIVE_Z_SHIFT && j > 0) + jj = num_tang_poss_pow2 - j; + if (small_z >= 0 && small_z <= maxplane) { + FT_rebinned_data[small_z][i][jj] += FT_current_sinogram[jj][i]; + Weights_for_FT_rebinned_data[small_z][i][jj] += 1.; + num_rebinned.ssrb += 1; + } - if(wmin >= num_tang_poss_pow2/2 || kmin >= num_views_pow2/2) { - warning(boost::format("FORE initialisation :: The parameter wmin or kmin is larger than the highest frequency component computed by the FFT algorithm\n" - " Choose an value smaller than the largest frequency\n" - " kmin must be smaller than %1% and wmin must be smaller than %2%") - % (num_tang_poss_pow2/2) % (num_views_pow2/2)); - return Succeeded::no; - } + } // shift_direction + } // end for j + } // end for i + } // end delta < deltamin +} + +void +FourierRebinning::do_log_file() { // CL Saving time details and write them to log file + char file[200]; + sprintf(file, "%s.log", output_filename_prefix.c_str()); + + std::ofstream logfile(file); + + if (logfile.fail() || logfile.bad()) { + warning("FORE Rebinning :: Error opening log file\n"); + } + + std::time_t now = std::time(NULL); + logfile << "FORE Rebinning :: Date of the FORE image reconstruction : " << std::asctime(std::localtime(&now)) + << parameter_info() << "\n\n CPU Time :\n" + << get_CPU_timer_value() << '\n'; +} + +void +FourierRebinning::do_display_count(PETCount_rebinned& count) { // Display rebinning statistics + std::cout << "FORE Rebinning :: Total rebinned: " << count.total << '\n' + << " Total missed: " << count.miss << '\n'; + + if (count.miss != 0) + std::cout << " (" << 100. * count.miss / (count.miss + count.ssrb) << " percent)\n"; + std::cout << "FORE Rebinning :: Total rebinned SSRB: " << count.ssrb << std::endl; +} - if(kc >= num_views_pow2/2) { - warning(boost::format("FORE initialisation :: Your parameter kc is larger than the highest frequency component in w (FTT of radial coordinate s)\n" - " Choose an value smaller than the largest frequency\n" - " kc must be smaller than %1%") - % num_views_pow2); - return Succeeded::no; - } +void +FourierRebinning::do_adjust_nb_views_to_pow2(SegmentBySinogram& segment) { + // Adjustment of the number of views to a power of two + // CON Use the STIR overlap_interpolate method and remove the simlar private implementation (adjust_pow2) here. + const int num_views_pow2 = stir::round(pow(2., ((int)ceil(log((float)segment.get_num_views()) / log(2.))))); + const float offset_for_overlap_interpolate = 0.F; + if (num_views_pow2 == segment.get_num_views()) + return; - if(max_segment_num_to_process > proj_data_sptr->get_num_segments()){ - warning(boost::format("FORE initialisation :: Your data set stores %1% segments\n" - " The maximum number of segments to process variable is larger than that.") - % (proj_data_sptr->get_num_segments()/2+1)); - return Succeeded::no; - } + // CON Create the projection data info ptr for the resized segment + shared_ptr out_proj_data_info_sptr(segment.get_proj_data_info_sptr()->clone()); + out_proj_data_info_sptr->set_num_views(num_views_pow2); + // CON the re-dimensioned segment + SegmentBySinogram out_segment = out_proj_data_info_sptr->get_empty_segment_by_sinogram(segment.get_segment_num()); + for (int axial_pos_num = segment.get_min_axial_pos_num(); axial_pos_num <= segment.get_max_axial_pos_num(); axial_pos_num++) { + const Sinogram sino2D = segment.get_sinogram(axial_pos_num); + Sinogram out_sino2D = out_segment.get_sinogram(axial_pos_num); - if(max_segment_num_to_process < 0){ - warning("FORE initialisation :: The maximum segment number to process must be an integer value >=0"); - return Succeeded::no; - } + float extension_factor = static_cast(out_sino2D.get_num_views()) / static_cast(sino2D.get_num_views()); + overlap_interpolate(out_sino2D, sino2D, extension_factor, offset_for_overlap_interpolate, true); - return Succeeded::yes; + out_segment.set_sinogram(out_sino2D); + } + segment = out_segment; } +Succeeded +FourierRebinning::fore_check_parameters(int num_tang_poss_pow2, int num_views_pow2, int max_segment_num_to_process) { + + // CON Check if the parameters given make sense. + + if (deltamin < 0) { + warning("FORE initialisation :: The deltamin parameter must be an integer value >= 0"); + return Succeeded::no; + } + + if (wmin < 0 || kmin < 0) { + warning("FORE initialisation :: The parameters wmin and kmin must be >=0\n" + " Negative frequencies are not allowed. Due to symmetry reasons they are implicitly defined by " + "the positive wmin/kmin cut off values"); + return Succeeded::no; + } + + if (wmin >= num_tang_poss_pow2 / 2 || kmin >= num_views_pow2 / 2) { + warning(boost::format("FORE initialisation :: The parameter wmin or kmin is larger than the highest frequency component " + "computed by the FFT algorithm\n" + " Choose an value smaller than the largest frequency\n" + " kmin must be smaller than %1% and wmin must be smaller than %2%") % + (num_tang_poss_pow2 / 2) % (num_views_pow2 / 2)); + return Succeeded::no; + } + + if (kc >= num_views_pow2 / 2) { + warning(boost::format("FORE initialisation :: Your parameter kc is larger than the highest frequency component in w (FTT of " + "radial coordinate s)\n" + " Choose an value smaller than the largest frequency\n" + " kc must be smaller than %1%") % + num_views_pow2); + return Succeeded::no; + } + + if (max_segment_num_to_process > proj_data_sptr->get_num_segments()) { + warning(boost::format("FORE initialisation :: Your data set stores %1% segments\n" + " The maximum number of segments to process variable is larger than that.") % + (proj_data_sptr->get_num_segments() / 2 + 1)); + return Succeeded::no; + } + + if (max_segment_num_to_process < 0) { + warning("FORE initialisation :: The maximum segment number to process must be an integer value >=0"); + return Succeeded::no; + } + + return Succeeded::yes; +} END_NAMESPACE_STIR diff --git a/src/recon_buildblock/GeneralisedObjectiveFunction.cxx b/src/recon_buildblock/GeneralisedObjectiveFunction.cxx index 80e9c0fcb9..e278f30dbf 100644 --- a/src/recon_buildblock/GeneralisedObjectiveFunction.cxx +++ b/src/recon_buildblock/GeneralisedObjectiveFunction.cxx @@ -41,9 +41,7 @@ START_NAMESPACE_STIR template void -GeneralisedObjectiveFunction:: -set_defaults() -{ +GeneralisedObjectiveFunction::set_defaults() { this->prior_sptr.reset(); // note: cannot use set_num_subsets(1) here, as other parameters (such as projectors) are not set-up yet. this->num_subsets = 1; @@ -52,87 +50,66 @@ set_defaults() template void -GeneralisedObjectiveFunction:: -initialise_keymap() -{ +GeneralisedObjectiveFunction::initialise_keymap() { this->parser.add_parsing_key("prior type", &prior_sptr); this->parser.add_key("Use TOF information", &use_tof); } template -GeneralisedObjectiveFunction:: -~GeneralisedObjectiveFunction() -{} +GeneralisedObjectiveFunction::~GeneralisedObjectiveFunction() {} template -Succeeded -GeneralisedObjectiveFunction:: -set_up(shared_ptr const& target_data_ptr) -{ - if (target_data_ptr->get_exam_info().imaging_modality.is_unknown() - || this->get_input_data().get_exam_info().imaging_modality.is_unknown()) +Succeeded +GeneralisedObjectiveFunction::set_up(shared_ptr const& target_data_ptr) { + if (target_data_ptr->get_exam_info().imaging_modality.is_unknown() || + this->get_input_data().get_exam_info().imaging_modality.is_unknown()) warning("Imaging modality is unknown for either the target or the input data or both.\n" "Going ahead anyway."); - else if (target_data_ptr->get_exam_info().imaging_modality != - this->get_input_data().get_exam_info().imaging_modality) + else if (target_data_ptr->get_exam_info().imaging_modality != this->get_input_data().get_exam_info().imaging_modality) error("Imaging modality should be the same for the target and the input data"); - if (!is_null_ptr(this->prior_sptr) && - this->prior_sptr->set_up(target_data_ptr) == Succeeded::no) + if (!is_null_ptr(this->prior_sptr) && this->prior_sptr->set_up(target_data_ptr) == Succeeded::no) return Succeeded::no; - if (this->num_subsets <= 0) - { - warning("Number of subsets %d should be larger than 0.", - this->num_subsets); - return Succeeded::no; - } + if (this->num_subsets <= 0) { + warning("Number of subsets %d should be larger than 0.", this->num_subsets); + return Succeeded::no; + } - if (use_tof) - { - info("Time-Of-Flight reconstruction activated!"); + if (use_tof) { + info("Time-Of-Flight reconstruction activated!"); } - return Succeeded::yes; + return Succeeded::yes; } template -GeneralisedPrior * const -GeneralisedObjectiveFunction:: -get_prior_ptr() const -{ return this->prior_sptr.get(); } +GeneralisedPrior* const +GeneralisedObjectiveFunction::get_prior_ptr() const { + return this->prior_sptr.get(); +} template -shared_ptr > -GeneralisedObjectiveFunction:: -get_prior_sptr() -{ - return this->prior_sptr; +shared_ptr> +GeneralisedObjectiveFunction::get_prior_sptr() { + return this->prior_sptr; } template void -GeneralisedObjectiveFunction:: -set_prior_sptr(const shared_ptr >& arg) -{ +GeneralisedObjectiveFunction::set_prior_sptr(const shared_ptr>& arg) { this->prior_sptr = arg; } template bool -GeneralisedObjectiveFunction:: -prior_is_zero() const -{ - return - is_null_ptr(this->prior_sptr) || - this->prior_sptr->get_penalisation_factor() == 0; +GeneralisedObjectiveFunction::prior_is_zero() const { + return is_null_ptr(this->prior_sptr) || this->prior_sptr->get_penalisation_factor() == 0; } template double -GeneralisedObjectiveFunction:: -compute_penalty(const TargetT& current_estimate) -{ +GeneralisedObjectiveFunction::compute_penalty(const TargetT& current_estimate) { if (this->prior_is_zero()) return 0.; else @@ -141,220 +118,161 @@ compute_penalty(const TargetT& current_estimate) template double -GeneralisedObjectiveFunction:: -compute_penalty(const TargetT& current_estimate, - const int subset_num) -{ - return this->compute_penalty(current_estimate)/this->get_num_subsets(); +GeneralisedObjectiveFunction::compute_penalty(const TargetT& current_estimate, const int subset_num) { + return this->compute_penalty(current_estimate) / this->get_num_subsets(); } template -void -GeneralisedObjectiveFunction:: -compute_sub_gradient(TargetT& gradient, - const TargetT ¤t_estimate, - const int subset_num) -{ +void +GeneralisedObjectiveFunction::compute_sub_gradient(TargetT& gradient, const TargetT& current_estimate, + const int subset_num) { assert(gradient.get_index_range() == current_estimate.get_index_range()); - if (subset_num<0 || subset_num>=this->get_num_subsets()) + if (subset_num < 0 || subset_num >= this->get_num_subsets()) error("compute_sub_gradient subset_num out-of-range error"); - this->compute_sub_gradient_without_penalty(gradient, - current_estimate, - subset_num); - if (!this->prior_is_zero()) - { - shared_ptr prior_gradient_sptr(gradient.get_empty_copy()); - this->prior_sptr->compute_gradient(*prior_gradient_sptr, current_estimate); - - // (*prior_gradient_sptr)/= num_subsets; - // gradient -= *prior_gradient_sptr; - typename TargetT::const_full_iterator prior_gradient_iter = prior_gradient_sptr->begin_all_const(); - const typename TargetT::const_full_iterator end_prior_gradient_iter = prior_gradient_sptr->end_all_const(); - typename TargetT::full_iterator gradient_iter = gradient.begin_all(); - while (prior_gradient_iter!=end_prior_gradient_iter) - { - *gradient_iter -= (*prior_gradient_iter)/this->get_num_subsets(); - ++gradient_iter; ++prior_gradient_iter; - } - } + this->compute_sub_gradient_without_penalty(gradient, current_estimate, subset_num); + if (!this->prior_is_zero()) { + shared_ptr prior_gradient_sptr(gradient.get_empty_copy()); + this->prior_sptr->compute_gradient(*prior_gradient_sptr, current_estimate); + + // (*prior_gradient_sptr)/= num_subsets; + // gradient -= *prior_gradient_sptr; + typename TargetT::const_full_iterator prior_gradient_iter = prior_gradient_sptr->begin_all_const(); + const typename TargetT::const_full_iterator end_prior_gradient_iter = prior_gradient_sptr->end_all_const(); + typename TargetT::full_iterator gradient_iter = gradient.begin_all(); + while (prior_gradient_iter != end_prior_gradient_iter) { + *gradient_iter -= (*prior_gradient_iter) / this->get_num_subsets(); + ++gradient_iter; + ++prior_gradient_iter; + } + } } template -int -GeneralisedObjectiveFunction:: -get_num_subsets() const -{ +int +GeneralisedObjectiveFunction::get_num_subsets() const { return this->num_subsets; } template bool -GeneralisedObjectiveFunction:: -get_tof_status() const -{ +GeneralisedObjectiveFunction::get_tof_status() const { return this->use_tof; } template double -GeneralisedObjectiveFunction:: -compute_objective_function_without_penalty(const TargetT& current_estimate) -{ +GeneralisedObjectiveFunction::compute_objective_function_without_penalty(const TargetT& current_estimate) { double result = 0.; - for (int subset_num=0; subset_numget_num_subsets(); ++subset_num) - result += - this->compute_objective_function_without_penalty(current_estimate, subset_num); + for (int subset_num = 0; subset_num < this->get_num_subsets(); ++subset_num) + result += this->compute_objective_function_without_penalty(current_estimate, subset_num); return result; } template double -GeneralisedObjectiveFunction:: -compute_objective_function_without_penalty(const TargetT& current_estimate, - const int subset_num) -{ - if (subset_num<0 || subset_num>=this->get_num_subsets()) +GeneralisedObjectiveFunction::compute_objective_function_without_penalty(const TargetT& current_estimate, + const int subset_num) { + if (subset_num < 0 || subset_num >= this->get_num_subsets()) error("compute_objective_function_without_penalty subset_num out-of-range error"); - return - this->actual_compute_objective_function_without_penalty(current_estimate, subset_num); + return this->actual_compute_objective_function_without_penalty(current_estimate, subset_num); } template double -GeneralisedObjectiveFunction:: -compute_objective_function(const TargetT& current_estimate, - const int subset_num) -{ - return - this->compute_objective_function_without_penalty(current_estimate, subset_num) - - this->compute_penalty(current_estimate, subset_num); +GeneralisedObjectiveFunction::compute_objective_function(const TargetT& current_estimate, const int subset_num) { + return this->compute_objective_function_without_penalty(current_estimate, subset_num) - + this->compute_penalty(current_estimate, subset_num); } template double -GeneralisedObjectiveFunction:: -compute_objective_function(const TargetT& current_estimate) -{ - return - this->compute_objective_function_without_penalty(current_estimate) - - this->compute_penalty(current_estimate); +GeneralisedObjectiveFunction::compute_objective_function(const TargetT& current_estimate) { + return this->compute_objective_function_without_penalty(current_estimate) - this->compute_penalty(current_estimate); } /////////////////////// Approximate Hessian template -Succeeded -GeneralisedObjectiveFunction:: -add_multiplication_with_approximate_sub_Hessian_without_penalty(TargetT& output, - const TargetT& input, - const int subset_num) const -{ - if (subset_num<0 || subset_num>=this->get_num_subsets()) +Succeeded +GeneralisedObjectiveFunction::add_multiplication_with_approximate_sub_Hessian_without_penalty( + TargetT& output, const TargetT& input, const int subset_num) const { + if (subset_num < 0 || subset_num >= this->get_num_subsets()) error("add_multiplication_with_approximate_sub_Hessian_without_penalty subset_num out-of-range error"); { string explanation; - if (!output.has_same_characteristics(input, - explanation)) - { - warning("GeneralisedObjectiveFunction:\n" - "input and output for add_multiplication_with_approximate_sub_Hessian_without_penalty\n" - "should have the same characteristics.\n%s", - explanation.c_str()); - return Succeeded::no; - } + if (!output.has_same_characteristics(input, explanation)) { + warning("GeneralisedObjectiveFunction:\n" + "input and output for add_multiplication_with_approximate_sub_Hessian_without_penalty\n" + "should have the same characteristics.\n%s", + explanation.c_str()); + return Succeeded::no; + } } - return - this->actual_add_multiplication_with_approximate_sub_Hessian_without_penalty(output, - input, - subset_num); + return this->actual_add_multiplication_with_approximate_sub_Hessian_without_penalty(output, input, subset_num); } template -Succeeded -GeneralisedObjectiveFunction:: -add_multiplication_with_approximate_sub_Hessian(TargetT& output, - const TargetT& input, - const int subset_num) const -{ - if (this->add_multiplication_with_approximate_sub_Hessian_without_penalty(output, input, subset_num) == - Succeeded::no) +Succeeded +GeneralisedObjectiveFunction::add_multiplication_with_approximate_sub_Hessian(TargetT& output, const TargetT& input, + const int subset_num) const { + if (this->add_multiplication_with_approximate_sub_Hessian_without_penalty(output, input, subset_num) == Succeeded::no) return Succeeded::no; - if (!this->prior_is_zero()) - { - // TODO used boost:scoped_ptr - shared_ptr prior_output_sptr(output.get_empty_copy()); - if (this->prior_sptr->add_multiplication_with_approximate_Hessian(*prior_output_sptr, output) == - Succeeded::no) - return Succeeded::no; - - - // (*prior_output_sptr)/= num_subsets; - // output -= *prior_output_sptr; - typename TargetT::const_full_iterator prior_output_iter = prior_output_sptr->begin_all_const(); - const typename TargetT::const_full_iterator end_prior_output_iter = prior_output_sptr->end_all_const(); - typename TargetT::full_iterator output_iter = output.begin_all(); - while (prior_output_iter!=end_prior_output_iter) - { - *output_iter -= (*prior_output_iter)/this->get_num_subsets(); - ++output_iter; ++prior_output_iter; - } - } + if (!this->prior_is_zero()) { + // TODO used boost:scoped_ptr + shared_ptr prior_output_sptr(output.get_empty_copy()); + if (this->prior_sptr->add_multiplication_with_approximate_Hessian(*prior_output_sptr, output) == Succeeded::no) + return Succeeded::no; - return Succeeded::yes; + // (*prior_output_sptr)/= num_subsets; + // output -= *prior_output_sptr; + typename TargetT::const_full_iterator prior_output_iter = prior_output_sptr->begin_all_const(); + const typename TargetT::const_full_iterator end_prior_output_iter = prior_output_sptr->end_all_const(); + typename TargetT::full_iterator output_iter = output.begin_all(); + while (prior_output_iter != end_prior_output_iter) { + *output_iter -= (*prior_output_iter) / this->get_num_subsets(); + ++output_iter; + ++prior_output_iter; + } + } + return Succeeded::yes; } - template -Succeeded -GeneralisedObjectiveFunction:: -add_multiplication_with_approximate_Hessian_without_penalty(TargetT& output, - const TargetT& input) const -{ - for (int subset_num=0; subset_numget_num_subsets(); ++subset_num) - { - if (this->add_multiplication_with_approximate_sub_Hessian_without_penalty(output, - input, - subset_num) == - Succeeded::no) - return Succeeded::no; - } +Succeeded +GeneralisedObjectiveFunction::add_multiplication_with_approximate_Hessian_without_penalty(TargetT& output, + const TargetT& input) const { + for (int subset_num = 0; subset_num < this->get_num_subsets(); ++subset_num) { + if (this->add_multiplication_with_approximate_sub_Hessian_without_penalty(output, input, subset_num) == Succeeded::no) + return Succeeded::no; + } return Succeeded::yes; } template -Succeeded -GeneralisedObjectiveFunction:: -add_multiplication_with_approximate_Hessian(TargetT& output, - const TargetT& input) const -{ - for (int subset_num=0; subset_numget_num_subsets(); ++subset_num) - { - if (this->add_multiplication_with_approximate_sub_Hessian(output, - input, - subset_num) == - Succeeded::no) - return Succeeded::no; - } +Succeeded +GeneralisedObjectiveFunction::add_multiplication_with_approximate_Hessian(TargetT& output, const TargetT& input) const { + for (int subset_num = 0; subset_num < this->get_num_subsets(); ++subset_num) { + if (this->add_multiplication_with_approximate_sub_Hessian(output, input, subset_num) == Succeeded::no) + return Succeeded::no; + } return Succeeded::yes; } template -Succeeded -GeneralisedObjectiveFunction:: -actual_add_multiplication_with_approximate_sub_Hessian_without_penalty(TargetT& output, - const TargetT& input, - const int subset_num) const -{ +Succeeded +GeneralisedObjectiveFunction::actual_add_multiplication_with_approximate_sub_Hessian_without_penalty( + TargetT& output, const TargetT& input, const int subset_num) const { error("GeneralisedObjectiveFunction:\n" - "actual_add_multiplication_with_approximate_sub_Hessian_without_penalty implementation is not overloaded by your objective function."); + "actual_add_multiplication_with_approximate_sub_Hessian_without_penalty implementation is not overloaded by your " + "objective function."); return Succeeded::no; } @@ -362,15 +280,10 @@ actual_add_multiplication_with_approximate_sub_Hessian_without_penalty(TargetT& template Succeeded -GeneralisedObjectiveFunction:: -accumulate_Hessian_times_input(TargetT& output, - const TargetT& current_image_estimate, - const TargetT& input) const -{ - for (int subset_num=0; subset_numget_num_subsets(); ++subset_num) - { - if (this->accumulate_sub_Hessian_times_input(output, - current_image_estimate, input, subset_num) == Succeeded::no) +GeneralisedObjectiveFunction::accumulate_Hessian_times_input(TargetT& output, const TargetT& current_image_estimate, + const TargetT& input) const { + for (int subset_num = 0; subset_num < this->get_num_subsets(); ++subset_num) { + if (this->accumulate_sub_Hessian_times_input(output, current_image_estimate, input, subset_num) == Succeeded::no) return Succeeded::no; } return Succeeded::yes; @@ -378,15 +291,12 @@ accumulate_Hessian_times_input(TargetT& output, template Succeeded -GeneralisedObjectiveFunction:: -accumulate_Hessian_times_input_without_penalty(TargetT& output, - const TargetT& current_image_estimate, - const TargetT& input) const -{ - for (int subset_num=0; subset_numget_num_subsets(); ++subset_num) - { - if (this->accumulate_sub_Hessian_times_input_without_penalty(output, - current_image_estimate, input, subset_num) == Succeeded::no) +GeneralisedObjectiveFunction::accumulate_Hessian_times_input_without_penalty(TargetT& output, + const TargetT& current_image_estimate, + const TargetT& input) const { + for (int subset_num = 0; subset_num < this->get_num_subsets(); ++subset_num) { + if (this->accumulate_sub_Hessian_times_input_without_penalty(output, current_image_estimate, input, subset_num) == + Succeeded::no) return Succeeded::no; } return Succeeded::yes; @@ -394,31 +304,25 @@ accumulate_Hessian_times_input_without_penalty(TargetT& output, template Succeeded -GeneralisedObjectiveFunction:: -accumulate_sub_Hessian_times_input(TargetT& output, - const TargetT& current_image_estimate, - const TargetT& input, - const int subset_num) const -{ +GeneralisedObjectiveFunction::accumulate_sub_Hessian_times_input(TargetT& output, const TargetT& current_image_estimate, + const TargetT& input, const int subset_num) const { if (this->accumulate_sub_Hessian_times_input_without_penalty(output, current_image_estimate, input, subset_num) == Succeeded::no) return Succeeded::no; - if (!this->prior_is_zero()) - { + if (!this->prior_is_zero()) { // TODO used boost:scoped_ptr - shared_ptr prior_output_sptr(output.get_empty_copy()); - if (this->prior_sptr->accumulate_Hessian_times_input(*prior_output_sptr, current_image_estimate, output) == - Succeeded::no) + shared_ptr prior_output_sptr(output.get_empty_copy()); + if (this->prior_sptr->accumulate_Hessian_times_input(*prior_output_sptr, current_image_estimate, output) == Succeeded::no) return Succeeded::no; typename TargetT::const_full_iterator prior_output_iter = prior_output_sptr->begin_all_const(); const typename TargetT::const_full_iterator end_prior_output_iter = prior_output_sptr->end_all_const(); typename TargetT::full_iterator output_iter = output.begin_all(); - while (prior_output_iter!=end_prior_output_iter) - { - *output_iter -= (*prior_output_iter)/this->get_num_subsets(); - ++output_iter; ++prior_output_iter; + while (prior_output_iter != end_prior_output_iter) { + *output_iter -= (*prior_output_iter) / this->get_num_subsets(); + ++output_iter; + ++prior_output_iter; } } return Succeeded::yes; @@ -426,13 +330,11 @@ accumulate_sub_Hessian_times_input(TargetT& output, template Succeeded -GeneralisedObjectiveFunction:: -accumulate_sub_Hessian_times_input_without_penalty(TargetT& output, - const TargetT& current_image_estimate, - const TargetT& input, - const int subset_num) const -{ - if (subset_num<0 || subset_num>=this->get_num_subsets()) +GeneralisedObjectiveFunction::accumulate_sub_Hessian_times_input_without_penalty(TargetT& output, + const TargetT& current_image_estimate, + const TargetT& input, + const int subset_num) const { + if (subset_num < 0 || subset_num >= this->get_num_subsets()) error("accumulate_sub_Hessian_times_input_without_penalty subset_num out-of-range error"); { @@ -454,77 +356,55 @@ accumulate_sub_Hessian_times_input_without_penalty(TargetT& output, } } - - return this->actual_accumulate_sub_Hessian_times_input_without_penalty(output, - current_image_estimate, - input, - subset_num); + return this->actual_accumulate_sub_Hessian_times_input_without_penalty(output, current_image_estimate, input, subset_num); } template Succeeded -GeneralisedObjectiveFunction:: -actual_accumulate_sub_Hessian_times_input_without_penalty(TargetT& output, - const TargetT& input, - const TargetT& current_image_estimate, - const int subset_num) const -{ +GeneralisedObjectiveFunction::actual_accumulate_sub_Hessian_times_input_without_penalty( + TargetT& output, const TargetT& input, const TargetT& current_image_estimate, const int subset_num) const { error("GeneralisedObjectiveFunction:\n" "actual_accumulate_sub_Hessian_times_input_without_penalty implementation is not overloaded by your objective function."); return Succeeded::no; } - /////////////////////// other functions template std::string -GeneralisedObjectiveFunction:: -get_objective_function_values_report(const TargetT& current_estimate) -{ +GeneralisedObjectiveFunction::get_objective_function_values_report(const TargetT& current_estimate) { #ifdef BOOST_NO_STRINGSTREAM char str[10000]; ostrstream s(str, 10000); #else std::ostringstream s; #endif - const double no_penalty = - this->compute_objective_function_without_penalty(current_estimate); - const double penalty = - this->compute_penalty(current_estimate); - s << "Objective function without penalty " << no_penalty - << "\nPenalty " << penalty - << "\nDifference (i.e. total) " << no_penalty-penalty - << '\n'; + const double no_penalty = this->compute_objective_function_without_penalty(current_estimate); + const double penalty = this->compute_penalty(current_estimate); + s << "Objective function without penalty " << no_penalty << "\nPenalty " << penalty + << "\nDifference (i.e. total) " << no_penalty - penalty << '\n'; return s.str(); } -template +template bool -GeneralisedObjectiveFunction:: -subsets_are_approximately_balanced() const -{ +GeneralisedObjectiveFunction::subsets_are_approximately_balanced() const { std::string dummy; return this->actual_subsets_are_approximately_balanced(dummy); } -template +template bool -GeneralisedObjectiveFunction:: -subsets_are_approximately_balanced(std::string& warning_message) const -{ +GeneralisedObjectiveFunction::subsets_are_approximately_balanced(std::string& warning_message) const { return this->actual_subsets_are_approximately_balanced(warning_message); } +#ifdef _MSC_VER +// prevent warning message on instantiation of abstract class +# pragma warning(disable : 4661) +#endif -# ifdef _MSC_VER -// prevent warning message on instantiation of abstract class -# pragma warning(disable:4661) -# endif - -template class GeneralisedObjectiveFunction >; -template class GeneralisedObjectiveFunction; +template class GeneralisedObjectiveFunction>; +template class GeneralisedObjectiveFunction; END_NAMESPACE_STIR - - diff --git a/src/recon_buildblock/GeneralisedPrior.cxx b/src/recon_buildblock/GeneralisedPrior.cxx index 156af7d71c..8155b99faf 100644 --- a/src/recon_buildblock/GeneralisedPrior.cxx +++ b/src/recon_buildblock/GeneralisedPrior.cxx @@ -20,9 +20,9 @@ \file \ingroup priors \brief implementation of the stir::GeneralisedPrior - + \author Kris Thielemans - \author Sanida Mustafovic + \author Sanida Mustafovic */ #include "stir/recon_buildblock/GeneralisedPrior.h" @@ -33,68 +33,56 @@ START_NAMESPACE_STIR - template -void -GeneralisedPrior::initialise_keymap() -{ - this->parser.add_key("penalisation factor", &this->penalisation_factor); +void +GeneralisedPrior::initialise_keymap() { + this->parser.add_key("penalisation factor", &this->penalisation_factor); } - template void -GeneralisedPrior::set_defaults() -{ +GeneralisedPrior::set_defaults() { _already_set_up = false; - this->penalisation_factor = 0; + this->penalisation_factor = 0; } template -Succeeded -GeneralisedPrior:: -set_up(shared_ptr const&) -{ +Succeeded +GeneralisedPrior::set_up(shared_ptr const&) { _already_set_up = true; return Succeeded::yes; } template -Succeeded -GeneralisedPrior:: -add_multiplication_with_approximate_Hessian(TargetT& output, - const TargetT& input) const -{ +Succeeded +GeneralisedPrior::add_multiplication_with_approximate_Hessian(TargetT& output, const TargetT& input) const { error("GeneralisedPrior:\n" - "add_multiplication_with_approximate_Hessian implementation is not overloaded by your prior."); + "add_multiplication_with_approximate_Hessian implementation is not overloaded by your prior."); return Succeeded::no; } template Succeeded -GeneralisedPrior:: -accumulate_Hessian_times_input(TargetT& output, - const TargetT& current_estimate, - const TargetT& input) const -{ +GeneralisedPrior::accumulate_Hessian_times_input(TargetT& output, const TargetT& current_estimate, + const TargetT& input) const { error("GeneralisedPrior:\n" "accumulate_Hessian_times_input implementation is not overloaded by your prior."); return Succeeded::no; } -template -void GeneralisedPrior::check(TargetT const& current_estimate) const -{ - if (!_already_set_up) +template +void +GeneralisedPrior::check(TargetT const& current_estimate) const { + if (!_already_set_up) error("The prior should already be set-up, but it's not."); } -# ifdef _MSC_VER -// prevent warning message on instantiation of abstract class -# pragma warning(disable:4661) -# endif +#ifdef _MSC_VER +// prevent warning message on instantiation of abstract class +# pragma warning(disable : 4661) +#endif -template class GeneralisedPrior >; -template class GeneralisedPrior; +template class GeneralisedPrior>; +template class GeneralisedPrior; END_NAMESPACE_STIR diff --git a/src/recon_buildblock/IterativeReconstruction.cxx b/src/recon_buildblock/IterativeReconstruction.cxx index bc5ac12fb4..6cef2518b4 100644 --- a/src/recon_buildblock/IterativeReconstruction.cxx +++ b/src/recon_buildblock/IterativeReconstruction.cxx @@ -23,13 +23,13 @@ \file \ingroup recon_buildblock - \brief implementation of the stir::IterativeReconstruction class - + \brief implementation of the stir::IterativeReconstruction class + \author Matthew Jacobson \author Kris Thielemans \author Sanida Mustafovic \author PARAPET project - + */ #include // for time(), used as seed for random stuff @@ -55,118 +55,100 @@ using std::endl; using std::string; #endif - START_NAMESPACE_STIR //********* parameters **************** template -void -IterativeReconstruction::set_defaults() -{ +void +IterativeReconstruction::set_defaults() { base_type::set_defaults(); this->objective_function_sptr.reset(); this->num_subsets = 1; this->start_subset_num = 0; this->num_subiterations = 1; - this->start_subiteration_num =1; + this->start_subiteration_num = 1; // default to all 1's this->initial_data_filename = "1"; - this->max_num_full_iterations=NumericInfo().max_value(); + this->max_num_full_iterations = NumericInfo().max_value(); this->save_interval = 1; this->inter_iteration_filter_interval = 0; this->inter_iteration_filter_ptr.reset(); -//MJ 02/08/99 added subset randomization + // MJ 02/08/99 added subset randomization this->randomise_subset_order = false; this->report_objective_function_values_interval = 0; - } template -void -IterativeReconstruction::initialise_keymap() -{ +void +IterativeReconstruction::initialise_keymap() { base_type::initialise_keymap(); this->parser.add_parsing_key("objective function type", &objective_function_sptr); - this->parser.add_key("number of subiterations", &num_subiterations); - this->parser.add_key("start at subiteration number", &start_subiteration_num); - this->parser.add_key("save estimates at subiteration intervals", &save_interval); + this->parser.add_key("number of subiterations", &num_subiterations); + this->parser.add_key("start at subiteration number", &start_subiteration_num); + this->parser.add_key("save estimates at subiteration intervals", &save_interval); this->parser.add_key("initial estimate", &initial_data_filename); this->parser.add_key("number of subsets", &this->num_subsets); this->parser.add_key("start at subset", &start_subset_num); this->parser.add_key("uniformly randomise subset order", &randomise_subset_order); - this->parser.add_key("inter-iteration filter subiteration interval",&inter_iteration_filter_interval); + this->parser.add_key("inter-iteration filter subiteration interval", &inter_iteration_filter_interval); this->parser.add_parsing_key("inter-iteration filter type", &inter_iteration_filter_ptr); - this->parser.add_key("report objective function values interval", - &this->report_objective_function_values_interval); + this->parser.add_key("report objective function values interval", &this->report_objective_function_values_interval); } template -void IterativeReconstruction:: -ask_parameters() -{ +void +IterativeReconstruction::ask_parameters() { base_type::ask_parameters(); - char initial_data_filename_char[max_filename_length]; - // KT 21/10/98 use new order of arguments - ask_filename_with_extension(initial_data_filename_char, - "Get initial estimate from which file (1 = 1's): ", ""); - - this->initial_data_filename=initial_data_filename_char; - - this->num_subsets= ask_num("Number of ordered sets: ", 1,100000000,1); - this->num_subiterations=ask_num("Number of subiterations", - 1,NumericInfo().max_value(),this->num_subsets); - - this->start_subiteration_num=ask_num("Start at what subiteration number : ", 1,NumericInfo().max_value(),1); - - this->start_subset_num=ask_num("Start with which ordered set : ", - 0,this->num_subsets-1,0); - - this->save_interval=ask_num("Save estimates at sub-iteration intervals of: ", - 1,this->num_subiterations,this->num_subiterations); - - - this->inter_iteration_filter_interval= - ask_num("Do inter-iteration filtering at sub-iteration intervals of: ", - 0, this->num_subiterations, 0); - - if(this->inter_iteration_filter_interval>0 ) - { - cerr<initial_data_filename = initial_data_filename_char; + + this->num_subsets = ask_num("Number of ordered sets: ", 1, 100000000, 1); + this->num_subiterations = ask_num("Number of subiterations", 1, NumericInfo().max_value(), this->num_subsets); + + this->start_subiteration_num = ask_num("Start at what subiteration number : ", 1, NumericInfo().max_value(), 1); + + this->start_subset_num = ask_num("Start with which ordered set : ", 0, this->num_subsets - 1, 0); + + this->save_interval = + ask_num("Save estimates at sub-iteration intervals of: ", 1, this->num_subiterations, this->num_subiterations); + + this->inter_iteration_filter_interval = + ask_num("Do inter-iteration filtering at sub-iteration intervals of: ", 0, this->num_subiterations, 0); + + if (this->inter_iteration_filter_interval > 0) { + cerr << endl << "Supply inter-iteration filter type:\nPossible values:\n"; DataProcessor::list_registered_names(cerr); - + const string inter_iteration_filter_type = ask_string(""); - - this->inter_iteration_filter_ptr. - reset(DataProcessor::read_registered_object(0, inter_iteration_filter_type)); - } - - - - this->randomise_subset_order= - ask("Randomly generate subset order?", false); - - -} + this->inter_iteration_filter_ptr.reset(DataProcessor::read_registered_object(0, inter_iteration_filter_type)); + } + + this->randomise_subset_order = ask("Randomly generate subset order?", false); +} template -bool IterativeReconstruction:: -post_processing() -{ +bool +IterativeReconstruction::post_processing() { if (base_type::post_processing()) return true; - if (is_null_ptr(this->objective_function_sptr)) - { warning("You have to specify an objective function"); return true; } + if (is_null_ptr(this->objective_function_sptr)) { + warning("You have to specify an objective function"); + return true; + } - if (this->initial_data_filename.length() == 0) - { warning("You need to specify an initial estimate file"); return true; } + if (this->initial_data_filename.length() == 0) { + warning("You need to specify an initial estimate file"); + return true; + } return false; } @@ -174,188 +156,159 @@ post_processing() //************ get_ functions **************** template GeneralisedObjectiveFunction const& -IterativeReconstruction:: -get_objective_function() const -{ return *this->objective_function_sptr; } +IterativeReconstruction::get_objective_function() const { + return *this->objective_function_sptr; +} template -shared_ptr > -IterativeReconstruction:: -get_objective_function_sptr() const -{ return this->objective_function_sptr; } +shared_ptr> +IterativeReconstruction::get_objective_function_sptr() const { + return this->objective_function_sptr; +} template const int -IterativeReconstruction:: -get_max_num_full_iterations() const -{ return this->max_num_full_iterations; } +IterativeReconstruction::get_max_num_full_iterations() const { + return this->max_num_full_iterations; +} template -const int -IterativeReconstruction:: -get_num_subsets() const -{ return this->num_subsets; } +const int +IterativeReconstruction::get_num_subsets() const { + return this->num_subsets; +} template -const int -IterativeReconstruction:: -get_num_subiterations() const -{ return this->num_subiterations; } +const int +IterativeReconstruction::get_num_subiterations() const { + return this->num_subiterations; +} template const int -IterativeReconstruction:: -get_start_subiteration_num() const -{ return this->start_subiteration_num; } +IterativeReconstruction::get_start_subiteration_num() const { + return this->start_subiteration_num; +} template -const int -IterativeReconstruction:: -get_start_subset_num() const -{ return this->start_subset_num; } +const int +IterativeReconstruction::get_start_subset_num() const { + return this->start_subset_num; +} template const int -IterativeReconstruction:: -get_save_interval() const -{ return this->save_interval; } +IterativeReconstruction::get_save_interval() const { + return this->save_interval; +} template const bool -IterativeReconstruction:: -get_randomise_subset_order() const -{ return this->randomise_subset_order; } +IterativeReconstruction::get_randomise_subset_order() const { + return this->randomise_subset_order; +} template -const DataProcessor& -IterativeReconstruction:: -get_inter_iteration_filter() const -{ return *this->inter_iteration_filter_ptr; } +const DataProcessor& +IterativeReconstruction::get_inter_iteration_filter() const { + return *this->inter_iteration_filter_ptr; +} template -shared_ptr > -IterativeReconstruction:: -get_inter_iteration_filter_sptr() -{ - return this->inter_iteration_filter_ptr; +shared_ptr> +IterativeReconstruction::get_inter_iteration_filter_sptr() { + return this->inter_iteration_filter_ptr; } template -const int -IterativeReconstruction:: -get_inter_iteration_filter_interval() const -{ return this->inter_iteration_filter_interval; } +const int +IterativeReconstruction::get_inter_iteration_filter_interval() const { + return this->inter_iteration_filter_interval; +} template -const int -IterativeReconstruction:: -get_report_objective_function_values_interval() const -{ return this->report_objective_function_values_interval; } +const int +IterativeReconstruction::get_report_objective_function_values_interval() const { + return this->report_objective_function_values_interval; +} //************ set_ functions **************** template void -IterativeReconstruction:: -set_objective_function_sptr(const shared_ptr >& arg) -{ - this->objective_function_sptr = arg; +IterativeReconstruction::set_objective_function_sptr(const shared_ptr>& arg) { + this->objective_function_sptr = arg; } template void -IterativeReconstruction:: -set_max_num_full_iterations(const int arg) -{ - this->max_num_full_iterations = arg; +IterativeReconstruction::set_max_num_full_iterations(const int arg) { + this->max_num_full_iterations = arg; } template void -IterativeReconstruction:: -set_num_subsets(const int arg) -{ +IterativeReconstruction::set_num_subsets(const int arg) { this->_already_set_up = false; - this->num_subsets = arg; + this->num_subsets = arg; } template void -IterativeReconstruction:: -set_num_subiterations(const int arg) -{ - this->num_subiterations = arg; +IterativeReconstruction::set_num_subiterations(const int arg) { + this->num_subiterations = arg; } template void -IterativeReconstruction:: -set_start_subiteration_num(const int arg) -{ - this->start_subiteration_num = arg; +IterativeReconstruction::set_start_subiteration_num(const int arg) { + this->start_subiteration_num = arg; } template void -IterativeReconstruction:: -set_start_subset_num(const int arg) -{ - if (arg<0 || arg>= this->num_subsets) +IterativeReconstruction::set_start_subset_num(const int arg) { + if (arg < 0 || arg >= this->num_subsets) error("set_start_subset_num out-of-range error"); - this->start_subset_num = arg; + this->start_subset_num = arg; } template void -IterativeReconstruction:: -set_save_interval(const int arg) -{ - this->save_interval = arg; +IterativeReconstruction::set_save_interval(const int arg) { + this->save_interval = arg; } template void -IterativeReconstruction:: -set_randomise_subset_order(const bool arg) -{ - this->randomise_subset_order = arg; +IterativeReconstruction::set_randomise_subset_order(const bool arg) { + this->randomise_subset_order = arg; } template void -IterativeReconstruction:: -set_inter_iteration_filter_ptr(const shared_ptr >& arg) -{ - this->inter_iteration_filter_ptr = arg; +IterativeReconstruction::set_inter_iteration_filter_ptr(const shared_ptr>& arg) { + this->inter_iteration_filter_ptr = arg; } template void -IterativeReconstruction:: -set_inter_iteration_filter_interval(const int arg) -{ - this->inter_iteration_filter_interval = arg; +IterativeReconstruction::set_inter_iteration_filter_interval(const int arg) { + this->inter_iteration_filter_interval = arg; } template void -IterativeReconstruction:: -set_report_objective_function_values_interval(const int arg) -{ +IterativeReconstruction::set_report_objective_function_values_interval(const int arg) { this->report_objective_function_values_interval = arg; } //************ other functions **************** template -IterativeReconstruction:: -IterativeReconstruction() -{ -} +IterativeReconstruction::IterativeReconstruction() {} template std::string -IterativeReconstruction:: -make_filename_prefix_subiteration_num(const std::string& filename_prefix) const -{ +IterativeReconstruction::make_filename_prefix_subiteration_num(const std::string& filename_prefix) const { char num[50]; sprintf(num, "_%d", subiteration_num); return filename_prefix + num; @@ -363,39 +316,31 @@ make_filename_prefix_subiteration_num(const std::string& filename_prefix) const template std::string -IterativeReconstruction:: -make_filename_prefix_subiteration_num() const -{ - return - this->make_filename_prefix_subiteration_num(this->output_filename_prefix); +IterativeReconstruction::make_filename_prefix_subiteration_num() const { + return this->make_filename_prefix_subiteration_num(this->output_filename_prefix); } template -TargetT * -IterativeReconstruction::get_initial_data_ptr() const -{ +TargetT* +IterativeReconstruction::get_initial_data_ptr() const { if (is_null_ptr(this->objective_function_sptr)) error("objective function needs to be set before calling get_initial_data_ptr"); - TargetT * result; + TargetT* result; - if(this->initial_data_filename=="0") - { + if (this->initial_data_filename == "0") { result = this->objective_function_sptr->construct_target_ptr(); - } - else if(this->initial_data_filename=="1") - { + } else if (this->initial_data_filename == "1") { result = this->objective_function_sptr->construct_target_ptr(); std::fill(result->begin_all(), result->end_all(), 1.F); + } else { + result = TargetT::read_from_file(this->initial_data_filename); + result->set_exam_info(*this->get_input_data().get_exam_info_sptr()); } - else - { - result = TargetT::read_from_file(this->initial_data_filename); - result->set_exam_info(*this->get_input_data().get_exam_info_sptr()); - } if (this->_already_set_up) if (!this->target_data_sptr->has_same_characteristics(*result)) - error("IterativeReconstruction::get_initial_data_ptr() results in different characteristics than what was used for set_up()"); + error( + "IterativeReconstruction::get_initial_data_ptr() results in different characteristics than what was used for set_up()"); return result; } @@ -403,18 +348,15 @@ IterativeReconstruction::get_initial_data_ptr() const // KT 10122001 new // NE Updated to be able to define the dataset to reconstruct. template -Succeeded -IterativeReconstruction:: -reconstruct() -{ +Succeeded +IterativeReconstruction::reconstruct() { this->start_timers(); this->target_data_sptr.reset(this->get_initial_data_ptr()); - if (this->set_up(this->target_data_sptr) == Succeeded::no) - { - this->stop_timers(); - return Succeeded::no; - } + if (this->set_up(this->target_data_sptr) == Succeeded::no) { + this->stop_timers(); + return Succeeded::no; + } this->stop_timers(); @@ -422,10 +364,8 @@ reconstruct() } template -Succeeded -IterativeReconstruction:: -reconstruct(shared_ptr const& target_data_sptr) -{ +Succeeded +IterativeReconstruction::reconstruct(shared_ptr const& target_data_sptr) { this->start_timers(); this->check(*target_data_sptr); #if 0 @@ -436,76 +376,81 @@ reconstruct(shared_ptr const& target_data_sptr) } #endif - for(subiteration_num=start_subiteration_num;subiteration_num<=num_subiterations && this->terminate_iterations==false; subiteration_num++) - { + for (subiteration_num = start_subiteration_num; subiteration_num <= num_subiterations && this->terminate_iterations == false; + subiteration_num++) { this->update_estimate(*target_data_sptr); this->end_of_iteration_processing(*target_data_sptr); } this->stop_timers(); - cerr << "Total CPU Time " << this->get_CPU_timer_value() << "secs"<get_CPU_timer_value() << "secs" << endl; // currently, if there was something wrong, the programme is just aborted // so, if we get here, everything was fine return Succeeded::yes; - } - template Succeeded -IterativeReconstruction:: -set_up(shared_ptr const& target_data_sptr) -{ +IterativeReconstruction::set_up(shared_ptr const& target_data_sptr) { if (base_type::set_up(target_data_sptr) == Succeeded::no) return Succeeded::no; - //initialize iteration loop terminator - this->terminate_iterations=false; + // initialize iteration loop terminator + this->terminate_iterations = false; + if (this->num_subsets < 1) { + warning("number of subsets should be positive"); + return Succeeded::no; + } - if (this->num_subsets<1 ) - { warning("number of subsets should be positive"); return Succeeded::no; } - - if (is_null_ptr(this->objective_function_sptr)) - { warning("You have to specify an objective function"); return Succeeded::no; } + if (is_null_ptr(this->objective_function_sptr)) { + warning("You have to specify an objective function"); + return Succeeded::no; + } - const int new_num_subsets = - this->objective_function_sptr->set_num_subsets(this->num_subsets); - if (new_num_subsets!=this->num_subsets) - { - warning("Number of subsets requested : %d, but actual number used is %d\n", - this->num_subsets, new_num_subsets); - this->num_subsets = new_num_subsets; - } + const int new_num_subsets = this->objective_function_sptr->set_num_subsets(this->num_subsets); + if (new_num_subsets != this->num_subsets) { + warning("Number of subsets requested : %d, but actual number used is %d\n", this->num_subsets, new_num_subsets); + this->num_subsets = new_num_subsets; + } if (this->objective_function_sptr->set_up(target_data_sptr) == Succeeded::no) return Succeeded::no; - if (this->num_subiterations<1) - { warning("Range error in number of subiterations"); return Succeeded::no; } - - if(this->start_subset_num<0 || this->start_subset_num>=this->num_subsets) - { warning("Range error in starting subset"); return Succeeded::no; } + if (this->num_subiterations < 1) { + warning("Range error in number of subiterations"); + return Succeeded::no; + } - if(this->save_interval<1 || this->save_interval>this->num_subiterations) - { warning("Range error in iteration save interval"); return Succeeded::no;} - - if (this->inter_iteration_filter_interval<0) - { warning("Range error in inter-iteration filter interval "); return Succeeded::no; } + if (this->start_subset_num < 0 || this->start_subset_num >= this->num_subsets) { + warning("Range error in starting subset"); + return Succeeded::no; + } + + if (this->save_interval < 1 || this->save_interval > this->num_subiterations) { + warning("Range error in iteration save interval"); + return Succeeded::no; + } + + if (this->inter_iteration_filter_interval < 0) { + warning("Range error in inter-iteration filter interval "); + return Succeeded::no; + } + + if (this->start_subiteration_num < 1) { + warning("Range error in starting subiteration number"); + return Succeeded::no; + } - if (this->start_subiteration_num<1) - { warning("Range error in starting subiteration number"); return Succeeded::no; } - ////////////////// subset order // KT 05/07/2000 made randomise_subset_order int - if (this->randomise_subset_order!=0){ - srand((unsigned int) (time(NULL)) ); //seed the rand() function + if (this->randomise_subset_order != 0) { + srand((unsigned int)(time(NULL))); // seed the rand() function } - // Building filters // This is not really necessary, as apply would call this anyway. // However, we have it here such that any errors in building the filters would @@ -533,101 +478,80 @@ set_up(shared_ptr const& target_data_sptr) error("Error building inter iteration filter\n"); } #endif - + return Succeeded::yes; } template -void IterativeReconstruction:: -end_of_iteration_processing(TargetT ¤t_estimate) -{ - std::stringstream cerr; +void +IterativeReconstruction::end_of_iteration_processing(TargetT& current_estimate) { + std::stringstream cerr; + + if (this->report_objective_function_values_interval > 0 && + (this->subiteration_num % this->report_objective_function_values_interval == 0 || + this->subiteration_num == this->num_subiterations)) { + /*std::*/ cerr << "Objective function values (before any additional filtering):\n" + << this->objective_function_sptr->get_objective_function_values_report(current_estimate); + } - if (this->report_objective_function_values_interval>0 && - (this->subiteration_num%this->report_objective_function_values_interval == 0 - || this->subiteration_num==this->num_subiterations)) - { - /*std::*/cerr << "Objective function values (before any additional filtering):\n" - << this->objective_function_sptr-> - get_objective_function_values_report(current_estimate); - } - - if(this->inter_iteration_filter_interval>0 && - !is_null_ptr(this->inter_iteration_filter_ptr) && - this->subiteration_num%this->inter_iteration_filter_interval==0) - { - cerr<inter_iteration_filter_ptr->apply(current_estimate); - } - - - cerr<< this->method_info() - << " subiteration #"<subiteration_num==this->num_subiterations && - !is_null_ptr(this->post_filter_sptr) ) - { - cerr<inter_iteration_filter_interval > 0 && !is_null_ptr(this->inter_iteration_filter_ptr) && + this->subiteration_num % this->inter_iteration_filter_interval == 0) { + cerr << endl << "Applying inter-iteration filter" << endl; + this->inter_iteration_filter_ptr->apply(current_estimate); + } + + cerr << this->method_info() << " subiteration #" << subiteration_num << " completed" << endl; + cerr << " min and max in current estimate " << *std::min_element(current_estimate.begin_all(), current_estimate.end_all()) + << " " << *std::max_element(current_estimate.begin_all(), current_estimate.end_all()) << endl; + + if (this->subiteration_num == this->num_subiterations && !is_null_ptr(this->post_filter_sptr)) { + cerr << endl << "Applying post-filter" << endl; this->post_filter_sptr->apply(current_estimate); - - cerr << " min and max after post-filtering " - << *std::min_element(current_estimate.begin_all(), current_estimate.end_all()) - << " " - << *std::max_element(current_estimate.begin_all(), current_estimate.end_all()) << endl; + + cerr << " min and max after post-filtering " << *std::min_element(current_estimate.begin_all(), current_estimate.end_all()) + << " " << *std::max_element(current_estimate.begin_all(), current_estimate.end_all()) << endl; } - + writeText(cerr.str().c_str(), INFORMATION_CHANNEL); // Save intermediate (or last) iteration -- if the output is not disabled - if((!(this->subiteration_num%this->save_interval) || - this->subiteration_num==this->num_subiterations) && !this->_disable_output) - { - this->output_file_format_ptr-> - write_to_file(this->make_filename_prefix_subiteration_num(), - current_estimate); - } + if ((!(this->subiteration_num % this->save_interval) || this->subiteration_num == this->num_subiterations) && + !this->_disable_output) { + this->output_file_format_ptr->write_to_file(this->make_filename_prefix_subiteration_num(), current_estimate); + } } template -VectorWithOffset -IterativeReconstruction:: -randomly_permute_subset_order() const -{ +VectorWithOffset +IterativeReconstruction::randomly_permute_subset_order() const { - VectorWithOffset temp_array(this->num_subsets),final_array(this->num_subsets); + VectorWithOffset temp_array(this->num_subsets), final_array(this->num_subsets); int index; - for(int i=0;inum_subsets;i++) temp_array[i]=i; - - for (int i=0;inum_subsets;i++) - { + for (int i = 0; i < this->num_subsets; i++) + temp_array[i] = i; - index = (int) (((float)rand()/(float)RAND_MAX)*(this->num_subsets-i)); - if (index==this->num_subsets-i) index--; - final_array[i]=temp_array[index]; - + for (int i = 0; i < this->num_subsets; i++) { - for (int j=index;jnum_subsets-(i+1);j++) - temp_array[j]=temp_array[j+1]; + index = (int)(((float)rand() / (float)RAND_MAX) * (this->num_subsets - i)); + if (index == this->num_subsets - i) + index--; + final_array[i] = temp_array[index]; - } - - cerr<num_subsets;i++) cerr<num_subsets - (i + 1); j++) + temp_array[j] = temp_array[j + 1]; + } - return final_array; + cerr << endl << "Generating new subset sequence: "; + for (int i = 0; i < this->num_subsets; i++) + cerr << final_array[i] << " "; + return final_array; } template void -IterativeReconstruction:: -set_input_data(const shared_ptr &arg) -{ +IterativeReconstruction::set_input_data(const shared_ptr& arg) { this->_already_set_up = false; if (is_null_ptr(this->objective_function_sptr)) error("objective function needs to be set before calling set_input_data"); @@ -636,41 +560,30 @@ set_input_data(const shared_ptr &arg) template const ExamData& -IterativeReconstruction:: -get_input_data() const -{ +IterativeReconstruction::get_input_data() const { if (is_null_ptr(this->objective_function_sptr)) error("objective function needs to be set before calling get_input_data"); return this->objective_function_sptr->get_input_data(); } - template int -IterativeReconstruction:: -get_subset_num() -{ - if(this->randomise_subset_order && (this->subiteration_num-1)%this->num_subsets==0) - { - this->_current_subset_array = this->randomly_permute_subset_order(); +IterativeReconstruction::get_subset_num() { + if (this->randomise_subset_order && (this->subiteration_num - 1) % this->num_subsets == 0) { + this->_current_subset_array = this->randomly_permute_subset_order(); }; - - return - this->randomise_subset_order - ? this->_current_subset_array[(this->subiteration_num-1)%this->num_subsets] - : (this->subiteration_num+this->start_subset_num-1)%this->num_subsets; + + return this->randomise_subset_order ? this->_current_subset_array[(this->subiteration_num - 1) % this->num_subsets] + : (this->subiteration_num + this->start_subset_num - 1) % this->num_subsets; } -# ifdef _MSC_VER -// prevent warning message on reinstantiation, +#ifdef _MSC_VER +// prevent warning message on reinstantiation, // note that we get a linking error if we don't have the explicit instantiation below -# pragma warning(disable:4660) -# endif +# pragma warning(disable : 4660) +#endif -template class IterativeReconstruction >; -template class IterativeReconstruction; +template class IterativeReconstruction>; +template class IterativeReconstruction; END_NAMESPACE_STIR - - - diff --git a/src/recon_buildblock/LogcoshPrior.cxx b/src/recon_buildblock/LogcoshPrior.cxx index c05f4dd3ff..9e745def38 100644 --- a/src/recon_buildblock/LogcoshPrior.cxx +++ b/src/recon_buildblock/LogcoshPrior.cxx @@ -45,8 +45,7 @@ START_NAMESPACE_STIR template void -LogcoshPrior::initialise_keymap() -{ +LogcoshPrior::initialise_keymap() { base_type::initialise_keymap(); this->parser.add_start_key("Logcosh Prior Parameters"); this->parser.add_key("only 2D", &only_2D); @@ -59,46 +58,39 @@ LogcoshPrior::initialise_keymap() template bool -LogcoshPrior::post_processing() -{ - if (base_type::post_processing()==true) +LogcoshPrior::post_processing() { + if (base_type::post_processing() == true) return true; if (kappa_filename.size() != 0) - this->kappa_ptr = read_from_file >(kappa_filename); + this->kappa_ptr = read_from_file>(kappa_filename); bool warn_about_even_size = false; - if (this->weights.size() ==0) - { + if (this->weights.size() == 0) { // will call compute_weights() to fill it in - } - else - { - if (!this->weights.is_regular()) - { + } else { + if (!this->weights.is_regular()) { warning("Sorry. LogcoshPrior currently only supports regular arrays for the weights"); return true; } const unsigned int size_z = this->weights.size(); - if (size_z%2==0) + if (size_z % 2 == 0) warn_about_even_size = true; - const int min_index_z = -static_cast(size_z/2); + const int min_index_z = -static_cast(size_z / 2); this->weights.set_min_index(min_index_z); - for (int z = min_index_z; z<= this->weights.get_max_index(); ++z) - { + for (int z = min_index_z; z <= this->weights.get_max_index(); ++z) { const unsigned int size_y = this->weights[z].size(); - if (size_y%2==0) + if (size_y % 2 == 0) warn_about_even_size = true; - const int min_index_y = -static_cast(size_y/2); + const int min_index_y = -static_cast(size_y / 2); this->weights[z].set_min_index(min_index_y); - for (int y = min_index_y; y<= this->weights[z].get_max_index(); ++y) - { + for (int y = min_index_y; y <= this->weights[z].get_max_index(); ++y) { const unsigned int size_x = this->weights[z][y].size(); - if (size_x%2==0) + if (size_x % 2 == 0) warn_about_even_size = true; - const int min_index_x = -static_cast(size_x/2); + const int min_index_x = -static_cast(size_x / 2); this->weights[z][y].set_min_index(min_index_x); } } @@ -108,13 +100,11 @@ LogcoshPrior::post_processing() warning("Parsing LogcoshPrior: even number of weights occurred in either x,y or z dimension.\n" "I'll (effectively) make this odd by appending a 0 at the end."); return false; - } template void -LogcoshPrior::set_defaults() -{ +LogcoshPrior::set_defaults() { base_type::set_defaults(); this->only_2D = false; this->scalar = 1.0; @@ -123,30 +113,22 @@ LogcoshPrior::set_defaults() } template <> -const char * const - LogcoshPrior::registered_name = - "Logcosh"; +const char* const LogcoshPrior::registered_name = "Logcosh"; template -LogcoshPrior::LogcoshPrior() -{ +LogcoshPrior::LogcoshPrior() { set_defaults(); } - template -LogcoshPrior::LogcoshPrior(const bool only_2D_v, float penalisation_factor_v) - : only_2D(only_2D_v) -{ +LogcoshPrior::LogcoshPrior(const bool only_2D_v, float penalisation_factor_v) : only_2D(only_2D_v) { set_defaults(); this->penalisation_factor = penalisation_factor_v; } - template LogcoshPrior::LogcoshPrior(const bool only_2D_v, float penalisation_factor_v, const float scalar_v) - : only_2D(only_2D_v), scalar(scalar_v) -{ + : only_2D(only_2D_v), scalar(scalar_v) { set_defaults(); this->penalisation_factor = penalisation_factor_v; this->scalar = scalar_v; @@ -154,17 +136,17 @@ LogcoshPrior::LogcoshPrior(const bool only_2D_v, float penalisation_facto //! get penalty weights for the neighbourhood template -Array<3,float> -LogcoshPrior:: -get_weights() const -{ return this->weights; } +Array<3, float> +LogcoshPrior::get_weights() const { + return this->weights; +} //! set penalty weights for the neighbourhood template void -LogcoshPrior:: -set_weights(const Array<3,float>& w) -{ this->weights = w; } +LogcoshPrior::set_weights(const Array<3, float>& w) { + this->weights = w; +} //! get current kappa image /*! \warning As this function returns a shared_ptr, this is dangerous. You should not @@ -172,81 +154,67 @@ set_weights(const Array<3,float>& w) Unpredictable results will occur. */ template -shared_ptr > -LogcoshPrior:: -get_kappa_sptr() const -{ return this->kappa_ptr; } +shared_ptr> +LogcoshPrior::get_kappa_sptr() const { + return this->kappa_ptr; +} //! set kappa image template void -LogcoshPrior:: -set_kappa_sptr(const shared_ptr >& k) -{ this->kappa_ptr = k; } +LogcoshPrior::set_kappa_sptr(const shared_ptr>& k) { + this->kappa_ptr = k; +} //! Get the scalar value template float -LogcoshPrior:: -get_scalar() const -{ return this->scalar; } +LogcoshPrior::get_scalar() const { + return this->scalar; +} //! Set the scalar value template void -LogcoshPrior:: -set_scalar(const float scalar_v) -{ scalar = scalar_v; } - +LogcoshPrior::set_scalar(const float scalar_v) { + scalar = scalar_v; +} // TODO move to set_up // initialise to 1/Euclidean distance static void -compute_weights(Array<3,float>& weights, const CartesianCoordinate3D& grid_spacing, const bool only_2D) -{ +compute_weights(Array<3, float>& weights, const CartesianCoordinate3D& grid_spacing, const bool only_2D) { int min_dz, max_dz; - if (only_2D) - { + if (only_2D) { min_dz = max_dz = 0; - } - else - { + } else { min_dz = -1; max_dz = 1; } - weights = Array<3,float>(IndexRange3D(min_dz,max_dz,-1,1,-1,1)); - for (int z=min_dz;z<=max_dz;++z) - for (int y=-1;y<=1;++y) - for (int x=-1;x<=1;++x) - { - if (z==0 && y==0 && x==0) + weights = Array<3, float>(IndexRange3D(min_dz, max_dz, -1, 1, -1, 1)); + for (int z = min_dz; z <= max_dz; ++z) + for (int y = -1; y <= 1; ++y) + for (int x = -1; x <= 1; ++x) { + if (z == 0 && y == 0 && x == 0) weights[0][0][0] = 0; - else - { + else { weights[z][y][x] = - grid_spacing.x()/ - sqrt(square(x*grid_spacing.x())+ - square(y*grid_spacing.y())+ - square(z*grid_spacing.z())); + grid_spacing.x() / sqrt(square(x * grid_spacing.x()) + square(y * grid_spacing.y()) + square(z * grid_spacing.z())); } } } template double -LogcoshPrior:: -compute_value(const DiscretisedDensity<3,elemT> ¤t_image_estimate) -{ - if (this->penalisation_factor==0) - { +LogcoshPrior::compute_value(const DiscretisedDensity<3, elemT>& current_image_estimate) { + if (this->penalisation_factor == 0) { return 0.; } - const DiscretisedDensityOnCartesianGrid<3,elemT>& current_image_cast = - dynamic_cast< const DiscretisedDensityOnCartesianGrid<3,elemT> &>(current_image_estimate); + const DiscretisedDensityOnCartesianGrid<3, elemT>& current_image_cast = + dynamic_cast&>(current_image_estimate); - if (this->weights.get_length() ==0) - { + if (this->weights.get_length() == 0) { compute_weights(this->weights, current_image_cast.get_grid_spacing(), this->only_2D); } @@ -258,26 +226,23 @@ compute_value(const DiscretisedDensity<3,elemT> ¤t_image_estimate) double result = 0.; const int min_z = current_image_estimate.get_min_index(); const int max_z = current_image_estimate.get_max_index(); - for (int z=min_z; z<=max_z; z++) - { - const int min_dz = max(weights.get_min_index(), min_z-z); - const int max_dz = min(weights.get_max_index(), max_z-z); + for (int z = min_z; z <= max_z; z++) { + const int min_dz = max(weights.get_min_index(), min_z - z); + const int max_dz = min(weights.get_max_index(), max_z - z); const int min_y = current_image_estimate[z].get_min_index(); const int max_y = current_image_estimate[z].get_max_index(); - for (int y=min_y;y<= max_y;y++) - { - const int min_dy = max(weights[0].get_min_index(), min_y-y); - const int max_dy = min(weights[0].get_max_index(), max_y-y); + for (int y = min_y; y <= max_y; y++) { + const int min_dy = max(weights[0].get_min_index(), min_y - y); + const int max_dy = min(weights[0].get_max_index(), max_y - y); const int min_x = current_image_estimate[z][y].get_min_index(); const int max_x = current_image_estimate[z][y].get_max_index(); - for (int x=min_x;x<= max_x;x++) - { - const int min_dx = max(weights[0][0].get_min_index(), min_x-x); - const int max_dx = min(weights[0][0].get_max_index(), max_x-x); + for (int x = min_x; x <= max_x; x++) { + const int min_dx = max(weights[0][0].get_min_index(), min_x - x); + const int max_dx = min(weights[0][0].get_max_index(), max_x - x); /* formula: sum_dx,dy,dz @@ -285,41 +250,36 @@ compute_value(const DiscretisedDensity<3,elemT> ¤t_image_estimate) log(cosh(current_image_estimate[z][y][x] - current_image_estimate[z+dz][y+dy][x+dx])) * (*kappa_ptr)[z][y][x] * (*kappa_ptr)[z+dz][y+dy][x+dx]; */ - for (int dz=min_dz;dz<=max_dz;++dz) - for (int dy=min_dy;dy<=max_dy;++dy) - for (int dx=min_dx;dx<=max_dx;++dx) - { + for (int dz = min_dz; dz <= max_dz; ++dz) + for (int dy = min_dy; dy <= max_dy; ++dy) + for (int dx = min_dx; dx <= max_dx; ++dx) { // 1/scalar^2 * log(cosh(x * scalar)) - elemT voxel_diff= current_image_estimate[z][y][x] - current_image_estimate[z+dz][y+dy][x+dx]; - elemT current = weights[dz][dy][dx] * 1/(this->scalar*this->scalar) * logcosh(this->scalar*voxel_diff); + elemT voxel_diff = current_image_estimate[z][y][x] - current_image_estimate[z + dz][y + dy][x + dx]; + elemT current = weights[dz][dy][dx] * 1 / (this->scalar * this->scalar) * logcosh(this->scalar * voxel_diff); if (do_kappa) - current *= (*kappa_ptr)[z][y][x] * (*kappa_ptr)[z+dz][y+dy][x+dx]; + current *= (*kappa_ptr)[z][y][x] * (*kappa_ptr)[z + dz][y + dy][x + dx]; result += static_cast(current); } } } } - return result * this->penalisation_factor/2.0; + return result * this->penalisation_factor / 2.0; } template void -LogcoshPrior:: -compute_gradient(DiscretisedDensity<3,elemT>& prior_gradient, - const DiscretisedDensity<3,elemT> ¤t_image_estimate) -{ +LogcoshPrior::compute_gradient(DiscretisedDensity<3, elemT>& prior_gradient, + const DiscretisedDensity<3, elemT>& current_image_estimate) { assert(prior_gradient.has_same_characteristics(current_image_estimate)); - if (this->penalisation_factor==0) - { + if (this->penalisation_factor == 0) { prior_gradient.fill(0); return; } - const DiscretisedDensityOnCartesianGrid<3,elemT>& current_image_cast = - dynamic_cast< const DiscretisedDensityOnCartesianGrid<3,elemT> &>(current_image_estimate); + const DiscretisedDensityOnCartesianGrid<3, elemT>& current_image_cast = + dynamic_cast&>(current_image_estimate); - if (this->weights.get_length() ==0) - { + if (this->weights.get_length() == 0) { compute_weights(this->weights, current_image_cast.get_grid_spacing(), this->only_2D); } @@ -329,42 +289,38 @@ compute_gradient(DiscretisedDensity<3,elemT>& prior_gradient, const int min_z = current_image_estimate.get_min_index(); const int max_z = current_image_estimate.get_max_index(); - for (int z=min_z; z<=max_z; z++) - { - const int min_dz = max(weights.get_min_index(), min_z-z); - const int max_dz = min(weights.get_max_index(), max_z-z); + for (int z = min_z; z <= max_z; z++) { + const int min_dz = max(weights.get_min_index(), min_z - z); + const int max_dz = min(weights.get_max_index(), max_z - z); const int min_y = current_image_estimate[z].get_min_index(); const int max_y = current_image_estimate[z].get_max_index(); - for (int y=min_y;y<= max_y;y++) - { - const int min_dy = max(weights[0].get_min_index(), min_y-y); - const int max_dy = min(weights[0].get_max_index(), max_y-y); + for (int y = min_y; y <= max_y; y++) { + const int min_dy = max(weights[0].get_min_index(), min_y - y); + const int max_dy = min(weights[0].get_max_index(), max_y - y); const int min_x = current_image_estimate[z][y].get_min_index(); const int max_x = current_image_estimate[z][y].get_max_index(); - for (int x=min_x;x<= max_x;x++) - { - const int min_dx = max(weights[0][0].get_min_index(), min_x-x); - const int max_dx = min(weights[0][0].get_max_index(), max_x-x); + for (int x = min_x; x <= max_x; x++) { + const int min_dx = max(weights[0][0].get_min_index(), min_x - x); + const int max_dx = min(weights[0][0].get_max_index(), max_x - x); elemT gradient = 0; - for (int dz=min_dz;dz<=max_dz;++dz) - for (int dy=min_dy;dy<=max_dy;++dy) - for (int dx=min_dx;dx<=max_dx;++dx) - { + for (int dz = min_dz; dz <= max_dz; ++dz) + for (int dy = min_dy; dy <= max_dy; ++dy) + for (int dx = min_dx; dx <= max_dx; ++dx) { // 1/scalar * tanh(x * scalar) - elemT voxel_diff= current_image_estimate[z][y][x] - current_image_estimate[z+dz][y+dy][x+dx]; - elemT current = weights[dz][dy][dx] * (1/this->scalar) * tanh(this->scalar*voxel_diff); + elemT voxel_diff = current_image_estimate[z][y][x] - current_image_estimate[z + dz][y + dy][x + dx]; + elemT current = weights[dz][dy][dx] * (1 / this->scalar) * tanh(this->scalar * voxel_diff); if (do_kappa) - current *= (*kappa_ptr)[z][y][x] * (*kappa_ptr)[z+dz][y+dy][x+dx]; + current *= (*kappa_ptr)[z][y][x] * (*kappa_ptr)[z + dz][y + dy][x + dx]; gradient += current; } - prior_gradient[z][y][x]= gradient * this->penalisation_factor; + prior_gradient[z][y][x] = gradient * this->penalisation_factor; } } } @@ -373,37 +329,31 @@ compute_gradient(DiscretisedDensity<3,elemT>& prior_gradient, static int count = 0; ++count; - if (gradient_filename_prefix.size()>0) - { - char *filename = new char[gradient_filename_prefix.size()+100]; + if (gradient_filename_prefix.size() > 0) { + char* filename = new char[gradient_filename_prefix.size() + 100]; sprintf(filename, "%s%d.v", gradient_filename_prefix.c_str(), count); - OutputFileFormat >::default_sptr()-> - write_to_file(filename, prior_gradient); + OutputFileFormat>::default_sptr()->write_to_file(filename, prior_gradient); delete[] filename; } } template void -LogcoshPrior:: -compute_Hessian(DiscretisedDensity<3,elemT>& prior_Hessian_for_single_densel, - const BasicCoordinate<3,int>& coords, - const DiscretisedDensity<3,elemT> ¤t_image_estimate) -{ - assert( prior_Hessian_for_single_densel.has_same_characteristics(current_image_estimate)); +LogcoshPrior::compute_Hessian(DiscretisedDensity<3, elemT>& prior_Hessian_for_single_densel, + const BasicCoordinate<3, int>& coords, + const DiscretisedDensity<3, elemT>& current_image_estimate) { + assert(prior_Hessian_for_single_densel.has_same_characteristics(current_image_estimate)); prior_Hessian_for_single_densel.fill(0); - if (this->penalisation_factor==0) - { + if (this->penalisation_factor == 0) { return; } - const DiscretisedDensityOnCartesianGrid<3,elemT>& current_image_cast = - dynamic_cast< const DiscretisedDensityOnCartesianGrid<3,elemT> &>(current_image_estimate); + const DiscretisedDensityOnCartesianGrid<3, elemT>& current_image_cast = + dynamic_cast&>(current_image_estimate); - DiscretisedDensityOnCartesianGrid<3,elemT>& prior_Hessian_for_single_densel_cast = - dynamic_cast &>(prior_Hessian_for_single_densel); + DiscretisedDensityOnCartesianGrid<3, elemT>& prior_Hessian_for_single_densel_cast = + dynamic_cast&>(prior_Hessian_for_single_densel); - if (weights.get_length() ==0) - { + if (weights.get_length() == 0) { compute_weights(weights, current_image_cast.get_grid_spacing(), this->only_2D); } @@ -415,52 +365,48 @@ compute_Hessian(DiscretisedDensity<3,elemT>& prior_Hessian_for_single_densel, const int z = coords[1]; const int y = coords[2]; const int x = coords[3]; - const int min_dz = max(weights.get_min_index(), prior_Hessian_for_single_densel.get_min_index()-z); - const int max_dz = min(weights.get_max_index(), prior_Hessian_for_single_densel.get_max_index()-z); + const int min_dz = max(weights.get_min_index(), prior_Hessian_for_single_densel.get_min_index() - z); + const int max_dz = min(weights.get_max_index(), prior_Hessian_for_single_densel.get_max_index() - z); - const int min_dy = max(weights[0].get_min_index(), prior_Hessian_for_single_densel[z].get_min_index()-y); - const int max_dy = min(weights[0].get_max_index(), prior_Hessian_for_single_densel[z].get_max_index()-y); + const int min_dy = max(weights[0].get_min_index(), prior_Hessian_for_single_densel[z].get_min_index() - y); + const int max_dy = min(weights[0].get_max_index(), prior_Hessian_for_single_densel[z].get_max_index() - y); - const int min_dx = max(weights[0][0].get_min_index(), prior_Hessian_for_single_densel[z][y].get_min_index()-x); - const int max_dx = min(weights[0][0].get_max_index(), prior_Hessian_for_single_densel[z][y].get_max_index()-x); + const int min_dx = max(weights[0][0].get_min_index(), prior_Hessian_for_single_densel[z][y].get_min_index() - x); + const int max_dx = min(weights[0][0].get_max_index(), prior_Hessian_for_single_densel[z][y].get_max_index() - x); elemT diagonal = 0; - for (int dz=min_dz;dz<=max_dz;++dz) - for (int dy=min_dy;dy<=max_dy;++dy) - for (int dx=min_dx;dx<=max_dx;++dx) - { + for (int dz = min_dz; dz <= max_dz; ++dz) + for (int dy = min_dy; dy <= max_dy; ++dy) + for (int dx = min_dx; dx <= max_dx; ++dx) { // sech^2(x * scalar); sech(x) = 1/cosh(x) - elemT voxel_diff= current_image_estimate[z][y][x] - current_image_estimate[z+dz][y+dy][x+dx]; + elemT voxel_diff = current_image_estimate[z][y][x] - current_image_estimate[z + dz][y + dy][x + dx]; elemT current = weights[dz][dy][dx] * Hessian(voxel_diff, this->scalar); if (do_kappa) - current *= (*kappa_ptr)[z][y][x] * (*kappa_ptr)[z+dz][y+dy][x+dx]; + current *= (*kappa_ptr)[z][y][x] * (*kappa_ptr)[z + dz][y + dy][x + dx]; diagonal += current; - prior_Hessian_for_single_densel_cast[z+dz][y+dy][x+dx] = -current*this->penalisation_factor; + prior_Hessian_for_single_densel_cast[z + dz][y + dy][x + dx] = -current * this->penalisation_factor; } - prior_Hessian_for_single_densel[z][y][x]= diagonal * this->penalisation_factor; + prior_Hessian_for_single_densel[z][y][x] = diagonal * this->penalisation_factor; } template void -LogcoshPrior::parabolic_surrogate_curvature(DiscretisedDensity<3,elemT>& parabolic_surrogate_curvature, - const DiscretisedDensity<3,elemT> ¤t_image_estimate) -{ +LogcoshPrior::parabolic_surrogate_curvature(DiscretisedDensity<3, elemT>& parabolic_surrogate_curvature, + const DiscretisedDensity<3, elemT>& current_image_estimate) { - assert( parabolic_surrogate_curvature.has_same_characteristics(current_image_estimate)); - if (this->penalisation_factor==0) - { + assert(parabolic_surrogate_curvature.has_same_characteristics(current_image_estimate)); + if (this->penalisation_factor == 0) { parabolic_surrogate_curvature.fill(0); return; } - const DiscretisedDensityOnCartesianGrid<3,elemT>& current_image_cast = - dynamic_cast< const DiscretisedDensityOnCartesianGrid<3,elemT> &>(current_image_estimate); + const DiscretisedDensityOnCartesianGrid<3, elemT>& current_image_cast = + dynamic_cast&>(current_image_estimate); - if (weights.get_length() ==0) - { + if (weights.get_length() == 0) { compute_weights(weights, current_image_cast.get_grid_spacing(), this->only_2D); } @@ -471,68 +417,60 @@ LogcoshPrior::parabolic_surrogate_curvature(DiscretisedDensity<3,elemT>& const int min_z = current_image_estimate.get_min_index(); const int max_z = current_image_estimate.get_max_index(); - for (int z=min_z; z<=max_z; z++) - { - const int min_dz = max(weights.get_min_index(), min_z-z); - const int max_dz = min(weights.get_max_index(), max_z-z); + for (int z = min_z; z <= max_z; z++) { + const int min_dz = max(weights.get_min_index(), min_z - z); + const int max_dz = min(weights.get_max_index(), max_z - z); const int min_y = current_image_estimate[z].get_min_index(); const int max_y = current_image_estimate[z].get_max_index(); - for (int y=min_y;y<= max_y;y++) - { - const int min_dy = max(weights[0].get_min_index(), min_y-y); - const int max_dy = min(weights[0].get_max_index(), max_y-y); + for (int y = min_y; y <= max_y; y++) { + const int min_dy = max(weights[0].get_min_index(), min_y - y); + const int max_dy = min(weights[0].get_max_index(), max_y - y); const int min_x = current_image_estimate[z][y].get_min_index(); const int max_x = current_image_estimate[z][y].get_max_index(); - for (int x=min_x;x<= max_x;x++) - { - const int min_dx = max(weights[0][0].get_min_index(), min_x-x); - const int max_dx = min(weights[0][0].get_max_index(), max_x-x); + for (int x = min_x; x <= max_x; x++) { + const int min_dx = max(weights[0][0].get_min_index(), min_x - x); + const int max_dx = min(weights[0][0].get_max_index(), max_x - x); elemT gradient = 0; - for (int dz=min_dz;dz<=max_dz;++dz) - for (int dy=min_dy;dy<=max_dy;++dy) - for (int dx=min_dx;dx<=max_dx;++dx) - { + for (int dz = min_dz; dz <= max_dz; ++dz) + for (int dy = min_dy; dy <= max_dy; ++dy) + for (int dx = min_dx; dx <= max_dx; ++dx) { // psi'(t)/t = tanh/t - elemT voxel_diff=current_image_estimate[z][y][x] - current_image_estimate[z+dz][y+dy][x+dx]; + elemT voxel_diff = current_image_estimate[z][y][x] - current_image_estimate[z + dz][y + dy][x + dx]; elemT current = weights[dz][dy][dx] * surrogate(voxel_diff, this->scalar); if (do_kappa) - current *= (*kappa_ptr)[z][y][x] * (*kappa_ptr)[z+dz][y+dy][x+dx]; + current *= (*kappa_ptr)[z][y][x] * (*kappa_ptr)[z + dz][y + dy][x + dx]; gradient += current; } - parabolic_surrogate_curvature[z][y][x]= gradient * this->penalisation_factor; + parabolic_surrogate_curvature[z][y][x] = gradient * this->penalisation_factor; } } } - info(boost::format("parabolic_surrogate_curvature max %1%, min %2%\n") % parabolic_surrogate_curvature.find_max() % parabolic_surrogate_curvature.find_min()); + info(boost::format("parabolic_surrogate_curvature max %1%, min %2%\n") % parabolic_surrogate_curvature.find_max() % + parabolic_surrogate_curvature.find_min()); } template Succeeded -LogcoshPrior:: -accumulate_Hessian_times_input(DiscretisedDensity<3,elemT>& output, - const DiscretisedDensity<3,elemT>& current_estimate, - const DiscretisedDensity<3,elemT>& input) const -{ +LogcoshPrior::accumulate_Hessian_times_input(DiscretisedDensity<3, elemT>& output, + const DiscretisedDensity<3, elemT>& current_estimate, + const DiscretisedDensity<3, elemT>& input) const { // TODO this function overlaps enormously with parabolic_surrogate_curvature // the only difference is that parabolic_surrogate_curvature uses input==1 - assert( output.has_same_characteristics(input)); - if (this->penalisation_factor==0) - { + assert(output.has_same_characteristics(input)); + if (this->penalisation_factor == 0) { return Succeeded::yes; } - DiscretisedDensityOnCartesianGrid<3,elemT>& output_cast = - dynamic_cast &>(output); + DiscretisedDensityOnCartesianGrid<3, elemT>& output_cast = dynamic_cast&>(output); - if (weights.get_length() ==0) - { + if (weights.get_length() == 0) { compute_weights(weights, output_cast.get_grid_spacing(), this->only_2D); } @@ -543,37 +481,33 @@ accumulate_Hessian_times_input(DiscretisedDensity<3,elemT>& output, const int min_z = output.get_min_index(); const int max_z = output.get_max_index(); - for (int z=min_z; z<=max_z; z++) - { - const int min_dz = max(weights.get_min_index(), min_z-z); - const int max_dz = min(weights.get_max_index(), max_z-z); + for (int z = min_z; z <= max_z; z++) { + const int min_dz = max(weights.get_min_index(), min_z - z); + const int max_dz = min(weights.get_max_index(), max_z - z); const int min_y = output[z].get_min_index(); const int max_y = output[z].get_max_index(); - for (int y=min_y;y<= max_y;y++) - { - const int min_dy = max(weights[0].get_min_index(), min_y-y); - const int max_dy = min(weights[0].get_max_index(), max_y-y); + for (int y = min_y; y <= max_y; y++) { + const int min_dy = max(weights[0].get_min_index(), min_y - y); + const int max_dy = min(weights[0].get_max_index(), max_y - y); const int min_x = output[z][y].get_min_index(); const int max_x = output[z][y].get_max_index(); - for (int x=min_x;x<= max_x;x++) - { - const int min_dx = max(weights[0][0].get_min_index(), min_x-x); - const int max_dx = min(weights[0][0].get_max_index(), max_x-x); + for (int x = min_x; x <= max_x; x++) { + const int min_dx = max(weights[0][0].get_min_index(), min_x - x); + const int max_dx = min(weights[0][0].get_max_index(), max_x - x); elemT result = 0; - for (int dz=min_dz;dz<=max_dz;++dz) - for (int dy=min_dy;dy<=max_dy;++dy) - for (int dx=min_dx;dx<=max_dx;++dx) - { - elemT voxel_diff= current_estimate[z][y][x] - current_estimate[z+dz][y+dy][x+dx]; - elemT current = weights[dz][dy][dx] * Hessian( voxel_diff, this->scalar) * input[z+dz][y+dy][x+dx]; + for (int dz = min_dz; dz <= max_dz; ++dz) + for (int dy = min_dy; dy <= max_dy; ++dy) + for (int dx = min_dx; dx <= max_dx; ++dx) { + elemT voxel_diff = current_estimate[z][y][x] - current_estimate[z + dz][y + dy][x + dx]; + elemT current = weights[dz][dy][dx] * Hessian(voxel_diff, this->scalar) * input[z + dz][y + dy][x + dx]; if (do_kappa) - current *= (*kappa_ptr)[z][y][x] * (*kappa_ptr)[z+dz][y+dy][x+dx]; + current *= (*kappa_ptr)[z][y][x] * (*kappa_ptr)[z + dz][y + dy][x + dx]; result += current; } @@ -585,12 +519,12 @@ accumulate_Hessian_times_input(DiscretisedDensity<3,elemT>& output, return Succeeded::yes; } -# ifdef _MSC_VER - // prevent warning message on reinstantiation, +#ifdef _MSC_VER +// prevent warning message on reinstantiation, // note that we get a linking error if we don't have the explicit instantiation below -# pragma warning(disable:4660) -# endif +# pragma warning(disable : 4660) +#endif - template class LogcoshPrior; +template class LogcoshPrior; END_NAMESPACE_STIR diff --git a/src/recon_buildblock/NiftyPET_projector/BackProjectorByBinNiftyPET.cxx b/src/recon_buildblock/NiftyPET_projector/BackProjectorByBinNiftyPET.cxx index 2dab2c4994..ab2fc28430 100644 --- a/src/recon_buildblock/NiftyPET_projector/BackProjectorByBinNiftyPET.cxx +++ b/src/recon_buildblock/NiftyPET_projector/BackProjectorByBinNiftyPET.cxx @@ -8,7 +8,7 @@ \brief non-inline implementations for stir::BackProjectorByBinNiftyPET \author Richard Brown - + */ /* Copyright (C) 2019, University College London @@ -38,24 +38,16 @@ START_NAMESPACE_STIR ////////////////////////////////////////////////////////// -const char * const -BackProjectorByBinNiftyPET::registered_name = - "NiftyPET"; - -BackProjectorByBinNiftyPET::BackProjectorByBinNiftyPET() : - _cuda_device(0), _cuda_verbosity(true), _use_truncation(false) -{ - this->_already_set_up = false; -} +const char* const BackProjectorByBinNiftyPET::registered_name = "NiftyPET"; -BackProjectorByBinNiftyPET::~BackProjectorByBinNiftyPET() -{ +BackProjectorByBinNiftyPET::BackProjectorByBinNiftyPET() : _cuda_device(0), _cuda_verbosity(true), _use_truncation(false) { + this->_already_set_up = false; } +BackProjectorByBinNiftyPET::~BackProjectorByBinNiftyPET() {} + void -BackProjectorByBinNiftyPET:: -initialise_keymap() -{ +BackProjectorByBinNiftyPET::initialise_keymap() { parser.add_start_key("Back Projector Using NiftyPET Parameters"); parser.add_stop_key("End Back Projector Using NiftyPET Parameters"); parser.add_key("CUDA device", &_cuda_device); @@ -63,106 +55,91 @@ initialise_keymap() } void -BackProjectorByBinNiftyPET:: -set_up(const shared_ptr& proj_data_info_sptr, - const shared_ptr >& density_info_sptr) -{ - BackProjectorByBin::set_up(proj_data_info_sptr,density_info_sptr); - check(*proj_data_info_sptr, *_density_sptr); - _symmetries_sptr.reset(new TrivialDataSymmetriesForBins(proj_data_info_sptr)); - - // Get span - shared_ptr - proj_data_info_cy_no_ar_cor_sptr( - dynamic_pointer_cast( - proj_data_info_sptr)); - if (is_null_ptr(proj_data_info_cy_no_ar_cor_sptr)) - error("BackProjectorByBinNiftyPET: Failed casting to ProjDataInfoCylindricalNoArcCorr"); - int span = proj_data_info_cy_no_ar_cor_sptr->get_max_ring_difference(0) - - proj_data_info_cy_no_ar_cor_sptr->get_min_ring_difference(0) + 1; - - // Set up the niftyPET binary helper - _helper.set_cuda_device_id ( _cuda_device ); - _helper.set_scanner_type(proj_data_info_sptr->get_scanner_ptr()->get_type()); - _helper.set_span ( static_cast(span) ); - _helper.set_att(0); - _helper.set_verbose(_cuda_verbosity); - _helper.set_up(); - - // Create sinogram - _np_sino_w_gaps = _helper.create_niftyPET_sinogram_with_gaps(); +BackProjectorByBinNiftyPET::set_up(const shared_ptr& proj_data_info_sptr, + const shared_ptr>& density_info_sptr) { + BackProjectorByBin::set_up(proj_data_info_sptr, density_info_sptr); + check(*proj_data_info_sptr, *_density_sptr); + _symmetries_sptr.reset(new TrivialDataSymmetriesForBins(proj_data_info_sptr)); + + // Get span + shared_ptr proj_data_info_cy_no_ar_cor_sptr( + dynamic_pointer_cast(proj_data_info_sptr)); + if (is_null_ptr(proj_data_info_cy_no_ar_cor_sptr)) + error("BackProjectorByBinNiftyPET: Failed casting to ProjDataInfoCylindricalNoArcCorr"); + int span = proj_data_info_cy_no_ar_cor_sptr->get_max_ring_difference(0) - + proj_data_info_cy_no_ar_cor_sptr->get_min_ring_difference(0) + 1; + + // Set up the niftyPET binary helper + _helper.set_cuda_device_id(_cuda_device); + _helper.set_scanner_type(proj_data_info_sptr->get_scanner_ptr()->get_type()); + _helper.set_span(static_cast(span)); + _helper.set_att(0); + _helper.set_verbose(_cuda_verbosity); + _helper.set_up(); + + // Create sinogram + _np_sino_w_gaps = _helper.create_niftyPET_sinogram_with_gaps(); } -const DataSymmetriesForViewSegmentNumbers * -BackProjectorByBinNiftyPET:: -get_symmetries_used() const -{ +const DataSymmetriesForViewSegmentNumbers* +BackProjectorByBinNiftyPET::get_symmetries_used() const { if (!this->_already_set_up) error("BackProjectorByBin method called without calling set_up first."); return _symmetries_sptr.get(); } void -BackProjectorByBinNiftyPET:: -back_project(const ProjData& proj_data, int subset_num, int num_subsets) -{ - // Check the user has tried to project all data - if (subset_num != 0 || num_subsets != 1) - error("BackProjectorByBinNiftyPET::back_project " - "only works with all data (no subsets)."); - - _helper.convert_proj_data_stir_to_niftyPET(_np_sino_w_gaps,proj_data); +BackProjectorByBinNiftyPET::back_project(const ProjData& proj_data, int subset_num, int num_subsets) { + // Check the user has tried to project all data + if (subset_num != 0 || num_subsets != 1) + error("BackProjectorByBinNiftyPET::back_project " + "only works with all data (no subsets)."); + + _helper.convert_proj_data_stir_to_niftyPET(_np_sino_w_gaps, proj_data); } void -BackProjectorByBinNiftyPET:: -get_output(DiscretisedDensity<3,float> &density) const -{ - // --------------------------------------------------------------- // - // Remove gaps from sinogram - // --------------------------------------------------------------- // - - std::vector sino_no_gaps = _helper.create_niftyPET_sinogram_no_gaps(); - _helper.remove_gaps(sino_no_gaps, _np_sino_w_gaps); - - // --------------------------------------------------------------- // - // Back project - // --------------------------------------------------------------- // - - std::vector np_im = _helper.create_niftyPET_image(); - _helper.back_project(np_im,sino_no_gaps); - - // --------------------------------------------------------------- // - // NiftyPET -> STIR image conversion - // --------------------------------------------------------------- // - - _helper.convert_image_niftyPET_to_stir(density,np_im); - - // After the back projection, we enforce a truncation outside of the FOV. - // This is because the NiftyPET FOV is smaller than the STIR FOV and this - // could cause some voxel values to spiral out of control. - if (_use_truncation) - truncate_rim(density,17); +BackProjectorByBinNiftyPET::get_output(DiscretisedDensity<3, float>& density) const { + // --------------------------------------------------------------- // + // Remove gaps from sinogram + // --------------------------------------------------------------- // + + std::vector sino_no_gaps = _helper.create_niftyPET_sinogram_no_gaps(); + _helper.remove_gaps(sino_no_gaps, _np_sino_w_gaps); + + // --------------------------------------------------------------- // + // Back project + // --------------------------------------------------------------- // + + std::vector np_im = _helper.create_niftyPET_image(); + _helper.back_project(np_im, sino_no_gaps); + + // --------------------------------------------------------------- // + // NiftyPET -> STIR image conversion + // --------------------------------------------------------------- // + + _helper.convert_image_niftyPET_to_stir(density, np_im); + + // After the back projection, we enforce a truncation outside of the FOV. + // This is because the NiftyPET FOV is smaller than the STIR FOV and this + // could cause some voxel values to spiral out of control. + if (_use_truncation) + truncate_rim(density, 17); } void -BackProjectorByBinNiftyPET:: -start_accumulating_in_new_target() -{ - // Call base level - BackProjectorByBin::start_accumulating_in_new_target(); - // Also reset the NiftyPET sinogram - _np_sino_w_gaps = _helper.create_niftyPET_sinogram_with_gaps(); +BackProjectorByBinNiftyPET::start_accumulating_in_new_target() { + // Call base level + BackProjectorByBin::start_accumulating_in_new_target(); + // Also reset the NiftyPET sinogram + _np_sino_w_gaps = _helper.create_niftyPET_sinogram_with_gaps(); } void -BackProjectorByBinNiftyPET:: -actual_back_project(const RelatedViewgrams& related_viewgrams, - const int, const int, - const int, const int) -{ - for(stir::RelatedViewgrams::const_iterator iter = related_viewgrams.begin(); iter != related_viewgrams.end(); ++iter) - _helper.convert_viewgram_stir_to_niftyPET(_np_sino_w_gaps,*iter); +BackProjectorByBinNiftyPET::actual_back_project(const RelatedViewgrams& related_viewgrams, const int, const int, const int, + const int) { + for (stir::RelatedViewgrams::const_iterator iter = related_viewgrams.begin(); iter != related_viewgrams.end(); ++iter) + _helper.convert_viewgram_stir_to_niftyPET(_np_sino_w_gaps, *iter); } END_NAMESPACE_STIR diff --git a/src/recon_buildblock/NiftyPET_projector/ForwardProjectorByBinNiftyPET.cxx b/src/recon_buildblock/NiftyPET_projector/ForwardProjectorByBinNiftyPET.cxx index 0635abcaf0..4b9fbb51de 100644 --- a/src/recon_buildblock/NiftyPET_projector/ForwardProjectorByBinNiftyPET.cxx +++ b/src/recon_buildblock/NiftyPET_projector/ForwardProjectorByBinNiftyPET.cxx @@ -40,24 +40,16 @@ START_NAMESPACE_STIR ////////////////////////////////////////////////////////// -const char * const -ForwardProjectorByBinNiftyPET::registered_name = - "NiftyPET"; - -ForwardProjectorByBinNiftyPET::ForwardProjectorByBinNiftyPET() : - _cuda_device(0), _cuda_verbosity(true), _use_truncation(false) -{ - this->_already_set_up = false; -} +const char* const ForwardProjectorByBinNiftyPET::registered_name = "NiftyPET"; -ForwardProjectorByBinNiftyPET::~ForwardProjectorByBinNiftyPET() -{ +ForwardProjectorByBinNiftyPET::ForwardProjectorByBinNiftyPET() : _cuda_device(0), _cuda_verbosity(true), _use_truncation(false) { + this->_already_set_up = false; } +ForwardProjectorByBinNiftyPET::~ForwardProjectorByBinNiftyPET() {} + void -ForwardProjectorByBinNiftyPET:: -initialise_keymap() -{ +ForwardProjectorByBinNiftyPET::initialise_keymap() { parser.add_start_key("Forward Projector Using NiftyPET Parameters"); parser.add_stop_key("End Forward Projector Using NiftyPET Parameters"); parser.add_key("CUDA device", &_cuda_device); @@ -65,108 +57,91 @@ initialise_keymap() } void -ForwardProjectorByBinNiftyPET:: -set_up(const shared_ptr& proj_data_info_sptr, - const shared_ptr >& density_info_sptr) -{ - ForwardProjectorByBin::set_up(proj_data_info_sptr,density_info_sptr); - check(*proj_data_info_sptr, *_density_sptr); - _symmetries_sptr.reset(new TrivialDataSymmetriesForBins(proj_data_info_sptr)); - - // Get span - shared_ptr - proj_data_info_cy_no_ar_cor_sptr( - dynamic_pointer_cast( - proj_data_info_sptr)); - if (is_null_ptr(proj_data_info_cy_no_ar_cor_sptr)) - error("ForwardProjectorByBinNiftyPET: Failed casting to ProjDataInfoCylindricalNoArcCorr"); - int span = proj_data_info_cy_no_ar_cor_sptr->get_max_ring_difference(0) - - proj_data_info_cy_no_ar_cor_sptr->get_min_ring_difference(0) + 1; - - // Initialise projected_data_sptr from this->_proj_data_info_sptr - _projected_data_sptr.reset( - new ProjDataInMemory(this->_density_sptr->get_exam_info_sptr(), proj_data_info_sptr)); - - // Set up the niftyPET binary helper - _helper.set_scanner_type(proj_data_info_sptr->get_scanner_ptr()->get_type()); - _helper.set_cuda_device_id ( _cuda_device ); - _helper.set_span ( static_cast(span) ); - _helper.set_att(0); - _helper.set_verbose(_cuda_verbosity); - _helper.set_up(); +ForwardProjectorByBinNiftyPET::set_up(const shared_ptr& proj_data_info_sptr, + const shared_ptr>& density_info_sptr) { + ForwardProjectorByBin::set_up(proj_data_info_sptr, density_info_sptr); + check(*proj_data_info_sptr, *_density_sptr); + _symmetries_sptr.reset(new TrivialDataSymmetriesForBins(proj_data_info_sptr)); + + // Get span + shared_ptr proj_data_info_cy_no_ar_cor_sptr( + dynamic_pointer_cast(proj_data_info_sptr)); + if (is_null_ptr(proj_data_info_cy_no_ar_cor_sptr)) + error("ForwardProjectorByBinNiftyPET: Failed casting to ProjDataInfoCylindricalNoArcCorr"); + int span = proj_data_info_cy_no_ar_cor_sptr->get_max_ring_difference(0) - + proj_data_info_cy_no_ar_cor_sptr->get_min_ring_difference(0) + 1; + + // Initialise projected_data_sptr from this->_proj_data_info_sptr + _projected_data_sptr.reset(new ProjDataInMemory(this->_density_sptr->get_exam_info_sptr(), proj_data_info_sptr)); + + // Set up the niftyPET binary helper + _helper.set_scanner_type(proj_data_info_sptr->get_scanner_ptr()->get_type()); + _helper.set_cuda_device_id(_cuda_device); + _helper.set_span(static_cast(span)); + _helper.set_att(0); + _helper.set_verbose(_cuda_verbosity); + _helper.set_up(); } -const DataSymmetriesForViewSegmentNumbers * -ForwardProjectorByBinNiftyPET:: -get_symmetries_used() const -{ +const DataSymmetriesForViewSegmentNumbers* +ForwardProjectorByBinNiftyPET::get_symmetries_used() const { if (!this->_already_set_up) error("ForwardProjectorByBin method called without calling set_up first."); return _symmetries_sptr.get(); } void -ForwardProjectorByBinNiftyPET:: -actual_forward_project(RelatedViewgrams&, - const DiscretisedDensity<3,float>&, - const int, const int, - const int, const int) -{ - throw std::runtime_error("Need to use set_input() if wanting to use ForwardProjectorByBinNiftyPET."); +ForwardProjectorByBinNiftyPET::actual_forward_project(RelatedViewgrams&, const DiscretisedDensity<3, float>&, const int, + const int, const int, const int) { + throw std::runtime_error("Need to use set_input() if wanting to use ForwardProjectorByBinNiftyPET."); } void -ForwardProjectorByBinNiftyPET:: -actual_forward_project(RelatedViewgrams& viewgrams, - const int, const int, - const int, const int) -{ -// if (min_axial_pos_num != _proj_data_info_sptr->get_min_axial_pos_num() || -// ... ) -// error(); - - viewgrams = _projected_data_sptr->get_related_viewgrams( - viewgrams.get_basic_view_segment_num(), _symmetries_sptr); +ForwardProjectorByBinNiftyPET::actual_forward_project(RelatedViewgrams& viewgrams, const int, const int, const int, + const int) { + // if (min_axial_pos_num != _proj_data_info_sptr->get_min_axial_pos_num() || + // ... ) + // error(); + + viewgrams = _projected_data_sptr->get_related_viewgrams(viewgrams.get_basic_view_segment_num(), _symmetries_sptr); } void -ForwardProjectorByBinNiftyPET:: -set_input(const DiscretisedDensity<3,float> & density) -{ - ForwardProjectorByBin::set_input(density); +ForwardProjectorByBinNiftyPET::set_input(const DiscretisedDensity<3, float>& density) { + ForwardProjectorByBin::set_input(density); - // Before forward projection, we enforce a truncation outside of the FOV. - // This is because the NiftyPET FOV is smaller than the STIR FOV and this - // could cause some voxel values to spiral out of control. - if (_use_truncation) - truncate_rim(*_density_sptr,17); + // Before forward projection, we enforce a truncation outside of the FOV. + // This is because the NiftyPET FOV is smaller than the STIR FOV and this + // could cause some voxel values to spiral out of control. + if (_use_truncation) + truncate_rim(*_density_sptr, 17); - // --------------------------------------------------------------- // - // STIR -> NiftyPET image data conversion - // --------------------------------------------------------------- // + // --------------------------------------------------------------- // + // STIR -> NiftyPET image data conversion + // --------------------------------------------------------------- // - std::vector np_vec = _helper.create_niftyPET_image(); - _helper.convert_image_stir_to_niftyPET(np_vec,*_density_sptr); + std::vector np_vec = _helper.create_niftyPET_image(); + _helper.convert_image_stir_to_niftyPET(np_vec, *_density_sptr); - // --------------------------------------------------------------- // - // Forward projection - // --------------------------------------------------------------- // + // --------------------------------------------------------------- // + // Forward projection + // --------------------------------------------------------------- // - std::vector sino_no_gaps = _helper.create_niftyPET_sinogram_no_gaps(); - _helper.forward_project(sino_no_gaps, np_vec); + std::vector sino_no_gaps = _helper.create_niftyPET_sinogram_no_gaps(); + _helper.forward_project(sino_no_gaps, np_vec); - // --------------------------------------------------------------- // - // Put gaps back into sinogram - // --------------------------------------------------------------- // + // --------------------------------------------------------------- // + // Put gaps back into sinogram + // --------------------------------------------------------------- // - std::vector sino_w_gaps = _helper.create_niftyPET_sinogram_with_gaps(); - _helper.put_gaps(sino_w_gaps, sino_no_gaps); + std::vector sino_w_gaps = _helper.create_niftyPET_sinogram_with_gaps(); + _helper.put_gaps(sino_w_gaps, sino_no_gaps); - // --------------------------------------------------------------- // - // NiftyPET -> STIR projection data conversion - // --------------------------------------------------------------- // + // --------------------------------------------------------------- // + // NiftyPET -> STIR projection data conversion + // --------------------------------------------------------------- // - _helper.convert_proj_data_niftyPET_to_stir(*_projected_data_sptr,sino_w_gaps); + _helper.convert_proj_data_niftyPET_to_stir(*_projected_data_sptr, sino_w_gaps); } END_NAMESPACE_STIR diff --git a/src/recon_buildblock/NiftyPET_projector/NiftyPETHelper.cxx b/src/recon_buildblock/NiftyPET_projector/NiftyPETHelper.cxx index c5e7bdc32d..6028679292 100644 --- a/src/recon_buildblock/NiftyPET_projector/NiftyPETHelper.cxx +++ b/src/recon_buildblock/NiftyPET_projector/NiftyPETHelper.cxx @@ -57,1255 +57,1169 @@ START_NAMESPACE_STIR NiftyPETHelper::~NiftyPETHelper() {} -static void delete_axialLUT(axialLUT *axlut_ptr) -{ - if (!axlut_ptr) return; - delete [] axlut_ptr->li2rno; - delete [] axlut_ptr->li2sn; - delete [] axlut_ptr->li2nos; - delete [] axlut_ptr->sn1_rno; - delete [] axlut_ptr->sn1_sn11; - delete [] axlut_ptr->sn1_ssrb; - delete [] axlut_ptr->sn1_sn11no; +static void +delete_axialLUT(axialLUT* axlut_ptr) { + if (!axlut_ptr) + return; + delete[] axlut_ptr->li2rno; + delete[] axlut_ptr->li2sn; + delete[] axlut_ptr->li2nos; + delete[] axlut_ptr->sn1_rno; + delete[] axlut_ptr->sn1_sn11; + delete[] axlut_ptr->sn1_ssrb; + delete[] axlut_ptr->sn1_sn11no; } -static void delete_txLUT(txLUTs *txluts_ptr) -{ - if (!txluts_ptr) return; - free(txluts_ptr->s2cF); - free(txluts_ptr->c2sF); - free(txluts_ptr->cr2s); - free(txluts_ptr->s2c); - free(txluts_ptr->s2cr); - free(txluts_ptr->aw2sn); - free(txluts_ptr->aw2ali); - free(txluts_ptr->crsr); - free(txluts_ptr->msino); - free(txluts_ptr->cij); +static void +delete_txLUT(txLUTs* txluts_ptr) { + if (!txluts_ptr) + return; + free(txluts_ptr->s2cF); + free(txluts_ptr->c2sF); + free(txluts_ptr->cr2s); + free(txluts_ptr->s2c); + free(txluts_ptr->s2cr); + free(txluts_ptr->aw2sn); + free(txluts_ptr->aw2ali); + free(txluts_ptr->crsr); + free(txluts_ptr->msino); + free(txluts_ptr->cij); } -static -shared_ptr get_cnst(const Scanner &scanner, const bool cuda_verbose, const char cuda_device, const char span) -{ - shared_ptr cnt_sptr = MAKE_SHARED(); - - cnt_sptr->DEVID = cuda_device; // device (GPU) ID. allows choosing the device on which to perform calculations - cnt_sptr->VERBOSE = cuda_verbose; - - if (scanner.get_type() == Scanner::Siemens_mMR) { - if (!(span==0 || span==1 || span==11)) - throw std::runtime_error("NiftyPETHelper::getcnst() " - "only spans 0, 1 and 11 supported for scanner type: " + scanner.get_name()); - - cnt_sptr->A = NSANGLES; //sino angles - cnt_sptr->W = NSBINS; // sino bins for any angular index - cnt_sptr->aw = AW; //sino bins (active only) - - cnt_sptr->NCRS = nCRS; //number of crystals - cnt_sptr->NCRSR = nCRSR; //reduced number of crystals by gaps - cnt_sptr->NRNG = NRINGS; //number of axial rings - cnt_sptr->D = -1; //number of linear indexes along Michelogram diagonals /*unknown*/ - cnt_sptr->Bt = -1; //number of buckets transaxially /*unknown*/ - - cnt_sptr->B = NBUCKTS; //number of buckets (total) - cnt_sptr->Cbt = 32552; //number of crystals in bucket transaxially /*unknown*/ - cnt_sptr->Cba = 3; //number of crystals in bucket axially /*unknown*/ - - cnt_sptr->NSN1 = NSINOS; //number of sinos in span-1 - cnt_sptr->NSN11 = NSINOS11; //in span-11 - cnt_sptr->NSN64 = NRINGS*NRINGS; //with no MRD limit - - cnt_sptr->SPN = span; //span-1 (s=1) or span-11 (s=11, default) or SSRB (s=0) - cnt_sptr->NSEG0 = SEG0; - - cnt_sptr->RNG_STRT = 0; - cnt_sptr->RNG_END = NRINGS; - - cnt_sptr->TGAP = 9; // get the crystal gaps right in the sinogram, period and offset given /*unknown*/ - cnt_sptr->OFFGAP = 1; /*unknown*/ - - cnt_sptr->NSCRS = 21910; // number of scatter crystals used in scatter estimation /*unknown*/ - std::vector sct_irng = {0, 10, 19, 28, 35, 44, 53, 63}; // scatter ring definition - cnt_sptr->NSRNG = int(sct_irng.size()); - cnt_sptr->MRD = mxRD; // maximum ring difference - - cnt_sptr->ALPHA = aLPHA; //angle subtended by a crystal - float R = 32.8f; // ring radius - cnt_sptr->RE = R + 0.67f; // effective ring radius accounting for the depth of interaction - cnt_sptr->AXR = SZ_RING; //axial crystal dim - - cnt_sptr->COSUPSMX = 0.725f; //cosine of max allowed scatter angle - cnt_sptr->COSSTP = (1-cnt_sptr->COSUPSMX)/(255); //cosine step - - cnt_sptr->TOFBINN = 1; // number of TOF bins - cnt_sptr->TOFBINS = 3.9e-10f; // size of TOF bin in [ps] - float CLGHT = 29979245800.f; // speed of light [cm/s] - cnt_sptr->TOFBIND = cnt_sptr->TOFBINS * CLGHT; // size of TOF BIN in cm of travelled distance - cnt_sptr->ITOFBIND = 1.f / cnt_sptr->TOFBIND; // inverse of above - - cnt_sptr->BTP = 0; //0: no bootstrapping, 1: no-parametric, 2: parametric (recommended) - cnt_sptr->BTPRT = 1.f; // ratio of bootstrapped/original events in the target sinogram (1.0 default) - - cnt_sptr->ETHRLD = 0.05f; // intensity percentage threshold of voxels to be considered in the image - } - else - throw std::runtime_error("NiftyPETHelper::getcnst() " - "not implemented for scanner type: " + scanner.get_name()); - return cnt_sptr; +static shared_ptr +get_cnst(const Scanner& scanner, const bool cuda_verbose, const char cuda_device, const char span) { + shared_ptr cnt_sptr = MAKE_SHARED(); + + cnt_sptr->DEVID = cuda_device; // device (GPU) ID. allows choosing the device on which to perform calculations + cnt_sptr->VERBOSE = cuda_verbose; + + if (scanner.get_type() == Scanner::Siemens_mMR) { + if (!(span == 0 || span == 1 || span == 11)) + throw std::runtime_error("NiftyPETHelper::getcnst() " + "only spans 0, 1 and 11 supported for scanner type: " + + scanner.get_name()); + + cnt_sptr->A = NSANGLES; // sino angles + cnt_sptr->W = NSBINS; // sino bins for any angular index + cnt_sptr->aw = AW; // sino bins (active only) + + cnt_sptr->NCRS = nCRS; // number of crystals + cnt_sptr->NCRSR = nCRSR; // reduced number of crystals by gaps + cnt_sptr->NRNG = NRINGS; // number of axial rings + cnt_sptr->D = -1; // number of linear indexes along Michelogram diagonals /*unknown*/ + cnt_sptr->Bt = -1; // number of buckets transaxially /*unknown*/ + + cnt_sptr->B = NBUCKTS; // number of buckets (total) + cnt_sptr->Cbt = 32552; // number of crystals in bucket transaxially /*unknown*/ + cnt_sptr->Cba = 3; // number of crystals in bucket axially /*unknown*/ + + cnt_sptr->NSN1 = NSINOS; // number of sinos in span-1 + cnt_sptr->NSN11 = NSINOS11; // in span-11 + cnt_sptr->NSN64 = NRINGS * NRINGS; // with no MRD limit + + cnt_sptr->SPN = span; // span-1 (s=1) or span-11 (s=11, default) or SSRB (s=0) + cnt_sptr->NSEG0 = SEG0; + + cnt_sptr->RNG_STRT = 0; + cnt_sptr->RNG_END = NRINGS; + + cnt_sptr->TGAP = 9; // get the crystal gaps right in the sinogram, period and offset given /*unknown*/ + cnt_sptr->OFFGAP = 1; /*unknown*/ + + cnt_sptr->NSCRS = 21910; // number of scatter crystals used in scatter estimation /*unknown*/ + std::vector sct_irng = {0, 10, 19, 28, 35, 44, 53, 63}; // scatter ring definition + cnt_sptr->NSRNG = int(sct_irng.size()); + cnt_sptr->MRD = mxRD; // maximum ring difference + + cnt_sptr->ALPHA = aLPHA; // angle subtended by a crystal + float R = 32.8f; // ring radius + cnt_sptr->RE = R + 0.67f; // effective ring radius accounting for the depth of interaction + cnt_sptr->AXR = SZ_RING; // axial crystal dim + + cnt_sptr->COSUPSMX = 0.725f; // cosine of max allowed scatter angle + cnt_sptr->COSSTP = (1 - cnt_sptr->COSUPSMX) / (255); // cosine step + + cnt_sptr->TOFBINN = 1; // number of TOF bins + cnt_sptr->TOFBINS = 3.9e-10f; // size of TOF bin in [ps] + float CLGHT = 29979245800.f; // speed of light [cm/s] + cnt_sptr->TOFBIND = cnt_sptr->TOFBINS * CLGHT; // size of TOF BIN in cm of travelled distance + cnt_sptr->ITOFBIND = 1.f / cnt_sptr->TOFBIND; // inverse of above + + cnt_sptr->BTP = 0; // 0: no bootstrapping, 1: no-parametric, 2: parametric (recommended) + cnt_sptr->BTPRT = 1.f; // ratio of bootstrapped/original events in the target sinogram (1.0 default) + + cnt_sptr->ETHRLD = 0.05f; // intensity percentage threshold of voxels to be considered in the image + } else + throw std::runtime_error("NiftyPETHelper::getcnst() " + "not implemented for scanner type: " + + scanner.get_name()); + return cnt_sptr; } -static inline unsigned to_1d_idx(const unsigned nrow, const unsigned ncol, const unsigned row, const unsigned col) -{ - return col + ncol*row; +static inline unsigned +to_1d_idx(const unsigned nrow, const unsigned ncol, const unsigned row, const unsigned col) { + return col + ncol * row; } -template -dataType* create_heap_array(const unsigned numel, const dataType val = dataType(0)) -{ - dataType *array = new dataType[numel]; - std::fill(array, array+numel, val); - return array; +template +dataType* +create_heap_array(const unsigned numel, const dataType val = dataType(0)) { + dataType* array = new dataType[numel]; + std::fill(array, array + numel, val); + return array; } /// Converted from mmraux.py axial_lut -static void get_axLUT_sptr(shared_ptr &axlut_sptr, std::vector &li2rng, std::vector &li2sn_s, std::vector &li2nos_c, const Cnst &cnt) -{ - const int NRNG = cnt.NRNG; - int NRNG_c, NSN1_c; - - if (cnt.SPN == 1) { - // number of rings calculated for the given ring range (optionally we can use only part of the axial FOV) - NRNG_c = cnt.RNG_END - cnt.RNG_STRT; - // number of sinos in span-1 - NSN1_c = NRNG_c*NRNG_c; - // correct for the max. ring difference in the full axial extent (don't use ring range (1,63) as for this case no correction) - if (NRNG_c==64) - NSN1_c -= 12; +static void +get_axLUT_sptr(shared_ptr& axlut_sptr, std::vector& li2rng, std::vector& li2sn_s, + std::vector& li2nos_c, const Cnst& cnt) { + const int NRNG = cnt.NRNG; + int NRNG_c, NSN1_c; + + if (cnt.SPN == 1) { + // number of rings calculated for the given ring range (optionally we can use only part of the axial FOV) + NRNG_c = cnt.RNG_END - cnt.RNG_STRT; + // number of sinos in span-1 + NSN1_c = NRNG_c * NRNG_c; + // correct for the max. ring difference in the full axial extent (don't use ring range (1,63) as for this case no correction) + if (NRNG_c == 64) + NSN1_c -= 12; + } else { + NRNG_c = NRNG; + NSN1_c = cnt.NSN1; + if (cnt.RNG_END != NRNG || cnt.RNG_STRT != 0) + throw std::runtime_error("NiftyPETHelper::get_axLUT: the reduced axial FOV only works in span=1."); + } + + // ring dimensions + std::vector rng(NRNG * 2); + float z = -.5f * float(NRNG) * cnt.AXR; + for (unsigned i = 0; i < unsigned(NRNG); ++i) { + rng[to_1d_idx(NRNG, 2, i, 0)] = z; + z += cnt.AXR; + rng[to_1d_idx(NRNG, 2, i, 1)] = z; + } + + // --create mapping from ring difference to segment number + // ring difference range + std::vector rd(2 * cnt.MRD + 1); + for (unsigned i = 0; i < rd.size(); ++i) + rd[i] = i - cnt.MRD; + // ring difference to segment + std::vector rd2sg(rd.size() * 2, -1); + // minimum and maximum ring difference for each segment + std::vector minrd = {-5, -16, 6, -27, 17, -38, 28, -49, 39, -60, 50}; + std::vector maxrd = {5, -6, 16, -17, 27, -28, 38, -39, 49, -50, 60}; + for (unsigned i = 0; i < rd.size(); ++i) { + for (unsigned iseg = 0; iseg < minrd.size(); ++iseg) { + if (rd[i] >= minrd[iseg] && rd[i] <= maxrd[iseg]) { + rd2sg[to_1d_idx(rd.size(), 2, i, 0)] = rd[i]; + rd2sg[to_1d_idx(rd.size(), 2, i, 1)] = iseg; + } } - else { - NRNG_c = NRNG; - NSN1_c = cnt.NSN1; - if (cnt.RNG_END!=NRNG || cnt.RNG_STRT!=0) - throw std::runtime_error("NiftyPETHelper::get_axLUT: the reduced axial FOV only works in span=1."); + } + + // create two Michelograms for segments (Mseg) + // and absolute axial position for individual sinos (Mssrb) which is single slice rebinning + std::vector Mssrb(NRNG * NRNG, -1); + std::vector Mseg(NRNG * NRNG, -1); + for (int r1 = cnt.RNG_STRT; r1 < cnt.RNG_END; ++r1) { + for (int r0 = cnt.RNG_STRT; r0 < cnt.RNG_END; ++r0) { + if (abs(r0 - r1) > cnt.MRD) + continue; + int ssp = r0 + r1; // segment sino position (axially: 0-126) + int rdd = r1 - r0; + int jseg = -1; + for (unsigned i = 0; i < rd.size(); ++i) + if (rd2sg[to_1d_idx(rd.size(), 2, i, 0)] == rdd) + jseg = rd2sg[to_1d_idx(rd.size(), 2, i, 1)]; + Mssrb[to_1d_idx(NRNG, NRNG, r1, r0)] = ssp; + Mseg[to_1d_idx(NRNG, NRNG, r1, r0)] = jseg; // negative segments are on top diagonals } - - // ring dimensions - std::vector rng(NRNG*2); - float z = -.5f*float(NRNG)*cnt.AXR; - for (unsigned i=0; i Msn(NRNG * NRNG, -1); + // number of span-1 sinos per sino in span-11 + std::vector Mnos(NRNG * NRNG, -1); + std::vector seg = {127, 115, 115, 93, 93, 71, 71, 49, 49, 27, 27}; + std::vector msk(NRNG * NRNG, 0); + std::vector Mtmp(NRNG * NRNG); + int i = 0; + for (unsigned iseg = 0; iseg < seg.size(); ++iseg) { + // msk = (Mseg==iseg) + for (unsigned a = 0; a < unsigned(NRNG * NRNG); ++a) + msk[a] = Mseg[a] == int(iseg) ? 1 : 0; + // Mtmp = np.copy(Mssrb) + // Mtmp[~msk] = -1 + for (unsigned a = 0; a < unsigned(NRNG * NRNG); ++a) + Mtmp[a] = msk[a] ? Mssrb[a] : -1; + + // uq = np.unique(Mtmp[msk]) + std::vector uq; + for (unsigned a = 0; a < unsigned(NRNG * NRNG); ++a) + if (msk[a] && std::find(uq.begin(), uq.end(), Mtmp[a]) == uq.end()) + uq.push_back(Mtmp[a]); + // for u in range(0,len(uq)): + for (unsigned u = 0; u < uq.size(); ++u) { + // Msn [ Mtmp==uq[u] ] = i + for (unsigned a = 0; a < unsigned(NRNG * NRNG); ++a) + if (Mtmp[a] == uq[u]) + Msn[a] = i; + // Mnos[ Mtmp==uq[u] ] = np.sum(Mtmp==uq[u]) + int sum = 0; + for (unsigned a = 0; a < unsigned(NRNG * NRNG); ++a) + if (Mtmp[a] == uq[u]) + ++sum; + for (unsigned a = 0; a < unsigned(NRNG * NRNG); ++a) + if (Mtmp[a] == uq[u]) + Mnos[a] = sum; + ++i; } - - // --create mapping from ring difference to segment number - // ring difference range - std::vector rd(2*cnt.MRD+1); - for (unsigned i=0; i rd2sg(rd.size()*2, -1); - // minimum and maximum ring difference for each segment - std::vector minrd = {-5,-16, 6,-27,17,-38,28,-49,39,-60,50}; - std::vector maxrd = { 5, -6,16,-17,27,-28,38,-39,49,-50,60}; - for (unsigned i=0; i=minrd[iseg] && rd[i]<=maxrd[iseg]) { - rd2sg[to_1d_idx(rd.size(),2,i,0)] = rd[i]; - rd2sg[to_1d_idx(rd.size(),2,i,1)] = iseg; - } + } + + //====full LUT + short* sn1_rno = create_heap_array(NSN1_c * 2, 0); + short* sn1_ssrb = create_heap_array(NSN1_c, 0); + short* sn1_sn11 = create_heap_array(NSN1_c, 0); + char* sn1_sn11no = create_heap_array(NSN1_c, 0); + int sni = 0; // full linear index, up to 4084 + // michelogram of sino numbers for spn-1 + std::vector Msn1(NRNG * NRNG, -1); + for (unsigned ro = 0; ro < unsigned(NRNG); ++ro) { + unsigned oblique = ro == 0 ? 1 : 2; + // for m in range(oblique): + for (unsigned m = 0; m < oblique; ++m) { + // strt = NRNG*(ro+Cnt['RNG_STRT']) + Cnt['RNG_STRT'] + int strt = NRNG * (ro + cnt.RNG_STRT) + cnt.RNG_STRT; + int stop = (cnt.RNG_STRT + NRNG_c) * NRNG; + int step = NRNG + 1; + + // goes along a diagonal started in the first row at r1 + // for li in range(strt, stop, step): + for (int li = strt; li < stop; li += step) { + int r1, r0; + // linear indecies of michelogram --> subscript indecies for positive and negative RDs + if (m == 0) { + r1 = floor(float(li) / float(NRNG)); + r0 = li - r1 * NRNG; } - } - - // create two Michelograms for segments (Mseg) - // and absolute axial position for individual sinos (Mssrb) which is single slice rebinning - std::vector Mssrb(NRNG*NRNG, -1); - std::vector Mseg(NRNG*NRNG, -1); - for (int r1=cnt.RNG_STRT; r1cnt.MRD) - continue; - int ssp = r0+r1; // segment sino position (axially: 0-126) - int rdd = r1-r0; - int jseg = -1; - for (unsigned i=0; iMRD + if (Msn[to_1d_idx(NRNG, NRNG, r1, r0)] < 0) + continue; - // create a Michelogram map from rings to sino number in span-11 (1..837) - std::vector Msn(NRNG*NRNG, -1); - // number of span-1 sinos per sino in span-11 - std::vector Mnos(NRNG*NRNG, -1); - std::vector seg = {127,115,115,93,93,71,71,49,49,27,27}; - std::vector msk(NRNG*NRNG, 0); - std::vector Mtmp(NRNG*NRNG); - int i=0; - for (unsigned iseg=0; iseg uq; - for (unsigned a=0; a(NSN1_c*2, 0); - short *sn1_ssrb = create_heap_array(NSN1_c, 0); - short *sn1_sn11 = create_heap_array(NSN1_c, 0); - char *sn1_sn11no = create_heap_array(NSN1_c, 0); - int sni = 0; // full linear index, up to 4084 - // michelogram of sino numbers for spn-1 - std::vector Msn1(NRNG*NRNG, -1); - for (unsigned ro=0; ro subscript indecies for positive and negative RDs - if (m==0) { - r1 = floor(float(li)/float(NRNG)); - r0 = li - r1*NRNG; - } - // for positive now (? or vice versa) - else { - r0 = floor(float(li)/float(NRNG)); - r1 = li - r0*NRNG; - } - // avoid case when RD>MRD - if (Msn[to_1d_idx(NRNG,NRNG,r1,r0)]<0) - continue; - - sn1_rno[to_1d_idx(NSN1_c,2, sni,0)] = r0; - sn1_rno[to_1d_idx(NSN1_c,2, sni,1)] = r1; - - sn1_ssrb[sni] = Mssrb[to_1d_idx(NRNG,NRNG,r1,r0)]; - sn1_sn11[sni] = Msn[to_1d_idx(NRNG,NRNG,r0,r1)]; - - sn1_sn11no[sni] = Mnos[to_1d_idx(NRNG,NRNG,r0,r1)]; - - Msn1[to_1d_idx(NRNG,NRNG,r0,r1)] = sni; - //-- - sni += 1; - } - } - } + sn1_ssrb[sni] = Mssrb[to_1d_idx(NRNG, NRNG, r1, r0)]; + sn1_sn11[sni] = Msn[to_1d_idx(NRNG, NRNG, r0, r1)]; - // span-11 sino to SSRB - // sn11_ssrb = np.zeros(Cnt['NSN11'], dtype=np.int32); - std::vector sn11_ssrb(cnt.NSN11, -1); - // sn1_ssrno = np.zeros(Cnt['NSEG0'], dtype=np.int8) - std::vector sn1_ssrno(cnt.NSEG0, 0); - // for i in range(NSN1_c): - for (unsigned i=0; i sn11_ssrno(cnt.NSEG0, 0); - // for i in range(Cnt['NSN11']): - for (unsigned i=0; i0: sn11_ssrno[sn11_ssrb[i]] += 1 - if (sn11_ssrb[i]>0) - sn11_ssrno[sn11_ssrb[i]] += 1; - - // sn11_ssrb = sn11_ssrb[sn11_ssrb>=0] - for (unsigned i=0; i(NLI2R_c*2); - // the same as above but to sinos in span-11 - int *li2sn = create_heap_array(NLI2R_c*2); - std::vector li2sn1(NLI2R_c*2); - li2rng = std::vector(NLI2R_c*2); - // ...to number of sinos (nos) - int *li2nos = create_heap_array(NLI2R_c); - - int dli = 0; - for (unsigned ro=0; ro(NLI2R_c*2); - for (unsigned i=0; i(NLI2R_c); - for (unsigned i=0; i(new axialLUT, delete_axialLUT); - axlut_sptr->li2rno = li2r; // int linear indx to ring indx - axlut_sptr->li2sn = li2sn; // int linear michelogram index (along diagonals) to sino index - axlut_sptr->li2nos = li2nos; // int linear indx to no of sinos in span-11 - axlut_sptr->sn1_rno = sn1_rno; // short - axlut_sptr->sn1_sn11 = sn1_sn11; // short - axlut_sptr->sn1_ssrb = sn1_ssrb; // short - axlut_sptr->sn1_sn11no = sn1_sn11no; // char - // array sizes - axlut_sptr->Nli2rno[0] = NLI2R_c; - axlut_sptr->Nli2rno[1] = 2; - axlut_sptr->Nli2sn[0] = NLI2R_c; - axlut_sptr->Nli2sn[1] = 2; - axlut_sptr->Nli2nos = NLI2R_c; + } + + // span-11 sino to SSRB + // sn11_ssrb = np.zeros(Cnt['NSN11'], dtype=np.int32); + std::vector sn11_ssrb(cnt.NSN11, -1); + // sn1_ssrno = np.zeros(Cnt['NSEG0'], dtype=np.int8) + std::vector sn1_ssrno(cnt.NSEG0, 0); + // for i in range(NSN1_c): + for (unsigned i = 0; i < unsigned(NSN1_c); ++i) { + sn11_ssrb[sn1_sn11[i]] = sn1_ssrb[i]; + sn1_ssrno[sn1_ssrb[i]] += 1; + } + + // sn11_ssrno = np.zeros(Cnt['NSEG0'], dtype=np.int8) + std::vector sn11_ssrno(cnt.NSEG0, 0); + // for i in range(Cnt['NSN11']): + for (unsigned i = 0; i < unsigned(cnt.NSN11); ++i) + // if sn11_ssrb[i]>0: sn11_ssrno[sn11_ssrb[i]] += 1 + if (sn11_ssrb[i] > 0) + sn11_ssrno[sn11_ssrb[i]] += 1; + + // sn11_ssrb = sn11_ssrb[sn11_ssrb>=0] + for (unsigned i = 0; i < unsigned(cnt.NSN11); ++i) + if (sn11_ssrb[i] < 0) + sn11_ssrb[i] = 0; + + // --------------------------------------------------------------------- + // linear index (along diagonals of Michelogram) to rings + // the number of Michelogram elements considered in projection calculations + int NLI2R_c = int(float(NRNG_c * NRNG_c) / 2.f + float(NRNG_c) / 2.f); + + // if the whole scanner is used then account for the MRD and subtract 6 ring permutations + if (NRNG_c == NRNG) + NLI2R_c -= 6; + + int* li2r = create_heap_array(NLI2R_c * 2); + // the same as above but to sinos in span-11 + int* li2sn = create_heap_array(NLI2R_c * 2); + std::vector li2sn1(NLI2R_c * 2); + li2rng = std::vector(NLI2R_c * 2); + // ...to number of sinos (nos) + int* li2nos = create_heap_array(NLI2R_c); + + int dli = 0; + for (unsigned ro = 0; ro < unsigned(NRNG_c); ++ro) { + // selects the sub-Michelogram of the whole Michelogram + unsigned strt = NRNG * (ro + cnt.RNG_STRT) + cnt.RNG_STRT; + unsigned stop = (cnt.RNG_STRT + NRNG_c) * NRNG; + unsigned step = NRNG + 1; + + // goes along a diagonal started in the first row at r2o + for (unsigned li = strt; li < stop; li += step) { + // from the linear indexes of Michelogram get the subscript indexes + unsigned r1 = floor(float(li) / float(NRNG)); + unsigned r0 = li - r1 * NRNG; + if (Msn[to_1d_idx(NRNG, NRNG, r1, r0)] < 0) + continue; + + li2r[to_1d_idx(NLI2R_c, 2, dli, 0)] = r0; + li2r[to_1d_idx(NLI2R_c, 2, dli, 1)] = r1; + //--//rng[to_1d_idx(NRNG,2,i,1)] = z; + li2rng[to_1d_idx(NLI2R_c, 2, dli, 0)] = rng[to_1d_idx(NRNG, 2, r0, 0)]; + li2rng[to_1d_idx(NLI2R_c, 2, dli, 1)] = rng[to_1d_idx(NRNG, 2, r1, 0)]; + //-- + li2sn[to_1d_idx(NLI2R_c, 2, dli, 0)] = Msn[to_1d_idx(NRNG, NRNG, r0, r1)]; + li2sn[to_1d_idx(NLI2R_c, 2, dli, 1)] = Msn[to_1d_idx(NRNG, NRNG, r1, r0)]; + + li2sn1[to_1d_idx(NLI2R_c, 2, dli, 0)] = Msn1[to_1d_idx(NRNG, NRNG, r0, r1)]; + li2sn1[to_1d_idx(NLI2R_c, 2, dli, 1)] = Msn1[to_1d_idx(NRNG, NRNG, r1, r0)]; + + li2nos[dli] = Mnos[to_1d_idx(NRNG, NRNG, r1, r0)]; + + ++dli; + } + } + + // Need some results in a different data type + li2sn_s = std::vector(NLI2R_c * 2); + for (unsigned i = 0; i < unsigned(NLI2R_c * 2); ++i) + li2sn_s[i] = short(li2sn[i]); + li2nos_c = std::vector(NLI2R_c); + for (unsigned i = 0; i < unsigned(NLI2R_c); ++i) + li2nos_c[i] = char(li2nos[i]); + + // Fill in struct + axlut_sptr = shared_ptr(new axialLUT, delete_axialLUT); + axlut_sptr->li2rno = li2r; // int linear indx to ring indx + axlut_sptr->li2sn = li2sn; // int linear michelogram index (along diagonals) to sino index + axlut_sptr->li2nos = li2nos; // int linear indx to no of sinos in span-11 + axlut_sptr->sn1_rno = sn1_rno; // short + axlut_sptr->sn1_sn11 = sn1_sn11; // short + axlut_sptr->sn1_ssrb = sn1_ssrb; // short + axlut_sptr->sn1_sn11no = sn1_sn11no; // char + // array sizes + axlut_sptr->Nli2rno[0] = NLI2R_c; + axlut_sptr->Nli2rno[1] = 2; + axlut_sptr->Nli2sn[0] = NLI2R_c; + axlut_sptr->Nli2sn[1] = 2; + axlut_sptr->Nli2nos = NLI2R_c; } -static -void get_txLUT_sptr(shared_ptr &txlut_sptr, std::vector &crs, std::vector &s2c, Cnst &cnt) -{ - txlut_sptr = shared_ptr(new txLUTs, delete_txLUT); - *txlut_sptr = get_txlut(cnt); - - s2c = std::vector(txlut_sptr->naw*2); - for (unsigned i=0; inaw); ++i) { - s2c[ 2*i ] = txlut_sptr->s2c[i].c0; - s2c[2*i+1] = txlut_sptr->s2c[i].c1; - } - // from mmraux.py - const float bw = 3.209f; // block width - // const float dg = 0.474f; // block gap [cm] - const int NTBLK = 56; - const float alpha = 2*M_PI/float(NTBLK); // 2*pi/NTBLK - crs = std::vector(4 * cnt.NCRS); - float phi = 0.5f*M_PI - alpha/2.f - 0.001f; - for (int bi=0; bi& txlut_sptr, std::vector& crs, std::vector& s2c, Cnst& cnt) { + txlut_sptr = shared_ptr(new txLUTs, delete_txLUT); + *txlut_sptr = get_txlut(cnt); + + s2c = std::vector(txlut_sptr->naw * 2); + for (unsigned i = 0; i < unsigned(txlut_sptr->naw); ++i) { + s2c[2 * i] = txlut_sptr->s2c[i].c0; + s2c[2 * i + 1] = txlut_sptr->s2c[i].c1; + } + // from mmraux.py + const float bw = 3.209f; // block width + // const float dg = 0.474f; // block gap [cm] + const int NTBLK = 56; + const float alpha = 2 * M_PI / float(NTBLK); // 2*pi/NTBLK + crs = std::vector(4 * cnt.NCRS); + float phi = 0.5f * M_PI - alpha / 2.f - 0.001f; + for (int bi = 0; bi < NTBLK; ++bi) { + //-tangent point (ring against detector block) + // ye = RE*np.sin(phi) + // xe = RE*np.cos(phi) + float y = cnt.RE * sin(phi); + float x = cnt.RE * cos(phi); + //-vector for the face of crystals + float pv[2] = {-y, x}; + float pv_ = pow(pv[0] * pv[0] + pv[1] * pv[1], 0.5f); + pv[0] /= pv_; + pv[1] /= pv_; + // update phi for next block + phi -= alpha; + //-end block points + float xcp = x + (bw / 2) * pv[0]; + float ycp = y + (bw / 2) * pv[1]; + for (unsigned n = 1; n < 9; ++n) { + int c = bi * 9 + n - 1; + crs[to_1d_idx(4, cnt.NCRS, 0, c)] = xcp; + crs[to_1d_idx(4, cnt.NCRS, 1, c)] = ycp; + float xc = x + (bw / 2 - float(n) * bw / 8) * pv[0]; + float yc = y + (bw / 2 - float(n) * bw / 8) * pv[1]; + crs[to_1d_idx(4, cnt.NCRS, 2, c)] = xc; + crs[to_1d_idx(4, cnt.NCRS, 3, c)] = yc; + xcp = xc; + ycp = yc; } + } } void -NiftyPETHelper:: -set_up() -{ - if (_span < 0) - throw std::runtime_error("NiftyPETHelper::set_up() " - "sinogram span not set."); - - if (_att < 0) - throw std::runtime_error("NiftyPETHelper::set_up() " - "emission or transmission mode (att) not set."); - - if (_scanner_type == Scanner::Unknown_scanner) - throw std::runtime_error("NiftyPETHelper::set_up() " - "scanner type not set."); - - // Get consts - _cnt_sptr = get_cnst(_scanner_type, _verbose, _devid, _span); - - // Get txLUT - get_txLUT_sptr(_txlut_sptr, _crs, _s2c, *_cnt_sptr); - - // Get axLUT - get_axLUT_sptr(_axlut_sptr, _li2rng, _li2sn, _li2nos, *_cnt_sptr); - - switch(_cnt_sptr->SPN){ - case 11: - _nsinos = _cnt_sptr->NSN11; break; - case 1: - _nsinos = _cnt_sptr->NSEG0; break; - default: - throw std::runtime_error("Unsupported span"); - } - - // isub - _isub = std::vector(unsigned(AW)); - for (unsigned i = 0; iSPN) { + case 11: + _nsinos = _cnt_sptr->NSN11; + break; + case 1: + _nsinos = _cnt_sptr->NSEG0; + break; + default: + throw std::runtime_error("Unsupported span"); + } + + // isub + _isub = std::vector(unsigned(AW)); + for (unsigned i = 0; i < unsigned(AW); i++) + _isub[i] = int(i); + + _already_set_up = true; } void -NiftyPETHelper:: -check_set_up() const -{ - if (!_already_set_up) - throw std::runtime_error("NiftyPETHelper::check_set_up() " - "Make sure filenames have been set and set_up has been run."); +NiftyPETHelper::check_set_up() const { + if (!_already_set_up) + throw std::runtime_error("NiftyPETHelper::check_set_up() " + "Make sure filenames have been set and set_up has been run."); } std::vector -NiftyPETHelper:: -create_niftyPET_image() -{ - return std::vector(SZ_IMZ*SZ_IMX*SZ_IMY,0); +NiftyPETHelper::create_niftyPET_image() { + return std::vector(SZ_IMZ * SZ_IMX * SZ_IMY, 0); } -shared_ptr > -NiftyPETHelper:: -create_stir_im() -{ - int nz(SZ_IMZ), nx(SZ_IMX), ny(SZ_IMY); - float sz(SZ_VOXZ*10.f), sx(SZ_VOXY*10.f), sy(SZ_VOXY*10.f); - shared_ptr > out_im_stir_sptr = - MAKE_SHARED >( - IndexRange3D(0, nz - 1, -(ny / 2), -(ny / 2) + ny - 1, -(nx / 2), -(nx / 2) + nx - 1), - CartesianCoordinate3D(0.f, 0.f, 0.f), - CartesianCoordinate3D(sz, sy, sx)); - return out_im_stir_sptr; +shared_ptr> +NiftyPETHelper::create_stir_im() { + int nz(SZ_IMZ), nx(SZ_IMX), ny(SZ_IMY); + float sz(SZ_VOXZ * 10.f), sx(SZ_VOXY * 10.f), sy(SZ_VOXY * 10.f); + shared_ptr> out_im_stir_sptr = MAKE_SHARED>( + IndexRange3D(0, nz - 1, -(ny / 2), -(ny / 2) + ny - 1, -(nx / 2), -(nx / 2) + nx - 1), + CartesianCoordinate3D(0.f, 0.f, 0.f), CartesianCoordinate3D(sz, sy, sx)); + return out_im_stir_sptr; } std::vector -NiftyPETHelper:: -create_niftyPET_sinogram_no_gaps() const -{ - check_set_up(); - return std::vector(_isub.size() * static_cast(_nsinos), 0); +NiftyPETHelper::create_niftyPET_sinogram_no_gaps() const { + check_set_up(); + return std::vector(_isub.size() * static_cast(_nsinos), 0); } std::vector -NiftyPETHelper:: -create_niftyPET_sinogram_with_gaps() const -{ - return std::vector(NSBINS*NSANGLES*unsigned(_nsinos), 0); +NiftyPETHelper::create_niftyPET_sinogram_with_gaps() const { + return std::vector(NSBINS * NSANGLES * unsigned(_nsinos), 0); } -void get_stir_indices_and_dims(int stir_dim[3], Coordinate3D &min_indices, Coordinate3D &max_indices, const DiscretisedDensity<3,float >&stir) -{ - if (!stir.get_regular_range(min_indices, max_indices)) - throw std::runtime_error("NiftyPETHelper::set_input - " - "expected image to have regular range."); - for (int i=0; i<3; ++i) - stir_dim[i] = max_indices[i + 1] - min_indices[i + 1] + 1; +void +get_stir_indices_and_dims(int stir_dim[3], Coordinate3D& min_indices, Coordinate3D& max_indices, + const DiscretisedDensity<3, float>& stir) { + if (!stir.get_regular_range(min_indices, max_indices)) + throw std::runtime_error("NiftyPETHelper::set_input - " + "expected image to have regular range."); + for (int i = 0; i < 3; ++i) + stir_dim[i] = max_indices[i + 1] - min_indices[i + 1] + 1; } -unsigned convert_NiftyPET_im_3d_to_1d_idx(const unsigned x, const unsigned y, const unsigned z) -{ - return z*SZ_IMX*SZ_IMY + y*SZ_IMX + x; +unsigned +convert_NiftyPET_im_3d_to_1d_idx(const unsigned x, const unsigned y, const unsigned z) { + return z * SZ_IMX * SZ_IMY + y * SZ_IMX + x; } unsigned -NiftyPETHelper:: -convert_NiftyPET_proj_3d_to_1d_idx(const unsigned ang, const unsigned bins, const unsigned sino) const -{ - return sino*NSANGLES*NSBINS + ang*NSBINS + bins; +NiftyPETHelper::convert_NiftyPET_proj_3d_to_1d_idx(const unsigned ang, const unsigned bins, const unsigned sino) const { + return sino * NSANGLES * NSBINS + ang * NSBINS + bins; } void -NiftyPETHelper:: -permute(std::vector &output_array, const std::vector &orig_array, const unsigned output_dims[3], const unsigned permute_order[3]) const -{ +NiftyPETHelper::permute(std::vector& output_array, const std::vector& orig_array, const unsigned output_dims[3], + const unsigned permute_order[3]) const { #ifndef NDEBUG - // Check that in the permute order, each number is between 0 and 2 (can't be <0 because it's unsigned) - for (unsigned i=0; i<3; ++i) - if (permute_order[i]>2) - throw std::runtime_error("Permute order values should be between 0 and 2."); - // Check that each number is unique - for (unsigned i=0; i<3; ++i) - for (unsigned j=i+1; j<3; ++j) - if (permute_order[i] == permute_order[j]) - throw std::runtime_error("Permute order values should be unique."); - // Check that size of output_dims==arr.size() - assert(orig_array.size() == output_dims[0]*output_dims[1]*output_dims[2]); - // Check that output array is same size as input array - assert(orig_array.size() == output_array.size()); + // Check that in the permute order, each number is between 0 and 2 (can't be <0 because it's unsigned) + for (unsigned i = 0; i < 3; ++i) + if (permute_order[i] > 2) + throw std::runtime_error("Permute order values should be between 0 and 2."); + // Check that each number is unique + for (unsigned i = 0; i < 3; ++i) + for (unsigned j = i + 1; j < 3; ++j) + if (permute_order[i] == permute_order[j]) + throw std::runtime_error("Permute order values should be unique."); + // Check that size of output_dims==arr.size() + assert(orig_array.size() == output_dims[0] * output_dims[1] * output_dims[2]); + // Check that output array is same size as input array + assert(orig_array.size() == output_array.size()); #endif - // Calculate old dimensions - unsigned old_dims[3]; - for (unsigned i=0; i<3; ++i) - old_dims[permute_order[i]] = output_dims[i]; + // Calculate old dimensions + unsigned old_dims[3]; + for (unsigned i = 0; i < 3; ++i) + old_dims[permute_order[i]] = output_dims[i]; - // Loop over all elements - for (unsigned old_1d_idx=0; old_1d_idx &sino_no_gaps, const std::vector &sino_w_gaps) const -{ - check_set_up(); - assert(!sino_no_gaps.empty()); - - if (_verbose) - getMemUse(); - - ::remove_gaps(sino_no_gaps.data(), - const_cast(sino_w_gaps.data()), - _nsinos, - _txlut_sptr->aw2ali, - *_cnt_sptr); +NiftyPETHelper::remove_gaps(std::vector& sino_no_gaps, const std::vector& sino_w_gaps) const { + check_set_up(); + assert(!sino_no_gaps.empty()); + + if (_verbose) + getMemUse(); + + ::remove_gaps(sino_no_gaps.data(), const_cast(sino_w_gaps.data()), _nsinos, _txlut_sptr->aw2ali, *_cnt_sptr); } void -NiftyPETHelper:: -put_gaps(std::vector &sino_w_gaps, const std::vector &sino_no_gaps) const -{ - check_set_up(); - assert(!sino_w_gaps.empty()); - - std::vector unpermuted_sino_w_gaps = this->create_niftyPET_sinogram_with_gaps(); - - if (_verbose) - getMemUse(); - - ::put_gaps(unpermuted_sino_w_gaps.data(), - const_cast(sino_no_gaps.data()), - _txlut_sptr->aw2ali, - *_cnt_sptr); - - // Permute the data (as this is done on the NiftyPET python side after put gaps - unsigned output_dims[3] = {837, 252, 344}; - unsigned permute_order[3] = {2,0,1}; - this->permute(sino_w_gaps,unpermuted_sino_w_gaps,output_dims,permute_order); +NiftyPETHelper::put_gaps(std::vector& sino_w_gaps, const std::vector& sino_no_gaps) const { + check_set_up(); + assert(!sino_w_gaps.empty()); + + std::vector unpermuted_sino_w_gaps = this->create_niftyPET_sinogram_with_gaps(); + + if (_verbose) + getMemUse(); + + ::put_gaps(unpermuted_sino_w_gaps.data(), const_cast(sino_no_gaps.data()), _txlut_sptr->aw2ali, *_cnt_sptr); + + // Permute the data (as this is done on the NiftyPET python side after put gaps + unsigned output_dims[3] = {837, 252, 344}; + unsigned permute_order[3] = {2, 0, 1}; + this->permute(sino_w_gaps, unpermuted_sino_w_gaps, output_dims, permute_order); } void -NiftyPETHelper:: -back_project(std::vector &image, const std::vector &sino_no_gaps) const -{ - check_set_up(); - assert(!image.empty()); - - std::vector unpermuted_image = this->create_niftyPET_image(); - - if (_verbose) - getMemUse(); - - gpu_bprj(unpermuted_image.data(), - const_cast(sino_no_gaps.data()), - const_cast(_li2rng.data()), - const_cast(_li2sn.data()), - const_cast(_li2nos.data()), - const_cast(_s2c.data()), - _txlut_sptr->aw2ali, - const_cast(_crs.data()), - const_cast(_isub.data()), - int(_isub.size()), - AW, - 4, // n0crs - nCRS, - *_cnt_sptr); - - // Permute the data (as this is done on the NiftyPET python side after back projection - unsigned output_dims[3] = {127,320,320}; - unsigned permute_order[3] = {2,0,1}; - this->permute(image,unpermuted_image,output_dims,permute_order); +NiftyPETHelper::back_project(std::vector& image, const std::vector& sino_no_gaps) const { + check_set_up(); + assert(!image.empty()); + + std::vector unpermuted_image = this->create_niftyPET_image(); + + if (_verbose) + getMemUse(); + + gpu_bprj(unpermuted_image.data(), const_cast(sino_no_gaps.data()), const_cast(_li2rng.data()), + const_cast(_li2sn.data()), const_cast(_li2nos.data()), const_cast(_s2c.data()), + _txlut_sptr->aw2ali, const_cast(_crs.data()), const_cast(_isub.data()), int(_isub.size()), AW, + 4, // n0crs + nCRS, *_cnt_sptr); + + // Permute the data (as this is done on the NiftyPET python side after back projection + unsigned output_dims[3] = {127, 320, 320}; + unsigned permute_order[3] = {2, 0, 1}; + this->permute(image, unpermuted_image, output_dims, permute_order); } void -NiftyPETHelper:: -forward_project(std::vector &sino_no_gaps, const std::vector &image) const -{ - check_set_up(); - assert(!sino_no_gaps.empty()); - - // Permute the data (as this is done on the NiftyPET python side before forward projection - unsigned output_dims[3] = {320,320,127}; - unsigned permute_order[3] = {1,2,0}; - std::vector permuted_image = this->create_niftyPET_image(); - this->permute(permuted_image,image,output_dims,permute_order); - - if (_verbose) - getMemUse(); - - gpu_fprj(sino_no_gaps.data(), - permuted_image.data(), - const_cast(_li2rng.data()), - const_cast(_li2sn.data()), - const_cast(_li2nos.data()), - const_cast(_s2c.data()), - _txlut_sptr->aw2ali, - const_cast(_crs.data()), - const_cast(_isub.data()), - int(_isub.size()), - AW, - 4, // n0crs - nCRS, - *_cnt_sptr, - _att); +NiftyPETHelper::forward_project(std::vector& sino_no_gaps, const std::vector& image) const { + check_set_up(); + assert(!sino_no_gaps.empty()); + + // Permute the data (as this is done on the NiftyPET python side before forward projection + unsigned output_dims[3] = {320, 320, 127}; + unsigned permute_order[3] = {1, 2, 0}; + std::vector permuted_image = this->create_niftyPET_image(); + this->permute(permuted_image, image, output_dims, permute_order); + + if (_verbose) + getMemUse(); + + gpu_fprj(sino_no_gaps.data(), permuted_image.data(), const_cast(_li2rng.data()), const_cast(_li2sn.data()), + const_cast(_li2nos.data()), const_cast(_s2c.data()), _txlut_sptr->aw2ali, + const_cast(_crs.data()), const_cast(_isub.data()), int(_isub.size()), AW, + 4, // n0crs + nCRS, *_cnt_sptr, _att); } shared_ptr -NiftyPETHelper::create_stir_sino() -{ - const int span=11; - const int max_ring_diff=60; - const int view_mash_factor=1; - shared_ptr ei_sptr = MAKE_SHARED(); - ei_sptr->imaging_modality = ImagingModality::PT; - shared_ptr scanner_sptr(Scanner::get_scanner_from_name("mMR")); - int num_views = scanner_sptr->get_num_detectors_per_ring() / 2 / view_mash_factor; - int num_tang_pos = scanner_sptr->get_max_num_non_arccorrected_bins(); - shared_ptr pdi_sptr = ProjDataInfo::construct_proj_data_info - (scanner_sptr, span, max_ring_diff, num_views, num_tang_pos, false); - shared_ptr pd_sptr = MAKE_SHARED(ei_sptr, pdi_sptr); - return pd_sptr; +NiftyPETHelper::create_stir_sino() { + const int span = 11; + const int max_ring_diff = 60; + const int view_mash_factor = 1; + shared_ptr ei_sptr = MAKE_SHARED(); + ei_sptr->imaging_modality = ImagingModality::PT; + shared_ptr scanner_sptr(Scanner::get_scanner_from_name("mMR")); + int num_views = scanner_sptr->get_num_detectors_per_ring() / 2 / view_mash_factor; + int num_tang_pos = scanner_sptr->get_max_num_non_arccorrected_bins(); + shared_ptr pdi_sptr = + ProjDataInfo::construct_proj_data_info(scanner_sptr, span, max_ring_diff, num_views, num_tang_pos, false); + shared_ptr pd_sptr = MAKE_SHARED(ei_sptr, pdi_sptr); + return pd_sptr; } template -static -dataType * -read_from_binary_file(std::ifstream &file, const unsigned long num_elements) -{ - // Get current position, get size to end and go back to current position - const unsigned long current_pos = file.tellg(); - file.seekg(std::ios::cur, std::ios::end); - const unsigned long remaining_elements = file.tellg() / sizeof(dataType); - file.seekg(current_pos, std::ios::beg); - - if (remaining_elements(num_elements); - file.read(reinterpret_cast(contents), num_elements*sizeof(dataType)); - return contents; +static dataType* +read_from_binary_file(std::ifstream& file, const unsigned long num_elements) { + // Get current position, get size to end and go back to current position + const unsigned long current_pos = file.tellg(); + file.seekg(std::ios::cur, std::ios::end); + const unsigned long remaining_elements = file.tellg() / sizeof(dataType); + file.seekg(current_pos, std::ios::beg); + + if (remaining_elements < num_elements) + throw std::runtime_error("File smaller than requested."); + + dataType* contents = create_heap_array(num_elements); + file.read(reinterpret_cast(contents), num_elements * sizeof(dataType)); + return contents; } /// Read numpy file. No error checking here (assume not fortran order etc.) /// Use std::cout << header if debugging. -static -float * -read_numpy_axf1(const unsigned long num_elements) -{ - const char* NP_SOURCE = std::getenv("NP_SOURCE"); - if (!NP_SOURCE) - throw std::runtime_error("NP_SOURCE not defined, cannot find data"); - - std::string numpy_filename = std::string(NP_SOURCE) + "/niftypet/auxdata/AxialFactorForSpan1.npy"; - // Skip over the header (first newline) - std::ifstream numpy_file(numpy_filename, std::ios::in | std::ios::binary); - if (!numpy_file.is_open()) - throw std::runtime_error("Failed to open numpy file: " + numpy_filename); - - std::string header; - std::getline(numpy_file, header); - // Read - float * axf1 = read_from_binary_file(numpy_file,num_elements); - - // Close file - numpy_file.close(); - - return axf1; +static float* +read_numpy_axf1(const unsigned long num_elements) { + const char* NP_SOURCE = std::getenv("NP_SOURCE"); + if (!NP_SOURCE) + throw std::runtime_error("NP_SOURCE not defined, cannot find data"); + + std::string numpy_filename = std::string(NP_SOURCE) + "/niftypet/auxdata/AxialFactorForSpan1.npy"; + // Skip over the header (first newline) + std::ifstream numpy_file(numpy_filename, std::ios::in | std::ios::binary); + if (!numpy_file.is_open()) + throw std::runtime_error("Failed to open numpy file: " + numpy_filename); + + std::string header; + std::getline(numpy_file, header); + // Read + float* axf1 = read_from_binary_file(numpy_file, num_elements); + + // Close file + numpy_file.close(); + + return axf1; } // Taken from mmrnorm.py -static -NormCmp get_norm_helper_struct(const std::string &norm_binary_file, const Cnst &cnt) -{ - // Open the norm binary file - std::ifstream norm_file(norm_binary_file, std::ios::in | std::ios::binary); - if (!norm_file.is_open()) - throw std::runtime_error("Failed to open norm binary: " + norm_binary_file); - - NormCmp normc; - - // Dimensions of arrays - normc.ngeo[0] = cnt.NSEG0; - normc.ngeo[1] = cnt.W; - normc.ncinf[0] = cnt.W; - normc.ncinf[1] = 9; - normc.nceff[0] = cnt.NRNG; - normc.nceff[1] = cnt.NCRS; - normc.naxe = cnt.NSN11; - normc.nrdt = cnt.NRNG; - normc.ncdt = 9; - - // geo - normc.geo = read_from_binary_file(norm_file, normc.ngeo[0]*normc.ngeo[1]); - // crystal interference - normc.cinf = read_from_binary_file(norm_file, normc.ncinf[0]*normc.ncinf[1]); - // crystal efficiencies - normc.ceff = read_from_binary_file(norm_file, normc.nceff[0]*normc.nceff[1]); - // axial effects - normc.axe1 = read_from_binary_file(norm_file, normc.naxe); - // paralyzing ring DT parameters - normc.dtp = read_from_binary_file(norm_file, normc.nrdt); - // non-paralyzing ring DT parameters - normc.dtnp = read_from_binary_file(norm_file, normc.nrdt); - // TX crystal DT parameter - normc.dtc = read_from_binary_file(norm_file, normc.ncdt); - // additional axial effects - normc.axe2 = read_from_binary_file(norm_file, normc.naxe); - - // Close file - norm_file.close(); - - // One of the pieces of data is stored as a numpy file. Read it. - normc.axf1 = read_numpy_axf1(NSINOS); - - return normc; +static NormCmp +get_norm_helper_struct(const std::string& norm_binary_file, const Cnst& cnt) { + // Open the norm binary file + std::ifstream norm_file(norm_binary_file, std::ios::in | std::ios::binary); + if (!norm_file.is_open()) + throw std::runtime_error("Failed to open norm binary: " + norm_binary_file); + + NormCmp normc; + + // Dimensions of arrays + normc.ngeo[0] = cnt.NSEG0; + normc.ngeo[1] = cnt.W; + normc.ncinf[0] = cnt.W; + normc.ncinf[1] = 9; + normc.nceff[0] = cnt.NRNG; + normc.nceff[1] = cnt.NCRS; + normc.naxe = cnt.NSN11; + normc.nrdt = cnt.NRNG; + normc.ncdt = 9; + + // geo + normc.geo = read_from_binary_file(norm_file, normc.ngeo[0] * normc.ngeo[1]); + // crystal interference + normc.cinf = read_from_binary_file(norm_file, normc.ncinf[0] * normc.ncinf[1]); + // crystal efficiencies + normc.ceff = read_from_binary_file(norm_file, normc.nceff[0] * normc.nceff[1]); + // axial effects + normc.axe1 = read_from_binary_file(norm_file, normc.naxe); + // paralyzing ring DT parameters + normc.dtp = read_from_binary_file(norm_file, normc.nrdt); + // non-paralyzing ring DT parameters + normc.dtnp = read_from_binary_file(norm_file, normc.nrdt); + // TX crystal DT parameter + normc.dtc = read_from_binary_file(norm_file, normc.ncdt); + // additional axial effects + normc.axe2 = read_from_binary_file(norm_file, normc.naxe); + + // Close file + norm_file.close(); + + // One of the pieces of data is stored as a numpy file. Read it. + normc.axf1 = read_numpy_axf1(NSINOS); + + return normc; } /// Get bucket singles (from mmrhist.py) std::vector -get_buckets(unsigned int *bck, const unsigned B, const unsigned nitag) -{ - // number of single rates reported for the given second - // nsr = (hstout['bck'][1,:,:]>>30) - std::vector nsr(nitag * B); - for (unsigned i=0; i> 30; - - - // average in a second period - // hstout['bck'][0,nsr>0] /= nsr[nsr>0] - for (unsigned i=0; i0) - bck[nitag * B + to_1d_idx(nitag,B,i,j)] /= - nsr[to_1d_idx(nitag,B,i,j)]; - - // time indices when single rates given - // tmsk = np.sum(nsr,axis=1)>0 - std::vector tmsk(nitag,false); - for (unsigned i=0; i0) { - tmsk[i] = true; - break; - } - - // single_rate = np.copy(hstout['bck'][0,tmsk,:]) - std::vector single_rate; - for (unsigned i=0; i buckets(B,0); - for (unsigned i=0; i>30) + std::vector nsr(nitag * B); + for (unsigned i = 0; i < nitag; ++i) + for (unsigned j = 0; j < B; ++j) + nsr[to_1d_idx(nitag, B, i, j)] = bck[nitag * B + to_1d_idx(nitag, B, i, j)] >> 30; + + // average in a second period + // hstout['bck'][0,nsr>0] /= nsr[nsr>0] + for (unsigned i = 0; i < nitag; ++i) + for (unsigned j = 0; j < B; ++j) + if (nsr[to_1d_idx(nitag, B, i, j)] > 0) + bck[nitag * B + to_1d_idx(nitag, B, i, j)] /= nsr[to_1d_idx(nitag, B, i, j)]; + + // time indices when single rates given + // tmsk = np.sum(nsr,axis=1)>0 + std::vector tmsk(nitag, false); + for (unsigned i = 0; i < nitag; ++i) + for (unsigned j = 0; j < B; ++j) + if (nsr[to_1d_idx(nitag, B, i, j)] > 0) { + tmsk[i] = true; + break; + } + + // single_rate = np.copy(hstout['bck'][0,tmsk,:]) + std::vector single_rate; + for (unsigned i = 0; i < nitag; ++i) + if (tmsk[i]) + for (unsigned j = 0; j < B; ++j) + single_rate.push_back(bck[to_1d_idx(nitag, B, i, j)]); + unsigned sr_dim0 = single_rate.size() / B; + + // get the average bucket singles: + // buckets = np.int32( np.sum(single_rate,axis=0)/single_rate.shape[0] ) + std::vector buckets(B, 0); + for (unsigned i = 0; i < sr_dim0; ++i) + for (unsigned j = 0; j < B; ++j) + buckets[j] += int(single_rate[to_1d_idx(sr_dim0, B, i, j)]); + for (unsigned i = 0; i < B; ++i) + buckets[i] /= sr_dim0; + + return buckets; } void -NiftyPETHelper:: -lm_to_proj_data(shared_ptr &prompts_sptr, shared_ptr &delayeds_sptr, - shared_ptr &randoms_sptr, shared_ptr &norm_sptr, - const int tstart, const int tstop, - const std::string &lm_binary_file, const std::string &norm_binary_file) const -{ - check_set_up(); - - // Get LM file as absolute path - std::string lm_abs = lm_binary_file; - if (!FilePath::is_absolute(lm_binary_file)) { - FilePath fp_lm_binary(lm_binary_file); - fp_lm_binary.prepend_directory_name(FilePath::get_current_working_directory()); - lm_abs = fp_lm_binary.get_as_string(); - } - - // Get listmode info - getLMinfo(const_cast(lm_abs.c_str()), *_cnt_sptr); - free(lmprop.atag); - free(lmprop.btag); - free(lmprop.ele4chnk); - free(lmprop.ele4thrd); - free(lmprop.t2dfrm); - - // preallocate all the output arrays - in def.h VTIME=2 (), MXNITAG=5400 (max time 1h30) - const int nitag = lmprop.nitag; - const int pow_2_MXNITAG = pow(2,VTIME); - int tn; - if (nitag>MXNITAG) - tn = MXNITAG/pow_2_MXNITAG; - else - tn = (nitag+pow_2_MXNITAG-1)/pow_2_MXNITAG; - - unsigned short frames(0); - int nfrm(1); - - // structure of output data - // var | type | python var | description | shape - // ------+--------------------|------------+----------------------------------+----------------------------------------------------------------- - // nitag | int | | gets set inside lmproc | - // sne | int | | gets set inside lmproc | - // snv | unsigned int * | pvs | sino views | [ tn, Cnt['NSEG0'], Cnt['NSBINS'] ] - // hcp | unsigned int * | phc | head curve prompts | [ nitag ] - // hcd | unsigned int * | dhc | head curve delayeds | [ nitag ] - // fan | unsigned int * | fan | fansums | [ nfrm, Cnt['NRNG'], Cnt['NCRS'] ] - // bck | unsigned int * | bck | buckets (singles) | [ 2, nitag, Cnt['NBCKT'] ] - // mss | float * | mss | centre of mass (axially) | [ nitag ] - // ssr | unsigned int * | ssr | | [ Cnt['NSEG0'], Cnt['NSANGLES'], Cnt['NSBINS'] ] - // psn | void * | psino | if nfrm==1, unsigned int* | [ nfrm, nsinos, Cnt['NSANGLES'], Cnt['NSBINS'] ] - // dsn | void * | dsino | if nfrm==1, unsigned int* | [ nfrm, nsinos, Cnt['NSANGLES'], Cnt['NSBINS'] ] - // psm | unsigned long long | | gets set inside lmproc | - // dsm | unsigned long long | | gets set inside lmproc | - // tot | unsigned int | | gets set inside lmproc | - const unsigned int num_sino_elements = _nsinos * _cnt_sptr->A * _cnt_sptr->W; - hstout dicout; - dicout.snv = create_heap_array(tn * _cnt_sptr->NSEG0 * _cnt_sptr->W); - dicout.hcp = create_heap_array(nitag); - dicout.hcd = create_heap_array(nitag); - dicout.fan = create_heap_array(nfrm * _cnt_sptr->NRNG * _cnt_sptr->NCRS); - dicout.bck = create_heap_array(2 * nitag * _cnt_sptr->B); - dicout.mss = create_heap_array (nitag); - dicout.ssr = create_heap_array(_cnt_sptr->NSEG0 * _cnt_sptr->A * _cnt_sptr->W); - if (nfrm == 1) { - dicout.psn = create_heap_array(nfrm * num_sino_elements); - dicout.dsn = create_heap_array(nfrm * num_sino_elements); - } - else - throw std::runtime_error("NiftyPETHelper::lm_to_proj_data: If nfrm>1, " - "dicout.psn and dicout.dsn should be unsigned char*. Not " - "tested, but should be pretty easy."); - - lmproc(dicout, // hstout (struct): output - const_cast(lm_abs.c_str()), // char *: binary filename (.s, .bf) - &frames, // unsigned short *: think for one frame, frames = 0 - nfrm, // int: num frames - tstart, // int - tstop, // int - _txlut_sptr->s2cF, // *LORcc (struct) - *_axlut_sptr, // axialLUT (struct) - *_cnt_sptr); // Cnst (struct) - - // Convert prompts and delayeds to STIR sinogram - const unsigned short *psn_int = (const unsigned short*)dicout.psn; - const unsigned short *dsn_int = (const unsigned short*)dicout.dsn; - std::vector np_prompts = create_niftyPET_sinogram_with_gaps(); - std::vector np_delayeds = create_niftyPET_sinogram_with_gaps(); - for (unsigned i=0; i cmap(_cnt_sptr->NCRS*_cnt_sptr->NRNG, 0); - - // Estiamte randoms from delayeds - std::vector np_randoms = create_niftyPET_sinogram_with_gaps(); - gpu_randoms(np_randoms.data(), // float *rsn - cmap.data(), // float *cmap, - dicout.fan, // unsigned int * fansums, - *_txlut_sptr, // txLUTs txlut, - _axlut_sptr->sn1_rno, // short *sn1_rno, - _axlut_sptr->sn1_sn11, // short *sn1_sn11, - *_cnt_sptr // const Cnst Cnt) - ); - - randoms_sptr = create_stir_sino(); - convert_proj_data_niftyPET_to_stir(*randoms_sptr, np_randoms); - - // If norm binary has been supplied, generate the norm sinogram - if (!norm_binary_file.empty()) { - - // Get helper - NormCmp normc = get_norm_helper_struct(norm_binary_file, *_cnt_sptr); - - // Get bucket singles - std::vector buckets = get_buckets(dicout.bck, unsigned(_cnt_sptr->B), unsigned(nitag)); - - std::vector np_norm_no_gaps = this->create_niftyPET_sinogram_no_gaps(); - - // Do the conversion - norm_from_components(np_norm_no_gaps.data(), - normc, - *_axlut_sptr, - _txlut_sptr->aw2ali, - buckets.data(), - *_cnt_sptr); - - // Add gaps - std::vector np_norm_w_gaps = this->create_niftyPET_sinogram_with_gaps(); - put_gaps(np_norm_w_gaps,np_norm_no_gaps); - - // Convert to STIR sinogram - norm_sptr = create_stir_sino(); - convert_proj_data_niftyPET_to_stir(*norm_sptr, np_norm_w_gaps); - - // Clear up - delete [] normc.geo; - delete [] normc.cinf; - delete [] normc.ceff; - delete [] normc.axe1; - delete [] normc.dtp; - delete [] normc.dtnp; - delete [] normc.dtc; - delete [] normc.axe2; - delete [] normc.axf1; - } +NiftyPETHelper::lm_to_proj_data(shared_ptr& prompts_sptr, shared_ptr& delayeds_sptr, + shared_ptr& randoms_sptr, shared_ptr& norm_sptr, const int tstart, + const int tstop, const std::string& lm_binary_file, const std::string& norm_binary_file) const { + check_set_up(); + + // Get LM file as absolute path + std::string lm_abs = lm_binary_file; + if (!FilePath::is_absolute(lm_binary_file)) { + FilePath fp_lm_binary(lm_binary_file); + fp_lm_binary.prepend_directory_name(FilePath::get_current_working_directory()); + lm_abs = fp_lm_binary.get_as_string(); + } + + // Get listmode info + getLMinfo(const_cast(lm_abs.c_str()), *_cnt_sptr); + free(lmprop.atag); + free(lmprop.btag); + free(lmprop.ele4chnk); + free(lmprop.ele4thrd); + free(lmprop.t2dfrm); + + // preallocate all the output arrays - in def.h VTIME=2 (), MXNITAG=5400 (max time 1h30) + const int nitag = lmprop.nitag; + const int pow_2_MXNITAG = pow(2, VTIME); + int tn; + if (nitag > MXNITAG) + tn = MXNITAG / pow_2_MXNITAG; + else + tn = (nitag + pow_2_MXNITAG - 1) / pow_2_MXNITAG; + + unsigned short frames(0); + int nfrm(1); + + // structure of output data + // var | type | python var | description | shape + // ------+--------------------|------------+----------------------------------+----------------------------------------------------------------- + // nitag | int | | gets set inside lmproc | + // sne | int | | gets set inside lmproc | + // snv | unsigned int * | pvs | sino views | [ tn, Cnt['NSEG0'], Cnt['NSBINS'] ] + // hcp | unsigned int * | phc | head curve prompts | [ nitag ] hcd | unsigned int * | dhc | + // head curve delayeds | [ nitag ] fan | unsigned int * + // | fan | fansums | [ nfrm, Cnt['NRNG'], Cnt['NCRS'] ] bck | + // unsigned int * | bck | buckets (singles) | [ 2, nitag, Cnt['NBCKT'] ] mss | + // float * | mss | centre of mass (axially) | [ nitag ] ssr | unsigned int * | ssr | | [ + // Cnt['NSEG0'], Cnt['NSANGLES'], Cnt['NSBINS'] ] psn | void * | psino | if nfrm==1, + // unsigned int* | [ nfrm, nsinos, Cnt['NSANGLES'], Cnt['NSBINS'] ] dsn | void * | dsino + // | if nfrm==1, unsigned int* | [ nfrm, nsinos, Cnt['NSANGLES'], Cnt['NSBINS'] ] psm | unsigned long + // long | | gets set inside lmproc | dsm | unsigned long long | | gets set inside lmproc | tot + // | unsigned int | | gets set inside lmproc | + const unsigned int num_sino_elements = _nsinos * _cnt_sptr->A * _cnt_sptr->W; + hstout dicout; + dicout.snv = create_heap_array(tn * _cnt_sptr->NSEG0 * _cnt_sptr->W); + dicout.hcp = create_heap_array(nitag); + dicout.hcd = create_heap_array(nitag); + dicout.fan = create_heap_array(nfrm * _cnt_sptr->NRNG * _cnt_sptr->NCRS); + dicout.bck = create_heap_array(2 * nitag * _cnt_sptr->B); + dicout.mss = create_heap_array(nitag); + dicout.ssr = create_heap_array(_cnt_sptr->NSEG0 * _cnt_sptr->A * _cnt_sptr->W); + if (nfrm == 1) { + dicout.psn = create_heap_array(nfrm * num_sino_elements); + dicout.dsn = create_heap_array(nfrm * num_sino_elements); + } else + throw std::runtime_error("NiftyPETHelper::lm_to_proj_data: If nfrm>1, " + "dicout.psn and dicout.dsn should be unsigned char*. Not " + "tested, but should be pretty easy."); + + lmproc(dicout, // hstout (struct): output + const_cast(lm_abs.c_str()), // char *: binary filename (.s, .bf) + &frames, // unsigned short *: think for one frame, frames = 0 + nfrm, // int: num frames + tstart, // int + tstop, // int + _txlut_sptr->s2cF, // *LORcc (struct) + *_axlut_sptr, // axialLUT (struct) + *_cnt_sptr); // Cnst (struct) + + // Convert prompts and delayeds to STIR sinogram + const unsigned short* psn_int = (const unsigned short*)dicout.psn; + const unsigned short* dsn_int = (const unsigned short*)dicout.dsn; + std::vector np_prompts = create_niftyPET_sinogram_with_gaps(); + std::vector np_delayeds = create_niftyPET_sinogram_with_gaps(); + for (unsigned i = 0; i < num_sino_elements; ++i) { + np_prompts[i] = float(psn_int[i]); + np_delayeds[i] = float(dsn_int[i]); + } + prompts_sptr = create_stir_sino(); + delayeds_sptr = create_stir_sino(); + convert_proj_data_niftyPET_to_stir(*prompts_sptr, np_prompts); + convert_proj_data_niftyPET_to_stir(*delayeds_sptr, np_delayeds); + + // estimated crystal map of singles + // cmap = np.zeros((Cnt['NCRS'], Cnt['NRNG']), dtype=np.float32) + std::vector cmap(_cnt_sptr->NCRS * _cnt_sptr->NRNG, 0); + + // Estiamte randoms from delayeds + std::vector np_randoms = create_niftyPET_sinogram_with_gaps(); + gpu_randoms(np_randoms.data(), // float *rsn + cmap.data(), // float *cmap, + dicout.fan, // unsigned int * fansums, + *_txlut_sptr, // txLUTs txlut, + _axlut_sptr->sn1_rno, // short *sn1_rno, + _axlut_sptr->sn1_sn11, // short *sn1_sn11, + *_cnt_sptr // const Cnst Cnt) + ); + + randoms_sptr = create_stir_sino(); + convert_proj_data_niftyPET_to_stir(*randoms_sptr, np_randoms); + + // If norm binary has been supplied, generate the norm sinogram + if (!norm_binary_file.empty()) { + + // Get helper + NormCmp normc = get_norm_helper_struct(norm_binary_file, *_cnt_sptr); + + // Get bucket singles + std::vector buckets = get_buckets(dicout.bck, unsigned(_cnt_sptr->B), unsigned(nitag)); + + std::vector np_norm_no_gaps = this->create_niftyPET_sinogram_no_gaps(); + + // Do the conversion + norm_from_components(np_norm_no_gaps.data(), normc, *_axlut_sptr, _txlut_sptr->aw2ali, buckets.data(), *_cnt_sptr); + + // Add gaps + std::vector np_norm_w_gaps = this->create_niftyPET_sinogram_with_gaps(); + put_gaps(np_norm_w_gaps, np_norm_no_gaps); + + // Convert to STIR sinogram + norm_sptr = create_stir_sino(); + convert_proj_data_niftyPET_to_stir(*norm_sptr, np_norm_w_gaps); // Clear up - delete [] dicout.snv; - delete [] dicout.hcp; - delete [] dicout.hcd; - delete [] dicout.fan; - delete [] dicout.bck; - delete [] dicout.mss; - delete [] dicout.ssr; - if (nfrm == 1) { - delete [] (unsigned short*)dicout.psn; - delete [] (unsigned short*)dicout.dsn; - } - else - throw std::runtime_error("NiftyPETHelper::lm_to_proj_data: If nfrm>1, " - "need to cast before deleting as is stored as void*."); + delete[] normc.geo; + delete[] normc.cinf; + delete[] normc.ceff; + delete[] normc.axe1; + delete[] normc.dtp; + delete[] normc.dtnp; + delete[] normc.dtc; + delete[] normc.axe2; + delete[] normc.axf1; + } + + // Clear up + delete[] dicout.snv; + delete[] dicout.hcp; + delete[] dicout.hcd; + delete[] dicout.fan; + delete[] dicout.bck; + delete[] dicout.mss; + delete[] dicout.ssr; + if (nfrm == 1) { + delete[](unsigned short*) dicout.psn; + delete[](unsigned short*) dicout.dsn; + } else + throw std::runtime_error("NiftyPETHelper::lm_to_proj_data: If nfrm>1, " + "need to cast before deleting as is stored as void*."); } -void check_im_sizes(const int stir_dim[3], const int np_dim[3]) -{ - for (int i=0; i<3; ++i) - if (stir_dim[i] != np_dim[i]) - throw std::runtime_error((boost::format( - "NiftyPETHelper::check_im_sizes() - " - "STIR image (%1%, %2%, %3%) should be == (%4%,%5%,%6%).") - % stir_dim[0] % stir_dim[1] % stir_dim[2] - % np_dim[0] % np_dim[1] % np_dim[2]).str()); +void +check_im_sizes(const int stir_dim[3], const int np_dim[3]) { + for (int i = 0; i < 3; ++i) + if (stir_dim[i] != np_dim[i]) + throw std::runtime_error((boost::format("NiftyPETHelper::check_im_sizes() - " + "STIR image (%1%, %2%, %3%) should be == (%4%,%5%,%6%).") % + stir_dim[0] % stir_dim[1] % stir_dim[2] % np_dim[0] % np_dim[1] % np_dim[2]) + .str()); } -void check_voxel_spacing(const DiscretisedDensity<3, float> &stir) -{ - // Requires image to be a VoxelsOnCartesianGrid - const VoxelsOnCartesianGrid &stir_vocg = - dynamic_cast&>(stir); - const BasicCoordinate<3,float> stir_spacing = stir_vocg.get_grid_spacing(); - - // Get NiftyPET image spacing (need to *10 for mm) - float np_spacing[3] = { 10.f*SZ_VOXZ, 10.f*SZ_VOXY, 10.f*SZ_VOXY }; - - for (unsigned i=0; i<3; ++i) - if (std::abs(stir_spacing[int(i)+1] - np_spacing[i]) > 1e-4f) - throw std::runtime_error((boost::format( - "NiftyPETHelper::check_voxel_spacing() - " - "STIR image (%1%, %2%, %3%) should be == (%4%,%5%,%6%).") - % stir_spacing[1] % stir_spacing[2] % stir_spacing[3] - % np_spacing[0] % np_spacing[1] % np_spacing[2]).str()); +void +check_voxel_spacing(const DiscretisedDensity<3, float>& stir) { + // Requires image to be a VoxelsOnCartesianGrid + const VoxelsOnCartesianGrid& stir_vocg = dynamic_cast&>(stir); + const BasicCoordinate<3, float> stir_spacing = stir_vocg.get_grid_spacing(); + + // Get NiftyPET image spacing (need to *10 for mm) + float np_spacing[3] = {10.f * SZ_VOXZ, 10.f * SZ_VOXY, 10.f * SZ_VOXY}; + + for (unsigned i = 0; i < 3; ++i) + if (std::abs(stir_spacing[int(i) + 1] - np_spacing[i]) > 1e-4f) + throw std::runtime_error((boost::format("NiftyPETHelper::check_voxel_spacing() - " + "STIR image (%1%, %2%, %3%) should be == (%4%,%5%,%6%).") % + stir_spacing[1] % stir_spacing[2] % stir_spacing[3] % np_spacing[0] % np_spacing[1] % + np_spacing[2]) + .str()); } void -NiftyPETHelper:: -convert_image_stir_to_niftyPET(std::vector &np_vec, const DiscretisedDensity<3, float> &stir) -{ - // Get the dimensions of the input image - Coordinate3D min_indices; - Coordinate3D max_indices; - int stir_dim[3]; - get_stir_indices_and_dims(stir_dim,min_indices,max_indices,stir); - - // NiftyPET requires the image to be (z,x,y)=(SZ_IMZ,SZ_IMX,SZ_IMY) - // which at the time of writing was (127,320,320). - const int np_dim[3] = {SZ_IMZ,SZ_IMX,SZ_IMY}; - check_im_sizes(stir_dim,np_dim); - check_voxel_spacing(stir); - - // Copy data from STIR to NiftyPET image - unsigned np_z, np_y, np_x, np_1d; - for (int z = min_indices[1]; z <= max_indices[1]; z++) { - for (int y = min_indices[2]; y <= max_indices[2]; y++) { - for (int x = min_indices[3]; x <= max_indices[3]; x++) { - // Convert the stir 3d index to a NiftyPET 1d index - np_z = unsigned(z - min_indices[1]); - np_y = unsigned(y - min_indices[2]); - np_x = unsigned(x - min_indices[3]); - np_1d = convert_NiftyPET_im_3d_to_1d_idx(np_x,np_y,np_z); - np_vec[np_1d] = stir[z][y][x]; - } - } +NiftyPETHelper::convert_image_stir_to_niftyPET(std::vector& np_vec, const DiscretisedDensity<3, float>& stir) { + // Get the dimensions of the input image + Coordinate3D min_indices; + Coordinate3D max_indices; + int stir_dim[3]; + get_stir_indices_and_dims(stir_dim, min_indices, max_indices, stir); + + // NiftyPET requires the image to be (z,x,y)=(SZ_IMZ,SZ_IMX,SZ_IMY) + // which at the time of writing was (127,320,320). + const int np_dim[3] = {SZ_IMZ, SZ_IMX, SZ_IMY}; + check_im_sizes(stir_dim, np_dim); + check_voxel_spacing(stir); + + // Copy data from STIR to NiftyPET image + unsigned np_z, np_y, np_x, np_1d; + for (int z = min_indices[1]; z <= max_indices[1]; z++) { + for (int y = min_indices[2]; y <= max_indices[2]; y++) { + for (int x = min_indices[3]; x <= max_indices[3]; x++) { + // Convert the stir 3d index to a NiftyPET 1d index + np_z = unsigned(z - min_indices[1]); + np_y = unsigned(y - min_indices[2]); + np_x = unsigned(x - min_indices[3]); + np_1d = convert_NiftyPET_im_3d_to_1d_idx(np_x, np_y, np_z); + np_vec[np_1d] = stir[z][y][x]; + } } + } } void -NiftyPETHelper:: -convert_image_niftyPET_to_stir(DiscretisedDensity<3,float> &stir, const std::vector &np_vec) -{ - // Get the dimensions of the input image - Coordinate3D min_indices; - Coordinate3D max_indices; - int stir_dim[3]; - get_stir_indices_and_dims(stir_dim,min_indices,max_indices,stir); - - // NiftyPET requires the image to be (z,x,y)=(SZ_IMZ,SZ_IMX,SZ_IMY) - // which at the time of writing was (127,320,320). - const int np_dim[3] = {SZ_IMZ,SZ_IMX,SZ_IMY}; - check_im_sizes(stir_dim,np_dim); - check_voxel_spacing(stir); - - // Copy data from NiftyPET to STIR image - unsigned np_z, np_y, np_x, np_1d; - for (int z = min_indices[1]; z <= max_indices[1]; z++) { - for (int y = min_indices[2]; y <= max_indices[2]; y++) { - for (int x = min_indices[3]; x <= max_indices[3]; x++) { - // Convert the stir 3d index to a NiftyPET 1d index - np_z = unsigned(z - min_indices[1]); - np_y = unsigned(y - min_indices[2]); - np_x = unsigned(x - min_indices[3]); - np_1d = convert_NiftyPET_im_3d_to_1d_idx(np_x,np_y,np_z); - stir[z][y][x] = np_vec[np_1d]; - } - } +NiftyPETHelper::convert_image_niftyPET_to_stir(DiscretisedDensity<3, float>& stir, const std::vector& np_vec) { + // Get the dimensions of the input image + Coordinate3D min_indices; + Coordinate3D max_indices; + int stir_dim[3]; + get_stir_indices_and_dims(stir_dim, min_indices, max_indices, stir); + + // NiftyPET requires the image to be (z,x,y)=(SZ_IMZ,SZ_IMX,SZ_IMY) + // which at the time of writing was (127,320,320). + const int np_dim[3] = {SZ_IMZ, SZ_IMX, SZ_IMY}; + check_im_sizes(stir_dim, np_dim); + check_voxel_spacing(stir); + + // Copy data from NiftyPET to STIR image + unsigned np_z, np_y, np_x, np_1d; + for (int z = min_indices[1]; z <= max_indices[1]; z++) { + for (int y = min_indices[2]; y <= max_indices[2]; y++) { + for (int x = min_indices[3]; x <= max_indices[3]; x++) { + // Convert the stir 3d index to a NiftyPET 1d index + np_z = unsigned(z - min_indices[1]); + np_y = unsigned(y - min_indices[2]); + np_x = unsigned(x - min_indices[3]); + np_1d = convert_NiftyPET_im_3d_to_1d_idx(np_x, np_y, np_z); + stir[z][y][x] = np_vec[np_1d]; + } } + } } void -get_vals_for_proj_data_conversion(std::vector &sizes, std::vector &segment_sequence, - int &num_sinograms, int &min_view, int &max_view, - int &min_tang_pos, int &max_tang_pos, - const ProjDataInfo& proj_data_info, const std::vector &np_vec) -{ - const ProjDataInfoCylindricalNoArcCorr * info_sptr = - dynamic_cast(&proj_data_info); - if (is_null_ptr(info_sptr)) - error("NiftyPETHelper: only works with cylindrical projection data without arc-correction"); - - segment_sequence = ecat::find_segment_sequence(proj_data_info); - sizes.resize(segment_sequence.size()); - for (std::size_t s=0U; s& sizes, std::vector& segment_sequence, int& num_sinograms, int& min_view, + int& max_view, int& min_tang_pos, int& max_tang_pos, const ProjDataInfo& proj_data_info, + const std::vector& np_vec) { + const ProjDataInfoCylindricalNoArcCorr* info_sptr = dynamic_cast(&proj_data_info); + if (is_null_ptr(info_sptr)) + error("NiftyPETHelper: only works with cylindrical projection data without arc-correction"); + + segment_sequence = ecat::find_segment_sequence(proj_data_info); + sizes.resize(segment_sequence.size()); + for (std::size_t s = 0U; s < segment_sequence.size(); ++s) + sizes[s] = proj_data_info.get_num_axial_poss(segment_sequence[s]); + + // Get dimensions of STIR sinogram + min_view = proj_data_info.get_min_view_num(); + max_view = proj_data_info.get_max_view_num(); + min_tang_pos = proj_data_info.get_min_tangential_pos_num(); + max_tang_pos = proj_data_info.get_max_tangential_pos_num(); + + num_sinograms = proj_data_info.get_num_axial_poss(0); + for (int s = 1; s <= proj_data_info.get_max_segment_num(); ++s) + num_sinograms += 2 * proj_data_info.get_num_axial_poss(s); + + int num_proj_data_elems = num_sinograms * (1 + max_view - min_view) * (1 + max_tang_pos - min_tang_pos); + + // Make sure they're the same size + if (np_vec.size() != unsigned(num_proj_data_elems)) + error(boost::format("NiftyPETHelper::get_vals_for_proj_data_conversion " + "NiftyPET and STIR sinograms are different sizes (%1% for STIR versus %2% for NP") % + num_proj_data_elems % np_vec.size()); } -void get_stir_segment_and_axial_pos_from_NiftyPET_sino(int &segment, int &axial_pos, const unsigned np_sino, const std::vector &sizes, const std::vector &segment_sequence) -{ - int z = int(np_sino); - for (unsigned i=0; i& sizes, const std::vector& segment_sequence) { + int z = int(np_sino); + for (unsigned i = 0; i < segment_sequence.size(); ++i) { + if (z < sizes[i]) { + axial_pos = z; + segment = segment_sequence[i]; + return; + } else { + z -= sizes[i]; } + } } -void get_NiftyPET_sino_from_stir_segment_and_axial_pos(unsigned &np_sino, const int segment, const int axial_pos, const std::vector &sizes, const std::vector &segment_sequence) -{ - np_sino = 0U; - for (unsigned i=0; i& sizes, const std::vector& segment_sequence) { + np_sino = 0U; + for (unsigned i = 0; i < segment_sequence.size(); ++i) { + if (segment == segment_sequence[i]) { + np_sino += axial_pos; + return; + } else { + np_sino += sizes[i]; } - throw std::runtime_error("NiftyPETHelper::get_NiftyPET_sino_from_stir_segment_and_axial_pos(): Failed to find NiftyPET sinogram."); + } + throw std::runtime_error( + "NiftyPETHelper::get_NiftyPET_sino_from_stir_segment_and_axial_pos(): Failed to find NiftyPET sinogram."); } void -NiftyPETHelper:: -convert_viewgram_stir_to_niftyPET(std::vector &np_vec, const Viewgram& viewgram) const -{ - // Get the values (and LUT) to be able to switch between STIR and NiftyPET projDatas - std::vector sizes, segment_sequence; - int num_sinograms, min_view, max_view, min_tang_pos, max_tang_pos; - get_vals_for_proj_data_conversion(sizes, segment_sequence, num_sinograms, min_view, max_view, - min_tang_pos, max_tang_pos, *viewgram.get_proj_data_info_sptr(), np_vec); - - const int segment = viewgram.get_segment_num(); - const int view = viewgram.get_view_num(); +NiftyPETHelper::convert_viewgram_stir_to_niftyPET(std::vector& np_vec, const Viewgram& viewgram) const { + // Get the values (and LUT) to be able to switch between STIR and NiftyPET projDatas + std::vector sizes, segment_sequence; + int num_sinograms, min_view, max_view, min_tang_pos, max_tang_pos; + get_vals_for_proj_data_conversion(sizes, segment_sequence, num_sinograms, min_view, max_view, min_tang_pos, max_tang_pos, + *viewgram.get_proj_data_info_sptr(), np_vec); - // Loop over the STIR view and tangential position - for (int ax_pos=viewgram.get_min_axial_pos_num(); ax_pos<=viewgram.get_max_axial_pos_num(); ++ax_pos) { + const int segment = viewgram.get_segment_num(); + const int view = viewgram.get_view_num(); - unsigned np_sino; + // Loop over the STIR view and tangential position + for (int ax_pos = viewgram.get_min_axial_pos_num(); ax_pos <= viewgram.get_max_axial_pos_num(); ++ax_pos) { - // Convert the NiftyPET sinogram to STIR's segment and axial position - get_NiftyPET_sino_from_stir_segment_and_axial_pos(np_sino, segment, ax_pos, sizes, segment_sequence); + unsigned np_sino; - for (int tang_pos=min_tang_pos; tang_pos<=max_tang_pos; ++tang_pos) { + // Convert the NiftyPET sinogram to STIR's segment and axial position + get_NiftyPET_sino_from_stir_segment_and_axial_pos(np_sino, segment, ax_pos, sizes, segment_sequence); - unsigned np_ang = unsigned(view-min_view); - unsigned np_bin = unsigned(tang_pos-min_tang_pos); - unsigned np_1d = convert_NiftyPET_proj_3d_to_1d_idx(np_ang,np_bin,np_sino); - np_vec.at(np_1d) = viewgram.at(ax_pos).at(tang_pos); - } + for (int tang_pos = min_tang_pos; tang_pos <= max_tang_pos; ++tang_pos) { + + unsigned np_ang = unsigned(view - min_view); + unsigned np_bin = unsigned(tang_pos - min_tang_pos); + unsigned np_1d = convert_NiftyPET_proj_3d_to_1d_idx(np_ang, np_bin, np_sino); + np_vec.at(np_1d) = viewgram.at(ax_pos).at(tang_pos); } + } } void -NiftyPETHelper:: -convert_proj_data_stir_to_niftyPET(std::vector &np_vec, const ProjData& stir) const -{ - const int min_view = stir.get_min_view_num(); - const int max_view = stir.get_max_view_num(); - const int min_segment = stir.get_min_segment_num(); - const int max_segment = stir.get_max_segment_num(); - - for (int view=min_view; view<=max_view; ++view) { - for (int segment=min_segment; segment<=max_segment; ++segment) { - convert_viewgram_stir_to_niftyPET(np_vec, stir.get_viewgram(view,segment)); - } +NiftyPETHelper::convert_proj_data_stir_to_niftyPET(std::vector& np_vec, const ProjData& stir) const { + const int min_view = stir.get_min_view_num(); + const int max_view = stir.get_max_view_num(); + const int min_segment = stir.get_min_segment_num(); + const int max_segment = stir.get_max_segment_num(); + + for (int view = min_view; view <= max_view; ++view) { + for (int segment = min_segment; segment <= max_segment; ++segment) { + convert_viewgram_stir_to_niftyPET(np_vec, stir.get_viewgram(view, segment)); } + } } void -NiftyPETHelper:: -convert_proj_data_niftyPET_to_stir(ProjData &stir, const std::vector &np_vec) const -{ - // Get the values (and LUT) to be able to switch between STIR and NiftyPET projDatas - std::vector sizes, segment_sequence; - int num_sinograms, min_view, max_view, min_tang_pos, max_tang_pos; - get_vals_for_proj_data_conversion(sizes, segment_sequence, num_sinograms, min_view, max_view, - min_tang_pos, max_tang_pos, *stir.get_proj_data_info_sptr(), np_vec); - - int segment, axial_pos; - // Loop over all NiftyPET sinograms - for (unsigned np_sino = 0; np_sino < unsigned(num_sinograms); ++np_sino) { - - // Convert the NiftyPET sinogram to STIR's segment and axial position - get_stir_segment_and_axial_pos_from_NiftyPET_sino(segment, axial_pos, np_sino, sizes, segment_sequence); - - // Get the corresponding STIR sinogram - Sinogram sino = stir.get_empty_sinogram(axial_pos,segment); - - // Loop over the STIR view and tangential position - for (int view=min_view; view<=max_view; ++view) { - for (int tang_pos=min_tang_pos; tang_pos<=max_tang_pos; ++tang_pos) { - - unsigned np_ang = unsigned(view-min_view); - unsigned np_bin = unsigned(tang_pos-min_tang_pos); - unsigned np_1d = convert_NiftyPET_proj_3d_to_1d_idx(np_ang,np_bin,np_sino); - sino.at(view).at(tang_pos) = np_vec.at(np_1d); - } - } - stir.set_sinogram(sino); +NiftyPETHelper::convert_proj_data_niftyPET_to_stir(ProjData& stir, const std::vector& np_vec) const { + // Get the values (and LUT) to be able to switch between STIR and NiftyPET projDatas + std::vector sizes, segment_sequence; + int num_sinograms, min_view, max_view, min_tang_pos, max_tang_pos; + get_vals_for_proj_data_conversion(sizes, segment_sequence, num_sinograms, min_view, max_view, min_tang_pos, max_tang_pos, + *stir.get_proj_data_info_sptr(), np_vec); + + int segment, axial_pos; + // Loop over all NiftyPET sinograms + for (unsigned np_sino = 0; np_sino < unsigned(num_sinograms); ++np_sino) { + + // Convert the NiftyPET sinogram to STIR's segment and axial position + get_stir_segment_and_axial_pos_from_NiftyPET_sino(segment, axial_pos, np_sino, sizes, segment_sequence); + + // Get the corresponding STIR sinogram + Sinogram sino = stir.get_empty_sinogram(axial_pos, segment); + + // Loop over the STIR view and tangential position + for (int view = min_view; view <= max_view; ++view) { + for (int tang_pos = min_tang_pos; tang_pos <= max_tang_pos; ++tang_pos) { + + unsigned np_ang = unsigned(view - min_view); + unsigned np_bin = unsigned(tang_pos - min_tang_pos); + unsigned np_1d = convert_NiftyPET_proj_3d_to_1d_idx(np_ang, np_bin, np_sino); + sino.at(view).at(tang_pos) = np_vec.at(np_1d); + } } + stir.set_sinogram(sino); + } } END_NAMESPACE_STIR diff --git a/src/recon_buildblock/NiftyPET_projector/ProjectorByBinPairUsingNiftyPET.cxx b/src/recon_buildblock/NiftyPET_projector/ProjectorByBinPairUsingNiftyPET.cxx index 89ea36ded3..b70e6f2052 100644 --- a/src/recon_buildblock/NiftyPET_projector/ProjectorByBinPairUsingNiftyPET.cxx +++ b/src/recon_buildblock/NiftyPET_projector/ProjectorByBinPairUsingNiftyPET.cxx @@ -6,9 +6,9 @@ \ingroup NiftyPET \brief non-inline implementations for stir::ProjectorByBinPairUsingNiftyPET - + \author Richard Brown - + */ /* Copyright (C) 2019, University College London @@ -27,52 +27,41 @@ See STIR/LICENSE.txt for details */ - #include "stir/recon_buildblock/NiftyPET_projector/ProjectorByBinPairUsingNiftyPET.h" #include "stir/recon_buildblock/NiftyPET_projector/ForwardProjectorByBinNiftyPET.h" #include "stir/recon_buildblock/NiftyPET_projector/BackProjectorByBinNiftyPET.h" START_NAMESPACE_STIR +const char* const ProjectorByBinPairUsingNiftyPET::registered_name = "NiftyPET"; -const char * const -ProjectorByBinPairUsingNiftyPET::registered_name = - "NiftyPET"; - - -void -ProjectorByBinPairUsingNiftyPET::initialise_keymap() -{ +void +ProjectorByBinPairUsingNiftyPET::initialise_keymap() { base_type::initialise_keymap(); parser.add_start_key("Projector Pair Using NiftyPET Parameters"); parser.add_stop_key("End Projector Pair Using NiftyPET Parameters"); - parser.add_key("verbosity",&_verbosity); - parser.add_key("use_truncation",&_use_truncation); + parser.add_key("verbosity", &_verbosity); + parser.add_key("use_truncation", &_use_truncation); } - void -ProjectorByBinPairUsingNiftyPET::set_defaults() -{ +ProjectorByBinPairUsingNiftyPET::set_defaults() { base_type::set_defaults(); this->set_verbosity(true); this->set_use_truncation(false); } bool -ProjectorByBinPairUsingNiftyPET::post_processing() -{ - this->set_verbosity(this->_verbosity); - this->set_use_truncation(this->_use_truncation); +ProjectorByBinPairUsingNiftyPET::post_processing() { + this->set_verbosity(this->_verbosity); + this->set_use_truncation(this->_use_truncation); if (base_type::post_processing()) return true; return false; } -ProjectorByBinPairUsingNiftyPET:: -ProjectorByBinPairUsingNiftyPET() -{ +ProjectorByBinPairUsingNiftyPET::ProjectorByBinPairUsingNiftyPET() { this->forward_projector_sptr.reset(new ForwardProjectorByBinNiftyPET); this->back_projector_sptr.reset(new BackProjectorByBinNiftyPET); set_defaults(); @@ -82,7 +71,7 @@ ProjectorByBinPairUsingNiftyPET() ProjectorByBinPairUsingNiftyPET:: set_up(const shared_ptr& proj_data_info_sptr, const shared_ptr >& image_info_sptr) -{ +{ // proj_matrix_sptr->set_up() not needed as the projection matrix will be set_up indirectly by // the forward_projector->set_up (which is called in the base class) // proj_matrix_sptr->set_up(proj_data_info_sptr, image_info_sptr); @@ -93,34 +82,34 @@ set_up(const shared_ptr& proj_data_info_sptr, return Succeeded::yes; }*/ -void ProjectorByBinPairUsingNiftyPET::set_verbosity(const bool verbosity) -{ - _verbosity = verbosity; - - shared_ptr fwd_prj_downcast_sptr = - dynamic_pointer_cast(this->forward_projector_sptr); - if (fwd_prj_downcast_sptr) - fwd_prj_downcast_sptr->set_verbosity(_verbosity); - - shared_ptr bck_prj_downcast_sptr = - dynamic_pointer_cast(this->back_projector_sptr); - if (bck_prj_downcast_sptr) - bck_prj_downcast_sptr->set_verbosity(_verbosity); +void +ProjectorByBinPairUsingNiftyPET::set_verbosity(const bool verbosity) { + _verbosity = verbosity; + + shared_ptr fwd_prj_downcast_sptr = + dynamic_pointer_cast(this->forward_projector_sptr); + if (fwd_prj_downcast_sptr) + fwd_prj_downcast_sptr->set_verbosity(_verbosity); + + shared_ptr bck_prj_downcast_sptr = + dynamic_pointer_cast(this->back_projector_sptr); + if (bck_prj_downcast_sptr) + bck_prj_downcast_sptr->set_verbosity(_verbosity); } -void ProjectorByBinPairUsingNiftyPET::set_use_truncation(const bool use_truncation) -{ - _use_truncation = use_truncation; - - shared_ptr fwd_prj_downcast_sptr = - dynamic_pointer_cast(this->forward_projector_sptr); - if (fwd_prj_downcast_sptr) - fwd_prj_downcast_sptr->set_use_truncation(_use_truncation); - - shared_ptr bck_prj_downcast_sptr = - dynamic_pointer_cast(this->back_projector_sptr); - if (bck_prj_downcast_sptr) - bck_prj_downcast_sptr->set_use_truncation(_use_truncation); +void +ProjectorByBinPairUsingNiftyPET::set_use_truncation(const bool use_truncation) { + _use_truncation = use_truncation; + + shared_ptr fwd_prj_downcast_sptr = + dynamic_pointer_cast(this->forward_projector_sptr); + if (fwd_prj_downcast_sptr) + fwd_prj_downcast_sptr->set_use_truncation(_use_truncation); + + shared_ptr bck_prj_downcast_sptr = + dynamic_pointer_cast(this->back_projector_sptr); + if (bck_prj_downcast_sptr) + bck_prj_downcast_sptr->set_use_truncation(_use_truncation); } END_NAMESPACE_STIR diff --git a/src/recon_buildblock/PLSPrior.cxx b/src/recon_buildblock/PLSPrior.cxx index 3d9fa9f954..42989c293d 100644 --- a/src/recon_buildblock/PLSPrior.cxx +++ b/src/recon_buildblock/PLSPrior.cxx @@ -46,8 +46,7 @@ START_NAMESPACE_STIR template void -PLSPrior::initialise_keymap() -{ +PLSPrior::initialise_keymap() { base_type::initialise_keymap(); this->parser.add_start_key("PLS Prior Parameters"); this->parser.add_key("only 2D", &only_2D); @@ -59,69 +58,65 @@ PLSPrior::initialise_keymap() this->parser.add_stop_key("END PLS Prior Parameters"); } - template Succeeded -PLSPrior::set_up (shared_ptr > const& target_sptr) -{ - base_type::set_up(target_sptr); - - if (is_null_ptr( this->anatomical_sptr)) - { - error("PLS prior needs an anatomical image"); - return Succeeded::no; - } +PLSPrior::set_up(shared_ptr> const& target_sptr) { + base_type::set_up(target_sptr); + + if (is_null_ptr(this->anatomical_sptr)) { + error("PLS prior needs an anatomical image"); + return Succeeded::no; + } - if(! (*target_sptr).has_same_characteristics(*this->anatomical_sptr)) - error("anatomical and target images are not compatible! Make sure they are"); + if (!(*target_sptr).has_same_characteristics(*this->anatomical_sptr)) + error("anatomical and target images are not compatible! Make sure they are"); - shared_ptr > anatomical_im_grad_z_sptr; - if (!only_2D) - anatomical_im_grad_z_sptr.reset(this->anatomical_sptr->get_empty_copy ()); + shared_ptr> anatomical_im_grad_z_sptr; + if (!only_2D) + anatomical_im_grad_z_sptr.reset(this->anatomical_sptr->get_empty_copy()); - shared_ptr > anatomical_im_grad_y_sptr(this->anatomical_sptr->get_empty_copy ()); - shared_ptr > anatomical_im_grad_x_sptr(this->anatomical_sptr->get_empty_copy ()); - shared_ptr > norm_sptr(this->anatomical_sptr->get_empty_copy ()); + shared_ptr> anatomical_im_grad_y_sptr(this->anatomical_sptr->get_empty_copy()); + shared_ptr> anatomical_im_grad_x_sptr(this->anatomical_sptr->get_empty_copy()); + shared_ptr> norm_sptr(this->anatomical_sptr->get_empty_copy()); - if (!only_2D) - compute_image_gradient_element ((*anatomical_im_grad_z_sptr),0,*this->anatomical_sptr); + if (!only_2D) + compute_image_gradient_element((*anatomical_im_grad_z_sptr), 0, *this->anatomical_sptr); - compute_image_gradient_element (*anatomical_im_grad_y_sptr,1,*this->anatomical_sptr); - compute_image_gradient_element (*anatomical_im_grad_x_sptr,2,*this->anatomical_sptr); + compute_image_gradient_element(*anatomical_im_grad_y_sptr, 1, *this->anatomical_sptr); + compute_image_gradient_element(*anatomical_im_grad_x_sptr, 2, *this->anatomical_sptr); - if (!only_2D) - this->set_anatomical_grad_sptr (anatomical_im_grad_z_sptr,0); + if (!only_2D) + this->set_anatomical_grad_sptr(anatomical_im_grad_z_sptr, 0); - this->set_anatomical_grad_sptr (anatomical_im_grad_y_sptr,1); - this->set_anatomical_grad_sptr (anatomical_im_grad_x_sptr,2); + this->set_anatomical_grad_sptr(anatomical_im_grad_y_sptr, 1); + this->set_anatomical_grad_sptr(anatomical_im_grad_x_sptr, 2); - compute_normalisation_anatomical_gradient (*norm_sptr,*anatomical_im_grad_z_sptr,*anatomical_im_grad_y_sptr,*anatomical_im_grad_x_sptr ); + compute_normalisation_anatomical_gradient(*norm_sptr, *anatomical_im_grad_z_sptr, *anatomical_im_grad_y_sptr, + *anatomical_im_grad_x_sptr); - this->set_anatomical_grad_norm_sptr (shared_ptr >(norm_sptr)); + this->set_anatomical_grad_norm_sptr(shared_ptr>(norm_sptr)); -return Succeeded::yes; + return Succeeded::yes; } template bool -PLSPrior::post_processing() -{ - if (base_type::post_processing()==true) +PLSPrior::post_processing() { + if (base_type::post_processing() == true) return true; if (kappa_filename.size() != 0) this->set_kappa_filename(kappa_filename); if (anatomical_filename.size() != 0) - this->set_anatomical_filename(anatomical_filename); + this->set_anatomical_filename(anatomical_filename); return false; - } template -void PLSPrior::check(DiscretisedDensity<3,elemT> const& current_image_estimate) const -{ +void +PLSPrior::check(DiscretisedDensity<3, elemT> const& current_image_estimate) const { // Do base-class check base_type::check(current_image_estimate); @@ -132,557 +127,484 @@ void PLSPrior::check(DiscretisedDensity<3,elemT> const& current_image_est template void -PLSPrior::set_defaults() -{ +PLSPrior::set_defaults() { base_type::set_defaults(); this->only_2D = false; - this->alpha=1; - this->eta=1; + this->alpha = 1; + this->eta = 1; this->kappa_ptr.reset(); } template <> -const char * const -PLSPrior::registered_name = - "PLS"; +const char* const PLSPrior::registered_name = "PLS"; template -PLSPrior::PLSPrior() -{ +PLSPrior::PLSPrior() { set_defaults(); } - template -PLSPrior::PLSPrior(const bool only_2D_v, float penalisation_factor_v) - : only_2D(only_2D_v) -{ +PLSPrior::PLSPrior(const bool only_2D_v, float penalisation_factor_v) : only_2D(only_2D_v) { set_defaults(); this->penalisation_factor = penalisation_factor_v; } template -shared_ptr > -PLSPrior:: -get_anatomical_grad_sptr(int direction) const{ - - if(direction==2){ - return this->anatomical_grad_x_sptr;} - if(direction==1){ - return this->anatomical_grad_y_sptr; - } - if(direction==0){ - return this->anatomical_grad_z_sptr; - } - error(boost::format("PLSPrior::get_anatomical_grad_sptr called with out-of-range argument: %1%") % direction); - // will never get here, but this avoids a compiler warning - shared_ptr > dummy; - return dummy; +shared_ptr> +PLSPrior::get_anatomical_grad_sptr(int direction) const { + + if (direction == 2) { + return this->anatomical_grad_x_sptr; + } + if (direction == 1) { + return this->anatomical_grad_y_sptr; + } + if (direction == 0) { + return this->anatomical_grad_z_sptr; + } + error(boost::format("PLSPrior::get_anatomical_grad_sptr called with out-of-range argument: %1%") % direction); + // will never get here, but this avoids a compiler warning + shared_ptr> dummy; + return dummy; } template -shared_ptr > -PLSPrior:: -get_norm_sptr () const{ -return this->norm_sptr; +shared_ptr> +PLSPrior::get_norm_sptr() const { + return this->norm_sptr; } template -shared_ptr > -PLSPrior:: -get_anatomical_image_sptr() const{ -return this->anatomical_sptr; +shared_ptr> +PLSPrior::get_anatomical_image_sptr() const { + return this->anatomical_sptr; } template double -PLSPrior:: -get_eta() const{ -return this->eta; +PLSPrior::get_eta() const { + return this->eta; } template double -PLSPrior:: -get_alpha() const{ -return this->alpha; +PLSPrior::get_alpha() const { + return this->alpha; } template void -PLSPrior:: -set_anatomical_image_sptr (const shared_ptr >& arg) -{ - this->anatomical_sptr = arg; - this->anatomical_filename = ""; // Clear filename in case it was set. +PLSPrior::set_anatomical_image_sptr(const shared_ptr>& arg) { + this->anatomical_sptr = arg; + this->anatomical_filename = ""; // Clear filename in case it was set. } template void -PLSPrior:: -set_eta (const double arg) -{ this->eta = arg; } +PLSPrior::set_eta(const double arg) { + this->eta = arg; +} template void -PLSPrior:: -set_alpha (const double arg) -{ this->alpha = arg; } +PLSPrior::set_alpha(const double arg) { + this->alpha = arg; +} template void -PLSPrior::set_anatomical_grad_sptr(const shared_ptr >& arg, int direction){ +PLSPrior::set_anatomical_grad_sptr(const shared_ptr>& arg, int direction) { - if(direction==2){ - this->anatomical_grad_x_sptr=arg;} - if(direction==1){ - this->anatomical_grad_y_sptr=arg; - } - if(direction==0){ - this->anatomical_grad_z_sptr=arg; - } + if (direction == 2) { + this->anatomical_grad_x_sptr = arg; + } + if (direction == 1) { + this->anatomical_grad_y_sptr = arg; + } + if (direction == 0) { + this->anatomical_grad_z_sptr = arg; + } } template void -PLSPrior::set_anatomical_grad_norm_sptr (const shared_ptr >& arg){ - +PLSPrior::set_anatomical_grad_norm_sptr(const shared_ptr>& arg) { - this->norm_sptr=arg; + this->norm_sptr = arg; } - //! get current kappa image - /*! \warning As this function returns a shared_ptr, this is dangerous. You should not - modify the image by manipulating the image refered to by this pointer. - Unpredictable results will occur. - */ +//! get current kappa image +/*! \warning As this function returns a shared_ptr, this is dangerous. You should not + modify the image by manipulating the image refered to by this pointer. + Unpredictable results will occur. +*/ template -shared_ptr > -PLSPrior:: -get_kappa_sptr() const -{ return this->kappa_ptr; } +shared_ptr> +PLSPrior::get_kappa_sptr() const { + return this->kappa_ptr; +} //! set kappa image template void -PLSPrior:: -set_kappa_sptr(const shared_ptr >& k) -{ - this->kappa_ptr = k; - kappa_filename = ""; // Clear filename in case it was set. +PLSPrior::set_kappa_sptr(const shared_ptr>& k) { + this->kappa_ptr = k; + kappa_filename = ""; // Clear filename in case it was set. } //! Set kappa filename -template void PLSPrior::set_kappa_filename(const std::string& filename) -{ - kappa_filename = filename; - this->kappa_ptr = read_from_file >(kappa_filename); - info(boost::format("Reading kappa data '%1%'") % kappa_filename ); +template +void +PLSPrior::set_kappa_filename(const std::string& filename) { + kappa_filename = filename; + this->kappa_ptr = read_from_file>(kappa_filename); + info(boost::format("Reading kappa data '%1%'") % kappa_filename); } //! Set anatomical filename -template void PLSPrior::set_anatomical_filename(const std::string& filename) -{ - anatomical_filename = filename; - this->anatomical_sptr = read_from_file >(anatomical_filename); - info(boost::format("Reading anatomical data '%1%'") % anatomical_filename ); +template +void +PLSPrior::set_anatomical_filename(const std::string& filename) { + anatomical_filename = filename; + this->anatomical_sptr = read_from_file>(anatomical_filename); + info(boost::format("Reading anatomical data '%1%'") % anatomical_filename); } template -void PLSPrior::compute_image_gradient_element(DiscretisedDensity<3,elemT> & image_gradient_elem, int direction, const DiscretisedDensity<3,elemT> & image ){ -//std::cout<<"dentro ="<max_z) - continue; - image_gradient_elem[z][y][x]=image[z+1][y][x]- image[z][y][x]; - - } - if(direction==1){ - if(y+1>max_y) - continue; - image_gradient_elem[z][y][x]=image[z][y+1][x]- image[z][y][x]; -// std::cout<<"grady ="<max_x ) - continue; - image_gradient_elem[z][y][x]=image[z][y][x+1]- image[z][y][x]; - } - - } - } - } - +void +PLSPrior::compute_image_gradient_element(DiscretisedDensity<3, elemT>& image_gradient_elem, int direction, + const DiscretisedDensity<3, elemT>& image) { + // std::cout<<"dentro ="< max_z) + continue; + image_gradient_elem[z][y][x] = image[z + 1][y][x] - image[z][y][x]; + } + if (direction == 1) { + if (y + 1 > max_y) + continue; + image_gradient_elem[z][y][x] = image[z][y + 1][x] - image[z][y][x]; + // std::cout<<"grady ="< max_x) + continue; + image_gradient_elem[z][y][x] = image[z][y][x + 1] - image[z][y][x]; + } + } + } + } } template void -PLSPrior::compute_normalisation_anatomical_gradient(DiscretisedDensity<3,elemT> &norm_im_grad, - const DiscretisedDensity<3,elemT> &image_grad_z, - const DiscretisedDensity<3,elemT> &image_grad_y, - const DiscretisedDensity<3,elemT> &image_grad_x){ - - const int min_z = image_grad_x.get_min_index(); - const int max_z = image_grad_x.get_max_index(); - +PLSPrior::compute_normalisation_anatomical_gradient(DiscretisedDensity<3, elemT>& norm_im_grad, + const DiscretisedDensity<3, elemT>& image_grad_z, + const DiscretisedDensity<3, elemT>& image_grad_y, + const DiscretisedDensity<3, elemT>& image_grad_x) { - for (int z=min_z; z<=max_z; z++) - { + const int min_z = image_grad_x.get_min_index(); + const int max_z = image_grad_x.get_max_index(); - const int min_y = image_grad_x[z].get_min_index(); - const int max_y = image_grad_x[z].get_max_index(); + for (int z = min_z; z <= max_z; z++) { + const int min_y = image_grad_x[z].get_min_index(); + const int max_y = image_grad_x[z].get_max_index(); + for (int y = min_y; y <= max_y; y++) { - for (int y=min_y;y<= max_y;y++) - { + const int min_x = image_grad_x[z][y].get_min_index(); + const int max_x = image_grad_x[z][y].get_max_index(); - const int min_x = image_grad_x[z][y].get_min_index(); - const int max_x = image_grad_x[z][y].get_max_index(); - - - - for (int x=min_x;x<= max_x;x++) - { - if(only_2D){ - norm_im_grad[z][y][x] = sqrt (square(image_grad_y[z][y][x]) + square(image_grad_x[z][y][x]) + square(this->eta));} - else{ - norm_im_grad[z][y][x] = sqrt (square(image_grad_z[z][y][x]) + square(image_grad_y[z][y][x]) + - square(image_grad_x[z][y][x]) + square(this->eta)); - - } - } - - } - } + for (int x = min_x; x <= max_x; x++) { + if (only_2D) { + norm_im_grad[z][y][x] = sqrt(square(image_grad_y[z][y][x]) + square(image_grad_x[z][y][x]) + square(this->eta)); + } else { + norm_im_grad[z][y][x] = sqrt(square(image_grad_z[z][y][x]) + square(image_grad_y[z][y][x]) + + square(image_grad_x[z][y][x]) + square(this->eta)); + } + } + } + } } template void -PLSPrior:: -compute_inner_product_and_penalty(DiscretisedDensity<3,elemT> &inner_product, - DiscretisedDensity<3,elemT> &penalty, - DiscretisedDensity<3,elemT> &pet_im_grad_z, - DiscretisedDensity<3,elemT> &pet_im_grad_y, - DiscretisedDensity<3,elemT> &pet_im_grad_x, - const DiscretisedDensity<3,elemT> &pet_image){ - - - - if(!only_2D) - compute_image_gradient_element (pet_im_grad_z,0,pet_image); - - compute_image_gradient_element (pet_im_grad_y,1,pet_image); - compute_image_gradient_element (pet_im_grad_x,2,pet_image); - +PLSPrior::compute_inner_product_and_penalty(DiscretisedDensity<3, elemT>& inner_product, + DiscretisedDensity<3, elemT>& penalty, + DiscretisedDensity<3, elemT>& pet_im_grad_z, + DiscretisedDensity<3, elemT>& pet_im_grad_y, + DiscretisedDensity<3, elemT>& pet_im_grad_x, + const DiscretisedDensity<3, elemT>& pet_image) { - const int min_z = pet_image.get_min_index(); - const int max_z = pet_image.get_max_index(); + if (!only_2D) + compute_image_gradient_element(pet_im_grad_z, 0, pet_image); + compute_image_gradient_element(pet_im_grad_y, 1, pet_image); + compute_image_gradient_element(pet_im_grad_x, 2, pet_image); - for (int z=min_z; z<=max_z; z++) - { + const int min_z = pet_image.get_min_index(); + const int max_z = pet_image.get_max_index(); - const int min_y = pet_image[z].get_min_index(); - const int max_y = pet_image[z].get_max_index(); + for (int z = min_z; z <= max_z; z++) { + const int min_y = pet_image[z].get_min_index(); + const int max_y = pet_image[z].get_max_index(); + for (int y = min_y; y <= max_y; y++) { - for (int y=min_y;y<= max_y;y++) - { + const int min_x = pet_image[z][y].get_min_index(); + const int max_x = pet_image[z][y].get_max_index(); - const int min_x = pet_image[z][y].get_min_index(); - const int max_x = pet_image[z][y].get_max_index(); + for (int x = min_x; x <= max_x; x++) { + if (only_2D) { + inner_product[z][y][x] = ((pet_im_grad_y[z][y][x] * (*anatomical_grad_y_sptr)[z][y][x] / (*get_norm_sptr())[z][y][x]) + + (pet_im_grad_x[z][y][x] * (*anatomical_grad_x_sptr)[z][y][x] / (*get_norm_sptr())[z][y][x])); + penalty[z][y][x] = sqrt(square(this->alpha) + square(pet_im_grad_y[z][y][x]) + square(pet_im_grad_x[z][y][x]) - + square(inner_product[z][y][x])); + } else { + inner_product[z][y][x] = (pet_im_grad_z[z][y][x] * (*anatomical_grad_z_sptr)[z][y][x] + + pet_im_grad_y[z][y][x] * (*anatomical_grad_y_sptr)[z][y][x] + + pet_im_grad_x[z][y][x] * (*anatomical_grad_x_sptr)[z][y][x]) / + (*get_norm_sptr())[z][y][x]; - - for (int x=min_x;x<= max_x;x++) - { - if(only_2D){ - inner_product[z][y][x] = ((pet_im_grad_y[z][y][x]*(*anatomical_grad_y_sptr)[z][y][x]/(*get_norm_sptr())[z][y][x]) + - (pet_im_grad_x[z][y][x]*(*anatomical_grad_x_sptr)[z][y][x]/(*get_norm_sptr())[z][y][x])); - - penalty[z][y][x]= sqrt (square(this->alpha) + square(pet_im_grad_y[z][y][x]) + - square(pet_im_grad_x[z][y][x]) - - square(inner_product[z][y][x])); - } - else{ - inner_product[z][y][x] = (pet_im_grad_z[z][y][x]*(*anatomical_grad_z_sptr)[z][y][x] + - pet_im_grad_y[z][y][x]*(*anatomical_grad_y_sptr)[z][y][x] + - pet_im_grad_x[z][y][x]*(*anatomical_grad_x_sptr)[z][y][x])/(*get_norm_sptr())[z][y][x]; - - penalty[z][y][x]= sqrt (square(this->alpha) + square(pet_im_grad_z[z][y][x]) + - square(pet_im_grad_y[z][y][x]) + - square(pet_im_grad_x[z][y][x]) - - square(inner_product[z][y][x])); - } - } - } - } - + penalty[z][y][x] = sqrt(square(this->alpha) + square(pet_im_grad_z[z][y][x]) + square(pet_im_grad_y[z][y][x]) + + square(pet_im_grad_x[z][y][x]) - square(inner_product[z][y][x])); + } + } + } + } } template double -PLSPrior:: -compute_value(const DiscretisedDensity<3,elemT> ¤t_image_estimate) -{ - if (this->penalisation_factor==0) - { +PLSPrior::compute_value(const DiscretisedDensity<3, elemT>& current_image_estimate) { + if (this->penalisation_factor == 0) { return 0.; } this->check(current_image_estimate); - shared_ptr > pet_im_grad_z_sptr; - if(!only_2D) - pet_im_grad_z_sptr.reset(this->anatomical_sptr->get_empty_copy ()); + shared_ptr> pet_im_grad_z_sptr; + if (!only_2D) + pet_im_grad_z_sptr.reset(this->anatomical_sptr->get_empty_copy()); - shared_ptr > pet_im_grad_y_sptr(this->anatomical_sptr->get_empty_copy ()); - shared_ptr > pet_im_grad_x_sptr(this->anatomical_sptr->get_empty_copy ()); + shared_ptr> pet_im_grad_y_sptr(this->anatomical_sptr->get_empty_copy()); + shared_ptr> pet_im_grad_x_sptr(this->anatomical_sptr->get_empty_copy()); - shared_ptr > inner_product_sptr(this->anatomical_sptr.get ()->get_empty_copy ()); - shared_ptr > penalty_sptr(this->anatomical_sptr.get ()->get_empty_copy ()); + shared_ptr> inner_product_sptr(this->anatomical_sptr.get()->get_empty_copy()); + shared_ptr> penalty_sptr(this->anatomical_sptr.get()->get_empty_copy()); - compute_inner_product_and_penalty (*inner_product_sptr, - *penalty_sptr, - *pet_im_grad_z_sptr, - *pet_im_grad_y_sptr, - *pet_im_grad_x_sptr, - current_image_estimate); + compute_inner_product_and_penalty(*inner_product_sptr, *penalty_sptr, *pet_im_grad_z_sptr, *pet_im_grad_y_sptr, + *pet_im_grad_x_sptr, current_image_estimate); const bool do_kappa = !is_null_ptr(kappa_ptr); if (do_kappa && !kappa_ptr->has_same_characteristics(current_image_estimate)) error("PLSPrior: kappa image has not the same index range as the reconstructed image\n"); - double result = 0.; const int min_z = current_image_estimate.get_min_index(); const int max_z = current_image_estimate.get_max_index(); - for (int z=min_z; z<=max_z; z++) - { - - const int min_y = current_image_estimate[z].get_min_index(); - const int max_y = current_image_estimate[z].get_max_index(); + for (int z = min_z; z <= max_z; z++) { - for (int y=min_y;y<= max_y;y++) - { + const int min_y = current_image_estimate[z].get_min_index(); + const int max_y = current_image_estimate[z].get_max_index(); - const int min_x = current_image_estimate[z][y].get_min_index(); - const int max_x = current_image_estimate[z][y].get_max_index(); + for (int y = min_y; y <= max_y; y++) { - for (int x=min_x;x<= max_x;x++) - { + const int min_x = current_image_estimate[z][y].get_min_index(); + const int max_x = current_image_estimate[z][y].get_max_index(); - /* formula: - sum_x,y,z + for (int x = min_x; x <= max_x; x++) { - (penalty[z][y][x]) * (*kappa_ptr)[z][y][x]; - */ + /* formula: + sum_x,y,z - elemT current = (*penalty_sptr)[z][y][x]; + (penalty[z][y][x]) * (*kappa_ptr)[z][y][x]; + */ - if (do_kappa) - current *= - (*kappa_ptr)[z][y][x] ; + elemT current = (*penalty_sptr)[z][y][x]; - result += static_cast(current); + if (do_kappa) + current *= (*kappa_ptr)[z][y][x]; - } - } + result += static_cast(current); + } } + } return result * this->penalisation_factor; } template void -PLSPrior:: -compute_gradient(DiscretisedDensity<3,elemT>& prior_gradient, - const DiscretisedDensity<3,elemT> ¤t_image_estimate) -{ +PLSPrior::compute_gradient(DiscretisedDensity<3, elemT>& prior_gradient, + const DiscretisedDensity<3, elemT>& current_image_estimate) { this->check(current_image_estimate); - if (this->penalisation_factor==0) - { + if (this->penalisation_factor == 0) { prior_gradient.fill(0); return; } - shared_ptr > pet_im_grad_z_sptr; - shared_ptr > gradientz_sptr; + shared_ptr> pet_im_grad_z_sptr; + shared_ptr> gradientz_sptr; - if(!only_2D){ - pet_im_grad_z_sptr.reset(this->anatomical_sptr->get_empty_copy ()); - gradientz_sptr.reset(this->anatomical_sptr->get_empty_copy ()); + if (!only_2D) { + pet_im_grad_z_sptr.reset(this->anatomical_sptr->get_empty_copy()); + gradientz_sptr.reset(this->anatomical_sptr->get_empty_copy()); } - shared_ptr > pet_im_grad_y_sptr(this->anatomical_sptr->get_empty_copy ()); - shared_ptr > pet_im_grad_x_sptr(this->anatomical_sptr->get_empty_copy ()); + shared_ptr> pet_im_grad_y_sptr(this->anatomical_sptr->get_empty_copy()); + shared_ptr> pet_im_grad_x_sptr(this->anatomical_sptr->get_empty_copy()); - shared_ptr > inner_product_sptr(this->anatomical_sptr->get_empty_copy ()); - shared_ptr > penalty_sptr(this->anatomical_sptr->get_empty_copy ()); + shared_ptr> inner_product_sptr(this->anatomical_sptr->get_empty_copy()); + shared_ptr> penalty_sptr(this->anatomical_sptr->get_empty_copy()); - shared_ptr > gradienty_sptr(this->anatomical_sptr->get_empty_copy ()); - shared_ptr > gradientx_sptr(this->anatomical_sptr->get_empty_copy ()); - - compute_inner_product_and_penalty (*inner_product_sptr, - *penalty_sptr, - *pet_im_grad_z_sptr, - *pet_im_grad_y_sptr, - *pet_im_grad_x_sptr, - current_image_estimate); + shared_ptr> gradienty_sptr(this->anatomical_sptr->get_empty_copy()); + shared_ptr> gradientx_sptr(this->anatomical_sptr->get_empty_copy()); + compute_inner_product_and_penalty(*inner_product_sptr, *penalty_sptr, *pet_im_grad_z_sptr, *pet_im_grad_y_sptr, + *pet_im_grad_x_sptr, current_image_estimate); const bool do_kappa = !is_null_ptr(kappa_ptr); if (do_kappa && !kappa_ptr->has_same_characteristics(current_image_estimate)) error("PLSPrior: kappa image has not the same index range as the reconstructed image\n"); - shared_ptr > gradient_sptr(this->anatomical_sptr->get_empty_copy ()); + shared_ptr> gradient_sptr(this->anatomical_sptr->get_empty_copy()); const int min_z = current_image_estimate.get_min_index(); const int max_z = current_image_estimate.get_max_index(); - for (int z=min_z; z<=max_z; z++) - { - - const int min_y = current_image_estimate[z].get_min_index(); - const int max_y = current_image_estimate[z].get_max_index(); - - for (int y=min_y;y<= max_y;y++) - { - - - const int min_x = current_image_estimate[z][y].get_min_index(); - const int max_x = current_image_estimate[z][y].get_max_index(); - - for (int x=min_x;x<= max_x;x++) - { - - if(x+1>max_x || y+1>max_y ||(z+1>max_z && !only_2D)) - continue; - - /* formula: - sum_x,y,z - div * (pet_im_grad[z][y][x]-inner_product[z][y][x]*anatomical_im_grad[z][y][x]/(*get_norm_sptr ())[z][y][x])* - (*kappa_ptr)[z][y][x] /penalty[z][y][x]; - */ - - if(only_2D){ - (*gradientx_sptr)[z][y][x+1] = - (((*pet_im_grad_x_sptr)[z][y][x+1]-(*anatomical_grad_x_sptr)[z][y][x+1]*(*inner_product_sptr)[z][y][x+1]/ - (*get_norm_sptr ())[z][y][x+1])/(*penalty_sptr)[z][y][x+1] - - (((*pet_im_grad_x_sptr)[z][y][x]-(*anatomical_grad_x_sptr)[z][y][x]*(*inner_product_sptr)[z][y][x]/(*get_norm_sptr ())[z][y][x])/ - (*penalty_sptr)[z][y][x]) ); - - (*gradienty_sptr)[z][y+1][x] = - (((*pet_im_grad_y_sptr)[z][y+1][x]-(*anatomical_grad_y_sptr)[z][y+1][x]*(*inner_product_sptr)[z][y+1][x]/ - (*get_norm_sptr ())[z][y+1][x])/(*penalty_sptr)[z][y+1][x] - - (((*pet_im_grad_y_sptr)[z][y][x]-(*anatomical_grad_y_sptr)[z][y][x]*(*inner_product_sptr)[z][y][x]/(*get_norm_sptr ())[z][y][x])/ - (*penalty_sptr)[z][y][x]) ); - } - else{ - - (*gradientx_sptr)[z][y][x+1] = - (((*pet_im_grad_x_sptr)[z][y][x+1]-(*anatomical_grad_x_sptr)[z][y][x+1]*(*inner_product_sptr)[z][y][x+1]/(*get_norm_sptr ())[z][y][x+1])/ - (*penalty_sptr)[z][y][x+1] - - ((*pet_im_grad_x_sptr)[z][y][x]-(*anatomical_grad_x_sptr)[z][y][x]*(*inner_product_sptr)[z][y][x]/(*get_norm_sptr ())[z][y][x])/ - (*penalty_sptr)[z][y][x]); - - (*gradienty_sptr)[z][y+1][x] = - (((*pet_im_grad_y_sptr)[z][y+1][x]-(*anatomical_grad_y_sptr)[z][y+1][x]*(*inner_product_sptr)[z][y+1][x]/ - (*get_norm_sptr ())[z][y+1][x])/(*penalty_sptr)[z][y+1][x] - - (((*pet_im_grad_y_sptr)[z][y][x]-(*anatomical_grad_y_sptr)[z][y][x]*(*inner_product_sptr)[z][y][x]/ - (*get_norm_sptr ())[z][y][x])/(*penalty_sptr)[z][y][x]) ); - - (*gradientz_sptr)[z+1][y][x] = - (((*pet_im_grad_z_sptr)[z+1][y][x]-(*anatomical_grad_z_sptr)[z+1][y][x]*(*inner_product_sptr)[z+1][y][x]/ - (*get_norm_sptr ())[z+1][y][x])/(*penalty_sptr)[z+1][y][x] - - (((*pet_im_grad_z_sptr)[z][y][x]-(*anatomical_grad_z_sptr)[z][y][x]*(*inner_product_sptr)[z][y][x]/ - (*get_norm_sptr ())[z][y][x])/(*penalty_sptr)[z][y][x]) ); - } - }}} - - for (int z=min_z; z<=max_z; z++) - { - - const int min_y = current_image_estimate[z].get_min_index(); - const int max_y = current_image_estimate[z].get_max_index(); - - for (int y=min_y;y<= max_y;y++) - { - + for (int z = min_z; z <= max_z; z++) { + + const int min_y = current_image_estimate[z].get_min_index(); + const int max_y = current_image_estimate[z].get_max_index(); + + for (int y = min_y; y <= max_y; y++) { + + const int min_x = current_image_estimate[z][y].get_min_index(); + const int max_x = current_image_estimate[z][y].get_max_index(); + + for (int x = min_x; x <= max_x; x++) { + + if (x + 1 > max_x || y + 1 > max_y || (z + 1 > max_z && !only_2D)) + continue; + + /* formula: + sum_x,y,z + div * (pet_im_grad[z][y][x]-inner_product[z][y][x]*anatomical_im_grad[z][y][x]/(*get_norm_sptr ())[z][y][x])* + (*kappa_ptr)[z][y][x] /penalty[z][y][x]; + */ + + if (only_2D) { + (*gradientx_sptr)[z][y][x + 1] = + (((*pet_im_grad_x_sptr)[z][y][x + 1] - + (*anatomical_grad_x_sptr)[z][y][x + 1] * (*inner_product_sptr)[z][y][x + 1] / (*get_norm_sptr())[z][y][x + 1]) / + (*penalty_sptr)[z][y][x + 1] - + (((*pet_im_grad_x_sptr)[z][y][x] - + (*anatomical_grad_x_sptr)[z][y][x] * (*inner_product_sptr)[z][y][x] / (*get_norm_sptr())[z][y][x]) / + (*penalty_sptr)[z][y][x])); + + (*gradienty_sptr)[z][y + 1][x] = + (((*pet_im_grad_y_sptr)[z][y + 1][x] - + (*anatomical_grad_y_sptr)[z][y + 1][x] * (*inner_product_sptr)[z][y + 1][x] / (*get_norm_sptr())[z][y + 1][x]) / + (*penalty_sptr)[z][y + 1][x] - + (((*pet_im_grad_y_sptr)[z][y][x] - + (*anatomical_grad_y_sptr)[z][y][x] * (*inner_product_sptr)[z][y][x] / (*get_norm_sptr())[z][y][x]) / + (*penalty_sptr)[z][y][x])); + } else { + + (*gradientx_sptr)[z][y][x + 1] = + (((*pet_im_grad_x_sptr)[z][y][x + 1] - + (*anatomical_grad_x_sptr)[z][y][x + 1] * (*inner_product_sptr)[z][y][x + 1] / (*get_norm_sptr())[z][y][x + 1]) / + (*penalty_sptr)[z][y][x + 1] - + ((*pet_im_grad_x_sptr)[z][y][x] - + (*anatomical_grad_x_sptr)[z][y][x] * (*inner_product_sptr)[z][y][x] / (*get_norm_sptr())[z][y][x]) / + (*penalty_sptr)[z][y][x]); + + (*gradienty_sptr)[z][y + 1][x] = + (((*pet_im_grad_y_sptr)[z][y + 1][x] - + (*anatomical_grad_y_sptr)[z][y + 1][x] * (*inner_product_sptr)[z][y + 1][x] / (*get_norm_sptr())[z][y + 1][x]) / + (*penalty_sptr)[z][y + 1][x] - + (((*pet_im_grad_y_sptr)[z][y][x] - + (*anatomical_grad_y_sptr)[z][y][x] * (*inner_product_sptr)[z][y][x] / (*get_norm_sptr())[z][y][x]) / + (*penalty_sptr)[z][y][x])); + + (*gradientz_sptr)[z + 1][y][x] = + (((*pet_im_grad_z_sptr)[z + 1][y][x] - + (*anatomical_grad_z_sptr)[z + 1][y][x] * (*inner_product_sptr)[z + 1][y][x] / (*get_norm_sptr())[z + 1][y][x]) / + (*penalty_sptr)[z + 1][y][x] - + (((*pet_im_grad_z_sptr)[z][y][x] - + (*anatomical_grad_z_sptr)[z][y][x] * (*inner_product_sptr)[z][y][x] / (*get_norm_sptr())[z][y][x]) / + (*penalty_sptr)[z][y][x])); + } + } + } + } - const int min_x = current_image_estimate[z][y].get_min_index(); - const int max_x = current_image_estimate[z][y].get_max_index(); + for (int z = min_z; z <= max_z; z++) { - for (int x=min_x;x<= max_x;x++) - { - if(only_2D){ + const int min_y = current_image_estimate[z].get_min_index(); + const int max_y = current_image_estimate[z].get_max_index(); - (*gradient_sptr)[z][y][x] = -((*gradienty_sptr)[z][y][x] + (*gradientx_sptr)[z][y][x]); + for (int y = min_y; y <= max_y; y++) { - } - else{ + const int min_x = current_image_estimate[z][y].get_min_index(); + const int max_x = current_image_estimate[z][y].get_max_index(); - (*gradient_sptr)[z][y][x] = -((*gradientz_sptr)[z][y][x] + (*gradienty_sptr)[z][y][x] + (*gradientx_sptr)[z][y][x]); + for (int x = min_x; x <= max_x; x++) { + if (only_2D) { - } + (*gradient_sptr)[z][y][x] = -((*gradienty_sptr)[z][y][x] + (*gradientx_sptr)[z][y][x]); - if (do_kappa) - (*gradient_sptr)[z][y][x] *= - (*kappa_ptr)[z][y][x] ; + } else { + (*gradient_sptr)[z][y][x] = -((*gradientz_sptr)[z][y][x] + (*gradienty_sptr)[z][y][x] + (*gradientx_sptr)[z][y][x]); + } + if (do_kappa) + (*gradient_sptr)[z][y][x] *= (*kappa_ptr)[z][y][x]; - prior_gradient[z][y][x]= (*gradient_sptr)[z][y][x] * this->penalisation_factor; - }}} + prior_gradient[z][y][x] = (*gradient_sptr)[z][y][x] * this->penalisation_factor; + } + } + } info(boost::format("Prior gradient max %1%, min %2%\n") % prior_gradient.find_max() % prior_gradient.find_min()); static int count = 0; ++count; - if (gradient_filename_prefix.size()>0) - { - char *filename = new char[gradient_filename_prefix.size()+100]; - sprintf(filename, "%s%d.v", gradient_filename_prefix.c_str(), count); - write_to_file(filename, prior_gradient); - delete[] filename; - } + if (gradient_filename_prefix.size() > 0) { + char* filename = new char[gradient_filename_prefix.size() + 100]; + sprintf(filename, "%s%d.v", gradient_filename_prefix.c_str(), count); + write_to_file(filename, prior_gradient); + delete[] filename; + } } - -# ifdef _MSC_VER +#ifdef _MSC_VER // prevent warning message on reinstantiation, // note that we get a linking error if we don't have the explicit instantiation below -# pragma warning(disable:4660) -# endif - +# pragma warning(disable : 4660) +#endif template class PLSPrior; END_NAMESPACE_STIR - diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData.cxx index 13c816e6a9..9a3fe280f8 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData.cxx @@ -30,12 +30,11 @@ START_NAMESPACE_STIR +#ifdef _MSC_VER +// prevent warning message on instantiation of abstract class +# pragma warning(disable : 4661) +#endif // _MSC_VER -# ifdef _MSC_VER -// prevent warning message on instantiation of abstract class -# pragma warning(disable:4661) -# endif // _MSC_VER - -template class PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData; +template class PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData; END_NAMESPACE_STIR diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMean.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMean.cxx index 22b6c184f4..3b74ca9184 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMean.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMean.cxx @@ -42,472 +42,349 @@ using std::string; START_NAMESPACE_STIR -template +template void -PoissonLogLikelihoodWithLinearModelForMean:: -set_defaults() -{ +PoissonLogLikelihoodWithLinearModelForMean::set_defaults() { base_type::set_defaults(); - this->sensitivity_filename = ""; - this->subsensitivity_filenames = ""; + this->sensitivity_filename = ""; + this->subsensitivity_filenames = ""; this->recompute_sensitivity = false; this->use_subset_sensitivities = true; this->subsensitivity_sptrs.resize(0); } -template +template void -PoissonLogLikelihoodWithLinearModelForMean:: -initialise_keymap() -{ +PoissonLogLikelihoodWithLinearModelForMean::initialise_keymap() { base_type::initialise_keymap(); this->parser.add_key("sensitivity filename", &this->sensitivity_filename); this->parser.add_key("subset sensitivity filenames", &this->subsensitivity_filenames); this->parser.add_key("recompute sensitivity", &this->recompute_sensitivity); this->parser.add_key("use_subset_sensitivities", &this->use_subset_sensitivities); - } -template +template bool -PoissonLogLikelihoodWithLinearModelForMean:: -post_processing() -{ +PoissonLogLikelihoodWithLinearModelForMean::post_processing() { if (base_type::post_processing() == true) return true; return false; } -template +template std::string -PoissonLogLikelihoodWithLinearModelForMean:: -get_sensitivity_filename() const -{ +PoissonLogLikelihoodWithLinearModelForMean::get_sensitivity_filename() const { return this->sensitivity_filename; } -template +template std::string -PoissonLogLikelihoodWithLinearModelForMean:: -get_subsensitivity_filenames() const -{ +PoissonLogLikelihoodWithLinearModelForMean::get_subsensitivity_filenames() const { return this->subsensitivity_filenames; } -template +template void -PoissonLogLikelihoodWithLinearModelForMean:: -set_sensitivity_filename(const std::string& filename) -{ +PoissonLogLikelihoodWithLinearModelForMean::set_sensitivity_filename(const std::string& filename) { this->sensitivity_filename = filename; } -template +template void -PoissonLogLikelihoodWithLinearModelForMean:: -set_subsensitivity_filenames(const std::string& filenames) -{ +PoissonLogLikelihoodWithLinearModelForMean::set_subsensitivity_filenames(const std::string& filenames) { this->subsensitivity_filenames = filenames; - try - { - const std::string test_sensitivity_filename = - boost::str(boost::format(this->subsensitivity_filenames) % 0); - } - catch (std::exception& e) - { - error("argument %s to set_subsensitivity_filenames is invalid (see boost::format documentation)\n. Error message: %s", filenames.c_str(), e.what()); - } - + try { + const std::string test_sensitivity_filename = boost::str(boost::format(this->subsensitivity_filenames) % 0); + } catch (std::exception& e) { + error("argument %s to set_subsensitivity_filenames is invalid (see boost::format documentation)\n. Error message: %s", + filenames.c_str(), e.what()); + } } - -template -shared_ptr -PoissonLogLikelihoodWithLinearModelForMean:: -get_subset_sensitivity_sptr(const int subset_num) const -{ +template +shared_ptr +PoissonLogLikelihoodWithLinearModelForMean::get_subset_sensitivity_sptr(const int subset_num) const { return this->subsensitivity_sptrs[subset_num]; } -template +template const TargetT& -PoissonLogLikelihoodWithLinearModelForMean:: -get_subset_sensitivity(const int subset_num) const -{ +PoissonLogLikelihoodWithLinearModelForMean::get_subset_sensitivity(const int subset_num) const { return *get_subset_sensitivity_sptr(subset_num); } -template +template const TargetT& -PoissonLogLikelihoodWithLinearModelForMean:: -get_sensitivity() const -{ +PoissonLogLikelihoodWithLinearModelForMean::get_sensitivity() const { return *this->sensitivity_sptr; } -template +template bool -PoissonLogLikelihoodWithLinearModelForMean:: -get_recompute_sensitivity() const -{ +PoissonLogLikelihoodWithLinearModelForMean::get_recompute_sensitivity() const { return this->recompute_sensitivity; } -template +template void -PoissonLogLikelihoodWithLinearModelForMean:: -set_recompute_sensitivity(const bool arg) -{ +PoissonLogLikelihoodWithLinearModelForMean::set_recompute_sensitivity(const bool arg) { this->recompute_sensitivity = arg; - } -template +template bool -PoissonLogLikelihoodWithLinearModelForMean:: -get_use_subset_sensitivities() const -{ +PoissonLogLikelihoodWithLinearModelForMean::get_use_subset_sensitivities() const { return this->use_subset_sensitivities; } -template +template void -PoissonLogLikelihoodWithLinearModelForMean:: -set_use_subset_sensitivities(const bool arg) -{ +PoissonLogLikelihoodWithLinearModelForMean::set_use_subset_sensitivities(const bool arg) { this->use_subset_sensitivities = arg; } -template +template void -PoissonLogLikelihoodWithLinearModelForMean:: -set_subset_sensitivity_sptr(const shared_ptr& arg, const int subset_num) -{ +PoissonLogLikelihoodWithLinearModelForMean::set_subset_sensitivity_sptr(const shared_ptr& arg, + const int subset_num) { this->subsensitivity_sptrs[subset_num] = arg; } -template -Succeeded -PoissonLogLikelihoodWithLinearModelForMean:: -set_up(shared_ptr const& target_sptr) -{ +template +Succeeded +PoissonLogLikelihoodWithLinearModelForMean::set_up(shared_ptr const& target_sptr) { if (base_type::set_up(target_sptr) != Succeeded::yes) return Succeeded::no; this->subsensitivity_sptrs.resize(this->num_subsets); - if(!this->recompute_sensitivity) - { - if(is_null_ptr(this->subsensitivity_sptrs[0]) && - ((this->get_use_subset_sensitivities() && this->subsensitivity_filenames=="") || - (!this->get_use_subset_sensitivities() && this->sensitivity_filename==""))) - { - info("(subset)sensitivity filename(s) not set so I will compute the (subset)sensitivities", 2); - this->recompute_sensitivity = true; - // initialisation of pointers will be done below - } - else if(this->sensitivity_filename=="1") - { - if (this->get_use_subset_sensitivities()) - { - error("PoissonLogLikelihoodWithLinearModelForMean limitation:\n" - "currently cannot use subset_sensitivities if sensitivity is forced to 1"); + if (!this->recompute_sensitivity) { + if (is_null_ptr(this->subsensitivity_sptrs[0]) && + ((this->get_use_subset_sensitivities() && this->subsensitivity_filenames == "") || + (!this->get_use_subset_sensitivities() && this->sensitivity_filename == ""))) { + info("(subset)sensitivity filename(s) not set so I will compute the (subset)sensitivities", 2); + this->recompute_sensitivity = true; + // initialisation of pointers will be done below + } else if (this->sensitivity_filename == "1") { + if (this->get_use_subset_sensitivities()) { + error("PoissonLogLikelihoodWithLinearModelForMean limitation:\n" + "currently cannot use subset_sensitivities if sensitivity is forced to 1"); + return Succeeded::no; + } + this->sensitivity_sptr.reset(target_sptr->get_empty_copy()); + std::fill(this->sensitivity_sptr->begin_all(), this->sensitivity_sptr->end_all(), 1); + } else { + // read from file + try { + if (this->get_use_subset_sensitivities()) { + if (this->subsensitivity_filenames.empty()) { + error("'subset sensitivity filenames' is empty. You need to set this before using it."); + return Succeeded::no; + } + // read subsensitivies + for (int subset = 0; subset < this->get_num_subsets(); ++subset) { + std::string current_sensitivity_filename; + try { + current_sensitivity_filename = boost::str(boost::format(this->subsensitivity_filenames) % subset); + } catch (std::exception& e) { + error(boost::format("Error using 'subset sensitivity filenames' pattern (which is set to '%1%'). " + "Check syntax for boost::format. Error is:\n%2%") % + this->subsensitivity_filenames % e.what()); return Succeeded::no; } - this->sensitivity_sptr.reset(target_sptr->get_empty_copy()); - std::fill(this->sensitivity_sptr->begin_all(), this->sensitivity_sptr->end_all(), 1); - } - else - { - // read from file - try - { - if (this->get_use_subset_sensitivities()) - { - if (this->subsensitivity_filenames.empty()) - { - error("'subset sensitivity filenames' is empty. You need to set this before using it."); - return Succeeded::no; - } - // read subsensitivies - for (int subset=0; subsetget_num_subsets(); ++subset) - { - std::string current_sensitivity_filename; - try - { - current_sensitivity_filename = - boost::str(boost::format(this->subsensitivity_filenames) % subset); - } - catch (std::exception& e) - { - error(boost::format("Error using 'subset sensitivity filenames' pattern (which is set to '%1%'). " - "Check syntax for boost::format. Error is:\n%2%") % - this->subsensitivity_filenames % e.what()); - return Succeeded::no; - } - info(boost::format("Reading sensitivity from '%1%'") % current_sensitivity_filename); - - this->subsensitivity_sptrs[subset] = - read_from_file(current_sensitivity_filename); - string explanation; - if (!target_sptr->has_same_characteristics(*this->subsensitivity_sptrs[subset], - explanation)) - { - error("sensitivity and target should have the same characteristics.\n%s", - explanation.c_str()); - return Succeeded::no; - } - } - } - else - { - if (this->sensitivity_filename.empty()) - { - error("'sensitivity filename' is empty. You need to set this before using it."); - return Succeeded::no; - } - // reading single sensitivity - const std::string current_sensitivity_filename = - this->sensitivity_filename; - info(boost::format("Reading sensitivity from '%1%'") % current_sensitivity_filename); - - this->sensitivity_sptr = read_from_file(current_sensitivity_filename); - string explanation; - if (!target_sptr->has_same_characteristics(*this->sensitivity_sptr, - explanation)) - { - error("sensitivity and target should have the same characteristics.\n%s", - explanation.c_str()); - return Succeeded::no; - } - } - } - catch (std::exception& e) - { - error("Error reading sensitivity from file:\n%s", e.what()); + info(boost::format("Reading sensitivity from '%1%'") % current_sensitivity_filename); + + this->subsensitivity_sptrs[subset] = read_from_file(current_sensitivity_filename); + string explanation; + if (!target_sptr->has_same_characteristics(*this->subsensitivity_sptrs[subset], explanation)) { + error("sensitivity and target should have the same characteristics.\n%s", explanation.c_str()); return Succeeded::no; } - // compute total from subsensitivity or vice versa - this->set_total_or_subset_sensitivities(); + } + } else { + if (this->sensitivity_filename.empty()) { + error("'sensitivity filename' is empty. You need to set this before using it."); + return Succeeded::no; + } + // reading single sensitivity + const std::string current_sensitivity_filename = this->sensitivity_filename; + info(boost::format("Reading sensitivity from '%1%'") % current_sensitivity_filename); + + this->sensitivity_sptr = read_from_file(current_sensitivity_filename); + string explanation; + if (!target_sptr->has_same_characteristics(*this->sensitivity_sptr, explanation)) { + error("sensitivity and target should have the same characteristics.\n%s", explanation.c_str()); + return Succeeded::no; + } } - } // end of !recompute_sensitivity case - - if (this->set_up_before_sensitivity(target_sptr) == Succeeded::no) - { - return Succeeded::no; + } catch (std::exception& e) { + error("Error reading sensitivity from file:\n%s", e.what()); + return Succeeded::no; + } + // compute total from subsensitivity or vice versa + this->set_total_or_subset_sensitivities(); } + } // end of !recompute_sensitivity case - if(!this->subsets_are_approximately_balanced() && !this->get_use_subset_sensitivities()) - { - error("Number of subsets %d is such that subsets will be very unbalanced.\n" - "You need to set 'use_subset_sensitivities' to true to handle this.", - this->num_subsets); - return Succeeded::no; - } + if (this->set_up_before_sensitivity(target_sptr) == Succeeded::no) { + return Succeeded::no; + } - if(this->recompute_sensitivity) - { - info("Computing sensitivity"); - CPUTimer sens_timer; - sens_timer.start(); - // preallocate one such that compute_sensitivities knows the size - this->subsensitivity_sptrs[0].reset(target_sptr->get_empty_copy()); - this->compute_sensitivities(); - sens_timer.stop(); - info("Done computing sensitivity"); - info("This took " + boost::lexical_cast(sens_timer.value()) + " seconds CPU time."); - - // write to file - try - { - if (this->get_use_subset_sensitivities()) - { - if (this->subsensitivity_filenames.size()!=0) - { - for (int subset=0; subsetget_num_subsets(); ++subset) - { - const std::string current_sensitivity_filename = - boost::str(boost::format(this->subsensitivity_filenames) % subset); - info(boost::format("Writing sensitivity to '%1%'") % current_sensitivity_filename); - write_to_file(current_sensitivity_filename, - this->get_subset_sensitivity(subset)); - } - } - } - else - { - if (this->sensitivity_filename.size()!=0) - { - const std::string current_sensitivity_filename = - this->sensitivity_filename; - info(boost::format("Writing sensitivity to '%1%'") % current_sensitivity_filename); - write_to_file(current_sensitivity_filename, - this->get_sensitivity()); - } - } + if (!this->subsets_are_approximately_balanced() && !this->get_use_subset_sensitivities()) { + error("Number of subsets %d is such that subsets will be very unbalanced.\n" + "You need to set 'use_subset_sensitivities' to true to handle this.", + this->num_subsets); + return Succeeded::no; + } + + if (this->recompute_sensitivity) { + info("Computing sensitivity"); + CPUTimer sens_timer; + sens_timer.start(); + // preallocate one such that compute_sensitivities knows the size + this->subsensitivity_sptrs[0].reset(target_sptr->get_empty_copy()); + this->compute_sensitivities(); + sens_timer.stop(); + info("Done computing sensitivity"); + info("This took " + boost::lexical_cast(sens_timer.value()) + " seconds CPU time."); + + // write to file + try { + if (this->get_use_subset_sensitivities()) { + if (this->subsensitivity_filenames.size() != 0) { + for (int subset = 0; subset < this->get_num_subsets(); ++subset) { + const std::string current_sensitivity_filename = boost::str(boost::format(this->subsensitivity_filenames) % subset); + info(boost::format("Writing sensitivity to '%1%'") % current_sensitivity_filename); + write_to_file(current_sensitivity_filename, this->get_subset_sensitivity(subset)); + } } - catch (std::exception& e) - { - error("Error writing sensitivity to file:\n%s", e.what()); - return Succeeded::no; + } else { + if (this->sensitivity_filename.size() != 0) { + const std::string current_sensitivity_filename = this->sensitivity_filename; + info(boost::format("Writing sensitivity to '%1%'") % current_sensitivity_filename); + write_to_file(current_sensitivity_filename, this->get_sensitivity()); } + } + } catch (std::exception& e) { + error("Error writing sensitivity to file:\n%s", e.what()); + return Succeeded::no; } - + } + return Succeeded::yes; } -template +template void -PoissonLogLikelihoodWithLinearModelForMean:: -compute_sub_gradient_without_penalty(TargetT& gradient, - const TargetT ¤t_estimate, - const int subset_num) -{ - if (subset_num<0 || subset_num>=this->get_num_subsets()) +PoissonLogLikelihoodWithLinearModelForMean::compute_sub_gradient_without_penalty(TargetT& gradient, + const TargetT& current_estimate, + const int subset_num) { + if (subset_num < 0 || subset_num >= this->get_num_subsets()) error("compute_sub_gradient_without_penalty subset_num out-of-range error"); - this-> - compute_sub_gradient_without_penalty_plus_sensitivity(gradient, - current_estimate, - subset_num); + this->compute_sub_gradient_without_penalty_plus_sensitivity(gradient, current_estimate, subset_num); // compute gradient -= sub_sensitivity { - typename TargetT::full_iterator gradient_iter = - gradient.begin_all(); - const typename TargetT::full_iterator gradient_end = - gradient.end_all(); - typename TargetT::const_full_iterator sensitivity_iter = - this->get_subset_sensitivity(subset_num).begin_all_const(); - while (gradient_iter != gradient_end) - { - *gradient_iter -= (*sensitivity_iter); - ++gradient_iter; ++sensitivity_iter; - } + typename TargetT::full_iterator gradient_iter = gradient.begin_all(); + const typename TargetT::full_iterator gradient_end = gradient.end_all(); + typename TargetT::const_full_iterator sensitivity_iter = this->get_subset_sensitivity(subset_num).begin_all_const(); + while (gradient_iter != gradient_end) { + *gradient_iter -= (*sensitivity_iter); + ++gradient_iter; + ++sensitivity_iter; + } } } - -template +template void -PoissonLogLikelihoodWithLinearModelForMean:: -compute_sensitivities() -{ +PoissonLogLikelihoodWithLinearModelForMean::compute_sensitivities() { // check subset balancing - if (this->use_subset_sensitivities == false) - { + if (this->use_subset_sensitivities == false) { std::string warning_message = "PoissonLogLikelihoodWithLinearModelForMean:\n"; - if (!this->subsets_are_approximately_balanced(warning_message)) - { - error("%s\n . you need to set use_subset_sensitivities to true", - warning_message.c_str()); - } + if (!this->subsets_are_approximately_balanced(warning_message)) { + error("%s\n . you need to set use_subset_sensitivities to true", warning_message.c_str()); + } } // end check balancing // compute subset sensitivities - for (int subset_num=0; subset_numnum_subsets; ++subset_num) - { - if (subset_num == 0) - { - std::fill(this->subsensitivity_sptrs[subset_num]->begin_all(), - this->subsensitivity_sptrs[subset_num]->end_all(), - 0); - } - else - { - if (this->get_use_subset_sensitivities()) - { - this->subsensitivity_sptrs[subset_num].reset(this->subsensitivity_sptrs[0]->get_empty_copy()); - } - else - { - // copy subsensitivity[0] pointer to current subset. - // do this as pointer such that we don't use more memory. - // This also means that we just accumulate in subsensitivity[0] for the moment. - // we will correct that below. - this->subsensitivity_sptrs[subset_num] = this->subsensitivity_sptrs[0]; - } - } - this->add_subset_sensitivity(*this->get_subset_sensitivity_sptr(subset_num), subset_num); - } - if (!this->get_use_subset_sensitivities()) - { - // copy full sensitivity (currently stored in subsensitivity[0]) - this->sensitivity_sptr = this->subsensitivity_sptrs[0]; - this->subsensitivity_sptrs[0].reset(); + for (int subset_num = 0; subset_num < this->num_subsets; ++subset_num) { + if (subset_num == 0) { + std::fill(this->subsensitivity_sptrs[subset_num]->begin_all(), this->subsensitivity_sptrs[subset_num]->end_all(), 0); + } else { + if (this->get_use_subset_sensitivities()) { + this->subsensitivity_sptrs[subset_num].reset(this->subsensitivity_sptrs[0]->get_empty_copy()); + } else { + // copy subsensitivity[0] pointer to current subset. + // do this as pointer such that we don't use more memory. + // This also means that we just accumulate in subsensitivity[0] for the moment. + // we will correct that below. + this->subsensitivity_sptrs[subset_num] = this->subsensitivity_sptrs[0]; + } } + this->add_subset_sensitivity(*this->get_subset_sensitivity_sptr(subset_num), subset_num); + } + if (!this->get_use_subset_sensitivities()) { + // copy full sensitivity (currently stored in subsensitivity[0]) + this->sensitivity_sptr = this->subsensitivity_sptrs[0]; + this->subsensitivity_sptrs[0].reset(); + } // compute total from subsensitivity or vice versa this->set_total_or_subset_sensitivities(); } -template +template void -PoissonLogLikelihoodWithLinearModelForMean:: -set_total_or_subset_sensitivities() -{ - if (this->get_use_subset_sensitivities()) - { - // add subset sensitivities, just in case we need the total somewhere - this->sensitivity_sptr.reset(this->subsensitivity_sptrs[0]->clone()); - for (int subset_num=1; subset_numnum_subsets; ++subset_num) - { - typename TargetT::full_iterator sens_iter = this->sensitivity_sptr->begin_all(); - typename TargetT::full_iterator subsens_iter = this->subsensitivity_sptrs[subset_num]->begin_all(); - while (sens_iter != this->sensitivity_sptr->end_all()) - { - *sens_iter += *subsens_iter; - ++sens_iter; ++ subsens_iter; - } - } +PoissonLogLikelihoodWithLinearModelForMean::set_total_or_subset_sensitivities() { + if (this->get_use_subset_sensitivities()) { + // add subset sensitivities, just in case we need the total somewhere + this->sensitivity_sptr.reset(this->subsensitivity_sptrs[0]->clone()); + for (int subset_num = 1; subset_num < this->num_subsets; ++subset_num) { + typename TargetT::full_iterator sens_iter = this->sensitivity_sptr->begin_all(); + typename TargetT::full_iterator subsens_iter = this->subsensitivity_sptrs[subset_num]->begin_all(); + while (sens_iter != this->sensitivity_sptr->end_all()) { + *sens_iter += *subsens_iter; + ++sens_iter; + ++subsens_iter; + } } - else - { - // copy full sensitivity - this->subsensitivity_sptrs[0].reset(this->sensitivity_sptr->clone()); - // divide subsensitivity[0] by num_subsets - for (typename TargetT::full_iterator subsens_iter = this->subsensitivity_sptrs[0]->begin_all(); - subsens_iter != this->subsensitivity_sptrs[0]->end_all(); - ++subsens_iter) - { - *subsens_iter /= this->num_subsets; - } - // set all other pointers the same - for (int subset_num=1; subset_numnum_subsets; ++subset_num) - this->subsensitivity_sptrs[subset_num] = this->subsensitivity_sptrs[0]; + } else { + // copy full sensitivity + this->subsensitivity_sptrs[0].reset(this->sensitivity_sptr->clone()); + // divide subsensitivity[0] by num_subsets + for (typename TargetT::full_iterator subsens_iter = this->subsensitivity_sptrs[0]->begin_all(); + subsens_iter != this->subsensitivity_sptrs[0]->end_all(); ++subsens_iter) { + *subsens_iter /= this->num_subsets; } - + // set all other pointers the same + for (int subset_num = 1; subset_num < this->num_subsets; ++subset_num) + this->subsensitivity_sptrs[subset_num] = this->subsensitivity_sptrs[0]; + } } - -template +template void -PoissonLogLikelihoodWithLinearModelForMean:: -fill_nonidentifiable_target_parameters(TargetT& target, const float value) const -{ +PoissonLogLikelihoodWithLinearModelForMean::fill_nonidentifiable_target_parameters(TargetT& target, + const float value) const { typename TargetT::full_iterator target_iter = target.begin_all(); typename TargetT::full_iterator target_end_iter = target.end_all(); - typename TargetT::const_full_iterator sens_iter = - this->get_sensitivity().begin_all_const(); - - for (; - target_iter != target_end_iter; - ++target_iter, ++sens_iter) - { - if (*sens_iter == 0) - *target_iter = value; - } + typename TargetT::const_full_iterator sens_iter = this->get_sensitivity().begin_all_const(); + + for (; target_iter != target_end_iter; ++target_iter, ++sens_iter) { + if (*sens_iter == 0) + *target_iter = value; + } } -# ifdef _MSC_VER -// prevent warning message on instantiation of abstract class -# pragma warning(disable:4661) -# endif +#ifdef _MSC_VER +// prevent warning message on instantiation of abstract class +# pragma warning(disable : 4661) +#endif -template class PoissonLogLikelihoodWithLinearModelForMean >; -template class PoissonLogLikelihoodWithLinearModelForMean; +template class PoissonLogLikelihoodWithLinearModelForMean>; +template class PoissonLogLikelihoodWithLinearModelForMean; END_NAMESPACE_STIR - - diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion.cxx index 290f5b76e4..435e4cea96 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion.cxx @@ -1,19 +1,19 @@ /* Copyright (C) 2009- 2013, King's College London This file is part of STIR. - + This file is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.3 of the License, or (at your option) any later version. - + This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - + See STIR/LICENSE.txt for details - */ + */ /*! \file \ingroup GeneralisedObjectiveFunction @@ -26,11 +26,10 @@ START_NAMESPACE_STIR -# ifdef _MSC_VER -// prevent warning message on instantiation of abstract class -# pragma warning(disable:4661) -# endif // _MSC_VER -template class -PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion >; +#ifdef _MSC_VER +// prevent warning message on instantiation of abstract class +# pragma warning(disable : 4661) +#endif // _MSC_VER +template class PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion>; END_NAMESPACE_STIR diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeData.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeData.cxx index 0dbab24c41..4ee0e9af0d 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeData.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeData.cxx @@ -1,6 +1,6 @@ // -// -/* +// +/* Copyright (C) 2003- 2011, Hammersmith Imanet Ltd Copyright (C) 2018, University College London This file is part of STIR. @@ -14,22 +14,22 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - See STIR/LICENSE.txt for details -*/ -/*! + See STIR/LICENSE.txt for details +*/ +/*! \file - \ingroup GeneralisedObjectiveFunction + \ingroup GeneralisedObjectiveFunction \brief Declaration of class - stir::PoissonLogLikelihoodWithLinearModelForMeanAndListModeData - - \author Kris Thielemans - \author Sanida Mustafovic - -*/ - -#include "stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeData.h" -#include "stir/VoxelsOnCartesianGrid.h" -#include "stir/Succeeded.h" + stir::PoissonLogLikelihoodWithLinearModelForMeanAndListModeData + + \author Kris Thielemans + \author Sanida Mustafovic + +*/ + +#include "stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeData.h" +#include "stir/VoxelsOnCartesianGrid.h" +#include "stir/Succeeded.h" #include "stir/IO/read_from_file.h" using std::vector; @@ -37,131 +37,108 @@ using std::pair; START_NAMESPACE_STIR - -template -PoissonLogLikelihoodWithLinearModelForMeanAndListModeData:: -PoissonLogLikelihoodWithLinearModelForMeanAndListModeData() -{ - this->set_defaults(); -} +template +PoissonLogLikelihoodWithLinearModelForMeanAndListModeData::PoissonLogLikelihoodWithLinearModelForMeanAndListModeData() { + this->set_defaults(); +} -template +template void -PoissonLogLikelihoodWithLinearModelForMeanAndListModeData:: -set_defaults() -{ - base_type::set_defaults(); - this->list_mode_filename =""; - this->frame_defs_filename =""; - this->list_mode_data_sptr.reset(); +PoissonLogLikelihoodWithLinearModelForMeanAndListModeData::set_defaults() { + base_type::set_defaults(); + this->list_mode_filename = ""; + this->frame_defs_filename = ""; + this->list_mode_data_sptr.reset(); this->current_frame_num = 1; this->num_events_to_use = 0L; - + this->target_parameter_parser.set_defaults(); - -} - -template -void -PoissonLogLikelihoodWithLinearModelForMeanAndListModeData:: -initialise_keymap() -{ - base_type::initialise_keymap(); - this->parser.add_key("list mode filename", &this->list_mode_filename); +} + +template +void +PoissonLogLikelihoodWithLinearModelForMeanAndListModeData::initialise_keymap() { + base_type::initialise_keymap(); + this->parser.add_key("list mode filename", &this->list_mode_filename); this->target_parameter_parser.add_to_keymap(this->parser); this->parser.add_key("time frame definition filename", &this->frame_defs_filename); // SM TODO -- later do not parse this->parser.add_key("time frame number", &this->current_frame_num); - this->parser.add_parsing_key("Bin Normalisation type", &this->normalisation_sptr); -} + this->parser.add_parsing_key("Bin Normalisation type", &this->normalisation_sptr); +} -template -bool -PoissonLogLikelihoodWithLinearModelForMeanAndListModeData::post_processing() -{ - if (base_type::post_processing() == true) - return true; +template +bool +PoissonLogLikelihoodWithLinearModelForMeanAndListModeData::post_processing() { + if (base_type::post_processing() == true) + return true; - if (this->list_mode_filename.length() == 0) - { warning("You need to specify an input file\n"); return true; } + if (this->list_mode_filename.length() == 0) { + warning("You need to specify an input file\n"); + return true; + } - this->list_mode_data_sptr= - read_from_file(this->list_mode_filename); + this->list_mode_data_sptr = read_from_file(this->list_mode_filename); - if (this->frame_defs_filename.size()!=0) + if (this->frame_defs_filename.size() != 0) this->frame_defs = TimeFrameDefinitions(this->frame_defs_filename); - else - { - // make a single frame starting from 0. End value will be ignored. - vector > frame_times(1, pair(0,0)); - this->frame_defs = TimeFrameDefinitions(frame_times); - } + else { + // make a single frame starting from 0. End value will be ignored. + vector> frame_times(1, pair(0, 0)); + this->frame_defs = TimeFrameDefinitions(frame_times); + } target_parameter_parser.check_values(); return false; -} +} template void -PoissonLogLikelihoodWithLinearModelForMeanAndListModeData:: -set_input_data(const shared_ptr & arg) -{ - this->list_mode_data_sptr = dynamic_pointer_cast(arg); +PoissonLogLikelihoodWithLinearModelForMeanAndListModeData::set_input_data(const shared_ptr& arg) { + this->list_mode_data_sptr = dynamic_pointer_cast(arg); } template const ListModeData& -PoissonLogLikelihoodWithLinearModelForMeanAndListModeData:: -get_input_data() const -{ +PoissonLogLikelihoodWithLinearModelForMeanAndListModeData::get_input_data() const { return *this->list_mode_data_sptr; } -template +template void -PoissonLogLikelihoodWithLinearModelForMeanAndListModeData:: -set_additive_proj_data_sptr(const shared_ptr &arg) -{ - this->additive_proj_data_sptr = dynamic_pointer_cast(arg); +PoissonLogLikelihoodWithLinearModelForMeanAndListModeData::set_additive_proj_data_sptr(const shared_ptr& arg) { + this->additive_proj_data_sptr = dynamic_pointer_cast(arg); } -template +template void -PoissonLogLikelihoodWithLinearModelForMeanAndListModeData:: -set_normalisation_sptr(const shared_ptr& arg) -{ +PoissonLogLikelihoodWithLinearModelForMeanAndListModeData::set_normalisation_sptr( + const shared_ptr& arg) { this->normalisation_sptr = arg; } -template +template void -PoissonLogLikelihoodWithLinearModelForMeanAndListModeData:: -start_new_time_frame(const unsigned int) -{} +PoissonLogLikelihoodWithLinearModelForMeanAndListModeData::start_new_time_frame(const unsigned int) {} -template +template Succeeded -PoissonLogLikelihoodWithLinearModelForMeanAndListModeData:: -set_up(shared_ptr const& target_sptr) -{ - if ( base_type::set_up(target_sptr) != Succeeded::yes) +PoissonLogLikelihoodWithLinearModelForMeanAndListModeData::set_up(shared_ptr const& target_sptr) { + if (base_type::set_up(target_sptr) != Succeeded::yes) return Succeeded::no; // handle time frame definitions etc - if(this->num_events_to_use==0 && this->frame_defs_filename.size() == 0) - do_time_frame = true; - - return Succeeded::yes; -} + if (this->num_events_to_use == 0 && this->frame_defs_filename.size() == 0) + do_time_frame = true; -# ifdef _MSC_VER -// prevent warning message on instantiation of abstract class -# pragma warning(disable:4661) -# endif - -template class -PoissonLogLikelihoodWithLinearModelForMeanAndListModeData >; + return Succeeded::yes; +} +#ifdef _MSC_VER +// prevent warning message on instantiation of abstract class +# pragma warning(disable : 4661) +#endif +template class PoissonLogLikelihoodWithLinearModelForMeanAndListModeData>; END_NAMESPACE_STIR diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx index d2924859f5..bcd5a26e9c 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.cxx @@ -18,7 +18,7 @@ /*! \file \ingroup GeneralisedObjectiveFunction - \brief Implementation of class + \brief Implementation of class stir::PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin \author Nikos Efthimiou @@ -26,8 +26,8 @@ \author Sanida Mustafovic */ -#include "stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h" -#include "stir/recon_buildblock/ProjMatrixByBinUsingRayTracing.h" +#include "stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin.h" +#include "stir/recon_buildblock/ProjMatrixByBinUsingRayTracing.h" #include "stir/recon_buildblock/ProjMatrixElemsForOneBin.h" #include "stir/recon_buildblock/ProjectorByBinPairUsingProjMatrixByBin.h" #include "stir/LORCoordinates.h" @@ -58,208 +58,165 @@ #include "stir/recon_buildblock/PresmoothingForwardProjectorByBin.h" #include "stir/recon_buildblock/PostsmoothingBackProjectorByBin.h" #ifdef STIR_MPI -#include "stir/recon_buildblock/distributed_functions.h" +# include "stir/recon_buildblock/distributed_functions.h" #endif - #include START_NAMESPACE_STIR -template -const char * const -PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin:: -registered_name = -"PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin"; - -template -PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin:: -PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin() -{ - this->set_defaults(); -} - -template -void -PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin:: -set_defaults() -{ +template +const char* const PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin::registered_name = + "PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin"; + +template +PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin< + TargetT>::PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin() { + this->set_defaults(); +} + +template +void +PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin::set_defaults() { base_type::set_defaults(); this->additive_proj_data_sptr.reset(); - this->additive_projection_data_filename ="0"; - this->max_ring_difference_num_to_process =-1; - this->PM_sptr.reset(new ProjMatrixByBinUsingRayTracing()); + this->additive_projection_data_filename = "0"; + this->max_ring_difference_num_to_process = -1; + this->PM_sptr.reset(new ProjMatrixByBinUsingRayTracing()); this->normalisation_sptr.reset(new TrivialBinNormalisation); this->do_time_frame = false; this->use_tofsens = false; -} - -template -void -PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin:: -initialise_keymap() -{ - base_type::initialise_keymap(); - this->parser.add_start_key("PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin Parameters"); - this->parser.add_stop_key("End PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin Parameters"); +} + +template +void +PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin::initialise_keymap() { + base_type::initialise_keymap(); + this->parser.add_start_key("PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin Parameters"); + this->parser.add_stop_key("End PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin Parameters"); this->parser.add_key("use time-of-flight sensitivities", &this->use_tofsens); this->parser.add_key("max ring difference num to process", &this->max_ring_difference_num_to_process); - this->parser.add_parsing_key("Matrix type", &this->PM_sptr); - this->parser.add_key("additive sinogram",&this->additive_projection_data_filename); - - this->parser.add_key("num_events_to_use",&this->num_events_to_use); - -} -template -int -PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin:: -set_num_subsets(const int new_num_subsets) -{ + this->parser.add_parsing_key("Matrix type", &this->PM_sptr); + this->parser.add_key("additive sinogram", &this->additive_projection_data_filename); + + this->parser.add_key("num_events_to_use", &this->num_events_to_use); +} +template +int +PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin::set_num_subsets( + const int new_num_subsets) { this->num_subsets = new_num_subsets; return this->num_subsets; } -template +template bool -PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin:: -actual_subsets_are_approximately_balanced(std::string& warning_message) const -{ - assert(this->num_subsets>0); - const DataSymmetriesForBins& symmetries = - *this->PM_sptr->get_symmetries_ptr(); - - Array<1,int> num_bins_in_subset(this->num_subsets); - num_bins_in_subset.fill(0); - - if (this->num_subsets == 1) - return true; - - - for (int subset_num=0; subset_numnum_subsets; ++subset_num) - { - for (int timing_pos_num = proj_data_info_sptr->get_min_tof_pos_num(); - timing_pos_num <= proj_data_info_sptr->get_max_tof_pos_num(); - ++timing_pos_num) - { - for (int segment_num = -this->max_ring_difference_num_to_process; - segment_num <= this->max_ring_difference_num_to_process; ++segment_num) - { - for (int axial_num = proj_data_info_sptr->get_min_axial_pos_num(segment_num); - axial_num < proj_data_info_sptr->get_max_axial_pos_num(segment_num); - axial_num ++) - { - // For debugging. - // std::cout <get_min_tangential_pos_num(); - tang_num < proj_data_info_sptr->get_max_tangential_pos_num(); - tang_num ++ ) - { - for(int view_num = proj_data_info_sptr->get_min_view_num() + subset_num; - view_num <= proj_data_info_sptr->get_max_view_num(); - view_num += this->num_subsets) - { - const Bin tmp_bin(segment_num, - view_num, - axial_num, - tang_num, - timing_pos_num, - 1); - - if (!this->PM_sptr->get_symmetries_ptr()->is_basic(tmp_bin) ) - continue; - - num_bins_in_subset[subset_num] += - symmetries.num_related_bins(tmp_bin); - - } - } - } - } - } - } +PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin::actual_subsets_are_approximately_balanced( + std::string& warning_message) const { + assert(this->num_subsets > 0); + const DataSymmetriesForBins& symmetries = *this->PM_sptr->get_symmetries_ptr(); + + Array<1, int> num_bins_in_subset(this->num_subsets); + num_bins_in_subset.fill(0); + + if (this->num_subsets == 1) + return true; - for (int subset_num=1; subset_numnum_subsets; ++subset_num) - { - if(num_bins_in_subset[subset_num] != num_bins_in_subset[0]) - { - std::stringstream str(warning_message); - str <<"Number of subsets is such that subsets will be very unbalanced.\n" - << "Number of Bins in each subset would be:\n" - << num_bins_in_subset - << "\nEither reduce the number of symmetries used by the projector, or\n" - "change the number of subsets. It usually should be a divisor of\n" - << proj_data_info_sptr->get_num_views() - << "/4 (or if that's not an integer, a divisor of " - << proj_data_info_sptr->get_num_views() - << "/2 or " - << proj_data_info_sptr->get_num_views() - << ").\n"; - warning_message = str.str(); - return false; + for (int subset_num = 0; subset_num < this->num_subsets; ++subset_num) { + for (int timing_pos_num = proj_data_info_sptr->get_min_tof_pos_num(); + timing_pos_num <= proj_data_info_sptr->get_max_tof_pos_num(); ++timing_pos_num) { + for (int segment_num = -this->max_ring_difference_num_to_process; segment_num <= this->max_ring_difference_num_to_process; + ++segment_num) { + for (int axial_num = proj_data_info_sptr->get_min_axial_pos_num(segment_num); + axial_num < proj_data_info_sptr->get_max_axial_pos_num(segment_num); axial_num++) { + // For debugging. + // std::cout <get_min_tangential_pos_num(); + tang_num < proj_data_info_sptr->get_max_tangential_pos_num(); tang_num++) { + for (int view_num = proj_data_info_sptr->get_min_view_num() + subset_num; + view_num <= proj_data_info_sptr->get_max_view_num(); view_num += this->num_subsets) { + const Bin tmp_bin(segment_num, view_num, axial_num, tang_num, timing_pos_num, 1); + + if (!this->PM_sptr->get_symmetries_ptr()->is_basic(tmp_bin)) + continue; + + num_bins_in_subset[subset_num] += symmetries.num_related_bins(tmp_bin); } + } } - return true; + } + } + } + + for (int subset_num = 1; subset_num < this->num_subsets; ++subset_num) { + if (num_bins_in_subset[subset_num] != num_bins_in_subset[0]) { + std::stringstream str(warning_message); + str << "Number of subsets is such that subsets will be very unbalanced.\n" + << "Number of Bins in each subset would be:\n" + << num_bins_in_subset + << "\nEither reduce the number of symmetries used by the projector, or\n" + "change the number of subsets. It usually should be a divisor of\n" + << proj_data_info_sptr->get_num_views() << "/4 (or if that's not an integer, a divisor of " + << proj_data_info_sptr->get_num_views() << "/2 or " << proj_data_info_sptr->get_num_views() << ").\n"; + warning_message = str.str(); + return false; + } + } + return true; } -template -Succeeded -PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin:: -set_up_before_sensitivity(shared_ptr const& target_sptr) -{ +template +Succeeded +PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin::set_up_before_sensitivity( + shared_ptr const& target_sptr) { #ifdef STIR_MPI - //broadcast objective_function (100=PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin) - distributed::send_int_value(100, -1); + // broadcast objective_function (100=PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin) + distributed::send_int_value(100, -1); #endif - // set projector to be used for the calculations - this->PM_sptr->set_up(proj_data_info_sptr->create_shared_clone(),target_sptr); + // set projector to be used for the calculations + this->PM_sptr->set_up(proj_data_info_sptr->create_shared_clone(), target_sptr); - //this->PM_sptr->enable_tof(proj_data_info_sptr->create_shared_clone(), this->use_tof); - shared_ptr forward_projector_ptr(new ForwardProjectorByBinUsingProjMatrixByBin(this->PM_sptr)); - shared_ptr back_projector_ptr(new BackProjectorByBinUsingProjMatrixByBin(this->PM_sptr)); + // this->PM_sptr->enable_tof(proj_data_info_sptr->create_shared_clone(), this->use_tof); + shared_ptr forward_projector_ptr(new ForwardProjectorByBinUsingProjMatrixByBin(this->PM_sptr)); + shared_ptr back_projector_ptr(new BackProjectorByBinUsingProjMatrixByBin(this->PM_sptr)); - this->projector_pair_sptr.reset( - new ProjectorByBinPairUsingSeparateProjectors(forward_projector_ptr, back_projector_ptr)); + this->projector_pair_sptr.reset(new ProjectorByBinPairUsingSeparateProjectors(forward_projector_ptr, back_projector_ptr)); - this->projector_pair_sptr->set_up(proj_data_info_sptr->create_shared_clone(),target_sptr); + this->projector_pair_sptr->set_up(proj_data_info_sptr->create_shared_clone(), target_sptr); - // sets non-tof backprojector for sensitivity calculation (clone of the back_projector + set projdatainfo to non-tof) - this->sens_backprojector_sptr.reset(projector_pair_sptr->get_back_projector_sptr()->clone()); - if (!this->use_tofsens) - this->sens_backprojector_sptr->set_up(proj_data_info_sptr->create_non_tof_clone(), target_sptr); + // sets non-tof backprojector for sensitivity calculation (clone of the back_projector + set projdatainfo to non-tof) + this->sens_backprojector_sptr.reset(projector_pair_sptr->get_back_projector_sptr()->clone()); + if (!this->use_tofsens) + this->sens_backprojector_sptr->set_up(proj_data_info_sptr->create_non_tof_clone(), target_sptr); - if (is_null_ptr(this->normalisation_sptr)) - { - warning("Invalid normalisation object"); - return Succeeded::no; - } + if (is_null_ptr(this->normalisation_sptr)) { + warning("Invalid normalisation object"); + return Succeeded::no; + } - if (this->normalisation_sptr->set_up( - this->list_mode_data_sptr->get_exam_info_sptr(), proj_data_info_sptr->create_shared_clone()) == Succeeded::no) - return Succeeded::no; + if (this->normalisation_sptr->set_up(this->list_mode_data_sptr->get_exam_info_sptr(), + proj_data_info_sptr->create_shared_clone()) == Succeeded::no) + return Succeeded::no; - if (this->current_frame_num<=0) - { - warning("frame_num should be >= 1"); - return Succeeded::no; - } + if (this->current_frame_num <= 0) { + warning("frame_num should be >= 1"); + return Succeeded::no; + } - if (this->current_frame_num > this->frame_defs.get_num_frames()) - { - warning("frame_num is %d, but should be less than the number of frames %d.", - this->current_frame_num, this->frame_defs.get_num_frames()); - return Succeeded::no; - } + if (this->current_frame_num > this->frame_defs.get_num_frames()) { + warning("frame_num is %d, but should be less than the number of frames %d.", this->current_frame_num, + this->frame_defs.get_num_frames()); + return Succeeded::no; + } - return Succeeded::yes; -} - - -template -bool -PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin::post_processing() -{ + return Succeeded::yes; +} + +template +bool +PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin::post_processing() { if (base_type::post_processing() == true) return true; @@ -267,210 +224,166 @@ PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBinPM_sptr)) - { warning("You need to specify a projection matrix"); return true; } + { + warning("You need to specify a projection matrix"); + return true; + } #else - if(is_null_ptr(this->projector_pair_sptr->get_forward_projector_sptr())) - { - warning("No valid forward projector is defined"); return true; - } + if (is_null_ptr(this->projector_pair_sptr->get_forward_projector_sptr())) { + warning("No valid forward projector is defined"); + return true; + } - if(is_null_ptr(this->projector_pair_sptr->get_back_projector_sptr())) - { - warning("No valid back projector is defined"); return true; - } + if (is_null_ptr(this->projector_pair_sptr->get_back_projector_sptr())) { + warning("No valid back projector is defined"); + return true; + } #endif shared_ptr scanner_sptr(new Scanner(*this->list_mode_data_sptr->get_scanner_ptr())); - if (this->max_ring_difference_num_to_process == -1) - { - this->max_ring_difference_num_to_process = - scanner_sptr->get_num_rings()-1; - } + if (this->max_ring_difference_num_to_process == -1) { + this->max_ring_difference_num_to_process = scanner_sptr->get_num_rings() - 1; + } - if (this->additive_projection_data_filename != "0") - { - info(boost::format("Reading additive projdata data '%1%'") - % additive_projection_data_filename ); - shared_ptr temp_additive_proj_data_sptr = - ProjData::read_from_file(this->additive_projection_data_filename); - this->additive_proj_data_sptr.reset(new ProjDataInMemory(* temp_additive_proj_data_sptr)); - } + if (this->additive_projection_data_filename != "0") { + info(boost::format("Reading additive projdata data '%1%'") % additive_projection_data_filename); + shared_ptr temp_additive_proj_data_sptr = ProjData::read_from_file(this->additive_projection_data_filename); + this->additive_proj_data_sptr.reset(new ProjDataInMemory(*temp_additive_proj_data_sptr)); + } - proj_data_info_sptr = this->list_mode_data_sptr->get_proj_data_info_sptr()->create_shared_clone(); - - if (max_ring_difference_num_to_process > proj_data_info_sptr->get_max_segment_num()) - { - warning("In the parameter file, the 'maximum ring difference' is larger than the number of segments" - "in the listmode file. Abort."); - return true; - } - else if (max_ring_difference_num_to_process < proj_data_info_sptr->get_max_segment_num()) - { - proj_data_info_sptr->reduce_segment_range(-max_ring_difference_num_to_process, - max_ring_difference_num_to_process); - } - - // Daniel: abilitate do_time_frame if there is a fdef file - if (this->frame_defs_filename.size()!=0) - { - this->frame_defs = TimeFrameDefinitions(this->frame_defs_filename); - this->do_time_frame = true; - } - - if(!is_null_ptr(this->additive_proj_data_sptr)) - if (*(this->additive_proj_data_sptr->get_proj_data_info_sptr()) != *proj_data_info_sptr) - { - const ProjDataInfo& add_proj = *(this->additive_proj_data_sptr->get_proj_data_info_sptr()); - const ProjDataInfo& proj = *this->proj_data_info_sptr; - bool ok = - typeid(add_proj) == typeid(proj) && - *add_proj.get_scanner_ptr()== *(proj.get_scanner_ptr()) && - (add_proj.get_min_view_num()==proj.get_min_view_num()) && - (add_proj.get_max_view_num()==proj.get_max_view_num()) && - (add_proj.get_min_tangential_pos_num() ==proj.get_min_tangential_pos_num())&& - (add_proj.get_max_tangential_pos_num() ==proj.get_max_tangential_pos_num()) && - add_proj.get_min_segment_num() <= proj.get_min_segment_num() && - add_proj.get_max_segment_num() >= proj.get_max_segment_num() && - add_proj.get_min_tof_pos_num() <= proj.get_min_tof_pos_num() && - add_proj.get_max_tof_pos_num() >= proj.get_max_tof_pos_num(); - for (int segment_num=proj.get_min_segment_num(); - ok && segment_num<=proj.get_max_segment_num(); - ++segment_num) - { - ok = - add_proj.get_min_axial_pos_num(segment_num) <= proj.get_min_axial_pos_num(segment_num) && - add_proj.get_max_axial_pos_num(segment_num) >= proj.get_max_axial_pos_num(segment_num); - } - if (!ok) - { - warning(boost::format("Incompatible additive projection data:\nAdditive projdata info:\n%s\nEmission projdata info:\n%s\n" - "--- (end of incompatible projection data info)---\n") - % add_proj.parameter_info() - % proj.parameter_info()); - return true; - } - } + proj_data_info_sptr = this->list_mode_data_sptr->get_proj_data_info_sptr()->create_shared_clone(); - if( this->normalisation_sptr->set_up(this->list_mode_data_sptr->get_exam_info_sptr(), proj_data_info_sptr) - == Succeeded::no) - { -warning("PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin: " - "set-up of normalisation failed."); -return true; + if (max_ring_difference_num_to_process > proj_data_info_sptr->get_max_segment_num()) { + warning("In the parameter file, the 'maximum ring difference' is larger than the number of segments" + "in the listmode file. Abort."); + return true; + } else if (max_ring_difference_num_to_process < proj_data_info_sptr->get_max_segment_num()) { + proj_data_info_sptr->reduce_segment_range(-max_ring_difference_num_to_process, max_ring_difference_num_to_process); + } + + // Daniel: abilitate do_time_frame if there is a fdef file + if (this->frame_defs_filename.size() != 0) { + this->frame_defs = TimeFrameDefinitions(this->frame_defs_filename); + this->do_time_frame = true; + } + + if (!is_null_ptr(this->additive_proj_data_sptr)) + if (*(this->additive_proj_data_sptr->get_proj_data_info_sptr()) != *proj_data_info_sptr) { + const ProjDataInfo& add_proj = *(this->additive_proj_data_sptr->get_proj_data_info_sptr()); + const ProjDataInfo& proj = *this->proj_data_info_sptr; + bool ok = typeid(add_proj) == typeid(proj) && *add_proj.get_scanner_ptr() == *(proj.get_scanner_ptr()) && + (add_proj.get_min_view_num() == proj.get_min_view_num()) && + (add_proj.get_max_view_num() == proj.get_max_view_num()) && + (add_proj.get_min_tangential_pos_num() == proj.get_min_tangential_pos_num()) && + (add_proj.get_max_tangential_pos_num() == proj.get_max_tangential_pos_num()) && + add_proj.get_min_segment_num() <= proj.get_min_segment_num() && + add_proj.get_max_segment_num() >= proj.get_max_segment_num() && + add_proj.get_min_tof_pos_num() <= proj.get_min_tof_pos_num() && + add_proj.get_max_tof_pos_num() >= proj.get_max_tof_pos_num(); + for (int segment_num = proj.get_min_segment_num(); ok && segment_num <= proj.get_max_segment_num(); ++segment_num) { + ok = add_proj.get_min_axial_pos_num(segment_num) <= proj.get_min_axial_pos_num(segment_num) && + add_proj.get_max_axial_pos_num(segment_num) >= proj.get_max_axial_pos_num(segment_num); + } + if (!ok) { + warning(boost::format("Incompatible additive projection data:\nAdditive projdata info:\n%s\nEmission projdata info:\n%s\n" + "--- (end of incompatible projection data info)---\n") % + add_proj.parameter_info() % proj.parameter_info()); + return true; + } } - return false; + if (this->normalisation_sptr->set_up(this->list_mode_data_sptr->get_exam_info_sptr(), proj_data_info_sptr) == Succeeded::no) { + warning("PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin: " + "set-up of normalisation failed."); + return true; + } -} - -template + return false; +} + +template void -PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin:: -add_subset_sensitivity(TargetT& sensitivity, const int subset_num) const -{ +PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin::add_subset_sensitivity( + TargetT& sensitivity, const int subset_num) const { - const int min_segment_num = proj_data_info_sptr->get_min_segment_num(); - const int max_segment_num = proj_data_info_sptr->get_max_segment_num(); + const int min_segment_num = proj_data_info_sptr->get_min_segment_num(); + const int max_segment_num = proj_data_info_sptr->get_max_segment_num(); - this->sens_backprojector_sptr-> - start_accumulating_in_new_target(); + this->sens_backprojector_sptr->start_accumulating_in_new_target(); - // warning: has to be same as subset scheme used as in distributable_computation - for (int segment_num = min_segment_num; segment_num <= max_segment_num; ++segment_num) - { - for (int view = proj_data_info_sptr->get_min_view_num() + subset_num; - view <= proj_data_info_sptr->get_max_view_num(); - view += this->num_subsets) - { - const ViewSegmentNumbers view_segment_num(view, segment_num); + // warning: has to be same as subset scheme used as in distributable_computation + for (int segment_num = min_segment_num; segment_num <= max_segment_num; ++segment_num) { + for (int view = proj_data_info_sptr->get_min_view_num() + subset_num; view <= proj_data_info_sptr->get_max_view_num(); + view += this->num_subsets) { + const ViewSegmentNumbers view_segment_num(view, segment_num); - if (! this->projector_pair_sptr->get_symmetries_used()->is_basic(view_segment_num)) - continue; - this->add_view_seg_to_sensitivity(view_segment_num); - } + if (!this->projector_pair_sptr->get_symmetries_used()->is_basic(view_segment_num)) + continue; + this->add_view_seg_to_sensitivity(view_segment_num); } - this->sens_backprojector_sptr-> - get_output(sensitivity); + } + this->sens_backprojector_sptr->get_output(sensitivity); } -template +template void -PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin:: -add_view_seg_to_sensitivity(const ViewSegmentNumbers& view_seg_nums) const -{ - int min_timing_pos_num = use_tofsens ? this->proj_data_info_sptr->get_min_tof_pos_num() : 0; - int max_timing_pos_num = use_tofsens ? this->proj_data_info_sptr->get_max_tof_pos_num() : 0; - for (int timing_pos_num = min_timing_pos_num; timing_pos_num <= max_timing_pos_num; ++timing_pos_num) - { - shared_ptr symmetries_used - (this->projector_pair_sptr->get_symmetries_used()->clone()); - - RelatedViewgrams viewgrams = - proj_data_info_sptr->get_empty_related_viewgrams( - view_seg_nums, symmetries_used, false, timing_pos_num); - - viewgrams.fill(1.F); - // find efficiencies - { - const double start_frame = this->frame_defs.get_start_time(this->current_frame_num); - const double end_frame = this->frame_defs.get_end_time(this->current_frame_num); - this->normalisation_sptr->undo(viewgrams, start_frame, end_frame); - } - // backproject - { - const int min_ax_pos_num = - viewgrams.get_min_axial_pos_num(); - const int max_ax_pos_num = - viewgrams.get_max_axial_pos_num(); - - this->sens_backprojector_sptr->back_project(viewgrams, - min_ax_pos_num, max_ax_pos_num); - } - } +PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin::add_view_seg_to_sensitivity( + const ViewSegmentNumbers& view_seg_nums) const { + int min_timing_pos_num = use_tofsens ? this->proj_data_info_sptr->get_min_tof_pos_num() : 0; + int max_timing_pos_num = use_tofsens ? this->proj_data_info_sptr->get_max_tof_pos_num() : 0; + for (int timing_pos_num = min_timing_pos_num; timing_pos_num <= max_timing_pos_num; ++timing_pos_num) { + shared_ptr symmetries_used(this->projector_pair_sptr->get_symmetries_used()->clone()); + + RelatedViewgrams viewgrams = + proj_data_info_sptr->get_empty_related_viewgrams(view_seg_nums, symmetries_used, false, timing_pos_num); + + viewgrams.fill(1.F); + // find efficiencies + { + const double start_frame = this->frame_defs.get_start_time(this->current_frame_num); + const double end_frame = this->frame_defs.get_end_time(this->current_frame_num); + this->normalisation_sptr->undo(viewgrams, start_frame, end_frame); + } + // backproject + { + const int min_ax_pos_num = viewgrams.get_min_axial_pos_num(); + const int max_ax_pos_num = viewgrams.get_max_axial_pos_num(); + this->sens_backprojector_sptr->back_project(viewgrams, min_ax_pos_num, max_ax_pos_num); + } + } } -template - std::unique_ptr - PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin:: - get_exam_info_uptr_for_target() const -{ - auto exam_info_uptr = this->get_exam_info_uptr_for_target(); - if (auto norm_ptr = dynamic_cast(get_normalisation_sptr().get())) - { - exam_info_uptr->set_calibration_factor(norm_ptr->get_calibration_factor()); - // somehow tell the image that it's calibrated (do we have a way?) - } - else - { - exam_info_uptr->set_calibration_factor(1.F); - // somehow tell the image that it's not calibrated (do we have a way?) - } - return exam_info_uptr; +template +std::unique_ptr +PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin::get_exam_info_uptr_for_target() const { + auto exam_info_uptr = this->get_exam_info_uptr_for_target(); + if (auto norm_ptr = dynamic_cast(get_normalisation_sptr().get())) { + exam_info_uptr->set_calibration_factor(norm_ptr->get_calibration_factor()); + // somehow tell the image that it's calibrated (do we have a way?) + } else { + exam_info_uptr->set_calibration_factor(1.F); + // somehow tell the image that it's not calibrated (do we have a way?) + } + return exam_info_uptr; } +template +TargetT* +PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin::construct_target_ptr() const { -template -TargetT * -PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin:: -construct_target_ptr() const -{ + return this->target_parameter_parser.create(this->get_input_data()); +} - return - this->target_parameter_parser.create(this->get_input_data()); -} - template void -PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin:: -compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, - const TargetT ¤t_estimate, - const int subset_num) -{ +PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin< + TargetT>::compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, const TargetT& current_estimate, + const int subset_num) { - assert(subset_num>=0); - assert(subset_numnum_subsets); + assert(subset_num >= 0); + assert(subset_num < this->num_subsets); const double start_time = this->frame_defs.get_start_time(this->current_frame_num); const double end_time = this->frame_defs.get_end_time(this->current_frame_num); @@ -482,7 +395,7 @@ compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, Bin measured_bin; Bin fwd_bin; - //go to the beginning of this frame + // go to the beginning of this frame // list_mode_data_sptr->set_get_position(start_time); // TODO implement function that will do this for a random time this->list_mode_data_sptr->reset(); @@ -492,109 +405,96 @@ compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, shared_ptr record_sptr = this->list_mode_data_sptr->get_empty_record_sptr(); ListRecord& record = *record_sptr; - VectorWithOffset - frame_start_positions(1, static_cast(this->frame_defs.get_num_frames())); + VectorWithOffset frame_start_positions(1, static_cast(this->frame_defs.get_num_frames())); - long int more_events = - this->do_time_frame? 1 : (this->num_events_to_use / this->num_subsets); + long int more_events = this->do_time_frame ? 1 : (this->num_events_to_use / this->num_subsets); - while (more_events)//this->list_mode_data_sptr->get_next_record(record) == Succeeded::yes) - { - - if (this->list_mode_data_sptr->get_next_record(record) == Succeeded::no) - { - info("End of file!"); - break; //get out of while loop - } + while (more_events) // this->list_mode_data_sptr->get_next_record(record) == Succeeded::yes) + { - if(record.is_time() && end_time > 0.01) - { - current_time = record.time().get_time_in_secs(); - if (this->do_time_frame && current_time >= end_time) - break; // get out of while loop - if (current_time < start_time) - continue; - } + if (this->list_mode_data_sptr->get_next_record(record) == Succeeded::no) { + info("End of file!"); + break; // get out of while loop + } - if (record.is_event() && record.event().is_prompt()) - { - measured_bin.set_bin_value(1.0f); - - record.event().get_bin(measured_bin, *proj_data_info_sptr); - - // In theory we have already done all these checks so we can - // remove this if statement. - if (measured_bin.get_bin_value() != 1.0f - || measured_bin.segment_num() < proj_data_info_sptr->get_min_segment_num() - || measured_bin.segment_num() > proj_data_info_sptr->get_max_segment_num() - || measured_bin.tangential_pos_num() < proj_data_info_sptr->get_min_tangential_pos_num() - || measured_bin.tangential_pos_num() > proj_data_info_sptr->get_max_tangential_pos_num() - || measured_bin.axial_pos_num() < proj_data_info_sptr->get_min_axial_pos_num(measured_bin.segment_num()) - || measured_bin.axial_pos_num() > proj_data_info_sptr->get_max_axial_pos_num(measured_bin.segment_num()) - || measured_bin.timing_pos_num() < proj_data_info_sptr->get_min_tof_pos_num() - || measured_bin.timing_pos_num() > proj_data_info_sptr->get_max_tof_pos_num()) - { - continue; - } + if (record.is_time() && end_time > 0.01) { + current_time = record.time().get_time_in_secs(); + if (this->do_time_frame && current_time >= end_time) + break; // get out of while loop + if (current_time < start_time) + continue; + } - measured_bin.set_bin_value(1.0f); - // If more than 1 subsets, check if the current bin belongs to - // the current. + if (record.is_event() && record.event().is_prompt()) { + measured_bin.set_bin_value(1.0f); + + record.event().get_bin(measured_bin, *proj_data_info_sptr); + + // In theory we have already done all these checks so we can + // remove this if statement. + if (measured_bin.get_bin_value() != 1.0f || measured_bin.segment_num() < proj_data_info_sptr->get_min_segment_num() || + measured_bin.segment_num() > proj_data_info_sptr->get_max_segment_num() || + measured_bin.tangential_pos_num() < proj_data_info_sptr->get_min_tangential_pos_num() || + measured_bin.tangential_pos_num() > proj_data_info_sptr->get_max_tangential_pos_num() || + measured_bin.axial_pos_num() < proj_data_info_sptr->get_min_axial_pos_num(measured_bin.segment_num()) || + measured_bin.axial_pos_num() > proj_data_info_sptr->get_max_axial_pos_num(measured_bin.segment_num()) || + measured_bin.timing_pos_num() < proj_data_info_sptr->get_min_tof_pos_num() || + measured_bin.timing_pos_num() > proj_data_info_sptr->get_max_tof_pos_num()) { + continue; + } - if (this->num_subsets > 1) - { - Bin basic_bin = measured_bin; - if (!this->PM_sptr->get_symmetries_ptr()->is_basic(measured_bin) ) - this->PM_sptr->get_symmetries_ptr()->find_basic_bin(basic_bin); + measured_bin.set_bin_value(1.0f); + // If more than 1 subsets, check if the current bin belongs to + // the current. - if (subset_num != static_cast(basic_bin.view_num() % this->num_subsets)) - { - continue; - } - } + if (this->num_subsets > 1) { + Bin basic_bin = measured_bin; + if (!this->PM_sptr->get_symmetries_ptr()->is_basic(measured_bin)) + this->PM_sptr->get_symmetries_ptr()->find_basic_bin(basic_bin); - measured_bin.set_bin_value(1.0f); - this->PM_sptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, measured_bin); - //in_the_range++; - fwd_bin.set_bin_value(0.0f); - proj_matrix_row.forward_project(fwd_bin,current_estimate); - // additive sinogram - if (!is_null_ptr(this->additive_proj_data_sptr)) - { - float add_value = this->additive_proj_data_sptr->get_bin_value(measured_bin); - float value= fwd_bin.get_bin_value()+add_value; - fwd_bin.set_bin_value(value); + if (subset_num != static_cast(basic_bin.view_num() % this->num_subsets)) { + continue; } - float measured_div_fwd = 0.0f; + } - if(!this->do_time_frame) - more_events -=1 ; + measured_bin.set_bin_value(1.0f); + this->PM_sptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, measured_bin); + // in_the_range++; + fwd_bin.set_bin_value(0.0f); + proj_matrix_row.forward_project(fwd_bin, current_estimate); + // additive sinogram + if (!is_null_ptr(this->additive_proj_data_sptr)) { + float add_value = this->additive_proj_data_sptr->get_bin_value(measured_bin); + float value = fwd_bin.get_bin_value() + add_value; + fwd_bin.set_bin_value(value); + } + float measured_div_fwd = 0.0f; - num_used_events += 1; + if (!this->do_time_frame) + more_events -= 1; - if (num_used_events%200000L==0) - info( boost::format("Stored Events: %1% ") % num_used_events); + num_used_events += 1; - if ( measured_bin.get_bin_value() <= max_quotient *fwd_bin.get_bin_value()) - measured_div_fwd = 1.0f /fwd_bin.get_bin_value(); - else - continue; + if (num_used_events % 200000L == 0) + info(boost::format("Stored Events: %1% ") % num_used_events); - measured_bin.set_bin_value(measured_div_fwd); - proj_matrix_row.back_project(gradient, measured_bin); + if (measured_bin.get_bin_value() <= max_quotient * fwd_bin.get_bin_value()) + measured_div_fwd = 1.0f / fwd_bin.get_bin_value(); + else + continue; + measured_bin.set_bin_value(measured_div_fwd); + proj_matrix_row.back_project(gradient, measured_bin); } } info(boost::format("Number of used events: %1%") % num_used_events); } -# ifdef _MSC_VER -// prevent warning message on instantiation of abstract class -# pragma warning(disable:4661) -# endif - -template class -PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin >; +#ifdef _MSC_VER +// prevent warning message on instantiation of abstract class +# pragma warning(disable : 4661) +#endif +template class PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin>; END_NAMESPACE_STIR diff --git a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx index 7774e72cf3..55e4d96a59 100644 --- a/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx +++ b/src/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx @@ -39,19 +39,19 @@ #include "stir/DiscretisedDensity.h" #ifdef STIR_MPI -#include "stir/recon_buildblock/DistributedCachingInformation.h" +# include "stir/recon_buildblock/DistributedCachingInformation.h" #endif #include "stir/recon_buildblock/distributable.h" // for get_symmetries_ptr() #include "stir/DataSymmetriesForViewSegmentNumbers.h" // include the following to set defaults #ifndef USE_PMRT -#include "stir/recon_buildblock/ForwardProjectorByBinUsingRayTracing.h" -#include "stir/recon_buildblock/BackProjectorByBinUsingInterpolation.h" +# include "stir/recon_buildblock/ForwardProjectorByBinUsingRayTracing.h" +# include "stir/recon_buildblock/BackProjectorByBinUsingInterpolation.h" #else -#include "stir/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.h" -#include "stir/recon_buildblock/BackProjectorByBinUsingProjMatrixByBin.h" -#include "stir/recon_buildblock/ProjMatrixByBinUsingRayTracing.h" +# include "stir/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.h" +# include "stir/recon_buildblock/BackProjectorByBinUsingProjMatrixByBin.h" +# include "stir/recon_buildblock/ProjMatrixByBinUsingRayTracing.h" #endif #include "stir/recon_buildblock/ProjectorByBinPairUsingSeparateProjectors.h" #include "stir/recon_buildblock/find_basic_vs_nums_in_subsets.h" @@ -67,7 +67,7 @@ #include #include #ifdef STIR_MPI -#include "stir/recon_buildblock/distributed_functions.h" +# include "stir/recon_buildblock/distributed_functions.h" #endif #include "stir/CPUTimer.h" #include "stir/info.h" @@ -80,62 +80,55 @@ using std::ends; using std::max; #endif - START_NAMESPACE_STIR const int rim_truncation_sino = 0; // TODO get rid of this -template -const char * const -PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -registered_name = -"PoissonLogLikelihoodWithLinearModelForMeanAndProjData"; +template +const char* const PoissonLogLikelihoodWithLinearModelForMeanAndProjData::registered_name = + "PoissonLogLikelihoodWithLinearModelForMeanAndProjData"; -template +template void -PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -set_defaults() -{ +PoissonLogLikelihoodWithLinearModelForMeanAndProjData::set_defaults() { base_type::set_defaults(); - this->input_filename=""; - this->max_segment_num_to_process=-1; - this->max_timing_pos_num_to_process=0; + this->input_filename = ""; + this->max_segment_num_to_process = -1; + this->max_timing_pos_num_to_process = 0; // KT 20/06/2001 disabled - //num_views_to_add=1; - this->proj_data_sptr.reset(); //MJ added + // num_views_to_add=1; + this->proj_data_sptr.reset(); // MJ added this->zero_seg0_end_planes = 0; this->use_tofsens = false; this->additive_projection_data_filename = "0"; this->additive_proj_data_sptr.reset(); - // set default for projector_pair_ptr #ifndef USE_PMRT shared_ptr forward_projector_ptr(new ForwardProjectorByBinUsingRayTracing()); shared_ptr back_projector_ptr(new BackProjectorByBinUsingInterpolation()); #else - shared_ptr PM(new ProjMatrixByBinUsingRayTracing()); + shared_ptr PM(new ProjMatrixByBinUsingRayTracing()); // PM->set_num_tangential_LORs(5); - shared_ptr forward_projector_ptr(new ForwardProjectorByBinUsingProjMatrixByBin(PM)); + shared_ptr forward_projector_ptr(new ForwardProjectorByBinUsingProjMatrixByBin(PM)); shared_ptr back_projector_ptr(new BackProjectorByBinUsingProjMatrixByBin(PM)); #endif - this->projector_pair_ptr.reset( - new ProjectorByBinPairUsingSeparateProjectors(forward_projector_ptr, back_projector_ptr)); + this->projector_pair_ptr.reset(new ProjectorByBinPairUsingSeparateProjectors(forward_projector_ptr, back_projector_ptr)); this->normalisation_sptr.reset(new TrivialBinNormalisation); this->frame_num = 1; this->frame_definition_filename = ""; // make a single frame starting from 0 to 1. - vector > frame_times(1, pair(0,1)); + vector> frame_times(1, pair(0, 1)); this->frame_defs = TimeFrameDefinitions(frame_times); this->target_parameter_parser.set_defaults(); - + #ifdef STIR_MPI - //distributed stuff + // distributed stuff this->distributed_cache_enabled = false; this->distributed_tests_enabled = false; this->message_timings_enabled = false; @@ -144,18 +137,16 @@ set_defaults() #endif } -template +template void -PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -initialise_keymap() -{ +PoissonLogLikelihoodWithLinearModelForMeanAndProjData::initialise_keymap() { base_type::initialise_keymap(); this->parser.add_start_key("PoissonLogLikelihoodWithLinearModelForMeanAndProjData Parameters"); this->parser.add_stop_key("End PoissonLogLikelihoodWithLinearModelForMeanAndProjData Parameters"); this->parser.add_key("use time-of-flight sensitivities", &this->use_tofsens); - this->parser.add_key("input file",&this->input_filename); + this->parser.add_key("input file", &this->input_filename); // KT 20/06/2001 disabled - //parser.add_key("mash x views", &num_views_to_add); + // parser.add_key("mash x views", &num_views_to_add); this->parser.add_key("maximum absolute segment number to process", &this->max_segment_num_to_process); this->parser.add_key("zero end planes of segment 0", &this->zero_seg0_end_planes); @@ -163,14 +154,14 @@ initialise_keymap() this->target_parameter_parser.add_to_keymap(this->parser); this->parser.add_parsing_key("Projector pair type", &this->projector_pair_ptr); - this->parser.add_key("additive sinogram",&this->additive_projection_data_filename); + this->parser.add_key("additive sinogram", &this->additive_projection_data_filename); // normalisation (and attenuation correction) - this->parser.add_key("time frame definition filename", &this->frame_definition_filename); + this->parser.add_key("time frame definition filename", &this->frame_definition_filename); this->parser.add_key("time frame number", &this->frame_num); this->parser.add_parsing_key("Bin Normalisation type", &this->normalisation_sptr); #ifdef STIR_MPI - //distributed stuff + // distributed stuff this->parser.add_key("enable distributed caching", &distributed_cache_enabled); this->parser.add_key("enable distributed tests", &distributed_tests_enabled); this->parser.add_key("enable message timings", &message_timings_enabled); @@ -179,51 +170,44 @@ initialise_keymap() #endif } -template +template bool -PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -post_processing() -{ +PoissonLogLikelihoodWithLinearModelForMeanAndProjData::post_processing() { if (base_type::post_processing() == true) return true; - // KT 20/06/2001 disabled as not functional yet + // KT 20/06/2001 disabled as not functional yet #if 0 if (num_views_to_add!=1 && (num_views_to_add<=0 || num_views_to_add%2 != 0)) { warning("The 'mash x views' key has an invalid value (must be 1 or even number)"); return true; } #endif - - if (this->input_filename.length() > 0 ) - { - this->proj_data_sptr= ProjData::read_from_file(input_filename); - if (is_null_ptr(this->proj_data_sptr)) - { - error("Failed to read input file %s", input_filename.c_str()); - return true; - } + if (this->input_filename.length() > 0) { + this->proj_data_sptr = ProjData::read_from_file(input_filename); + + if (is_null_ptr(this->proj_data_sptr)) { + error("Failed to read input file %s", input_filename.c_str()); + return true; + } } target_parameter_parser.check_values(); - if (this->additive_projection_data_filename != "0") - { + if (this->additive_projection_data_filename != "0") { info(boost::format("Reading additive projdata data %1%") % this->additive_projection_data_filename); - this->additive_proj_data_sptr = - ProjData::read_from_file(this->additive_projection_data_filename); + this->additive_proj_data_sptr = ProjData::read_from_file(this->additive_projection_data_filename); }; - // read time frame def - if (this->frame_definition_filename.size()!=0) + // read time frame def + if (this->frame_definition_filename.size() != 0) this->frame_defs = TimeFrameDefinitions(this->frame_definition_filename); - else - { - // make a single frame starting from 0 to 1. - vector > frame_times(1, pair(0,1)); - this->frame_defs = TimeFrameDefinitions(frame_times); - } + else { + // make a single frame starting from 0 to 1. + vector> frame_times(1, pair(0, 1)); + this->frame_defs = TimeFrameDefinitions(frame_times); + } #ifndef STIR_MPI -#if 0 +# if 0 //check caching enabled value if (this->distributed_cache_enabled==true) { @@ -236,336 +220,283 @@ post_processing() warning("STIR must be compiled with MPI-compiler and debug symbols to use distributed testing.\n\tDistributed tests will not be performed!"); this->distributed_tests_enabled=false; } -#endif -#else - //check caching enabled value - if (this->distributed_cache_enabled==true) - info("Will use distributed caching!"); - else info("Distributed caching is disabled. Will use standard distributed version without forced caching!"); - -#ifndef NDEBUG - //check tests enabled value - if (this->distributed_tests_enabled==true) - { - warning("\nWill perform distributed tests! Beware that this decreases the performance"); - distributed::test=true; - } -#else - //check tests enabled value - if (this->distributed_tests_enabled==true) - { - warning("\nDistributed tests only abvailable in debug mode!"); - distributed::test=false; - } -#endif - - //check timing values - if (this->message_timings_enabled==true) - { - info("Will print timings of MPI-Messages! This is used to find bottlenecks!"); - distributed::test_send_receive_times=true; - } - //set timing threshold - distributed::min_threshold=this->message_timings_threshold; - - if (this->rpc_timings_enabled==true) - { - info("Will print run-times of processing RPC_process_related_viewgrams_gradient for every slave! This will give an idea of the parallelization effect!"); - distributed::rpc_time=true; - } - +# endif +#else + // check caching enabled value + if (this->distributed_cache_enabled == true) + info("Will use distributed caching!"); + else + info("Distributed caching is disabled. Will use standard distributed version without forced caching!"); + +# ifndef NDEBUG + // check tests enabled value + if (this->distributed_tests_enabled == true) { + warning("\nWill perform distributed tests! Beware that this decreases the performance"); + distributed::test = true; + } +# else + // check tests enabled value + if (this->distributed_tests_enabled == true) { + warning("\nDistributed tests only abvailable in debug mode!"); + distributed::test = false; + } +# endif + + // check timing values + if (this->message_timings_enabled == true) { + info("Will print timings of MPI-Messages! This is used to find bottlenecks!"); + distributed::test_send_receive_times = true; + } + // set timing threshold + distributed::min_threshold = this->message_timings_threshold; + + if (this->rpc_timings_enabled == true) { + info("Will print run-times of processing RPC_process_related_viewgrams_gradient for every slave! This will give an idea of " + "the parallelization effect!"); + distributed::rpc_time = true; + } + #endif - //this->already_setup = false; - return false; + // this->already_setup = false; + return false; } template -PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -PoissonLogLikelihoodWithLinearModelForMeanAndProjData() -{ +PoissonLogLikelihoodWithLinearModelForMeanAndProjData::PoissonLogLikelihoodWithLinearModelForMeanAndProjData() { this->set_defaults(); } template -PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -~PoissonLogLikelihoodWithLinearModelForMeanAndProjData() -{ +PoissonLogLikelihoodWithLinearModelForMeanAndProjData::~PoissonLogLikelihoodWithLinearModelForMeanAndProjData() { end_distributable_computation(); } template -TargetT * -PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -construct_target_ptr() const -{ - return - target_parameter_parser.create(this->get_input_data()); +TargetT* +PoissonLogLikelihoodWithLinearModelForMeanAndProjData::construct_target_ptr() const { + return target_parameter_parser.create(this->get_input_data()); } /*************************************************************** get_ functions ***************************************************************/ template -const ProjData& -PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -get_proj_data() const -{ return *this->proj_data_sptr; } +const ProjData& +PoissonLogLikelihoodWithLinearModelForMeanAndProjData::get_proj_data() const { + return *this->proj_data_sptr; +} template -const shared_ptr& -PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -get_proj_data_sptr() const -{ return this->proj_data_sptr; } +const shared_ptr& +PoissonLogLikelihoodWithLinearModelForMeanAndProjData::get_proj_data_sptr() const { + return this->proj_data_sptr; +} template -const int -PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -get_max_segment_num_to_process() const -{ return this->max_segment_num_to_process; } +const int +PoissonLogLikelihoodWithLinearModelForMeanAndProjData::get_max_segment_num_to_process() const { + return this->max_segment_num_to_process; +} template const int -PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -get_max_timing_pos_num_to_process() const -{ return this->max_timing_pos_num_to_process; } +PoissonLogLikelihoodWithLinearModelForMeanAndProjData::get_max_timing_pos_num_to_process() const { + return this->max_timing_pos_num_to_process; +} template -const bool -PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -get_zero_seg0_end_planes() const -{ return this->zero_seg0_end_planes; } +const bool +PoissonLogLikelihoodWithLinearModelForMeanAndProjData::get_zero_seg0_end_planes() const { + return this->zero_seg0_end_planes; +} template -const ProjData& -PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -get_additive_proj_data() const -{ return *this->additive_proj_data_sptr; } +const ProjData& +PoissonLogLikelihoodWithLinearModelForMeanAndProjData::get_additive_proj_data() const { + return *this->additive_proj_data_sptr; +} template -const shared_ptr& -PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -get_additive_proj_data_sptr() const -{ return this->additive_proj_data_sptr; } +const shared_ptr& +PoissonLogLikelihoodWithLinearModelForMeanAndProjData::get_additive_proj_data_sptr() const { + return this->additive_proj_data_sptr; +} template -const ProjectorByBinPair& -PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -get_projector_pair() const -{ return *this->projector_pair_ptr; } +const ProjectorByBinPair& +PoissonLogLikelihoodWithLinearModelForMeanAndProjData::get_projector_pair() const { + return *this->projector_pair_ptr; +} template -const shared_ptr& -PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -get_projector_pair_sptr() const -{ return this->projector_pair_ptr; } +const shared_ptr& +PoissonLogLikelihoodWithLinearModelForMeanAndProjData::get_projector_pair_sptr() const { + return this->projector_pair_ptr; +} template -const int -PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -get_time_frame_num() const -{ return this->frame_num; } +const int +PoissonLogLikelihoodWithLinearModelForMeanAndProjData::get_time_frame_num() const { + return this->frame_num; +} template -const TimeFrameDefinitions& -PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -get_time_frame_definitions() const -{ return this->frame_defs; } +const TimeFrameDefinitions& +PoissonLogLikelihoodWithLinearModelForMeanAndProjData::get_time_frame_definitions() const { + return this->frame_defs; +} template -const BinNormalisation& -PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -get_normalisation() const -{ return *this->normalisation_sptr; } +const BinNormalisation& +PoissonLogLikelihoodWithLinearModelForMeanAndProjData::get_normalisation() const { + return *this->normalisation_sptr; +} template -const shared_ptr& -PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -get_normalisation_sptr() const -{ return this->normalisation_sptr; } - +const shared_ptr& +PoissonLogLikelihoodWithLinearModelForMeanAndProjData::get_normalisation_sptr() const { + return this->normalisation_sptr; +} /*************************************************************** set_ functions ***************************************************************/ -template +template int -PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -set_num_subsets(const int new_num_subsets) -{ - this->num_subsets = std::max(new_num_subsets,1); +PoissonLogLikelihoodWithLinearModelForMeanAndProjData::set_num_subsets(const int new_num_subsets) { + this->num_subsets = std::max(new_num_subsets, 1); return this->num_subsets; - } -template +template void -PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -set_proj_data_sptr(const shared_ptr& arg) -{ +PoissonLogLikelihoodWithLinearModelForMeanAndProjData::set_proj_data_sptr(const shared_ptr& arg) { this->proj_data_sptr = arg; } -template +template void -PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -set_max_segment_num_to_process(const int arg) -{ +PoissonLogLikelihoodWithLinearModelForMeanAndProjData::set_max_segment_num_to_process(const int arg) { this->max_segment_num_to_process = arg; } -template +template void -PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -set_max_timing_pos_num_to_process(const int arg) -{ +PoissonLogLikelihoodWithLinearModelForMeanAndProjData::set_max_timing_pos_num_to_process(const int arg) { this->max_timing_pos_num_to_process = arg; } -template +template void -PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -set_zero_seg0_end_planes(const bool arg) -{ +PoissonLogLikelihoodWithLinearModelForMeanAndProjData::set_zero_seg0_end_planes(const bool arg) { this->zero_seg0_end_planes = arg; } -template +template void -PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -set_additive_proj_data_sptr(const shared_ptr &arg) -{ - this->additive_proj_data_sptr = dynamic_pointer_cast(arg); +PoissonLogLikelihoodWithLinearModelForMeanAndProjData::set_additive_proj_data_sptr(const shared_ptr& arg) { + this->additive_proj_data_sptr = dynamic_pointer_cast(arg); } -template +template void -PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -set_projector_pair_sptr(const shared_ptr& arg) -{ +PoissonLogLikelihoodWithLinearModelForMeanAndProjData::set_projector_pair_sptr( + const shared_ptr& arg) { this->projector_pair_ptr = arg; } -template +template void -PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -set_frame_num(const int arg) -{ +PoissonLogLikelihoodWithLinearModelForMeanAndProjData::set_frame_num(const int arg) { this->frame_num = arg; } -template +template void -PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -set_frame_definitions(const TimeFrameDefinitions& arg) -{ +PoissonLogLikelihoodWithLinearModelForMeanAndProjData::set_frame_definitions(const TimeFrameDefinitions& arg) { this->frame_defs = arg; } -template +template void -PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -set_normalisation_sptr(const shared_ptr& arg) -{ +PoissonLogLikelihoodWithLinearModelForMeanAndProjData::set_normalisation_sptr(const shared_ptr& arg) { this->normalisation_sptr = arg; } -template +template void -PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -set_input_data(const shared_ptr & arg) -{ - this->proj_data_sptr = dynamic_pointer_cast(arg); +PoissonLogLikelihoodWithLinearModelForMeanAndProjData::set_input_data(const shared_ptr& arg) { + this->proj_data_sptr = dynamic_pointer_cast(arg); } -template +template const ProjData& -PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -get_input_data() const -{ +PoissonLogLikelihoodWithLinearModelForMeanAndProjData::get_input_data() const { return *this->proj_data_sptr; } - /*************************************************************** subset balancing ***************************************************************/ -template +template bool -PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -actual_subsets_are_approximately_balanced(std::string& warning_message) const -{ - assert(this->num_subsets>0); +PoissonLogLikelihoodWithLinearModelForMeanAndProjData::actual_subsets_are_approximately_balanced( + std::string& warning_message) const { + assert(this->num_subsets > 0); const DataSymmetriesForViewSegmentNumbers& symmetries = - *this->projector_pair_ptr->get_back_projector_sptr()->get_symmetries_used(); + *this->projector_pair_ptr->get_back_projector_sptr()->get_symmetries_used(); - Array<1,int> num_vs_in_subset(this->num_subsets); + Array<1, int> num_vs_in_subset(this->num_subsets); num_vs_in_subset.fill(0); - for (int subset_num=0; subset_numnum_subsets; ++subset_num) - { - for (int segment_num = -this->max_segment_num_to_process; - segment_num <= this->max_segment_num_to_process; - ++segment_num) - for (int view_num = this->proj_data_sptr->get_min_view_num() + subset_num; - view_num <= this->proj_data_sptr->get_max_view_num(); - view_num += this->num_subsets) - { - const ViewSegmentNumbers view_segment_num(view_num, segment_num); - if (!symmetries.is_basic(view_segment_num)) - continue; - num_vs_in_subset[subset_num] += - symmetries.num_related_view_segment_numbers(view_segment_num); - } - } - for (int subset_num=1; subset_numnum_subsets; ++subset_num) - { - if(num_vs_in_subset[subset_num] != num_vs_in_subset[0]) - { - std::stringstream str(warning_message); - str <<"Number of subsets is such that subsets will be very unbalanced.\n" - << "Number of viewgrams in each subset would be:\n" - << num_vs_in_subset - << "\nEither reduce the number of symmetries used by the projector, or\n" - "change the number of subsets. It usually should be a divisor of\n" - << this->proj_data_sptr->get_num_views() - << "/4 (or if that's not an integer, a divisor of " - << this->proj_data_sptr->get_num_views() - << "/2 or " - << this->proj_data_sptr->get_num_views() - << ").\n"; - warning_message = str.str(); - return false; - } + for (int subset_num = 0; subset_num < this->num_subsets; ++subset_num) { + for (int segment_num = -this->max_segment_num_to_process; segment_num <= this->max_segment_num_to_process; ++segment_num) + for (int view_num = this->proj_data_sptr->get_min_view_num() + subset_num; + view_num <= this->proj_data_sptr->get_max_view_num(); view_num += this->num_subsets) { + const ViewSegmentNumbers view_segment_num(view_num, segment_num); + if (!symmetries.is_basic(view_segment_num)) + continue; + num_vs_in_subset[subset_num] += symmetries.num_related_view_segment_numbers(view_segment_num); + } + } + for (int subset_num = 1; subset_num < this->num_subsets; ++subset_num) { + if (num_vs_in_subset[subset_num] != num_vs_in_subset[0]) { + std::stringstream str(warning_message); + str << "Number of subsets is such that subsets will be very unbalanced.\n" + << "Number of viewgrams in each subset would be:\n" + << num_vs_in_subset + << "\nEither reduce the number of symmetries used by the projector, or\n" + "change the number of subsets. It usually should be a divisor of\n" + << this->proj_data_sptr->get_num_views() << "/4 (or if that's not an integer, a divisor of " + << this->proj_data_sptr->get_num_views() << "/2 or " << this->proj_data_sptr->get_num_views() << ").\n"; + warning_message = str.str(); + return false; } + } return true; } /*************************************************************** set_up() ***************************************************************/ -template -Succeeded -PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -set_up_before_sensitivity(shared_ptr const& target_sptr) -{ +template +Succeeded +PoissonLogLikelihoodWithLinearModelForMeanAndProjData::set_up_before_sensitivity( + shared_ptr const& target_sptr) { if (is_null_ptr(this->proj_data_sptr)) - error("you need to set the input data before calling set_up"); + error("you need to set the input data before calling set_up"); - if (this->max_segment_num_to_process==-1) - this->max_segment_num_to_process = - this->proj_data_sptr->get_max_segment_num(); + if (this->max_segment_num_to_process == -1) + this->max_segment_num_to_process = this->proj_data_sptr->get_max_segment_num(); - if (this->max_segment_num_to_process > this->proj_data_sptr->get_max_segment_num()) - { - error("max_segment_num_to_process (%d) is too large", - this->max_segment_num_to_process); - return Succeeded::no; - } + if (this->max_segment_num_to_process > this->proj_data_sptr->get_max_segment_num()) { + error("max_segment_num_to_process (%d) is too large", this->max_segment_num_to_process); + return Succeeded::no; + } - this->max_timing_pos_num_to_process = - this->proj_data_sptr->get_max_tof_pos_num(); + this->max_timing_pos_num_to_process = this->proj_data_sptr->get_max_tof_pos_num(); shared_ptr proj_data_info_sptr(this->proj_data_sptr->get_proj_data_info_sptr()->clone()); @@ -575,44 +506,39 @@ set_up_before_sensitivity(shared_ptr const& target_sptr) reduce_segment_range(-this->max_segment_num_to_process, +this->max_segment_num_to_process); #endif - if (is_null_ptr(this->projector_pair_ptr)) - { error("You need to specify a projector pair"); return Succeeded::no; } + if (is_null_ptr(this->projector_pair_ptr)) { + error("You need to specify a projector pair"); + return Succeeded::no; + } // set projectors to be used for the calculations - setup_distributable_computation(this->projector_pair_ptr, - this->proj_data_sptr->get_exam_info_sptr(), - this->proj_data_sptr->get_proj_data_info_sptr(), - target_sptr, - zero_seg0_end_planes, + setup_distributable_computation(this->projector_pair_ptr, this->proj_data_sptr->get_exam_info_sptr(), + this->proj_data_sptr->get_proj_data_info_sptr(), target_sptr, zero_seg0_end_planes, distributed_cache_enabled); - + #ifdef STIR_MPI - //set up distributed caching object - if (distributed_cache_enabled) - { - this->caching_info_ptr = new DistributedCachingInformation(distributed::num_processors); - } - else caching_info_ptr = NULL; -#else - //non parallel version + // set up distributed caching object + if (distributed_cache_enabled) { + this->caching_info_ptr = new DistributedCachingInformation(distributed::num_processors); + } else + caching_info_ptr = NULL; +#else + // non parallel version caching_info_ptr = NULL; -#endif +#endif - this->projector_pair_ptr->set_up(proj_data_info_sptr, - target_sptr); + this->projector_pair_ptr->set_up(proj_data_info_sptr, target_sptr); // sets non-tof backprojector for sensitivity calculation (clone of the back_projector + set projdatainfo to non-tof) this->sens_backprojector_sptr.reset(projector_pair_ptr->get_back_projector_sptr()->clone()); if (!this->use_tofsens) - this->sens_backprojector_sptr->set_up(proj_data_info_sptr->create_non_tof_clone(), target_sptr); - + this->sens_backprojector_sptr->set_up(proj_data_info_sptr->create_non_tof_clone(), target_sptr); + // TODO check compatibility between symmetries for forward and backprojector - this->symmetries_sptr.reset( - this->projector_pair_ptr->get_back_projector_sptr()->get_symmetries_used()->clone()); + this->symmetries_sptr.reset(this->projector_pair_ptr->get_back_projector_sptr()->get_symmetries_used()->clone()); - if (is_null_ptr(this->normalisation_sptr)) - { + if (is_null_ptr(this->normalisation_sptr)) { error("Invalid normalisation object"); return Succeeded::no; } @@ -620,18 +546,15 @@ set_up_before_sensitivity(shared_ptr const& target_sptr) if (this->normalisation_sptr->set_up(proj_data_sptr->get_exam_info_sptr(), proj_data_info_sptr) == Succeeded::no) return Succeeded::no; - if (frame_num<=0) - { - error("frame_num should be >= 1"); - return Succeeded::no; - } + if (frame_num <= 0) { + error("frame_num should be >= 1"); + return Succeeded::no; + } - if (static_cast(frame_num)> frame_defs.get_num_frames()) - { - error("frame_num is %d, but should be less than the number of frames %d.", - frame_num, frame_defs.get_num_frames()); - return Succeeded::no; - } + if (static_cast(frame_num) > frame_defs.get_num_frames()) { + error("frame_num is %d, but should be less than the number of frames %d.", frame_num, frame_defs.get_num_frames()); + return Succeeded::no; + } return Succeeded::yes; } @@ -640,64 +563,34 @@ set_up_before_sensitivity(shared_ptr const& target_sptr) functions that compute the value/gradient of the objective function etc ***************************************************************/ -template +template void -PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -compute_sub_gradient_without_penalty_plus_sensitivity(TargetT& gradient, - const TargetT ¤t_estimate, - const int subset_num) -{ - assert(subset_num>=0); - assert(subset_numnum_subsets); - distributable_compute_gradient(this->projector_pair_ptr->get_forward_projector_sptr(), - this->projector_pair_ptr->get_back_projector_sptr(), - this->symmetries_sptr, - gradient, - current_estimate, - this->proj_data_sptr, - subset_num, - this->num_subsets, - -this->max_segment_num_to_process, - this->max_segment_num_to_process, - this->zero_seg0_end_planes!=0, - NULL, - this->additive_proj_data_sptr - , caching_info_ptr, - -this->max_timing_pos_num_to_process, - this->max_timing_pos_num_to_process - ); - - +PoissonLogLikelihoodWithLinearModelForMeanAndProjData::compute_sub_gradient_without_penalty_plus_sensitivity( + TargetT& gradient, const TargetT& current_estimate, const int subset_num) { + assert(subset_num >= 0); + assert(subset_num < this->num_subsets); + distributable_compute_gradient( + this->projector_pair_ptr->get_forward_projector_sptr(), this->projector_pair_ptr->get_back_projector_sptr(), + this->symmetries_sptr, gradient, current_estimate, this->proj_data_sptr, subset_num, this->num_subsets, + -this->max_segment_num_to_process, this->max_segment_num_to_process, this->zero_seg0_end_planes != 0, NULL, + this->additive_proj_data_sptr, caching_info_ptr, -this->max_timing_pos_num_to_process, this->max_timing_pos_num_to_process); } +template +double +PoissonLogLikelihoodWithLinearModelForMeanAndProjData::actual_compute_objective_function_without_penalty( + const TargetT& current_estimate, const int subset_num) { + double accum = 0.; + + distributable_accumulate_loglikelihood( + this->projector_pair_ptr->get_forward_projector_sptr(), this->projector_pair_ptr->get_back_projector_sptr(), + this->symmetries_sptr, current_estimate, this->proj_data_sptr, subset_num, this->get_num_subsets(), + -this->max_segment_num_to_process, this->max_segment_num_to_process, this->zero_seg0_end_planes != 0, &accum, + this->additive_proj_data_sptr, this->normalisation_sptr, + this->get_time_frame_definitions().get_start_time(this->get_time_frame_num()), + this->get_time_frame_definitions().get_end_time(this->get_time_frame_num()), this->caching_info_ptr, + -this->max_timing_pos_num_to_process, this->max_timing_pos_num_to_process); -template -double -PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -actual_compute_objective_function_without_penalty(const TargetT& current_estimate, - const int subset_num) -{ - double accum=0.; - - distributable_accumulate_loglikelihood(this->projector_pair_ptr->get_forward_projector_sptr(), - this->projector_pair_ptr->get_back_projector_sptr(), - this->symmetries_sptr, - current_estimate, - this->proj_data_sptr, - subset_num, this->get_num_subsets(), - -this->max_segment_num_to_process, - this->max_segment_num_to_process, - this->zero_seg0_end_planes != 0, &accum, - this->additive_proj_data_sptr, - this->normalisation_sptr, - this->get_time_frame_definitions().get_start_time(this->get_time_frame_num()), - this->get_time_frame_definitions().get_end_time(this->get_time_frame_num()), - this->caching_info_ptr, - -this->max_timing_pos_num_to_process, - this->max_timing_pos_num_to_process - ); - - return accum; } @@ -744,66 +637,51 @@ sum_projection_data() const #endif -template +template void -PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -add_subset_sensitivity(TargetT& sensitivity, const int subset_num) const -{ +PoissonLogLikelihoodWithLinearModelForMeanAndProjData::add_subset_sensitivity(TargetT& sensitivity, + const int subset_num) const { const int min_segment_num = -this->max_segment_num_to_process; const int max_segment_num = this->max_segment_num_to_process; #if 1 - shared_ptr sensitivity_this_subset_sptr(sensitivity.clone()); - shared_ptr sens_proj_data_sptr; - // have to create a ProjData object filled with 1 here because otherwise zero_seg0_endplanes will not be effective - if (!this->use_tofsens) - sens_proj_data_sptr.reset(new ProjDataInMemory(this->proj_data_sptr->get_exam_info_sptr(), this->proj_data_sptr->get_proj_data_info_sptr()->create_non_tof_clone())); - else - sens_proj_data_sptr.reset(new ProjDataInMemory(this->proj_data_sptr->get_exam_info_sptr(), this->proj_data_sptr->get_proj_data_info_sptr())); - sens_proj_data_sptr->fill(1.0F); - - distributable_sensitivity_computation(this->projector_pair_ptr->get_forward_projector_sptr(), - this->sens_backprojector_sptr, - this->symmetries_sptr, - *sensitivity_this_subset_sptr, - sensitivity, - sens_proj_data_sptr, - subset_num, - this->num_subsets, - min_segment_num, - max_segment_num, - this->zero_seg0_end_planes!=0, - NULL, - this->additive_proj_data_sptr, - this->normalisation_sptr, - this->get_time_frame_definitions().get_start_time(this->get_time_frame_num()), - this->get_time_frame_definitions().get_end_time(this->get_time_frame_num()), - this->caching_info_ptr, - use_tofsens ? -this->max_timing_pos_num_to_process : 0, - use_tofsens ? this->max_timing_pos_num_to_process : 0); - - std::transform(sensitivity.begin_all(), sensitivity.end_all(), - sensitivity_this_subset_sptr->begin_all(), sensitivity.begin_all(), - std::plus()); + shared_ptr sensitivity_this_subset_sptr(sensitivity.clone()); + shared_ptr sens_proj_data_sptr; + // have to create a ProjData object filled with 1 here because otherwise zero_seg0_endplanes will not be effective + if (!this->use_tofsens) + sens_proj_data_sptr.reset(new ProjDataInMemory(this->proj_data_sptr->get_exam_info_sptr(), + this->proj_data_sptr->get_proj_data_info_sptr()->create_non_tof_clone())); + else + sens_proj_data_sptr.reset( + new ProjDataInMemory(this->proj_data_sptr->get_exam_info_sptr(), this->proj_data_sptr->get_proj_data_info_sptr())); + sens_proj_data_sptr->fill(1.0F); + + distributable_sensitivity_computation( + this->projector_pair_ptr->get_forward_projector_sptr(), this->sens_backprojector_sptr, this->symmetries_sptr, + *sensitivity_this_subset_sptr, sensitivity, sens_proj_data_sptr, subset_num, this->num_subsets, min_segment_num, + max_segment_num, this->zero_seg0_end_planes != 0, NULL, this->additive_proj_data_sptr, this->normalisation_sptr, + this->get_time_frame_definitions().get_start_time(this->get_time_frame_num()), + this->get_time_frame_definitions().get_end_time(this->get_time_frame_num()), this->caching_info_ptr, + use_tofsens ? -this->max_timing_pos_num_to_process : 0, use_tofsens ? this->max_timing_pos_num_to_process : 0); + + std::transform(sensitivity.begin_all(), sensitivity.end_all(), sensitivity_this_subset_sptr->begin_all(), + sensitivity.begin_all(), std::plus()); #else // warning: has to be same as subset scheme used as in distributable_computation - for (int segment_num = min_segment_num; segment_num <= max_segment_num; ++segment_num) - { - //CPUTimer timer; - //timer.start(); - - for (int view = this->proj_data_sptr->get_min_view_num() + subset_num; - view <= this->proj_data_sptr->get_max_view_num(); - view += this->num_subsets) - { + for (int segment_num = min_segment_num; segment_num <= max_segment_num; ++segment_num) { + // CPUTimer timer; + // timer.start(); + + for (int view = this->proj_data_sptr->get_min_view_num() + subset_num; view <= this->proj_data_sptr->get_max_view_num(); + view += this->num_subsets) { const ViewSegmentNumbers view_segment_num(view, segment_num); - + if (!symmetries_sptr->is_basic(view_segment_num)) continue; this->add_view_seg_to_sensitivity(sensitivity, view_segment_num); } - // cerr< - std::unique_ptr - PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: - get_exam_info_uptr_for_target() const -{ - auto exam_info_uptr = this->get_exam_info_uptr_for_target(); - if (auto norm_ptr = dynamic_cast(get_normalisation_sptr().get())) - { - exam_info_uptr->set_calibration_factor(norm_ptr->get_calibration_factor()); - // somehow tell the image that it's calibrated - } - else - { - exam_info_uptr->set_calibration_factor(-1.F); - // somehow tell the image that it's not calibrated - } - return exam_info_uptr; +template +std::unique_ptr +PoissonLogLikelihoodWithLinearModelForMeanAndProjData::get_exam_info_uptr_for_target() const { + auto exam_info_uptr = this->get_exam_info_uptr_for_target(); + if (auto norm_ptr = dynamic_cast(get_normalisation_sptr().get())) { + exam_info_uptr->set_calibration_factor(norm_ptr->get_calibration_factor()); + // somehow tell the image that it's calibrated + } else { + exam_info_uptr->set_calibration_factor(-1.F); + // somehow tell the image that it's not calibrated + } + return exam_info_uptr; } -template +template Succeeded -PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -actual_add_multiplication_with_approximate_sub_Hessian_without_penalty(TargetT& output, - const TargetT& input, - const int subset_num) const -{ +PoissonLogLikelihoodWithLinearModelForMeanAndProjData< + TargetT>::actual_add_multiplication_with_approximate_sub_Hessian_without_penalty(TargetT& output, const TargetT& input, + const int subset_num) const { { std::string explanation; - if (!input.has_same_characteristics(this->get_sensitivity(), - explanation)) - { - error("PoissonLogLikelihoodWithLinearModelForMeanAndProjData:\n" - "sensitivity and input for add_multiplication_with_approximate_Hessian_without_penalty\n" - "should have the same characteristics.\n%s", - explanation.c_str()); - return Succeeded::no; - } - } + if (!input.has_same_characteristics(this->get_sensitivity(), explanation)) { + error("PoissonLogLikelihoodWithLinearModelForMeanAndProjData:\n" + "sensitivity and input for add_multiplication_with_approximate_Hessian_without_penalty\n" + "should have the same characteristics.\n%s", + explanation.c_str()); + return Succeeded::no; + } + } - shared_ptr symmetries_sptr( - this->get_projector_pair().get_symmetries_used()->clone()); + shared_ptr symmetries_sptr(this->get_projector_pair().get_symmetries_used()->clone()); - const double start_time = - this->get_time_frame_definitions().get_start_time(this->get_time_frame_num()); - const double end_time = - this->get_time_frame_definitions().get_end_time(this->get_time_frame_num()); + const double start_time = this->get_time_frame_definitions().get_start_time(this->get_time_frame_num()); + const double end_time = this->get_time_frame_definitions().get_end_time(this->get_time_frame_num()); this->get_projector_pair().get_forward_projector_sptr()->set_input(input); this->get_projector_pair().get_back_projector_sptr()->start_accumulating_in_new_target(); - const std::vector vs_nums_to_process = - detail::find_basic_vs_nums_in_subset(* this->get_proj_data().get_proj_data_info_sptr(), - *symmetries_sptr, - -this->get_max_segment_num_to_process(), - this->get_max_segment_num_to_process(), - subset_num, this->get_num_subsets()); + const std::vector vs_nums_to_process = detail::find_basic_vs_nums_in_subset( + *this->get_proj_data().get_proj_data_info_sptr(), *symmetries_sptr, -this->get_max_segment_num_to_process(), + this->get_max_segment_num_to_process(), subset_num, this->get_num_subsets()); info("Forward projecting input image.", 2); #ifdef STIR_OPENMP -#pragma omp parallel for schedule(runtime) +# pragma omp parallel for schedule(runtime) #endif // note: older versions of openmp need an int as loop - for (int i=0; i(vs_nums_to_process.size()); ++i) - { + for (int i = 0; i < static_cast(vs_nums_to_process.size()); ++i) { #ifdef STIR_OPENMP - const int thread_num = omp_get_thread_num(); - info(boost::format("Thread %d/%d calculating segment_num: %d, view_num: %d") - % thread_num % omp_get_num_threads() - % vs_nums_to_process[i].segment_num() % vs_nums_to_process[i].view_num(), 2); + const int thread_num = omp_get_thread_num(); + info(boost::format("Thread %d/%d calculating segment_num: %d, view_num: %d") % thread_num % omp_get_num_threads() % + vs_nums_to_process[i].segment_num() % vs_nums_to_process[i].view_num(), + 2); #else - info(boost::format("calculating segment_num: %d, view_num: %d") - % vs_nums_to_process[i].segment_num() % vs_nums_to_process[i].view_num(), 2); + info(boost::format("calculating segment_num: %d, view_num: %d") % vs_nums_to_process[i].segment_num() % + vs_nums_to_process[i].view_num(), + 2); #endif - const ViewSegmentNumbers view_segment_num=vs_nums_to_process[i]; - - // first compute data-term: y*norm^2 - RelatedViewgrams viewgrams = - this->get_proj_data().get_related_viewgrams(view_segment_num, symmetries_sptr, false); - // TODO add 1 for 1/(y+1) approximation - - this->get_normalisation().apply(viewgrams, start_time, end_time); - - // smooth TODO - - RelatedViewgrams tmp_viewgrams; - // set tmp_viewgrams to geometric forward projection of input - { - tmp_viewgrams = this->get_proj_data().get_empty_related_viewgrams(view_segment_num, symmetries_sptr); - this->get_projector_pair().get_forward_projector_sptr()-> - forward_project(tmp_viewgrams); - } - - // now divide by the data term - { - int tmp1=0, tmp2=0;// ignore counters returned by divide_and_truncate - divide_and_truncate(tmp_viewgrams, viewgrams, 0, tmp1, tmp2); - } - - // back-project - this->get_projector_pair().get_back_projector_sptr()-> - back_project(tmp_viewgrams); + const ViewSegmentNumbers view_segment_num = vs_nums_to_process[i]; + + // first compute data-term: y*norm^2 + RelatedViewgrams viewgrams = this->get_proj_data().get_related_viewgrams(view_segment_num, symmetries_sptr, false); + // TODO add 1 for 1/(y+1) approximation + + this->get_normalisation().apply(viewgrams, start_time, end_time); + + // smooth TODO + + RelatedViewgrams tmp_viewgrams; + // set tmp_viewgrams to geometric forward projection of input + { + tmp_viewgrams = this->get_proj_data().get_empty_related_viewgrams(view_segment_num, symmetries_sptr); + this->get_projector_pair().get_forward_projector_sptr()->forward_project(tmp_viewgrams); + } + + // now divide by the data term + { + int tmp1 = 0, tmp2 = 0; // ignore counters returned by divide_and_truncate + divide_and_truncate(tmp_viewgrams, viewgrams, 0, tmp1, tmp2); + } + + // back-project + this->get_projector_pair().get_back_projector_sptr()->back_project(tmp_viewgrams); } // end of loop over view/segments shared_ptr tmp(output.get_empty_copy()); this->get_projector_pair().get_back_projector_sptr()->get_output(*tmp); // output += tmp; - std::transform(output.begin_all(), output.end_all(), - tmp->begin_all(), output.begin_all(), - std::plus()); + std::transform(output.begin_all(), output.end_all(), tmp->begin_all(), output.begin_all(), + std::plus()); return Succeeded::yes; } - -template +template Succeeded -PoissonLogLikelihoodWithLinearModelForMeanAndProjData:: -actual_accumulate_sub_Hessian_times_input_without_penalty(TargetT& output, - const TargetT& current_image_estimate, - const TargetT& input, - const int subset_num) const -{ +PoissonLogLikelihoodWithLinearModelForMeanAndProjData::actual_accumulate_sub_Hessian_times_input_without_penalty( + TargetT& output, const TargetT& current_image_estimate, const TargetT& input, const int subset_num) const { { // check characteristics std::string explanation; - if (!output.has_same_characteristics(this->get_sensitivity(),explanation)) - { + if (!output.has_same_characteristics(this->get_sensitivity(), explanation)) { error("PoissonLogLikelihoodWithLinearModelForMeanAndProjData:\n" "sensitivity and output for add_multiplication_with_approximate_Hessian_without_penalty\n" "should have the same characteristics.\n%s", @@ -980,8 +833,7 @@ actual_accumulate_sub_Hessian_times_input_without_penalty(TargetT& output, return Succeeded::no; } - if (!input.has_same_characteristics(this->get_sensitivity(),explanation)) - { + if (!input.has_same_characteristics(this->get_sensitivity(), explanation)) { error("PoissonLogLikelihoodWithLinearModelForMeanAndProjData:\n" "sensitivity and input for add_multiplication_with_approximate_Hessian_without_penalty\n" "should have the same characteristics.\n%s", @@ -989,8 +841,7 @@ actual_accumulate_sub_Hessian_times_input_without_penalty(TargetT& output, return Succeeded::no; } - if (!current_image_estimate.has_same_characteristics(this->get_sensitivity(),explanation)) - { + if (!current_image_estimate.has_same_characteristics(this->get_sensitivity(), explanation)) { error("PoissonLogLikelihoodWithLinearModelForMeanAndProjData:\n" "sensitivity and current_image_estimate for add_multiplication_with_approximate_Hessian_without_penalty\n" "should have the same characteristics.\n%s", @@ -999,70 +850,61 @@ actual_accumulate_sub_Hessian_times_input_without_penalty(TargetT& output, } } - shared_ptr symmetries_sptr( - this->get_projector_pair().get_symmetries_used()->clone()); + shared_ptr symmetries_sptr(this->get_projector_pair().get_symmetries_used()->clone()); this->get_projector_pair().get_forward_projector_sptr()->set_input(input); this->get_projector_pair().get_back_projector_sptr()->start_accumulating_in_new_target(); - const std::vector vs_nums_to_process = - detail::find_basic_vs_nums_in_subset(* this->get_proj_data().get_proj_data_info_sptr(), - *symmetries_sptr, - -this->get_max_segment_num_to_process(), - this->get_max_segment_num_to_process(), - subset_num, this->get_num_subsets()); - - + const std::vector vs_nums_to_process = detail::find_basic_vs_nums_in_subset( + *this->get_proj_data().get_proj_data_info_sptr(), *symmetries_sptr, -this->get_max_segment_num_to_process(), + this->get_max_segment_num_to_process(), subset_num, this->get_num_subsets()); // Create and populate the input_viewgrams_vec with empty values. // This is needed to make the order of the vector correct w.r.t vs_nums_to_process. - //OMP may mess this up + // OMP may mess this up // Try: std::vector> input_viewgrams_vec(vs_nums_to_process.size()); std::vector> input_viewgrams_vec; - for (int i=0; i(vs_nums_to_process.size()); ++i) - { + for (int i = 0; i < static_cast(vs_nums_to_process.size()); ++i) { const ViewSegmentNumbers view_segment_num = vs_nums_to_process[i]; input_viewgrams_vec.push_back(this->get_proj_data().get_empty_related_viewgrams(view_segment_num, symmetries_sptr)); } - // Forward project input image - info("Forward projecting input image.",2); + info("Forward projecting input image.", 2); #ifdef STIR_OPENMP -#pragma omp parallel for schedule(runtime) +# pragma omp parallel for schedule(runtime) #endif - for (int i=0; i(vs_nums_to_process.size()); ++i) - { // Loop over eah of the viewgrams in input_viewgrams_vec, forward projecting input into them + for (int i = 0; i < static_cast(vs_nums_to_process.size()); + ++i) { // Loop over eah of the viewgrams in input_viewgrams_vec, forward projecting input into them #ifdef STIR_OPENMP const int thread_num = omp_get_thread_num(); - info(boost::format("Thread %d/%d calculating segment_num: %d, view_num: %d") - % thread_num % omp_get_num_threads() - % vs_nums_to_process[i].segment_num() % vs_nums_to_process[i].view_num(), 2); + info(boost::format("Thread %d/%d calculating segment_num: %d, view_num: %d") % thread_num % omp_get_num_threads() % + vs_nums_to_process[i].segment_num() % vs_nums_to_process[i].view_num(), + 2); #else - info(boost::format("calculating segment_num: %d, view_num: %d") - % vs_nums_to_process[i].segment_num() % vs_nums_to_process[i].view_num(), 2); + info(boost::format("calculating segment_num: %d, view_num: %d") % vs_nums_to_process[i].segment_num() % + vs_nums_to_process[i].view_num(), + 2); #endif input_viewgrams_vec[i] = this->get_proj_data().get_empty_related_viewgrams(vs_nums_to_process[i], symmetries_sptr); this->get_projector_pair().get_forward_projector_sptr()->forward_project(input_viewgrams_vec[i]); } - - info("Forward projecting current image estimate and back projecting to output.", 2); this->get_projector_pair().get_forward_projector_sptr()->set_input(current_image_estimate); #ifdef STIR_OPENMP -#pragma omp parallel for schedule(runtime) +# pragma omp parallel for schedule(runtime) #endif - for (int i = 0; i < static_cast(vs_nums_to_process.size()); ++i) - { + for (int i = 0; i < static_cast(vs_nums_to_process.size()); ++i) { #ifdef STIR_OPENMP const int thread_num = omp_get_thread_num(); - info(boost::format("Thread %d/%d calculating segment_num: %d, view_num: %d") - % thread_num % omp_get_num_threads() - % vs_nums_to_process[i].segment_num() % vs_nums_to_process[i].view_num(), 2); + info(boost::format("Thread %d/%d calculating segment_num: %d, view_num: %d") % thread_num % omp_get_num_threads() % + vs_nums_to_process[i].segment_num() % vs_nums_to_process[i].view_num(), + 2); #else - info(boost::format("calculating segment_num: %d, view_num: %d") - % vs_nums_to_process[i].segment_num() % vs_nums_to_process[i].view_num(), 2); + info(boost::format("calculating segment_num: %d, view_num: %d") % vs_nums_to_process[i].segment_num() % + vs_nums_to_process[i].view_num(), + 2); #endif // Compute ybar_sq_viewgram = [ F(current_image_est) + additive ]^2 RelatedViewgrams ybar_sq_viewgram; @@ -1070,7 +912,7 @@ actual_accumulate_sub_Hessian_times_input_without_penalty(TargetT& output, ybar_sq_viewgram = this->get_proj_data().get_empty_related_viewgrams(vs_nums_to_process[i], symmetries_sptr); this->get_projector_pair().get_forward_projector_sptr()->forward_project(ybar_sq_viewgram); - //add additive sinogram to forward projection + // add additive sinogram to forward projection if (!(is_null_ptr(this->get_additive_proj_data_sptr()))) ybar_sq_viewgram += this->get_additive_proj_data().get_related_viewgrams(vs_nums_to_process[i], symmetries_sptr); // square ybar @@ -1083,22 +925,20 @@ actual_accumulate_sub_Hessian_times_input_without_penalty(TargetT& output, { // Mult input_viewgram final_viewgram *= input_viewgrams_vec[i]; - int tmp1 = 0, tmp2 = 0;// ignore counters returned by divide_and_truncate + int tmp1 = 0, tmp2 = 0; // ignore counters returned by divide_and_truncate // Divide final_viewgeam by ybar_sq_viewgram divide_and_truncate(final_viewgram, ybar_sq_viewgram, 0, tmp1, tmp2); } // back-project final_viewgram - this->get_projector_pair().get_back_projector_sptr()-> - back_project(final_viewgram); + this->get_projector_pair().get_back_projector_sptr()->back_project(final_viewgram); } // end of loop over view/segments shared_ptr tmp(output.get_empty_copy()); this->get_projector_pair().get_back_projector_sptr()->get_output(*tmp); // output += tmp; - std::transform(output.begin_all(), output.end_all(), - tmp->begin_all(), output.begin_all(), + std::transform(output.begin_all(), output.end_all(), tmp->begin_all(), output.begin_all(), std::plus()); return Succeeded::yes; @@ -1119,7 +959,7 @@ RPC_process_related_viewgrams_type RPC_process_related_viewgrams_accumulate_logl //! Call-back function for sensitivity_computation RPC_process_related_viewgrams_type RPC_process_related_viewgrams_sensitivity_computation; -#else +#else //! Call-back function for compute_gradient static RPC_process_related_viewgrams_type RPC_process_related_viewgrams_gradient; @@ -1130,137 +970,79 @@ static RPC_process_related_viewgrams_type RPC_process_related_viewgrams_accumula static RPC_process_related_viewgrams_type RPC_process_related_viewgrams_sensitivity_computation; #endif -void distributable_compute_gradient(const shared_ptr& forward_projector_sptr, - const shared_ptr& back_projector_sptr, - const shared_ptr& symmetries_sptr, - DiscretisedDensity<3,float>& output_image, - const DiscretisedDensity<3,float>& input_image, - const shared_ptr& proj_dat, - int subset_num, int num_subsets, - int min_segment, int max_segment, - bool zero_seg0_end_planes, - double* log_likelihood_ptr, - shared_ptr const& additive_binwise_correction, - DistributedCachingInformation* caching_info_ptr, - int min_timing_pos_num, int max_timing_pos_num - ) -{ - - distributable_computation(forward_projector_sptr, - back_projector_sptr, - symmetries_sptr, - &output_image, &input_image, - proj_dat, true, //i.e. do read projection data - subset_num, num_subsets, - min_segment, max_segment, - zero_seg0_end_planes, - log_likelihood_ptr, - additive_binwise_correction, - /* normalisation info to be ignored */ shared_ptr(), 0., 0., - &RPC_process_related_viewgrams_gradient, - caching_info_ptr, - min_timing_pos_num, max_timing_pos_num - ); +void +distributable_compute_gradient(const shared_ptr& forward_projector_sptr, + const shared_ptr& back_projector_sptr, + const shared_ptr& symmetries_sptr, + DiscretisedDensity<3, float>& output_image, const DiscretisedDensity<3, float>& input_image, + const shared_ptr& proj_dat, int subset_num, int num_subsets, int min_segment, + int max_segment, bool zero_seg0_end_planes, double* log_likelihood_ptr, + shared_ptr const& additive_binwise_correction, + DistributedCachingInformation* caching_info_ptr, int min_timing_pos_num, int max_timing_pos_num) { + + distributable_computation(forward_projector_sptr, back_projector_sptr, symmetries_sptr, &output_image, &input_image, proj_dat, + true, // i.e. do read projection data + subset_num, num_subsets, min_segment, max_segment, zero_seg0_end_planes, log_likelihood_ptr, + additive_binwise_correction, + /* normalisation info to be ignored */ shared_ptr(), 0., 0., + &RPC_process_related_viewgrams_gradient, caching_info_ptr, min_timing_pos_num, max_timing_pos_num); } +void +distributable_accumulate_loglikelihood( + const shared_ptr& forward_projector_sptr, const shared_ptr& back_projector_sptr, + const shared_ptr& symmetries_sptr, const DiscretisedDensity<3, float>& input_image, + const shared_ptr& proj_dat, int subset_num, int num_subsets, int min_segment, int max_segment, + bool zero_seg0_end_planes, double* log_likelihood_ptr, shared_ptr const& additive_binwise_correction, + shared_ptr const& normalisation_sptr, const double start_time_of_frame, const double end_time_of_frame, + DistributedCachingInformation* caching_info_ptr, int min_timing_pos_num, int max_timing_pos_num) -void distributable_accumulate_loglikelihood( - const shared_ptr& forward_projector_sptr, - const shared_ptr& back_projector_sptr, - const shared_ptr& symmetries_sptr, - const DiscretisedDensity<3,float>& input_image, - const shared_ptr& proj_dat, - int subset_num, int num_subsets, - int min_segment, int max_segment, - bool zero_seg0_end_planes, - double* log_likelihood_ptr, - shared_ptr const& additive_binwise_correction, - shared_ptr const& normalisation_sptr, - const double start_time_of_frame, - const double end_time_of_frame, - DistributedCachingInformation* caching_info_ptr, - int min_timing_pos_num, int max_timing_pos_num - ) - { - distributable_computation(forward_projector_sptr, - back_projector_sptr, - symmetries_sptr, - NULL, &input_image, - proj_dat, true, //i.e. do read projection data - subset_num, num_subsets, - min_segment, max_segment, - zero_seg0_end_planes, - log_likelihood_ptr, - additive_binwise_correction, - normalisation_sptr, - start_time_of_frame, - end_time_of_frame, - &RPC_process_related_viewgrams_accumulate_loglikelihood, - caching_info_ptr, - min_timing_pos_num, max_timing_pos_num - ); + distributable_computation(forward_projector_sptr, back_projector_sptr, symmetries_sptr, NULL, &input_image, proj_dat, + true, // i.e. do read projection data + subset_num, num_subsets, min_segment, max_segment, zero_seg0_end_planes, log_likelihood_ptr, + additive_binwise_correction, normalisation_sptr, start_time_of_frame, end_time_of_frame, + &RPC_process_related_viewgrams_accumulate_loglikelihood, caching_info_ptr, min_timing_pos_num, + max_timing_pos_num); } -void distributable_sensitivity_computation( - const shared_ptr& forward_projector_sptr, - const shared_ptr& back_projector_sptr, - const shared_ptr& symmetries_sptr, - DiscretisedDensity<3,float>& sensitivity, - const DiscretisedDensity<3,float>& input_image, - const shared_ptr& proj_dat, - int subset_num, int num_subsets, - int min_segment, int max_segment, - bool zero_seg0_end_planes, - double* log_likelihood_ptr, - shared_ptr const& additive_binwise_correction, - shared_ptr const& normalisation_sptr, - const double start_time_of_frame, - const double end_time_of_frame, - DistributedCachingInformation* caching_info_ptr, - int min_timing_pos_num, int max_timing_pos_num - ) +void +distributable_sensitivity_computation(const shared_ptr& forward_projector_sptr, + const shared_ptr& back_projector_sptr, + const shared_ptr& symmetries_sptr, + DiscretisedDensity<3, float>& sensitivity, const DiscretisedDensity<3, float>& input_image, + const shared_ptr& proj_dat, int subset_num, int num_subsets, int min_segment, + int max_segment, bool zero_seg0_end_planes, double* log_likelihood_ptr, + shared_ptr const& additive_binwise_correction, + shared_ptr const& normalisation_sptr, const double start_time_of_frame, + const double end_time_of_frame, DistributedCachingInformation* caching_info_ptr, + int min_timing_pos_num, int max_timing_pos_num) { - distributable_computation(forward_projector_sptr, - back_projector_sptr, - symmetries_sptr, - &sensitivity, &input_image, - proj_dat, true, //i.e. do read projection data - subset_num, num_subsets, - min_segment, max_segment, - zero_seg0_end_planes, - log_likelihood_ptr, - additive_binwise_correction, - normalisation_sptr, - start_time_of_frame, - end_time_of_frame, - &RPC_process_related_viewgrams_sensitivity_computation, - caching_info_ptr, - min_timing_pos_num, max_timing_pos_num - ); - + distributable_computation(forward_projector_sptr, back_projector_sptr, symmetries_sptr, &sensitivity, &input_image, proj_dat, + true, // i.e. do read projection data + subset_num, num_subsets, min_segment, max_segment, zero_seg0_end_planes, log_likelihood_ptr, + additive_binwise_correction, normalisation_sptr, start_time_of_frame, end_time_of_frame, + &RPC_process_related_viewgrams_sensitivity_computation, caching_info_ptr, min_timing_pos_num, + max_timing_pos_num); } - //////////// RPC functions - -void RPC_process_related_viewgrams_gradient( - const shared_ptr& forward_projector_sptr, - const shared_ptr& back_projector_sptr, - RelatedViewgrams* measured_viewgrams_ptr, - int& count, int& count2, double* log_likelihood_ptr /* = NULL */, - const RelatedViewgrams* additive_binwise_correction_ptr, - const RelatedViewgrams* mult_viewgrams_ptr) -{ +void +RPC_process_related_viewgrams_gradient(const shared_ptr& forward_projector_sptr, + const shared_ptr& back_projector_sptr, + RelatedViewgrams* measured_viewgrams_ptr, int& count, int& count2, + double* log_likelihood_ptr /* = NULL */, + const RelatedViewgrams* additive_binwise_correction_ptr, + const RelatedViewgrams* mult_viewgrams_ptr) { assert(measured_viewgrams_ptr != NULL); if (!is_null_ptr(mult_viewgrams_ptr)) error("Internal error: mult_viewgrams_ptr should be zero when computing gradient"); RelatedViewgrams estimated_viewgrams = measured_viewgrams_ptr->get_empty_copy(); - - /*if (distributed::first_iteration) + + /*if (distributed::first_iteration) { stir::RelatedViewgrams::iterator viewgrams_iter = measured_viewgrams_ptr->begin(); stir::RelatedViewgrams::iterator viewgrams_end = measured_viewgrams_ptr->end(); @@ -1268,9 +1050,9 @@ void RPC_process_related_viewgrams_gradient( { printf("\nSLAVE VIEWGRAM\n"); int pos=0; - for ( int tang_pos = -144 ;tang_pos <= 143 ;++tang_pos) + for ( int tang_pos = -144 ;tang_pos <= 143 ;++tang_pos) for ( int ax_pos = 0; ax_pos <= 62 ;++ax_pos) - { + { if (pos>3616 && pos <3632) printf("%f, ",(*viewgrams_iter)[ax_pos][tang_pos]); pos++; } @@ -1279,91 +1061,68 @@ void RPC_process_related_viewgrams_gradient( } */ forward_projector_sptr->forward_project(estimated_viewgrams); - - - - if (additive_binwise_correction_ptr != NULL) - { + + if (additive_binwise_correction_ptr != NULL) { estimated_viewgrams += (*additive_binwise_correction_ptr); } - - - - - // for sinogram division - + divide_and_truncate(*measured_viewgrams_ptr, estimated_viewgrams, rim_truncation_sino, count, count2, log_likelihood_ptr); - - back_projector_sptr->back_project(*measured_viewgrams_ptr); -}; + back_projector_sptr->back_project(*measured_viewgrams_ptr); +}; -void RPC_process_related_viewgrams_accumulate_loglikelihood( - const shared_ptr& forward_projector_sptr, - const shared_ptr& back_projector_sptr, - RelatedViewgrams* measured_viewgrams_ptr, - int& count, int& count2, double* log_likelihood_ptr, - const RelatedViewgrams* additive_binwise_correction_ptr, - const RelatedViewgrams* mult_viewgrams_ptr) -{ +void +RPC_process_related_viewgrams_accumulate_loglikelihood(const shared_ptr& forward_projector_sptr, + const shared_ptr& back_projector_sptr, + RelatedViewgrams* measured_viewgrams_ptr, int& count, int& count2, + double* log_likelihood_ptr, + const RelatedViewgrams* additive_binwise_correction_ptr, + const RelatedViewgrams* mult_viewgrams_ptr) { assert(measured_viewgrams_ptr != NULL); assert(log_likelihood_ptr != NULL); RelatedViewgrams estimated_viewgrams = measured_viewgrams_ptr->get_empty_copy(); forward_projector_sptr->forward_project(estimated_viewgrams); - - if (additive_binwise_correction_ptr != NULL) - { + + if (additive_binwise_correction_ptr != NULL) { estimated_viewgrams += (*additive_binwise_correction_ptr); }; - - if (mult_viewgrams_ptr != NULL) - { + + if (mult_viewgrams_ptr != NULL) { estimated_viewgrams *= (*mult_viewgrams_ptr); } - RelatedViewgrams::iterator meas_viewgrams_iter = - measured_viewgrams_ptr->begin(); - RelatedViewgrams::const_iterator est_viewgrams_iter = - estimated_viewgrams.begin(); + RelatedViewgrams::iterator meas_viewgrams_iter = measured_viewgrams_ptr->begin(); + RelatedViewgrams::const_iterator est_viewgrams_iter = estimated_viewgrams.begin(); // call function that does the actual work, it sits in recon_array_funtions.cxx (TODO) - for (; - meas_viewgrams_iter != measured_viewgrams_ptr->end(); - ++meas_viewgrams_iter, ++est_viewgrams_iter) - accumulate_loglikelihood(*meas_viewgrams_iter, - *est_viewgrams_iter, - rim_truncation_sino, log_likelihood_ptr); -}; - -void RPC_process_related_viewgrams_sensitivity_computation( - const shared_ptr& forward_projector_sptr, - const shared_ptr& back_projector_sptr, - RelatedViewgrams* measured_viewgrams_ptr, - int& count, int& count2, double* log_likelihood_ptr, - const RelatedViewgrams* additive_binwise_correction_ptr, - const RelatedViewgrams* mult_viewgrams_ptr) -{ + for (; meas_viewgrams_iter != measured_viewgrams_ptr->end(); ++meas_viewgrams_iter, ++est_viewgrams_iter) + accumulate_loglikelihood(*meas_viewgrams_iter, *est_viewgrams_iter, rim_truncation_sino, log_likelihood_ptr); +}; + +void +RPC_process_related_viewgrams_sensitivity_computation(const shared_ptr& forward_projector_sptr, + const shared_ptr& back_projector_sptr, + RelatedViewgrams* measured_viewgrams_ptr, int& count, int& count2, + double* log_likelihood_ptr, + const RelatedViewgrams* additive_binwise_correction_ptr, + const RelatedViewgrams* mult_viewgrams_ptr) { assert(measured_viewgrams_ptr != NULL); - if( mult_viewgrams_ptr ) - { + if (mult_viewgrams_ptr) { back_projector_sptr->back_project(*mult_viewgrams_ptr); - } - else - { + } else { back_projector_sptr->back_project(*measured_viewgrams_ptr); } - } -# ifdef _MSC_VER -// prevent warning message on instantiation of abstract class -# pragma warning(disable:4661) -# endif +#ifdef _MSC_VER +// prevent warning message on instantiation of abstract class +# pragma warning(disable : 4661) +#endif -template class PoissonLogLikelihoodWithLinearModelForMeanAndProjData >; +template class PoissonLogLikelihoodWithLinearModelForMeanAndProjData>; END_NAMESPACE_STIR diff --git a/src/recon_buildblock/PostsmoothingBackProjectorByBin.cxx b/src/recon_buildblock/PostsmoothingBackProjectorByBin.cxx index a13a51d980..fcb1460351 100644 --- a/src/recon_buildblock/PostsmoothingBackProjectorByBin.cxx +++ b/src/recon_buildblock/PostsmoothingBackProjectorByBin.cxx @@ -32,23 +32,16 @@ #include "stir/is_null_ptr.h" START_NAMESPACE_STIR -const char * const -PostsmoothingBackProjectorByBin::registered_name = - "Post Smoothing"; - +const char* const PostsmoothingBackProjectorByBin::registered_name = "Post Smoothing"; void -PostsmoothingBackProjectorByBin:: -set_defaults() -{ +PostsmoothingBackProjectorByBin::set_defaults() { original_back_projector_ptr.reset(); _post_data_processor_sptr.reset(); } void -PostsmoothingBackProjectorByBin:: -initialise_keymap() -{ +PostsmoothingBackProjectorByBin::initialise_keymap() { parser.add_start_key("Post Smoothing Back Projector Parameters"); parser.add_stop_key("End Post Smoothing Back Projector Parameters"); parser.add_parsing_key("Original Back projector type", &original_back_projector_ptr); @@ -56,118 +49,86 @@ initialise_keymap() } bool -PostsmoothingBackProjectorByBin:: -post_processing() -{ - if (is_null_ptr(original_back_projector_ptr)) - { +PostsmoothingBackProjectorByBin::post_processing() { + if (is_null_ptr(original_back_projector_ptr)) { warning("Pre Smoothing Back Projector: original back projector needs to be set"); return true; } return false; } -PostsmoothingBackProjectorByBin:: - PostsmoothingBackProjectorByBin() -{ - set_defaults(); -} +PostsmoothingBackProjectorByBin::PostsmoothingBackProjectorByBin() { set_defaults(); } -void PostsmoothingBackProjectorByBin:: -update_filtered_density_image(DiscretisedDensity<3, float> &density) -{ -// image_processor_ptr->apply(*filtered_density_sptr); -// density += *filtered_density_sptr; -// filtered_density_sptr->fill(0.f); +void +PostsmoothingBackProjectorByBin::update_filtered_density_image(DiscretisedDensity<3, float>& density) { + // image_processor_ptr->apply(*filtered_density_sptr); + // density += *filtered_density_sptr; + // filtered_density_sptr->fill(0.f); } BackProjectorByBin* -PostsmoothingBackProjectorByBin::get_original_back_projector_ptr() const -{ - return original_back_projector_ptr.get(); +PostsmoothingBackProjectorByBin::get_original_back_projector_ptr() const { + return original_back_projector_ptr.get(); } PostsmoothingBackProjectorByBin* -PostsmoothingBackProjectorByBin::clone() const -{ - PostsmoothingBackProjectorByBin* sptr(new PostsmoothingBackProjectorByBin(*this)); -// sptr->original_back_projector_ptr.reset(this->original_back_projector_ptr->clone()); - return sptr; +PostsmoothingBackProjectorByBin::clone() const { + PostsmoothingBackProjectorByBin* sptr(new PostsmoothingBackProjectorByBin(*this)); + // sptr->original_back_projector_ptr.reset(this->original_back_projector_ptr->clone()); + return sptr; } -void PostsmoothingBackProjectorByBin:: -init_filtered_density_image(DiscretisedDensity<3, float> &density) -{ - filtered_density_sptr.reset( - density.get_empty_discretised_density()); - assert(density.get_index_range() == filtered_density_sptr->get_index_range()); +void +PostsmoothingBackProjectorByBin::init_filtered_density_image(DiscretisedDensity<3, float>& density) { + filtered_density_sptr.reset(density.get_empty_discretised_density()); + assert(density.get_index_range() == filtered_density_sptr->get_index_range()); } -PostsmoothingBackProjectorByBin:: -PostsmoothingBackProjectorByBin( - const shared_ptr& original_back_projector_ptr, - const shared_ptr > >& image_processor_ptr) - : original_back_projector_ptr(original_back_projector_ptr) -{ - _post_data_processor_sptr = image_processor_ptr; +PostsmoothingBackProjectorByBin::PostsmoothingBackProjectorByBin( + const shared_ptr& original_back_projector_ptr, + const shared_ptr>>& image_processor_ptr) + : original_back_projector_ptr(original_back_projector_ptr) { + _post_data_processor_sptr = image_processor_ptr; } -PostsmoothingBackProjectorByBin:: -~PostsmoothingBackProjectorByBin() -{} +PostsmoothingBackProjectorByBin::~PostsmoothingBackProjectorByBin() {} void -PostsmoothingBackProjectorByBin:: -set_up(const shared_ptr& proj_data_info_ptr, - const shared_ptr >& image_info_ptr) -{ +PostsmoothingBackProjectorByBin::set_up(const shared_ptr& proj_data_info_ptr, + const shared_ptr>& image_info_ptr) { BackProjectorByBin::set_up(proj_data_info_ptr, image_info_ptr); original_back_projector_ptr->set_up(proj_data_info_ptr, image_info_ptr); } -const DataSymmetriesForViewSegmentNumbers * -PostsmoothingBackProjectorByBin:: -get_symmetries_used() const -{ +const DataSymmetriesForViewSegmentNumbers* +PostsmoothingBackProjectorByBin::get_symmetries_used() const { return original_back_projector_ptr->get_symmetries_used(); } #ifdef STIR_PROJECTORS_AS_V3 -void -PostsmoothingBackProjectorByBin:: -actual_back_project(DiscretisedDensity<3,float>& density, - const RelatedViewgrams& viewgrams, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) -{ - if (!is_null_ptr(image_processor_ptr)) - { - shared_ptr > filtered_density_ptr - (density.get_empty_discretised_density()); - assert(density.get_index_range() == filtered_density_ptr->get_index_range()); - original_back_projector_ptr->back_project(*filtered_density_ptr, viewgrams, - min_axial_pos_num, max_axial_pos_num, - min_tangential_pos_num, max_tangential_pos_num); - image_processor_ptr->apply(*filtered_density_ptr); - density += *filtered_density_ptr; - } - else - { - original_back_projector_ptr->back_project(density, viewgrams, - min_axial_pos_num, max_axial_pos_num, - min_tangential_pos_num, max_tangential_pos_num); - } +void +PostsmoothingBackProjectorByBin::actual_back_project(DiscretisedDensity<3, float>& density, + const RelatedViewgrams& viewgrams, const int min_axial_pos_num, + const int max_axial_pos_num, const int min_tangential_pos_num, + const int max_tangential_pos_num) { + if (!is_null_ptr(image_processor_ptr)) { + shared_ptr> filtered_density_ptr(density.get_empty_discretised_density()); + assert(density.get_index_range() == filtered_density_ptr->get_index_range()); + original_back_projector_ptr->back_project(*filtered_density_ptr, viewgrams, min_axial_pos_num, max_axial_pos_num, + min_tangential_pos_num, max_tangential_pos_num); + image_processor_ptr->apply(*filtered_density_ptr); + density += *filtered_density_ptr; + } else { + original_back_projector_ptr->back_project(density, viewgrams, min_axial_pos_num, max_axial_pos_num, min_tangential_pos_num, + max_tangential_pos_num); + } } #endif void -PostsmoothingBackProjectorByBin:: -actual_back_project(const RelatedViewgrams& viewgrams, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) -{ - original_back_projector_ptr->back_project(viewgrams, - min_axial_pos_num, max_axial_pos_num, - min_tangential_pos_num, max_tangential_pos_num); +PostsmoothingBackProjectorByBin::actual_back_project(const RelatedViewgrams& viewgrams, const int min_axial_pos_num, + const int max_axial_pos_num, const int min_tangential_pos_num, + const int max_tangential_pos_num) { + original_back_projector_ptr->back_project(viewgrams, min_axial_pos_num, max_axial_pos_num, min_tangential_pos_num, + max_tangential_pos_num); } - END_NAMESPACE_STIR diff --git a/src/recon_buildblock/PresmoothingForwardProjectorByBin.cxx b/src/recon_buildblock/PresmoothingForwardProjectorByBin.cxx index 0b1704ee98..0aec8f6418 100644 --- a/src/recon_buildblock/PresmoothingForwardProjectorByBin.cxx +++ b/src/recon_buildblock/PresmoothingForwardProjectorByBin.cxx @@ -33,23 +33,16 @@ #include "stir/is_null_ptr.h" START_NAMESPACE_STIR -const char * const -PresmoothingForwardProjectorByBin::registered_name = - "Pre Smoothing"; - +const char* const PresmoothingForwardProjectorByBin::registered_name = "Pre Smoothing"; void -PresmoothingForwardProjectorByBin:: -set_defaults() -{ +PresmoothingForwardProjectorByBin::set_defaults() { original_forward_projector_ptr.reset(); _pre_data_processor_sptr.reset(); } void -PresmoothingForwardProjectorByBin:: -initialise_keymap() -{ +PresmoothingForwardProjectorByBin::initialise_keymap() { parser.add_start_key("Pre Smoothing Forward Projector Parameters"); parser.add_stop_key("End Pre Smoothing Forward Projector Parameters"); parser.add_parsing_key("Original Forward projector type", &original_forward_projector_ptr); @@ -57,54 +50,39 @@ initialise_keymap() } bool -PresmoothingForwardProjectorByBin:: -post_processing() -{ - if (is_null_ptr(original_forward_projector_ptr)) - { +PresmoothingForwardProjectorByBin::post_processing() { + if (is_null_ptr(original_forward_projector_ptr)) { warning("Pre Smoothing Forward Projector: original forward projector needs to be set\n"); return true; } return false; } -PresmoothingForwardProjectorByBin:: - PresmoothingForwardProjectorByBin() -{ - set_defaults(); -} +PresmoothingForwardProjectorByBin::PresmoothingForwardProjectorByBin() { set_defaults(); } -PresmoothingForwardProjectorByBin:: -PresmoothingForwardProjectorByBin( - const shared_ptr& original_forward_projector_ptr, - const shared_ptr > >& image_processor_ptr) - : original_forward_projector_ptr(original_forward_projector_ptr) -{ - _pre_data_processor_sptr = image_processor_ptr; +PresmoothingForwardProjectorByBin::PresmoothingForwardProjectorByBin( + const shared_ptr& original_forward_projector_ptr, + const shared_ptr>>& image_processor_ptr) + : original_forward_projector_ptr(original_forward_projector_ptr) { + _pre_data_processor_sptr = image_processor_ptr; } -PresmoothingForwardProjectorByBin:: -~PresmoothingForwardProjectorByBin() -{} +PresmoothingForwardProjectorByBin::~PresmoothingForwardProjectorByBin() {} void -PresmoothingForwardProjectorByBin:: -set_up(const shared_ptr& proj_data_info_ptr, - const shared_ptr >& image_info_ptr) -{ +PresmoothingForwardProjectorByBin::set_up(const shared_ptr& proj_data_info_ptr, + const shared_ptr>& image_info_ptr) { ForwardProjectorByBin::set_up(proj_data_info_ptr, image_info_ptr); original_forward_projector_ptr->set_up(proj_data_info_ptr, image_info_ptr); } -const DataSymmetriesForViewSegmentNumbers * -PresmoothingForwardProjectorByBin:: -get_symmetries_used() const -{ +const DataSymmetriesForViewSegmentNumbers* +PresmoothingForwardProjectorByBin::get_symmetries_used() const { return original_forward_projector_ptr->get_symmetries_used(); } -//void PresmoothingForwardProjectorByBin:: -//update_filtered_density_image(const DiscretisedDensity<3,float>& density) +// void PresmoothingForwardProjectorByBin:: +// update_filtered_density_image(const DiscretisedDensity<3,float>& density) //{ //// filtered_density_sptr.reset(density.get_empty_discretised_density()); //// image_processor_ptr->apply(*filtered_density_sptr, density); @@ -112,40 +90,30 @@ get_symmetries_used() const //} #ifdef STIR_PROJECTORS_AS_V3 -void -PresmoothingForwardProjectorByBin:: -actual_forward_project(RelatedViewgrams& viewgrams, - const DiscretisedDensity<3,float>& density, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) -{ - if (!is_null_ptr(image_processor_ptr)) - { - shared_ptr > filtered_density_ptr(density.get_empty_discretised_density()); - image_processor_ptr->apply(*filtered_density_ptr, density); - assert(density.get_index_range() == filtered_density_ptr->get_index_range()); - original_forward_projector_ptr->forward_project(viewgrams, *filtered_density_ptr, - min_axial_pos_num, max_axial_pos_num, - min_tangential_pos_num, max_tangential_pos_num); - } - else - { - original_forward_projector_ptr->forward_project(viewgrams, density, - min_axial_pos_num, max_axial_pos_num, - min_tangential_pos_num, max_tangential_pos_num); - } +void +PresmoothingForwardProjectorByBin::actual_forward_project(RelatedViewgrams& viewgrams, + const DiscretisedDensity<3, float>& density, + const int min_axial_pos_num, const int max_axial_pos_num, + const int min_tangential_pos_num, const int max_tangential_pos_num) { + if (!is_null_ptr(image_processor_ptr)) { + shared_ptr> filtered_density_ptr(density.get_empty_discretised_density()); + image_processor_ptr->apply(*filtered_density_ptr, density); + assert(density.get_index_range() == filtered_density_ptr->get_index_range()); + original_forward_projector_ptr->forward_project(viewgrams, *filtered_density_ptr, min_axial_pos_num, max_axial_pos_num, + min_tangential_pos_num, max_tangential_pos_num); + } else { + original_forward_projector_ptr->forward_project(viewgrams, density, min_axial_pos_num, max_axial_pos_num, + min_tangential_pos_num, max_tangential_pos_num); + } } #endif void -PresmoothingForwardProjectorByBin:: -actual_forward_project(RelatedViewgrams& viewgrams, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) -{ - // No need to do the data processing since it was already done on set_input() - original_forward_projector_ptr->forward_project(viewgrams, - min_axial_pos_num, max_axial_pos_num, - min_tangential_pos_num, max_tangential_pos_num); +PresmoothingForwardProjectorByBin::actual_forward_project(RelatedViewgrams& viewgrams, const int min_axial_pos_num, + const int max_axial_pos_num, const int min_tangential_pos_num, + const int max_tangential_pos_num) { + // No need to do the data processing since it was already done on set_input() + original_forward_projector_ptr->forward_project(viewgrams, min_axial_pos_num, max_axial_pos_num, min_tangential_pos_num, + max_tangential_pos_num); } #if 0 // disabled as currently not used. needs to be written in the new style anyway diff --git a/src/recon_buildblock/ProjDataRebinning.cxx b/src/recon_buildblock/ProjDataRebinning.cxx index 28c6fc76d6..64ede446eb 100644 --- a/src/recon_buildblock/ProjDataRebinning.cxx +++ b/src/recon_buildblock/ProjDataRebinning.cxx @@ -3,9 +3,9 @@ /*! \file \ingroup recon_buildblock - \brief implementation of the ProjDataRebinning class + \brief implementation of the ProjDataRebinning class \author Kris Thielemans - + */ /* Copyright (C) 2003- 2005, Hammersmith Imanet Ltd @@ -32,45 +32,41 @@ using std::string; START_NAMESPACE_STIR -void -ProjDataRebinning:: -set_defaults() -{ - output_filename_prefix=""; - input_filename=""; - max_segment_num_to_process=-1; +void +ProjDataRebinning::set_defaults() { + output_filename_prefix = ""; + input_filename = ""; + max_segment_num_to_process = -1; } -void -ProjDataRebinning:: -initialise_keymap() -{ - parser.add_key("input file",&input_filename); - //parser.add_key("mash x views", &num_views_to_add); +void +ProjDataRebinning::initialise_keymap() { + parser.add_key("input file", &input_filename); + // parser.add_key("mash x views", &num_views_to_add); parser.add_key("maximum absolute segment number to process", &max_segment_num_to_process); - parser.add_key("output filename prefix",&output_filename_prefix); - + parser.add_key("output filename prefix", &output_filename_prefix); } +bool +ProjDataRebinning::post_processing() { + if (output_filename_prefix.length() == 0) { + warning("You need to specify an output prefix\n"); + return true; + } -bool -ProjDataRebinning:: -post_processing() -{ - if (output_filename_prefix.length() == 0) - { warning("You need to specify an output prefix\n"); return true; } - - if (input_filename.length() == 0) - { warning("You need to specify an input file\n"); return true; } + if (input_filename.length() == 0) { + warning("You need to specify an input file\n"); + return true; + } #if 0 if (num_views_to_add!=1 && (num_views_to_add<=0 || num_views_to_add%2 != 0)) { warning("The 'mash x views' key has an invalid value (must be 1 or even number)\n"); return true; } #endif - - proj_data_sptr= ProjData::read_from_file(input_filename); - + + proj_data_sptr = ProjData::read_from_file(input_filename); + return false; } #if 0 @@ -101,69 +97,55 @@ else } #endif - + Succeeded -ProjDataRebinning:: -set_up() -{ - if (is_null_ptr(proj_data_sptr)) - { - warning("ProjDataRebinning: input projection data not set"); - return Succeeded::no; - } +ProjDataRebinning::set_up() { + if (is_null_ptr(proj_data_sptr)) { + warning("ProjDataRebinning: input projection data not set"); + return Succeeded::no; + } if (max_segment_num_to_process == -1) max_segment_num_to_process = proj_data_sptr->get_max_segment_num(); - else if(max_segment_num_to_process > proj_data_sptr->get_max_segment_num()) - { - warning("ProjDataRebinning: Range error in number of segments to process.\n" - "Max segment number in data is %d while you asked for %d", - proj_data_sptr->get_max_segment_num(), - max_segment_num_to_process); - return Succeeded::no; - } + else if (max_segment_num_to_process > proj_data_sptr->get_max_segment_num()) { + warning("ProjDataRebinning: Range error in number of segments to process.\n" + "Max segment number in data is %d while you asked for %d", + proj_data_sptr->get_max_segment_num(), max_segment_num_to_process); + return Succeeded::no; + } return Succeeded::yes; } -ProjDataRebinning:: -~ProjDataRebinning() -{} +ProjDataRebinning::~ProjDataRebinning() {} -void ProjDataRebinning::set_max_segment_num_to_process(int ns) -{ max_segment_num_to_process = ns; +void +ProjDataRebinning::set_max_segment_num_to_process(int ns) { + max_segment_num_to_process = ns; } -int ProjDataRebinning::get_max_segment_num_to_process() const -{ return max_segment_num_to_process; +int +ProjDataRebinning::get_max_segment_num_to_process() const { + return max_segment_num_to_process; } -void -ProjDataRebinning::set_output_filename_prefix(const string& s) -{ +void +ProjDataRebinning::set_output_filename_prefix(const string& s) { output_filename_prefix = s; } -string -ProjDataRebinning::get_output_filename_prefix() const -{ +string +ProjDataRebinning::get_output_filename_prefix() const { return output_filename_prefix; } - -shared_ptr -ProjDataRebinning:: -get_proj_data_sptr() -{ - /* KT: deleted warning messages about null pointers. - The user should check this, and might not want the have the +shared_ptr +ProjDataRebinning::get_proj_data_sptr() { + /* KT: deleted warning messages about null pointers. + The user should check this, and might not want the have the warnings written to stderr. */ - return proj_data_sptr; - } - + return proj_data_sptr; +} -void -ProjDataRebinning:: -set_input_proj_data_sptr(const shared_ptr& new_proj_data_sptr) -{ +void +ProjDataRebinning::set_input_proj_data_sptr(const shared_ptr& new_proj_data_sptr) { proj_data_sptr = new_proj_data_sptr; -} - +} END_NAMESPACE_STIR diff --git a/src/recon_buildblock/ProjMatrixByBin.cxx b/src/recon_buildblock/ProjMatrixByBin.cxx index 57299eadc4..d9ede0f986 100644 --- a/src/recon_buildblock/ProjMatrixByBin.cxx +++ b/src/recon_buildblock/ProjMatrixByBin.cxx @@ -2,8 +2,8 @@ \file \ingroup projection - - \brief implementation of the stir::ProjMatrixByBin class + + \brief implementation of the stir::ProjMatrixByBin class \author Nikos Efthimiou \author Mustapha Sadki @@ -31,100 +31,84 @@ See STIR/LICENSE.txt for details */ - #include "stir/recon_buildblock/ProjMatrixByBin.h" #include "stir/recon_buildblock/ProjMatrixElemsForOneBin.h" // define a local preprocessor symbol to keep code relatively clean #ifdef STIR_NO_MUTABLE -#define STIR_MUTABLE_CONST +# define STIR_MUTABLE_CONST #else -#define STIR_MUTABLE_CONST const +# define STIR_MUTABLE_CONST const #endif START_NAMESPACE_STIR -void ProjMatrixByBin::set_defaults() -{ - cache_disabled=false; - cache_stores_only_basic_bins=true; +void +ProjMatrixByBin::set_defaults() { + cache_disabled = false; + cache_stores_only_basic_bins = true; gauss_sigma_in_mm = 0.f; r_sqrt2_gauss_sigma = 0.f; } -void -ProjMatrixByBin::initialise_keymap() -{ +void +ProjMatrixByBin::initialise_keymap() { parser.add_key("disable caching", &cache_disabled); parser.add_key("store_only_basic_bins_in_cache", &cache_stores_only_basic_bins); } bool -ProjMatrixByBin::post_processing() -{ +ProjMatrixByBin::post_processing() { return false; } -ProjMatrixByBin::ProjMatrixByBin() -{ - set_defaults(); +ProjMatrixByBin::ProjMatrixByBin() { set_defaults(); } + +void +ProjMatrixByBin::enable_cache(const bool v) { + cache_disabled = !v; } - -void -ProjMatrixByBin:: -enable_cache(const bool v) -{ cache_disabled = !v;} void -ProjMatrixByBin:: -enable_tof(const shared_ptr& _proj_data_info_sptr, const bool v) -{ - if (v) - { - tof_enabled = true; - proj_data_info_sptr = _proj_data_info_sptr; - gauss_sigma_in_mm = ProjDataInfo::tof_delta_time_to_mm(proj_data_info_sptr->get_scanner_ptr()->get_timing_resolution()) / 2.355f; - r_sqrt2_gauss_sigma = 1.0f/ (gauss_sigma_in_mm * static_cast(sqrt(2.0))); - } +ProjMatrixByBin::enable_tof(const shared_ptr& _proj_data_info_sptr, const bool v) { + if (v) { + tof_enabled = true; + proj_data_info_sptr = _proj_data_info_sptr; + gauss_sigma_in_mm = + ProjDataInfo::tof_delta_time_to_mm(proj_data_info_sptr->get_scanner_ptr()->get_timing_resolution()) / 2.355f; + r_sqrt2_gauss_sigma = 1.0f / (gauss_sigma_in_mm * static_cast(sqrt(2.0))); + } } -void -ProjMatrixByBin:: -store_only_basic_bins_in_cache(const bool v) -{ cache_stores_only_basic_bins=v;} +void +ProjMatrixByBin::store_only_basic_bins_in_cache(const bool v) { + cache_stores_only_basic_bins = v; +} -bool -ProjMatrixByBin:: -is_cache_enabled() const -{ return !cache_disabled; } +bool +ProjMatrixByBin::is_cache_enabled() const { + return !cache_disabled; +} -bool -ProjMatrixByBin:: -does_cache_store_only_basic_bins() const -{ return cache_stores_only_basic_bins; } +bool +ProjMatrixByBin::does_cache_store_only_basic_bins() const { + return cache_stores_only_basic_bins; +} -void -ProjMatrixByBin:: -clear_cache() STIR_MUTABLE_CONST -{ +void +ProjMatrixByBin::clear_cache() STIR_MUTABLE_CONST { #ifdef STIR_OPENMP -#pragma omp critical(PROJMATRIXBYBINCLEARCACHE) +# pragma omp critical(PROJMATRIXBYBINCLEARCACHE) #endif - for (int i=this->cache_collection.get_min_index(); - i<=this->cache_collection.get_max_index(); - ++i) - { - for (int j=this->cache_collection[i].get_min_index(); - j<=this->cache_collection[i].get_max_index(); - ++j) - { - this->cache_collection[i][j].clear(); - } + for (int i = this->cache_collection.get_min_index(); i <= this->cache_collection.get_max_index(); ++i) { + for (int j = this->cache_collection[i].get_min_index(); j <= this->cache_collection[i].get_max_index(); ++j) { + this->cache_collection[i][j].clear(); } + } } /* -void +void ProjMatrixByBin:: reserve_num_elements_in_cache(const std::size_t num_elems) { @@ -135,23 +119,19 @@ reserve_num_elements_in_cache(const std::size_t num_elems) */ void -ProjMatrixByBin:: -set_up( - const shared_ptr& proj_data_info_sptr, - const shared_ptr >& /*density_info_ptr*/ // TODO should be Info only - ) -{ +ProjMatrixByBin::set_up(const shared_ptr& proj_data_info_sptr, + const shared_ptr>& /*density_info_ptr*/ // TODO should be Info only +) { const int min_view_num = proj_data_info_sptr->get_min_view_num(); const int max_view_num = proj_data_info_sptr->get_max_view_num(); const int min_segment_num = proj_data_info_sptr->get_min_segment_num(); const int max_segment_num = proj_data_info_sptr->get_max_segment_num(); if (proj_data_info_sptr->is_tof_data()) - enable_tof(proj_data_info_sptr,true); - else - { - tof_enabled = false; - this->proj_data_info_sptr = proj_data_info_sptr; + enable_tof(proj_data_info_sptr, true); + else { + tof_enabled = false; + this->proj_data_info_sptr = proj_data_info_sptr; } this->cache_collection.recycle(); @@ -161,113 +141,94 @@ set_up( this->cache_locks.resize(min_view_num, max_view_num); #endif - for (int view_num=min_view_num; view_num<=max_view_num; ++view_num) - { - this->cache_collection[view_num].resize(min_segment_num, max_segment_num); + for (int view_num = min_view_num; view_num <= max_view_num; ++view_num) { + this->cache_collection[view_num].resize(min_segment_num, max_segment_num); #ifdef STIR_OPENMP - this->cache_locks[view_num].resize(min_segment_num, max_segment_num); - for (int seg_num = min_segment_num; seg_num <=max_segment_num; ++seg_num) - omp_init_lock(&this->cache_locks[view_num][seg_num]); + this->cache_locks[view_num].resize(min_segment_num, max_segment_num); + for (int seg_num = min_segment_num; seg_num <= max_segment_num; ++seg_num) + omp_init_lock(&this->cache_locks[view_num][seg_num]); #endif - } + } } - /*! \warning Preconditions
  • abs(axial_pos_num) fits in 17 bits -
  • abs(tangential_pos_num) fits in 11 bits +
  • abs(tangential_pos_num) fits in 11 bits */ ProjMatrixByBin::CacheKey -ProjMatrixByBin::cache_key(const Bin& bin) const -{ - assert(static_cast(abs(bin.axial_pos_num())) < (static_cast(1)<<18)); - assert(abs(bin.tangential_pos_num()) < (1<<12)); - return (CacheKey)( - (static_cast(bin.axial_pos_num()>=0?0:1) << 31) - | (static_cast(abs(bin.axial_pos_num()))<<13) - | (static_cast(bin.tangential_pos_num()>=0?0:1) << 12) - | static_cast(abs(bin.tangential_pos_num())) ); -} +ProjMatrixByBin::cache_key(const Bin& bin) const { + assert(static_cast(abs(bin.axial_pos_num())) < (static_cast(1) << 18)); + assert(abs(bin.tangential_pos_num()) < (1 << 12)); + return (CacheKey)((static_cast(bin.axial_pos_num() >= 0 ? 0 : 1) << 31) | + (static_cast(abs(bin.axial_pos_num())) << 13) | + (static_cast(bin.tangential_pos_num() >= 0 ? 0 : 1) << 12) | + static_cast(abs(bin.tangential_pos_num()))); +} +void +ProjMatrixByBin::cache_proj_matrix_elems_for_one_bin(const ProjMatrixElemsForOneBin& probabilities) STIR_MUTABLE_CONST { + if (cache_disabled) + return; -void -ProjMatrixByBin:: -cache_proj_matrix_elems_for_one_bin( - const ProjMatrixElemsForOneBin& probabilities) STIR_MUTABLE_CONST -{ - if ( cache_disabled ) return; - - //std::cerr << "cached lor size " << probabilities.size() << " capacity " << probabilities.capacity() << std::endl; - // insert probabilities into the collection + // std::cerr << "cached lor size " << probabilities.size() << " capacity " << probabilities.capacity() << std::endl; + // insert probabilities into the collection const Bin bin = probabilities.get_bin(); #ifdef STIR_OPENMP omp_set_lock(&this->cache_locks[bin.view_num()][bin.segment_num()]); #endif - cache_collection[bin.view_num()][bin.segment_num()].insert(MapProjMatrixElemsForOneBin::value_type( cache_key(bin), - probabilities)); + cache_collection[bin.view_num()][bin.segment_num()].insert( + MapProjMatrixElemsForOneBin::value_type(cache_key(bin), probabilities)); #ifdef STIR_OPENMP omp_unset_lock(&this->cache_locks[bin.view_num()][bin.segment_num()]); #endif } - -Succeeded -ProjMatrixByBin:: -get_cached_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& probabilities) const -{ - if ( cache_disabled ) +Succeeded +ProjMatrixByBin::get_cached_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& probabilities) const { + if (cache_disabled) return Succeeded::no; - + const Bin bin = probabilities.get_bin(); #ifndef NDEBUG - if (cache_stores_only_basic_bins) - { + if (cache_stores_only_basic_bins) { // Check that this is a 'basic' coordinate - Bin bin_copy = bin; - assert ( symmetries_sptr->find_basic_bin(bin_copy) == 0); + Bin bin_copy = bin; + assert(symmetries_sptr->find_basic_bin(bin_copy) == 0); } -#endif - - bool found=false; +#endif + + bool found = false; #ifdef STIR_OPENMP omp_set_lock(&this->cache_locks[bin.view_num()][bin.segment_num()]); #endif { - const_MapProjMatrixElemsForOneBinIterator pos = - cache_collection[bin.view_num()][bin.segment_num()].find(cache_key( bin)); - - if ( pos != cache_collection[bin.view_num()][bin.segment_num()]. end()) - { - //cout << Key << " =========>> entry found in cache " << endl; - probabilities = pos->second; - // note: cannot return from inside an OPENMP critical section - //return Succeeded::yes; - found=true; - } + const_MapProjMatrixElemsForOneBinIterator pos = cache_collection[bin.view_num()][bin.segment_num()].find(cache_key(bin)); + + if (pos != cache_collection[bin.view_num()][bin.segment_num()].end()) { + // cout << Key << " =========>> entry found in cache " << endl; + probabilities = pos->second; + // note: cannot return from inside an OPENMP critical section + // return Succeeded::yes; + found = true; + } } #ifdef STIR_OPENMP omp_unset_lock(&this->cache_locks[bin.view_num()][bin.segment_num()]); #endif if (found) - return Succeeded::yes; - else - { - //cout << " This entry is not in the cache :" << Key << endl; - return Succeeded::no; - } + return Succeeded::yes; + else { + // cout << " This entry is not in the cache :" << Key << endl; + return Succeeded::no; + } } +// TODO - -//TODO - - - -////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// #if 0 // KT moved here //! store the projection matrix to the file by rows @@ -318,8 +279,6 @@ void ProjMatrixByBin::write_to_file_by_bin( cout << "End of write_to_file_by_bin " << endl; } - #endif - END_NAMESPACE_STIR diff --git a/src/recon_buildblock/ProjMatrixByBinFromFile.cxx b/src/recon_buildblock/ProjMatrixByBinFromFile.cxx index cf62e65b1e..7775cbe064 100644 --- a/src/recon_buildblock/ProjMatrixByBinFromFile.cxx +++ b/src/recon_buildblock/ProjMatrixByBinFromFile.cxx @@ -51,21 +51,12 @@ using std::string; START_NAMESPACE_STIR +const char* const ProjMatrixByBinFromFile::registered_name = "From File"; -const char * const -ProjMatrixByBinFromFile::registered_name = -"From File"; +ProjMatrixByBinFromFile::ProjMatrixByBinFromFile() { set_defaults(); } -ProjMatrixByBinFromFile:: -ProjMatrixByBinFromFile() -{ - set_defaults(); -} - -void -ProjMatrixByBinFromFile:: -initialise_keymap() -{ +void +ProjMatrixByBinFromFile::initialise_keymap() { parser.add_start_key("Projection Matrix By Bin From File Parameters"); ProjMatrixByBin::initialise_keymap(); @@ -74,28 +65,26 @@ initialise_keymap() parser.add_key("data_filename", &data_filename); parser.add_key("Version", &this->parsed_version); - parser.add_key("symmetries type", &this->symmetries_type) ; - - //parser.add_key("PET_CartesianGrid symmetries parameters", + parser.add_key("symmetries type", &this->symmetries_type); + + // parser.add_key("PET_CartesianGrid symmetries parameters", // KeyArgument::NONE, &KeyParser::do_nothing); parser.add_key("do_symmetry_90degrees_min_phi", &do_symmetry_90degrees_min_phi); parser.add_key("do_symmetry_180degrees_min_phi", &do_symmetry_180degrees_min_phi); parser.add_key("do_symmetry_swap_segment", &do_symmetry_swap_segment); parser.add_key("do_symmetry_swap_s", &do_symmetry_swap_s); parser.add_key("do_symmetry_shift_z", &do_symmetry_shift_z); - //parser.add_key("End PET_CartesianGrid symmetries parameters", + // parser.add_key("End PET_CartesianGrid symmetries parameters", // KeyArgument::NONE, &KeyParser::do_nothing); parser.add_stop_key("End Projection Matrix By Bin From File Parameters"); } - void -ProjMatrixByBinFromFile::set_defaults() -{ +ProjMatrixByBinFromFile::set_defaults() { ProjMatrixByBin::set_defaults(); - template_density_filename=""; - template_proj_data_filename=""; - data_filename=""; + template_density_filename = ""; + template_proj_data_filename = ""; + data_filename = ""; do_symmetry_90degrees_min_phi = true; do_symmetry_180degrees_min_phi = true; @@ -104,95 +93,70 @@ ProjMatrixByBinFromFile::set_defaults() do_symmetry_shift_z = true; } - bool -ProjMatrixByBinFromFile::post_processing() -{ +ProjMatrixByBinFromFile::post_processing() { if (ProjMatrixByBin::post_processing() == true) return true; - if (this->parsed_version != "1.0") - { - warning("version has to be 1.0"); - return true; - } + if (this->parsed_version != "1.0") { + warning("version has to be 1.0"); + return true; + } this->symmetries_type = standardise_interfile_keyword(this->symmetries_type); - if (this->symmetries_type != standardise_interfile_keyword("PET_CartesianGrid") && this->symmetries_type != "none") - { - warning("symmetries type has to be PET_CartesianGrid or None"); - return true; - } + if (this->symmetries_type != standardise_interfile_keyword("PET_CartesianGrid") && this->symmetries_type != "none") { + warning("symmetries type has to be PET_CartesianGrid or None"); + return true; + } - if (template_density_filename.size()==0) - { - warning("template_density_filename has to be specified.\n"); - return true; - } - if (template_proj_data_filename.size()==0) - { - warning("template_proj_data_filename has to be specified.\n"); - return true; - } - if (data_filename.size()==0) - { - warning("data_filename has to be specified.\n"); - return true; - } + if (template_density_filename.size() == 0) { + warning("template_density_filename has to be specified.\n"); + return true; + } + if (template_proj_data_filename.size() == 0) { + warning("template_proj_data_filename has to be specified.\n"); + return true; + } + if (data_filename.size() == 0) { + warning("data_filename has to be specified.\n"); + return true; + } { - shared_ptr proj_data_sptr = - ProjData::read_from_file(template_proj_data_filename); + shared_ptr proj_data_sptr = ProjData::read_from_file(template_proj_data_filename); this->proj_data_info_ptr.reset(proj_data_sptr->get_proj_data_info_sptr()->clone()); } - shared_ptr > - density_info_sptr(read_from_file >(template_density_filename)); + shared_ptr> density_info_sptr( + read_from_file>(template_density_filename)); { - const VoxelsOnCartesianGrid * image_info_ptr = - dynamic_cast*> (density_info_sptr.get()); + const VoxelsOnCartesianGrid* image_info_ptr = + dynamic_cast*>(density_info_sptr.get()); if (image_info_ptr == NULL) error("ProjMatrixByBinFromFile initialised with a wrong type of DiscretisedDensity"); - + densel_range = image_info_ptr->get_index_range(); voxel_size = image_info_ptr->get_voxel_size(); origin = image_info_ptr->get_origin(); } - - - if (this->symmetries_type == standardise_interfile_keyword("PET_CartesianGrid")) - { - symmetries_sptr.reset( - new DataSymmetriesForBins_PET_CartesianGrid(proj_data_info_ptr, - density_info_sptr, - do_symmetry_90degrees_min_phi, - do_symmetry_180degrees_min_phi, - do_symmetry_swap_segment, - do_symmetry_swap_s, - do_symmetry_shift_z)); - } - else if (this->symmetries_type == "none") - { - symmetries_sptr.reset(new TrivialDataSymmetriesForBins(proj_data_info_ptr)); - } - else - { - error("internal error: symmetries handling in ProjMatrixByBinFromFile"); - } + if (this->symmetries_type == standardise_interfile_keyword("PET_CartesianGrid")) { + symmetries_sptr.reset(new DataSymmetriesForBins_PET_CartesianGrid( + proj_data_info_ptr, density_info_sptr, do_symmetry_90degrees_min_phi, do_symmetry_180degrees_min_phi, + do_symmetry_swap_segment, do_symmetry_swap_s, do_symmetry_shift_z)); + } else if (this->symmetries_type == "none") { + symmetries_sptr.reset(new TrivialDataSymmetriesForBins(proj_data_info_ptr)); + } else { + error("internal error: symmetries handling in ProjMatrixByBinFromFile"); + } return false; } - void -ProjMatrixByBinFromFile:: -set_up( - const shared_ptr& proj_data_info_ptr_v, - const shared_ptr >& density_info_ptr // TODO should be Info only - ) -{ +ProjMatrixByBinFromFile::set_up(const shared_ptr& proj_data_info_ptr_v, + const shared_ptr>& density_info_ptr // TODO should be Info only +) { - const VoxelsOnCartesianGrid * image_info_ptr = - dynamic_cast*> (density_info_ptr.get()); + const VoxelsOnCartesianGrid* image_info_ptr = dynamic_cast*>(density_info_ptr.get()); if (image_info_ptr == NULL) error("ProjMatrixByBinFromFile set-up with a wrong type of DiscretisedDensity\n"); @@ -208,7 +172,7 @@ set_up( /* do consistency checks on projection data. It's safe as long as the stored range is larger than what we need. */ - if (!( *this->proj_data_info_ptr >= *proj_data_info_ptr_v)) + if (!(*this->proj_data_info_ptr >= *proj_data_info_ptr_v)) error("ProjMatrixByBinFromFile set-up with proj data with wrong characteristics"); // note: currently setting up with proj_data_info stored in the file @@ -216,153 +180,143 @@ set_up( // every LOR that's in the file in the cache ProjMatrixByBin::set_up(this->proj_data_info_ptr, density_info_ptr); - if (read_data() ==Succeeded::no) + if (read_data() == Succeeded::no) error("Something wrong reading the matrix from file. Exiting."); } ProjMatrixByBinFromFile* -ProjMatrixByBinFromFile::clone() const -{ - return new ProjMatrixByBinFromFile(*this); +ProjMatrixByBinFromFile::clone() const { + return new ProjMatrixByBinFromFile(*this); } // anonymous namespace for local functions namespace { - // static (i.e. private) function to write the data - static Succeeded - write_lor(std::ostream&fst, const ProjMatrixElemsForOneBin& lor) - { - const Bin bin = lor.get_bin(); - { - boost::int32_t c; - c = bin.segment_num(); fst.write ( (char*)&c, sizeof(boost::int32_t)); - c = bin.view_num(); fst.write ( (char*)&c, sizeof(boost::int32_t)); - c = bin.axial_pos_num(); fst.write ( (char*)&c, sizeof(boost::int32_t)); - c = bin.tangential_pos_num(); fst.write ( (char*)&c, sizeof(boost::int32_t)); - } - { - boost::uint32_t c= static_cast(lor.size()); - fst.write( (char*)&c , sizeof(boost::uint32_t)); - } +// static (i.e. private) function to write the data +static Succeeded +write_lor(std::ostream& fst, const ProjMatrixElemsForOneBin& lor) { + const Bin bin = lor.get_bin(); + { + boost::int32_t c; + c = bin.segment_num(); + fst.write((char*)&c, sizeof(boost::int32_t)); + c = bin.view_num(); + fst.write((char*)&c, sizeof(boost::int32_t)); + c = bin.axial_pos_num(); + fst.write((char*)&c, sizeof(boost::int32_t)); + c = bin.tangential_pos_num(); + fst.write((char*)&c, sizeof(boost::int32_t)); + } + { + boost::uint32_t c = static_cast(lor.size()); + fst.write((char*)&c, sizeof(boost::uint32_t)); + } + if (!fst) + return Succeeded::no; + ProjMatrixElemsForOneBin::const_iterator element_ptr = lor.begin(); + // todo add compression in this loop + while (element_ptr != lor.end()) { + boost::int16_t c; + c = static_cast(element_ptr->coord1()); + fst.write((char*)&c, sizeof(boost::int16_t)); + c = static_cast(element_ptr->coord2()); + fst.write((char*)&c, sizeof(boost::int16_t)); + c = static_cast(element_ptr->coord3()); + fst.write((char*)&c, sizeof(boost::int16_t)); + const float value = element_ptr->get_value(); + fst.write((char*)&value, sizeof(float)); if (!fst) return Succeeded::no; - ProjMatrixElemsForOneBin::const_iterator element_ptr = lor.begin(); - // todo add compression in this loop - while (element_ptr != lor.end()) - { - boost::int16_t c; - c = static_cast(element_ptr->coord1()); - fst.write ( (char*)&c, sizeof(boost::int16_t)); - c = static_cast(element_ptr->coord2()); - fst.write ( (char*)&c, sizeof(boost::int16_t)); - c = static_cast(element_ptr->coord3()); - fst.write ( (char*)&c, sizeof(boost::int16_t)); - const float value = element_ptr->get_value(); - fst.write ( (char*)&value, sizeof(float)); - if (!fst) - return Succeeded::no; - - ++element_ptr; - } - return Succeeded::yes; - } - - // return type for read_lor() - class readReturnType - { - public: - enum value { ok, eof, problem }; - readReturnType(const value& v) : v(v) {} - bool operator==(const readReturnType &v2) const { return v == v2.v; } - bool operator!=(const readReturnType &v2) const { return v != v2.v; } - private: - value v; - }; - - // static (i.e. private) function to read an lor - static - readReturnType - read_lor(std::istream&fst, ProjMatrixElemsForOneBin& lor ) - { - lor.erase(); - - { - Bin bin; - boost::int32_t c; - fst.read( (char*)&c, sizeof(boost::int32_t)); bin.segment_num()=c; - if (fst.gcount()==0 && fst.eof()) - { - // we were at EOF - return readReturnType::eof; - } - - fst.read( (char*)&c, sizeof(boost::int32_t)); bin.view_num()=c; - fst.read( (char*)&c, sizeof(boost::int32_t)); bin.axial_pos_num()=c; - fst.read( (char*)&c, sizeof(boost::int32_t)); bin.tangential_pos_num()=c; - bin.set_bin_value(0); - lor.set_bin(bin); - // info(boost::format("Read bin (s:%d,a:%d,v:%d,t:%d)") % - // bin.segment_num()%bin.axial_pos_num()%bin.view_num()%bin.tangential_pos_num()); + ++element_ptr; + } + return Succeeded::yes; +} + +// return type for read_lor() +class readReturnType { +public: + enum value { ok, eof, problem }; + readReturnType(const value& v) : v(v) {} + bool operator==(const readReturnType& v2) const { return v == v2.v; } + bool operator!=(const readReturnType& v2) const { return v != v2.v; } + +private: + value v; +}; + +// static (i.e. private) function to read an lor +static readReturnType +read_lor(std::istream& fst, ProjMatrixElemsForOneBin& lor) { + lor.erase(); + + { + Bin bin; + boost::int32_t c; + fst.read((char*)&c, sizeof(boost::int32_t)); + bin.segment_num() = c; + if (fst.gcount() == 0 && fst.eof()) { + // we were at EOF + return readReturnType::eof; } - boost::uint32_t count; - fst.read ( (char*)&count, sizeof(boost::uint32_t)); - if (!fst || fst.gcount() != 4) - return readReturnType::problem; + fst.read((char*)&c, sizeof(boost::int32_t)); + bin.view_num() = c; + fst.read((char*)&c, sizeof(boost::int32_t)); + bin.axial_pos_num() = c; + fst.read((char*)&c, sizeof(boost::int32_t)); + bin.tangential_pos_num() = c; + bin.set_bin_value(0); + lor.set_bin(bin); + // info(boost::format("Read bin (s:%d,a:%d,v:%d,t:%d)") % + // bin.segment_num()%bin.axial_pos_num()%bin.view_num()%bin.tangential_pos_num()); + } + boost::uint32_t count; + fst.read((char*)&count, sizeof(boost::uint32_t)); - if (count>10000) - error("Unbelievably high count of voxels in LOR: %d", count); - - lor.reserve(count); - - for ( boost::uint32_t i=0; i < count; ++i) - { - boost::int16_t c1,c2,c3; - fst.read ( (char*)&c1, sizeof(boost::int16_t)); - fst.read ( (char*)&c2, sizeof(boost::int16_t)); - fst.read ( (char*)&c3, sizeof(boost::int16_t)); - float value; - fst.read ( (char*)&value, sizeof(float)); - - if (!fst) - return readReturnType::problem; - const ProjMatrixElemsForOneBin::value_type - elem(Coordinate3D(c1,c2,c3), value); - lor.push_back( elem); - } - return readReturnType::ok; + if (!fst || fst.gcount() != 4) + return readReturnType::problem; + + if (count > 10000) + error("Unbelievably high count of voxels in LOR: %d", count); + + lor.reserve(count); + + for (boost::uint32_t i = 0; i < count; ++i) { + boost::int16_t c1, c2, c3; + fst.read((char*)&c1, sizeof(boost::int16_t)); + fst.read((char*)&c2, sizeof(boost::int16_t)); + fst.read((char*)&c3, sizeof(boost::int16_t)); + float value; + fst.read((char*)&value, sizeof(float)); + + if (!fst) + return readReturnType::problem; + const ProjMatrixElemsForOneBin::value_type elem(Coordinate3D(c1, c2, c3), value); + lor.push_back(elem); } + return readReturnType::ok; +} } // end of anonymous namespace - + Succeeded -ProjMatrixByBinFromFile:: -write_to_file(const std::string& output_filename_prefix, - const ProjMatrixByBin& proj_matrix, - const shared_ptr& proj_data_info_sptr, - const DiscretisedDensity<3,float>& template_density) -{ - - string template_density_filename = - output_filename_prefix + "_template_density"; +ProjMatrixByBinFromFile::write_to_file(const std::string& output_filename_prefix, const ProjMatrixByBin& proj_matrix, + const shared_ptr& proj_data_info_sptr, + const DiscretisedDensity<3, float>& template_density) { + + string template_density_filename = output_filename_prefix + "_template_density"; { - if (OutputFileFormat >::default_sptr()-> - write_to_file(template_density_filename, - template_density) != Succeeded::yes) - { - warning("Error writing template image"); - return Succeeded::no; - } + if (OutputFileFormat>::default_sptr()->write_to_file(template_density_filename, + template_density) != Succeeded::yes) { + warning("Error writing template image"); + return Succeeded::no; + } } - string template_proj_data_filename = - output_filename_prefix + "_template_proj_data"; + string template_proj_data_filename = output_filename_prefix + "_template_proj_data"; { // the following constructor will write an interfile header (and empty data) to disk shared_ptr exam_info_sptr(new ExamInfo); - ProjDataInterfile template_projdata(exam_info_sptr, - proj_data_info_sptr, - template_proj_data_filename); + ProjDataInterfile template_projdata(exam_info_sptr, proj_data_info_sptr, template_proj_data_filename); } string header_filename = output_filename_prefix; @@ -372,42 +326,33 @@ write_to_file(const std::string& output_filename_prefix, { std::ofstream header(header_filename.c_str()); - if (!header) - { - warning("Error opening header %s", - header_filename.c_str()); - return Succeeded::no; - } + if (!header) { + warning("Error opening header %s", header_filename.c_str()); + return Succeeded::no; + } header << "Projection Matrix By Bin From File Parameters:=\n" - << "Version := 1.0\n"; + << "Version := 1.0\n"; // TODO symmetries should not be hard-coded - if (!is_null_ptr(dynamic_cast(proj_matrix.get_symmetries_ptr()))) - { - const DataSymmetriesForBins_PET_CartesianGrid& symmetries = - dynamic_cast - (*proj_matrix.get_symmetries_ptr()); - - header << "symmetries type := PET_CartesianGrid\n" - << " PET_CartesianGrid symmetries parameters:=\n" - << " do_symmetry_90degrees_min_phi:= " << (symmetries.using_symmetry_90degrees_min_phi() ? 1 : 0) << '\n' - << " do_symmetry_180degrees_min_phi:= " << (symmetries.using_symmetry_180degrees_min_phi() ? 1 : 0) << '\n' - << " do_symmetry_swap_segment:= " << (symmetries.using_symmetry_swap_segment() ? 1 : 0) << '\n' - << " do_symmetry_swap_s:= " << (symmetries.using_symmetry_swap_s() ? 1 : 0) << '\n' - << " do_symmetry_shift_z:= " << (symmetries.using_symmetry_shift_z() ? 1 : 0) << '\n' - << " End PET_CartesianGrid symmetries parameters:=\n"; - } - else if (!is_null_ptr(dynamic_cast(proj_matrix.get_symmetries_ptr()))) - { - header << "symmetries type := none\n"; - } - else - { - warning("ProjMatrixByBinFromFile does not yet support this type of symmetries. sorry"); - return Succeeded::no; - } - - + if (!is_null_ptr(dynamic_cast(proj_matrix.get_symmetries_ptr()))) { + const DataSymmetriesForBins_PET_CartesianGrid& symmetries = + dynamic_cast(*proj_matrix.get_symmetries_ptr()); + + header << "symmetries type := PET_CartesianGrid\n" + << " PET_CartesianGrid symmetries parameters:=\n" + << " do_symmetry_90degrees_min_phi:= " << (symmetries.using_symmetry_90degrees_min_phi() ? 1 : 0) << '\n' + << " do_symmetry_180degrees_min_phi:= " << (symmetries.using_symmetry_180degrees_min_phi() ? 1 : 0) << '\n' + << " do_symmetry_swap_segment:= " << (symmetries.using_symmetry_swap_segment() ? 1 : 0) << '\n' + << " do_symmetry_swap_s:= " << (symmetries.using_symmetry_swap_s() ? 1 : 0) << '\n' + << " do_symmetry_shift_z:= " << (symmetries.using_symmetry_shift_z() ? 1 : 0) << '\n' + << " End PET_CartesianGrid symmetries parameters:=\n"; + } else if (!is_null_ptr(dynamic_cast(proj_matrix.get_symmetries_ptr()))) { + header << "symmetries type := none\n"; + } else { + warning("ProjMatrixByBinFromFile does not yet support this type of symmetries. sorry"); + return Succeeded::no; + } + header << "template proj data filename:=" << template_proj_data_filename << '\n'; header << "template density filename:=" << template_density_filename << '\n'; @@ -418,7 +363,7 @@ write_to_file(const std::string& output_filename_prefix, std::ofstream fst; open_write_binary(fst, data_filename.c_str()); - + // loop over bins // the complication here is that we cannot just test if each bin in the range is 'basic' // and write only those. The reason is that symmetry operations can construct a @@ -427,9 +372,9 @@ write_to_file(const std::string& output_filename_prefix, // The complication is then that we need to keep track which one we wrote already. // Originally, I did this via a std::list. Checking if a bin was already written // is terribly slow however. Instead, I currently use a vector of shared_ptrs. - // This wastes only a little bit of memory, but the bounds are difficult to + // This wastes only a little bit of memory, but the bounds are difficult to // determine in general. - // A better approach (and simpler) would be to have access to the internal cache of the + // A better approach (and simpler) would be to have access to the internal cache of the // projection matrix. { // defined here to avoid reallocation for every bin @@ -439,31 +384,25 @@ write_to_file(const std::string& output_filename_prefix, std::list already_processed; #else typedef VectorWithOffset tpos_t; - typedef VectorWithOffset > vpos_t; - typedef VectorWithOffset > apos_t; - typedef VectorWithOffset > spos_t; + typedef VectorWithOffset> vpos_t; + typedef VectorWithOffset> apos_t; + typedef VectorWithOffset> spos_t; // vector that will contain (vectors of bools) to check if we wrote a bin already or not // upper boundary takes into account that symmetries convert negative segment_num to positive - spos_t already_processed(proj_data_info_sptr->get_min_segment_num(), - std::max(proj_data_info_sptr->get_max_segment_num(), - -proj_data_info_sptr->get_min_segment_num())); + spos_t already_processed(proj_data_info_sptr->get_min_segment_num(), + std::max(proj_data_info_sptr->get_max_segment_num(), -proj_data_info_sptr->get_min_segment_num())); #endif - for (int segment_num = proj_data_info_sptr->get_min_segment_num(); - segment_num <= proj_data_info_sptr->get_max_segment_num(); - ++segment_num) - for (int axial_pos_num = proj_data_info_sptr->get_min_axial_pos_num(segment_num); - axial_pos_num <= proj_data_info_sptr->get_max_axial_pos_num(segment_num); - ++axial_pos_num) - for (int view_num = proj_data_info_sptr->get_min_view_num(); - view_num <= proj_data_info_sptr->get_max_view_num(); - ++view_num) - for (int tang_pos_num = proj_data_info_sptr->get_min_tangential_pos_num(); - tang_pos_num <= proj_data_info_sptr->get_max_tangential_pos_num(); - ++tang_pos_num) - { - Bin bin(segment_num,view_num, axial_pos_num, tang_pos_num); - proj_matrix.get_symmetries_ptr()->find_basic_bin(bin); + for (int segment_num = proj_data_info_sptr->get_min_segment_num(); segment_num <= proj_data_info_sptr->get_max_segment_num(); + ++segment_num) + for (int axial_pos_num = proj_data_info_sptr->get_min_axial_pos_num(segment_num); + axial_pos_num <= proj_data_info_sptr->get_max_axial_pos_num(segment_num); ++axial_pos_num) + for (int view_num = proj_data_info_sptr->get_min_view_num(); view_num <= proj_data_info_sptr->get_max_view_num(); + ++view_num) + for (int tang_pos_num = proj_data_info_sptr->get_min_tangential_pos_num(); + tang_pos_num <= proj_data_info_sptr->get_max_tangential_pos_num(); ++tang_pos_num) { + Bin bin(segment_num, view_num, axial_pos_num, tang_pos_num); + proj_matrix.get_symmetries_ptr()->find_basic_bin(bin); #if 0 if (std::find(already_processed.begin(), already_processed.end(), bin) != already_processed.end()) @@ -471,73 +410,61 @@ write_to_file(const std::string& output_filename_prefix, already_processed.push_back(bin); #else - if (is_null_ptr(already_processed[bin.segment_num()])) - { - // range attempts to take into account that symmetries normally bring axial_pos_num back to 0 or 1 - already_processed[bin.segment_num()]. - reset(new apos_t(std::min(0,proj_data_info_sptr->get_min_axial_pos_num(bin.segment_num())), - std::max(1,proj_data_info_sptr->get_max_axial_pos_num(bin.segment_num())))); - } - if (is_null_ptr((*already_processed[bin.segment_num()])[bin.axial_pos_num()])) - { - (*already_processed[bin.segment_num()])[bin.axial_pos_num()]. - reset(new vpos_t(proj_data_info_sptr->get_min_view_num(), - proj_data_info_sptr->get_max_view_num())); - } - if (is_null_ptr((*(*already_processed[bin.segment_num()])[bin.axial_pos_num()])[bin.view_num()])) - { - // range takes into account that symmetries bring negative tangential_pos_num to positive - (*(*already_processed[bin.segment_num()])[bin.axial_pos_num()])[bin.view_num()]. - reset(new tpos_t(proj_data_info_sptr->get_min_tangential_pos_num(), - std::max(proj_data_info_sptr->get_max_tangential_pos_num(), - -proj_data_info_sptr->get_min_tangential_pos_num()))); - (*(*already_processed[bin.segment_num()])[bin.axial_pos_num()])[bin.view_num()]->fill(false); - } - if ((*(*(*already_processed[bin.segment_num()])[bin.axial_pos_num()])[bin.view_num()])[bin.tangential_pos_num()]) - continue; - - (*(*(*already_processed[bin.segment_num()])[bin.axial_pos_num()])[bin.view_num()])[bin.tangential_pos_num()]=true; + if (is_null_ptr(already_processed[bin.segment_num()])) { + // range attempts to take into account that symmetries normally bring axial_pos_num back to 0 or 1 + already_processed[bin.segment_num()].reset( + new apos_t(std::min(0, proj_data_info_sptr->get_min_axial_pos_num(bin.segment_num())), + std::max(1, proj_data_info_sptr->get_max_axial_pos_num(bin.segment_num())))); + } + if (is_null_ptr((*already_processed[bin.segment_num()])[bin.axial_pos_num()])) { + (*already_processed[bin.segment_num()])[bin.axial_pos_num()].reset( + new vpos_t(proj_data_info_sptr->get_min_view_num(), proj_data_info_sptr->get_max_view_num())); + } + if (is_null_ptr((*(*already_processed[bin.segment_num()])[bin.axial_pos_num()])[bin.view_num()])) { + // range takes into account that symmetries bring negative tangential_pos_num to positive + (*(*already_processed[bin.segment_num()])[bin.axial_pos_num()])[bin.view_num()].reset( + new tpos_t(proj_data_info_sptr->get_min_tangential_pos_num(), + std::max(proj_data_info_sptr->get_max_tangential_pos_num(), + -proj_data_info_sptr->get_min_tangential_pos_num()))); + (*(*already_processed[bin.segment_num()])[bin.axial_pos_num()])[bin.view_num()]->fill(false); + } + if ((*(*(*already_processed[bin.segment_num()])[bin.axial_pos_num()])[bin.view_num()])[bin.tangential_pos_num()]) + continue; + + (*(*(*already_processed[bin.segment_num()])[bin.axial_pos_num()])[bin.view_num()])[bin.tangential_pos_num()] = true; #endif - //if (!proj_matrix.get_symmetries_ptr()->is_basic(bin)) - // continue; - - proj_matrix.get_proj_matrix_elems_for_one_bin(lor,bin); - if (write_lor(fst, lor) == Succeeded::no) - return Succeeded::no; - } + // if (!proj_matrix.get_symmetries_ptr()->is_basic(bin)) + // continue; + + proj_matrix.get_proj_matrix_elems_for_one_bin(lor, bin); + if (write_lor(fst, lor) == Succeeded::no) + return Succeeded::no; + } } return Succeeded::yes; } Succeeded -ProjMatrixByBinFromFile:: -read_data() -{ +ProjMatrixByBinFromFile::read_data() { std::ifstream fst; open_read_binary(fst, data_filename.c_str()); - + // defined here to avoid reallocation for every bin ProjMatrixElemsForOneBin lor; - while(!fst.eof()) - { - readReturnType return_value=read_lor(fst, lor); - if (return_value == readReturnType::problem) - return Succeeded::no; - if (return_value == readReturnType::eof) - return Succeeded::yes; - this->cache_proj_matrix_elems_for_one_bin(lor); - } + while (!fst.eof()) { + readReturnType return_value = read_lor(fst, lor); + if (return_value == readReturnType::problem) + return Succeeded::no; + if (return_value == readReturnType::eof) + return Succeeded::yes; + this->cache_proj_matrix_elems_for_one_bin(lor); + } return Succeeded::yes; } - -void -ProjMatrixByBinFromFile:: -calculate_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& lor - ) const -{ - //error("ProjMatrixByBinFromFile element not found in cache (and hence file)"); +void +ProjMatrixByBinFromFile::calculate_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& lor) const { + // error("ProjMatrixByBinFromFile element not found in cache (and hence file)"); lor.erase(); } END_NAMESPACE_STIR - diff --git a/src/recon_buildblock/ProjMatrixByBinSPECTUB.cxx b/src/recon_buildblock/ProjMatrixByBinSPECTUB.cxx index fea20d0fe0..13e2be4016 100644 --- a/src/recon_buildblock/ProjMatrixByBinSPECTUB.cxx +++ b/src/recon_buildblock/ProjMatrixByBinSPECTUB.cxx @@ -42,7 +42,7 @@ #include "stir/info.h" #include "stir/CPUTimer.h" #ifdef STIR_OPENMP -#include "stir/num_threads.h" +# include "stir/num_threads.h" #endif //#include "boost/cstdint.hpp" @@ -69,28 +69,19 @@ /* UB-SPECT global variables */ namespace SPECTUB { - wm_da_type wm; - wmh_type wmh; - float * Rrad; -} +wm_da_type wm; +wmh_type wmh; +float* Rrad; +} // namespace SPECTUB START_NAMESPACE_STIR +const char* const ProjMatrixByBinSPECTUB::registered_name = "SPECT UB"; -const char * const -ProjMatrixByBinSPECTUB::registered_name = -"SPECT UB"; - -ProjMatrixByBinSPECTUB:: -ProjMatrixByBinSPECTUB() -{ - set_defaults(); -} +ProjMatrixByBinSPECTUB::ProjMatrixByBinSPECTUB() { set_defaults(); } -void -ProjMatrixByBinSPECTUB:: -initialise_keymap() -{ +void +ProjMatrixByBinSPECTUB::initialise_keymap() { parser.add_start_key("Projection Matrix By Bin SPECT UB Parameters"); ProjMatrixByBin::initialise_keymap(); @@ -111,31 +102,27 @@ initialise_keymap() parser.add_stop_key("End Projection Matrix By Bin SPECT UB Parameters"); } - void -ProjMatrixByBinSPECTUB::set_defaults() -{ +ProjMatrixByBinSPECTUB::set_defaults() { ProjMatrixByBin::set_defaults(); - - this->already_setup= false; - - this->keep_all_views_in_cache=false; - minimum_weight=0.0; - maximum_number_of_sigmas= 2.; - spatial_resolution_PSF= 0.00001; - psf_type= "Geometrical"; - collimator_slope= 0.; - collimator_sigma_0= 0.; - attenuation_type= "no"; - attenuation_map= ""; - mask_type= "no"; - mask_file= ""; + this->already_setup = false; + + this->keep_all_views_in_cache = false; + minimum_weight = 0.0; + maximum_number_of_sigmas = 2.; + spatial_resolution_PSF = 0.00001; + psf_type = "Geometrical"; + collimator_slope = 0.; + collimator_sigma_0 = 0.; + attenuation_type = "no"; + attenuation_map = ""; + mask_type = "no"; + mask_file = ""; } bool -ProjMatrixByBinSPECTUB::post_processing() -{ +ProjMatrixByBinSPECTUB::post_processing() { if (ProjMatrixByBin::post_processing() == true) return true; @@ -145,610 +132,553 @@ ProjMatrixByBinSPECTUB::post_processing() else this->attenuation_image_sptr.reset(); - this->already_setup= false; + this->already_setup = false; return false; } bool -ProjMatrixByBinSPECTUB:: -get_keep_all_views_in_cache() const -{ +ProjMatrixByBinSPECTUB::get_keep_all_views_in_cache() const { return this->keep_all_views_in_cache; } void -ProjMatrixByBinSPECTUB:: -set_keep_all_views_in_cache(bool value) -{ - if (this->keep_all_views_in_cache != value) - { - this->keep_all_views_in_cache = value; - this->already_setup = false; - } +ProjMatrixByBinSPECTUB::set_keep_all_views_in_cache(bool value) { + if (this->keep_all_views_in_cache != value) { + this->keep_all_views_in_cache = value; + this->already_setup = false; + } } std::string -ProjMatrixByBinSPECTUB:: -get_attenuation_type() const -{ +ProjMatrixByBinSPECTUB::get_attenuation_type() const { return this->attenuation_type; } void -ProjMatrixByBinSPECTUB:: -set_attenuation_type(const std::string& value) -{ - if (this->attenuation_type != boost::algorithm::to_lower_copy(value)) - { - this->attenuation_type = boost::algorithm::to_lower_copy(value); - if ( this->attenuation_type != "no" && this->attenuation_type != "simple" && this->attenuation_type != "full") - error("attenuation_type has to be No, Simple or Full"); - this->already_setup = false; - } +ProjMatrixByBinSPECTUB::set_attenuation_type(const std::string& value) { + if (this->attenuation_type != boost::algorithm::to_lower_copy(value)) { + this->attenuation_type = boost::algorithm::to_lower_copy(value); + if (this->attenuation_type != "no" && this->attenuation_type != "simple" && this->attenuation_type != "full") + error("attenuation_type has to be No, Simple or Full"); + this->already_setup = false; + } } -shared_ptr > -ProjMatrixByBinSPECTUB:: -get_attenuation_image_sptr() const -{ +shared_ptr> +ProjMatrixByBinSPECTUB::get_attenuation_image_sptr() const { return this->attenuation_image_sptr; } void -ProjMatrixByBinSPECTUB:: -set_attenuation_image_sptr(const shared_ptr > value) -{ +ProjMatrixByBinSPECTUB::set_attenuation_image_sptr(const shared_ptr> value) { this->attenuation_image_sptr = value; - if (this->attenuation_type == "no") - { - info("Setting attenuation type to 'simple'"); - this->set_attenuation_type("simple"); - } + if (this->attenuation_type == "no") { + info("Setting attenuation type to 'simple'"); + this->set_attenuation_type("simple"); + } this->already_setup = false; } void -ProjMatrixByBinSPECTUB:: -set_attenuation_image_sptr(const std::string& value) -{ +ProjMatrixByBinSPECTUB::set_attenuation_image_sptr(const std::string& value) { this->attenuation_map = value; - shared_ptr > im_sptr(read_from_file >(this->attenuation_map)); + shared_ptr> im_sptr(read_from_file>(this->attenuation_map)); set_attenuation_image_sptr(im_sptr); } void -ProjMatrixByBinSPECTUB:: -set_resolution_model(const float collimator_sigma_0_in_mm, const float collimator_slope_in_mm, const bool full_3D) -{ +ProjMatrixByBinSPECTUB::set_resolution_model(const float collimator_sigma_0_in_mm, const float collimator_slope_in_mm, + const bool full_3D) { this->collimator_sigma_0 = collimator_sigma_0_in_mm / 10; this->collimator_slope = collimator_slope_in_mm / 10; - if (collimator_slope == 0.F && collimator_sigma_0 == 0.F) - { - this->psf_type = "geometrical"; - } - else if (full_3D) - { - this->psf_type = "3d"; - } - else - { - this->psf_type = "2d"; - } + if (collimator_slope == 0.F && collimator_sigma_0 == 0.F) { + this->psf_type = "geometrical"; + } else if (full_3D) { + this->psf_type = "3d"; + } else { + this->psf_type = "2d"; + } this->already_setup = false; } void -ProjMatrixByBinSPECTUB:: -set_up( - const shared_ptr& proj_data_info_ptr_v, - const shared_ptr >& density_info_ptr // TODO should be Info only - ) -{ +ProjMatrixByBinSPECTUB::set_up(const shared_ptr& proj_data_info_ptr_v, + const shared_ptr>& density_info_ptr // TODO should be Info only +) { ProjMatrixByBin::set_up(proj_data_info_ptr_v, density_info_ptr); #ifdef STIR_OPENMP - if (!this->keep_all_views_in_cache) - { - warning("SPECTUB matrix can currently only use single-threaded code unless all views are kept. Setting num_threads to 1"); - set_num_threads(1); - } + if (!this->keep_all_views_in_cache) { + warning("SPECTUB matrix can currently only use single-threaded code unless all views are kept. Setting num_threads to 1"); + set_num_threads(1); + } #endif using namespace SPECTUB; - const VoxelsOnCartesianGrid * image_info_ptr = - dynamic_cast*> (density_info_ptr.get()); + const VoxelsOnCartesianGrid* image_info_ptr = dynamic_cast*>(density_info_ptr.get()); if (image_info_ptr == NULL) error("ProjMatrixByBinFromFile set-up with a wrong type of DiscretisedDensity\n"); - if (this->already_setup) - { - if (this->densel_range == image_info_ptr->get_index_range() && - this->voxel_size == image_info_ptr->get_voxel_size() && - this->origin == image_info_ptr->get_origin() && - *proj_data_info_ptr_v == *this->proj_data_info_ptr) - { - // stored matrix should be compatible, so we can just reuse it - return; - } - else - { - this->clear_cache(); - this->delete_UB_SPECT_arrays(); - } + if (this->already_setup) { + if (this->densel_range == image_info_ptr->get_index_range() && this->voxel_size == image_info_ptr->get_voxel_size() && + this->origin == image_info_ptr->get_origin() && *proj_data_info_ptr_v == *this->proj_data_info_ptr) { + // stored matrix should be compatible, so we can just reuse it + return; + } else { + this->clear_cache(); + this->delete_UB_SPECT_arrays(); + } + } + + this->proj_data_info_ptr = proj_data_info_ptr_v; + symmetries_sptr.reset(new TrivialDataSymmetriesForBins(proj_data_info_ptr_v)); + + this->densel_range = image_info_ptr->get_index_range(); + this->voxel_size = image_info_ptr->get_voxel_size(); + this->origin = image_info_ptr->get_origin(); + + const ProjDataInfoCylindricalArcCorr* proj_Data_Info_Cylindrical = + dynamic_cast(this->proj_data_info_ptr.get()); + + CPUTimer timer; + timer.start(); + + //... fill prj structure from projection data info + + prj.Nbin = this->proj_data_info_ptr->get_num_tangential_poss(); + prj.szcm = this->proj_data_info_ptr->get_scanner_ptr()->get_default_bin_size() / 10.; + prj.Nang = this->proj_data_info_ptr->get_num_views(); + + //... fill vol structure from image_info_ptr + vol.Ncol = image_info_ptr->get_x_size(); // Image: number of columns + vol.Nrow = image_info_ptr->get_y_size(); // Image: number of rows + vol.Nsli = image_info_ptr->get_z_size(); // Image: and projections: number of slices + vol.szcm = image_info_ptr->get_voxel_size().x() / 10.; // Image: voxel size (cm) + vol.thcm = image_info_ptr->get_voxel_size().z() / 10.; // Image: slice thickness (cm) + + //..... geometrical and other derived parameters of the volume structure............... + vol.Npix = vol.Ncol * vol.Nrow; + vol.Nvox = vol.Npix * vol.Nsli; + + vol.Ncold2 = (float)vol.Ncol / (float)2.; // half of the matrix Nvox (integer or semi-integer) + vol.Nrowd2 = (float)vol.Nrow / (float)2.; // half of the matrix NbOS (integer or semi-integer) + vol.Nslid2 = (float)vol.Nsli / (float)2.; // half of the number of slices (integer or semi-integer) + + vol.Xcmd2 = vol.Ncold2 * vol.szcm; // Half of the size of the image volume, dimension x (cm); + vol.Ycmd2 = vol.Nrowd2 * vol.szcm; // Half of the size of the image volume, dimension y (cm); + vol.Zcmd2 = vol.Nslid2 * vol.thcm; // Half of the size of the image volume, dimension z (cm); + + vol.x0 = ((float)0.5 - vol.Ncold2) * vol.szcm; // coordinate x of the first voxel + vol.y0 = ((float)0.5 - vol.Nrowd2) * vol.szcm; // coordinate y of the first voxel + vol.z0 = ((float)0.5 - vol.Nslid2) * vol.thcm; // coordinate z of the first voxel + + vol.first_sl = 0; // Image: first slice to take into account (no weight bellow) + vol.last_sl = vol.Nsli; // Image: last slice to take into account (no weights above) + + wmh.vol = vol; + + //...... geometrical dimensions of the voxel structure ................. + vox.szcm = vol.szcm; + vox.thcm = vol.thcm; + + //... projecction parameters .......................................... + prj.ang0 = this->proj_data_info_ptr->get_scanner_ptr()->get_default_intrinsic_tilt() * float(180 / _PI); + prj.incr = proj_Data_Info_Cylindrical->get_azimuthal_angle_sampling() * float(180 / _PI); + prj.thcm = proj_Data_Info_Cylindrical->get_axial_sampling(0) / 10; + + //.......geometrical and other derived parameters of projection structure........... + prj.Nsli = proj_Data_Info_Cylindrical->get_num_axial_poss(0); // number of slices + prj.lngcm = prj.Nbin * prj.szcm; // length in cm of the detection line + prj.Nbp = prj.Nbin * prj.Nsli; // number of bins for each projection angle (2D-projection) + prj.Nbt = prj.Nbp * prj.Nang; // total number of bins considering all the projection angles + prj.Nbind2 = (float)prj.Nbin / (float)2.; // half of the number of bins in a detection line (integer or semi-integer) + prj.lngcmd2 = prj.lngcm / (float)2.; // half of the length of detection line (cm) + prj.Nslid2 = (float)prj.Nsli / (float)2.; // half of the number of slices (integer or semi-integer) + + //... number of subsets (always 1 in STIR) ................................................ + prj.NOS = prj.Nang; // number of subset in which to split the weight matrix + prj.NangOS = prj.Nang / prj.NOS; // number of angles of projection in each subset + prj.NbOS = prj.Nbt / prj.NOS; // total number of bins in each subset + + wmh.prj = prj; + // wmh.NpixAngOS = vol.Npix * prj.NangOS; + + if (abs(wmh.prj.thcm - vox.thcm) > .01F) + error(boost::format("SPECTUB Matrix (probably) only works with equal z-sampling for projection data (%1%) and image (%2%)") % + (wmh.prj.thcm * 10) % (vol.thcm * 10)); + if (abs(wmh.prj.Nsli - vol.Nsli) > .01F) + error(boost::format( + "SPECTUB Matrix (probably) only works with equal number of slices for projection data (%1%) and image (%2%)") % + wmh.prj.Nsli % vol.Nsli); + //....rotation radius ................................................. + const VectorWithOffset radius_all_views = proj_Data_Info_Cylindrical->get_ring_radii_for_all_views(); + + Rrad = new float[wmh.prj.Nang]; + for (int i = 0; i < wmh.prj.Nang; i++) { + // note: convert to cm for UB SPECT library + Rrad[i] = radius_all_views[i] / 10; + } + + //... resolution parameters .............................................. + wmh.min_w = minimum_weight; + wmh.maxsigm = maximum_number_of_sigmas; + wmh.psfres = spatial_resolution_PSF; + + bin.szcm = wmh.prj.szcm; + bin.szcmd2 = bin.szcm / (float)2.; + bin.thcm = wmh.prj.thcm; + bin.thcmd2 = bin.thcm / (float)2.; + bin.szdx = bin.szcm / wmh.psfres; + bin.thdx = bin.thcm / wmh.psfres; + + std::stringstream info_stream; + + //....PSF and collimator parameters ........................................ + boost::algorithm::to_lower(psf_type); + if (psf_type == "geometrical") + wmh.do_psf = false; + else { + wmh.do_psf = true; + + if (psf_type == "3d") { + wmh.do_psf_3d = true; + info_stream << "3D PSF Correction. Parallel geometry" << std::endl; + } else { + if (psf_type == "2d") { + wmh.do_psf_3d = false; + info_stream << "2D PSF Correction. Parallel geometry" << std::endl; + } else { + // error_wm_SPECT( 120, psf_type ); + error("PSF type has to be 2D, 3D or Geometrical"); + } + } + } + + if (wmh.do_psf) { + wmh.predef_col = false; + wmh.COL.A = collimator_slope; + wmh.COL.B = collimator_sigma_0; + } else { + // code to enable fan-beam collimator at some point... (not validated) + // before enabling, you will have to change the parameter file to give parameters of the fan-beam + // int num = collimator_number; + // if ( num ==0 ) { + wmh.COL.do_fb = false; + info_stream << "No correction for PSF. Parallel geometry" << std::endl; + //} + // else { + // wmh.COL.do_fb = true; + // wmh.COL.F = collimator_number; + // info_stream << "No correction for PSF. Fanbeam geometry with focal distance = " << wmh.COL.F << " cm " << std::endl; + //} + } + + //... attenuation parameters ......................... + boost::algorithm::to_lower(attenuation_type); + if (attenuation_type == "no") { + wmh.do_att = false; + } else { + wmh.do_att = true; + + if (attenuation_type == "simple") + wmh.do_full_att = false; + else { + if (attenuation_type == "full") + wmh.do_full_att = true; + else { + // error_wm_SPECT( 123, attenuation_type ); + error("attenuation_type has to be No, Simple or Full"); + } + } + } + + //... masking parameters............................. + boost::algorithm::to_lower(mask_type); + if (mask_type == "no") { + wmh.do_msk = wmh.do_msk_cyl = wmh.do_msk_att = wmh.do_msk_file = false; + } else { + wmh.do_msk = true; + if (mask_type == "cylinder") + wmh.do_msk_cyl = true; + else { + if (mask_type == "attenuation map") + wmh.do_msk_att = true; + else { + if (mask_type == "explicit mask") { + wmh.do_msk_file = true; + + wmh.msk_fn = mask_file; + + info_stream << "MASK filename = " << wmh.msk_fn << std::endl; + } else { + // error_wm_SPECT( 125, mask_type); + error("mask_type has to be No, Cylinder, Attenuation Map or Explicit Mask"); + } + } + } + } + + wmh.do_msk_slc = false; + + if (vol.first_sl > 0 || vol.last_sl < vol.Nsli) { + wmh.do_msk = true; + wmh.do_msk_slc = true; + } + + wm.do_save_STIR = true; + + //:: Control of read parameters + info_stream << "" << std::endl; + info_stream << "Parameters of SPECT UB matrix: (in cm)" << std::endl; + info_stream << "Image grid side row: " << wmh.vol.Nrow << "\tcol: " << wmh.vol.Ncol + << "\ttransverse voxel_size: " << wmh.vol.szcm << std::endl; + info_stream << "Number of slices: " << wmh.vol.Nsli << "\tslice_thickness: " << wmh.vol.thcm << std::endl; + info_stream << "Number of bins: " << wmh.prj.Nbin << "\tbin size: " << wmh.prj.szcm << "\taxial size: " << wmh.prj.thcm + << std::endl; + info_stream << "Number of angles: " << wmh.prj.Nang << "\tAngle increment: " << wmh.prj.incr + << "\tFirst angle: " << wmh.prj.ang0 << std::endl; + info_stream << "Number of subsets: " << wmh.prj.NOS << std::endl; + if (wmh.do_att) { + info_stream << "Correction for attenuation: " << wmh.att_fn << "\t\tdo_msk_att: " << wmh.do_msk_att << std::endl; + info_stream << "Attenuation map: " << wmh.att_fn << std::endl; + } + info_stream << "Rotation radii: {" << Rrad[0]; + for (int i = 1; i < prj.Nang; ++i) { + info_stream << ", " << Rrad[i]; + } + info_stream << "}\n"; + info_stream << "Minimum weight: " << wmh.min_w << std::endl; + + info(info_stream.str()); + + //... to sort angles into subsets ...................................... + + prj.order = new int[prj.Nang]; + index_calc(prj.order); + + //... to fill ang structure ............................................ + + ang = new angle_type[prj.Nang]; + fill_ang(ang); + + //... to fill high resolution discrete distribution functions .............. + + if (!wmh.do_psf) { + + //... trapezoid projection of a square voxel on a line ................. + + for (int i = 0; i < prj.Nang; i++) { + + ang[i].vxprj.val = new float[ang[i].vxprj.lng]; + ang[i].vxprj.acu = new float[ang[i].vxprj.lng]; + + calc_vxprj(&ang[i]); + } + } else { + + //... Gaussian density and distribution functions ........................ + + gaussdens.lngd2 = + (int)(wmh.maxsigm / wmh.psfres); // half of the length of the gaussian density function (in resolution elements) + gaussdens.lng = gaussdens.lngd2 * 2; // length of the gaussian density function (in resolution elements). + gaussdens.res = wmh.psfres; + + gaussdens.val = new float[gaussdens.lng + 1]; // density function allocation + gaussdens.acu = new float[gaussdens.lng]; // distribution function allocation + calc_gauss(&gaussdens); // to calculate N(0,1) density function and distribution function } - this->proj_data_info_ptr=proj_data_info_ptr_v; - symmetries_sptr.reset( - new TrivialDataSymmetriesForBins(proj_data_info_ptr_v)); - - this->densel_range = image_info_ptr->get_index_range(); - this->voxel_size = image_info_ptr->get_voxel_size(); - this->origin = image_info_ptr->get_origin(); - - const ProjDataInfoCylindricalArcCorr * proj_Data_Info_Cylindrical = - dynamic_cast (this->proj_data_info_ptr.get()); - - CPUTimer timer; - timer.start(); - - //... fill prj structure from projection data info - - prj.Nbin = this->proj_data_info_ptr->get_num_tangential_poss(); - prj.szcm = this->proj_data_info_ptr->get_scanner_ptr()->get_default_bin_size()/10.; - prj.Nang = this->proj_data_info_ptr->get_num_views(); - - - //... fill vol structure from image_info_ptr - vol.Ncol = image_info_ptr->get_x_size(); // Image: number of columns - vol.Nrow = image_info_ptr->get_y_size(); // Image: number of rows - vol.Nsli = image_info_ptr->get_z_size(); // Image: and projections: number of slices - vol.szcm = image_info_ptr->get_voxel_size().x()/10.; // Image: voxel size (cm) - vol.thcm = image_info_ptr->get_voxel_size().z()/10.; // Image: slice thickness (cm) - - //..... geometrical and other derived parameters of the volume structure............... - vol.Npix = vol.Ncol * vol.Nrow; - vol.Nvox = vol.Npix * vol.Nsli; - - vol.Ncold2 = (float)vol.Ncol / (float)2.; // half of the matrix Nvox (integer or semi-integer) - vol.Nrowd2 = (float)vol.Nrow / (float)2.; // half of the matrix NbOS (integer or semi-integer) - vol.Nslid2 = (float)vol.Nsli / (float)2.; // half of the number of slices (integer or semi-integer) - - vol.Xcmd2 = vol.Ncold2 * vol.szcm; // Half of the size of the image volume, dimension x (cm); - vol.Ycmd2 = vol.Nrowd2 * vol.szcm; // Half of the size of the image volume, dimension y (cm); - vol.Zcmd2 = vol.Nslid2 * vol.thcm; // Half of the size of the image volume, dimension z (cm); - - vol.x0 = ( (float)0.5 - vol.Ncold2) * vol.szcm; // coordinate x of the first voxel - vol.y0 = ( (float)0.5 - vol.Nrowd2) * vol.szcm; // coordinate y of the first voxel - vol.z0 = ( (float)0.5 - vol.Nslid2) * vol.thcm; // coordinate z of the first voxel - - vol.first_sl = 0; // Image: first slice to take into account (no weight bellow) - vol.last_sl = vol.Nsli; // Image: last slice to take into account (no weights above) - - wmh.vol = vol; - - //...... geometrical dimensions of the voxel structure ................. - vox.szcm = vol.szcm; - vox.thcm = vol.thcm; - - //... projecction parameters .......................................... - prj.ang0 = this->proj_data_info_ptr->get_scanner_ptr()->get_default_intrinsic_tilt() * float(180/_PI); - prj.incr = proj_Data_Info_Cylindrical->get_azimuthal_angle_sampling() * float(180/_PI); - prj.thcm = proj_Data_Info_Cylindrical->get_axial_sampling(0)/10; - - //.......geometrical and other derived parameters of projection structure........... - prj.Nsli = proj_Data_Info_Cylindrical->get_num_axial_poss(0); // number of slices - prj.lngcm = prj.Nbin * prj.szcm; // length in cm of the detection line - prj.Nbp = prj.Nbin * prj.Nsli; // number of bins for each projection angle (2D-projection) - prj.Nbt = prj.Nbp * prj.Nang; // total number of bins considering all the projection angles - prj.Nbind2 = (float)prj.Nbin / (float)2.; // half of the number of bins in a detection line (integer or semi-integer) - prj.lngcmd2 = prj.lngcm / (float)2.; // half of the length of detection line (cm) - prj.Nslid2 = (float)prj.Nsli / (float)2.; // half of the number of slices (integer or semi-integer) - - - //... number of subsets (always 1 in STIR) ................................................ - prj.NOS = prj.Nang; // number of subset in which to split the weight matrix - prj.NangOS = prj.Nang / prj.NOS; // number of angles of projection in each subset - prj.NbOS = prj.Nbt / prj.NOS; // total number of bins in each subset - - wmh.prj = prj; - // wmh.NpixAngOS = vol.Npix * prj.NangOS; - - if (abs(wmh.prj.thcm - vox.thcm)>.01F) - error(boost::format("SPECTUB Matrix (probably) only works with equal z-sampling for projection data (%1%) and image (%2%)") - % (wmh.prj.thcm*10) % (vol.thcm*10)); - if (abs(wmh.prj.Nsli - vol.Nsli)>.01F) - error(boost::format("SPECTUB Matrix (probably) only works with equal number of slices for projection data (%1%) and image (%2%)") - % wmh.prj.Nsli % vol.Nsli); - //....rotation radius ................................................. - const VectorWithOffset radius_all_views = - proj_Data_Info_Cylindrical->get_ring_radii_for_all_views(); - - Rrad = new float [ wmh.prj.Nang ]; - for ( int i = 0 ; i < wmh.prj.Nang ; i++ ) { - // note: convert to cm for UB SPECT library - Rrad[ i ] = radius_all_views[i]/10; - } - - //... resolution parameters .............................................. - wmh.min_w = minimum_weight; - wmh.maxsigm = maximum_number_of_sigmas; - wmh.psfres = spatial_resolution_PSF; - - bin.szcm = wmh.prj.szcm; - bin.szcmd2 = bin.szcm / (float)2.; - bin.thcm = wmh.prj.thcm; - bin.thcmd2 = bin.thcm / (float)2.; - bin.szdx = bin.szcm / wmh.psfres; - bin.thdx = bin.thcm / wmh.psfres; - - std::stringstream info_stream; - - //....PSF and collimator parameters ........................................ - boost::algorithm::to_lower(psf_type); - if ( psf_type == "geometrical" ) wmh.do_psf = false; - else{ - wmh.do_psf = true; - - if ( psf_type == "3d" ) { - wmh.do_psf_3d = true; - info_stream << "3D PSF Correction. Parallel geometry" << std::endl; - } - else { - if ( psf_type== "2d" ) { - wmh.do_psf_3d = false; - info_stream << "2D PSF Correction. Parallel geometry" << std::endl; - } - else - { - //error_wm_SPECT( 120, psf_type ); - error("PSF type has to be 2D, 3D or Geometrical"); - } - } - } - - if ( wmh.do_psf ){ - wmh.predef_col = false; - wmh.COL.A = collimator_slope; - wmh.COL.B = collimator_sigma_0; - } - else{ - // code to enable fan-beam collimator at some point... (not validated) - // before enabling, you will have to change the parameter file to give parameters of the fan-beam - //int num = collimator_number; - //if ( num ==0 ) { - wmh.COL.do_fb = false; - info_stream << "No correction for PSF. Parallel geometry" << std::endl; - //} - //else { - // wmh.COL.do_fb = true; - // wmh.COL.F = collimator_number; - // info_stream << "No correction for PSF. Fanbeam geometry with focal distance = " << wmh.COL.F << " cm " << std::endl; - //} - } - - //... attenuation parameters ......................... - boost::algorithm::to_lower(attenuation_type); - if ( attenuation_type == "no" ) { - wmh.do_att = false; - } - else{ - wmh.do_att = true; - - if ( attenuation_type == "simple" ) wmh.do_full_att = false; - else { - if (attenuation_type == "full" ) wmh.do_full_att = true; - else - { - //error_wm_SPECT( 123, attenuation_type ); - error("attenuation_type has to be No, Simple or Full"); - } - } - } - - //... masking parameters............................. - boost::algorithm::to_lower(mask_type); - if( mask_type == "no" ){ - wmh.do_msk = wmh.do_msk_cyl = wmh.do_msk_att = wmh.do_msk_file = false; - } - else{ - wmh.do_msk = true; - if( mask_type == "cylinder" ) wmh.do_msk_cyl = true; - else { - if( mask_type == "attenuation map" ) wmh.do_msk_att = true; - else{ - if( mask_type == "explicit mask" ){ - wmh.do_msk_file = true; - - wmh.msk_fn = mask_file; - - info_stream << "MASK filename = " << wmh.msk_fn << std::endl; - } - else - { - // error_wm_SPECT( 125, mask_type); - error("mask_type has to be No, Cylinder, Attenuation Map or Explicit Mask"); - } - } - } - } - - wmh.do_msk_slc = false; - - if ( vol.first_sl > 0 || vol.last_sl < vol.Nsli ){ - wmh.do_msk = true; - wmh.do_msk_slc = true; - } - - wm.do_save_STIR = true; - - //:: Control of read parameters - info_stream << "" << std::endl; - info_stream << "Parameters of SPECT UB matrix: (in cm)" << std::endl; - info_stream << "Image grid side row: " << wmh.vol.Nrow << "\tcol: " << wmh.vol.Ncol << "\ttransverse voxel_size: " << wmh.vol.szcm<< std::endl; - info_stream << "Number of slices: " << wmh.vol.Nsli << "\tslice_thickness: " << wmh.vol.thcm << std::endl; - info_stream << "Number of bins: " << wmh.prj.Nbin << "\tbin size: " << wmh.prj.szcm << "\taxial size: " << wmh.prj.thcm << std::endl; - info_stream << "Number of angles: " << wmh.prj.Nang << "\tAngle increment: " << wmh.prj.incr << "\tFirst angle: " << wmh.prj.ang0 << std::endl; - info_stream << "Number of subsets: " << wmh.prj.NOS << std::endl; - if ( wmh.do_att ){ - info_stream << "Correction for attenuation: " << wmh.att_fn << "\t\tdo_msk_att: " << wmh.do_msk_att << std::endl; - info_stream << "Attenuation map: " << wmh.att_fn << std::endl; - } - info_stream << "Rotation radii: {" << Rrad[0]; - for (int i=1; ihas_same_characteristics(*attenuation_image_sptr)) - error("Currently the attenuation map and emission image must have the same dimension, orientation and voxel size"); - - attmap = new float [ vol.Nvox ]; - std::copy(attenuation_image_sptr->begin_all(), attenuation_image_sptr->end_all(),attmap); - for (int i = 0 ; i < wmh.vol.Nvox ; i++ ){ - if ((boost::math::isnan)(attmap [ i ])){ - attmap [ i ] = 0; - } - } - //read_att_map( attmap ); - } - else attmap = NULL; - - //... to generate mask.......................................................... - - if ( wmh.do_msk ) - { - msk_3d = new bool [ vol.Nvox ]; - msk_2d = new bool [ vol.Npix ]; - if (!wmh.do_msk_att && wmh.do_msk_file) - { - shared_ptr > mask_sptr( - read_from_file >(wmh.msk_fn)); - if (!density_info_ptr->has_same_characteristics(*mask_sptr)) - error("Currently the mask image and emission image must have the same dimension, orientation and voxel size"); - float * mask_from_file = new float [ vol.Nvox ]; - std::copy(mask_sptr->begin_all(), mask_sptr->end_all(),mask_from_file); - // call UB generate_msk pretending that this mask is an attenuation image - // we do this to avoid using its own read_msk_file - wmh.do_msk_file = false; - wmh.do_msk_att = true; - generate_msk( msk_3d, msk_2d, mask_from_file, &vol); - delete[] mask_from_file; - } - else - { - generate_msk( msk_3d, msk_2d, attmap, &vol); - } - } - else msk_2d = msk_3d = NULL; - - //... Initialization and memory allocation for the weight matrix ................... + //... to read attenuation map .................................................. - wm.NbOS = prj.NbOS; // number of rows in the weight matrix - wm.Nvox = vol.Nvox; // number of columnes in the weight matrix + if (wmh.do_att || wmh.do_msk_att) { + if (is_null_ptr(attenuation_image_sptr)) + error("Attenation image not set"); + if (!density_info_ptr->has_same_characteristics(*attenuation_image_sptr)) + error("Currently the attenuation map and emission image must have the same dimension, orientation and voxel size"); - //... setting PSF maximum size (in bins) and memory allocation for PSF values ....... + attmap = new float[vol.Nvox]; + std::copy(attenuation_image_sptr->begin_all(), attenuation_image_sptr->end_all(), attmap); + for (int i = 0; i < wmh.vol.Nvox; i++) { + if ((boost::math::isnan)(attmap[i])) { + attmap[i] = 0; + } + } + // read_att_map( attmap ); + } else + attmap = NULL; + + //... to generate mask.......................................................... + + if (wmh.do_msk) { + msk_3d = new bool[vol.Nvox]; + msk_2d = new bool[vol.Npix]; + if (!wmh.do_msk_att && wmh.do_msk_file) { + shared_ptr> mask_sptr(read_from_file>(wmh.msk_fn)); + if (!density_info_ptr->has_same_characteristics(*mask_sptr)) + error("Currently the mask image and emission image must have the same dimension, orientation and voxel size"); + float* mask_from_file = new float[vol.Nvox]; + std::copy(mask_sptr->begin_all(), mask_sptr->end_all(), mask_from_file); + // call UB generate_msk pretending that this mask is an attenuation image + // we do this to avoid using its own read_msk_file + wmh.do_msk_file = false; + wmh.do_msk_att = true; + generate_msk(msk_3d, msk_2d, mask_from_file, &vol); + delete[] mask_from_file; + } else { + generate_msk(msk_3d, msk_2d, attmap, &vol); + } + } else + msk_2d = msk_3d = NULL; - this->maxszb = max_psf_szb( ang ); // maximum PSF size (horizontal component of PSF) - NITEMS = new int * [prj.NOS]; - for (int kOS=0; kOSmaxszb = max_psf_szb(ang); // maximum PSF size (horizontal component of PSF) + NITEMS = new int*[prj.NOS]; + for (int kOS = 0; kOS < prj.NOS; ++kOS) { + NITEMS[kOS] = new int[wm.NbOS]; + } - if ( ( wm.ne = new (std::nothrow) int [ wm.NbOS + 1 ]) == 0 ) - { - // error_wm_SPECT(200,"wm.ne[]"); - error("Error allocating space to store number of elements for SPECTUB matrix"); - } - - - //... STIR indices ....................................................................... - - if ( wm.do_save_STIR ){ - wm.ns = new int [ prj.NbOS ]; - wm.nb = new int [ prj.NbOS ]; - wm.na = new int [ prj.NbOS ]; - - wm.nx = new short int [ vol.Nvox ]; - wm.ny = new short int [ vol.Nvox ]; - wm.nz = new short int [ vol.Nvox ]; - } - - //... memory allocation for wmh ......................................................... - - wmh.index = new int [ wmh.prj.NangOS ]; - wmh.Rrad = new float [ wmh.prj.NangOS ]; - - //.......................................................................................... - //... CALCULATION OF MATRICES .............................................................. - //.......................................................................................... - - //... LOOP: Subsets ................................................................. - subset_already_processed.assign(prj.NOS, false); - for ( int kOS = 0 ; kOS < prj.NOS ; kOS++ ){ - wmh.subset_ind = kOS; - - for ( int i = 0 ; i < prj.NangOS ; i ++ ){ - - wmh.index[ i ] = prj.order[ i + kOS * prj.NangOS ]; - wmh.Rrad [ i ] = Rrad[ wmh.index[ i ] ]; - } - - //... NITEMS initialization ...................... + //... double array wm.val and wm.col ..................................................... - for ( int i = 0 ; i < prj.NbOS ; i++ ) NITEMS[kOS][ i ] = 1; + if ((wm.val = new (std::nothrow) float*[wm.NbOS]) == NULL) { + // error_wm_SPECT( 200, "wm.val[]" ); + error("Error allocating space to store values for SPECTUB matrix"); + } + if ((wm.col = new (std::nothrow) int*[wm.NbOS]) == NULL) { + // error_wm_SPECT( 200, "wm.col[]" ); + error("Error allocating space to store column indices for SPECTUB matrix"); + } + //... array wm.ne ......................................................................... + + if ((wm.ne = new (std::nothrow) int[wm.NbOS + 1]) == 0) { + // error_wm_SPECT(200,"wm.ne[]"); + error("Error allocating space to store number of elements for SPECTUB matrix"); + } + + //... STIR indices ....................................................................... + + if (wm.do_save_STIR) { + wm.ns = new int[prj.NbOS]; + wm.nb = new int[prj.NbOS]; + wm.na = new int[prj.NbOS]; + + wm.nx = new short int[vol.Nvox]; + wm.ny = new short int[vol.Nvox]; + wm.nz = new short int[vol.Nvox]; + } - //... size estimations ........................................................ + //... memory allocation for wmh ......................................................... - wm_size_estimation ( kOS, ang, vox, bin, vol, prj, msk_3d, msk_2d, maxszb, &gaussdens, NITEMS[kOS] ); - - //cout << "\nwm_SPECT. Size estimation done. time (s): " << double( clock()-ini )/CLOCKS_PER_SEC <already_setup= true; + for (int i = 0; i < prj.NangOS; i++) { + + wmh.index[i] = prj.order[i + kOS * prj.NangOS]; + wmh.Rrad[i] = Rrad[wmh.index[i]]; + } + + //... NITEMS initialization ...................... + + for (int i = 0; i < prj.NbOS; i++) + NITEMS[kOS][i] = 1; + + //... size estimations ........................................................ + + wm_size_estimation(kOS, ang, vox, bin, vol, prj, msk_3d, msk_2d, maxszb, &gaussdens, NITEMS[kOS]); + + // cout << "\nwm_SPECT. Size estimation done. time (s): " << double( clock()-ini )/CLOCKS_PER_SEC <already_setup = true; } -ProjMatrixByBinSPECTUB* -ProjMatrixByBinSPECTUB::clone() const -{ - return new ProjMatrixByBinSPECTUB(*this); +ProjMatrixByBinSPECTUB* +ProjMatrixByBinSPECTUB::clone() const { + return new ProjMatrixByBinSPECTUB(*this); } -ProjMatrixByBinSPECTUB:: -~ProjMatrixByBinSPECTUB() -{ - // delete_UB_SPECT_arrays(); +ProjMatrixByBinSPECTUB::~ProjMatrixByBinSPECTUB() { + // delete_UB_SPECT_arrays(); } void -ProjMatrixByBinSPECTUB:: -delete_UB_SPECT_arrays() -{ +ProjMatrixByBinSPECTUB::delete_UB_SPECT_arrays() { if (!this->already_setup) return; //... freeing matrix memory.................................... using namespace SPECTUB; - delete [] Rrad; + delete[] Rrad; - if ( !wmh.do_psf ){ - for ( int i = 0 ; i < prj.Nang ; i++ ){ - delete [] ang[ i ].vxprj.val; - delete [] ang[ i ].vxprj.acu ; + if (!wmh.do_psf) { + for (int i = 0; i < prj.Nang; i++) { + delete[] ang[i].vxprj.val; + delete[] ang[i].vxprj.acu; } } - delete [] wm.val; - delete [] wm.col; - delete [] wm.ne; + delete[] wm.val; + delete[] wm.col; + delete[] wm.ne; //... freeing memory ............................................. - delete [] prj.order; - delete [] ang; - for (int kOS=0; kOS(wm.nz[ wm.col[ j ][ i ] ],wm.ny[ wm.col[ j ][ i ] ],wm.nx[ wm.col[ j ][ i ] ]), wm.val[ j ][ i ]); - lor.push_back( elem); + const ProjMatrixElemsForOneBin::value_type elem( + Coordinate3D(wm.nz[wm.col[j][i]], wm.ny[wm.col[j][i]], wm.nx[wm.col[j][i]]), wm.val[j][i]); + lor.push_back(elem); } - delete [] wm.val[ j ]; - delete [] wm.col[ j ]; + delete[] wm.val[j]; + delete[] wm.col[j]; this->cache_proj_matrix_elems_for_one_bin(lor); } - info(boost::format("Total time after transfering to ProjMatrixElemsForOneBin. time %1% (s)") % timer.value(), - 2); - + info(boost::format("Total time after transfering to ProjMatrixElemsForOneBin. time %1% (s)") % timer.value(), 2); } -void -ProjMatrixByBinSPECTUB:: -calculate_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& lor - ) const -{ - //error("ProjMatrixByBinSPECTUB element not found in cache (and hence file)"); - - const int view_num=lor.get_bin().view_num(); +void +ProjMatrixByBinSPECTUB::calculate_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& lor) const { + // error("ProjMatrixByBinSPECTUB element not found in cache (and hence file)"); + + const int view_num = lor.get_bin().view_num(); // find which "UB-subset" this view is in - int kOS=0; - for (kOS=0; kOSkeep_all_views_in_cache) - { - this->clear_cache(); - subset_already_processed.assign(prj.NOS,false); - } - info(boost::format("Computing matrix elements for view %1%") % view_num, - 2); - compute_one_subset(kOS); - subset_already_processed[kOS]=true; + if (!subset_already_processed[kOS]) { + if (!this->keep_all_views_in_cache) { + this->clear_cache(); + subset_already_processed.assign(prj.NOS, false); } + info(boost::format("Computing matrix elements for view %1%") % view_num, 2); + compute_one_subset(kOS); + subset_already_processed[kOS] = true; + } lor.erase(); } END_NAMESPACE_STIR - diff --git a/src/recon_buildblock/ProjMatrixByBinUsingInterpolation.cxx b/src/recon_buildblock/ProjMatrixByBinUsingInterpolation.cxx index ca76052e5a..906de46578 100644 --- a/src/recon_buildblock/ProjMatrixByBinUsingInterpolation.cxx +++ b/src/recon_buildblock/ProjMatrixByBinUsingInterpolation.cxx @@ -41,36 +41,25 @@ START_NAMESPACE_STIR -ProjMatrixByBinUsingInterpolation::JacobianForIntBP:: -JacobianForIntBP(const ProjDataInfoCylindrical* proj_data_info_ptr, bool exact) - - : R2(square(proj_data_info_ptr->get_ring_radius())), - ring_spacing2 (square(proj_data_info_ptr->get_ring_spacing())), - arccor(dynamic_cast(proj_data_info_ptr)!=0), - backprojection_normalisation - (proj_data_info_ptr->get_ring_spacing()/2/proj_data_info_ptr->get_num_views()), - use_exact_Jacobian_now(exact) -{ - assert(arccor || - dynamic_cast(proj_data_info_ptr)!=0); +ProjMatrixByBinUsingInterpolation::JacobianForIntBP::JacobianForIntBP(const ProjDataInfoCylindrical* proj_data_info_ptr, + bool exact) + + : R2(square(proj_data_info_ptr->get_ring_radius())), ring_spacing2(square(proj_data_info_ptr->get_ring_spacing())), + arccor(dynamic_cast(proj_data_info_ptr) != 0), + backprojection_normalisation(proj_data_info_ptr->get_ring_spacing() / 2 / proj_data_info_ptr->get_num_views()), + use_exact_Jacobian_now(exact) { + assert(arccor || dynamic_cast(proj_data_info_ptr) != 0); } -const char * const -ProjMatrixByBinUsingInterpolation::registered_name = - "Interpolation"; +const char* const ProjMatrixByBinUsingInterpolation::registered_name = "Interpolation"; -ProjMatrixByBinUsingInterpolation:: -ProjMatrixByBinUsingInterpolation() -{ - set_defaults(); -} +ProjMatrixByBinUsingInterpolation::ProjMatrixByBinUsingInterpolation() { set_defaults(); } -void -ProjMatrixByBinUsingInterpolation::initialise_keymap() -{ +void +ProjMatrixByBinUsingInterpolation::initialise_keymap() { parser.add_start_key("Interpolation Matrix Parameters"); parser.add_key("use_piecewise_linear_interpolation", &use_piecewise_linear_interpolation_now); - parser.add_key("use_exact_Jacobian",&use_exact_Jacobian_now); + parser.add_key("use_exact_Jacobian", &use_exact_Jacobian_now); ProjMatrixByBin::initialise_keymap(); parser.add_key("do_symmetry_90degrees_min_phi", &do_symmetry_90degrees_min_phi); parser.add_key("do_symmetry_180degrees_min_phi", &do_symmetry_180degrees_min_phi); @@ -80,10 +69,8 @@ ProjMatrixByBinUsingInterpolation::initialise_keymap() parser.add_stop_key("End Interpolation Matrix Parameters"); } - void -ProjMatrixByBinUsingInterpolation::set_defaults() -{ +ProjMatrixByBinUsingInterpolation::set_defaults() { ProjMatrixByBin::set_defaults(); do_symmetry_90degrees_min_phi = true; do_symmetry_180degrees_min_phi = true; @@ -95,93 +82,70 @@ ProjMatrixByBinUsingInterpolation::set_defaults() use_exact_Jacobian_now = true; } - bool -ProjMatrixByBinUsingInterpolation::post_processing() -{ +ProjMatrixByBinUsingInterpolation::post_processing() { if (ProjMatrixByBin::post_processing() == true) return true; return false; } - void -ProjMatrixByBinUsingInterpolation:: -set_up( +ProjMatrixByBinUsingInterpolation::set_up( const shared_ptr& proj_data_info_ptr_v, - const shared_ptr >& density_info_ptr // TODO should be Info only - ) -{ + const shared_ptr>& density_info_ptr // TODO should be Info only +) { ProjMatrixByBin::set_up(proj_data_info_ptr_v, density_info_ptr); - proj_data_info_ptr= proj_data_info_ptr_v; + proj_data_info_ptr = proj_data_info_ptr_v; - const VoxelsOnCartesianGrid * image_info_ptr = - dynamic_cast*> (density_info_ptr.get()); + const VoxelsOnCartesianGrid* image_info_ptr = dynamic_cast*>(density_info_ptr.get()); if (image_info_ptr == NULL) error("ProjMatrixByBinUsingInterpolation initialised with a wrong type of DiscretisedDensity\n"); - + densel_range = image_info_ptr->get_index_range(); voxel_size = image_info_ptr->get_voxel_size(); origin = image_info_ptr->get_origin(); - const float z_to_middle = - (densel_range.get_max_index() + densel_range.get_min_index())*voxel_size.z()/2.F; + const float z_to_middle = (densel_range.get_max_index() + densel_range.get_min_index()) * voxel_size.z() / 2.F; origin.z() -= z_to_middle; - symmetries_sptr.reset( - new DataSymmetriesForBins_PET_CartesianGrid(proj_data_info_ptr, - density_info_ptr, - do_symmetry_90degrees_min_phi, - do_symmetry_180degrees_min_phi, - do_symmetry_swap_segment, - do_symmetry_swap_s, - do_symmetry_shift_z)); + symmetries_sptr.reset(new DataSymmetriesForBins_PET_CartesianGrid( + proj_data_info_ptr, density_info_ptr, do_symmetry_90degrees_min_phi, do_symmetry_180degrees_min_phi, + do_symmetry_swap_segment, do_symmetry_swap_s, do_symmetry_shift_z)); - if (dynamic_cast(proj_data_info_ptr.get())==0) + if (dynamic_cast(proj_data_info_ptr.get()) == 0) error("ProjMatrixByBinUsingInterpolation needs ProjDataInfoCylindrical for jacobian\n"); jacobian = JacobianForIntBP(&(proj_data_info_cyl()), use_exact_Jacobian_now); // TODO assumes that all segments have span or not { - const float relative_vox_sampling = - voxel_size.z() / - proj_data_info_ptr->get_sampling_in_m(Bin(0,0,0,0)); - if (use_piecewise_linear_interpolation_now) - { - if (fabs(relative_vox_sampling-.5)<.01) - warning("Using piecewise-linear interpolation\n"); - else - { - warning("Switching OFF piecewise-linear interpolation\n"); - use_piecewise_linear_interpolation_now = false; - if (fabs(relative_vox_sampling-1)>.01) - warning("because non-standard voxel size.\n"); - } + const float relative_vox_sampling = voxel_size.z() / proj_data_info_ptr->get_sampling_in_m(Bin(0, 0, 0, 0)); + if (use_piecewise_linear_interpolation_now) { + if (fabs(relative_vox_sampling - .5) < .01) + warning("Using piecewise-linear interpolation\n"); + else { + warning("Switching OFF piecewise-linear interpolation\n"); + use_piecewise_linear_interpolation_now = false; + if (fabs(relative_vox_sampling - 1) > .01) + warning("because non-standard voxel size.\n"); } - } + } + } } ProjMatrixByBinUsingInterpolation* -ProjMatrixByBinUsingInterpolation::clone() const -{ - return new ProjMatrixByBinUsingInterpolation(*this); +ProjMatrixByBinUsingInterpolation::clone() const { + return new ProjMatrixByBinUsingInterpolation(*this); } // point should be w.r.t. middle of the scanner! -static inline -void -find_s_m_of_voxel(float& s, float& m, - const CartesianCoordinate3D& point, - const float cphi, const float sphi, - const float tantheta) -{ - s = (point.x()*cphi+point.y()*sphi); - - m = - point.z()- tantheta*(-point.x()*sphi+point.y()*cphi); -} +static inline void +find_s_m_of_voxel(float& s, float& m, const CartesianCoordinate3D& point, const float cphi, const float sphi, + const float tantheta) { + s = (point.x() * cphi + point.y() * sphi); + m = point.z() - tantheta * (-point.x() * sphi + point.y() * cphi); +} /* @@ -194,59 +158,43 @@ interpolationkernel[s_,ssize_,xsize_] \ */ // piecewise_linear interpolation for bin between -1 and 1 // vox -static inline -float -piecewise_linear_interpolate(const float s, const float vox_size) -{ - if (fabs(s)(2+vox_size)/2) +static inline float +piecewise_linear_interpolate(const float s, const float vox_size) { + if (fabs(s) < fabs(2 - vox_size) / 2) + return std::min(1.F, 2 / vox_size); + else if (fabs(s) > (2 + vox_size) / 2) return 0; else - return (-fabs(s)+(2+vox_size)/2)/vox_size; + return (-fabs(s) + (2 + vox_size) / 2) / vox_size; } // linear interpolation between -1 and 1 -static inline -float -linear_interpolate(const float t) -{ +static inline float +linear_interpolate(const float t) { const float abst = fabs(t); - if (abst>=1) + if (abst >= 1) return 0; else - return 1-abst; + return 1 - abst; } -static inline -float -interpolate_tang_pos(const float tang_pos_diff) -{ +static inline float +interpolate_tang_pos(const float tang_pos_diff) { return linear_interpolate(tang_pos_diff); } - float -ProjMatrixByBinUsingInterpolation:: -get_element(const Bin& bin, - const CartesianCoordinate3D& densel_ctr) const -{ +ProjMatrixByBinUsingInterpolation::get_element(const Bin& bin, const CartesianCoordinate3D& densel_ctr) const { const float phi = proj_data_info_ptr->get_phi(bin); const float cphi = cos(phi); - const float sphi = sin(phi); + const float sphi = sin(phi); const float tantheta = proj_data_info_ptr->get_tantheta(bin); float s_densel, m_densel; - find_s_m_of_voxel(s_densel, m_densel, - densel_ctr, - cphi, sphi, - tantheta); - const float s_diff = - s_densel - proj_data_info_ptr->get_s(bin); + find_s_m_of_voxel(s_densel, m_densel, densel_ctr, cphi, sphi, tantheta); + const float s_diff = s_densel - proj_data_info_ptr->get_s(bin); - const float m_diff = - m_densel - - proj_data_info_ptr->get_m(bin); + const float m_diff = m_densel - proj_data_info_ptr->get_m(bin); #if 0 // alternative way to get m_diff using other code @@ -277,44 +225,28 @@ get_element(const Bin& bin, < .001*proj_data_info_ptr->get_sampling_in_m(bin)); #endif - const float s_max = - std::max(cphi>sphi? voxel_size.x() : voxel_size.y(), - proj_data_info_ptr->get_sampling_in_s(bin)); - float result = interpolate_tang_pos(s_diff/s_max); - if (result==0) + const float s_max = std::max(cphi > sphi ? voxel_size.x() : voxel_size.y(), proj_data_info_ptr->get_sampling_in_s(bin)); + float result = interpolate_tang_pos(s_diff / s_max); + if (result == 0) return 0; - const float m_max = - std::max(voxel_size.z(), - proj_data_info_ptr->get_sampling_in_m(bin)); - - result *= - (use_piecewise_linear_interpolation_now? - piecewise_linear_interpolate(m_diff/m_max, - std::min(voxel_size.z(), - proj_data_info_ptr->get_sampling_in_m(bin)) - /m_max) - : - linear_interpolate(m_diff/m_max) - ); - - if (result==0) + const float m_max = std::max(voxel_size.z(), proj_data_info_ptr->get_sampling_in_m(bin)); + + result *= (use_piecewise_linear_interpolation_now + ? piecewise_linear_interpolate(m_diff / m_max, + std::min(voxel_size.z(), proj_data_info_ptr->get_sampling_in_m(bin)) / m_max) + : linear_interpolate(m_diff / m_max)); + + if (result == 0) return 0; - return - result * - jacobian(proj_data_info_cyl().get_average_ring_difference(bin.segment_num()), - proj_data_info_ptr->get_s(bin)); + return result * jacobian(proj_data_info_cyl().get_average_ring_difference(bin.segment_num()), proj_data_info_ptr->get_s(bin)); } - - -void -ProjMatrixByBinUsingInterpolation:: -calculate_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& lor) const -{ + +void +ProjMatrixByBinUsingInterpolation::calculate_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& lor) const { const Bin& bin = lor.get_bin(); - assert(bin.segment_num() >= proj_data_info_ptr->get_min_segment_num()); - assert(bin.segment_num() <= proj_data_info_ptr->get_max_segment_num()); + assert(bin.segment_num() >= proj_data_info_ptr->get_min_segment_num()); + assert(bin.segment_num() <= proj_data_info_ptr->get_max_segment_num()); assert(lor.size() == 0); @@ -332,7 +264,7 @@ calculate_proj_matrix_elems_for_one_bin( Horrible. */ - BasicCoordinate<3,int> c; + BasicCoordinate<3, int> c; int min1; int max1; // find z-range (this would depend on origin and the symmetries though) @@ -358,35 +290,21 @@ calculate_proj_matrix_elems_for_one_bin( /* Here we use geometric info. However, the code below only works for DiscretisedDensityOnCartesianGrid (with a regular_range) */ - min1=densel_range.get_min_index(); + min1 = densel_range.get_min_index(); // find radius of cylinder around all of the image - const float max_radius = - std::max( - std::max(-densel_range[min1].get_min_index(), - densel_range[min1].get_max_index() - )*voxel_size[2], - std::max(-densel_range[min1][0].get_min_index(), - densel_range[min1][0].get_max_index() - )*voxel_size[1] - ); + const float max_radius = + std::max(std::max(-densel_range[min1].get_min_index(), densel_range[min1].get_max_index()) * voxel_size[2], + std::max(-densel_range[min1][0].get_min_index(), densel_range[min1][0].get_max_index()) * voxel_size[1]); // find width of the 'tube of response' - const float z_width_of_TOR = - proj_data_info_ptr->get_sampling_in_m(bin); + const float z_width_of_TOR = proj_data_info_ptr->get_sampling_in_m(bin); // now find where tube enters/leaves image // we use a safety margin here as we could probably use z_width_of_LOR/2 - const float z_middle_LOR = - proj_data_info_ptr->get_m(bin) - origin.z(); - const float min_z_LOR = - z_middle_LOR - - fabs(proj_data_info_ptr->get_tantheta(bin))*max_radius - - z_width_of_TOR; - const float max_z_LOR = - z_middle_LOR + - fabs(proj_data_info_ptr->get_tantheta(bin))*max_radius + - z_width_of_TOR; - - min1 = round(floor(min_z_LOR/voxel_size[1])); - max1 = round(ceil(max_z_LOR/voxel_size[1])); + const float z_middle_LOR = proj_data_info_ptr->get_m(bin) - origin.z(); + const float min_z_LOR = z_middle_LOR - fabs(proj_data_info_ptr->get_tantheta(bin)) * max_radius - z_width_of_TOR; + const float max_z_LOR = z_middle_LOR + fabs(proj_data_info_ptr->get_tantheta(bin)) * max_radius + z_width_of_TOR; + + min1 = round(floor(min_z_LOR / voxel_size[1])); + max1 = round(ceil(max_z_LOR / voxel_size[1])); #endif } /* we loop over all coordinates, but for optimisation do the following: @@ -394,82 +312,73 @@ calculate_proj_matrix_elems_for_one_bin( So, we keep track if a non-zero element was found and break out of the loop if we find a 0 after finding a non-zero. */ - bool found_nonzero1=false, found_nonzero2, found_nonzero3; - for (c[1]=min1; c[1]<=max1; ++c[1]) - { - // note: because c1 can be outside the allowed range, we have - // in principle trouble getting the min_index() for c[2]. - // We'll assume that it is the same as for a(ny) c[1] within the range. - // That's of course ok for VoxelsOnCartesianGrid - const IndexRange<2>& range2d = - densel_range[std::min(std::max(c[1],densel_range.get_min_index()), - densel_range.get_max_index())]; + bool found_nonzero1 = false, found_nonzero2, found_nonzero3; + for (c[1] = min1; c[1] <= max1; ++c[1]) { + // note: because c1 can be outside the allowed range, we have + // in principle trouble getting the min_index() for c[2]. + // We'll assume that it is the same as for a(ny) c[1] within the range. + // That's of course ok for VoxelsOnCartesianGrid + const IndexRange<2>& range2d = + densel_range[std::min(std::max(c[1], densel_range.get_min_index()), densel_range.get_max_index())]; #if 0 const int min2=range2d.get_min_index(); const int max2=range2d.get_max_index(); #else - // TODO ugly stuff to avoid having symmetries obtaining voxels - // which are outside the FOV - // this will break when non-zero origin.y() or x() - // (but then there would be no relevant symmetries I guess) - assert(origin.y()==0); - assert(origin.x()==0); - const int first_min2=range2d.get_min_index(); - const int first_max2=range2d.get_max_index(); - const int min2 = std::max(first_min2, -first_max2); - const int max2 = std::min(-first_min2, first_max2); + // TODO ugly stuff to avoid having symmetries obtaining voxels + // which are outside the FOV + // this will break when non-zero origin.y() or x() + // (but then there would be no relevant symmetries I guess) + assert(origin.y() == 0); + assert(origin.x() == 0); + const int first_min2 = range2d.get_min_index(); + const int first_max2 = range2d.get_max_index(); + const int min2 = std::max(first_min2, -first_max2); + const int max2 = std::min(-first_min2, first_max2); #endif - found_nonzero2=false; - for (c[2]=min2; c[2]<=max2; ++c[2]) - { + found_nonzero2 = false; + for (c[2] = min2; c[2] <= max2; ++c[2]) { #if 0 const int min3=range2d[c[2]].get_min_index(); const int max3=range2d[c[2]].get_max_index(); #else - // TODO ugly stuff to avoid having symmetries obtaining voxels - // which are outside the FOV - // this will break when non-zero origin.y() or x() - const int first_min3=range2d[c[2]].get_min_index(); - const int first_max3=range2d[c[2]].get_max_index(); - const int min3 = std::max(first_min3, -first_max3); - const int max3 = std::min(-first_min3, first_max3); + // TODO ugly stuff to avoid having symmetries obtaining voxels + // which are outside the FOV + // this will break when non-zero origin.y() or x() + const int first_min3 = range2d[c[2]].get_min_index(); + const int first_max3 = range2d[c[2]].get_max_index(); + const int min3 = std::max(first_min3, -first_max3); + const int max3 = std::min(-first_min3, first_max3); #endif - found_nonzero3 = false; - for (c[3]=min3; c[3]<=max3; ++c[3]) - { - // TODO call a virtual function of DiscretisedDensity? - const CartesianCoordinate3D coords = - CartesianCoordinate3D(c[1]*voxel_size[1], - c[2]*voxel_size[2], - c[3]*voxel_size[3]) - +origin; - const float element_value = - get_element(bin, coords); - if (element_value>0) - { - found_nonzero3=true; - lor.push_back(ProjMatrixElemsForOneBin::value_type(c, element_value)); - } + found_nonzero3 = false; + for (c[3] = min3; c[3] <= max3; ++c[3]) { + // TODO call a virtual function of DiscretisedDensity? + const CartesianCoordinate3D coords = + CartesianCoordinate3D(c[1] * voxel_size[1], c[2] * voxel_size[2], c[3] * voxel_size[3]) + origin; + const float element_value = get_element(bin, coords); + if (element_value > 0) { + found_nonzero3 = true; + lor.push_back(ProjMatrixElemsForOneBin::value_type(c, element_value)); + } #ifndef __PMByBinElement_SLOW__ - else if (found_nonzero3) - break; + else if (found_nonzero3) + break; #endif - } - if (found_nonzero3) - found_nonzero2=true; + } + if (found_nonzero3) + found_nonzero2 = true; #ifndef __PMByBinElement_SLOW__ - else if (found_nonzero2) - break; + else if (found_nonzero2) + break; #endif - } - if (found_nonzero2) - found_nonzero1=true; + } + if (found_nonzero2) + found_nonzero1 = true; #ifndef __PMByBinElement_SLOW__ - else if (found_nonzero1) - break; + else if (found_nonzero1) + break; #endif - } + } } END_NAMESPACE_STIR diff --git a/src/recon_buildblock/ProjMatrixByBinUsingRayTracing.cxx b/src/recon_buildblock/ProjMatrixByBinUsingRayTracing.cxx index a75bf8daac..45973b1aa4 100644 --- a/src/recon_buildblock/ProjMatrixByBinUsingRayTracing.cxx +++ b/src/recon_buildblock/ProjMatrixByBinUsingRayTracing.cxx @@ -36,14 +36,14 @@ added registry things KT 21/02/2002 added option for square FOV - KT 15/05/2002 + KT 15/05/2002 added possibility of multiple LORs in tangential direction call ProjMatrixByBin's new parsing functions - KT 28/06/02 + KT 28/06/02 added option to take actual detector boundaries into account KT 25/09/03 allow disabling more symmetries - allow smaller z- voxel sizes (but z-sampling in the projdata still has to + allow smaller z- voxel sizes (but z-sampling in the projdata still has to be an integer multiple of the z-voxel size). */ @@ -67,26 +67,17 @@ using std::max; #endif START_NAMESPACE_STIR +const char* const ProjMatrixByBinUsingRayTracing::registered_name = "Ray Tracing"; -const char * const -ProjMatrixByBinUsingRayTracing::registered_name = - "Ray Tracing"; - -ProjMatrixByBinUsingRayTracing:: -ProjMatrixByBinUsingRayTracing() -{ - set_defaults(); -} +ProjMatrixByBinUsingRayTracing::ProjMatrixByBinUsingRayTracing() { set_defaults(); } //******************** parsing ************* -void -ProjMatrixByBinUsingRayTracing::initialise_keymap() -{ +void +ProjMatrixByBinUsingRayTracing::initialise_keymap() { ProjMatrixByBin::initialise_keymap(); parser.add_start_key("Ray Tracing Matrix Parameters"); parser.add_key("restrict to cylindrical FOV", &restrict_to_cylindrical_FOV); - parser.add_key("number of rays in tangential direction to trace for each bin", - &num_tangential_LORs); + parser.add_key("number of rays in tangential direction to trace for each bin", &num_tangential_LORs); parser.add_key("use actual detector boundaries", &use_actual_detector_boundaries); parser.add_key("do_symmetry_90degrees_min_phi", &do_symmetry_90degrees_min_phi); parser.add_key("do_symmetry_180degrees_min_phi", &do_symmetry_180degrees_min_phi); @@ -96,10 +87,8 @@ ProjMatrixByBinUsingRayTracing::initialise_keymap() parser.add_stop_key("End Ray Tracing Matrix Parameters"); } - void -ProjMatrixByBinUsingRayTracing::set_defaults() -{ +ProjMatrixByBinUsingRayTracing::set_defaults() { ProjMatrixByBin::set_defaults(); this->restrict_to_cylindrical_FOV = true; this->num_tangential_LORs = 1; @@ -112,16 +101,13 @@ ProjMatrixByBinUsingRayTracing::set_defaults() this->already_setup = false; } - bool -ProjMatrixByBinUsingRayTracing::post_processing() -{ +ProjMatrixByBinUsingRayTracing::post_processing() { if (ProjMatrixByBin::post_processing() == true) return true; - if (this->num_tangential_LORs<1) - { - warning(boost::format("ProjMatrixByBinUsingRayTracing: num_tangential_LORs should be at least 1, but is %d") - % this->num_tangential_LORs); + if (this->num_tangential_LORs < 1) { + warning(boost::format("ProjMatrixByBinUsingRayTracing: num_tangential_LORs should be at least 1, but is %d") % + this->num_tangential_LORs); return true; } this->already_setup = false; @@ -131,130 +117,93 @@ ProjMatrixByBinUsingRayTracing::post_processing() //******************** get/set pairs ************* bool -ProjMatrixByBinUsingRayTracing:: -get_restrict_to_cylindrical_FOV() const -{ +ProjMatrixByBinUsingRayTracing::get_restrict_to_cylindrical_FOV() const { return this->restrict_to_cylindrical_FOV; } void -ProjMatrixByBinUsingRayTracing:: -set_restrict_to_cylindrical_FOV(bool val) -{ +ProjMatrixByBinUsingRayTracing::set_restrict_to_cylindrical_FOV(bool val) { this->already_setup = (this->restrict_to_cylindrical_FOV == val); this->restrict_to_cylindrical_FOV = val; } int -ProjMatrixByBinUsingRayTracing:: -get_num_tangential_LORs() const -{ +ProjMatrixByBinUsingRayTracing::get_num_tangential_LORs() const { return this->num_tangential_LORs; } void -ProjMatrixByBinUsingRayTracing:: -set_num_tangential_LORs(int val) -{ +ProjMatrixByBinUsingRayTracing::set_num_tangential_LORs(int val) { this->already_setup = (this->num_tangential_LORs == val); this->num_tangential_LORs = val; } bool -ProjMatrixByBinUsingRayTracing:: -get_use_actual_detector_boundaries() const -{ +ProjMatrixByBinUsingRayTracing::get_use_actual_detector_boundaries() const { return this->use_actual_detector_boundaries; } void -ProjMatrixByBinUsingRayTracing:: -set_use_actual_detector_boundaries(bool val) -{ +ProjMatrixByBinUsingRayTracing::set_use_actual_detector_boundaries(bool val) { this->already_setup = (this->use_actual_detector_boundaries == val); this->use_actual_detector_boundaries = val; } bool -ProjMatrixByBinUsingRayTracing:: -get_do_symmetry_90degrees_min_phi() const -{ +ProjMatrixByBinUsingRayTracing::get_do_symmetry_90degrees_min_phi() const { return this->do_symmetry_90degrees_min_phi; } void -ProjMatrixByBinUsingRayTracing:: -set_do_symmetry_90degrees_min_phi(bool val) -{ +ProjMatrixByBinUsingRayTracing::set_do_symmetry_90degrees_min_phi(bool val) { this->already_setup = (this->do_symmetry_90degrees_min_phi == val); this->do_symmetry_90degrees_min_phi = val; } - bool -ProjMatrixByBinUsingRayTracing:: -get_do_symmetry_180degrees_min_phi() const -{ +ProjMatrixByBinUsingRayTracing::get_do_symmetry_180degrees_min_phi() const { return this->do_symmetry_180degrees_min_phi; } void -ProjMatrixByBinUsingRayTracing:: -set_do_symmetry_180degrees_min_phi(bool val) -{ +ProjMatrixByBinUsingRayTracing::set_do_symmetry_180degrees_min_phi(bool val) { this->already_setup = (this->do_symmetry_180degrees_min_phi == val); this->do_symmetry_180degrees_min_phi = val; } - bool -ProjMatrixByBinUsingRayTracing:: -get_do_symmetry_swap_segment() const -{ +ProjMatrixByBinUsingRayTracing::get_do_symmetry_swap_segment() const { return this->do_symmetry_swap_segment; } void -ProjMatrixByBinUsingRayTracing:: -set_do_symmetry_swap_segment(bool val) -{ +ProjMatrixByBinUsingRayTracing::set_do_symmetry_swap_segment(bool val) { this->already_setup = (this->do_symmetry_swap_segment == val); this->do_symmetry_swap_segment = val; } - bool -ProjMatrixByBinUsingRayTracing:: -get_do_symmetry_swap_s() const -{ +ProjMatrixByBinUsingRayTracing::get_do_symmetry_swap_s() const { return this->do_symmetry_swap_s; } void -ProjMatrixByBinUsingRayTracing:: -set_do_symmetry_swap_s(bool val) -{ +ProjMatrixByBinUsingRayTracing::set_do_symmetry_swap_s(bool val) { this->already_setup = (this->do_symmetry_swap_s == val); this->do_symmetry_swap_s = val; } - bool -ProjMatrixByBinUsingRayTracing:: -get_do_symmetry_shift_z() const -{ +ProjMatrixByBinUsingRayTracing::get_do_symmetry_shift_z() const { return this->do_symmetry_shift_z; } void -ProjMatrixByBinUsingRayTracing:: -set_do_symmetry_shift_z(bool val) -{ +ProjMatrixByBinUsingRayTracing::set_do_symmetry_shift_z(bool val) { this->already_setup = (this->do_symmetry_shift_z == val); this->do_symmetry_shift_z = val; } - //******************** actual implementation ************* #if 0 @@ -267,83 +216,65 @@ static bool is_multiple(const float a, const float b) #endif void -ProjMatrixByBinUsingRayTracing:: -set_up( +ProjMatrixByBinUsingRayTracing::set_up( const shared_ptr& proj_data_info_sptr_v, - const shared_ptr >& density_info_sptr_v // TODO should be Info only - ) -{ + const shared_ptr>& density_info_sptr_v // TODO should be Info only +) { ProjMatrixByBin::set_up(proj_data_info_sptr_v, density_info_sptr_v); - image_info_sptr.reset( - dynamic_cast* > (density_info_sptr_v->clone() )); -// const VoxelsOnCartesianGrid * image_info_ptr = -// dynamic_cast*> (density_info_ptr.get()); + image_info_sptr.reset(dynamic_cast*>(density_info_sptr_v->clone())); + // const VoxelsOnCartesianGrid * image_info_ptr = + // dynamic_cast*> (density_info_ptr.get()); - if(is_null_ptr(image_info_sptr)) + if (is_null_ptr(image_info_sptr)) error("ProjMatrixByBinUsingRayTracing initialised with a wrong type of DiscretisedDensity\n"); - + voxel_size = image_info_sptr->get_voxel_size(); origin = image_info_sptr->get_origin(); - if (abs(origin.x())>.05F || abs(origin.y())>.05F) + if (abs(origin.x()) > .05F || abs(origin.y()) > .05F) error("ProjMatrixByBinUsingRayTracing sadly doesn't support shifted x/y origin yet"); image_info_sptr->get_regular_range(min_index, max_index); - symmetries_sptr.reset( - new DataSymmetriesForBins_PET_CartesianGrid(proj_data_info_sptr, - density_info_sptr_v, - do_symmetry_90degrees_min_phi, - do_symmetry_180degrees_min_phi, - do_symmetry_swap_segment, - do_symmetry_swap_s, - do_symmetry_shift_z)); - const float sampling_distance_of_adjacent_LORs_xy = - proj_data_info_sptr->get_sampling_in_s(Bin(0,0,0,0)); - - if(sampling_distance_of_adjacent_LORs_xy/num_tangential_LORs > voxel_size.x() + 1.E-3 || - sampling_distance_of_adjacent_LORs_xy/num_tangential_LORs > voxel_size.y() + 1.E-3) - warning("WARNING: ProjMatrixByBinUsingRayTracing used for pixel size (in x,y) " - "that is smaller than the bin size divided by num_tangential_LORs.\n" - "This matrix will completely miss some voxels for some (or all) views.\n"); - if(sampling_distance_of_adjacent_LORs_xy < voxel_size.x() - 1.E-3 || - sampling_distance_of_adjacent_LORs_xy < voxel_size.y() - 1.E-3) - warning("WARNING: ProjMatrixByBinUsingRayTracing used for pixel size (in x,y) " - "that is larger than the bin size.\n" - "Backprojecting with this matrix might have artefacts at views 0 and 90 degrees.\n"); - - if (use_actual_detector_boundaries) - { - const ProjDataInfoCylindricalNoArcCorr * proj_data_info_cyl_ptr = - dynamic_cast(proj_data_info_sptr.get()); - if (proj_data_info_cyl_ptr== 0) - { - warning("ProjMatrixByBinUsingRayTracing: use_actual_detector_boundaries" - " is reset to false as the projection data should be non-arccorected.\n"); - use_actual_detector_boundaries = false; - } - else - { - bool nocompression = - proj_data_info_cyl_ptr->get_view_mashing_factor()==1; - for (int segment_num=proj_data_info_cyl_ptr->get_min_segment_num(); - nocompression && segment_num <= proj_data_info_cyl_ptr->get_max_segment_num(); - ++segment_num) - nocompression= - proj_data_info_cyl_ptr->get_min_ring_difference(segment_num) == - proj_data_info_cyl_ptr->get_max_ring_difference(segment_num); - - if (!nocompression) - { - warning("ProjMatrixByBinUsingRayTracing: use_actual_detector_boundaries" - " is reset to false as the projection data as either mashed or uses axial compression\n"); - use_actual_detector_boundaries = false; - } - } - - if (use_actual_detector_boundaries) - warning("ProjMatrixByBinUsingRayTracing: use_actual_detector_boundaries==true\n"); + symmetries_sptr.reset(new DataSymmetriesForBins_PET_CartesianGrid( + proj_data_info_sptr, density_info_sptr_v, do_symmetry_90degrees_min_phi, do_symmetry_180degrees_min_phi, + do_symmetry_swap_segment, do_symmetry_swap_s, do_symmetry_shift_z)); + const float sampling_distance_of_adjacent_LORs_xy = proj_data_info_sptr->get_sampling_in_s(Bin(0, 0, 0, 0)); + + if (sampling_distance_of_adjacent_LORs_xy / num_tangential_LORs > voxel_size.x() + 1.E-3 || + sampling_distance_of_adjacent_LORs_xy / num_tangential_LORs > voxel_size.y() + 1.E-3) + warning("WARNING: ProjMatrixByBinUsingRayTracing used for pixel size (in x,y) " + "that is smaller than the bin size divided by num_tangential_LORs.\n" + "This matrix will completely miss some voxels for some (or all) views.\n"); + if (sampling_distance_of_adjacent_LORs_xy < voxel_size.x() - 1.E-3 || + sampling_distance_of_adjacent_LORs_xy < voxel_size.y() - 1.E-3) + warning("WARNING: ProjMatrixByBinUsingRayTracing used for pixel size (in x,y) " + "that is larger than the bin size.\n" + "Backprojecting with this matrix might have artefacts at views 0 and 90 degrees.\n"); + + if (use_actual_detector_boundaries) { + const ProjDataInfoCylindricalNoArcCorr* proj_data_info_cyl_ptr = + dynamic_cast(proj_data_info_sptr.get()); + if (proj_data_info_cyl_ptr == 0) { + warning("ProjMatrixByBinUsingRayTracing: use_actual_detector_boundaries" + " is reset to false as the projection data should be non-arccorected.\n"); + use_actual_detector_boundaries = false; + } else { + bool nocompression = proj_data_info_cyl_ptr->get_view_mashing_factor() == 1; + for (int segment_num = proj_data_info_cyl_ptr->get_min_segment_num(); + nocompression && segment_num <= proj_data_info_cyl_ptr->get_max_segment_num(); ++segment_num) + nocompression = proj_data_info_cyl_ptr->get_min_ring_difference(segment_num) == + proj_data_info_cyl_ptr->get_max_ring_difference(segment_num); + + if (!nocompression) { + warning("ProjMatrixByBinUsingRayTracing: use_actual_detector_boundaries" + " is reset to false as the projection data as either mashed or uses axial compression\n"); + use_actual_detector_boundaries = false; + } + } - } + if (use_actual_detector_boundaries) + warning("ProjMatrixByBinUsingRayTracing: use_actual_detector_boundaries==true\n"); + } #if 0 // test if our 2D code does not have problems @@ -360,25 +291,20 @@ set_up( } #endif - this->already_setup = true; this->clear_cache(); }; ProjMatrixByBinUsingRayTracing* -ProjMatrixByBinUsingRayTracing::clone() const -{ - return new ProjMatrixByBinUsingRayTracing(*this); +ProjMatrixByBinUsingRayTracing::clone() const { + return new ProjMatrixByBinUsingRayTracing(*this); } -/* this is used when +/* this is used when (tantheta==0 && sampling_distance_of_adjacent_LORs_z==2*voxel_size.z()) it adds two adjacents z with their half value */ -static void -add_adjacent_z(ProjMatrixElemsForOneBin& lor, - const float z_of_first_voxel, - const float right_edge_of_TOR); +static void add_adjacent_z(ProjMatrixElemsForOneBin& lor, const float z_of_first_voxel, const float right_edge_of_TOR); #if 0 /* Complicated business to add the same values at z+1 @@ -390,91 +316,76 @@ static void merge_zplus1(ProjMatrixElemsForOneBin& lor); #endif template -static inline int sign(const T& t) -{ - return t<0 ? -1 : 1; +static inline int +sign(const T& t) { + return t < 0 ? -1 : 1; } // just do 1 LOR, returns true if lor is not empty static void -ray_trace_one_lor(ProjMatrixElemsForOneBin& lor, - const float s_in_mm, const float t_in_mm, - const float cphi, const float sphi, - const float costheta, const float tantheta, - const float offset_in_z, - const float fovrad_in_mm, - const CartesianCoordinate3D& voxel_size, - const bool restrict_to_cylindrical_FOV, - const int num_LORs) -{ +ray_trace_one_lor(ProjMatrixElemsForOneBin& lor, const float s_in_mm, const float t_in_mm, const float cphi, const float sphi, + const float costheta, const float tantheta, const float offset_in_z, const float fovrad_in_mm, + const CartesianCoordinate3D& voxel_size, const bool restrict_to_cylindrical_FOV, const int num_LORs) { assert(lor.size() == 0); /* Find Intersection points of LOR and image FOV (assuming infinitely long scanner)*/ /* (in voxel units) */ - CartesianCoordinate3D start_point; + CartesianCoordinate3D start_point; CartesianCoordinate3D stop_point; { /* parametrisation of LOR is - X= s*cphi + a*sphi, - Y= s*sphi - a*cphi, + X= s*cphi + a*sphi, + Y= s*sphi - a*cphi, Z= t/costheta+offset_in_z - a*tantheta - find now min_a, max_a such that end-points intersect border of FOV + find now min_a, max_a such that end-points intersect border of FOV */ float max_a; float min_a; - - if (restrict_to_cylindrical_FOV) - { + + if (restrict_to_cylindrical_FOV) { #ifdef STIR_PMRT_LARGER_FOV - if (fabs(s_in_mm) >= fovrad_in_mm) return; + if (fabs(s_in_mm) >= fovrad_in_mm) + return; #else - if (fabs(s_in_mm) > fovrad_in_mm) return; + if (fabs(s_in_mm) > fovrad_in_mm) + return; #endif - // a has to be such that X^2+Y^2 == fovrad^2 - if (fabs(s_in_mm) == fovrad_in_mm) - { - max_a = min_a = 0; - } - else - { - max_a = sqrt(square(fovrad_in_mm) - square(s_in_mm)); - min_a = -max_a; - } + // a has to be such that X^2+Y^2 == fovrad^2 + if (fabs(s_in_mm) == fovrad_in_mm) { + max_a = min_a = 0; + } else { + max_a = sqrt(square(fovrad_in_mm) - square(s_in_mm)); + min_a = -max_a; + } } // restrict_to_cylindrical_FOV - else - { + else { // use FOV which is square. // note that we use square and not rectangular as otherwise symmetries // would take us out of the FOV. TODO /* - a has to be such that + a has to be such that |X| <= fovrad_in_mm && |Y| <= fovrad_in_mm */ - if (fabs(cphi) < 1.E-3 || fabs(sphi) < 1.E-3) - { + if (fabs(cphi) < 1.E-3 || fabs(sphi) < 1.E-3) { if (fovrad_in_mm < fabs(s_in_mm)) return; max_a = fovrad_in_mm; min_a = -fovrad_in_mm; - } - else - { - max_a = min((fovrad_in_mm*sign(sphi) - s_in_mm*cphi)/sphi, - (fovrad_in_mm*sign(cphi) + s_in_mm*sphi)/cphi); - min_a = max((-fovrad_in_mm*sign(sphi) - s_in_mm*cphi)/sphi, - (-fovrad_in_mm*sign(cphi) + s_in_mm*sphi)/cphi); - if (min_a > max_a - 1.E-3*voxel_size.x()) + } else { + max_a = min((fovrad_in_mm * sign(sphi) - s_in_mm * cphi) / sphi, (fovrad_in_mm * sign(cphi) + s_in_mm * sphi) / cphi); + min_a = max((-fovrad_in_mm * sign(sphi) - s_in_mm * cphi) / sphi, (-fovrad_in_mm * sign(cphi) + s_in_mm * sphi) / cphi); + if (min_a > max_a - 1.E-3 * voxel_size.x()) return; } - - } //!restrict_to_cylindrical_FOV - - start_point.x() = (s_in_mm*cphi + max_a*sphi)/voxel_size.x(); - start_point.y() = (s_in_mm*sphi - max_a*cphi)/voxel_size.y(); - start_point.z() = (t_in_mm/costheta+offset_in_z - max_a*tantheta)/voxel_size.z(); - stop_point.x() = (s_in_mm*cphi + min_a*sphi)/voxel_size.x(); - stop_point.y() = (s_in_mm*sphi - min_a*cphi)/voxel_size.y(); - stop_point.z() = (t_in_mm/costheta+offset_in_z - min_a*tantheta)/voxel_size.z(); + + } //! restrict_to_cylindrical_FOV + + start_point.x() = (s_in_mm * cphi + max_a * sphi) / voxel_size.x(); + start_point.y() = (s_in_mm * sphi - max_a * cphi) / voxel_size.y(); + start_point.z() = (t_in_mm / costheta + offset_in_z - max_a * tantheta) / voxel_size.z(); + stop_point.x() = (s_in_mm * cphi + min_a * sphi) / voxel_size.x(); + stop_point.y() = (s_in_mm * sphi - min_a * cphi) / voxel_size.y(); + stop_point.z() = (t_in_mm / costheta + offset_in_z - min_a * tantheta) / voxel_size.z(); #if 0 // KT 18/05/2005 this is no longer necessary @@ -501,59 +412,50 @@ ray_trace_one_lor(ProjMatrixElemsForOneBin& lor, #endif // find out in which direction we should do the ray tracing to obtain a sorted lor - // we want to go from small z to large z, + // we want to go from small z to large z, // or if z are equal, from small y to large y and so on const bool from_start_to_stop = - start_point.z() < stop_point.z() || - (start_point.z() == stop_point.z() && - (start_point.y() < stop_point.y() || - (start_point.y() == stop_point.y() && - (start_point.x() <= stop_point.x())))); + start_point.z() < stop_point.z() || + (start_point.z() == stop_point.z() && + (start_point.y() < stop_point.y() || (start_point.y() == stop_point.y() && (start_point.x() <= stop_point.x())))); // do actual ray tracing for this LOR - - RayTraceVoxelsOnCartesianGrid(lor, - from_start_to_stop? start_point : stop_point, - !from_start_to_stop? start_point : stop_point, - voxel_size, + + RayTraceVoxelsOnCartesianGrid(lor, from_start_to_stop ? start_point : stop_point, + !from_start_to_stop ? start_point : stop_point, voxel_size, #ifdef NEWSCALE - 1.F/num_LORs // normalise to mm + 1.F / num_LORs // normalise to mm #else - 1/voxel_size.x()/num_LORs // normalise to some kind of 'pixel units' + 1 / voxel_size.x() / num_LORs // normalise to some kind of 'pixel units' #endif - ); + ); #ifndef NDEBUG { // TODO output is still not sorted... why? - //ProjMatrixElemsForOneBin sorted_lor = lor; - //sorted_lor.sort(); - //assert(lor == sorted_lor); + // ProjMatrixElemsForOneBin sorted_lor = lor; + // sorted_lor.sort(); + // assert(lor == sorted_lor); lor.check_state(); } #endif return; } - } ////////////////////////////////////// -void -ProjMatrixByBinUsingRayTracing:: -calculate_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& lor) const -{ - if (!this->already_setup) - { - error("ProjMatrixByBinUsingRayTracing used before calling setup"); - } +void +ProjMatrixByBinUsingRayTracing::calculate_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& lor) const { + if (!this->already_setup) { + error("ProjMatrixByBinUsingRayTracing used before calling setup"); + } const Bin bin = lor.get_bin(); - assert(bin.segment_num() >= proj_data_info_sptr->get_min_segment_num()); - assert(bin.segment_num() <= proj_data_info_sptr->get_max_segment_num()); + assert(bin.segment_num() >= proj_data_info_sptr->get_min_segment_num()); + assert(bin.segment_num() <= proj_data_info_sptr->get_max_segment_num()); assert(lor.size() == 0); - + float phi; float s_in_mm = proj_data_info_sptr->get_s(bin); /* Implementation note. @@ -564,69 +466,55 @@ calculate_proj_matrix_elems_for_one_bin( on Linux on x86. A bit of a mistery that. - TODO this is maybe solved now by having more decent handling of + TODO this is maybe solved now by having more decent handling of start and end voxels. */ - if (!use_actual_detector_boundaries) - { + if (!use_actual_detector_boundaries) { phi = proj_data_info_sptr->get_phi(bin); - //s_in_mm = proj_data_info_sptr->get_s(bin); - } - else - { + // s_in_mm = proj_data_info_sptr->get_s(bin); + } else { // can be static_cast later on const ProjDataInfoCylindricalNoArcCorr& proj_data_info_noarccor = - dynamic_cast(*proj_data_info_sptr); + dynamic_cast(*proj_data_info_sptr); // TODO check on 180 degrees for views - const int num_detectors = - proj_data_info_sptr->get_scanner_ptr()->get_num_detectors_per_ring(); - const float ring_radius = - proj_data_info_sptr->get_scanner_ptr()->get_effective_ring_radius(); - - int det_num1=0, det_num2=0; - proj_data_info_noarccor. - get_det_num_pair_for_view_tangential_pos_num(det_num1, - det_num2, - bin.view_num(), - bin.tangential_pos_num()); - phi = static_cast((det_num1+det_num2)*_PI/num_detectors-_PI/2); - const float old_phi=proj_data_info_sptr->get_phi(bin); - if (fabs(phi-old_phi)>2*_PI/num_detectors) - warning("view %d old_phi %g new_phi %g\n",bin.view_num(), old_phi, phi); - - s_in_mm = static_cast(ring_radius*sin((det_num1-det_num2)*_PI/num_detectors+_PI/2)); - const float old_s_in_mm=proj_data_info_sptr->get_s(bin); - if (fabs(s_in_mm-old_s_in_mm)>proj_data_info_sptr->get_sampling_in_s(bin)*.0001) - warning("tangential_pos_num %d old_s_in_mm %g new_s_in_mm %g\n",bin.tangential_pos_num(), old_s_in_mm, s_in_mm); - + const int num_detectors = proj_data_info_sptr->get_scanner_ptr()->get_num_detectors_per_ring(); + const float ring_radius = proj_data_info_sptr->get_scanner_ptr()->get_effective_ring_radius(); + + int det_num1 = 0, det_num2 = 0; + proj_data_info_noarccor.get_det_num_pair_for_view_tangential_pos_num(det_num1, det_num2, bin.view_num(), + bin.tangential_pos_num()); + phi = static_cast((det_num1 + det_num2) * _PI / num_detectors - _PI / 2); + const float old_phi = proj_data_info_sptr->get_phi(bin); + if (fabs(phi - old_phi) > 2 * _PI / num_detectors) + warning("view %d old_phi %g new_phi %g\n", bin.view_num(), old_phi, phi); + + s_in_mm = static_cast(ring_radius * sin((det_num1 - det_num2) * _PI / num_detectors + _PI / 2)); + const float old_s_in_mm = proj_data_info_sptr->get_s(bin); + if (fabs(s_in_mm - old_s_in_mm) > proj_data_info_sptr->get_sampling_in_s(bin) * .0001) + warning("tangential_pos_num %d old_s_in_mm %g new_s_in_mm %g\n", bin.tangential_pos_num(), old_s_in_mm, s_in_mm); } - + const float cphi = cos(phi); const float sphi = sin(phi); - + const float tantheta = proj_data_info_sptr->get_tantheta(bin); - const float costheta = 1/sqrt(1+square(tantheta)); + const float costheta = 1 / sqrt(1 + square(tantheta)); const float t_in_mm = proj_data_info_sptr->get_t(bin); - - const float sampling_distance_of_adjacent_LORs_z = - proj_data_info_sptr->get_sampling_in_t(bin)/costheta; - + + const float sampling_distance_of_adjacent_LORs_z = proj_data_info_sptr->get_sampling_in_t(bin) / costheta; // find number of LORs we have to take, such that we don't miss voxels // we have to subtract a tiny amount from the quotient, to avoid having too many LORs // solely due to numerical rounding errors - const int num_lors_per_axial_pos = - static_cast(ceil(sampling_distance_of_adjacent_LORs_z / voxel_size.z() - 1.E-3)); + const int num_lors_per_axial_pos = static_cast(ceil(sampling_distance_of_adjacent_LORs_z / voxel_size.z() - 1.E-3)); - assert(num_lors_per_axial_pos>0); + assert(num_lors_per_axial_pos > 0); // theta=0 assumes that centre of 1 voxel coincides with centre of bin. // TODO test - //if (num_lors_per_axial_pos>1 && tantheta==0 num_lors_per_axial_pos%2==0); + // if (num_lors_per_axial_pos>1 && tantheta==0 num_lors_per_axial_pos%2==0); // merging code assumes integer multiple - assert(fabs(sampling_distance_of_adjacent_LORs_z/voxel_size.z() - - num_lors_per_axial_pos) <= 1E-4); - + assert(fabs(sampling_distance_of_adjacent_LORs_z / voxel_size.z() - num_lors_per_axial_pos) <= 1E-4); // find offset in z, taking into account if there are 1 or more LORs // KT 20/06/2001 take origin.z() into account @@ -641,106 +529,73 @@ calculate_proj_matrix_elems_for_one_bin( dz = sampling_distance_of_adjacent_LORs_z/num_lors_per_axial_pos. Then you have to make sure that the middle of the set of rays for this bin corresponds to the middle of the TOR, i.e. the offset given above for 1 ray. - So, we put the rays from + So, we put the rays from -dz*(num_lors_per_axial_pos-1)/2 to +dz*(num_lors_per_axial_pos-1)/2 Now we look at direct rays (tantheta=0).We have to choose rays which - do not go exactly on the edge of 2 planes as this would give unreliable + do not go exactly on the edge of 2 planes as this would give unreliable results due to rounding errors. - In addition, we can give a weight to the rays according to how much the + In addition, we can give a weight to the rays according to how much the voxel overlaps with the TOR (in axial direction). - Note that RayTracing* now sorts this out itself, so we could dispense with this + Note that RayTracing* now sorts this out itself, so we could dispense with this complication here. However, we can do it slightly more efficient here as we might be using 2 rays for one ring. */ const float z_position_of_first_LOR_wrt_centre_of_TOR = - (-sampling_distance_of_adjacent_LORs_z/(2*num_lors_per_axial_pos)* - (num_lors_per_axial_pos-1)) - - origin.z(); - float offset_in_z = - z_position_of_first_LOR_wrt_centre_of_TOR - +(max_index.z()+min_index.z())/2.F * voxel_size.z(); - - if (tantheta==0) - { - // make sure we don't ray-trace exactly between 2 planes - // z-coordinate (in voxel units) will be - // (t_in_mm+offset_in_z)/voxel_size.z(); - // if so, we ray trace first to the voxels at smaller z, but will add the - // other plane later (in add_adjacent_z) - if (fabs(modulo((t_in_mm+offset_in_z)/voxel_size.z(),1.F)-.5)<.001) - offset_in_z -= .1F*voxel_size.z(); - } - + (-sampling_distance_of_adjacent_LORs_z / (2 * num_lors_per_axial_pos) * (num_lors_per_axial_pos - 1)) - origin.z(); + float offset_in_z = z_position_of_first_LOR_wrt_centre_of_TOR + (max_index.z() + min_index.z()) / 2.F * voxel_size.z(); + + if (tantheta == 0) { + // make sure we don't ray-trace exactly between 2 planes + // z-coordinate (in voxel units) will be + // (t_in_mm+offset_in_z)/voxel_size.z(); + // if so, we ray trace first to the voxels at smaller z, but will add the + // other plane later (in add_adjacent_z) + if (fabs(modulo((t_in_mm + offset_in_z) / voxel_size.z(), 1.F) - .5) < .001) + offset_in_z -= .1F * voxel_size.z(); + } // use FOV which is slightly 'inside' the image to avoid // index out of range #ifdef STIR_PMRT_LARGER_FOV - const float fovrad_in_mm = - min((min(max_index.x(), -min_index.x())+.45F)*voxel_size.x(), - (min(max_index.y(), -min_index.y())+.45F)*voxel_size.y()); + const float fovrad_in_mm = min((min(max_index.x(), -min_index.x()) + .45F) * voxel_size.x(), + (min(max_index.y(), -min_index.y()) + .45F) * voxel_size.y()); #else - const float fovrad_in_mm = - min((min(max_index.x(), -min_index.x()))*voxel_size.x(), - (min(max_index.y(), -min_index.y()))*voxel_size.y()); + const float fovrad_in_mm = + min((min(max_index.x(), -min_index.x())) * voxel_size.x(), (min(max_index.y(), -min_index.y())) * voxel_size.y()); #endif - if (num_tangential_LORs == 1) - { - ray_trace_one_lor(lor, s_in_mm, t_in_mm, - cphi, sphi, costheta, tantheta, - offset_in_z, fovrad_in_mm, - voxel_size, - restrict_to_cylindrical_FOV, - num_lors_per_axial_pos); - } - else - { + if (num_tangential_LORs == 1) { + ray_trace_one_lor(lor, s_in_mm, t_in_mm, cphi, sphi, costheta, tantheta, offset_in_z, fovrad_in_mm, voxel_size, + restrict_to_cylindrical_FOV, num_lors_per_axial_pos); + } else { ProjMatrixElemsForOneBin ray_traced_lor; // get_sampling_in_s returns sampling in interleaved case // interleaved case has a sampling which is twice as high - const float s_inc = - (!use_actual_detector_boundaries ? 1 : 2) * - proj_data_info_sptr->get_sampling_in_s(bin)/num_tangential_LORs; - float current_s_in_mm = - s_in_mm - s_inc*(num_tangential_LORs-1)/2.F; - for (int s_LOR_num=1; s_LOR_num<=num_tangential_LORs; ++s_LOR_num, current_s_in_mm+=s_inc) - { + const float s_inc = + (!use_actual_detector_boundaries ? 1 : 2) * proj_data_info_sptr->get_sampling_in_s(bin) / num_tangential_LORs; + float current_s_in_mm = s_in_mm - s_inc * (num_tangential_LORs - 1) / 2.F; + for (int s_LOR_num = 1; s_LOR_num <= num_tangential_LORs; ++s_LOR_num, current_s_in_mm += s_inc) { ray_traced_lor.erase(); - ray_trace_one_lor(ray_traced_lor, current_s_in_mm, t_in_mm, - cphi, sphi, costheta, tantheta, - offset_in_z, fovrad_in_mm, - voxel_size, - restrict_to_cylindrical_FOV, - num_lors_per_axial_pos*num_tangential_LORs); - //std::cerr << "ray traced size " << ray_traced_lor.size() << std::endl; + ray_trace_one_lor(ray_traced_lor, current_s_in_mm, t_in_mm, cphi, sphi, costheta, tantheta, offset_in_z, fovrad_in_mm, + voxel_size, restrict_to_cylindrical_FOV, num_lors_per_axial_pos * num_tangential_LORs); + // std::cerr << "ray traced size " << ray_traced_lor.size() << std::endl; lor.merge(ray_traced_lor); } } - + // now add on other LORs in axial direction - if (lor.size()>0) - { - if (tantheta==0 ) - { - const float z_of_first_voxel= - lor.begin()->coord1() + - origin.z()/voxel_size.z() - - (max_index.z() + min_index.z())/2.F; - const float left_edge_of_TOR = - (t_in_mm - sampling_distance_of_adjacent_LORs_z/2 - )/voxel_size.z(); - const float right_edge_of_TOR = - (t_in_mm + sampling_distance_of_adjacent_LORs_z/2 - )/voxel_size.z(); - - add_adjacent_z(lor, z_of_first_voxel - left_edge_of_TOR, right_edge_of_TOR -left_edge_of_TOR); - } - else if (num_lors_per_axial_pos>1) - { + if (lor.size() > 0) { + if (tantheta == 0) { + const float z_of_first_voxel = lor.begin()->coord1() + origin.z() / voxel_size.z() - (max_index.z() + min_index.z()) / 2.F; + const float left_edge_of_TOR = (t_in_mm - sampling_distance_of_adjacent_LORs_z / 2) / voxel_size.z(); + const float right_edge_of_TOR = (t_in_mm + sampling_distance_of_adjacent_LORs_z / 2) / voxel_size.z(); + + add_adjacent_z(lor, z_of_first_voxel - left_edge_of_TOR, right_edge_of_TOR - left_edge_of_TOR); + } else if (num_lors_per_axial_pos > 1) { #if 0 if (num_lors_per_axial_pos==2) { @@ -748,123 +603,92 @@ calculate_proj_matrix_elems_for_one_bin( } else #endif - { - // make copy of LOR that will be used to add adjacent z - ProjMatrixElemsForOneBin lor_with_next_z = lor; - // reserve enough memory to avoid reallocations - lor.reserve(lor.size()*num_lors_per_axial_pos); - // now add adjacent z - for (int z_index=1; z_index(element_ptr->coord1()+1, - element_ptr->coord2(), - element_ptr->coord3()), - element_ptr->get_value()); - ++element_ptr; - } - // now merge it into the original - lor.merge(lor_with_next_z); - } + { + // make copy of LOR that will be used to add adjacent z + ProjMatrixElemsForOneBin lor_with_next_z = lor; + // reserve enough memory to avoid reallocations + lor.reserve(lor.size() * num_lors_per_axial_pos); + // now add adjacent z + for (int z_index = 1; z_index < num_lors_per_axial_pos; ++z_index) { + // add 1 to each z in the LOR + ProjMatrixElemsForOneBin::iterator element_ptr = lor_with_next_z.begin(); + const ProjMatrixElemsForOneBin::iterator element_end = lor_with_next_z.end(); + while (element_ptr != element_end) { + *element_ptr = ProjMatrixElemsForOneBin::value_type( + Coordinate3D(element_ptr->coord1() + 1, element_ptr->coord2(), element_ptr->coord3()), + element_ptr->get_value()); + ++element_ptr; } - } // if( tantheta!=0 && num_lors_per_axial_pos>1) - } //if (lor.size()!=0) - + // now merge it into the original + lor.merge(lor_with_next_z); + } + } + } // if( tantheta!=0 && num_lors_per_axial_pos>1) + } // if (lor.size()!=0) } -static void -add_adjacent_z(ProjMatrixElemsForOneBin& lor, - const float z_of_first_voxel, - const float right_edge_of_TOR) -{ - assert(lor.size()>0); - assert(z_of_first_voxel+.5>=0); - assert(z_of_first_voxel-.5<=right_edge_of_TOR); +static void +add_adjacent_z(ProjMatrixElemsForOneBin& lor, const float z_of_first_voxel, const float right_edge_of_TOR) { + assert(lor.size() > 0); + assert(z_of_first_voxel + .5 >= 0); + assert(z_of_first_voxel - .5 <= right_edge_of_TOR); // first reserve enough memory for the whole vector // this speeds things up. - const int num_overlapping_voxels = - round(ceil(right_edge_of_TOR-z_of_first_voxel+.5)); + const int num_overlapping_voxels = round(ceil(right_edge_of_TOR - z_of_first_voxel + .5)); lor.reserve(lor.size() * num_overlapping_voxels); - + // point to end of original LOR, i.e. first plane // const ProjMatrixElemsForOneBin::const_iterator element_end = lor.end(); const std::size_t org_size = lor.size(); - for (int z_index= 1; /* no end condition here */; ++z_index) + for (int z_index = 1; /* no end condition here */; ++z_index) { + const float overlap_of_voxel_with_TOR = + std::min(right_edge_of_TOR, z_of_first_voxel + z_index + .5F) - std::max(0.F, z_of_first_voxel + z_index - .5F); + if (overlap_of_voxel_with_TOR <= 0.0001) // check if beyond TOR or overlap too small to bother { - const float overlap_of_voxel_with_TOR = - std::min(right_edge_of_TOR, z_of_first_voxel + z_index + .5F) - - std::max(0.F, z_of_first_voxel + z_index - .5F); - if (overlap_of_voxel_with_TOR<=0.0001) // check if beyond TOR or overlap too small to bother - { - assert(num_overlapping_voxels>=z_index); - break; - } - assert(overlap_of_voxel_with_TOR < 1.0001); - const int new_z = lor.begin()->coord1()+z_index; - if (overlap_of_voxel_with_TOR>.9999) // test if it is 1 - { - // just copy the value - std::size_t count = 0; // counter for elements in original LOR - for ( ProjMatrixElemsForOneBin::const_iterator element_ptr = lor.begin(); - count != org_size; //element_ptr != element_end; - ++element_ptr, ++count) - { - assert(lor.size()+1 <= lor.capacity()); // not really necessary now, but check on reserve() best for performance - assert(new_z == element_ptr->coord1()+z_index); - lor.push_back( - ProjMatrixElemsForOneBin:: - value_type( - Coordinate3D(new_z, - element_ptr->coord2(), - element_ptr->coord3()), - element_ptr->get_value())); - } - } - else - { - // multiply the value with the overlap - std::size_t count = 0; // counter for elements in original LOR - for ( ProjMatrixElemsForOneBin::const_iterator element_ptr = lor.begin(); - count != org_size; //element_ptr != element_end; - ++element_ptr, ++count) - { - assert(lor.size()+1 <= lor.capacity()); - assert(new_z == element_ptr->coord1()+z_index); - lor.push_back( - ProjMatrixElemsForOneBin:: - value_type( - Coordinate3D(new_z, - element_ptr->coord2(), - element_ptr->coord3()), - element_ptr->get_value()*overlap_of_voxel_with_TOR)); - } - } - } // loop over z_index + assert(num_overlapping_voxels >= z_index); + break; + } + assert(overlap_of_voxel_with_TOR < 1.0001); + const int new_z = lor.begin()->coord1() + z_index; + if (overlap_of_voxel_with_TOR > .9999) // test if it is 1 + { + // just copy the value + std::size_t count = 0; // counter for elements in original LOR + for (ProjMatrixElemsForOneBin::const_iterator element_ptr = lor.begin(); count != org_size; // element_ptr != element_end; + ++element_ptr, ++count) { + assert(lor.size() + 1 <= lor.capacity()); // not really necessary now, but check on reserve() best for performance + assert(new_z == element_ptr->coord1() + z_index); + lor.push_back(ProjMatrixElemsForOneBin::value_type(Coordinate3D(new_z, element_ptr->coord2(), element_ptr->coord3()), + element_ptr->get_value())); + } + } else { + // multiply the value with the overlap + std::size_t count = 0; // counter for elements in original LOR + for (ProjMatrixElemsForOneBin::const_iterator element_ptr = lor.begin(); count != org_size; // element_ptr != element_end; + ++element_ptr, ++count) { + assert(lor.size() + 1 <= lor.capacity()); + assert(new_z == element_ptr->coord1() + z_index); + lor.push_back(ProjMatrixElemsForOneBin::value_type(Coordinate3D(new_z, element_ptr->coord2(), element_ptr->coord3()), + element_ptr->get_value() * overlap_of_voxel_with_TOR)); + } + } + } // loop over z_index // now check original z { const float overlap_of_voxel_with_TOR = - std::min(right_edge_of_TOR, z_of_first_voxel + .5F) - - std::max(0.F, z_of_first_voxel - .5F); - assert (overlap_of_voxel_with_TOR>0); + std::min(right_edge_of_TOR, z_of_first_voxel + .5F) - std::max(0.F, z_of_first_voxel - .5F); + assert(overlap_of_voxel_with_TOR > 0); assert(overlap_of_voxel_with_TOR < 1.0001); - if (overlap_of_voxel_with_TOR<.9999) // test if it is 1 - { - // multiply the value with the overlap - std::size_t count = 0; // counter for elements in original LOR - for ( ProjMatrixElemsForOneBin::iterator element_ptr = lor.begin(); - count != org_size; //element_ptr != element_end; - ++element_ptr, ++count) - *element_ptr *= overlap_of_voxel_with_TOR; - } + if (overlap_of_voxel_with_TOR < .9999) // test if it is 1 + { + // multiply the value with the overlap + std::size_t count = 0; // counter for elements in original LOR + for (ProjMatrixElemsForOneBin::iterator element_ptr = lor.begin(); count != org_size; // element_ptr != element_end; + ++element_ptr, ++count) + *element_ptr *= overlap_of_voxel_with_TOR; + } } #ifndef NDEBUG { @@ -902,7 +726,7 @@ static void merge_zplus1(ProjMatrixElemsForOneBin& lor) lor.reserve(lor.size()*2); cerr << "before merge\n"; -#if 0 +# if 0 ProjMatrixElemsForOneBin::const_iterator iter = lor.begin(); while (iter!= lor.end()) { @@ -911,7 +735,7 @@ static void merge_zplus1(ProjMatrixElemsForOneBin& lor) << '\n'; ++iter; } -#endif +# endif float next_value; @@ -950,4 +774,3 @@ static void merge_zplus1(ProjMatrixElemsForOneBin& lor) #endif END_NAMESPACE_STIR - diff --git a/src/recon_buildblock/ProjMatrixElemsForOneBin.cxx b/src/recon_buildblock/ProjMatrixElemsForOneBin.cxx index 93ddb8a9ec..ae8b6a9e73 100644 --- a/src/recon_buildblock/ProjMatrixElemsForOneBin.cxx +++ b/src/recon_buildblock/ProjMatrixElemsForOneBin.cxx @@ -22,15 +22,15 @@ \file \ingroup projection \brief non-inline implementations for stir::ProjMatrixElemsForOneBin - + \author Mustapha Sadki \author Kris Thielemans \author PARAPET project - + */ /* History - KT 15/05/2002 + KT 15/05/2002 rewrote merge(). it worked only on sorted arrays before, while the end-result wasn't sorted */ #include "stir/Succeeded.h" @@ -50,87 +50,67 @@ using std::copy; START_NAMESPACE_STIR - -ProjMatrixElemsForOneBin:: -ProjMatrixElemsForOneBin(const Bin& bin, const int default_capacity) -: bin(bin) -{ - elements.reserve(default_capacity); +ProjMatrixElemsForOneBin::ProjMatrixElemsForOneBin(const Bin& bin, const int default_capacity) : bin(bin) { + elements.reserve(default_capacity); } - -void -ProjMatrixElemsForOneBin:: -reserve(size_type max_number) -{ +void +ProjMatrixElemsForOneBin::reserve(size_type max_number) { elements.reserve(max_number); } - -void ProjMatrixElemsForOneBin::erase() -{ +void +ProjMatrixElemsForOneBin::erase() { elements.resize(0); } -ProjMatrixElemsForOneBin::size_type -ProjMatrixElemsForOneBin:: -capacity() const -{ +ProjMatrixElemsForOneBin::size_type +ProjMatrixElemsForOneBin::capacity() const { return elements.capacity(); } -ProjMatrixElemsForOneBin& ProjMatrixElemsForOneBin::operator*=(const float d) -{ +ProjMatrixElemsForOneBin& +ProjMatrixElemsForOneBin::operator*=(const float d) { // KT 21/02/2002 added check on 1 - if (d != 1.F) - { + if (d != 1.F) { iterator element_ptr = begin(); - while (element_ptr != end()) - { - *element_ptr *= d; + while (element_ptr != end()) { + *element_ptr *= d; ++element_ptr; } - } + } return *this; } -ProjMatrixElemsForOneBin& ProjMatrixElemsForOneBin::operator/=(const float d) -{ - assert( d != 0); +ProjMatrixElemsForOneBin& +ProjMatrixElemsForOneBin::operator/=(const float d) { + assert(d != 0); // KT 21/02/2002 added check on 1 - if (d != 1.F) - { + if (d != 1.F) { iterator element_ptr = begin(); - while (element_ptr != end()) - { + while (element_ptr != end()) { *element_ptr /= d; ++element_ptr; - } + } } return *this; } - -Succeeded ProjMatrixElemsForOneBin::check_state() const -{ +Succeeded +ProjMatrixElemsForOneBin::check_state() const { Succeeded success = Succeeded::yes; - if (size()==0) + if (size() == 0) return success; ProjMatrixElemsForOneBin lor = *this; lor.sort(); - - for (ProjMatrixElemsForOneBin::const_iterator lor_iter = lor.begin(); - lor_iter != lor.end()-1; - ++lor_iter) - { - if (value_type::coordinates_equal(*lor_iter, *(lor_iter+1))) - { + + for (ProjMatrixElemsForOneBin::const_iterator lor_iter = lor.begin(); lor_iter != lor.end() - 1; ++lor_iter) { + if (value_type::coordinates_equal(*lor_iter, *(lor_iter + 1))) { warning("ProjMatrixElemsForOneBin: coordinates occur more than once %d,%d,%d for bin s=%d, tofbin=%d, v=%d, a=%d, t=%d\n", - lor_iter->coord1(), lor_iter->coord2(), lor_iter->coord3(), - bin.segment_num(), bin.timing_pos_num(), bin.view_num(), - bin.axial_pos_num(), bin.tangential_pos_num()); + lor_iter->coord1(), lor_iter->coord2(), lor_iter->coord3(), bin.segment_num(), bin.timing_pos_num(), bin.view_num(), + bin.axial_pos_num(), bin.tangential_pos_num()); #if 0 const_iterator iter = begin(); while (iter!= end()) @@ -147,129 +127,110 @@ Succeeded ProjMatrixElemsForOneBin::check_state() const return success; } - -void ProjMatrixElemsForOneBin::sort() -{ +void +ProjMatrixElemsForOneBin::sort() { // need explicit std:: here to resolve possible name conflict // this might give you trouble if your compiler does not support namespaces #if !defined(STIR_NO_NAMESPACES) || (__GNUC__ == 2 && __GNUC_MINOR__ <= 8) std:: -#endif - sort(begin(), end(), value_type::coordinates_less); +#endif + sort(begin(), end(), value_type::coordinates_less); } - -float ProjMatrixElemsForOneBin::square_sum() const -{ - float sq_sum=0; +float +ProjMatrixElemsForOneBin::square_sum() const { + float sq_sum = 0; const_iterator element_ptr = begin(); - while (element_ptr != end()) - { - sq_sum += square(element_ptr->get_value()); + while (element_ptr != end()) { + sq_sum += square(element_ptr->get_value()); ++element_ptr; - } + } return sq_sum; } // TODO make sure we can have a const argument -void -ProjMatrixElemsForOneBin:: -merge( ProjMatrixElemsForOneBin &lor2 ) -{ +void +ProjMatrixElemsForOneBin::merge(ProjMatrixElemsForOneBin& lor2) { assert(check_state() == Succeeded::yes); assert(lor2.check_state() == Succeeded::yes); - if (lor2.size()==0) + if (lor2.size() == 0) return; #ifndef NDEBUG // we will check at the end of the total probability is conserved, so we compute it first float old_sum = 0; - for (const_iterator iter= begin(); iter != end(); ++iter) - old_sum+= iter->get_value(); - for (const_iterator iter= lor2.begin(); iter != lor2.end(); ++iter) - old_sum+= iter->get_value(); + for (const_iterator iter = begin(); iter != end(); ++iter) + old_sum += iter->get_value(); + for (const_iterator iter = lor2.begin(); iter != lor2.end(); ++iter) + old_sum += iter->get_value(); #endif // KT 15/05/2002 insert sort first, as both implementations assume this sort(); lor2.sort(); iterator element_ptr = begin(); - iterator element_ptr2= lor2.begin(); + iterator element_ptr2 = lor2.begin(); // implementation note: cannot use const_iterator in the line above // as some compilers (including gcc 3.* and VC 7.0) would not compile - // the elements.insert() line below, as the types of the iterators no longer + // the elements.insert() line below, as the types of the iterators no longer // match exactly. #if 1 // KT 15/05/2002 much faster implementation than before, and its output is sorted -#ifndef NDEBUG - //unsigned int insertions = 0; -#endif - while ( element_ptr2 != lor2.end() ) - { - if (element_ptr == end()) - { +# ifndef NDEBUG + // unsigned int insertions = 0; +# endif + while (element_ptr2 != lor2.end()) { + if (element_ptr == end()) { elements.reserve((lor2.end() - element_ptr2) + size()); - elements.insert(end(), element_ptr2, lor2.end()); + elements.insert(end(), element_ptr2, lor2.end()); break; } - if (value_type::coordinates_equal(*element_ptr2, *element_ptr)) - { + if (value_type::coordinates_equal(*element_ptr2, *element_ptr)) { *element_ptr++ += *element_ptr2++; - } - else if (value_type::coordinates_less(*element_ptr2, *element_ptr)) - { + } else if (value_type::coordinates_less(*element_ptr2, *element_ptr)) { element_ptr = elements.insert(element_ptr, *element_ptr2); assert(*element_ptr == *element_ptr2); - ++element_ptr; ++element_ptr2; -#ifndef NDEBUG + ++element_ptr; + ++element_ptr2; +# ifndef NDEBUG //++insertions; -#endif - } - else - { +# endif + } else { ++element_ptr; } } - #else // this old version assumed the input arrays were sorted, but its output wasn't // it could be rewritten to avoid lor2.erase() such that this method could be const // this would probably speed it up anyway - bool found=false; - while ( element_ptr2 != lor2.end() ) - { + bool found = false; + while (element_ptr2 != lor2.end()) { // here it is assumed that the input is sorted // this could be possibly be dropped by initialising dup_xyz = begin() // performance would be bad however - iterator dup_xyz = element_ptr; - while ( dup_xyz != end() ) - { - if (value_type::coordinates_equal(*element_ptr2, *dup_xyz)) - { - *dup_xyz += *element_ptr2; - element_ptr = dup_xyz+1; + iterator dup_xyz = element_ptr; + while (dup_xyz != end()) { + if (value_type::coordinates_equal(*element_ptr2, *dup_xyz)) { + *dup_xyz += *element_ptr2; + element_ptr = dup_xyz + 1; found = true; break; // assume no more duplicated point - } - else + } else ++dup_xyz; } - if( found ) - { - element_ptr2 = lor2.erase(element_ptr2); + if (found) { + element_ptr2 = lor2.erase(element_ptr2); found = false; + } else { + ++element_ptr2; } - else{ - ++element_ptr2; - } } // append the rest (but not sorted) - element_ptr2 = lor2.begin(); - while ( element_ptr2 != lor2.end() ) - { - push_back( *element_ptr2); + element_ptr2 = lor2.begin(); + while (element_ptr2 != lor2.end()) { + push_back(*element_ptr2); ++element_ptr2; } // KT 15/05/2002 added sort for compatiblity with other version @@ -278,18 +239,17 @@ merge( ProjMatrixElemsForOneBin &lor2 ) #ifndef NDEBUG // check that the values sum to the same as before merging float new_sum = 0; - for (const_iterator iter= begin(); iter != end(); ++iter) - new_sum+= iter->get_value(); - assert(fabs(new_sum-old_sum)get_value(); + assert(fabs(new_sum - old_sum) < old_sum * 10E-4); // check that it's sorted - for (const_iterator iter=begin(); iter+1!=end(); ++iter) - assert(value_type::coordinates_less(*iter, *(iter+1))); - //std::cerr << insertions << " insertions, size " << size() << std::endl; + for (const_iterator iter = begin(); iter + 1 != end(); ++iter) + assert(value_type::coordinates_less(*iter, *(iter + 1))); + // std::cerr << insertions << " insertions, size " << size() << std::endl; #endif assert(check_state() == Succeeded::yes); } - #if 0 // todo remove this void ProjMatrixElemsForOneBin::clean_neg_z() @@ -343,108 +303,84 @@ void ProjMatrixElemsForOneBin::read( fstream&fst ) } #endif -/////////////////// projection operations ////////////////////////////////// -void -ProjMatrixElemsForOneBin:: -back_project(DiscretisedDensity<3,float>& density, - const Bin& single) const -{ - { - const float data = single.get_bin_value() ; +/////////////////// projection operations ////////////////////////////////// +void +ProjMatrixElemsForOneBin::back_project(DiscretisedDensity<3, float>& density, const Bin& single) const { + { + const float data = single.get_bin_value(); // KT 21/02/2002 added check on 0 if (data == 0) return; - - BasicCoordinate<3,int> coords; - const_iterator element_ptr = - begin(); - while (element_ptr != end()) - { + + BasicCoordinate<3, int> coords; + const_iterator element_ptr = begin(); + while (element_ptr != end()) { coords = element_ptr->get_coords(); if (coords[1] >= density.get_min_index() && coords[1] <= density.get_max_index()) - density[coords[1]][coords[2]][coords[3]] += element_ptr->get_value() * data; - element_ptr++; - } - } + density[coords[1]][coords[2]][coords[3]] += element_ptr->get_value() * data; + element_ptr++; + } + } } +void +ProjMatrixElemsForOneBin::forward_project(Bin& single, const DiscretisedDensity<3, float>& density) const { + { -void -ProjMatrixElemsForOneBin:: -forward_project(Bin& single, - const DiscretisedDensity<3,float>& density) const -{ - { - - BasicCoordinate<3,int> coords; + BasicCoordinate<3, int> coords; const_iterator element_ptr = begin(); - - while (element_ptr != end()) - { + + while (element_ptr != end()) { coords = element_ptr->get_coords(); - + if (coords[1] >= density.get_min_index() && coords[1] <= density.get_max_index()) single += density[coords[1]][coords[2]][coords[3]] * element_ptr->get_value(); - ++element_ptr; - } - } + ++element_ptr; + } + } } +void +ProjMatrixElemsForOneBin::back_project(DiscretisedDensity<3, float>& density, const RelatedBins& r_bins) const { + const DataSymmetriesForBins* symmetries = r_bins.get_symmetries_ptr(); -void -ProjMatrixElemsForOneBin:: -back_project(DiscretisedDensity<3,float>& density, - const RelatedBins& r_bins) const -{ - const DataSymmetriesForBins* symmetries = r_bins.get_symmetries_ptr(); - - RelatedBins::const_iterator r_bins_iterator =r_bins.begin(); + RelatedBins::const_iterator r_bins_iterator = r_bins.begin(); ProjMatrixElemsForOneBin row_copy; while (r_bins_iterator != r_bins.end()) - - { + + { row_copy = *this; - + Bin symmetric_bin = *r_bins_iterator; // KT 21/02/2002 added check on 0 if (symmetric_bin.get_bin_value() == 0) return; - unique_ptr symm_ptr = - symmetries->find_symmetry_operation_from_basic_bin(symmetric_bin); + unique_ptr symm_ptr = symmetries->find_symmetry_operation_from_basic_bin(symmetric_bin); symm_ptr->transform_proj_matrix_elems_for_one_bin(row_copy); - row_copy.back_project(density,symmetric_bin); - } + row_copy.back_project(density, symmetric_bin); + } } +void +ProjMatrixElemsForOneBin::forward_project(RelatedBins& r_bins, const DiscretisedDensity<3, float>& density) const { + const DataSymmetriesForBins* symmetries = r_bins.get_symmetries_ptr(); -void -ProjMatrixElemsForOneBin:: -forward_project(RelatedBins& r_bins, - const DiscretisedDensity<3,float>& density) const -{ - const DataSymmetriesForBins* symmetries = r_bins.get_symmetries_ptr(); - - RelatedBins::iterator r_bins_iterator =r_bins.begin(); + RelatedBins::iterator r_bins_iterator = r_bins.begin(); ProjMatrixElemsForOneBin row_copy; - + while (r_bins_iterator != r_bins.end()) - - { + + { row_copy = *this; - - unique_ptr symm_op_ptr = - symmetries->find_symmetry_operation_from_basic_bin(*r_bins_iterator); + + unique_ptr symm_op_ptr = symmetries->find_symmetry_operation_from_basic_bin(*r_bins_iterator); symm_op_ptr->transform_proj_matrix_elems_for_one_bin(row_copy); - row_copy.forward_project(*r_bins_iterator,density); - } - + row_copy.forward_project(*r_bins_iterator, density); + } } - -bool -ProjMatrixElemsForOneBin:: -operator==(const ProjMatrixElemsForOneBin& lor) const -{ +bool +ProjMatrixElemsForOneBin::operator==(const ProjMatrixElemsForOneBin& lor) const { // first determine a tolerance for the floating point comparisons // do this by finding the maxumum value in the first lor float max_value = 0; @@ -453,59 +389,51 @@ operator==(const ProjMatrixElemsForOneBin& lor) const // extra brackets here to avoid VC 7.1 complaining about this_iter // being defined here in for() and a few lines further on // (it really shouldn't complain) - for (const_iterator this_iter= begin(); this_iter!= end(); ++this_iter) - { - const float current_value=this_iter->get_value(); - if (current_value> max_value) - max_value = current_value; - } + for (const_iterator this_iter = begin(); this_iter != end(); ++this_iter) { + const float current_value = this_iter->get_value(); + if (current_value > max_value) + max_value = current_value; + } } - const float tolerance = max_value*.002F; + const float tolerance = max_value * .002F; - const_iterator this_iter= begin(); + const_iterator this_iter = begin(); const_iterator lor_iter = lor.begin(); - while (this_iter!= end() && lor_iter!=lor.end()) - { - if (this_iter->get_coords() == lor_iter->get_coords()) - { - // coordinates are equal, so test for value - if (fabs(this_iter->get_value() - lor_iter->get_value()) > tolerance) - return false; - } - else - { - // coordinates are not equal, so check if value of one of them is small and we should skip it - if (this_iter->get_value() < tolerance) - { - ++this_iter; continue; - } - if (lor_iter->get_value() < tolerance) - { - ++lor_iter; continue; - } - // either one is not small - return false; - } - // we got here, so comparison was ok - ++this_iter; - ++lor_iter; - } - for (; this_iter!= end(); ++this_iter) - { - if (this_iter->get_value()> tolerance) - return false; - } - for (; lor_iter!= lor.end(); ++lor_iter) - { - if (lor_iter->get_value()> tolerance) - return false; + while (this_iter != end() && lor_iter != lor.end()) { + if (this_iter->get_coords() == lor_iter->get_coords()) { + // coordinates are equal, so test for value + if (fabs(this_iter->get_value() - lor_iter->get_value()) > tolerance) + return false; + } else { + // coordinates are not equal, so check if value of one of them is small and we should skip it + if (this_iter->get_value() < tolerance) { + ++this_iter; + continue; + } + if (lor_iter->get_value() < tolerance) { + ++lor_iter; + continue; + } + // either one is not small + return false; } + // we got here, so comparison was ok + ++this_iter; + ++lor_iter; + } + for (; this_iter != end(); ++this_iter) { + if (this_iter->get_value() > tolerance) + return false; + } + for (; lor_iter != lor.end(); ++lor_iter) { + if (lor_iter->get_value() > tolerance) + return false; + } return true; - } -bool -ProjMatrixElemsForOneBin:: -operator!=(const ProjMatrixElemsForOneBin& lor) const -{ return !(*this==lor); } +bool +ProjMatrixElemsForOneBin::operator!=(const ProjMatrixElemsForOneBin& lor) const { + return !(*this == lor); +} END_NAMESPACE_STIR diff --git a/src/recon_buildblock/ProjMatrixElemsForOneDensel.cxx b/src/recon_buildblock/ProjMatrixElemsForOneDensel.cxx index 38979c58cc..5f9219cda2 100644 --- a/src/recon_buildblock/ProjMatrixElemsForOneDensel.cxx +++ b/src/recon_buildblock/ProjMatrixElemsForOneDensel.cxx @@ -5,9 +5,9 @@ \file \ingroup projection \brief non-inline implementations for stir::ProjMatrixElemsForOneDensel - + \author Kris Thielemans - + */ /* Copyright (C) 2000- 2009, Hammersmith Imanet Ltd @@ -36,58 +36,42 @@ START_NAMESPACE_STIR - -ProjMatrixElemsForOneDensel:: -ProjMatrixElemsForOneDensel(const Densel& densel, const int default_capacity) -: densel(densel) -{ - elements.reserve(default_capacity); -} - -ProjMatrixElemsForOneDensel:: -ProjMatrixElemsForOneDensel() -{ - elements.reserve(300); +ProjMatrixElemsForOneDensel::ProjMatrixElemsForOneDensel(const Densel& densel, const int default_capacity) : densel(densel) { + elements.reserve(default_capacity); } +ProjMatrixElemsForOneDensel::ProjMatrixElemsForOneDensel() { elements.reserve(300); } -void -ProjMatrixElemsForOneDensel:: -reserve(size_type max_number) -{ +void +ProjMatrixElemsForOneDensel::reserve(size_type max_number) { elements.reserve(max_number); } - -void ProjMatrixElemsForOneDensel::erase() -{ +void +ProjMatrixElemsForOneDensel::erase() { elements.resize(0); } -ProjMatrixElemsForOneDensel& ProjMatrixElemsForOneDensel::operator*=(const float d) -{ +ProjMatrixElemsForOneDensel& +ProjMatrixElemsForOneDensel::operator*=(const float d) { // KT 21/02/2002 added check on 1 - if (d != 1.F) - { + if (d != 1.F) { iterator element_ptr = begin(); - while (element_ptr != end()) - { - *element_ptr *= d; + while (element_ptr != end()) { + *element_ptr *= d; ++element_ptr; - } + } } return *this; } -ProjMatrixElemsForOneDensel& ProjMatrixElemsForOneDensel::operator/=(const float d) -{ - assert( d != 0); +ProjMatrixElemsForOneDensel& +ProjMatrixElemsForOneDensel::operator/=(const float d) { + assert(d != 0); // KT 21/02/2002 added check on 1 - if (d != 1.F) - { + if (d != 1.F) { iterator element_ptr = begin(); - while (element_ptr != end()) - { + while (element_ptr != end()) { *element_ptr /= d; ++element_ptr; } @@ -95,110 +79,91 @@ ProjMatrixElemsForOneDensel& ProjMatrixElemsForOneDensel::operator/=(const float return *this; } - -Succeeded ProjMatrixElemsForOneDensel::check_state() const -{ +Succeeded +ProjMatrixElemsForOneDensel::check_state() const { Succeeded success = Succeeded::yes; - if (size()==0) + if (size() == 0) return success; ProjMatrixElemsForOneDensel lor = *this; lor.sort(); - - for (ProjMatrixElemsForOneDensel::const_iterator lor_iter = lor.begin(); - lor_iter != lor.end()-1; - ++lor_iter) - { - if (value_type::coordinates_equal(*lor_iter, *(lor_iter+1))) - { - warning("ProjMatrixElemsForOneDensel: coordinates occur more than once %d,%d,%d,%d\n", - lor_iter->segment_num(), lor_iter->view_num(), - lor_iter->axial_pos_num(), lor_iter->tangential_pos_num()); + + for (ProjMatrixElemsForOneDensel::const_iterator lor_iter = lor.begin(); lor_iter != lor.end() - 1; ++lor_iter) { + if (value_type::coordinates_equal(*lor_iter, *(lor_iter + 1))) { + warning("ProjMatrixElemsForOneDensel: coordinates occur more than once %d,%d,%d,%d\n", lor_iter->segment_num(), + lor_iter->view_num(), lor_iter->axial_pos_num(), lor_iter->tangential_pos_num()); success = Succeeded::no; } } return success; } - -void ProjMatrixElemsForOneDensel::sort() -{ +void +ProjMatrixElemsForOneDensel::sort() { // need explicit std:: here to resolve possible name conflict // this might give you trouble if your compiler does not support namespaces #if !defined(STIR_NO_NAMESPACES) || (__GNUC__ == 2 && __GNUC_MINOR__ <= 8) std:: -#endif - sort(begin(), end(), value_type::coordinates_less); +#endif + sort(begin(), end(), value_type::coordinates_less); } - -float ProjMatrixElemsForOneDensel::square_sum() const -{ - float sq_sum=0; +float +ProjMatrixElemsForOneDensel::square_sum() const { + float sq_sum = 0; const_iterator element_ptr = begin(); - while (element_ptr != end()) - { - sq_sum += square(element_ptr->get_bin_value()); + while (element_ptr != end()) { + sq_sum += square(element_ptr->get_bin_value()); ++element_ptr; - } + } return sq_sum; } // TODO make sure we can have a const argument // not calling lor2.erase() would probably speed it up anyway -void -ProjMatrixElemsForOneDensel:: -merge( ProjMatrixElemsForOneDensel &lor2 ) -{ +void +ProjMatrixElemsForOneDensel::merge(ProjMatrixElemsForOneDensel& lor2) { assert(check_state() == Succeeded::yes); assert(lor2.check_state() == Succeeded::yes); iterator element_ptr = begin(); - iterator element_ptr2= lor2.begin(); - - bool found=false; - while ( element_ptr2 != lor2.end() ) - { - //unsigned int key = make_key( element_ptr2->x, element_ptr2->y,element_ptr2->z); - iterator dup_xyz = element_ptr; - while ( dup_xyz != end() ) - { - //unsigned int dup_key = make_key( dup_xyz->x,dup_xyz->y,dup_xyz->z); - - //if ( dup_key == key ) - if (value_type::coordinates_equal(*element_ptr2, *dup_xyz)) - { - //TEST///////// - *dup_xyz += *element_ptr2; + iterator element_ptr2 = lor2.begin(); + + bool found = false; + while (element_ptr2 != lor2.end()) { + // unsigned int key = make_key( element_ptr2->x, element_ptr2->y,element_ptr2->z); + iterator dup_xyz = element_ptr; + while (dup_xyz != end()) { + // unsigned int dup_key = make_key( dup_xyz->x,dup_xyz->y,dup_xyz->z); + + // if ( dup_key == key ) + if (value_type::coordinates_equal(*element_ptr2, *dup_xyz)) { + // TEST///////// + *dup_xyz += *element_ptr2; /////////////// - element_ptr = dup_xyz+1; + element_ptr = dup_xyz + 1; found = true; - break; // assume no more duplicated point, only 2 lors - } - else + break; // assume no more duplicated point, only 2 lors + } else ++dup_xyz; } - if( found ) - { - element_ptr2 = lor2.erase(element_ptr2); + if (found) { + element_ptr2 = lor2.erase(element_ptr2); found = false; + } else { + ++element_ptr2; } - else{ - ++element_ptr2; - } } // append the rest - element_ptr2 = lor2.begin(); - while ( element_ptr2 != lor2.end() ) - { - push_back( *element_ptr2); + element_ptr2 = lor2.begin(); + while (element_ptr2 != lor2.end()) { + push_back(*element_ptr2); ++element_ptr2; } assert(check_state() == Succeeded::yes); } - #if 0 // todo remove this void ProjMatrixElemsForOneDensel::clean_neg_z() @@ -252,7 +217,7 @@ void ProjMatrixElemsForOneDensel::read( fstream&fst ) } #endif -/////////////////// projection operations ////////////////////////////////// +/////////////////// projection operations ////////////////////////////////// #if 0 // TODO void diff --git a/src/recon_buildblock/ProjectorByBinPair.cxx b/src/recon_buildblock/ProjectorByBinPair.cxx index 618ae26fd6..d0f9df5e66 100644 --- a/src/recon_buildblock/ProjectorByBinPair.cxx +++ b/src/recon_buildblock/ProjectorByBinPair.cxx @@ -5,9 +5,9 @@ \ingroup projection \brief non-inline implementations for stir::ProjectorByBinPair - + \author Kris Thielemans - + */ /* Copyright (C) 2000- 2009, Hammersmith Imanet Ltd @@ -27,7 +27,6 @@ See STIR/LICENSE.txt for details */ - #include "stir/recon_buildblock/ProjectorByBinPair.h" #include "stir/ProjDataInfo.h" #include "stir/DiscretisedDensity.h" @@ -41,19 +40,13 @@ START_NAMESPACE_STIR - -ProjectorByBinPair:: -ProjectorByBinPair() - : _already_set_up(false) -{ -} +ProjectorByBinPair::ProjectorByBinPair() : _already_set_up(false) {} Succeeded -ProjectorByBinPair:: -set_up(const shared_ptr& proj_data_info_sptr, - const shared_ptr >& image_info_sptr) +ProjectorByBinPair::set_up(const shared_ptr& proj_data_info_sptr, + const shared_ptr>& image_info_sptr) -{ +{ _already_set_up = true; _proj_data_info_sptr = proj_data_info_sptr->create_shared_clone(); _density_info_sptr = image_info_sptr; @@ -63,31 +56,26 @@ set_up(const shared_ptr& proj_data_info_sptr, } void -ProjectorByBinPair:: -check(const ProjDataInfo& proj_data_info, const DiscretisedDensity<3,float>& density_info) const -{ +ProjectorByBinPair::check(const ProjDataInfo& proj_data_info, const DiscretisedDensity<3, float>& density_info) const { if (!this->_already_set_up) error("ProjectorByBinPair method called without calling set_up first."); if (!(*this->_proj_data_info_sptr >= proj_data_info)) - error(boost::format("ProjectorByBinPair set-up with different geometry for projection data.\nSet_up was with\n%1%\nCalled with\n%2%") - % this->_proj_data_info_sptr->parameter_info() % proj_data_info.parameter_info()); - if (! this->_density_info_sptr->has_same_characteristics(density_info)) + error(boost::format( + "ProjectorByBinPair set-up with different geometry for projection data.\nSet_up was with\n%1%\nCalled with\n%2%") % + this->_proj_data_info_sptr->parameter_info() % proj_data_info.parameter_info()); + if (!this->_density_info_sptr->has_same_characteristics(density_info)) error("ProjectorByBinPair set-up with different geometry for density or volume data."); } -//ForwardProjectorByBin const * +// ForwardProjectorByBin const * const shared_ptr -ProjectorByBinPair:: -get_forward_projector_sptr() const -{ +ProjectorByBinPair::get_forward_projector_sptr() const { return forward_projector_sptr; } -//BackProjectorByBin const * +// BackProjectorByBin const * const shared_ptr -ProjectorByBinPair:: -get_back_projector_sptr() const -{ +ProjectorByBinPair::get_back_projector_sptr() const { return back_projector_sptr; } diff --git a/src/recon_buildblock/ProjectorByBinPairUsingProjMatrixByBin.cxx b/src/recon_buildblock/ProjectorByBinPairUsingProjMatrixByBin.cxx index 8c18feeab9..52ae8f6ce1 100644 --- a/src/recon_buildblock/ProjectorByBinPairUsingProjMatrixByBin.cxx +++ b/src/recon_buildblock/ProjectorByBinPairUsingProjMatrixByBin.cxx @@ -5,9 +5,9 @@ \ingroup projection \brief non-inline implementations for stir::ProjectorByBinPairUsingProjMatrixByBin - + \author Kris Thielemans - + */ /* Copyright (C) 2000- 2011, Hammersmith Imanet Ltd @@ -27,7 +27,6 @@ See STIR/LICENSE.txt for details */ - #include "stir/recon_buildblock/ProjectorByBinPairUsingProjMatrixByBin.h" #include "stir/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.h" #include "stir/recon_buildblock/BackProjectorByBinUsingProjMatrixByBin.h" @@ -36,59 +35,45 @@ START_NAMESPACE_STIR +const char* const ProjectorByBinPairUsingProjMatrixByBin::registered_name = "Matrix"; -const char * const -ProjectorByBinPairUsingProjMatrixByBin::registered_name = - "Matrix"; - - -void -ProjectorByBinPairUsingProjMatrixByBin::initialise_keymap() -{ +void +ProjectorByBinPairUsingProjMatrixByBin::initialise_keymap() { base_type::initialise_keymap(); parser.add_start_key("Projector Pair Using Matrix Parameters"); parser.add_stop_key("End Projector Pair Using Matrix Parameters"); - parser.add_parsing_key("Matrix type",&proj_matrix_sptr); + parser.add_parsing_key("Matrix type", &proj_matrix_sptr); } - void -ProjectorByBinPairUsingProjMatrixByBin::set_defaults() -{ +ProjectorByBinPairUsingProjMatrixByBin::set_defaults() { base_type::set_defaults(); this->proj_matrix_sptr.reset(); } bool -ProjectorByBinPairUsingProjMatrixByBin::post_processing() -{ +ProjectorByBinPairUsingProjMatrixByBin::post_processing() { if (base_type::post_processing()) return true; - if (is_null_ptr(proj_matrix_sptr)) - { warning("No valid projection matrix is defined\n"); return true; } + if (is_null_ptr(proj_matrix_sptr)) { + warning("No valid projection matrix is defined\n"); + return true; + } this->forward_projector_sptr.reset(new ForwardProjectorByBinUsingProjMatrixByBin(proj_matrix_sptr)); this->back_projector_sptr.reset(new BackProjectorByBinUsingProjMatrixByBin(proj_matrix_sptr)); return false; } -ProjectorByBinPairUsingProjMatrixByBin:: -ProjectorByBinPairUsingProjMatrixByBin() -{ - set_defaults(); -} +ProjectorByBinPairUsingProjMatrixByBin::ProjectorByBinPairUsingProjMatrixByBin() { set_defaults(); } -ProjectorByBinPairUsingProjMatrixByBin:: -ProjectorByBinPairUsingProjMatrixByBin( - const shared_ptr& proj_matrix_sptr_) -{ +ProjectorByBinPairUsingProjMatrixByBin::ProjectorByBinPairUsingProjMatrixByBin( + const shared_ptr& proj_matrix_sptr_) { this->set_proj_matrix_sptr(proj_matrix_sptr_); } Succeeded -ProjectorByBinPairUsingProjMatrixByBin:: -set_up(const shared_ptr& proj_data_info_sptr, - const shared_ptr >& image_info_sptr) -{ +ProjectorByBinPairUsingProjMatrixByBin::set_up(const shared_ptr& proj_data_info_sptr, + const shared_ptr>& image_info_sptr) { // proj_matrix_sptr->set_up() not needed as the projection matrix will be set_up indirectly by // the forward_projector->set_up (which is called in the base class) // proj_matrix_sptr->set_up(proj_data_info_sptr, image_info_sptr); @@ -99,24 +84,18 @@ set_up(const shared_ptr& proj_data_info_sptr, return Succeeded::yes; } -ProjMatrixByBin const * -ProjectorByBinPairUsingProjMatrixByBin:: -get_proj_matrix_ptr() const -{ +ProjMatrixByBin const* +ProjectorByBinPairUsingProjMatrixByBin::get_proj_matrix_ptr() const { return proj_matrix_sptr.get(); } shared_ptr -ProjectorByBinPairUsingProjMatrixByBin:: -get_proj_matrix_sptr() const -{ - return proj_matrix_sptr; +ProjectorByBinPairUsingProjMatrixByBin::get_proj_matrix_sptr() const { + return proj_matrix_sptr; } void -ProjectorByBinPairUsingProjMatrixByBin:: -set_proj_matrix_sptr(const shared_ptr& sptr) -{ +ProjectorByBinPairUsingProjMatrixByBin::set_proj_matrix_sptr(const shared_ptr& sptr) { this->proj_matrix_sptr = sptr; this->forward_projector_sptr.reset(new ForwardProjectorByBinUsingProjMatrixByBin(this->proj_matrix_sptr)); this->back_projector_sptr.reset(new BackProjectorByBinUsingProjMatrixByBin(this->proj_matrix_sptr)); diff --git a/src/recon_buildblock/ProjectorByBinPairUsingSeparateProjectors.cxx b/src/recon_buildblock/ProjectorByBinPairUsingSeparateProjectors.cxx index 5f11846013..deb9d31fad 100644 --- a/src/recon_buildblock/ProjectorByBinPairUsingSeparateProjectors.cxx +++ b/src/recon_buildblock/ProjectorByBinPairUsingSeparateProjectors.cxx @@ -5,9 +5,9 @@ \ingroup projection \brief non-inline implementations for stir::ProjectorByBinPairUsingSeparateProjectors - + \author Kris Thielemans - + */ /* Copyright (C) 2000- 2011, Hammersmith Imanet Ltd @@ -26,66 +26,53 @@ See STIR/LICENSE.txt for details */ - #include "stir/recon_buildblock/ProjectorByBinPairUsingSeparateProjectors.h" #include "stir/is_null_ptr.h" #include "stir/Succeeded.h" START_NAMESPACE_STIR +const char* const ProjectorByBinPairUsingSeparateProjectors::registered_name = "Separate Projectors"; -const char * const -ProjectorByBinPairUsingSeparateProjectors::registered_name = - "Separate Projectors"; - - -void -ProjectorByBinPairUsingSeparateProjectors::initialise_keymap() -{ +void +ProjectorByBinPairUsingSeparateProjectors::initialise_keymap() { parser.add_start_key("Projector Pair Using Separate Projectors Parameters"); parser.add_stop_key("End Projector Pair Using Separate Projectors Parameters"); - parser.add_parsing_key("Forward projector type",&forward_projector_sptr); - parser.add_parsing_key("Back projector type",&back_projector_sptr); + parser.add_parsing_key("Forward projector type", &forward_projector_sptr); + parser.add_parsing_key("Back projector type", &back_projector_sptr); } - void -ProjectorByBinPairUsingSeparateProjectors:: -set_defaults() -{ +ProjectorByBinPairUsingSeparateProjectors::set_defaults() { base_type::set_defaults(); forward_projector_sptr.reset(); back_projector_sptr.reset(); } bool -ProjectorByBinPairUsingSeparateProjectors:: -post_processing() -{ +ProjectorByBinPairUsingSeparateProjectors::post_processing() { if (base_type::post_processing()) return true; - if (is_null_ptr(forward_projector_sptr)) - { warning("No valid forward projector is defined\n"); return true; } + if (is_null_ptr(forward_projector_sptr)) { + warning("No valid forward projector is defined\n"); + return true; + } - if (is_null_ptr(back_projector_sptr)) - { warning("No valid back projector is defined\n"); return true; } + if (is_null_ptr(back_projector_sptr)) { + warning("No valid back projector is defined\n"); + return true; + } return false; } -ProjectorByBinPairUsingSeparateProjectors:: -ProjectorByBinPairUsingSeparateProjectors() -{ - set_defaults(); -} +ProjectorByBinPairUsingSeparateProjectors::ProjectorByBinPairUsingSeparateProjectors() { set_defaults(); } -ProjectorByBinPairUsingSeparateProjectors:: -ProjectorByBinPairUsingSeparateProjectors(const shared_ptr& forward_projector_sptr_v, - const shared_ptr& back_projector_sptr_v) -{ +ProjectorByBinPairUsingSeparateProjectors::ProjectorByBinPairUsingSeparateProjectors( + const shared_ptr& forward_projector_sptr_v, + const shared_ptr& back_projector_sptr_v) { forward_projector_sptr = forward_projector_sptr_v; back_projector_sptr = back_projector_sptr_v; } - END_NAMESPACE_STIR diff --git a/src/recon_buildblock/QuadraticPrior.cxx b/src/recon_buildblock/QuadraticPrior.cxx index 68603f2962..ccb75095fd 100644 --- a/src/recon_buildblock/QuadraticPrior.cxx +++ b/src/recon_buildblock/QuadraticPrior.cxx @@ -19,8 +19,8 @@ /*! \file \ingroup priors - \brief implementation of the stir::QuadraticPrior class - + \brief implementation of the stir::QuadraticPrior class + \author Kris Thielemans \author Sanida Mustafovic @@ -44,12 +44,11 @@ using std::max; START_NAMESPACE_STIR template -void -QuadraticPrior::initialise_keymap() -{ +void +QuadraticPrior::initialise_keymap() { base_type::initialise_keymap(); this->parser.add_start_key("Quadratic Prior Parameters"); - this->parser.add_key("only 2D", &only_2D); + this->parser.add_key("only 2D", &only_2D); this->parser.add_key("kappa filename", &kappa_filename); this->parser.add_key("weights", &weights); this->parser.add_key("gradient filename prefix", &gradient_filename_prefix); @@ -57,523 +56,449 @@ QuadraticPrior::initialise_keymap() } template -bool -QuadraticPrior::post_processing() -{ - if (base_type::post_processing()==true) +bool +QuadraticPrior::post_processing() { + if (base_type::post_processing() == true) return true; if (kappa_filename.size() != 0) - this->kappa_ptr = read_from_file >(kappa_filename); + this->kappa_ptr = read_from_file>(kappa_filename); bool warn_about_even_size = false; - if (this->weights.size() ==0) - { - // will call compute_weights() to fill it in + if (this->weights.size() == 0) { + // will call compute_weights() to fill it in + } else { + if (!this->weights.is_regular()) { + warning("Sorry. QuadraticPrior currently only supports regular arrays for the weights"); + return true; } - else - { - if (!this->weights.is_regular()) - { - warning("Sorry. QuadraticPrior currently only supports regular arrays for the weights"); - return true; - } - const unsigned int size_z = this->weights.size(); - if (size_z%2==0) - warn_about_even_size = true; - const int min_index_z = -static_cast(size_z/2); - this->weights.set_min_index(min_index_z); + const unsigned int size_z = this->weights.size(); + if (size_z % 2 == 0) + warn_about_even_size = true; + const int min_index_z = -static_cast(size_z / 2); + this->weights.set_min_index(min_index_z); - for (int z = min_index_z; z<= this->weights.get_max_index(); ++z) - { - const unsigned int size_y = this->weights[z].size(); - if (size_y%2==0) - warn_about_even_size = true; - const int min_index_y = -static_cast(size_y/2); - this->weights[z].set_min_index(min_index_y); - for (int y = min_index_y; y<= this->weights[z].get_max_index(); ++y) - { - const unsigned int size_x = this->weights[z][y].size(); - if (size_x%2==0) - warn_about_even_size = true; - const int min_index_x = -static_cast(size_x/2); - this->weights[z][y].set_min_index(min_index_x); - } - } + for (int z = min_index_z; z <= this->weights.get_max_index(); ++z) { + const unsigned int size_y = this->weights[z].size(); + if (size_y % 2 == 0) + warn_about_even_size = true; + const int min_index_y = -static_cast(size_y / 2); + this->weights[z].set_min_index(min_index_y); + for (int y = min_index_y; y <= this->weights[z].get_max_index(); ++y) { + const unsigned int size_x = this->weights[z][y].size(); + if (size_x % 2 == 0) + warn_about_even_size = true; + const int min_index_x = -static_cast(size_x / 2); + this->weights[z][y].set_min_index(min_index_x); + } } + } if (warn_about_even_size) warning("Parsing QuadraticPrior: even number of weights occured in either x,y or z dimension.\n" "I'll (effectively) make this odd by appending a 0 at the end."); return false; - } template Succeeded -QuadraticPrior::set_up (shared_ptr > const& target_sptr) -{ +QuadraticPrior::set_up(shared_ptr> const& target_sptr) { base_type::set_up(target_sptr); return Succeeded::yes; } template -void QuadraticPrior::check(DiscretisedDensity<3,elemT> const& current_image_estimate) const -{ +void +QuadraticPrior::check(DiscretisedDensity<3, elemT> const& current_image_estimate) const { // Do base-class check base_type::check(current_image_estimate); } template void -QuadraticPrior::set_defaults() -{ +QuadraticPrior::set_defaults() { base_type::set_defaults(); this->only_2D = false; - this->kappa_ptr.reset(); + this->kappa_ptr.reset(); this->weights.recycle(); } template <> -const char * const -QuadraticPrior::registered_name = - "Quadratic"; +const char* const QuadraticPrior::registered_name = "Quadratic"; template -QuadraticPrior::QuadraticPrior() -{ +QuadraticPrior::QuadraticPrior() { set_defaults(); } - template -QuadraticPrior::QuadraticPrior(const bool only_2D_v, float penalisation_factor_v) - : only_2D(only_2D_v) -{ +QuadraticPrior::QuadraticPrior(const bool only_2D_v, float penalisation_factor_v) : only_2D(only_2D_v) { this->penalisation_factor = penalisation_factor_v; } - - //! get penalty weights for the neigbourhood +//! get penalty weights for the neigbourhood template -Array<3,float> -QuadraticPrior:: -get_weights() const -{ return this->weights; } +Array<3, float> +QuadraticPrior::get_weights() const { + return this->weights; +} - //! set penalty weights for the neigbourhood -template -void -QuadraticPrior:: -set_weights(const Array<3,float>& w) -{ this->weights = w; } - - //! get current kappa image - /*! \warning As this function returns a shared_ptr, this is dangerous. You should not - modify the image by manipulating the image refered to by this pointer. - Unpredictable results will occur. - */ +//! set penalty weights for the neigbourhood template -shared_ptr > -QuadraticPrior:: -get_kappa_sptr() const -{ return this->kappa_ptr; } +void +QuadraticPrior::set_weights(const Array<3, float>& w) { + this->weights = w; +} - //! set kappa image +//! get current kappa image +/*! \warning As this function returns a shared_ptr, this is dangerous. You should not + modify the image by manipulating the image refered to by this pointer. + Unpredictable results will occur. +*/ template -void -QuadraticPrior:: -set_kappa_sptr(const shared_ptr >& k) -{ this->kappa_ptr = k; } +shared_ptr> +QuadraticPrior::get_kappa_sptr() const { + return this->kappa_ptr; +} +//! set kappa image +template +void +QuadraticPrior::set_kappa_sptr(const shared_ptr>& k) { + this->kappa_ptr = k; +} // TODO move to set_up // initialise to 1/Euclidean distance -static void -compute_weights(Array<3,float>& weights, const CartesianCoordinate3D& grid_spacing, const bool only_2D) -{ +static void +compute_weights(Array<3, float>& weights, const CartesianCoordinate3D& grid_spacing, const bool only_2D) { int min_dz, max_dz; - if (only_2D) - { - min_dz = max_dz = 0; - } - else - { - min_dz = -1; - max_dz = 1; - } - weights = Array<3,float>(IndexRange3D(min_dz,max_dz,-1,1,-1,1)); - for (int z=min_dz;z<=max_dz;++z) - for (int y=-1;y<=1;++y) - for (int x=-1;x<=1;++x) - { - if (z==0 && y==0 && x==0) - weights[0][0][0] = 0; - else - { - weights[z][y][x] = - grid_spacing.x()/ - sqrt(square(x*grid_spacing.x())+ - square(y*grid_spacing.y())+ - square(z*grid_spacing.z())); - } + if (only_2D) { + min_dz = max_dz = 0; + } else { + min_dz = -1; + max_dz = 1; + } + weights = Array<3, float>(IndexRange3D(min_dz, max_dz, -1, 1, -1, 1)); + for (int z = min_dz; z <= max_dz; ++z) + for (int y = -1; y <= 1; ++y) + for (int x = -1; x <= 1; ++x) { + if (z == 0 && y == 0 && x == 0) + weights[0][0][0] = 0; + else { + weights[z][y][x] = + grid_spacing.x() / sqrt(square(x * grid_spacing.x()) + square(y * grid_spacing.y()) + square(z * grid_spacing.z())); } + } } template double -QuadraticPrior:: -compute_value(const DiscretisedDensity<3,elemT> ¤t_image_estimate) -{ - if (this->penalisation_factor==0) - { +QuadraticPrior::compute_value(const DiscretisedDensity<3, elemT>& current_image_estimate) { + if (this->penalisation_factor == 0) { return 0.; } - + this->check(current_image_estimate); - - const DiscretisedDensityOnCartesianGrid<3,elemT>& current_image_cast = - dynamic_cast< const DiscretisedDensityOnCartesianGrid<3,elemT> &>(current_image_estimate); - - if (this->weights.get_length() ==0) - { + + const DiscretisedDensityOnCartesianGrid<3, elemT>& current_image_cast = + dynamic_cast&>(current_image_estimate); + + if (this->weights.get_length() == 0) { compute_weights(this->weights, current_image_cast.get_grid_spacing(), this->only_2D); } - + const bool do_kappa = !is_null_ptr(kappa_ptr); - + if (do_kappa && !kappa_ptr->has_same_characteristics(current_image_estimate)) error("QuadraticPrior: kappa image has not the same index range as the reconstructed image\n"); - double result = 0.; - const int min_z = current_image_estimate.get_min_index(); - const int max_z = current_image_estimate.get_max_index(); - for (int z=min_z; z<=max_z; z++) - { - const int min_dz = max(weights.get_min_index(), min_z-z); - const int max_dz = min(weights.get_max_index(), max_z-z); - - const int min_y = current_image_estimate[z].get_min_index(); - const int max_y = current_image_estimate[z].get_max_index(); - - for (int y=min_y;y<= max_y;y++) - { - const int min_dy = max(weights[0].get_min_index(), min_y-y); - const int max_dy = min(weights[0].get_max_index(), max_y-y); - - const int min_x = current_image_estimate[z][y].get_min_index(); - const int max_x = current_image_estimate[z][y].get_max_index(); - - for (int x=min_x;x<= max_x;x++) - { - const int min_dx = max(weights[0][0].get_min_index(), min_x-x); - const int max_dx = min(weights[0][0].get_max_index(), max_x-x); - - /* formula: - sum_dx,dy,dz - 1/4 weights[dz][dy][dx] * - (current_image_estimate[z][y][x] - current_image_estimate[z+dz][y+dy][x+dx])^2 * - (*kappa_ptr)[z][y][x] * (*kappa_ptr)[z+dz][y+dy][x+dx]; - */ - for (int dz=min_dz;dz<=max_dz;++dz) - for (int dy=min_dy;dy<=max_dy;++dy) - for (int dx=min_dx;dx<=max_dx;++dx) - { - elemT current = - weights[dz][dy][dx] * - square(current_image_estimate[z][y][x] - current_image_estimate[z+dz][y+dy][x+dx])/4; - - if (do_kappa) - current *= - (*kappa_ptr)[z][y][x] * (*kappa_ptr)[z+dz][y+dy][x+dx]; - - result += static_cast(current); - } - } - } + const int min_z = current_image_estimate.get_min_index(); + const int max_z = current_image_estimate.get_max_index(); + for (int z = min_z; z <= max_z; z++) { + const int min_dz = max(weights.get_min_index(), min_z - z); + const int max_dz = min(weights.get_max_index(), max_z - z); + + const int min_y = current_image_estimate[z].get_min_index(); + const int max_y = current_image_estimate[z].get_max_index(); + + for (int y = min_y; y <= max_y; y++) { + const int min_dy = max(weights[0].get_min_index(), min_y - y); + const int max_dy = min(weights[0].get_max_index(), max_y - y); + + const int min_x = current_image_estimate[z][y].get_min_index(); + const int max_x = current_image_estimate[z][y].get_max_index(); + + for (int x = min_x; x <= max_x; x++) { + const int min_dx = max(weights[0][0].get_min_index(), min_x - x); + const int max_dx = min(weights[0][0].get_max_index(), max_x - x); + + /* formula: + sum_dx,dy,dz + 1/4 weights[dz][dy][dx] * + (current_image_estimate[z][y][x] - current_image_estimate[z+dz][y+dy][x+dx])^2 * + (*kappa_ptr)[z][y][x] * (*kappa_ptr)[z+dz][y+dy][x+dx]; + */ + for (int dz = min_dz; dz <= max_dz; ++dz) + for (int dy = min_dy; dy <= max_dy; ++dy) + for (int dx = min_dx; dx <= max_dx; ++dx) { + elemT current = weights[dz][dy][dx] * + square(current_image_estimate[z][y][x] - current_image_estimate[z + dz][y + dy][x + dx]) / 4; + + if (do_kappa) + current *= (*kappa_ptr)[z][y][x] * (*kappa_ptr)[z + dz][y + dy][x + dx]; + + result += static_cast(current); + } + } } + } return result * this->penalisation_factor; } template -void -QuadraticPrior:: -compute_gradient(DiscretisedDensity<3,elemT>& prior_gradient, - const DiscretisedDensity<3,elemT> ¤t_image_estimate) -{ - assert( prior_gradient.has_same_characteristics(current_image_estimate)); - if (this->penalisation_factor==0) - { +void +QuadraticPrior::compute_gradient(DiscretisedDensity<3, elemT>& prior_gradient, + const DiscretisedDensity<3, elemT>& current_image_estimate) { + assert(prior_gradient.has_same_characteristics(current_image_estimate)); + if (this->penalisation_factor == 0) { prior_gradient.fill(0); return; } this->check(current_image_estimate); - - - const DiscretisedDensityOnCartesianGrid<3,elemT>& current_image_cast = - dynamic_cast< const DiscretisedDensityOnCartesianGrid<3,elemT> &>(current_image_estimate); - - if (this->weights.get_length() ==0) - { + + const DiscretisedDensityOnCartesianGrid<3, elemT>& current_image_cast = + dynamic_cast&>(current_image_estimate); + + if (this->weights.get_length() == 0) { compute_weights(this->weights, current_image_cast.get_grid_spacing(), this->only_2D); } - - - + const bool do_kappa = !is_null_ptr(kappa_ptr); if (do_kappa && !kappa_ptr->has_same_characteristics(current_image_estimate)) error("QuadraticPrior: kappa image has not the same index range as the reconstructed image\n"); - const int min_z = current_image_estimate.get_min_index(); - const int max_z = current_image_estimate.get_max_index(); - for (int z=min_z; z<=max_z; z++) - { - const int min_dz = max(weights.get_min_index(), min_z-z); - const int max_dz = min(weights.get_max_index(), max_z-z); - - const int min_y = current_image_estimate[z].get_min_index(); - const int max_y = current_image_estimate[z].get_max_index(); - - for (int y=min_y;y<= max_y;y++) - { - const int min_dy = max(weights[0].get_min_index(), min_y-y); - const int max_dy = min(weights[0].get_max_index(), max_y-y); - - const int min_x = current_image_estimate[z][y].get_min_index(); - const int max_x = current_image_estimate[z][y].get_max_index(); - - for (int x=min_x;x<= max_x;x++) - { - const int min_dx = max(weights[0][0].get_min_index(), min_x-x); - const int max_dx = min(weights[0][0].get_max_index(), max_x-x); - - /* formula: - sum_dx,dy,dz - weights[dz][dy][dx] * - (current_image_estimate[z][y][x] - current_image_estimate[z+dz][y+dy][x+dx]) * - (*kappa_ptr)[z][y][x] * (*kappa_ptr)[z+dz][y+dy][x+dx]; - */ + const int min_z = current_image_estimate.get_min_index(); + const int max_z = current_image_estimate.get_max_index(); + for (int z = min_z; z <= max_z; z++) { + const int min_dz = max(weights.get_min_index(), min_z - z); + const int max_dz = min(weights.get_max_index(), max_z - z); + + const int min_y = current_image_estimate[z].get_min_index(); + const int max_y = current_image_estimate[z].get_max_index(); + + for (int y = min_y; y <= max_y; y++) { + const int min_dy = max(weights[0].get_min_index(), min_y - y); + const int max_dy = min(weights[0].get_max_index(), max_y - y); + + const int min_x = current_image_estimate[z][y].get_min_index(); + const int max_x = current_image_estimate[z][y].get_max_index(); + + for (int x = min_x; x <= max_x; x++) { + const int min_dx = max(weights[0][0].get_min_index(), min_x - x); + const int max_dx = min(weights[0][0].get_max_index(), max_x - x); + + /* formula: + sum_dx,dy,dz + weights[dz][dy][dx] * + (current_image_estimate[z][y][x] - current_image_estimate[z+dz][y+dy][x+dx]) * + (*kappa_ptr)[z][y][x] * (*kappa_ptr)[z+dz][y+dy][x+dx]; + */ #if 1 - elemT gradient = 0; - for (int dz=min_dz;dz<=max_dz;++dz) - for (int dy=min_dy;dy<=max_dy;++dy) - for (int dx=min_dx;dx<=max_dx;++dx) - { - elemT current = - weights[dz][dy][dx] * - (current_image_estimate[z][y][x] - current_image_estimate[z+dz][y+dy][x+dx]); - - if (do_kappa) - current *= - (*kappa_ptr)[z][y][x] * (*kappa_ptr)[z+dz][y+dy][x+dx]; - - gradient += current; - } + elemT gradient = 0; + for (int dz = min_dz; dz <= max_dz; ++dz) + for (int dy = min_dy; dy <= max_dy; ++dy) + for (int dx = min_dx; dx <= max_dx; ++dx) { + elemT current = + weights[dz][dy][dx] * (current_image_estimate[z][y][x] - current_image_estimate[z + dz][y + dy][x + dx]); + + if (do_kappa) + current *= (*kappa_ptr)[z][y][x] * (*kappa_ptr)[z + dz][y + dy][x + dx]; + + gradient += current; + } #else - // attempt to speed up by precomputing the sum of weights. - // The current code gives identical results but is actually slower - // than the above, at least when kappas are present. - - - // precompute sum of weights - // TODO without kappas, this is just weights.sum() most of the time, - // but not near edges - float sum_of_weights = 0; - { - if (do_kappa) - { - for (int dz=min_dz;dz<=max_dz;++dz) - for (int dy=min_dy;dy<=max_dy;++dy) - for (int dx=min_dx;dx<=max_dx;++dx) - sum_of_weights += weights[dz][dy][dx]*(*kappa_ptr)[z+dz][y+dy][x+dx]; - } - else - { - for (int dz=min_dz;dz<=max_dz;++dz) - for (int dy=min_dy;dy<=max_dy;++dy) - for (int dx=min_dx;dx<=max_dx;++dx) - sum_of_weights += weights[dz][dy][dx]; - } - } - // now compute contribution of central term - elemT gradient = sum_of_weights * current_image_estimate[z][y][x] ; - - // subtract the rest - for (int dz=min_dz;dz<=max_dz;++dz) - for (int dy=min_dy;dy<=max_dy;++dy) - for (int dx=min_dx;dx<=max_dx;++dx) - { - elemT current = - weights[dz][dy][dx] * current_image_estimate[z+dz][y+dy][x+dx]; - - if (do_kappa) - current *= (*kappa_ptr)[z+dz][y+dy][x+dx]; - - gradient -= current; - } - // multiply with central kappa - if (do_kappa) - gradient *= (*kappa_ptr)[z][y][x]; -#endif - prior_gradient[z][y][x]= gradient * this->penalisation_factor; - } + // attempt to speed up by precomputing the sum of weights. + // The current code gives identical results but is actually slower + // than the above, at least when kappas are present. + + // precompute sum of weights + // TODO without kappas, this is just weights.sum() most of the time, + // but not near edges + float sum_of_weights = 0; + { + if (do_kappa) { + for (int dz = min_dz; dz <= max_dz; ++dz) + for (int dy = min_dy; dy <= max_dy; ++dy) + for (int dx = min_dx; dx <= max_dx; ++dx) + sum_of_weights += weights[dz][dy][dx] * (*kappa_ptr)[z + dz][y + dy][x + dx]; + } else { + for (int dz = min_dz; dz <= max_dz; ++dz) + for (int dy = min_dy; dy <= max_dy; ++dy) + for (int dx = min_dx; dx <= max_dx; ++dx) + sum_of_weights += weights[dz][dy][dx]; } + } + // now compute contribution of central term + elemT gradient = sum_of_weights * current_image_estimate[z][y][x]; + + // subtract the rest + for (int dz = min_dz; dz <= max_dz; ++dz) + for (int dy = min_dy; dy <= max_dy; ++dy) + for (int dx = min_dx; dx <= max_dx; ++dx) { + elemT current = weights[dz][dy][dx] * current_image_estimate[z + dz][y + dy][x + dx]; + + if (do_kappa) + current *= (*kappa_ptr)[z + dz][y + dy][x + dx]; + + gradient -= current; + } + // multiply with central kappa + if (do_kappa) + gradient *= (*kappa_ptr)[z][y][x]; +#endif + prior_gradient[z][y][x] = gradient * this->penalisation_factor; + } } + } info(boost::format("Prior gradient max %1%, min %2%\n") % prior_gradient.find_max() % prior_gradient.find_min()); static int count = 0; ++count; - if (gradient_filename_prefix.size()>0) - { - char *filename = new char[gradient_filename_prefix.size()+100]; - sprintf(filename, "%s%d.v", gradient_filename_prefix.c_str(), count); - write_to_file(filename, prior_gradient); - delete[] filename; - } + if (gradient_filename_prefix.size() > 0) { + char* filename = new char[gradient_filename_prefix.size() + 100]; + sprintf(filename, "%s%d.v", gradient_filename_prefix.c_str(), count); + write_to_file(filename, prior_gradient); + delete[] filename; + } } template -void -QuadraticPrior:: -compute_Hessian(DiscretisedDensity<3,elemT>& prior_Hessian_for_single_densel, - const BasicCoordinate<3,int>& coords, - const DiscretisedDensity<3,elemT> ¤t_image_estimate) -{ - assert( prior_Hessian_for_single_densel.has_same_characteristics(current_image_estimate)); +void +QuadraticPrior::compute_Hessian(DiscretisedDensity<3, elemT>& prior_Hessian_for_single_densel, + const BasicCoordinate<3, int>& coords, + const DiscretisedDensity<3, elemT>& current_image_estimate) { + assert(prior_Hessian_for_single_densel.has_same_characteristics(current_image_estimate)); prior_Hessian_for_single_densel.fill(0); - if (this->penalisation_factor==0) - { + if (this->penalisation_factor == 0) { return; } - + this->check(current_image_estimate); - - const DiscretisedDensityOnCartesianGrid<3,elemT>& current_image_cast = - dynamic_cast< const DiscretisedDensityOnCartesianGrid<3,elemT> &>(current_image_estimate); - - DiscretisedDensityOnCartesianGrid<3,elemT>& prior_Hessian_for_single_densel_cast = - dynamic_cast &>(prior_Hessian_for_single_densel); - - if (weights.get_length() ==0) - { + + const DiscretisedDensityOnCartesianGrid<3, elemT>& current_image_cast = + dynamic_cast&>(current_image_estimate); + + DiscretisedDensityOnCartesianGrid<3, elemT>& prior_Hessian_for_single_densel_cast = + dynamic_cast&>(prior_Hessian_for_single_densel); + + if (weights.get_length() == 0) { compute_weights(weights, current_image_cast.get_grid_spacing(), this->only_2D); } - - + const bool do_kappa = !is_null_ptr(kappa_ptr); - + if (do_kappa && kappa_ptr->has_same_characteristics(current_image_estimate)) error("QuadraticPrior: kappa image has not the same index range as the reconstructed image\n"); const int z = coords[1]; const int y = coords[2]; const int x = coords[3]; - const int min_dz = max(weights.get_min_index(), prior_Hessian_for_single_densel.get_min_index()-z); - const int max_dz = min(weights.get_max_index(), prior_Hessian_for_single_densel.get_max_index()-z); - - const int min_dy = max(weights[0].get_min_index(), prior_Hessian_for_single_densel[z].get_min_index()-y); - const int max_dy = min(weights[0].get_max_index(), prior_Hessian_for_single_densel[z].get_max_index()-y); - - const int min_dx = max(weights[0][0].get_min_index(), prior_Hessian_for_single_densel[z][y].get_min_index()-x); - const int max_dx = min(weights[0][0].get_max_index(), prior_Hessian_for_single_densel[z][y].get_max_index()-x); - + const int min_dz = max(weights.get_min_index(), prior_Hessian_for_single_densel.get_min_index() - z); + const int max_dz = min(weights.get_max_index(), prior_Hessian_for_single_densel.get_max_index() - z); + + const int min_dy = max(weights[0].get_min_index(), prior_Hessian_for_single_densel[z].get_min_index() - y); + const int max_dy = min(weights[0].get_max_index(), prior_Hessian_for_single_densel[z].get_max_index() - y); + + const int min_dx = max(weights[0][0].get_min_index(), prior_Hessian_for_single_densel[z][y].get_min_index() - x); + const int max_dx = min(weights[0][0].get_max_index(), prior_Hessian_for_single_densel[z][y].get_max_index() - x); + elemT diagonal = 0; - for (int dz=min_dz;dz<=max_dz;++dz) - for (int dy=min_dy;dy<=max_dy;++dy) - for (int dx=min_dx;dx<=max_dx;++dx) - { + for (int dz = min_dz; dz <= max_dz; ++dz) + for (int dy = min_dy; dy <= max_dy; ++dy) + for (int dx = min_dx; dx <= max_dx; ++dx) { // dz==0,dy==0,dx==0 will have weight 0, so we can just include it in the loop - elemT current = - weights[dz][dy][dx]; - + elemT current = weights[dz][dy][dx]; + if (do_kappa) - current *= - (*kappa_ptr)[z][y][x] * (*kappa_ptr)[z+dz][y+dy][x+dx]; - + current *= (*kappa_ptr)[z][y][x] * (*kappa_ptr)[z + dz][y + dy][x + dx]; + diagonal += current; - prior_Hessian_for_single_densel_cast[z+dz][y+dy][x+dx] = -current*this->penalisation_factor; + prior_Hessian_for_single_densel_cast[z + dz][y + dy][x + dx] = -current * this->penalisation_factor; } - - prior_Hessian_for_single_densel[z][y][x]= diagonal * this->penalisation_factor; -} + + prior_Hessian_for_single_densel[z][y][x] = diagonal * this->penalisation_factor; +} template -void -QuadraticPrior::parabolic_surrogate_curvature(DiscretisedDensity<3,elemT>& parabolic_surrogate_curvature, - const DiscretisedDensity<3,elemT> ¤t_image_estimate) -{ - - assert( parabolic_surrogate_curvature.has_same_characteristics(current_image_estimate)); - if (this->penalisation_factor==0) - { +void +QuadraticPrior::parabolic_surrogate_curvature(DiscretisedDensity<3, elemT>& parabolic_surrogate_curvature, + const DiscretisedDensity<3, elemT>& current_image_estimate) { + + assert(parabolic_surrogate_curvature.has_same_characteristics(current_image_estimate)); + if (this->penalisation_factor == 0) { parabolic_surrogate_curvature.fill(0); return; } - + this->check(current_image_estimate); - - const DiscretisedDensityOnCartesianGrid<3,elemT>& current_image_cast = - dynamic_cast< const DiscretisedDensityOnCartesianGrid<3,elemT> &>(current_image_estimate); - - if (weights.get_length() ==0) - { + + const DiscretisedDensityOnCartesianGrid<3, elemT>& current_image_cast = + dynamic_cast&>(current_image_estimate); + + if (weights.get_length() == 0) { compute_weights(weights, current_image_cast.get_grid_spacing(), this->only_2D); - } - + } + const bool do_kappa = !is_null_ptr(kappa_ptr); - + if (do_kappa && !kappa_ptr->has_same_characteristics(current_image_estimate)) error("QuadraticPrior: kappa image has not the same index range as the reconstructed image\n"); - const int min_z = current_image_estimate.get_min_index(); - const int max_z = current_image_estimate.get_max_index(); - for (int z=min_z; z<=max_z; z++) - { - const int min_dz = max(weights.get_min_index(), min_z-z); - const int max_dz = min(weights.get_max_index(), max_z-z); - - const int min_y = current_image_estimate[z].get_min_index(); - const int max_y = current_image_estimate[z].get_max_index(); - - for (int y=min_y;y<= max_y;y++) - { - const int min_dy = max(weights[0].get_min_index(), min_y-y); - const int max_dy = min(weights[0].get_max_index(), max_y-y); - - const int min_x = current_image_estimate[z][y].get_min_index(); - const int max_x = current_image_estimate[z][y].get_max_index(); - for (int x=min_x;x<= max_x;x++) - { - const int min_dx = max(weights[0][0].get_min_index(), min_x-x); - const int max_dx = min(weights[0][0].get_max_index(), max_x-x); - - elemT gradient = 0; - for (int dz=min_dz;dz<=max_dz;++dz) - for (int dy=min_dy;dy<=max_dy;++dy) - for (int dx=min_dx;dx<=max_dx;++dx) - { - // 1 comes from omega = psi'(t)/t = 2*t/2t =1 - elemT current = - weights[dz][dy][dx] *1; - - if (do_kappa) - current *= - (*kappa_ptr)[z][y][x] * (*kappa_ptr)[z+dz][y+dy][x+dx]; - - gradient += current; - } - - parabolic_surrogate_curvature[z][y][x]= gradient * this->penalisation_factor; - } - } + const int min_z = current_image_estimate.get_min_index(); + const int max_z = current_image_estimate.get_max_index(); + for (int z = min_z; z <= max_z; z++) { + const int min_dz = max(weights.get_min_index(), min_z - z); + const int max_dz = min(weights.get_max_index(), max_z - z); + + const int min_y = current_image_estimate[z].get_min_index(); + const int max_y = current_image_estimate[z].get_max_index(); + + for (int y = min_y; y <= max_y; y++) { + const int min_dy = max(weights[0].get_min_index(), min_y - y); + const int max_dy = min(weights[0].get_max_index(), max_y - y); + + const int min_x = current_image_estimate[z][y].get_min_index(); + const int max_x = current_image_estimate[z][y].get_max_index(); + for (int x = min_x; x <= max_x; x++) { + const int min_dx = max(weights[0][0].get_min_index(), min_x - x); + const int max_dx = min(weights[0][0].get_max_index(), max_x - x); + + elemT gradient = 0; + for (int dz = min_dz; dz <= max_dz; ++dz) + for (int dy = min_dy; dy <= max_dy; ++dy) + for (int dx = min_dx; dx <= max_dx; ++dx) { + // 1 comes from omega = psi'(t)/t = 2*t/2t =1 + elemT current = weights[dz][dy][dx] * 1; + + if (do_kappa) + current *= (*kappa_ptr)[z][y][x] * (*kappa_ptr)[z + dz][y + dy][x + dx]; + + gradient += current; + } + + parabolic_surrogate_curvature[z][y][x] = gradient * this->penalisation_factor; + } } + } - info(boost::format("parabolic_surrogate_curvature max %1%, min %2%\n") % parabolic_surrogate_curvature.find_max() % parabolic_surrogate_curvature.find_min()); + info(boost::format("parabolic_surrogate_curvature max %1%, min %2%\n") % parabolic_surrogate_curvature.find_max() % + parabolic_surrogate_curvature.find_min()); /*{ static int count = 0; ++count; @@ -584,97 +509,82 @@ QuadraticPrior::parabolic_surrogate_curvature(DiscretisedDensity<3,elemT> } template -Succeeded -QuadraticPrior:: -add_multiplication_with_approximate_Hessian(DiscretisedDensity<3,elemT>& output, - const DiscretisedDensity<3,elemT>& input) const -{ +Succeeded +QuadraticPrior::add_multiplication_with_approximate_Hessian(DiscretisedDensity<3, elemT>& output, + const DiscretisedDensity<3, elemT>& input) const { return accumulate_Hessian_times_input(output, input, input); } template Succeeded -QuadraticPrior:: -accumulate_Hessian_times_input(DiscretisedDensity<3,elemT>& output, - const DiscretisedDensity<3,elemT>& /*current_estimate*/, - const DiscretisedDensity<3,elemT>& input) const -{ +QuadraticPrior::accumulate_Hessian_times_input(DiscretisedDensity<3, elemT>& output, + const DiscretisedDensity<3, elemT>& /*current_estimate*/, + const DiscretisedDensity<3, elemT>& input) const { // TODO this function overlaps enormously with parabolic_surrogate_curvature // the only difference is that parabolic_surrogate_curvature uses input==1 - assert( output.has_same_characteristics(input)); - if (this->penalisation_factor==0) - { + assert(output.has_same_characteristics(input)); + if (this->penalisation_factor == 0) { return Succeeded::yes; } this->check(input); - - DiscretisedDensityOnCartesianGrid<3,elemT>& output_cast = - dynamic_cast &>(output); - if (weights.get_length() ==0) - { + DiscretisedDensityOnCartesianGrid<3, elemT>& output_cast = dynamic_cast&>(output); + + if (weights.get_length() == 0) { compute_weights(weights, output_cast.get_grid_spacing(), this->only_2D); - } - + } + const bool do_kappa = !is_null_ptr(kappa_ptr); - + if (do_kappa && !kappa_ptr->has_same_characteristics(input)) error("QuadraticPrior: kappa image has not the same index range as the reconstructed image\n"); - const int min_z = output.get_min_index(); - const int max_z = output.get_max_index(); - for (int z=min_z; z<=max_z; z++) - { - const int min_dz = max(weights.get_min_index(), min_z-z); - const int max_dz = min(weights.get_max_index(), max_z-z); - - const int min_y = output[z].get_min_index(); - const int max_y = output[z].get_max_index(); - - for (int y=min_y;y<= max_y;y++) - { - const int min_dy = max(weights[0].get_min_index(), min_y-y); - const int max_dy = min(weights[0].get_max_index(), max_y-y); - - const int min_x = output[z][y].get_min_index(); - const int max_x = output[z][y].get_max_index(); - - for (int x=min_x;x<= max_x;x++) - { - const int min_dx = max(weights[0][0].get_min_index(), min_x-x); - const int max_dx = min(weights[0][0].get_max_index(), max_x-x); - - elemT result = 0; - for (int dz=min_dz;dz<=max_dz;++dz) - for (int dy=min_dy;dy<=max_dy;++dy) - for (int dx=min_dx;dx<=max_dx;++dx) - { - elemT current = - weights[dz][dy][dx] * input[z+dz][y+dy][x+dx]; - - if (do_kappa) - current *= - (*kappa_ptr)[z][y][x] * (*kappa_ptr)[z+dz][y+dy][x+dx]; - result += current; - } - - output[z][y][x] += result * this->penalisation_factor; - } - } + const int min_z = output.get_min_index(); + const int max_z = output.get_max_index(); + for (int z = min_z; z <= max_z; z++) { + const int min_dz = max(weights.get_min_index(), min_z - z); + const int max_dz = min(weights.get_max_index(), max_z - z); + + const int min_y = output[z].get_min_index(); + const int max_y = output[z].get_max_index(); + + for (int y = min_y; y <= max_y; y++) { + const int min_dy = max(weights[0].get_min_index(), min_y - y); + const int max_dy = min(weights[0].get_max_index(), max_y - y); + + const int min_x = output[z][y].get_min_index(); + const int max_x = output[z][y].get_max_index(); + + for (int x = min_x; x <= max_x; x++) { + const int min_dx = max(weights[0][0].get_min_index(), min_x - x); + const int max_dx = min(weights[0][0].get_max_index(), max_x - x); + + elemT result = 0; + for (int dz = min_dz; dz <= max_dz; ++dz) + for (int dy = min_dy; dy <= max_dy; ++dy) + for (int dx = min_dx; dx <= max_dx; ++dx) { + elemT current = weights[dz][dy][dx] * input[z + dz][y + dy][x + dx]; + + if (do_kappa) + current *= (*kappa_ptr)[z][y][x] * (*kappa_ptr)[z + dz][y + dy][x + dx]; + result += current; + } + + output[z][y][x] += result * this->penalisation_factor; + } } + } return Succeeded::yes; } -# ifdef _MSC_VER -// prevent warning message on reinstantiation, +#ifdef _MSC_VER +// prevent warning message on reinstantiation, // note that we get a linking error if we don't have the explicit instantiation below -# pragma warning(disable:4660) -# endif - +# pragma warning(disable : 4660) +#endif template class QuadraticPrior; END_NAMESPACE_STIR - diff --git a/src/recon_buildblock/RayTraceVoxelsOnCartesianGrid.cxx b/src/recon_buildblock/RayTraceVoxelsOnCartesianGrid.cxx index 8d445e7612..f1633e78b5 100644 --- a/src/recon_buildblock/RayTraceVoxelsOnCartesianGrid.cxx +++ b/src/recon_buildblock/RayTraceVoxelsOnCartesianGrid.cxx @@ -21,7 +21,7 @@ \file \ingroup recon_buildblock - \brief Implementation of RayTraceVoxelsOnCartesianGrid + \brief Implementation of RayTraceVoxelsOnCartesianGrid \author Kris Thielemans \author Mustapha Sadki @@ -30,7 +30,7 @@ */ /* Modification history: - KT 30/05/2002 + KT 30/05/2002 start and stop point can now be arbitrarily located treatment of LORs parallel to planes is now scale independent (and checked with asserts) KT 18/05/2005 @@ -52,49 +52,39 @@ using std::max; START_NAMESPACE_STIR static inline bool -is_half_integer(const float a) -{ - return - fabs(floor(a)+.5F - a)<.0001F; +is_half_integer(const float a) { + return fabs(floor(a) + .5F - a) < .0001F; } -void -RayTraceVoxelsOnCartesianGrid - (ProjMatrixElemsForOneBin& lor, - const CartesianCoordinate3D& start_point, - const CartesianCoordinate3D& stop_point, - const CartesianCoordinate3D& voxel_size, - const float normalisation_constant) -{ - - const CartesianCoordinate3D difference = stop_point-start_point; - - if (norm(difference)<=.00001F) - { - // TODO - // not sure how to handle this case as we're normally ray tracing from voxel edges - warning("ray tracing with equal start and end point. Returning zero"); - return; - } +void +RayTraceVoxelsOnCartesianGrid(ProjMatrixElemsForOneBin& lor, const CartesianCoordinate3D& start_point, + const CartesianCoordinate3D& stop_point, const CartesianCoordinate3D& voxel_size, + const float normalisation_constant) { + + const CartesianCoordinate3D difference = stop_point - start_point; + + if (norm(difference) <= .00001F) { + // TODO + // not sure how to handle this case as we're normally ray tracing from voxel edges + warning("ray tracing with equal start and end point. Returning zero"); + return; + } // Find number of contributing elements. This will be used to // make sure there's enough space in the LOR to avoid reallocation. // This will make it faster, but also avoid over-allocation // (as most STL implementations double the allocated size at over-run). const int unsigned lor_size = - static_cast(ceil(fabs(difference.z())) + - ceil(fabs(difference.y())) + - ceil(fabs(difference.x()))) + 3; + static_cast(ceil(fabs(difference.z())) + ceil(fabs(difference.y())) + ceil(fabs(difference.x()))) + 3; // d12 is distance between the 2 points // it turns out we can multiply here with the normalisation_constant // (as that just scales the coordinate system) - const float d12 = - static_cast(norm(difference*voxel_size) * normalisation_constant); - - const int sign_x = difference.x()>=0 ? 1 : -1; - const int sign_y = difference.y()>=0 ? 1 : -1; - const int sign_z = difference.z()>=0 ? 1 : -1; + const float d12 = static_cast(norm(difference * voxel_size) * normalisation_constant); + + const int sign_x = difference.x() >= 0 ? 1 : -1; + const int sign_y = difference.y() >= 0 ? 1 : -1; + const int sign_z = difference.z() >= 0 ? 1 : -1; /* parametrise line in grid units as {z,y,x} = start_point + a difference/d12 @@ -104,52 +94,38 @@ RayTraceVoxelsOnCartesianGrid inc_x = d12*sign_x/difference.x() i.e. inc_x is always positive - Special treatment is necessary when the line is parallel to one of the - coordinate planes. This is determined by comparing difference with the + Special treatment is necessary when the line is parallel to one of the + coordinate planes. This is determined by comparing difference with the constant small_difference below. (Note that difference is in grid-units, so it has a natural scale of 1.) */ const float small_difference = 1.E-4F; - const bool zero_diff_in_x = fabs(difference.x())<=small_difference; - const bool zero_diff_in_y = fabs(difference.y())<=small_difference; - const bool zero_diff_in_z = fabs(difference.z())<=small_difference; + const bool zero_diff_in_x = fabs(difference.x()) <= small_difference; + const bool zero_diff_in_y = fabs(difference.y()) <= small_difference; + const bool zero_diff_in_z = fabs(difference.z()) <= small_difference; /* check if ray is in one of the planes between voxels. - If so, we will ray trace twice, i.e. to the 'left' and 'right', and store half + If so, we will ray trace twice, i.e. to the 'left' and 'right', and store half the value for each voxel. */ { - CartesianCoordinate3D inc(0,0,0); + CartesianCoordinate3D inc(0, 0, 0); // z - if (zero_diff_in_z && is_half_integer(start_point.z())) - { - inc = CartesianCoordinate3D (.5F,0,0); - } - else if (zero_diff_in_y && is_half_integer(start_point.y())) - { - inc = CartesianCoordinate3D (0,.5F,0); - } - else if (zero_diff_in_x && is_half_integer(start_point.x())) - { - inc = CartesianCoordinate3D (0,0,.5F); - } - if (norm(inc)>.1) - { - lor.reserve(lor.size() + 2*lor_size); - RayTraceVoxelsOnCartesianGrid(lor, - start_point - inc, - stop_point - inc, - voxel_size, - normalisation_constant/2); - - RayTraceVoxelsOnCartesianGrid(lor, - start_point + inc, - stop_point + inc, - voxel_size, - normalisation_constant/2); - lor.sort(); - return; - } + if (zero_diff_in_z && is_half_integer(start_point.z())) { + inc = CartesianCoordinate3D(.5F, 0, 0); + } else if (zero_diff_in_y && is_half_integer(start_point.y())) { + inc = CartesianCoordinate3D(0, .5F, 0); + } else if (zero_diff_in_x && is_half_integer(start_point.x())) { + inc = CartesianCoordinate3D(0, 0, .5F); + } + if (norm(inc) > .1) { + lor.reserve(lor.size() + 2 * lor_size); + RayTraceVoxelsOnCartesianGrid(lor, start_point - inc, stop_point - inc, voxel_size, normalisation_constant / 2); + + RayTraceVoxelsOnCartesianGrid(lor, start_point + inc, stop_point + inc, voxel_size, normalisation_constant / 2); + lor.sort(); + return; + } } // now start the normal case @@ -159,17 +135,17 @@ RayTraceVoxelsOnCartesianGrid lor.reserve(lor.size() + lor_size); - const float inc_x = zero_diff_in_x ? d12*1000000.F : d12 / fabs(difference.x()); - const float inc_y = zero_diff_in_y ? d12*1000000.F : d12 / fabs(difference.y()); - const float inc_z = zero_diff_in_z ? d12*1000000.F : d12 / fabs(difference.z()); - - // intersection points with intra-voxel planes : + const float inc_x = zero_diff_in_x ? d12 * 1000000.F : d12 / fabs(difference.x()); + const float inc_y = zero_diff_in_y ? d12 * 1000000.F : d12 / fabs(difference.y()); + const float inc_z = zero_diff_in_z ? d12 * 1000000.F : d12 / fabs(difference.z()); + + // intersection points with intra-voxel planes : // find voxel which contains the end_point, and go to its 'right' edge - const float xmax = round(stop_point.x()) + sign_x*0.5F; - const float ymax = round(stop_point.y()) + sign_y*0.5F; - const float zmax = round(stop_point.z()) + sign_z*0.5F; + const float xmax = round(stop_point.x()) + sign_x * 0.5F; + const float ymax = round(stop_point.y()) + sign_y * 0.5F; + const float zmax = round(stop_point.z()) + sign_z * 0.5F; - /* Find a?end for the last intersections with the coordinate planes. + /* Find a?end for the last intersections with the coordinate planes. amax will then be the smallest of all these a?end. If the LOR is parallel to a plane, take care that its a?end is larger than all the others. @@ -178,26 +154,26 @@ RayTraceVoxelsOnCartesianGrid In fact, we will take a?end slightly smaller than the actual last value (i.e. we multiply with a factor .9999). This is to avoid rounding errors in the loop below. In this loop, we try to detect the end of the LOR by comparing a (which is either ax,ay or az) with - aend. With exact arithmetic, a? would have been incremented exactly to - a?end_exact = a?start + (?max-?end)*inc_?*sign_?, + aend. With exact arithmetic, a? would have been incremented exactly to + a?end_exact = a?start + (?max-?end)*inc_?*sign_?, so we could loop until a==aend_exact. However, because of numerical precision, - a? might turn out be a tiny bit smaller then a?end_exact. So, we set aend a tiny bit + a? might turn out be a tiny bit smaller then a?end_exact. So, we set aend a tiny bit smaller than aend_exact. */ -const float axend = zero_diff_in_x ? d12*1000000.F : (xmax - start_point.x()) * inc_x * sign_x *.9999F; - const float ayend = zero_diff_in_y ? d12*1000000.F : (ymax - start_point.y()) * inc_y * sign_y *.9999F; - const float azend = zero_diff_in_z ? d12*1000000.F : (zmax - start_point.z()) * inc_z * sign_z *.9999F; - + const float axend = zero_diff_in_x ? d12 * 1000000.F : (xmax - start_point.x()) * inc_x * sign_x * .9999F; + const float ayend = zero_diff_in_y ? d12 * 1000000.F : (ymax - start_point.y()) * inc_y * sign_y * .9999F; + const float azend = zero_diff_in_z ? d12 * 1000000.F : (zmax - start_point.z()) * inc_z * sign_z * .9999F; + const float amax = min(axend, min(ayend, azend)); - + // just to be sure, check that axend was set large enough when difference.x() was small. - assert(fabs(difference.x())>small_difference || axend>amax); - assert(fabs(difference.y())>small_difference || ayend>amax); - assert(fabs(difference.z())>small_difference || azend>amax); + assert(fabs(difference.x()) > small_difference || axend > amax); + assert(fabs(difference.y()) > small_difference || ayend > amax); + assert(fabs(difference.z()) > small_difference || azend > amax); // coordinates of the first Voxel: CartesianCoordinate3D current_voxel = round(start_point); - + /* Find the a? values of the intersection points of the LOR with the planes between voxels at the 'left' side of the start_point.. This normally goes as follows: @@ -211,9 +187,9 @@ const float axend = zero_diff_in_x ? d12*1000000.F : (xmax - start_point.x()) * of numerical precision. Note on special handling of rays parallel to one of the planes: - + The corresponding a? value would be -infinity. We just set it to - a value low enough such that the start value of 'a' is not compromised + a value low enough such that the start value of 'a' is not compromised further on. Normally a? = (?min-start_point.?) * inc_? * sign_? @@ -223,56 +199,63 @@ const float axend = zero_diff_in_x ? d12*1000000.F : (xmax - start_point.x()) * -inc_? is a very low number. */ // with the previous xy-plane - float az = zero_diff_in_z ? -inc_z : - ((current_voxel.z() - start_point.z()) - sign_z*0.5F) * inc_z * sign_z; + float az = zero_diff_in_z ? -inc_z : ((current_voxel.z() - start_point.z()) - sign_z * 0.5F) * inc_z * sign_z; // with the previous yz-plane - float ax = zero_diff_in_x ? -inc_x : - ((current_voxel.x() - start_point.x()) - sign_x*0.5F) * inc_x * sign_x; + float ax = zero_diff_in_x ? -inc_x : ((current_voxel.x() - start_point.x()) - sign_x * 0.5F) * inc_x * sign_x; // with the previous xz-plane - float ay = zero_diff_in_y ? -inc_y : - ((current_voxel.y() - start_point.y()) - sign_y*0.5F) * inc_y * sign_y; - - // The biggest a? value gives the start of the a-row + float ay = zero_diff_in_y ? -inc_y : ((current_voxel.y() - start_point.y()) - sign_y * 0.5F) * inc_y * sign_y; + + // The biggest a? value gives the start of the a-row // Note that we should use a=0 if we want to start from start_point // (and not from the 'left' edge of the voxel containing start_point) - float a = max(ax, max(ay,az)); + float a = max(ax, max(ay, az)); // now go the intersections with next plane - if (zero_diff_in_x) ax = axend; else ax += inc_x; - if (zero_diff_in_y) ay = ayend; else ay += inc_y; - if (zero_diff_in_z) az = azend; else az += inc_z; - + if (zero_diff_in_x) + ax = axend; + else + ax += inc_x; + if (zero_diff_in_y) + ay = ayend; + else + ay += inc_y; + if (zero_diff_in_z) + az = azend; + else + az += inc_z; + // just to be sure, check that ax was set large enough when difference.x() was small. - assert(!zero_diff_in_x || ax>amax); - assert(!zero_diff_in_y || ay>amax); - assert(!zero_diff_in_z || az>amax); - - { - // go along the LOR - while ( a < amax) { - if ( ax < ay ) - if ( ax < az ) - { // LOR leaves voxel through yz-plane - lor.push_back(ProjMatrixElemsForOneBin::value_type(current_voxel,ax - a)); - a = ax;ax += inc_x; - current_voxel.x()+=sign_x; - } - else{ // LOR leaves voxel through xy-plane - lor.push_back(ProjMatrixElemsForOneBin::value_type(current_voxel,az - a)); - a = az ; az += inc_z; - current_voxel.z()+=sign_z; - } - else if ( ay < az) { // LOR leaves voxel through xz-plane - lor.push_back(ProjMatrixElemsForOneBin::value_type(current_voxel,ay - a)); - a = ay; ay += inc_y; - current_voxel.y()+=sign_y; - } - else {// LOR leaves voxel through xy-plane - lor.push_back(ProjMatrixElemsForOneBin::value_type(current_voxel,az - a )); - a = az; az += inc_z; - current_voxel.z()+=sign_z; + assert(!zero_diff_in_x || ax > amax); + assert(!zero_diff_in_y || ay > amax); + assert(!zero_diff_in_z || az > amax); + + { + // go along the LOR + while (a < amax) { + if (ax < ay) + if (ax < az) { // LOR leaves voxel through yz-plane + lor.push_back(ProjMatrixElemsForOneBin::value_type(current_voxel, ax - a)); + a = ax; + ax += inc_x; + current_voxel.x() += sign_x; + } else { // LOR leaves voxel through xy-plane + lor.push_back(ProjMatrixElemsForOneBin::value_type(current_voxel, az - a)); + a = az; + az += inc_z; + current_voxel.z() += sign_z; } - } // end of while (a -Reconstruction::Reconstruction() -{ +Reconstruction::Reconstruction() { this->set_defaults(); } - // parameters template -void -Reconstruction::set_defaults() -{ - this->_already_set_up=false; - this->output_filename_prefix=""; - this->output_file_format_ptr = - OutputFileFormat::default_sptr(); +void +Reconstruction::set_defaults() { + this->_already_set_up = false; + this->output_filename_prefix = ""; + this->output_file_format_ptr = OutputFileFormat::default_sptr(); this->post_filter_sptr.reset(); this->_disable_output = false; this->_verbosity = -1; - } template -void -Reconstruction::initialise_keymap() -{ +void +Reconstruction::initialise_keymap() { - this->parser.add_key("output filename prefix",&this->output_filename_prefix); + this->parser.add_key("output filename prefix", &this->output_filename_prefix); this->parser.add_parsing_key("output file format type", &this->output_file_format_ptr); - this->parser.add_parsing_key("post-filter type", &this->post_filter_sptr); + this->parser.add_parsing_key("post-filter type", &this->post_filter_sptr); this->parser.add_key("disable output", &_disable_output); this->parser.add_key("verbosity", &_verbosity); -// parser.add_key("END", &KeyParser::stop_parsing); - + // parser.add_key("END", &KeyParser::stop_parsing); } template -void -Reconstruction::initialise(const std::string& parameter_filename) -{ +void +Reconstruction::initialise(const std::string& parameter_filename) { _already_set_up = false; - if(parameter_filename.size()==0) - { + if (parameter_filename.size() == 0) { this->set_defaults(); this->ask_parameters(); } -else - { + else { this->set_defaults(); - if(!this->parse(parameter_filename.c_str())) - { + if (!this->parse(parameter_filename.c_str())) { error("Error parsing input file %s, exiting", parameter_filename.c_str()); } - } } - template -bool -Reconstruction:: -post_processing() -{ +bool +Reconstruction::post_processing() { - if ((this->_disable_output) & (this->get_registered_name ()=="KOSMAPOSL") ) - { warning("You have disabled the alpha coefficient output. Only emission image files will be written to " - "disk after or during reconstuction"); } + if ((this->_disable_output) & (this->get_registered_name() == "KOSMAPOSL")) { + warning("You have disabled the alpha coefficient output. Only emission image files will be written to " + "disk after or during reconstuction"); + } - else if (this->_disable_output) - { warning("You have disabled the output. No files will be written to " - "disk after or during reconstuction"); } + else if (this->_disable_output) { + warning("You have disabled the output. No files will be written to " + "disk after or during reconstuction"); + } - if (this->output_filename_prefix.length() == 0 && - !this->_disable_output)// KT 160899 changed name of variable - { warning("You need to specify an output prefix"); return true; } + if (this->output_filename_prefix.length() == 0 && !this->_disable_output) // KT 160899 changed name of variable + { + warning("You need to specify an output prefix"); + return true; + } - if (is_null_ptr(this->output_file_format_ptr)) - { warning("output file format has to be set to valid value"); return true; } + if (is_null_ptr(this->output_file_format_ptr)) { + warning("output file format has to be set to valid value"); + return true; + } if (_verbosity >= 0) - Verbosity::set(_verbosity); + Verbosity::set(_verbosity); return false; } template void -Reconstruction:: -set_output_filename_prefix(const std::string& arg) -{ - this->output_filename_prefix = arg; +Reconstruction::set_output_filename_prefix(const std::string& arg) { + this->output_filename_prefix = arg; } template void -Reconstruction:: -set_output_file_format_ptr(const shared_ptr >& arg) -{ - this->output_file_format_ptr = arg; +Reconstruction::set_output_file_format_ptr(const shared_ptr>& arg) { + this->output_file_format_ptr = arg; } template void -Reconstruction:: -set_post_processor_sptr(const shared_ptr > & arg) -{ +Reconstruction::set_post_processor_sptr(const shared_ptr>& arg) { _already_set_up = false; - this->post_filter_sptr = arg; + this->post_filter_sptr = arg; } - + template Succeeded -Reconstruction:: -set_up(shared_ptr const& target_data_sptr_v) -{ +Reconstruction::set_up(shared_ptr const& target_data_sptr_v) { _already_set_up = true; this->target_data_sptr = target_data_sptr_v; - if(!is_null_ptr(this->post_filter_sptr)) - { + if (!is_null_ptr(this->post_filter_sptr)) { info("Building post filter kernel"); - - if (this->post_filter_sptr->set_up(*target_data_sptr) - == Succeeded::no) - { - warning("Error building post filter"); - return Succeeded::no; - } + + if (this->post_filter_sptr->set_up(*target_data_sptr) == Succeeded::no) { + warning("Error building post filter"); + return Succeeded::no; + } } return Succeeded::yes; } template void -Reconstruction:: -check(TargetT const& target_data) const -{ +Reconstruction::check(TargetT const& target_data) const { if (!this->_already_set_up) error("Reconstruction method called without calling set_up first."); - if (! this->target_data_sptr->has_same_characteristics(target_data)) + if (!this->target_data_sptr->has_same_characteristics(target_data)) error("Reconstruction set-up with different geometry for target."); } template void -Reconstruction:: -set_disable_output(bool _val) -{ - this->_disable_output = _val; +Reconstruction::set_disable_output(bool _val) { + this->_disable_output = _val; } template void -Reconstruction:: -set_enable_output(bool _val) -{ - this->_disable_output = _val; +Reconstruction::set_enable_output(bool _val) { + this->_disable_output = _val; } -template < typename TargetT> -shared_ptr -Reconstruction:: -get_target_image() -{ - return target_data_sptr; +template +shared_ptr +Reconstruction::get_target_image() { + return target_data_sptr; } -template class Reconstruction >; -template class Reconstruction; +template class Reconstruction>; +template class Reconstruction; END_NAMESPACE_STIR - diff --git a/src/recon_buildblock/RegisteredObject.cxx b/src/recon_buildblock/RegisteredObject.cxx index b7cc2fffbf..b9e2aaacf6 100644 --- a/src/recon_buildblock/RegisteredObject.cxx +++ b/src/recon_buildblock/RegisteredObject.cxx @@ -32,45 +32,43 @@ #ifdef __STIR_REGISTRY_NOT_INLINE -#pragma message("instantiating RegisteredObject >") -#include "stir/recon_buildblock/GeneralisedPrior.h" +# pragma message("instantiating RegisteredObject >") +# include "stir/recon_buildblock/GeneralisedPrior.h" -#pragma message("instantiating RegisteredObject") -#include "stir/recon_buildblock/ProjMatrixByBin.h" +# pragma message("instantiating RegisteredObject") +# include "stir/recon_buildblock/ProjMatrixByBin.h" -#pragma message("instantiating RegisteredObject") -#include "stir/recon_buildblock/ProjectorByBinPair.h" +# pragma message("instantiating RegisteredObject") +# include "stir/recon_buildblock/ProjectorByBinPair.h" -#pragma message("instantiating RegisteredObject") -#include "stir/recon_buildblock/ForwardProjectorByBin.h" +# pragma message("instantiating RegisteredObject") +# include "stir/recon_buildblock/ForwardProjectorByBin.h" -#pragma message("instantiating RegisteredObject") -#include "stir/recon_buildblock/BackProjectorByBin.h" +# pragma message("instantiating RegisteredObject") +# include "stir/recon_buildblock/BackProjectorByBin.h" -#pragma message("instantiating RegisteredObject") -#include "stir/recon_buildblock/BinNormalisation.h" +# pragma message("instantiating RegisteredObject") +# include "stir/recon_buildblock/BinNormalisation.h" // and others START_NAMESPACE_STIR template -RegisteredObject::RegistryType& -RegisteredObject::registry () -{ +RegisteredObject::RegistryType& +RegisteredObject::registry() { static RegistryType the_registry("None", 0); return the_registry; } # ifdef _MSC_VER -// prevent warning message on reinstantiation, +// prevent warning message on reinstantiation, // note that we get a linking error if we don't have the explicit instantiation below -# pragma warning(disable:4660) +# pragma warning(disable : 4660) # endif +template RegisteredObject>; -template RegisteredObject >; - -template RegisteredObject; +template RegisteredObject; template RegisteredObject; diff --git a/src/recon_buildblock/RelativeDifferencePrior.cxx b/src/recon_buildblock/RelativeDifferencePrior.cxx index dd6454205d..eb1df317e2 100644 --- a/src/recon_buildblock/RelativeDifferencePrior.cxx +++ b/src/recon_buildblock/RelativeDifferencePrior.cxx @@ -21,7 +21,7 @@ \file \ingroup priors \brief implementation of the stir::RelativeDifferencePrior class - + \author Kris Thielemans \author Sanida Mustafovic \author Robert Twyman @@ -46,12 +46,11 @@ using std::max; START_NAMESPACE_STIR template -void -RelativeDifferencePrior::initialise_keymap() -{ +void +RelativeDifferencePrior::initialise_keymap() { base_type::initialise_keymap(); this->parser.add_start_key("Relative Difference Prior Parameters"); - this->parser.add_key("only 2D", &only_2D); + this->parser.add_key("only 2D", &only_2D); this->parser.add_key("kappa filename", &kappa_filename); this->parser.add_key("weights", &weights); this->parser.add_key("gradient filename prefix", &gradient_filename_prefix); @@ -61,391 +60,347 @@ RelativeDifferencePrior::initialise_keymap() } template -bool -RelativeDifferencePrior::post_processing() -{ - if (base_type::post_processing()==true) +bool +RelativeDifferencePrior::post_processing() { + if (base_type::post_processing() == true) return true; if (kappa_filename.size() != 0) - this->kappa_ptr = read_from_file >(kappa_filename); + this->kappa_ptr = read_from_file>(kappa_filename); bool warn_about_even_size = false; - if (this->weights.size() ==0) - { - // will call compute_weights() to fill it in + if (this->weights.size() == 0) { + // will call compute_weights() to fill it in + } else { + if (!this->weights.is_regular()) { + warning("Sorry. RelativeDifferencePrior currently only supports regular arrays for the weights"); + return true; } - else - { - if (!this->weights.is_regular()) - { - warning("Sorry. RelativeDifferencePrior currently only supports regular arrays for the weights"); - return true; - } - const unsigned int size_z = this->weights.size(); - if (size_z%2==0) + const unsigned int size_z = this->weights.size(); + if (size_z % 2 == 0) + warn_about_even_size = true; + const int min_index_z = -static_cast(size_z / 2); + this->weights.set_min_index(min_index_z); + + for (int z = min_index_z; z <= this->weights.get_max_index(); ++z) { + const unsigned int size_y = this->weights[z].size(); + if (size_y % 2 == 0) warn_about_even_size = true; - const int min_index_z = -static_cast(size_z/2); - this->weights.set_min_index(min_index_z); - - for (int z = min_index_z; z<= this->weights.get_max_index(); ++z) - { - const unsigned int size_y = this->weights[z].size(); - if (size_y%2==0) - warn_about_even_size = true; - const int min_index_y = -static_cast(size_y/2); - this->weights[z].set_min_index(min_index_y); - for (int y = min_index_y; y<= this->weights[z].get_max_index(); ++y) - { - const unsigned int size_x = this->weights[z][y].size(); - if (size_x%2==0) - warn_about_even_size = true; - const int min_index_x = -static_cast(size_x/2); - this->weights[z][y].set_min_index(min_index_x); - } - } + const int min_index_y = -static_cast(size_y / 2); + this->weights[z].set_min_index(min_index_y); + for (int y = min_index_y; y <= this->weights[z].get_max_index(); ++y) { + const unsigned int size_x = this->weights[z][y].size(); + if (size_x % 2 == 0) + warn_about_even_size = true; + const int min_index_x = -static_cast(size_x / 2); + this->weights[z][y].set_min_index(min_index_x); + } } + } if (warn_about_even_size) warning("Parsing RelativeDifferencePrior: even number of weights occured in either x,y or z dimension.\n" "I'll (effectively) make this odd by appending a 0 at the end."); return false; - } template Succeeded -RelativeDifferencePrior::set_up (shared_ptr > const& target_sptr) -{ +RelativeDifferencePrior::set_up(shared_ptr> const& target_sptr) { base_type::set_up(target_sptr); return Succeeded::yes; } template -void RelativeDifferencePrior::check(DiscretisedDensity<3,elemT> const& current_image_estimate) const -{ +void +RelativeDifferencePrior::check(DiscretisedDensity<3, elemT> const& current_image_estimate) const { // Do base-class check base_type::check(current_image_estimate); } template void -RelativeDifferencePrior::set_defaults() -{ +RelativeDifferencePrior::set_defaults() { base_type::set_defaults(); this->only_2D = false; - this->kappa_ptr.reset(); + this->kappa_ptr.reset(); this->weights.recycle(); this->gamma = 2; this->epsilon = 0.0; } template <> -const char * const -RelativeDifferencePrior::registered_name = - "Relative Difference Prior"; +const char* const RelativeDifferencePrior::registered_name = "Relative Difference Prior"; template -RelativeDifferencePrior::RelativeDifferencePrior() -{ +RelativeDifferencePrior::RelativeDifferencePrior() { set_defaults(); } // Return the value of gamma - a RDP parameter template float -RelativeDifferencePrior:: -get_gamma() const -{ return this->gamma; } +RelativeDifferencePrior::get_gamma() const { + return this->gamma; +} // Set the value of gamma - a RDP parameter template void -RelativeDifferencePrior:: -set_gamma(float g) -{ this->gamma = g; } +RelativeDifferencePrior::set_gamma(float g) { + this->gamma = g; +} // Return the value of epsilon - a RDP parameter template float -RelativeDifferencePrior:: -get_epsilon() const -{ return this->epsilon; } +RelativeDifferencePrior::get_epsilon() const { + return this->epsilon; +} // Set the value of epsilon - a RDP parameter template void -RelativeDifferencePrior:: -set_epsilon(float g) -{ this->epsilon = g; } - +RelativeDifferencePrior::set_epsilon(float g) { + this->epsilon = g; +} template -RelativeDifferencePrior::RelativeDifferencePrior(const bool only_2D_v, float penalisation_factor_v, float gamma_v, float epsilon_v) - : only_2D(only_2D_v) -{ +RelativeDifferencePrior::RelativeDifferencePrior(const bool only_2D_v, float penalisation_factor_v, float gamma_v, + float epsilon_v) + : only_2D(only_2D_v) { this->penalisation_factor = penalisation_factor_v; this->gamma = gamma_v; this->epsilon = epsilon_v; } - - //! get penalty weights for the neighbourhood +//! get penalty weights for the neighbourhood template -Array<3,float> -RelativeDifferencePrior:: -get_weights() const -{ return this->weights; } +Array<3, float> +RelativeDifferencePrior::get_weights() const { + return this->weights; +} - //! set penalty weights for the neighbourhood -template -void -RelativeDifferencePrior:: -set_weights(const Array<3,float>& w) -{ this->weights = w; } - - //! get current kappa image - /*! \warning As this function returns a shared_ptr, this is dangerous. You should not - modify the image by manipulating the image referred to by this pointer. - Unpredictable results will occur. - */ +//! set penalty weights for the neighbourhood template -shared_ptr > -RelativeDifferencePrior:: -get_kappa_sptr() const -{ return this->kappa_ptr; } +void +RelativeDifferencePrior::set_weights(const Array<3, float>& w) { + this->weights = w; +} - //! set kappa image +//! get current kappa image +/*! \warning As this function returns a shared_ptr, this is dangerous. You should not + modify the image by manipulating the image referred to by this pointer. + Unpredictable results will occur. +*/ template -void -RelativeDifferencePrior:: -set_kappa_sptr(const shared_ptr >& k) -{ this->kappa_ptr = k; } +shared_ptr> +RelativeDifferencePrior::get_kappa_sptr() const { + return this->kappa_ptr; +} +//! set kappa image +template +void +RelativeDifferencePrior::set_kappa_sptr(const shared_ptr>& k) { + this->kappa_ptr = k; +} // TODO move to set_up // initialise to 1/Euclidean distance -static void -compute_weights(Array<3,float>& weights, const CartesianCoordinate3D& grid_spacing, const bool only_2D) -{ +static void +compute_weights(Array<3, float>& weights, const CartesianCoordinate3D& grid_spacing, const bool only_2D) { int min_dz, max_dz; - if (only_2D) - { - min_dz = max_dz = 0; - } - else - { - min_dz = -1; - max_dz = 1; - } - weights = Array<3,float>(IndexRange3D(min_dz,max_dz,-1,1,-1,1)); - for (int z=min_dz;z<=max_dz;++z) - for (int y=-1;y<=1;++y) - for (int x=-1;x<=1;++x) - { - if (z==0 && y==0 && x==0) - weights[0][0][0] = 0; - else - { - weights[z][y][x] = - grid_spacing.x()/ - sqrt(square(x*grid_spacing.x())+ - square(y*grid_spacing.y())+ - square(z*grid_spacing.z())); - } + if (only_2D) { + min_dz = max_dz = 0; + } else { + min_dz = -1; + max_dz = 1; + } + weights = Array<3, float>(IndexRange3D(min_dz, max_dz, -1, 1, -1, 1)); + for (int z = min_dz; z <= max_dz; ++z) + for (int y = -1; y <= 1; ++y) + for (int x = -1; x <= 1; ++x) { + if (z == 0 && y == 0 && x == 0) + weights[0][0][0] = 0; + else { + weights[z][y][x] = + grid_spacing.x() / sqrt(square(x * grid_spacing.x()) + square(y * grid_spacing.y()) + square(z * grid_spacing.z())); } + } } template double -RelativeDifferencePrior:: -compute_value(const DiscretisedDensity<3,elemT> ¤t_image_estimate) -{ - if (this->penalisation_factor==0) - { +RelativeDifferencePrior::compute_value(const DiscretisedDensity<3, elemT>& current_image_estimate) { + if (this->penalisation_factor == 0) { return 0.; } - + this->check(current_image_estimate); - - const DiscretisedDensityOnCartesianGrid<3,elemT>& current_image_cast = - dynamic_cast< const DiscretisedDensityOnCartesianGrid<3,elemT> &>(current_image_estimate); - - if (this->weights.get_length() ==0) - { + + const DiscretisedDensityOnCartesianGrid<3, elemT>& current_image_cast = + dynamic_cast&>(current_image_estimate); + + if (this->weights.get_length() == 0) { compute_weights(this->weights, current_image_cast.get_grid_spacing(), this->only_2D); } - + const bool do_kappa = !is_null_ptr(kappa_ptr); - + if (do_kappa && !kappa_ptr->has_same_characteristics(current_image_estimate)) error("RelativeDifferencePrior: kappa image has not the same index range as the reconstructed image\n"); - double result = 0.; - const int min_z = current_image_estimate.get_min_index(); - const int max_z = current_image_estimate.get_max_index(); - for (int z=min_z; z<=max_z; z++) - { - const int min_dz = max(weights.get_min_index(), min_z-z); - const int max_dz = min(weights.get_max_index(), max_z-z); - - const int min_y = current_image_estimate[z].get_min_index(); - const int max_y = current_image_estimate[z].get_max_index(); - - for (int y=min_y;y<= max_y;y++) - { - const int min_dy = max(weights[0].get_min_index(), min_y-y); - const int max_dy = min(weights[0].get_max_index(), max_y-y); - - const int min_x = current_image_estimate[z][y].get_min_index(); - const int max_x = current_image_estimate[z][y].get_max_index(); - - for (int x=min_x;x<= max_x;x++) - { - const int min_dx = max(weights[0][0].get_min_index(), min_x-x); - const int max_dx = min(weights[0][0].get_max_index(), max_x-x); - - for (int dz=min_dz;dz<=max_dz;++dz) - for (int dy=min_dy;dy<=max_dy;++dy) - for (int dx=min_dx;dx<=max_dx;++dx) - { - elemT current; - if (this->epsilon ==0.0 && current_image_estimate[z][y][x] == 0.0 && current_image_estimate[z+dz][y+dy][x+dx] == 0.0){ - // handle the undefined nature of the function - current = 0.0; - } else { - current = weights[dz][dy][dx] * 0.5 * - (pow(current_image_estimate[z][y][x]-current_image_estimate[z+dz][y+dy][x+dx],2)/ - (current_image_estimate[z][y][x]+current_image_estimate[z+dz][y+dy][x+dx] - + this->gamma * abs(current_image_estimate[z][y][x]-current_image_estimate[z+dz][y+dy][x+dx]) + this->epsilon )); - } - if (do_kappa) - current *= - (*kappa_ptr)[z][y][x] * (*kappa_ptr)[z+dz][y+dy][x+dx]; - - result += static_cast(current); - } - } - } + const int min_z = current_image_estimate.get_min_index(); + const int max_z = current_image_estimate.get_max_index(); + for (int z = min_z; z <= max_z; z++) { + const int min_dz = max(weights.get_min_index(), min_z - z); + const int max_dz = min(weights.get_max_index(), max_z - z); + + const int min_y = current_image_estimate[z].get_min_index(); + const int max_y = current_image_estimate[z].get_max_index(); + + for (int y = min_y; y <= max_y; y++) { + const int min_dy = max(weights[0].get_min_index(), min_y - y); + const int max_dy = min(weights[0].get_max_index(), max_y - y); + + const int min_x = current_image_estimate[z][y].get_min_index(); + const int max_x = current_image_estimate[z][y].get_max_index(); + + for (int x = min_x; x <= max_x; x++) { + const int min_dx = max(weights[0][0].get_min_index(), min_x - x); + const int max_dx = min(weights[0][0].get_max_index(), max_x - x); + + for (int dz = min_dz; dz <= max_dz; ++dz) + for (int dy = min_dy; dy <= max_dy; ++dy) + for (int dx = min_dx; dx <= max_dx; ++dx) { + elemT current; + if (this->epsilon == 0.0 && current_image_estimate[z][y][x] == 0.0 && + current_image_estimate[z + dz][y + dy][x + dx] == 0.0) { + // handle the undefined nature of the function + current = 0.0; + } else { + current = weights[dz][dy][dx] * 0.5 * + (pow(current_image_estimate[z][y][x] - current_image_estimate[z + dz][y + dy][x + dx], 2) / + (current_image_estimate[z][y][x] + current_image_estimate[z + dz][y + dy][x + dx] + + this->gamma * abs(current_image_estimate[z][y][x] - current_image_estimate[z + dz][y + dy][x + dx]) + + this->epsilon)); + } + if (do_kappa) + current *= (*kappa_ptr)[z][y][x] * (*kappa_ptr)[z + dz][y + dy][x + dx]; + + result += static_cast(current); + } + } } + } return result * this->penalisation_factor; } template -void -RelativeDifferencePrior:: -compute_gradient(DiscretisedDensity<3,elemT>& prior_gradient, - const DiscretisedDensity<3,elemT> ¤t_image_estimate) -{ - assert( prior_gradient.has_same_characteristics(current_image_estimate)); - if (this->penalisation_factor==0) - { +void +RelativeDifferencePrior::compute_gradient(DiscretisedDensity<3, elemT>& prior_gradient, + const DiscretisedDensity<3, elemT>& current_image_estimate) { + assert(prior_gradient.has_same_characteristics(current_image_estimate)); + if (this->penalisation_factor == 0) { prior_gradient.fill(0); return; } this->check(current_image_estimate); - - - const DiscretisedDensityOnCartesianGrid<3,elemT>& current_image_cast = - dynamic_cast< const DiscretisedDensityOnCartesianGrid<3,elemT> &>(current_image_estimate); - - if (this->weights.get_length() ==0) - { + + const DiscretisedDensityOnCartesianGrid<3, elemT>& current_image_cast = + dynamic_cast&>(current_image_estimate); + + if (this->weights.get_length() == 0) { compute_weights(this->weights, current_image_cast.get_grid_spacing(), this->only_2D); } - - + const bool do_kappa = !is_null_ptr(kappa_ptr); if (do_kappa && !kappa_ptr->has_same_characteristics(current_image_estimate)) error("RelativeDifferencePrior: kappa image has not the same index range as the reconstructed image\n"); - const int min_z = current_image_estimate.get_min_index(); - const int max_z = current_image_estimate.get_max_index(); - for (int z=min_z; z<=max_z; z++) - { - const int min_dz = max(weights.get_min_index(), min_z-z); - const int max_dz = min(weights.get_max_index(), max_z-z); - - const int min_y = current_image_estimate[z].get_min_index(); - const int max_y = current_image_estimate[z].get_max_index(); - - for (int y=min_y;y<= max_y;y++) - { - const int min_dy = max(weights[0].get_min_index(), min_y-y); - const int max_dy = min(weights[0].get_max_index(), max_y-y); - - const int min_x = current_image_estimate[z][y].get_min_index(); - const int max_x = current_image_estimate[z][y].get_max_index(); - - for (int x=min_x;x<= max_x;x++) - { - const int min_dx = max(weights[0][0].get_min_index(), min_x-x); - const int max_dx = min(weights[0][0].get_max_index(), max_x-x); - - elemT gradient = 0; - for (int dz=min_dz;dz<=max_dz;++dz) - for (int dy=min_dy;dy<=max_dy;++dy) - for (int dx=min_dx;dx<=max_dx;++dx) - { - - elemT current; - if (this->epsilon ==0.0 && current_image_estimate[z][y][x] == 0.0 && current_image_estimate[z+dz][y+dy][x+dx] == 0.0){ - // handle the undefined nature of the gradient - current = 0.0; - } else { - current = weights[dz][dy][dx] * - (((current_image_estimate[z][y][x] - current_image_estimate[z+dz][y+dy][x+dx]) * - (this->gamma * abs(current_image_estimate[z][y][x] - current_image_estimate[z+dz][y+dy][x+dx]) + - current_image_estimate[z][y][x] + 3 * current_image_estimate[z+dz][y+dy][x+dx] + 2 * this->epsilon))/ - (square((current_image_estimate[z][y][x] + current_image_estimate[z+dz][y+dy][x+dx]) + - this->gamma * abs(current_image_estimate[z][y][x] - current_image_estimate[z+dz][y+dy][x+dx]) + this->epsilon))); - } - if (do_kappa) - current *= (*kappa_ptr)[z][y][x] * (*kappa_ptr)[z+dz][y+dy][x+dx]; - - gradient += current; - } - - prior_gradient[z][y][x]= gradient * this->penalisation_factor; - } - } + const int min_z = current_image_estimate.get_min_index(); + const int max_z = current_image_estimate.get_max_index(); + for (int z = min_z; z <= max_z; z++) { + const int min_dz = max(weights.get_min_index(), min_z - z); + const int max_dz = min(weights.get_max_index(), max_z - z); + + const int min_y = current_image_estimate[z].get_min_index(); + const int max_y = current_image_estimate[z].get_max_index(); + + for (int y = min_y; y <= max_y; y++) { + const int min_dy = max(weights[0].get_min_index(), min_y - y); + const int max_dy = min(weights[0].get_max_index(), max_y - y); + + const int min_x = current_image_estimate[z][y].get_min_index(); + const int max_x = current_image_estimate[z][y].get_max_index(); + + for (int x = min_x; x <= max_x; x++) { + const int min_dx = max(weights[0][0].get_min_index(), min_x - x); + const int max_dx = min(weights[0][0].get_max_index(), max_x - x); + + elemT gradient = 0; + for (int dz = min_dz; dz <= max_dz; ++dz) + for (int dy = min_dy; dy <= max_dy; ++dy) + for (int dx = min_dx; dx <= max_dx; ++dx) { + + elemT current; + if (this->epsilon == 0.0 && current_image_estimate[z][y][x] == 0.0 && + current_image_estimate[z + dz][y + dy][x + dx] == 0.0) { + // handle the undefined nature of the gradient + current = 0.0; + } else { + current = + weights[dz][dy][dx] * + (((current_image_estimate[z][y][x] - current_image_estimate[z + dz][y + dy][x + dx]) * + (this->gamma * abs(current_image_estimate[z][y][x] - current_image_estimate[z + dz][y + dy][x + dx]) + + current_image_estimate[z][y][x] + 3 * current_image_estimate[z + dz][y + dy][x + dx] + + 2 * this->epsilon)) / + (square((current_image_estimate[z][y][x] + current_image_estimate[z + dz][y + dy][x + dx]) + + this->gamma * abs(current_image_estimate[z][y][x] - current_image_estimate[z + dz][y + dy][x + dx]) + + this->epsilon))); + } + if (do_kappa) + current *= (*kappa_ptr)[z][y][x] * (*kappa_ptr)[z + dz][y + dy][x + dx]; + + gradient += current; + } + + prior_gradient[z][y][x] = gradient * this->penalisation_factor; + } } + } info(boost::format("Prior gradient max %1%, min %2%\n") % prior_gradient.find_max() % prior_gradient.find_min()); static int count = 0; ++count; - if (gradient_filename_prefix.size()>0) - { - char *filename = new char[gradient_filename_prefix.size()+100]; - sprintf(filename, "%s%d.v", gradient_filename_prefix.c_str(), count); - write_to_file(filename, prior_gradient); - delete[] filename; - } + if (gradient_filename_prefix.size() > 0) { + char* filename = new char[gradient_filename_prefix.size() + 100]; + sprintf(filename, "%s%d.v", gradient_filename_prefix.c_str(), count); + write_to_file(filename, prior_gradient); + delete[] filename; + } } template -Succeeded -RelativeDifferencePrior:: -add_multiplication_with_approximate_Hessian(DiscretisedDensity<3,elemT>& output, - const DiscretisedDensity<3,elemT>& input) const -{ - error("add_multiplication_with_approximate_Hessian() is not implemented in Relative Difference Prior."); +Succeeded +RelativeDifferencePrior::add_multiplication_with_approximate_Hessian(DiscretisedDensity<3, elemT>& output, + const DiscretisedDensity<3, elemT>& input) const { + error("add_multiplication_with_approximate_Hessian() is not implemented in Relative Difference Prior."); return Succeeded::no; } -# ifdef _MSC_VER -// prevent warning message on reinstantiation, +#ifdef _MSC_VER +// prevent warning message on reinstantiation, // note that we get a linking error if we don't have the explicit instantiation below -# pragma warning(disable:4660) -# endif - +# pragma warning(disable : 4660) +#endif template class RelativeDifferencePrior; END_NAMESPACE_STIR - diff --git a/src/recon_buildblock/SPECTUB_Tools.cxx b/src/recon_buildblock/SPECTUB_Tools.cxx index b3a1a42fef..eab6924624 100644 --- a/src/recon_buildblock/SPECTUB_Tools.cxx +++ b/src/recon_buildblock/SPECTUB_Tools.cxx @@ -1,5 +1,5 @@ /* - Copyright (c) 2013, Biomedical Image Group (GIB), Universitat de Barcelona, Barcelona, Spain. + Copyright (c) 2013, Biomedical Image Group (GIB), Universitat de Barcelona, Barcelona, Spain. Copyright (c) 2013, University College London This file is part of STIR. @@ -18,7 +18,7 @@ \author Carles Falcon */ -//system libraries +// system libraries #include #include #include @@ -30,444 +30,457 @@ #include using namespace std; -//using std::string; +// using std::string; //... user defined libraries ....................................... #include "stir/recon_buildblock/SPECTUB_Tools.h" #include "stir/recon_buildblock/SPECTUB_Weight3d.h" - namespace SPECTUB { -#define NUMARG 29 +#define NUMARG 29 #define EPSILON 1e-12 #define EOS '\0' -#define maxim(a,b) ((a)>=(b)?(a):(b)) -#define minim(a,b) ((a)<=(b)?(a):(b)) -#define abs(a) ((a)>=0?(a):(-a)) -#define SIGN(a) (a<-EPSILON?-1:(a>EPSILON?1:0)) +#define maxim(a, b) ((a) >= (b) ? (a) : (b)) +#define minim(a, b) ((a) <= (b) ? (a) : (b)) +#define abs(a) ((a) >= 0 ? (a) : (-a)) +#define SIGN(a) (a < -EPSILON ? -1 : (a > EPSILON ? 1 : 0)) -#define DELIMITER1 '#' //delimiter character in input parameter text file -#define DELIMITER2 '%' //delimiter character in input parameter text file +#define DELIMITER1 '#' // delimiter character in input parameter text file +#define DELIMITER2 '%' // delimiter character in input parameter text file //... global variables .............................................. extern wm_da_type wm; -extern wmh_type wmh; -extern float * Rrad; - +extern wmh_type wmh; +extern float* Rrad; //============================================================================= //=== write_wm_FC ============================================================= //============================================================================= -void write_wm_FC() -{ - FILE *fid; - - int ia_acum = 0; - - if ( (fid = fopen( wm.OSfn.c_str(), "wb" ) ) == NULL ) error_wmtools_SPECT( 31, wm.OSfn ); - - fwrite ( &(wm.NbOS), sizeof(int), 1, fid); // to write number of rows of wm (NbOS) - fwrite ( &(wm.Nvox), sizeof(int), 1, fid); // to write number of columns of wm (Nvox) - - //... number of non-zero elements in the weight matrix ....... - - int ne = 0; - for ( int j=0 ; j < wm.NbOS ; j++ ) ne += wm.ne[j]; +void +write_wm_FC() { + FILE* fid; - fwrite ( &ne, sizeof(int), 1, fid); // to write number of non-zeros element in the weight matrix - - //... to write the array of weights (along rows) .............. - - for ( int i = 0 ; i < wm.NbOS ; i++ ){ - for (int j = 0 ; j < wm.ne[i] ; j++ ){ - fwrite ( &wm.val[ i ][ j ], sizeof(float), 1, fid); - } - } - - //... to write the column index of each weight (volume index of the voxel the weight is associated to) .... - - for ( int i = 0 ; i < wm.NbOS ; i++ ){ - for ( int j = 0 ; j < wm.ne[ i ] ; j++ ){ - fwrite ( &wm.col[ i ][ j ] ,sizeof(int) ,1 , fid); - } - } + int ia_acum = 0; - //... to write the indexs of the array of weights where a change of row happens ......... - - for ( int i = 0 ; i < wm.NbOS ; i++ ){ - fwrite ( &ia_acum, sizeof(int), 1, fid); - ia_acum += wm.ne[i]; - } + if ((fid = fopen(wm.OSfn.c_str(), "wb")) == NULL) + error_wmtools_SPECT(31, wm.OSfn); - //... to write the total number of saved weights .......................... - - fwrite ( &ia_acum, sizeof(int), 1, fid); - - cout << "number of non-zero elemnts: " << ia_acum << endl; + fwrite(&(wm.NbOS), sizeof(int), 1, fid); // to write number of rows of wm (NbOS) + fwrite(&(wm.Nvox), sizeof(int), 1, fid); // to write number of columns of wm (Nvox) + + //... number of non-zero elements in the weight matrix ....... + + int ne = 0; + for (int j = 0; j < wm.NbOS; j++) + ne += wm.ne[j]; + + fwrite(&ne, sizeof(int), 1, fid); // to write number of non-zeros element in the weight matrix + + //... to write the array of weights (along rows) .............. - fclose (fid); + for (int i = 0; i < wm.NbOS; i++) { + for (int j = 0; j < wm.ne[i]; j++) { + fwrite(&wm.val[i][j], sizeof(float), 1, fid); + } + } + + //... to write the column index of each weight (volume index of the voxel the weight is associated to) .... + + for (int i = 0; i < wm.NbOS; i++) { + for (int j = 0; j < wm.ne[i]; j++) { + fwrite(&wm.col[i][j], sizeof(int), 1, fid); + } + } + + //... to write the indexs of the array of weights where a change of row happens ......... + + for (int i = 0; i < wm.NbOS; i++) { + fwrite(&ia_acum, sizeof(int), 1, fid); + ia_acum += wm.ne[i]; + } + + //... to write the total number of saved weights .......................... + + fwrite(&ia_acum, sizeof(int), 1, fid); + + cout << "number of non-zero elemnts: " << ia_acum << endl; + + fclose(fid); } //============================================================================= //=== write_wm_hdr ============================================================ //============================================================================= -void write_wm_hdr() -{ - ofstream stream1( wm.fn_hdr.c_str() ); - if( !stream1 ) error_wmtools_SPECT( 31, wm.fn_hdr ); - - //....... image and projections characteristics......... - - stream1 << "Header for the matrix: " << wm.fn << endl; - stream1 << "number of columns: " << wmh.vol.Ncol << endl; - stream1 << "number of rows: " << wmh.vol.Nrow << endl; - stream1 << "number of slices: " << wmh.vol.Nsli << endl; - stream1 << "voxel size (cm): " << wmh.vol.szcm << endl; - stream1 << "slice thickness (cm): " << wmh.vol.thcm << endl; - - stream1 << "number of bins per line: " << wmh.prj.Nbin << endl; - stream1 << "bin size (cm): " << wmh.prj.szcm << endl; - stream1 << "number of angles: " << wmh.prj.Nang << endl; - stream1 << "first angle (deg): " << wmh.prj.ang0 << endl; - stream1 << "angle increment between consecutive projections (deg): " << wmh.prj.incr << endl; - - stream1 << "first slice to reconstruct : " << wmh.vol.first_sl << endl; - stream1 << "last slice to reconstruct : " << wmh.vol.last_sl << endl; - stream1 << "number of subsets in which to split the matrix: " << wmh.prj.NOS << endl; - stream1 << "number of angles per subsets: " << wmh.prj.NangOS << endl; - - stream1 << "minimum weight (geometrical contribution): " << wmh.min_w << endl; - stream1 << "psf resolution (discretization interval for Gaussian): " << wmh.psfres << endl; - stream1 << "maximum number of sigmas in psf calculation: " << wmh.maxsigm << endl; - - //........ rotation radius................................ - - if ( wmh.fixed_Rrad ) stream1 << "fixed rotation radius :" << wmh.Rrad[ 0 ] << " cm" << endl; - else stream1 << "variable rotation radius from :" << wmh.Rrad_fn << endl; - - //......... psf and collimator parameters ................. +void +write_wm_hdr() { + ofstream stream1(wm.fn_hdr.c_str()); + if (!stream1) + error_wmtools_SPECT(31, wm.fn_hdr); + + //....... image and projections characteristics......... + + stream1 << "Header for the matrix: " << wm.fn << endl; + stream1 << "number of columns: " << wmh.vol.Ncol << endl; + stream1 << "number of rows: " << wmh.vol.Nrow << endl; + stream1 << "number of slices: " << wmh.vol.Nsli << endl; + stream1 << "voxel size (cm): " << wmh.vol.szcm << endl; + stream1 << "slice thickness (cm): " << wmh.vol.thcm << endl; + + stream1 << "number of bins per line: " << wmh.prj.Nbin << endl; + stream1 << "bin size (cm): " << wmh.prj.szcm << endl; + stream1 << "number of angles: " << wmh.prj.Nang << endl; + stream1 << "first angle (deg): " << wmh.prj.ang0 << endl; + stream1 << "angle increment between consecutive projections (deg): " << wmh.prj.incr << endl; + + stream1 << "first slice to reconstruct : " << wmh.vol.first_sl << endl; + stream1 << "last slice to reconstruct : " << wmh.vol.last_sl << endl; + stream1 << "number of subsets in which to split the matrix: " << wmh.prj.NOS << endl; + stream1 << "number of angles per subsets: " << wmh.prj.NangOS << endl; + + stream1 << "minimum weight (geometrical contribution): " << wmh.min_w << endl; + stream1 << "psf resolution (discretization interval for Gaussian): " << wmh.psfres << endl; + stream1 << "maximum number of sigmas in psf calculation: " << wmh.maxsigm << endl; + + //........ rotation radius................................ + + if (wmh.fixed_Rrad) + stream1 << "fixed rotation radius :" << wmh.Rrad[0] << " cm" << endl; + else + stream1 << "variable rotation radius from :" << wmh.Rrad_fn << endl; + + //......... psf and collimator parameters ................. + + stream1 << "psf correction: " << wmh.do_psf << endl; + if (wmh.do_psf) { + if (wmh.do_psf_3d) + stream1 << "\tmode: 3d " << endl; + else + stream1 << "\tmode: 2d " << endl; + if (wmh.predef_col) + stream1 << "\tpredefined collimator number: " << wmh.COL.num << endl; + else + stream1 << "\tcollimator parameters from: " << wmh.col_fn << endl; + + if (wmh.COL.do_fb) + stream1 << "collimator geometry: fanbeam " << endl; + else + stream1 << "collimator geometry: parallel" << endl; + } else { + if (wmh.COL.num == 0) + stream1 << "collimator geometry: parallel " << endl; + else + stream1 << "collimator geometry: fanbeam with focal distance : " << wmh.COL.F << endl; + } - - stream1 << "psf correction: " << wmh.do_psf << endl; - if ( wmh.do_psf ){ - if ( wmh.do_psf_3d ) stream1 << "\tmode: 3d " << endl; - else stream1 << "\tmode: 2d " << endl; - if ( wmh.predef_col ) stream1 << "\tpredefined collimator number: " << wmh.COL.num << endl; - else stream1 << "\tcollimator parameters from: " << wmh.col_fn << endl; - - if ( wmh.COL.do_fb ) stream1 << "collimator geometry: fanbeam " << endl; - else stream1 << "collimator geometry: parallel" << endl; - } - else{ - if ( wmh.COL.num == 0 ) stream1 << "collimator geometry: parallel " << endl; - else stream1 << "collimator geometry: fanbeam with focal distance : " << wmh.COL.F << endl; - } - - stream1 << "attenuation correction: " << wmh.do_att << endl; - if ( wmh.do_att ){ - if ( wmh.do_full_att ) stream1 << "\tmode: full " << endl; - else stream1 << "\tmode: simple " << endl; - } - stream1 << "\tattenuation map: " << wmh.att_fn << endl; - - //......... masking .................................... - - stream1 << "masking: " << wmh.do_msk << endl; - if ( wmh.do_msk ){ - if ( wmh.do_msk_cyl ) stream1 << "\tmask type: cyl" << endl; - if ( wmh.do_msk_att ) stream1 << "\tmask type: att" << endl; - if ( wmh.do_msk_file ){ - stream1 << "\tmask type: file" << endl; - stream1 << "\tmask file name: " << wmh.msk_fn << endl; - } - if ( wmh.do_msk_slc ){ - stream1 << "first slice: " << wmh.vol.first_sl << endl; - stream1 << "last slice: " << wmh.vol.last_sl << endl; - } - - } - stream1.close(); + stream1 << "attenuation correction: " << wmh.do_att << endl; + if (wmh.do_att) { + if (wmh.do_full_att) + stream1 << "\tmode: full " << endl; + else + stream1 << "\tmode: simple " << endl; + } + stream1 << "\tattenuation map: " << wmh.att_fn << endl; + + //......... masking .................................... + + stream1 << "masking: " << wmh.do_msk << endl; + if (wmh.do_msk) { + if (wmh.do_msk_cyl) + stream1 << "\tmask type: cyl" << endl; + if (wmh.do_msk_att) + stream1 << "\tmask type: att" << endl; + if (wmh.do_msk_file) { + stream1 << "\tmask type: file" << endl; + stream1 << "\tmask file name: " << wmh.msk_fn << endl; + } + if (wmh.do_msk_slc) { + stream1 << "first slice: " << wmh.vol.first_sl << endl; + stream1 << "last slice: " << wmh.vol.last_sl << endl; + } + } + stream1.close(); } //============================================================================= //=== write_wm_STIR =========================================================== //============================================================================= -void write_wm_STIR() -{ - int seg_num = 0; // segment number for STIR matrix (always zero) - FILE *fid; - - if ( ( fid = fopen( wm.OSfn.c_str() , "wb" )) == NULL ) error_wmtools_SPECT( 31, wm.OSfn ); - - //...loop for matrix elements: projection index .................. - - for( int j = 0 ; j < wm.NbOS ; j++ ){ - - //... to write projection indices and number of elements ....... - - fwrite( &seg_num, sizeof(int), 1, fid); - fwrite( &wm.na [ j ], sizeof(int), 1, fid); - fwrite( &wm.ns [ j ], sizeof(int), 1, fid); - fwrite( &wm.nb [ j ], sizeof(int), 1, fid); - fwrite( &wm.ne [ j ], sizeof(int), 1, fid); - - //... loop for matrix elements: image indexs.................. - - for ( int i = 0 ; i < wm.ne[ j ] ; i++ ){ - - fwrite( &wm.nz[ wm.col[ j ][ i ] ], sizeof(short int), 1, fid); - fwrite( &wm.ny[ wm.col[ j ][ i ] ], sizeof(short int), 1, fid); - fwrite( &wm.nx[ wm.col[ j ][ i ] ], sizeof(short int), 1, fid); - fwrite( &wm.val[ j ][ i ], sizeof(float),1,fid); - } - } - fclose( fid ); +void +write_wm_STIR() { + int seg_num = 0; // segment number for STIR matrix (always zero) + FILE* fid; + + if ((fid = fopen(wm.OSfn.c_str(), "wb")) == NULL) + error_wmtools_SPECT(31, wm.OSfn); + + //...loop for matrix elements: projection index .................. + + for (int j = 0; j < wm.NbOS; j++) { + + //... to write projection indices and number of elements ....... + + fwrite(&seg_num, sizeof(int), 1, fid); + fwrite(&wm.na[j], sizeof(int), 1, fid); + fwrite(&wm.ns[j], sizeof(int), 1, fid); + fwrite(&wm.nb[j], sizeof(int), 1, fid); + fwrite(&wm.ne[j], sizeof(int), 1, fid); + + //... loop for matrix elements: image indexs.................. + + for (int i = 0; i < wm.ne[j]; i++) { + + fwrite(&wm.nz[wm.col[j][i]], sizeof(short int), 1, fid); + fwrite(&wm.ny[wm.col[j][i]], sizeof(short int), 1, fid); + fwrite(&wm.nx[wm.col[j][i]], sizeof(short int), 1, fid); + fwrite(&wm.val[j][i], sizeof(float), 1, fid); + } + } + fclose(fid); } //============================================================================= //=== index_calc ============================================================== //============================================================================= -void index_calc ( int *indexs ) -{ - if ( wmh.prj.NOS == 1 ){ - for ( int i = 0 ; i < wmh.prj.Nang ; i++ ){ - indexs[ i ] = i; // when one single matrix, sequential order - } - } - else{ - int j, *ple, *iOS, *a, *sa, *dif ; - - iOS = new int [ wmh.prj.NOS ]; - ple = new int [ wmh.prj.NOS ]; - a = new int [ wmh.prj.NOS ]; - sa = new int [ wmh.prj.NOS ]; - dif = new int [ wmh.prj.NOS + 1 ]; - - //... to initialize variables .................................. - - for ( int i = 0 ; i < wmh.prj.NOS ; i++ ){ - iOS[ i ] = ple[ i ] = a[ i ] = sa[ i ] = dif[ i ] = 0; - } - dif[ wmh.prj.NOS ] = 0; - ple[ 0 ] = 1; - - //... to fill differences vector ................................. - - int OS2 = wmh.prj.NOS * wmh.prj.NOS ; - - for ( int i = 1 ; i <= wmh.prj.NOS ; i++ ){ - dif[ i ] += 2 * ( i * ( i - wmh.prj.NOS ) ) + OS2 ; - } - - //... first angle for each subset: angle having a maximum distance with all precedent angles ... - - int im = 0; // first index is always set to zero - - for ( int k = 1 ; k < wmh.prj.NOS ; k++ ){ - - for ( int i = 1 ; i < wmh.prj.NOS ; i++ ){ - if( !ple[ i ] ){ - j = i - im; - a[ i ] = dif[ abs( j ) ]; - } - } - - for( int i = 0 ; i < wmh.prj.NOS ; i++ ){ - a[ i ] *= ( 1 - ple[ i ] ); - sa[ i ] *= ( 1 - ple[ i ] ); - sa[ i ] += a[ i ]; - } - - int m = 0; - int n = 0; - - for ( int i = 0 ; i < wmh.prj.NOS ; i++ ){ - m = maxim( m, sa[ i ] ); - } - - for( int i = 1 ; i < wmh.prj.NOS ; i++ ){ - if( !ple[ i ] ){ - m = minim( m, sa[ i ] ); - } - } - - for ( int i = 1 ; i < wmh.prj.NOS ; i++ ){ - if( sa[ i ] == m ){ - n = maxim( n, a[i] ); - } - } - for ( int i = wmh.prj.NOS - 1 ; i > 0 ; i-- ){ - if( sa[ i ] == m ){ - if( a[ i ] <= n ){ - n = a[ i ]; - im = i; - } - } - } - iOS[ k ] = im; - ple[ im ] = 1; - } - - //... to fill the rest of angles of each subset ................ - - for( int i = 0 ; i < wmh.prj.NOS ; i++ ){ - - for( int j = 0 ; j < wmh.prj.NangOS ; j++ ){ - - indexs[ i * wmh.prj.NangOS + j ] = iOS[ i ] + wmh.prj.NOS * j; - } - } - - delete [] iOS; - delete [] a; - delete [] sa; - delete [] dif; - delete [] ple; - } - +void +index_calc(int* indexs) { + if (wmh.prj.NOS == 1) { + for (int i = 0; i < wmh.prj.Nang; i++) { + indexs[i] = i; // when one single matrix, sequential order + } + } else { + int j, *ple, *iOS, *a, *sa, *dif; + + iOS = new int[wmh.prj.NOS]; + ple = new int[wmh.prj.NOS]; + a = new int[wmh.prj.NOS]; + sa = new int[wmh.prj.NOS]; + dif = new int[wmh.prj.NOS + 1]; + + //... to initialize variables .................................. + + for (int i = 0; i < wmh.prj.NOS; i++) { + iOS[i] = ple[i] = a[i] = sa[i] = dif[i] = 0; + } + dif[wmh.prj.NOS] = 0; + ple[0] = 1; + + //... to fill differences vector ................................. + + int OS2 = wmh.prj.NOS * wmh.prj.NOS; + + for (int i = 1; i <= wmh.prj.NOS; i++) { + dif[i] += 2 * (i * (i - wmh.prj.NOS)) + OS2; + } + + //... first angle for each subset: angle having a maximum distance with all precedent angles ... + + int im = 0; // first index is always set to zero + + for (int k = 1; k < wmh.prj.NOS; k++) { + + for (int i = 1; i < wmh.prj.NOS; i++) { + if (!ple[i]) { + j = i - im; + a[i] = dif[abs(j)]; + } + } + + for (int i = 0; i < wmh.prj.NOS; i++) { + a[i] *= (1 - ple[i]); + sa[i] *= (1 - ple[i]); + sa[i] += a[i]; + } + + int m = 0; + int n = 0; + + for (int i = 0; i < wmh.prj.NOS; i++) { + m = maxim(m, sa[i]); + } + + for (int i = 1; i < wmh.prj.NOS; i++) { + if (!ple[i]) { + m = minim(m, sa[i]); + } + } + + for (int i = 1; i < wmh.prj.NOS; i++) { + if (sa[i] == m) { + n = maxim(n, a[i]); + } + } + for (int i = wmh.prj.NOS - 1; i > 0; i--) { + if (sa[i] == m) { + if (a[i] <= n) { + n = a[i]; + im = i; + } + } + } + iOS[k] = im; + ple[im] = 1; + } + + //... to fill the rest of angles of each subset ................ + + for (int i = 0; i < wmh.prj.NOS; i++) { + + for (int j = 0; j < wmh.prj.NangOS; j++) { + + indexs[i * wmh.prj.NangOS + j] = iOS[i] + wmh.prj.NOS * j; + } + } + + delete[] iOS; + delete[] a; + delete[] sa; + delete[] dif; + delete[] ple; + } } //============================================================================= //=== read rotation radius ================================================== //============================================================================= -void read_Rrad() -{ - string line; - ifstream stream1( wmh.Rrad_fn.c_str() ); - if( !stream1 ) error_wmtools_SPECT( 114, wmh.Rrad_fn ); - - int i = 0; - - while ( !stream1.eof() ){ - getline ( stream1, line ); - Rrad[ i ] = atof ( line.c_str() ); - i++; - } - - if ( i != wmh.prj.Nang ) error_wmtools_SPECT( 11, wmh.Rrad_fn ); - stream1.close(); - - return; +void +read_Rrad() { + string line; + ifstream stream1(wmh.Rrad_fn.c_str()); + if (!stream1) + error_wmtools_SPECT(114, wmh.Rrad_fn); + + int i = 0; + + while (!stream1.eof()) { + getline(stream1, line); + Rrad[i] = atof(line.c_str()); + i++; + } + + if (i != wmh.prj.Nang) + error_wmtools_SPECT(11, wmh.Rrad_fn); + stream1.close(); + + return; } //============================================================================= //=== col params ============================================================== //============================================================================= -//void col_params( collim_type *COL ) +// void col_params( collim_type *COL ) //{ -// cout << "Using collimator: " << COL->num << endl; -// +// cout << "Using collimator: " << COL->num << endl; +// // switch(COL->num){ -// +// // case 1: //...................fanbeam: ELSCINT // COL->F = (float)35.5; // COL->L = (float)4.; -// COL->A_h = (float)0.3369; -// COL->A_v = (float)0.3369; +// COL->A_h = (float)0.3369; +// COL->A_v = (float)0.3369; // COL->D = (float)0.8; // COL->w = (float)0.0866; // COL->insgm = (float)0.17; // COL->do_fb = true; // break; -// +// // case 2: //....................fanbeam: ELSCINT D=0 // COL->F = (float)35.5; // COL->L = (float)4.; -// COL->A_h = (float)0.3369; -// COL->A_v = (float)0.3369; +// COL->A_h = (float)0.3369; +// COL->A_v = (float)0.3369; // COL->D = (float)0.; // COL->w = (float)0.0866; // COL->insgm = (float)0.17; // COL->do_fb = true; // break; -// +// // case 3: //....................parallel 3: low resolution // COL->A = (float)0.0275; // COL->B = (float)0.2; // COL->do_fb = false; // break; -// +// // case 4: //....................parallel 4: high resolution // COL->A = (float)0.0172; // COL->B = (float)0.2; // COL->do_fb = false; // break; -// +// // case 5: //....................parallel 5: (ECAM) // COL->A = (float)0.0167; // COL->B = (float)0.1405; // COL->do_fb = false; // break; -// +// // case 6: //....................fan_beam: prism3000 // COL->F = (float)65.0; // COL->L = (float)2.7; -// COL->A_h = (float)0.3575; -// COL->A_v = (float)0.3360; +// COL->A_h = (float)0.3575; +// COL->A_v = (float)0.3360; // COL->D = (float)0.0; // COL->w = (float)0.0866; // COL->insgm = (float)0.17; // COL->do_fb = true; // break; -// +// // case 10: //...................parallel: ECAM with L=40 mm // COL->A = (float)0.0101; // COL->B = (float)0.0998; // COL->do_fb = false; // break; -// -// case 11: //...................fan beam ELSCINT L=2,405 cm +// +// case 11: //...................fan beam ELSCINT L=2,405 cm // COL->F = (float)35.5; // COL->L = (float)2.405; -// COL->A_h = (float)0.3369; -// COL->A_v = (float)0.3369; +// COL->A_h = (float)0.3369; +// COL->A_v = (float)0.3369; // COL->D = (float)0.8; // COL->w = (float)0.0866; // COL->insgm = (float)0.17; // COL->do_fb = true; -// break; -// +// break; +// // case 13: //...................parallel: Hammamatsu collimator // COL->A = (float)0.0205; // COL->B = (float)0.10245; // COL->do_fb = false; // break; -// +// // case 14: //...................parallel. hexagonal holes. apotema=0.57mm, L=24mm, s=0.125mm -// COL->A = (float)0.0178; +// COL->A = (float)0.0178; // COL->B = (float)0.0886; // COL->do_fb = false; // break; -// +// // case 15: //...................parallel. hexagonal holes apotema=0.57mm, L=24mm, s=0.125mm -// COL->A = (float)0.0247; +// COL->A = (float)0.0247; // COL->B = (float)0.0752; // COL->do_fb = false; // break; -// +// // case 16: //...................parallel: Sentinella S102 colimador: experimental parameters -// COL->A = (float)0.0166; +// COL->A = (float)0.0166; // COL->B = (float)0.0924; // COL->do_fb = false; // break; -// +// // case 17: //...................parallel Infinia Hawkeye: experimental parametres -// COL->A = (float)0.0163; +// COL->A = (float)0.0163; // COL->B = (float)0.1466; // COL->do_fb = false; // break; -// +// // default: // char p[3]; // auxiliar variable for itoa // error_wmtools_SPECT( 21, itoa(COL->num,p)); @@ -478,14 +491,14 @@ void read_Rrad() //=== read collimator params ================================================== //============================================================================= -//void read_col_params( collim_type *COL ) +// void read_col_params( collim_type *COL ) //{ // string line; // ifstream stream1( wmh.col_fn.c_str() ); -// if( !stream1 ) error_wmtools_SPECT( 122, wmh.col_fn ); -// +// if( !stream1 ) error_wmtools_SPECT( 122, wmh.col_fn ); +// // getline ( stream1, line ); -// +// // if ( line[ 0 ] == 'f' ) COL->do_fb = true; // else{ // if ( line[ 0 ] == 'p' ) COL->do_fb = false; @@ -493,426 +506,429 @@ void read_Rrad() // } // // if ( COL->do_fb ){ -// +// // getline ( stream1, line ); // COL->F = atof( line.c_str() ); // Focal length (cm) -// +// // getline ( stream1, line ); // COL->L = atof( line.c_str() ); // collimator to detector distance (? cm) -// +// // getline ( stream1, line ); // COL->A_h = atof( line.c_str() ); // linear factor for dependency of sigma on distance (fanbeam horizontal) -// +// // getline ( stream1, line ); // COL->A_v = atof( line.c_str() ); // linear factor for dependency of sigma on distance (fanbeam vertical) -// +// // getline ( stream1, line ); // COL->D = atof( line.c_str() ); // (?) -// +// // getline ( stream1, line ); // COL->w = atof( line.c_str() ); // collimator thickness (? cm) -// +// // if( !stream1.eof() ) error_wmtools_SPECT( 13, wmh.col_fn ); -// +// // getline ( stream1, line ); // COL->insgm = atof( line.c_str() ); // intrinsic sigma (cristal resolution cm?) -// +// // } // else{ // getline ( stream1, line ); -// COL->A = atof( line.c_str() ); // linear factor for dependency of sigma on distance (parallel): sigma=A*dist+B -// +// COL->A = atof( line.c_str() ); // linear factor for dependency of sigma on distance (parallel): +// sigma=A*dist+B +// // if( !stream1.eof() ) error_wmtools_SPECT( 13, wmh.col_fn ); -// +// // getline ( stream1, line ); -// COL->B = atof( line.c_str() ); // Independent factor for dependency of sigma on distance: sigma=A*dist+B -// } -// +// COL->B = atof( line.c_str() ); // Independent factor for dependency of sigma on distance: sigma=A*dist+B +// } +// // stream1.close(); // return; //} - //========================================================================== //=== calc_sigma_v ========================================================= //========================================================================== -float calc_sigma_v( voxel_type vox, collim_type COL) -{ - float sigma; - if ( COL.do_fb ){ - float xc = (float)2. * COL.A_v * COL.w * ( vox.dv2dp + COL.L + COL.D ) / COL.L; - sigma = sqrt( COL.insgm * COL.insgm + xc * xc ); - - } - else sigma = COL.A * vox.dv2dp + COL.B ; - - return( sigma ); -} +float +calc_sigma_v(voxel_type vox, collim_type COL) { + float sigma; + if (COL.do_fb) { + float xc = (float)2. * COL.A_v * COL.w * (vox.dv2dp + COL.L + COL.D) / COL.L; + sigma = sqrt(COL.insgm * COL.insgm + xc * xc); + } else + sigma = COL.A * vox.dv2dp + COL.B; + + return (sigma); +} //============================================================================= //=== fill_ang ================================================================ //============================================================================= -void fill_ang ( angle_type *ang ) -{ - float DX = (float) 0.5 / wmh.psfres ; - float dg2rd = boost::math::constants::pi() / (float)180. ; - - for ( int i = 0; i < wmh.prj.Nang ; i++ ){ - - //... ratios calculation ....................................................... - - float deg = wmh.prj.ang0 + (float)i * wmh.prj.incr ; // angle in degrees - ang[ i ].cos = cos( deg * dg2rd ); // cosinus of the angle - ang[ i ].sin = sin( deg * dg2rd ); // sinus of the angle - - //... first octave (0->45degrees) equivalent angle and its trigonometric ratios ....... - - float angR = fabs( deg ); - int quad = (int) floor( angR / (float)90. ); // quadrant - - angR = fabs( angR - (float)90. * (float)quad ); // reduced angle: equivalent angle in 0->45degrees interval - if ( angR > (float)45. ) angR = fabs( (float)90. - angR ); - - float sinR = (float)sin( angR * dg2rd ); // sinus of the reduced angle - float cosR = (float)cos( angR * dg2rd ); // cosinus of the reduced angle - - //... parametres of the oblique projection of a square voxel size 1 (half a trapezoid) ....... - - if ( !wmh.do_psf ){ - - if ( angR < EPSILON ){ - - ang[ i ].p = (float)1. ; - ang[ i ].N1 = ang[ i ].N2 = (int) floor( DX ); - ang[ i ].m = ang[ i ].n = (float)0.; - } - else{ - ang[ i ].p = (float)1. / cosR; // plateau highness - ang[ i ].m = -wmh.psfres / ( sinR * cosR ); // slope of the trapezoid in DX units (negative) - ang[ i ].n = ( cosR + sinR ) * (float)0.5 / ( cosR * sinR ); // independent term of the slope of the trapezoid (cm) - ang[ i ].N1 = (int) floor( (float) fabs( cosR - sinR ) * DX ); // index of the first vertice (end of plateau) in res units - ang[ i ].N2 = (int) floor( ( cosR + sinR ) * DX ); // index of the second vertice (end of the slope) in res units - } - - ang[ i ].vxprj.lngd2 = ang[ i ].N2; - ang[ i ].vxprj.lng = 2 * ang[ i ].N2; - ang[ i ].vxprj.res = wmh.psfres; - } - //... rotation radius ................................................................ - - ang[ i ].Rrad = Rrad[ i ]; // assignation of (variable) rotation radius - - //... coordinates of the first bin of each projection and increments for consecutive bins .... - - if(wmh.do_att){ - - ang[ i ].incx = wmh.prj.szcm * ang[ i ].cos; - ang[ i ].incy = wmh.prj.szcm * ang[ i ].sin; - - ang[ i ].xbin0 = -ang[ i ].Rrad * ang[ i ].sin - wmh.prj.lngcmd2 * ang[ i ].cos ; - ang[ i ].ybin0 = ang[ i ].Rrad * ang[ i ].cos - wmh.prj.lngcmd2 * ang[ i ].sin ; - } - } +void +fill_ang(angle_type* ang) { + float DX = (float)0.5 / wmh.psfres; + float dg2rd = boost::math::constants::pi() / (float)180.; + + for (int i = 0; i < wmh.prj.Nang; i++) { + + //... ratios calculation ....................................................... + + float deg = wmh.prj.ang0 + (float)i * wmh.prj.incr; // angle in degrees + ang[i].cos = cos(deg * dg2rd); // cosinus of the angle + ang[i].sin = sin(deg * dg2rd); // sinus of the angle + + //... first octave (0->45degrees) equivalent angle and its trigonometric ratios ....... + + float angR = fabs(deg); + int quad = (int)floor(angR / (float)90.); // quadrant + + angR = fabs(angR - (float)90. * (float)quad); // reduced angle: equivalent angle in 0->45degrees interval + if (angR > (float)45.) + angR = fabs((float)90. - angR); + + float sinR = (float)sin(angR * dg2rd); // sinus of the reduced angle + float cosR = (float)cos(angR * dg2rd); // cosinus of the reduced angle + + //... parametres of the oblique projection of a square voxel size 1 (half a trapezoid) ....... + + if (!wmh.do_psf) { + + if (angR < EPSILON) { + + ang[i].p = (float)1.; + ang[i].N1 = ang[i].N2 = (int)floor(DX); + ang[i].m = ang[i].n = (float)0.; + } else { + ang[i].p = (float)1. / cosR; // plateau highness + ang[i].m = -wmh.psfres / (sinR * cosR); // slope of the trapezoid in DX units (negative) + ang[i].n = (cosR + sinR) * (float)0.5 / (cosR * sinR); // independent term of the slope of the trapezoid (cm) + ang[i].N1 = (int)floor((float)fabs(cosR - sinR) * DX); // index of the first vertice (end of plateau) in res units + ang[i].N2 = (int)floor((cosR + sinR) * DX); // index of the second vertice (end of the slope) in res units + } + + ang[i].vxprj.lngd2 = ang[i].N2; + ang[i].vxprj.lng = 2 * ang[i].N2; + ang[i].vxprj.res = wmh.psfres; + } + //... rotation radius ................................................................ + + ang[i].Rrad = Rrad[i]; // assignation of (variable) rotation radius + + //... coordinates of the first bin of each projection and increments for consecutive bins .... + + if (wmh.do_att) { + + ang[i].incx = wmh.prj.szcm * ang[i].cos; + ang[i].incy = wmh.prj.szcm * ang[i].sin; + + ang[i].xbin0 = -ang[i].Rrad * ang[i].sin - wmh.prj.lngcmd2 * ang[i].cos; + ang[i].ybin0 = ang[i].Rrad * ang[i].cos - wmh.prj.lngcmd2 * ang[i].sin; + } + } } //============================================================================= //=== generate msk ============================================================ //============================================================================= -void generate_msk ( bool *msk_3d, bool *msk_2d, float *attmap, volume_type * vol ) -{ - //... initialzation of msk to true ......................... - - for ( int i = 0 ; i < vol->Nvox ; i++ ){ - msk_3d[ i ] = true; - } - - //... initialzation of msk_2d to false ..................... - - for ( int i = 0 ; i < vol->Npix ; i++ ){ - msk_2d[ i ] = false; - } - - //... to create mask from attenuation map .................. - - if ( wmh.do_msk_att ){ - for ( int i = 0 ; i < wmh.vol.Nvox ; i++ ){ - msk_3d[ i ] = ( attmap[ i ] > EPSILON ); - } - } - else { - //... to create a cylindrical mask...................... - - if (wmh.do_msk_cyl){ - - float Rmax2,xi,yi; - - if ( vol->Nrow >= vol->Ncol ) Rmax2 = vol->Nrowd2 * vol->Nrowd2; // Maximum allowed radius (distance from volume centre) - else Rmax2 = vol->Ncold2 * vol->Ncold2; - - int ip = -1; // in-plane index of the voxel - - for ( int i = 0 ; i < vol->Ncol ; i++ ){ - - xi = i - vol->Ncold2 + (float)0.5 ; - xi *= xi; - - for ( int j=0 ; j < vol->Nrow ; j++ ){ - - ip++; - yi = j - vol->Nrowd2 + (float)0.5 ; - yi *= yi; - - if ( ( xi + yi ) > Rmax2 ){ - - for ( int k = 0 ; k < vol->Nsli ; k ++){ - - msk_3d[ ip + k * vol->Npix ] = false; // loop for all the slices - } - } - } - } - } - - else { - //... to read a mask from a (int) file .................... - - if ( wmh.do_msk_file ) read_msk_file( msk_3d ); - } - } +void +generate_msk(bool* msk_3d, bool* msk_2d, float* attmap, volume_type* vol) { + //... initialzation of msk to true ......................... - - //... to apply slice mask (to remove slices from matrix) .............. - - if ( wmh.do_msk_slc ){ - for ( int i = 0 ; i < wmh.vol.first_sl ; i++ ){ - for ( int j = 0 ; j < wmh.vol.Npix ; j++ ) { - msk_3d[ i * wmh.vol.Npix + j ] = false; - } - } - for ( int i = wmh.vol.last_sl ; i < wmh.vol.Nsli ; i++ ){ - for ( int j = 0 ; j < wmh.vol.Npix ; j++ ) { - msk_3d[ i * wmh.vol.Npix + j ] = false; - } - } - } - - //... to collapse mask to 2d_mask ......... - - if ( wmh.do_msk_cyl ){ - for ( int i = 0 ; i < wmh.vol.Npix ; i++ ){ - msk_2d[ i ] = msk_3d[ i + wmh.vol.first_sl * wmh.vol.Npix ]; - } - } - else{ - for ( int i = 0 ; i < wmh.vol.Npix ; i++ ){ - - for ( int k = wmh.vol.first_sl ; k < wmh.vol.last_sl ; k++ ){ - - if ( msk_3d[ k * wmh.vol.Npix + i ] ){ - msk_2d[ i ] = true; - break; - } - } - } - } + for (int i = 0; i < vol->Nvox; i++) { + msk_3d[i] = true; + } + + //... initialzation of msk_2d to false ..................... + + for (int i = 0; i < vol->Npix; i++) { + msk_2d[i] = false; + } + + //... to create mask from attenuation map .................. + + if (wmh.do_msk_att) { + for (int i = 0; i < wmh.vol.Nvox; i++) { + msk_3d[i] = (attmap[i] > EPSILON); + } + } else { + //... to create a cylindrical mask...................... + + if (wmh.do_msk_cyl) { + + float Rmax2, xi, yi; + + if (vol->Nrow >= vol->Ncol) + Rmax2 = vol->Nrowd2 * vol->Nrowd2; // Maximum allowed radius (distance from volume centre) + else + Rmax2 = vol->Ncold2 * vol->Ncold2; + + int ip = -1; // in-plane index of the voxel + + for (int i = 0; i < vol->Ncol; i++) { + + xi = i - vol->Ncold2 + (float)0.5; + xi *= xi; + + for (int j = 0; j < vol->Nrow; j++) { + + ip++; + yi = j - vol->Nrowd2 + (float)0.5; + yi *= yi; + + if ((xi + yi) > Rmax2) { + + for (int k = 0; k < vol->Nsli; k++) { + + msk_3d[ip + k * vol->Npix] = false; // loop for all the slices + } + } + } + } + } + + else { + //... to read a mask from a (int) file .................... + + if (wmh.do_msk_file) + read_msk_file(msk_3d); + } + } + + //... to apply slice mask (to remove slices from matrix) .............. + + if (wmh.do_msk_slc) { + for (int i = 0; i < wmh.vol.first_sl; i++) { + for (int j = 0; j < wmh.vol.Npix; j++) { + msk_3d[i * wmh.vol.Npix + j] = false; + } + } + for (int i = wmh.vol.last_sl; i < wmh.vol.Nsli; i++) { + for (int j = 0; j < wmh.vol.Npix; j++) { + msk_3d[i * wmh.vol.Npix + j] = false; + } + } + } + + //... to collapse mask to 2d_mask ......... + + if (wmh.do_msk_cyl) { + for (int i = 0; i < wmh.vol.Npix; i++) { + msk_2d[i] = msk_3d[i + wmh.vol.first_sl * wmh.vol.Npix]; + } + } else { + for (int i = 0; i < wmh.vol.Npix; i++) { + + for (int k = wmh.vol.first_sl; k < wmh.vol.last_sl; k++) { + + if (msk_3d[k * wmh.vol.Npix + i]) { + msk_2d[i] = true; + break; + } + } + } + } } //============================================================================= //=== read_mask file ========================================================== //============================================================================= -void read_msk_file( bool *msk ) -{ - FILE *fid; - int *aux; - - aux = new int [ wmh.vol.Nvox ]; - - if ( (fid = fopen( wmh.msk_fn.c_str() , "rb")) == NULL) error_wmtools_SPECT( 126, wmh.msk_fn); - fread( aux, sizeof(int), wmh.vol.Nvox, fid); - fclose(fid); - - for (int i = 0 ; i < wmh.vol.Nvox ; i ++ ){ - msk[i] = ( aux[i] != 0 ); - } - - delete [] aux; +void +read_msk_file(bool* msk) { + FILE* fid; + int* aux; + + aux = new int[wmh.vol.Nvox]; + + if ((fid = fopen(wmh.msk_fn.c_str(), "rb")) == NULL) + error_wmtools_SPECT(126, wmh.msk_fn); + fread(aux, sizeof(int), wmh.vol.Nvox, fid); + fclose(fid); + + for (int i = 0; i < wmh.vol.Nvox; i++) { + msk[i] = (aux[i] != 0); + } + + delete[] aux; } //============================================================================= //=== read_att_map ============================================================ //============================================================================= -void read_att_map( float *attmap ) -{ - FILE *fid; - if ( ( fid = fopen( wmh.att_fn.c_str() , "rb") ) == NULL ) error_wmtools_SPECT ( 124, wmh.att_fn ); - fread( attmap, sizeof(float), wmh.vol.Nvox, fid); - - bool exist_nan = false; - - for (int i = 0 ; i < wmh.vol.Nvox ; i++ ){ - if ((boost::math::isnan)(attmap [ i ])){ - attmap [ i ] = 0; - exist_nan = true; - } - } - - if ( exist_nan ) cout << "WARNING: att map contains NaN values. Converted to zero" << endl; - - fclose(fid); +void +read_att_map(float* attmap) { + FILE* fid; + if ((fid = fopen(wmh.att_fn.c_str(), "rb")) == NULL) + error_wmtools_SPECT(124, wmh.att_fn); + fread(attmap, sizeof(float), wmh.vol.Nvox, fid); + + bool exist_nan = false; + + for (int i = 0; i < wmh.vol.Nvox; i++) { + if ((boost::math::isnan)(attmap[i])) { + attmap[i] = 0; + exist_nan = true; + } + } + + if (exist_nan) + cout << "WARNING: att map contains NaN values. Converted to zero" << endl; + + fclose(fid); } //========================================================================== //=== max_psf_szb ========================================================== //========================================================================== -int max_psf_szb( angle_type *ang ) -{ - int maxszb; - float Rrad_max = ang[0].Rrad; - - for( int i = 1; i < wmh.prj.Nang ; i++ ){ - if ( ang[ i ].Rrad > Rrad_max ) Rrad_max = ang[ i ].Rrad; // maximum rotation radius - } - - if ( !wmh.do_psf ){ // NO-PSF - - if ( !wmh.COL.do_fb ){ // parallel - maxszb = (int)( (float) sqrt( (float)2. ) * wmh.vol.szcm / wmh.prj.szcm ) + 3; - } - - else{ // fanbeam - float dpmax = wmh.vol.szcm * maxim( wmh.vol.Ncold2, wmh.vol.Nrowd2) + Rrad_max; - - float lon = wmh.COL.F - dpmax; - if ( lon < EPSILON ) error_wmtools_SPECT( 46, ""); - - //... maximum lenght of psf in bins ........................ - - float f = (int)( (float) sqrt( (float)2. ) * (wmh.vol.szcm / wmh.prj.szcm) * ( wmh.COL.F / lon ) ) + 3; - maxszb = minim ( f , wmh.prj.Nbin ); - } - } - else{ // PSF - voxel_type vox; - - if ( wmh.COL.do_fb ){ - vox.costhe = (float)1. / sqrt( wmh.prj.lngcmd2 * wmh.prj.lngcmd2 / ( wmh.COL.F * wmh.COL.F ) + (float)1.); - } - //... maximum length of psf in bins ........................ - - vox.dv2dp = Rrad_max + wmh.vol.szcm * maxim( wmh.vol.Ncold2, wmh.vol.Nrowd2 ) * (float)1.5; - float sig_h_max_cm = calc_sigma_h( vox, wmh.COL ); - maxszb = (int)floor( wmh.maxsigm * (float)2. * sig_h_max_cm / wmh.prj.szcm ) + 3; - - if ( wmh.do_psf_3d ){ - float sig_v_max_cm = calc_sigma_v( vox, wmh.COL ); - int maxszb_v = (int)floor( wmh.maxsigm * (float)2. * sig_v_max_cm / wmh.prj.thcm ) + 3; - maxszb = maxim( maxszb , maxszb_v ); - } - } +int +max_psf_szb(angle_type* ang) { + int maxszb; + float Rrad_max = ang[0].Rrad; - return( maxszb ); + for (int i = 1; i < wmh.prj.Nang; i++) { + if (ang[i].Rrad > Rrad_max) + Rrad_max = ang[i].Rrad; // maximum rotation radius + } + + if (!wmh.do_psf) { // NO-PSF + + if (!wmh.COL.do_fb) { // parallel + maxszb = (int)((float)sqrt((float)2.) * wmh.vol.szcm / wmh.prj.szcm) + 3; + } + + else { // fanbeam + float dpmax = wmh.vol.szcm * maxim(wmh.vol.Ncold2, wmh.vol.Nrowd2) + Rrad_max; + + float lon = wmh.COL.F - dpmax; + if (lon < EPSILON) + error_wmtools_SPECT(46, ""); + + //... maximum lenght of psf in bins ........................ + + float f = (int)((float)sqrt((float)2.) * (wmh.vol.szcm / wmh.prj.szcm) * (wmh.COL.F / lon)) + 3; + maxszb = minim(f, wmh.prj.Nbin); + } + } else { // PSF + voxel_type vox; + + if (wmh.COL.do_fb) { + vox.costhe = (float)1. / sqrt(wmh.prj.lngcmd2 * wmh.prj.lngcmd2 / (wmh.COL.F * wmh.COL.F) + (float)1.); + } + //... maximum length of psf in bins ........................ + + vox.dv2dp = Rrad_max + wmh.vol.szcm * maxim(wmh.vol.Ncold2, wmh.vol.Nrowd2) * (float)1.5; + float sig_h_max_cm = calc_sigma_h(vox, wmh.COL); + maxszb = (int)floor(wmh.maxsigm * (float)2. * sig_h_max_cm / wmh.prj.szcm) + 3; + + if (wmh.do_psf_3d) { + float sig_v_max_cm = calc_sigma_v(vox, wmh.COL); + int maxszb_v = (int)floor(wmh.maxsigm * (float)2. * sig_v_max_cm / wmh.prj.thcm) + 3; + maxszb = maxim(maxszb, maxszb_v); + } + } + + return (maxszb); } //========================================================================== //=== calc_sigma_h ========================================================= //========================================================================== -float calc_sigma_h( voxel_type vox, collim_type COL ) -{ - float sigma; - - if ( COL.do_fb ){ - float denom = sqrt( COL.L * COL.L * (COL.F - vox.dv2dp) * (COL.F - vox.dv2dp) - COL.w * COL.w * (COL.L + (float)2. * vox.dv2dp) * (COL.L + (float)2. * vox.dv2dp)); - float xc = COL.A_h * (vox.dv2dp + COL.L + COL.D) * COL.w * ( (float)2. * COL.F + COL.L) / (vox.costhe * denom); - sigma = sqrt( COL.insgm * COL.insgm + xc * xc ); - } - else sigma = COL.A * vox.dv2dp + COL.B ; - - return( sigma ); +float +calc_sigma_h(voxel_type vox, collim_type COL) { + float sigma; + + if (COL.do_fb) { + float denom = sqrt(COL.L * COL.L * (COL.F - vox.dv2dp) * (COL.F - vox.dv2dp) - + COL.w * COL.w * (COL.L + (float)2. * vox.dv2dp) * (COL.L + (float)2. * vox.dv2dp)); + float xc = COL.A_h * (vox.dv2dp + COL.L + COL.D) * COL.w * ((float)2. * COL.F + COL.L) / (vox.costhe * denom); + sigma = sqrt(COL.insgm * COL.insgm + xc * xc); + } else + sigma = COL.A * vox.dv2dp + COL.B; + + return (sigma); } //============================================================================= //=== itoa ==================================================================== //============================================================================= -char *itoa(int n,char *s) -{ - int i,sign; - char c; - - if((sign=n)<0) { - n=-n; - } - - i=0; - do{ - s[i++]=n%10+'0'; - } - while((n/=10)>0); - - if(sign<0) { - s[i++]='-'; - } - - s[i]=EOS; - - for(int low=0,hi=i-1;low 0); + + if (sign < 0) { + s[i++] = '-'; + } + + s[i] = EOS; + + for (int low = 0, hi = i - 1; low < hi; low++, hi--) { + c = s[low]; + s[low] = s[hi]; + s[hi] = c; + } + + return (s); } //============================================================================= //=== free_wm ================================================================= //============================================================================= -void free_wm( wm_type *f ) -{ - delete [] f->ar; - delete [] f->ja; - delete [] f->ia; +void +free_wm(wm_type* f) { + delete[] f->ar; + delete[] f->ja; + delete[] f->ia; } //============================================================================= //=== free_wm_da ============================================================== //============================================================================= -void free_wm_da( wm_da_type *f ) -{ - for(int i=0; i< f->NbOS; i++){ - delete [] f->val[i]; - delete [] f->col[i]; - } - delete [] f->val; - delete [] f->col; - delete [] f->ne; - - if ( f->do_save_STIR ){ - delete [] f->nb; - delete [] f->ns; - delete [] f->na; - delete [] f->nx; - delete [] f->ny; - delete [] f->nz; - } +void +free_wm_da(wm_da_type* f) { + for (int i = 0; i < f->NbOS; i++) { + delete[] f->val[i]; + delete[] f->col[i]; + } + delete[] f->val; + delete[] f->col; + delete[] f->ne; + + if (f->do_save_STIR) { + delete[] f->nb; + delete[] f->ns; + delete[] f->na; + delete[] f->nx; + delete[] f->ny; + delete[] f->nz; + } } //============================================================================= //== error_wmtools_SPECT ====================================================== //============================================================================= -void error_wmtools_SPECT( int nerr, string txt ) -{ +void +error_wmtools_SPECT(int nerr, string txt) { #if 0 switch(nerr){ case 11: printf("\n\nError wm_SPECT: number of variable rotation radius in file: %s different from number of angles\n",txt.c_str());break; @@ -937,28 +953,54 @@ void error_wmtools_SPECT( int nerr, string txt ) exit(0); #else using stir::error; - switch(nerr){ - case 11: printf("\n\nError wm_SPECT: number of variable rotation radius in file: %s different from number of angles\n",txt.c_str());break; - case 12: error("\n\nError wm_SPECT: first parameter in collimator file should be 'p' or 'f' to indicate parallel or fanbeam collimator\n");break; - case 13: printf("\n\nError wm_SPECT: not enough parameters in collimator file: %s \n",txt.c_str());break; - case 21: printf("\n\nError wmtools_SPECT: undefined collimator. Collimator %s not found\n",txt.c_str()); break; - case 30: printf("\n\nError wmtools_SPECT: can not open %s for reading\n",txt.c_str()); break; - case 31: printf("\n\nError wmtools_SPECT: can not open %s for writing\n",txt.c_str()); break; - case 46: error( "\n\nError weight3d: there are voxels near or further than de FOCAL lenght\n"); break; - case 50: printf("\n\nError wmtools_SPECT: No header stored in %s \n",txt.c_str()); break; - + switch (nerr) { + case 11: + printf("\n\nError wm_SPECT: number of variable rotation radius in file: %s different from number of angles\n", txt.c_str()); + break; + case 12: + error("\n\nError wm_SPECT: first parameter in collimator file should be 'p' or 'f' to indicate parallel or fanbeam " + "collimator\n"); + break; + case 13: + printf("\n\nError wm_SPECT: not enough parameters in collimator file: %s \n", txt.c_str()); + break; + case 21: + printf("\n\nError wmtools_SPECT: undefined collimator. Collimator %s not found\n", txt.c_str()); + break; + case 30: + printf("\n\nError wmtools_SPECT: can not open %s for reading\n", txt.c_str()); + break; + case 31: + printf("\n\nError wmtools_SPECT: can not open %s for writing\n", txt.c_str()); + break; + case 46: + error("\n\nError weight3d: there are voxels near or further than de FOCAL lenght\n"); + break; + case 50: + printf("\n\nError wmtools_SPECT: No header stored in %s \n", txt.c_str()); + break; + //... error: value of argv[].......................... - - case 114: printf("\n\nError wm_SPECT: file with variable rotation radius: %s not found\n",txt.c_str());break; - case 122: printf("\n\nError wm_SPECT: file with variable collimator parameters: %s not found\n",txt.c_str());break; - case 124: printf("\n\nError wm_SPECT: can not open attenuation map-> argv[24]: %s for reading\n",txt.c_str()); break; - case 126: printf("\n\nError wm_SPECT: can not open file mask-> argv[26]: %s for reading\n",txt.c_str()); break; - - default: error("\n\nError wmtools_SPECT: unknown error number on error_wmtools_SPECT()"); + + case 114: + printf("\n\nError wm_SPECT: file with variable rotation radius: %s not found\n", txt.c_str()); + break; + case 122: + printf("\n\nError wm_SPECT: file with variable collimator parameters: %s not found\n", txt.c_str()); + break; + case 124: + printf("\n\nError wm_SPECT: can not open attenuation map-> argv[24]: %s for reading\n", txt.c_str()); + break; + case 126: + printf("\n\nError wm_SPECT: can not open file mask-> argv[26]: %s for reading\n", txt.c_str()); + break; + + default: + error("\n\nError wmtools_SPECT: unknown error number on error_wmtools_SPECT()"); } - + #endif -} +} #if 0 void error_wm_SPECT( int nerr, string txt) diff --git a/src/recon_buildblock/SPECTUB_Weight3d.cxx b/src/recon_buildblock/SPECTUB_Weight3d.cxx index 5dcd6a70e0..ebd8741399 100644 --- a/src/recon_buildblock/SPECTUB_Weight3d.cxx +++ b/src/recon_buildblock/SPECTUB_Weight3d.cxx @@ -1,5 +1,5 @@ /* - Copyright (c) 2013, Biomedical Image Group (GIB), Universitat de Barcelona, Barcelona, Spain. + Copyright (c) 2013, Biomedical Image Group (GIB), Universitat de Barcelona, Barcelona, Spain. Copyright (c) 2013, University College London This file is part of STIR. @@ -19,8 +19,7 @@ \author Carles Falcon */ - -//user defined libraries +// user defined libraries #include "stir/recon_buildblock/SPECTUB_Tools.h" #include "stir/recon_buildblock/SPECTUB_Weight3d.h" @@ -29,7 +28,7 @@ #include #include "stir/spatial_transformation/InvertAxis.h" -//system libraries +// system libraries #include #include #include @@ -41,923 +40,935 @@ namespace SPECTUB { #define EPSILON 1e-12 #define EOS '\0' -#define maxim(a,b) ((a)>=(b)?(a):(b)) -#define minim(a,b) ((a)<=(b)?(a):(b)) -#define abs(a) ((a)>=0?(a):(-a)) -#define SIGN(a) (a<-EPSILON?-1:(a>EPSILON?1:0)) - -#define REF_DIST 5. //reference distance for fanbeam PSF +#define maxim(a, b) ((a) >= (b) ? (a) : (b)) +#define minim(a, b) ((a) <= (b) ? (a) : (b)) +#define abs(a) ((a) >= 0 ? (a) : (-a)) +#define SIGN(a) (a < -EPSILON ? -1 : (a > EPSILON ? 1 : 0)) + +#define REF_DIST 5. // reference distance for fanbeam PSF using namespace std; //========================================================================== //=== wm_calculation ======================================================= //========================================================================== -void wm_calculation( const int kOS, - const angle_type *const ang, - voxel_type vox, - bin_type bin, - const volume_type& vol, - const proj_type& prj, - const float *attmap, - const bool *msk_3d, - const bool *msk_2d, - const int maxszb, - const discrf_type *const gaussdens, - const int *const NITEMS) -{ - - float weight; - float coeff_att = (float) 1.; - int jp; - float eff; - - //... variables for geometric component .............................................. - - psf1d_type psf1d_h, psf1d_v; - - psf1d_h.maxszb = maxszb; - psf1d_h.val = new float [ maxszb ]; - psf1d_h.ind = new int [ maxszb ]; - - if ( wmh.do_psf_3d ){ - psf1d_v.maxszb = maxszb; - psf1d_v.val = new float [ maxszb ]; - psf1d_v.ind = new int [ maxszb ]; +void +wm_calculation(const int kOS, const angle_type* const ang, voxel_type vox, bin_type bin, const volume_type& vol, + const proj_type& prj, const float* attmap, const bool* msk_3d, const bool* msk_2d, const int maxszb, + const discrf_type* const gaussdens, const int* const NITEMS) { + + float weight; + float coeff_att = (float)1.; + int jp; + float eff; + + //... variables for geometric component .............................................. + + psf1d_type psf1d_h, psf1d_v; + + psf1d_h.maxszb = maxszb; + psf1d_h.val = new float[maxszb]; + psf1d_h.ind = new int[maxszb]; + + if (wmh.do_psf_3d) { + psf1d_v.maxszb = maxszb; + psf1d_v.val = new float[maxszb]; + psf1d_v.ind = new int[maxszb]; + } + + psf2da_type psf; + + psf.maxszb_h = maxszb; + if (wmh.do_psf_3d) + psf.maxszb_v = maxszb; + else + psf.maxszb_v = 1; + psf.maxszb_t = psf.maxszb_h * psf.maxszb_v; + + psf.val = new float[psf.maxszb_t]; // allocation for PSF values + psf.ib = new int[psf.maxszb_t]; // allocation for PSF indices + psf.jb = new int[psf.maxszb_t]; // allocation for PSF indices + + //... variables for attenuation component ............................................. + + attpth_type* attpth = 0; // initialise to avoid compiler warning + int sizeattpth = 1; // initialise to avoid compiler warning + + if (wmh.do_att || wmh.do_msk_att) { + + if (!wmh.do_full_att) + sizeattpth = 1; + else + sizeattpth = psf.maxszb_t; + + attpth = new attpth_type[sizeattpth]; + attpth[0].maxlng = vol.Ncol + vol.Nrow + vol.Nsli; // maximum length of an attenuation path + + for (int i = 0; i < sizeattpth; i++) { + + attpth[i].dl = new float[attpth[0].maxlng]; + attpth[i].iv = new int[attpth[0].maxlng]; + attpth[i].maxlng = attpth[0].maxlng; } - - psf2da_type psf; - - psf.maxszb_h = maxszb; - if ( wmh.do_psf_3d ) psf.maxszb_v = maxszb; - else psf.maxszb_v = 1; - psf.maxszb_t = psf.maxszb_h * psf.maxszb_v; - - psf.val = new float [ psf.maxszb_t ]; // allocation for PSF values - psf.ib = new int [ psf.maxszb_t ]; // allocation for PSF indices - psf.jb = new int [ psf.maxszb_t ]; // allocation for PSF indices - - //... variables for attenuation component ............................................. - - attpth_type *attpth = 0; // initialise to avoid compiler warning - int sizeattpth = 1; // initialise to avoid compiler warning - - if ( wmh.do_att || wmh.do_msk_att ){ + } - if ( !wmh.do_full_att ) sizeattpth = 1 ; - else sizeattpth = psf.maxszb_t ; - - attpth = new attpth_type [ sizeattpth ] ; - attpth[ 0 ].maxlng = vol.Ncol + vol.Nrow + vol.Nsli ; // maximum length of an attenuation path - - for (int i = 0 ; i < sizeattpth ; i++ ){ - - attpth[ i ].dl = new float [ attpth[ 0 ].maxlng ]; - attpth[ i ].iv = new int [ attpth[ 0 ].maxlng ]; - attpth[ i ].maxlng = attpth[ 0 ].maxlng; - } - } - - //... to fill projection indices for STIR format ............................. - - if ( wm.do_save_STIR ){ - - jp = -1; // projection index (row index of the weight matrix ) - int j1; - - for ( int j = 0 ; j < prj.NangOS ; j++ ){ - - j1 = wmh.index[ j ]; - - for ( int k = 0 ; k < prj.Nsli ; k++ ){ - - for ( int i = 0 ; i < prj.Nbin ; i++){ - - jp++; - wm.na[ jp ] = j1; - wm.nb[ jp ] = i - (int)prj.Nbind2; - wm.ns[ jp ] = k; - } - } - } - } - - //=== LOOP1: IMAGE ROWS ======================================================================= - - for ( vox.irow = 0 ; vox.irow < vol.Nrow ; vox.irow++ ){ - - //cout << "weights: " << 100.*(vox.irow+1)/vol.Nrow << "%" << endl; - - vox.y = vol.y0 + vox.irow * vol.szcm ; // y coordinate of the voxel (index 0->Nrow-1: irow) - - //=== LOOP2: IMAGE COLUMNS ================================================================= - - for ( vox.icol = 0 ; vox.icol < vol.Ncol ; vox.icol++ ){ - - vox.x = vol.x0 + vox.icol * vol.szcm ; // x coordinate of the voxel (index 0->Ncol-1: icol) - vox.ip = vox.irow * vol.Ncol + vox.icol ; // in-plane index of the voxel considering the slice as an array - - //... to apply mask ......................................... - - if ( wmh.do_msk){ - - if ( !msk_2d[ vox.ip ] ) continue; // to skip voxel if it is outside the 2d_mask - } - - //=== LOOP3: ANGLES INTO SUBSETS ======================================================== - - for( int k = 0 ; k < prj.NangOS ; k++ ){ - - int ka = wmh.index[ k ]; // angle index of the current projection (considering the whole set of projections) - - //... perpendicular distance form voxel to detection plane ........................... - - vox.dv2dp = vox.x * ang[ ka ].sin - vox.y * ang[ ka ].cos + ang[ ka ].Rrad ; - - if ( vox.dv2dp <= 0. ) continue; // skipping voxel if it is beyond the detection plane (corner voxels) - - //... x coordinate in the rotated frame .............................................. - - vox.x1 = vox.x * ang[ ka ].cos + vox.y * ang[ ka ].sin ; - - //... to project voxels onto the detection plane and to calculate other distances ..... - - voxel_projection( &vox , &eff , prj.lngcmd2 ); - - //... setting PSF to zero ......................................... - - // for ( int i = 0 ; i < nel ; i++ ){ - // - // psf.val[ i ] = (float)0.; - // psf.ib[ i ] = psf.jb[ i ] = 0; - // } - - //... correction for PSF .............................. - - if ( !wmh.do_psf ) fill_psf_no ( &psf, &psf1d_h, vox, &ang[ ka ], bin.szdx ); - - else{ - - if ( wmh.do_psf_3d ) fill_psf_3d ( &psf, &psf1d_h, &psf1d_v, vox, gaussdens, bin.szdx, bin.thdx, bin.thcmd2 ); - - else fill_psf_2d ( &psf, &psf1d_h, vox, gaussdens, bin.szdx ); - } - - //... correction for attenuation ................................................. - - if ( wmh.do_att ){ - - vox.z = (float)0. ; - - if ( !wmh.do_full_att ){ // simple correction for attenuation - - bin.x = ang[ ka ].xbin0 + vox.xd0 * ang[ ka ].cos; // x coord of the projection of the center of the voxel in the detection line - bin.y = ang[ ka ].ybin0 + vox.xd0 * ang[ ka ].sin; - bin.z = (float)0. ; - - calc_att_path( bin, vox, vol, &attpth[ 0 ]); - } - else{ // full correction for attenuation - - for ( int i = 0 ; i < psf.Nib ; i++ ){ - - bin.x = ang[ ka ].xbin0 + ang[ ka ].incx * ( (float)psf.ib[ i ] + (float)0.5 ); - bin.y = ang[ ka ].ybin0 + ang[ ka ].incy * ( (float)psf.ib[ i ] + (float)0.5 ); - bin.z = (float)psf.jb[ i ] * vox.thcm ; - - calc_att_path( bin, vox, vol, &attpth[ i ]); - } - } - } - - //=== LOOP4: IMAGE SLICES ================================================================ - - for ( vox.islc = vol.first_sl ; vox.islc < vol.last_sl ; vox.islc++ ){ - - vox.iv = vox.ip + vox.islc * vol.Npix ; // volume index of the voxel (volume as an array) - - if ( wmh.do_msk ){ - if ( !msk_3d[ vox.iv ] ) continue; - } - - if ( wmh.do_att && !wmh.do_full_att ) coeff_att = calc_att( &attpth[ 0 ], attmap , vox.islc ); - - //... weight matrix values calculation ....................................... - - for ( int ie = 0 ; ie < psf.Nib ; ie++ ){ - - if ( psf.ib[ ie ] < 0 ) continue; - if ( psf.ib[ ie ] >= prj.Nbin ) continue; - - int ks = ( vox.islc + psf.jb[ ie ] ); - - if ( ks < 0 ) continue; - if ( ks >= vol.Nsli ) continue; - - jp = k * prj.Nbp + ks * prj.Nbin + psf.ib[ ie ]; - - if ( wmh.do_full_att ) coeff_att = calc_att( &attpth[ ie ], attmap, vox.islc ); - - weight = psf.val[ ie ] * eff * coeff_att ; - - //... fill image STIR indices ........................... - - if ( wm.do_save_STIR ){ - stir::InvertAxis invert; - wm.nx[ vox.iv ] = (short int)invert.invert_axis_index(( vox.icol - (int) floor( vol.Ncold2 ) ),vol.Ncold2*2, "x"); // centered index for STIR format - wm.ny[ vox.iv ] = (short int)( vox.irow - (int) floor( vol.Nrowd2 ) ); // centered index for STIR format - wm.nz[ vox.iv ] = (short int) vox.islc ; // non-centered index for STIR format - } - - //... fill wm values ..................... - - wm.col[ jp ][ wm.ne[ jp ] ] = vox.iv; - wm.val[ jp ][ wm.ne[ jp ] ] = weight; - wm.ne[ jp ]++; - - if ( wm.ne[ jp ] >= NITEMS[ jp ] ) error_weight3d(45, "" ); - } - } // end of LOOP4: image slices - } // end of LOOP3: projection angle into subset - } // end of LOOP2: image rows - } // end of LOOP1: image cols - - //... detele allocated memory .............. - - delete [] psf1d_h.val ; - delete [] psf1d_h.ind ; - - if ( wmh.do_psf_3d ){ - delete [] psf1d_v.val ; - delete [] psf1d_v.ind ; - } + //... to fill projection indices for STIR format ............................. - delete [] psf.val; - delete [] psf.ib; - delete [] psf.jb; - - if ( wmh.do_att ){ - for ( int i = 0 ; i < sizeattpth ; i++ ){ - delete [] attpth[ i ].dl; - delete [] attpth[ i ].iv; - } - delete [] attpth; - } -} + if (wm.do_save_STIR) { + + jp = -1; // projection index (row index of the weight matrix ) + int j1; + + for (int j = 0; j < prj.NangOS; j++) { + + j1 = wmh.index[j]; + + for (int k = 0; k < prj.Nsli; k++) { + + for (int i = 0; i < prj.Nbin; i++) { + + jp++; + wm.na[jp] = j1; + wm.nb[jp] = i - (int)prj.Nbind2; + wm.ns[jp] = k; + } + } + } + } + + //=== LOOP1: IMAGE ROWS ======================================================================= + + for (vox.irow = 0; vox.irow < vol.Nrow; vox.irow++) { + + // cout << "weights: " << 100.*(vox.irow+1)/vol.Nrow << "%" << endl; + + vox.y = vol.y0 + vox.irow * vol.szcm; // y coordinate of the voxel (index 0->Nrow-1: irow) + + //=== LOOP2: IMAGE COLUMNS ================================================================= + + for (vox.icol = 0; vox.icol < vol.Ncol; vox.icol++) { + + vox.x = vol.x0 + vox.icol * vol.szcm; // x coordinate of the voxel (index 0->Ncol-1: icol) + vox.ip = vox.irow * vol.Ncol + vox.icol; // in-plane index of the voxel considering the slice as an array + + //... to apply mask ......................................... + + if (wmh.do_msk) { + + if (!msk_2d[vox.ip]) + continue; // to skip voxel if it is outside the 2d_mask + } + + //=== LOOP3: ANGLES INTO SUBSETS ======================================================== + + for (int k = 0; k < prj.NangOS; k++) { + + int ka = wmh.index[k]; // angle index of the current projection (considering the whole set of projections) + + //... perpendicular distance form voxel to detection plane ........................... + + vox.dv2dp = vox.x * ang[ka].sin - vox.y * ang[ka].cos + ang[ka].Rrad; + + if (vox.dv2dp <= 0.) + continue; // skipping voxel if it is beyond the detection plane (corner voxels) + + //... x coordinate in the rotated frame .............................................. + + vox.x1 = vox.x * ang[ka].cos + vox.y * ang[ka].sin; + + //... to project voxels onto the detection plane and to calculate other distances ..... + + voxel_projection(&vox, &eff, prj.lngcmd2); + + //... setting PSF to zero ......................................... + + // for ( int i = 0 ; i < nel ; i++ ){ + // + // psf.val[ i ] = (float)0.; + // psf.ib[ i ] = psf.jb[ i ] = 0; + // } + + //... correction for PSF .............................. + + if (!wmh.do_psf) + fill_psf_no(&psf, &psf1d_h, vox, &ang[ka], bin.szdx); + + else { + + if (wmh.do_psf_3d) + fill_psf_3d(&psf, &psf1d_h, &psf1d_v, vox, gaussdens, bin.szdx, bin.thdx, bin.thcmd2); + + else + fill_psf_2d(&psf, &psf1d_h, vox, gaussdens, bin.szdx); + } + + //... correction for attenuation ................................................. + + if (wmh.do_att) { + + vox.z = (float)0.; + + if (!wmh.do_full_att) { // simple correction for attenuation + + bin.x = ang[ka].xbin0 + + vox.xd0 * ang[ka].cos; // x coord of the projection of the center of the voxel in the detection line + bin.y = ang[ka].ybin0 + vox.xd0 * ang[ka].sin; + bin.z = (float)0.; + + calc_att_path(bin, vox, vol, &attpth[0]); + } else { // full correction for attenuation + + for (int i = 0; i < psf.Nib; i++) { + + bin.x = ang[ka].xbin0 + ang[ka].incx * ((float)psf.ib[i] + (float)0.5); + bin.y = ang[ka].ybin0 + ang[ka].incy * ((float)psf.ib[i] + (float)0.5); + bin.z = (float)psf.jb[i] * vox.thcm; + + calc_att_path(bin, vox, vol, &attpth[i]); + } + } + } + + //=== LOOP4: IMAGE SLICES ================================================================ + + for (vox.islc = vol.first_sl; vox.islc < vol.last_sl; vox.islc++) { + + vox.iv = vox.ip + vox.islc * vol.Npix; // volume index of the voxel (volume as an array) + + if (wmh.do_msk) { + if (!msk_3d[vox.iv]) + continue; + } + + if (wmh.do_att && !wmh.do_full_att) + coeff_att = calc_att(&attpth[0], attmap, vox.islc); + + //... weight matrix values calculation ....................................... + + for (int ie = 0; ie < psf.Nib; ie++) { + + if (psf.ib[ie] < 0) + continue; + if (psf.ib[ie] >= prj.Nbin) + continue; + + int ks = (vox.islc + psf.jb[ie]); + + if (ks < 0) + continue; + if (ks >= vol.Nsli) + continue; + + jp = k * prj.Nbp + ks * prj.Nbin + psf.ib[ie]; + + if (wmh.do_full_att) + coeff_att = calc_att(&attpth[ie], attmap, vox.islc); + + weight = psf.val[ie] * eff * coeff_att; + + //... fill image STIR indices ........................... + + if (wm.do_save_STIR) { + stir::InvertAxis invert; + wm.nx[vox.iv] = (short int)invert.invert_axis_index((vox.icol - (int)floor(vol.Ncold2)), vol.Ncold2 * 2, + "x"); // centered index for STIR format + wm.ny[vox.iv] = (short int)(vox.irow - (int)floor(vol.Nrowd2)); // centered index for STIR format + wm.nz[vox.iv] = (short int)vox.islc; // non-centered index for STIR format + } + + //... fill wm values ..................... + + wm.col[jp][wm.ne[jp]] = vox.iv; + wm.val[jp][wm.ne[jp]] = weight; + wm.ne[jp]++; + + if (wm.ne[jp] >= NITEMS[jp]) + error_weight3d(45, ""); + } + } // end of LOOP4: image slices + } // end of LOOP3: projection angle into subset + } // end of LOOP2: image rows + } // end of LOOP1: image cols + + //... detele allocated memory .............. + delete[] psf1d_h.val; + delete[] psf1d_h.ind; + + if (wmh.do_psf_3d) { + delete[] psf1d_v.val; + delete[] psf1d_v.ind; + } + + delete[] psf.val; + delete[] psf.ib; + delete[] psf.jb; + + if (wmh.do_att) { + for (int i = 0; i < sizeattpth; i++) { + delete[] attpth[i].dl; + delete[] attpth[i].iv; + } + delete[] attpth; + } +} //============================================================================= //=== wm_size_estimation ==================================================== //============================================================================= -void wm_size_estimation (int kOS, - const angle_type * const ang, - voxel_type vox, - bin_type bin, - const volume_type& vol, - const proj_type& prj, - const bool * const msk_3d, - const bool *const msk_2d, - const int maxszb, - const discrf_type * const gaussdens, - int *NITEMS) -{ - int jp; - float eff; - - //... variables for geometric component .............................................. - - psf1d_type psf1d_h, psf1d_v; - - psf1d_h.maxszb = maxszb; - psf1d_h.val = new float [ maxszb ]; - psf1d_h.ind = new int [ maxszb ]; - - if ( wmh.do_psf_3d ){ - psf1d_v.maxszb = maxszb; - psf1d_v.val = new float [ maxszb ]; - psf1d_v.ind = new int [ maxszb ]; - } - - psf2da_type psf; - - psf.maxszb_h = maxszb; - if ( wmh.do_psf_3d ) psf.maxszb_v = maxszb; - else psf.maxszb_v = 1; - psf.maxszb_t = psf.maxszb_h * psf.maxszb_v; - - psf.val = new float [ psf.maxszb_t ]; // allocation for PSF values - psf.ib = new int [ psf.maxszb_t ]; // allocation for PSF indices - psf.jb = new int [ psf.maxszb_t ]; // allocation for PSF indices - - //=== LOOP1: IMAGE ROWS ======================================================================= - - for ( vox.irow = 0 ; vox.irow < vol.Nrow ; vox.irow++ ){ - - vox.y = vol.y0 + vox.irow * vol.szcm ; // y coordinate of the voxel (index 0->Nrow-1: irow) - - //=== LOOP2: IMAGE COLUMNS ================================================================= - - for ( vox.icol = 0 ; vox.icol < vol.Ncol ; vox.icol++ ){ - - vox.x = vol.x0 + vox.icol * vol.szcm ; // x coordinate of the voxel (index 0->Ncol-1: icol) - vox.ip = vox.irow * vol.Ncol + vox.icol ; // in-plane index of the voxel considering the slice as an array - - //... to apply mask ......................................... - - if ( wmh.do_msk){ - - if ( !msk_2d[ vox.ip ] ) continue; // to skip voxel if it is outside the 2d_mask - } - - //=== LOOP3: ANGLES INTO SUBSETS ======================================================== - - for( int k = 0 ; k < prj.NangOS ; k++ ){ - - int ka = wmh.index[ k ]; // angle index of the current projection (considering the whole set of projections) - - //... perpendicular distance form voxel to detection plane ........................... - - vox.dv2dp = vox.x * ang[ ka ].sin - vox.y * ang[ ka ].cos + ang[ ka ].Rrad ; - - if ( vox.dv2dp <= 0. ) continue; // skipping voxel if it is beyond the detection plane (corner voxels) - - //... x coordinate in the rotated frame .............................................. - - vox.x1 = vox.x * ang[ ka ].cos + vox.y * ang[ ka ].sin ; - - //... to project voxels onto the detection plane and to calculate other distances ..... - - voxel_projection( &vox , &eff , prj.lngcmd2 ); - - //... setting PSF to zero ......................................... - -// for ( int i = 0 ; i < psf.maxszb ; i++ ){ -// psf.val[ i ] = (float) 0.; -// psf.ib[ i ] = psf.jb[ i ] = 0; -// } - - //... correction for PSF .............................. - - if ( !wmh.do_psf ) fill_psf_no ( &psf, &psf1d_h, vox, &ang[ ka ], bin.szdx ); - - else{ - - if ( wmh.do_psf_3d ) fill_psf_3d ( &psf, &psf1d_h, &psf1d_v, vox, gaussdens, bin.szdx, bin.thdx, bin.thcmd2 ); - - else fill_psf_2d ( &psf, &psf1d_h, vox, gaussdens, bin.szdx ); - } - - - //=== LOOP4: IMAGE SLICES ================================================================ - - for ( vox.islc = vol.first_sl ; vox.islc < vol.last_sl ; vox.islc++ ){ - - vox.iv = vox.ip + vox.islc * vol.Npix ; // volume index of the voxel (volume as an array) - - if ( wmh.do_msk ){ - if ( !msk_3d[ vox.iv ] ) continue; - } - - //... weight matrix values calculation ....................................... - - for ( int ie = 0 ; ie < psf.Nib ; ie++ ){ - - if ( psf.ib[ ie ] < 0 ) continue; - if ( psf.ib[ ie ] >= prj.Nbin ) continue; - - int ks = ( vox.islc + psf.jb[ ie ] ); - - if ( ks < 0 ) continue; - if ( ks >= vol.Nsli ) continue; - - jp = k * prj.Nbp + ks * prj.Nbin + psf.ib[ ie ]; - - NITEMS[ jp ]++; - } - } - } // end of LOOP3: projection angle into subset - } // end of LOOP2: image rows - } // end of LOOP1: image cols - - //... detele allocated memory .............. - - delete [] psf1d_h.val ; - delete [] psf1d_h.ind ; - - if ( wmh.do_psf_3d ){ - delete [] psf1d_v.val ; - delete [] psf1d_v.ind ; - } - - delete [] psf.val; - delete [] psf.ib; - delete [] psf.jb; -} +void +wm_size_estimation(int kOS, const angle_type* const ang, voxel_type vox, bin_type bin, const volume_type& vol, + const proj_type& prj, const bool* const msk_3d, const bool* const msk_2d, const int maxszb, + const discrf_type* const gaussdens, int* NITEMS) { + int jp; + float eff; + + //... variables for geometric component .............................................. + + psf1d_type psf1d_h, psf1d_v; + + psf1d_h.maxszb = maxszb; + psf1d_h.val = new float[maxszb]; + psf1d_h.ind = new int[maxszb]; + + if (wmh.do_psf_3d) { + psf1d_v.maxszb = maxszb; + psf1d_v.val = new float[maxszb]; + psf1d_v.ind = new int[maxszb]; + } + + psf2da_type psf; + + psf.maxszb_h = maxszb; + if (wmh.do_psf_3d) + psf.maxszb_v = maxszb; + else + psf.maxszb_v = 1; + psf.maxszb_t = psf.maxszb_h * psf.maxszb_v; + + psf.val = new float[psf.maxszb_t]; // allocation for PSF values + psf.ib = new int[psf.maxszb_t]; // allocation for PSF indices + psf.jb = new int[psf.maxszb_t]; // allocation for PSF indices + + //=== LOOP1: IMAGE ROWS ======================================================================= + + for (vox.irow = 0; vox.irow < vol.Nrow; vox.irow++) { + + vox.y = vol.y0 + vox.irow * vol.szcm; // y coordinate of the voxel (index 0->Nrow-1: irow) + + //=== LOOP2: IMAGE COLUMNS ================================================================= + + for (vox.icol = 0; vox.icol < vol.Ncol; vox.icol++) { + + vox.x = vol.x0 + vox.icol * vol.szcm; // x coordinate of the voxel (index 0->Ncol-1: icol) + vox.ip = vox.irow * vol.Ncol + vox.icol; // in-plane index of the voxel considering the slice as an array + + //... to apply mask ......................................... + + if (wmh.do_msk) { + + if (!msk_2d[vox.ip]) + continue; // to skip voxel if it is outside the 2d_mask + } + + //=== LOOP3: ANGLES INTO SUBSETS ======================================================== + + for (int k = 0; k < prj.NangOS; k++) { + + int ka = wmh.index[k]; // angle index of the current projection (considering the whole set of projections) + + //... perpendicular distance form voxel to detection plane ........................... + + vox.dv2dp = vox.x * ang[ka].sin - vox.y * ang[ka].cos + ang[ka].Rrad; + + if (vox.dv2dp <= 0.) + continue; // skipping voxel if it is beyond the detection plane (corner voxels) + + //... x coordinate in the rotated frame .............................................. + + vox.x1 = vox.x * ang[ka].cos + vox.y * ang[ka].sin; + + //... to project voxels onto the detection plane and to calculate other distances ..... + + voxel_projection(&vox, &eff, prj.lngcmd2); + + //... setting PSF to zero ......................................... + + // for ( int i = 0 ; i < psf.maxszb ; i++ ){ + // psf.val[ i ] = (float) 0.; + // psf.ib[ i ] = psf.jb[ i ] = 0; + // } + + //... correction for PSF .............................. + + if (!wmh.do_psf) + fill_psf_no(&psf, &psf1d_h, vox, &ang[ka], bin.szdx); + + else { + + if (wmh.do_psf_3d) + fill_psf_3d(&psf, &psf1d_h, &psf1d_v, vox, gaussdens, bin.szdx, bin.thdx, bin.thcmd2); + + else + fill_psf_2d(&psf, &psf1d_h, vox, gaussdens, bin.szdx); + } + + //=== LOOP4: IMAGE SLICES ================================================================ + + for (vox.islc = vol.first_sl; vox.islc < vol.last_sl; vox.islc++) { + + vox.iv = vox.ip + vox.islc * vol.Npix; // volume index of the voxel (volume as an array) + + if (wmh.do_msk) { + if (!msk_3d[vox.iv]) + continue; + } + + //... weight matrix values calculation ....................................... + + for (int ie = 0; ie < psf.Nib; ie++) { + + if (psf.ib[ie] < 0) + continue; + if (psf.ib[ie] >= prj.Nbin) + continue; + + int ks = (vox.islc + psf.jb[ie]); + + if (ks < 0) + continue; + if (ks >= vol.Nsli) + continue; + + jp = k * prj.Nbp + ks * prj.Nbin + psf.ib[ie]; + + NITEMS[jp]++; + } + } + } // end of LOOP3: projection angle into subset + } // end of LOOP2: image rows + } // end of LOOP1: image cols + + //... detele allocated memory .............. + + delete[] psf1d_h.val; + delete[] psf1d_h.ind; + + if (wmh.do_psf_3d) { + delete[] psf1d_v.val; + delete[] psf1d_v.ind; + } + + delete[] psf.val; + delete[] psf.ib; + delete[] psf.jb; +} //========================================================================== //=== calc_gauss =========================================================== //========================================================================== -void calc_gauss( discrf_type *gaussdens ) -{ - const float K0 = 1.0f/boost::math::constants::root_two_pi(); //Normalization factor: 1/sqrt(2*M_PI) - float x = 0; - float g; - - gaussdens->val[ gaussdens->lngd2 ] = K0; - float resd2 = gaussdens->res / (float)2.0; - - - for( int i = 1 ; i <= gaussdens->lngd2 ; i++ ){ - - x += gaussdens->res; - g = K0 * exp( - x * x / (float)2.); - gaussdens->val[ gaussdens->lngd2 + i ] = gaussdens->val[ gaussdens->lngd2 - i ] = g; - } - - gaussdens->acu[ 0 ] = gaussdens->val[ 0 ] * resd2 ; - - for ( int i = 1 ; i < gaussdens->lng ; i++ ){ - gaussdens->acu[ i ] = gaussdens->acu[ i - 1 ] + ( gaussdens->val[ i -1 ] + gaussdens->val[ i ] ) * resd2; - } - - for ( int i = 0 ; i < gaussdens->lng ; i++ ){ - gaussdens->acu[ i ] = (gaussdens->acu[ i ] - gaussdens->acu[ 0 ] ) / gaussdens->acu[ gaussdens->lng - 1 ]; - } +void +calc_gauss(discrf_type* gaussdens) { + const float K0 = 1.0f / boost::math::constants::root_two_pi(); // Normalization factor: 1/sqrt(2*M_PI) + float x = 0; + float g; + + gaussdens->val[gaussdens->lngd2] = K0; + float resd2 = gaussdens->res / (float)2.0; + + for (int i = 1; i <= gaussdens->lngd2; i++) { + + x += gaussdens->res; + g = K0 * exp(-x * x / (float)2.); + gaussdens->val[gaussdens->lngd2 + i] = gaussdens->val[gaussdens->lngd2 - i] = g; + } + + gaussdens->acu[0] = gaussdens->val[0] * resd2; + + for (int i = 1; i < gaussdens->lng; i++) { + gaussdens->acu[i] = gaussdens->acu[i - 1] + (gaussdens->val[i - 1] + gaussdens->val[i]) * resd2; + } + + for (int i = 0; i < gaussdens->lng; i++) { + gaussdens->acu[i] = (gaussdens->acu[i] - gaussdens->acu[0]) / gaussdens->acu[gaussdens->lng - 1]; + } } //========================================================================== //=== calc_vxprj ========================================================= //========================================================================== -void calc_vxprj( angle_type *ang ) -{ - //... initialization to zero ..................................... - - for ( int j = 0 ; j < ang->vxprj.lng ; j++ ) ang->vxprj.acu[ j ] = ang->vxprj.val[ j ] = (float)0.; - - //... total number of points (at DX resolution) ........................ - - int Nmax= 2 * ang->N2 ; - float resd2 = ang->vxprj.res / (float)2.0; - - //... plateau........................................................... - - for ( int i = 0 ; i < ang->N1 ; i++ ){ - ang->vxprj.val[ ang->N2 - i - 1 ] = ang->vxprj.val[ ang->N2 + i ] = ang->p; - } - - //... slopes of the trapezoid .......................................... - - for ( int i = ang->N1 ; i < ang->N2 ; i++ ){ - ang->vxprj.val[ ang->N2 - i - 1 ] = ang->vxprj.val[ ang->N2 + i ] = maxim (ang->m * ((float)i + (float)0.5) + ang->n, 0); - } - - //... cumulative sum ................................................... - - ang->vxprj.acu[ 0 ] = ang->vxprj.val[ 0 ] * resd2 ; - - for ( int i = 1 ; i < Nmax ; i++ ){ - ang->vxprj.acu[ i ] = ang->vxprj.acu[ i - 1 ] + ( ang->vxprj.val[ i -1 ] + ang->vxprj.val[ i ] ) * resd2; - } - - //... forcing distribution function to have area 1 ...................... - - for ( int i = 0 ; i < Nmax ; i++ ){ - ang->vxprj.acu[ i ] /= ang->vxprj.acu[ Nmax - 1 ]; - } -} +void +calc_vxprj(angle_type* ang) { + //... initialization to zero ..................................... + + for (int j = 0; j < ang->vxprj.lng; j++) + ang->vxprj.acu[j] = ang->vxprj.val[j] = (float)0.; + //... total number of points (at DX resolution) ........................ + + int Nmax = 2 * ang->N2; + float resd2 = ang->vxprj.res / (float)2.0; + + //... plateau........................................................... + + for (int i = 0; i < ang->N1; i++) { + ang->vxprj.val[ang->N2 - i - 1] = ang->vxprj.val[ang->N2 + i] = ang->p; + } + + //... slopes of the trapezoid .......................................... + + for (int i = ang->N1; i < ang->N2; i++) { + ang->vxprj.val[ang->N2 - i - 1] = ang->vxprj.val[ang->N2 + i] = maxim(ang->m * ((float)i + (float)0.5) + ang->n, 0); + } + + //... cumulative sum ................................................... + + ang->vxprj.acu[0] = ang->vxprj.val[0] * resd2; + + for (int i = 1; i < Nmax; i++) { + ang->vxprj.acu[i] = ang->vxprj.acu[i - 1] + (ang->vxprj.val[i - 1] + ang->vxprj.val[i]) * resd2; + } + + //... forcing distribution function to have area 1 ...................... + + for (int i = 0; i < Nmax; i++) { + ang->vxprj.acu[i] /= ang->vxprj.acu[Nmax - 1]; + } +} //========================================================================== //=== voxel_projection ===================================================== //========================================================================== -void voxel_projection ( voxel_type *vox, float * eff, float lngcmd2) -{ - - if ( wmh.COL.do_fb ){ // fan_beam - - //... angle between voxel-focal line and center-focal line and distance from voxel projection to detection line relevant points........ - - vox->costhe = cos( atan ( vox->x1 / ( wmh.COL.F - vox->dv2dp ) ) ); - vox->xdc = wmh.COL.F * vox->x1 / ( wmh.COL.F - vox->dv2dp ); // distance to the center of the detection line - vox->xd0 = vox->xdc + lngcmd2 ; // distance to the begin of the detection line - - //... efficiency correction ............................................................... - - if ( wmh.do_psf ) *eff = vox->costhe * vox->costhe * ( wmh.COL.F - REF_DIST ) / ( wmh.COL.F - vox->dv2dp ); - else *eff = (float) 1.; - - } - else{ // parallel - - //... distance from projected voxel (center) to the begin of the detection line ............ - - vox->xd0 = vox->x1 + lngcmd2; - - *eff = (float) 1. ; - } +void +voxel_projection(voxel_type* vox, float* eff, float lngcmd2) { + + if (wmh.COL.do_fb) { // fan_beam + + //... angle between voxel-focal line and center-focal line and distance from voxel projection to detection line relevant + // points........ + + vox->costhe = cos(atan(vox->x1 / (wmh.COL.F - vox->dv2dp))); + vox->xdc = wmh.COL.F * vox->x1 / (wmh.COL.F - vox->dv2dp); // distance to the center of the detection line + vox->xd0 = vox->xdc + lngcmd2; // distance to the begin of the detection line + + //... efficiency correction ............................................................... + + if (wmh.do_psf) + *eff = vox->costhe * vox->costhe * (wmh.COL.F - REF_DIST) / (wmh.COL.F - vox->dv2dp); + else + *eff = (float)1.; + + } else { // parallel + + //... distance from projected voxel (center) to the begin of the detection line ............ + + vox->xd0 = vox->x1 + lngcmd2; + + *eff = (float)1.; + } } //========================================================================== //=== fill_psf_no ========================================================== //========================================================================== -void fill_psf_no( psf2da_type *psf, psf1d_type * psf1d_h, const voxel_type& vox, angle_type const *const ang , float szdx ) -{ - psf1d_h->sgmcm = vox.szcm; +void +fill_psf_no(psf2da_type* psf, psf1d_type* psf1d_h, const voxel_type& vox, angle_type const* const ang, float szdx) { + psf1d_h->sgmcm = vox.szcm; - if ( wmh.COL.do_fb){ - if ( fabs( vox.x1 ) > EPSILON ) psf1d_h->sgmcm *= vox.xdc / vox.x1; // fb expanded projection of the voxel - } - - psf1d_h->di = min( (int) floor( szdx / psf1d_h->sgmcm ), ang->vxprj.lng -1 ) ; - psf1d_h->lngcm = ( fabs ( ang->sin ) + fabs( ang->cos ) ) * psf1d_h->sgmcm ; - - psf1d_h->lngcmd2 = psf1d_h->lngcm / (float)2.; - psf1d_h->efres = ang->vxprj.res * psf1d_h->sgmcm; // to resize discretization resolution once applied sgmcm - - calc_psf_bin( vox.xd0, wmh.prj.szcm, &ang->vxprj, psf1d_h ); - - for ( int ie = 0 ; ie < psf1d_h->Nib ; ie++ ){ - - psf->val [ ie ] = psf1d_h->val[ ie ]; - psf->ib [ ie ] = psf1d_h->ind[ ie ]; - psf->jb [ ie ] = 0; - } - psf->Nib = psf1d_h->Nib; + if (wmh.COL.do_fb) { + if (fabs(vox.x1) > EPSILON) + psf1d_h->sgmcm *= vox.xdc / vox.x1; // fb expanded projection of the voxel + } + + psf1d_h->di = min((int)floor(szdx / psf1d_h->sgmcm), ang->vxprj.lng - 1); + psf1d_h->lngcm = (fabs(ang->sin) + fabs(ang->cos)) * psf1d_h->sgmcm; + + psf1d_h->lngcmd2 = psf1d_h->lngcm / (float)2.; + psf1d_h->efres = ang->vxprj.res * psf1d_h->sgmcm; // to resize discretization resolution once applied sgmcm + + calc_psf_bin(vox.xd0, wmh.prj.szcm, &ang->vxprj, psf1d_h); + + for (int ie = 0; ie < psf1d_h->Nib; ie++) { + + psf->val[ie] = psf1d_h->val[ie]; + psf->ib[ie] = psf1d_h->ind[ie]; + psf->jb[ie] = 0; + } + psf->Nib = psf1d_h->Nib; } //========================================================================== //=== fill_psf_2d ========================================================== //========================================================================== -void fill_psf_2d( psf2da_type *psf, psf1d_type * psf1d_h, const voxel_type& vox, discrf_type const* const gaussdens, float szdx ) -{ - - psf1d_h->sgmcm = calc_sigma_h( vox, wmh.COL ); - - psf1d_h->di = min ( (int) floor( szdx / psf1d_h->sgmcm ), gaussdens->lng -1) ; - psf1d_h->lngcmd2 = psf1d_h->sgmcm * wmh.maxsigm ; - psf1d_h->lngcm = psf1d_h->lngcmd2 * (float)2.; - - psf1d_h->efres = gaussdens->res * psf1d_h->sgmcm ; - - calc_psf_bin( vox.xd0, wmh.prj.szcm, gaussdens, psf1d_h ); - - for ( int ie = 0 ; ie < psf1d_h->Nib ; ie++ ){ - - psf->val [ ie ] = psf1d_h->val[ ie ]; - psf->ib [ ie ] = psf1d_h->ind[ ie ]; - psf->jb [ ie ] = 0; - - } - psf->Nib = psf1d_h->Nib; +void +fill_psf_2d(psf2da_type* psf, psf1d_type* psf1d_h, const voxel_type& vox, discrf_type const* const gaussdens, float szdx) { + + psf1d_h->sgmcm = calc_sigma_h(vox, wmh.COL); + + psf1d_h->di = min((int)floor(szdx / psf1d_h->sgmcm), gaussdens->lng - 1); + psf1d_h->lngcmd2 = psf1d_h->sgmcm * wmh.maxsigm; + psf1d_h->lngcm = psf1d_h->lngcmd2 * (float)2.; + + psf1d_h->efres = gaussdens->res * psf1d_h->sgmcm; + + calc_psf_bin(vox.xd0, wmh.prj.szcm, gaussdens, psf1d_h); + + for (int ie = 0; ie < psf1d_h->Nib; ie++) { + + psf->val[ie] = psf1d_h->val[ie]; + psf->ib[ie] = psf1d_h->ind[ie]; + psf->jb[ie] = 0; + } + psf->Nib = psf1d_h->Nib; } //========================================================================== //=== fill_psf_3d ========================================================== //========================================================================== -void fill_psf_3d (psf2da_type *psf, - psf1d_type *psf1d_h, - psf1d_type *psf1d_v, - const voxel_type& vox, discrf_type const * const gaussdens, float szdx, float thdx, float thcmd2 ) -{ - - //... horizontal component ........................... +void +fill_psf_3d(psf2da_type* psf, psf1d_type* psf1d_h, psf1d_type* psf1d_v, const voxel_type& vox, discrf_type const* const gaussdens, + float szdx, float thdx, float thcmd2) { - psf1d_h->sgmcm = calc_sigma_h( vox, wmh.COL); - psf1d_h->lngcmd2 = psf1d_h->sgmcm * wmh.maxsigm ; - psf1d_h->lngcm = psf1d_h->lngcmd2 * (float)2.; - psf1d_h->di = min( (int) floor( szdx / psf1d_h->sgmcm ), gaussdens->lng - 1 ) ; - psf1d_h->efres = gaussdens->res * psf1d_h->sgmcm ; - - //... setting PSF to zero ......................................... - -// for ( int i = 0 ; i < psf1d_h->maxszb ; i++ ){ -// psf1d_h->val[ i ] = (float)0.; -// psf1d_h->ind[ i ] = 0; -// } - - //... calculation of the horizontal component of psf ................... - - calc_psf_bin( vox.xd0, wmh.prj.szcm, gaussdens, psf1d_h ); + //... horizontal component ........................... - //... vertical component .............................. - - psf1d_v->sgmcm = calc_sigma_v( vox, wmh.COL); - psf1d_v->lngcmd2 = psf1d_v->sgmcm * wmh.maxsigm; - psf1d_v->lngcm = psf1d_v->lngcmd2 * (float)2.; - psf1d_v->di = min( (int) floor( thdx / psf1d_v->sgmcm ), gaussdens->lng - 1 ) ; - psf1d_v->efres = gaussdens->res * psf1d_v->sgmcm ; - - //... setting PSF to zero ......................................... - -// for ( int i = 0 ; i < psf1d_v->maxszb ; i++ ){ -// psf1d_v->val[ i ] = (float)0.; -// psf1d_v->ind[ i ] = 0; -// } - - //... calculation of the vertical component of psf .................... - - calc_psf_bin( thcmd2, wmh.prj.thcm, gaussdens, psf1d_v ); - - //... mixing and setting PSF area to 1 (to correct for tail truncation of Gaussian function) ..... - - float w; - float area = 0; - int ip = 0; - float Nib_hp2 = (float) ( psf1d_h->Nib * psf1d_h->Nib )/ (float)4. ; - float Nib_vp2 = (float) ( psf1d_v->Nib * psf1d_v->Nib )/ (float)4. ; - - for ( int i = 0 ; i < psf1d_h->Nib ; i++ ){ - - float b = ( (float) psf1d_h->ind [ 0 ] + (float) psf1d_h->ind [ psf1d_h->Nib - 1 ] ) / (float)2. ; - float a = ( (float) psf1d_h->ind [ i ] - b ) * ( (float) psf1d_h->ind [ i ] - b ) / Nib_hp2 ; - - for ( int j = 0 ; j < psf1d_v->Nib ; j++ ){ - - if ( ( a + (float)( psf1d_v->ind [ j ] * psf1d_v->ind [ j ] ) / Nib_vp2 ) > (float)1. ) continue; - - w = psf1d_h->val[ i ] * psf1d_v->val[ j ]; - - if ( w < wmh.min_w ) continue; - - psf->val[ ip ] = w; - psf->ib [ ip ] = psf1d_h->ind [ i ]; - psf->jb [ ip ] = psf1d_v->ind [ j ]; - ip++; - - area += w; - } - } - psf->Nib = ip; - - for( int i = 0 ; i < ip ; i++ ) psf->val[ i ] /= area ; - + psf1d_h->sgmcm = calc_sigma_h(vox, wmh.COL); + psf1d_h->lngcmd2 = psf1d_h->sgmcm * wmh.maxsigm; + psf1d_h->lngcm = psf1d_h->lngcmd2 * (float)2.; + psf1d_h->di = min((int)floor(szdx / psf1d_h->sgmcm), gaussdens->lng - 1); + psf1d_h->efres = gaussdens->res * psf1d_h->sgmcm; + + //... setting PSF to zero ......................................... + + // for ( int i = 0 ; i < psf1d_h->maxszb ; i++ ){ + // psf1d_h->val[ i ] = (float)0.; + // psf1d_h->ind[ i ] = 0; + // } + + //... calculation of the horizontal component of psf ................... + + calc_psf_bin(vox.xd0, wmh.prj.szcm, gaussdens, psf1d_h); + + //... vertical component .............................. + + psf1d_v->sgmcm = calc_sigma_v(vox, wmh.COL); + psf1d_v->lngcmd2 = psf1d_v->sgmcm * wmh.maxsigm; + psf1d_v->lngcm = psf1d_v->lngcmd2 * (float)2.; + psf1d_v->di = min((int)floor(thdx / psf1d_v->sgmcm), gaussdens->lng - 1); + psf1d_v->efres = gaussdens->res * psf1d_v->sgmcm; + + //... setting PSF to zero ......................................... + + // for ( int i = 0 ; i < psf1d_v->maxszb ; i++ ){ + // psf1d_v->val[ i ] = (float)0.; + // psf1d_v->ind[ i ] = 0; + // } + + //... calculation of the vertical component of psf .................... + + calc_psf_bin(thcmd2, wmh.prj.thcm, gaussdens, psf1d_v); + + //... mixing and setting PSF area to 1 (to correct for tail truncation of Gaussian function) ..... + + float w; + float area = 0; + int ip = 0; + float Nib_hp2 = (float)(psf1d_h->Nib * psf1d_h->Nib) / (float)4.; + float Nib_vp2 = (float)(psf1d_v->Nib * psf1d_v->Nib) / (float)4.; + + for (int i = 0; i < psf1d_h->Nib; i++) { + + float b = ((float)psf1d_h->ind[0] + (float)psf1d_h->ind[psf1d_h->Nib - 1]) / (float)2.; + float a = ((float)psf1d_h->ind[i] - b) * ((float)psf1d_h->ind[i] - b) / Nib_hp2; + + for (int j = 0; j < psf1d_v->Nib; j++) { + + if ((a + (float)(psf1d_v->ind[j] * psf1d_v->ind[j]) / Nib_vp2) > (float)1.) + continue; + + w = psf1d_h->val[i] * psf1d_v->val[j]; + + if (w < wmh.min_w) + continue; + + psf->val[ip] = w; + psf->ib[ip] = psf1d_h->ind[i]; + psf->jb[ip] = psf1d_v->ind[j]; + ip++; + + area += w; + } + } + psf->Nib = ip; + + for (int i = 0; i < ip; i++) + psf->val[i] /= area; } //========================================================================== //=== calc_psf_bin ========================================================= //========================================================================== -void calc_psf_bin (float center_psf, - float binszcm, - discrf_type const * const vxprj, - psf1d_type *psf) -{ - float weight, preval; +void +calc_psf_bin(float center_psf, float binszcm, discrf_type const* const vxprj, psf1d_type* psf) { + float weight, preval; - //... position (in cm and bin index) of the first extrem of the vxprj on the detection line........ - - float beg_psf = center_psf - psf->lngcmd2; // position of the begin of the psf in the detection line (cm) - int jm = (int) floor( beg_psf / binszcm ); // first index in detection line interacting with psf (it can be negative) - float r_nextb = (float)( jm + 1 ) * binszcm ; // position of the next change of bin (cm) - int i1 = min( (int) floor( ( r_nextb - beg_psf ) / psf->efres) , vxprj->lng -1 ) ; // index in vxprj distribution in which happens the change of bin - int Ncb = ( vxprj->lng - i1 - 1 ) / psf->di ; // number of complete bins covered by PSF - - //... first weigth calculation ............................................................................... - - int ip = 0; // counter for the number of surviving weights - float area = (float)0. ; - - weight = vxprj->acu[ i1 ] - vxprj->acu[ 0 ]; - - if ( weight >= wmh.min_w ){ - psf->val[ ip ] = weight; - psf->ind[ ip ] = jm; - area += weight; - ip++; - } - - //... weight for the complete bins ................................................................... - - preval = vxprj->acu[ i1 ]; - - for ( int i = 0 ; i < Ncb ; i++ ){ - jm++; - i1 += psf->di; - weight = vxprj->acu[ i1 ] - preval ; - preval = vxprj->acu[ i1 ]; - - if ( weight >= wmh.min_w){ - psf->val[ ip ] = weight; - psf->ind[ ip ] = jm; - area += weight; - ip++; - } - } - - //... weight for the last bin ................................................................... + //... position (in cm and bin index) of the first extrem of the vxprj on the detection line........ - weight = (float)1. - preval ; - jm++; - - if ( weight >= wmh.min_w){ - psf->val[ ip ] = weight; - psf->ind[ ip ] = jm; - area += weight; - ip++; - } - - if ( ip >= psf->maxszb ) - error_weight3d( 47, "" ); - for (int i = 0 ; i < ip ; i++) psf->val[ i ] /= area; - psf->Nib = ip; + float beg_psf = center_psf - psf->lngcmd2; // position of the begin of the psf in the detection line (cm) + int jm = (int)floor(beg_psf / binszcm); // first index in detection line interacting with psf (it can be negative) + float r_nextb = (float)(jm + 1) * binszcm; // position of the next change of bin (cm) + int i1 = min((int)floor((r_nextb - beg_psf) / psf->efres), + vxprj->lng - 1); // index in vxprj distribution in which happens the change of bin + int Ncb = (vxprj->lng - i1 - 1) / psf->di; // number of complete bins covered by PSF + + //... first weigth calculation ............................................................................... + + int ip = 0; // counter for the number of surviving weights + float area = (float)0.; + + weight = vxprj->acu[i1] - vxprj->acu[0]; + + if (weight >= wmh.min_w) { + psf->val[ip] = weight; + psf->ind[ip] = jm; + area += weight; + ip++; + } + + //... weight for the complete bins ................................................................... + + preval = vxprj->acu[i1]; + + for (int i = 0; i < Ncb; i++) { + jm++; + i1 += psf->di; + weight = vxprj->acu[i1] - preval; + preval = vxprj->acu[i1]; + + if (weight >= wmh.min_w) { + psf->val[ip] = weight; + psf->ind[ip] = jm; + area += weight; + ip++; + } + } + + //... weight for the last bin ................................................................... + + weight = (float)1. - preval; + jm++; + if (weight >= wmh.min_w) { + psf->val[ip] = weight; + psf->ind[ip] = jm; + area += weight; + ip++; + } + + if (ip >= psf->maxszb) + error_weight3d(47, ""); + for (int i = 0; i < ip; i++) + psf->val[i] /= area; + psf->Nib = ip; } //============================================================================= //=== cal_att_path ============================================================ //============================================================================= -void calc_att_path(const bin_type& bin, const voxel_type& vox, const volume_type& vol, attpth_type *attpth ) -{ - float dx, dy, dz; - float dlast_x, dlast_y, dlast_z, dlast; - float next_x, next_y, next_z; - int cas; - - //... to initializate attpth to zero.............................. - -// for (int i = 0 ; i < attpth->maxlng ; i++ ){ -// attpth->dl [ i ] = (float) 0.; -// attpth->iv [ i ] = 0; -// } - - //... vector from voxel to bin and the sign of its components .... - - float ux = bin.x - vox.x; // first component of voxel_to_bin vector - float uy = bin.y - vox.y; // second component of voxel_to_bin vector - float uz = bin.z - vox.z; // third component of voxel_to_bin vector - - int signx = SIGN(ux); // sign of ux - int signy = SIGN(uy); // sign of uy - int signz = SIGN(uz); // sign of uz - - //... corresponding unary vector ................................... - - float dpb = sqrt(ux*ux + uy*uy + uz*uz); // distance from voxel_to_bin (modulus of [ux,uy,uz]) - ux /= dpb; // unit vector ux - uy /= dpb; // unit vector uy - uz /= dpb; // unit vector uz - - //... next and last distance to the attenuation map grip .................. - - if ( signx < 0 ){ - next_x = ( (float) vox.icol - (float) 0.5 ) * vox.szcm + vol.x0; - dlast_x = ( -vol.Xcmd2 - vox.x ) / ( ux + EPSILON ) ; - } - else{ - next_x = ( (float) vox.icol + (float) 0.5 ) * vox.szcm + vol.x0; - dlast_x = ( vol.Xcmd2 - vox.x ) / ( ux + EPSILON ) ; - } - - if ( signy < 0 ){ - next_y = ( (float) vox.irow - (float) 0.5 ) * vox.szcm + vol.y0; - dlast_y = ( -vol.Ycmd2 - vox.y ) / ( uy + EPSILON ) ; - } - else{ - next_y = ( (float) vox.irow + (float) 0.5 ) * vox.szcm + vol.y0; - dlast_y = ( vol.Ycmd2 - vox.y ) / ( uy + EPSILON ) ; - } - - if ( signz < 0 ){ - next_z = ( - (float) 0.5 ) * vox.thcm ; - dlast_z = ( -vol.Zcmd2 - vox.z ) / ( uz + EPSILON ) ; - } - else{ - next_z = ( (float) 0.5 ) * vox.thcm ; - dlast_z = ( vol.Zcmd2 - vox.z ) / ( uz + EPSILON ) ; - } - - dlast = minim ( minim ( dlast_x, dlast_y ) , minim ( dlast_z , dpb ) ); - - // ... distance to next planes avoiding high values for parallel or almost parallel lines ... - - dx = ( next_x - vox.x ) / ( ux + EPSILON ) ; - dy = ( next_y - vox.y ) / ( uy + EPSILON ) ; - dz = ( next_z - vox.z ) / ( uz + EPSILON ) ; - - //... variables initialization ..................................... - - float dant = (float)0. ; // previous distance (distance from voxel to the last change of voxel in the attenuation map) - int ni = 0 ; // number of voxels in the attenuation path - int iv = vox.ip ; // voxel index on the attenuation map - - //... loop while attenuation ray is inside the attenuation map - - for(;;){ - - cas = comp_dist( dx, dy, dz, dlast ); - - if ( cas == 0 ){ - - attpth->lng = ni; - return; - } - - else{ - - if ( ni >= attpth->maxlng ) error_weight3d(49, ""); - - attpth->iv[ ni ] = iv ; - - switch(cas){ - case 1: - attpth->dl[ ni ] = ( dx - dant ) ; - dant = dx ; - iv += signx ; - next_x += vox.szcm * signx ; - dx = ( next_x - vox.x ) / ( ux + EPSILON ) ; - break; - case 2: - attpth->dl[ ni ] = ( dy - dant ) ; - dant = dy ; - iv += signy * vol.Ncol ; - next_y += vox.szcm * signy ; - dy = ( next_y - vox.y ) / ( uy + EPSILON ) ; - break; - case 3: - attpth->dl[ ni ] = ( dz - dant ) ; - dant = dz; - iv += signz * vol.Npix ; - next_z += vox.thcm * signz ; - dz = ( next_z - vox.z ) / ( uz + EPSILON ) ; - break; - default: - error_weight3d (40, ""); - } - ni++; - } - } +void +calc_att_path(const bin_type& bin, const voxel_type& vox, const volume_type& vol, attpth_type* attpth) { + float dx, dy, dz; + float dlast_x, dlast_y, dlast_z, dlast; + float next_x, next_y, next_z; + int cas; + + //... to initializate attpth to zero.............................. + + // for (int i = 0 ; i < attpth->maxlng ; i++ ){ + // attpth->dl [ i ] = (float) 0.; + // attpth->iv [ i ] = 0; + // } + + //... vector from voxel to bin and the sign of its components .... + + float ux = bin.x - vox.x; // first component of voxel_to_bin vector + float uy = bin.y - vox.y; // second component of voxel_to_bin vector + float uz = bin.z - vox.z; // third component of voxel_to_bin vector + + int signx = SIGN(ux); // sign of ux + int signy = SIGN(uy); // sign of uy + int signz = SIGN(uz); // sign of uz + + //... corresponding unary vector ................................... + + float dpb = sqrt(ux * ux + uy * uy + uz * uz); // distance from voxel_to_bin (modulus of [ux,uy,uz]) + ux /= dpb; // unit vector ux + uy /= dpb; // unit vector uy + uz /= dpb; // unit vector uz + + //... next and last distance to the attenuation map grip .................. + + if (signx < 0) { + next_x = ((float)vox.icol - (float)0.5) * vox.szcm + vol.x0; + dlast_x = (-vol.Xcmd2 - vox.x) / (ux + EPSILON); + } else { + next_x = ((float)vox.icol + (float)0.5) * vox.szcm + vol.x0; + dlast_x = (vol.Xcmd2 - vox.x) / (ux + EPSILON); + } + + if (signy < 0) { + next_y = ((float)vox.irow - (float)0.5) * vox.szcm + vol.y0; + dlast_y = (-vol.Ycmd2 - vox.y) / (uy + EPSILON); + } else { + next_y = ((float)vox.irow + (float)0.5) * vox.szcm + vol.y0; + dlast_y = (vol.Ycmd2 - vox.y) / (uy + EPSILON); + } + + if (signz < 0) { + next_z = (-(float)0.5) * vox.thcm; + dlast_z = (-vol.Zcmd2 - vox.z) / (uz + EPSILON); + } else { + next_z = ((float)0.5) * vox.thcm; + dlast_z = (vol.Zcmd2 - vox.z) / (uz + EPSILON); + } + + dlast = minim(minim(dlast_x, dlast_y), minim(dlast_z, dpb)); + + // ... distance to next planes avoiding high values for parallel or almost parallel lines ... + + dx = (next_x - vox.x) / (ux + EPSILON); + dy = (next_y - vox.y) / (uy + EPSILON); + dz = (next_z - vox.z) / (uz + EPSILON); + + //... variables initialization ..................................... + + float dant = (float)0.; // previous distance (distance from voxel to the last change of voxel in the attenuation map) + int ni = 0; // number of voxels in the attenuation path + int iv = vox.ip; // voxel index on the attenuation map + + //... loop while attenuation ray is inside the attenuation map + + for (;;) { + + cas = comp_dist(dx, dy, dz, dlast); + + if (cas == 0) { + + attpth->lng = ni; + return; + } + + else { + + if (ni >= attpth->maxlng) + error_weight3d(49, ""); + + attpth->iv[ni] = iv; + + switch (cas) { + case 1: + attpth->dl[ni] = (dx - dant); + dant = dx; + iv += signx; + next_x += vox.szcm * signx; + dx = (next_x - vox.x) / (ux + EPSILON); + break; + case 2: + attpth->dl[ni] = (dy - dant); + dant = dy; + iv += signy * vol.Ncol; + next_y += vox.szcm * signy; + dy = (next_y - vox.y) / (uy + EPSILON); + break; + case 3: + attpth->dl[ni] = (dz - dant); + dant = dz; + iv += signz * vol.Npix; + next_z += vox.thcm * signz; + dz = (next_z - vox.z) / (uz + EPSILON); + break; + default: + error_weight3d(40, ""); + } + ni++; + } + } } //============================================================================= //=== comp_dist =============================================================== //============================================================================= -int comp_dist( float dx, - float dy, - float dz, - float dlast) -{ - int cas; - - if ( dx < dy){ - if ( dx< dz) { - if ( dx > dlast ) cas = 0; // case 0: end of the iteration - else cas = 1; // case 1: minimum value = dx. Next index change in attenuation map is in x direction - } - else { - if ( dz > dlast ) cas = 0; // case 0: end of the iteration - else cas = 3; // case 3: minimum value = dz. Next index change in attenuation map is in z direction - } - } - else{ - if ( dy < dz) { - if ( dy > dlast ) cas = 0; // case 0: end of the iteration - else cas = 2; // case 2: minimum value = dy. Next index change in attenuation map is in y direction - } - else { - if ( dz > dlast ) cas = 0; // case 0: end of the iteration - else cas = 3; // case 3: minimum value = dz. Next index change in attenuation map is in z direction } - } - } - return( cas ); +int +comp_dist(float dx, float dy, float dz, float dlast) { + int cas; + + if (dx < dy) { + if (dx < dz) { + if (dx > dlast) + cas = 0; // case 0: end of the iteration + else + cas = 1; // case 1: minimum value = dx. Next index change in attenuation map is in x direction + } else { + if (dz > dlast) + cas = 0; // case 0: end of the iteration + else + cas = 3; // case 3: minimum value = dz. Next index change in attenuation map is in z direction + } + } else { + if (dy < dz) { + if (dy > dlast) + cas = 0; // case 0: end of the iteration + else + cas = 2; // case 2: minimum value = dy. Next index change in attenuation map is in y direction + } else { + if (dz > dlast) + cas = 0; // case 0: end of the iteration + else + cas = 3; // case 3: minimum value = dz. Next index change in attenuation map is in z direction } + } + } + return (cas); } //============================================================================= //=== cal_att ================================================================= //============================================================================= -float calc_att( const attpth_type *const attpth, const float *const attmap , int nsli ){ - - float att_coef = (float)0.; - int iv; - - for ( int i = 0 ; i < attpth->lng ; i++ ){ - - iv = attpth->iv[ i ] + wmh.vol.Npix * nsli ; - - if ( iv < 0 || iv >= wmh.vol.Nvox ) break; - - att_coef += attpth->dl[ i ] * attmap[ iv ]; - } - - att_coef = exp( -att_coef ); - return( att_coef ); +float +calc_att(const attpth_type* const attpth, const float* const attmap, int nsli) { + + float att_coef = (float)0.; + int iv; + + for (int i = 0; i < attpth->lng; i++) { + + iv = attpth->iv[i] + wmh.vol.Npix * nsli; + + if (iv < 0 || iv >= wmh.vol.Nvox) + break; + + att_coef += attpth->dl[i] * attmap[iv]; + } + + att_coef = exp(-att_coef); + return (att_coef); } //========================================================================== //=== error_weight3d ======================================================= //========================================================================== -void error_weight3d ( int nerr, const string& text ) -{ +void +error_weight3d(int nerr, const string& text) { #if 0 switch(nerr){ case 13: printf( "\n\nError weight3d: wm.NbOS and/or wm.Nvox are negative"); break; @@ -974,24 +985,41 @@ void error_weight3d ( int nerr, const string& text ) exit(0); #else - using stir::error; - switch(nerr){ - case 13: error( "\n\nError weight3d: wm.NbOS and/or wm.Nvox are negative"); break; - case 21: printf( "\n\nError weight3d: undefined collimator. Collimator %s not found\n",text.c_str() ); break; - case 30: printf( "\n\nError weight3d: can not open \n%s for reading\n", text.c_str() ); break; - case 31: printf( "\n\nError weight3d: can not open \n%s for writing\n", text.c_str() ); break; - case 40: error( "\n\nError weight3d: wrong codification in comp_dist function");break; - case 45: error( "\n\nError weight3d: Realloc needed for WM\n"); break; - case 47: error( "\n\nError weight3d: psf length greater than maxszb in calc_psf_bin\n"); break; - case 49: error( "\n\nError weight3d: attpth larger than allocated\n"); break; - case 50: printf( "\n\nError weight3d: No header stored in %s \n",text.c_str() ); break; - default: error( "\n\nError weight3d: unknown error number on error_weight3d()"); - } - - exit(0); -#endif -} - + using stir::error; + switch (nerr) { + case 13: + error("\n\nError weight3d: wm.NbOS and/or wm.Nvox are negative"); + break; + case 21: + printf("\n\nError weight3d: undefined collimator. Collimator %s not found\n", text.c_str()); + break; + case 30: + printf("\n\nError weight3d: can not open \n%s for reading\n", text.c_str()); + break; + case 31: + printf("\n\nError weight3d: can not open \n%s for writing\n", text.c_str()); + break; + case 40: + error("\n\nError weight3d: wrong codification in comp_dist function"); + break; + case 45: + error("\n\nError weight3d: Realloc needed for WM\n"); + break; + case 47: + error("\n\nError weight3d: psf length greater than maxszb in calc_psf_bin\n"); + break; + case 49: + error("\n\nError weight3d: attpth larger than allocated\n"); + break; + case 50: + printf("\n\nError weight3d: No header stored in %s \n", text.c_str()); + break; + default: + error("\n\nError weight3d: unknown error number on error_weight3d()"); + } + exit(0); +#endif +} } // namespace SPECTUB diff --git a/src/recon_buildblock/SqrtHessianRowSum.cxx b/src/recon_buildblock/SqrtHessianRowSum.cxx index b86d938c91..7a788e859d 100644 --- a/src/recon_buildblock/SqrtHessianRowSum.cxx +++ b/src/recon_buildblock/SqrtHessianRowSum.cxx @@ -33,29 +33,22 @@ #include "stir/recon_buildblock/GeneralisedObjectiveFunction.h" #include "stir/unique_ptr.h" - START_NAMESPACE_STIR template -SqrtHessianRowSum:: -SqrtHessianRowSum() -{ +SqrtHessianRowSum::SqrtHessianRowSum() { this->set_defaults(); } template -SqrtHessianRowSum:: -SqrtHessianRowSum(const std::string& filename) -{ +SqrtHessianRowSum::SqrtHessianRowSum(const std::string& filename) { this->set_defaults(); this->parse(filename.c_str()); } template void -SqrtHessianRowSum:: -set_defaults() -{ +SqrtHessianRowSum::set_defaults() { output_file_format_sptr = OutputFileFormat::default_sptr(); output_filename = ""; set_use_approximate_hessian(true); @@ -66,8 +59,7 @@ set_defaults() template void -SqrtHessianRowSum::initialise_keymap() -{ +SqrtHessianRowSum::initialise_keymap() { parser.add_start_key("SqrtHessianRowSum Parameters"); parser.add_key("output filename", &output_filename); parser.add_key("input image filename", &input_image_filename); @@ -79,11 +71,9 @@ SqrtHessianRowSum::initialise_keymap() template bool -SqrtHessianRowSum::post_processing() -{ +SqrtHessianRowSum::post_processing() { - if (input_image_filename.empty()) - { + if (input_image_filename.empty()) { error("Please define input_image_filename."); return true; } @@ -92,96 +82,74 @@ SqrtHessianRowSum::post_processing() } template -GeneralisedObjectiveFunction const& -SqrtHessianRowSum:: -get_objective_function_sptr() -{ - return static_cast&> (*objective_function_sptr); +GeneralisedObjectiveFunction const& +SqrtHessianRowSum::get_objective_function_sptr() { + return static_cast&>(*objective_function_sptr); } template void -SqrtHessianRowSum:: -set_objective_function_sptr(const shared_ptr >& obj_fun) -{ - this->objective_function_sptr = obj_fun; +SqrtHessianRowSum::set_objective_function_sptr(const shared_ptr>& obj_fun) { + this->objective_function_sptr = obj_fun; // it might be that it's already set-up, but we don't know _already_setup = false; } template -shared_ptr -SqrtHessianRowSum:: -get_input_image_sptr() -{ +shared_ptr +SqrtHessianRowSum::get_input_image_sptr() { return input_image_sptr; } template void -SqrtHessianRowSum:: -set_input_image_sptr(shared_ptr const& image_sptr) -{ +SqrtHessianRowSum::set_input_image_sptr(shared_ptr const& image_sptr) { if (_already_setup) _already_setup = input_image_sptr->has_same_characteristics(*image_sptr); input_image_sptr = image_sptr; } template -shared_ptr -SqrtHessianRowSum:: -get_output_target_sptr() -{ +shared_ptr +SqrtHessianRowSum::get_output_target_sptr() { return output_target_sptr; } template bool -SqrtHessianRowSum:: -get_use_approximate_hessian() const -{ +SqrtHessianRowSum::get_use_approximate_hessian() const { return use_approximate_hessian; } template void -SqrtHessianRowSum:: -set_use_approximate_hessian(bool use_approximate) -{ +SqrtHessianRowSum::set_use_approximate_hessian(bool use_approximate) { use_approximate_hessian = use_approximate; } template bool -SqrtHessianRowSum:: -get_compute_with_penalty() const -{ +SqrtHessianRowSum::get_compute_with_penalty() const { return compute_with_penalty; } template void -SqrtHessianRowSum:: -set_compute_with_penalty(bool with_penalty) -{ +SqrtHessianRowSum::set_compute_with_penalty(bool with_penalty) { compute_with_penalty = with_penalty; } template void -SqrtHessianRowSum:: -set_up() -{ - if (is_null_ptr(this->objective_function_sptr)) - { +SqrtHessianRowSum::set_up() { + if (is_null_ptr(this->objective_function_sptr)) { error("objective_function_sptr is null"); } - if (is_null_ptr(this->input_image_sptr)) - { + if (is_null_ptr(this->input_image_sptr)) { error("input_image_sptr is null"); } objective_function_sptr->set_up(input_image_sptr); - output_target_sptr = unique_ptr(input_image_sptr->get_empty_copy()); + output_target_sptr = unique_ptr(input_image_sptr->get_empty_copy()); std::fill(output_target_sptr->begin_all(), output_target_sptr->end_all(), 0.F); _already_setup = true; @@ -189,39 +157,30 @@ set_up() template void -SqrtHessianRowSum:: -process_data() -{ - if (get_use_approximate_hessian()) - { +SqrtHessianRowSum::process_data() { + if (get_use_approximate_hessian()) { // Compute the SqrtHessianRowSum image using the approximate hessian // The input image is used as a template for this method compute_approximate_Hessian_row_sum(); - } - else - { + } else { // Compute the SqrtHessianRowSum image with the full Hessian at the input image estimate. // The input image here is assumed to be the current_image_estimate at the point the Hessian will be computed compute_Hessian_row_sum(); } // Square Root the output of the Hessian_row_sum methods - std::for_each(output_target_sptr->begin_all(), output_target_sptr->end_all(), - [](float& a) { return a=sqrt(a); } ); + std::for_each(output_target_sptr->begin_all(), output_target_sptr->end_all(), [](float& a) { return a = sqrt(a); }); // Save the output - if (!output_filename.empty()) - { - output_file_format_sptr->write_to_file(output_filename, *output_target_sptr); - info("Output image of sqrt Hessian row sum has been computed and saved as " + output_filename + "."); - } + if (!output_filename.empty()) { + output_file_format_sptr->write_to_file(output_filename, *output_target_sptr); + info("Output image of sqrt Hessian row sum has been computed and saved as " + output_filename + "."); + } } template void -SqrtHessianRowSum:: -compute_Hessian_row_sum() -{ +SqrtHessianRowSum::compute_Hessian_row_sum() { if (!_already_setup) error("set_up() needs to be called first"); @@ -232,18 +191,17 @@ compute_Hessian_row_sum() if (get_compute_with_penalty()) objective_function_sptr->accumulate_Hessian_times_input(*output_target_sptr, *input_image_sptr, *ones_image_sptr); else - objective_function_sptr->accumulate_Hessian_times_input_without_penalty(*output_target_sptr, *input_image_sptr, *ones_image_sptr); + objective_function_sptr->accumulate_Hessian_times_input_without_penalty(*output_target_sptr, *input_image_sptr, + *ones_image_sptr); } template void -SqrtHessianRowSum:: -compute_approximate_Hessian_row_sum() -{ +SqrtHessianRowSum::compute_approximate_Hessian_row_sum() { if (!_already_setup) error("set_up() needs to be called first"); - output_target_sptr = unique_ptr(input_image_sptr->get_empty_copy()); + output_target_sptr = unique_ptr(input_image_sptr->get_empty_copy()); std::fill(output_target_sptr->begin_all(), output_target_sptr->end_all(), 0.F); info("Computing the approximate Hessian row sum, this may take a while..."); // Setup image @@ -254,11 +212,10 @@ compute_approximate_Hessian_row_sum() if (get_compute_with_penalty()) info("approximate Hessian row sum: Priors do not have an approximation of the Hessian. Ignoring the prior!"); - objective_function_sptr->add_multiplication_with_approximate_Hessian_without_penalty(*output_target_sptr, - *ones_image_sptr); + objective_function_sptr->add_multiplication_with_approximate_Hessian_without_penalty(*output_target_sptr, *ones_image_sptr); } -template class SqrtHessianRowSum >; -template class SqrtHessianRowSum; -//template class SqrtHessianRowSum; +template class SqrtHessianRowSum>; +template class SqrtHessianRowSum; +// template class SqrtHessianRowSum; END_NAMESPACE_STIR diff --git a/src/recon_buildblock/SymmetryOperation.cxx b/src/recon_buildblock/SymmetryOperation.cxx index d8e12078b1..e67cd815b6 100644 --- a/src/recon_buildblock/SymmetryOperation.cxx +++ b/src/recon_buildblock/SymmetryOperation.cxx @@ -34,18 +34,14 @@ START_NAMESPACE_STIR -void -SymmetryOperation:: -transform_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& lor) const -{ +void +SymmetryOperation::transform_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& lor) const { Bin bin = lor.get_bin(); transform_bin_coordinates(bin); lor.set_bin(bin); - + ProjMatrixElemsForOneBin::iterator element_ptr = lor.begin(); - while (element_ptr != lor.end()) - { + while (element_ptr != lor.end()) { Coordinate3D c(element_ptr->get_coords()); transform_image_coordinates(c); *element_ptr = ProjMatrixElemsForOneBin::value_type(c, element_ptr->get_value()); @@ -53,25 +49,19 @@ transform_proj_matrix_elems_for_one_bin( } } - -void -SymmetryOperation:: -transform_proj_matrix_elems_for_one_densel( - ProjMatrixElemsForOneDensel& probs) const -{ +void +SymmetryOperation::transform_proj_matrix_elems_for_one_densel(ProjMatrixElemsForOneDensel& probs) const { Densel densel = probs.get_densel(); transform_image_coordinates(densel); probs.set_densel(densel); - + ProjMatrixElemsForOneDensel::iterator element_ptr = probs.begin(); - while (element_ptr != probs.end()) - { + while (element_ptr != probs.end()) { Bin c(*element_ptr); transform_bin_coordinates(c); *element_ptr = ProjMatrixElemsForOneDensel::value_type(c); ++element_ptr; } -} - +} END_NAMESPACE_STIR diff --git a/src/recon_buildblock/SymmetryOperations_PET_CartesianGrid.cxx b/src/recon_buildblock/SymmetryOperations_PET_CartesianGrid.cxx index 77223e7e09..d801f11a74 100644 --- a/src/recon_buildblock/SymmetryOperations_PET_CartesianGrid.cxx +++ b/src/recon_buildblock/SymmetryOperations_PET_CartesianGrid.cxx @@ -40,21 +40,16 @@ #include "stir/Coordinate3D.h" #include "stir/recon_buildblock/ProjMatrixElemsForOneDensel.h" - START_NAMESPACE_STIR void -SymmetryOperation_PET_CartesianGrid_z_shift:: -transform_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& lor) const -{ +SymmetryOperation_PET_CartesianGrid_z_shift::transform_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& lor) const { Bin bin = lor.get_bin(); transform_bin_coordinates(bin); lor.set_bin(bin); ProjMatrixElemsForOneBin::iterator element_ptr = lor.begin(); - while (element_ptr != lor.end()) - { + while (element_ptr != lor.end()) { // TODO possibly an explicit z_shift here would be quicker, although a smart compiler should see it Coordinate3D c(element_ptr->get_coords()); self::transform_image_coordinates(c); @@ -63,39 +58,30 @@ transform_proj_matrix_elems_for_one_bin( } } - -void -SymmetryOperation_PET_CartesianGrid_z_shift:: -transform_proj_matrix_elems_for_one_densel( - ProjMatrixElemsForOneDensel& probs) const -{ +void +SymmetryOperation_PET_CartesianGrid_z_shift::transform_proj_matrix_elems_for_one_densel( + ProjMatrixElemsForOneDensel& probs) const { Densel densel = probs.get_densel(); transform_image_coordinates(densel); probs.set_densel(densel); - + ProjMatrixElemsForOneDensel::iterator element_ptr = probs.begin(); - while (element_ptr != probs.end()) - { + while (element_ptr != probs.end()) { Bin c(*element_ptr); self::transform_bin_coordinates(c); *element_ptr = ProjMatrixElemsForOneDensel::value_type(c); ++element_ptr; } -} - +} void -SymmetryOperation_PET_CartesianGrid_swap_xmx_zq:: -transform_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& lor) const -{ +SymmetryOperation_PET_CartesianGrid_swap_xmx_zq::transform_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& lor) const { Bin bin = lor.get_bin(); transform_bin_coordinates(bin); lor.set_bin(bin); ProjMatrixElemsForOneBin::iterator element_ptr = lor.begin(); - while (element_ptr != lor.end()) - { + while (element_ptr != lor.end()) { Coordinate3D c(element_ptr->get_coords()); self::transform_image_coordinates(c); *element_ptr = ProjMatrixElemsForOneBin::value_type(c, element_ptr->get_value()); @@ -103,39 +89,30 @@ transform_proj_matrix_elems_for_one_bin( } } - -void -SymmetryOperation_PET_CartesianGrid_swap_xmx_zq:: -transform_proj_matrix_elems_for_one_densel( - ProjMatrixElemsForOneDensel& probs) const -{ +void +SymmetryOperation_PET_CartesianGrid_swap_xmx_zq::transform_proj_matrix_elems_for_one_densel( + ProjMatrixElemsForOneDensel& probs) const { Densel densel = probs.get_densel(); transform_image_coordinates(densel); probs.set_densel(densel); - + ProjMatrixElemsForOneDensel::iterator element_ptr = probs.begin(); - while (element_ptr != probs.end()) - { + while (element_ptr != probs.end()) { Bin c(*element_ptr); self::transform_bin_coordinates(c); *element_ptr = ProjMatrixElemsForOneDensel::value_type(c); ++element_ptr; } -} - +} void -SymmetryOperation_PET_CartesianGrid_swap_xmy_yx_zq:: -transform_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& lor) const -{ +SymmetryOperation_PET_CartesianGrid_swap_xmy_yx_zq::transform_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& lor) const { Bin bin = lor.get_bin(); transform_bin_coordinates(bin); lor.set_bin(bin); ProjMatrixElemsForOneBin::iterator element_ptr = lor.begin(); - while (element_ptr != lor.end()) - { + while (element_ptr != lor.end()) { Coordinate3D c(element_ptr->get_coords()); self::transform_image_coordinates(c); *element_ptr = ProjMatrixElemsForOneBin::value_type(c, element_ptr->get_value()); @@ -143,39 +120,30 @@ transform_proj_matrix_elems_for_one_bin( } } - -void -SymmetryOperation_PET_CartesianGrid_swap_xmy_yx_zq:: -transform_proj_matrix_elems_for_one_densel( - ProjMatrixElemsForOneDensel& probs) const -{ +void +SymmetryOperation_PET_CartesianGrid_swap_xmy_yx_zq::transform_proj_matrix_elems_for_one_densel( + ProjMatrixElemsForOneDensel& probs) const { Densel densel = probs.get_densel(); transform_image_coordinates(densel); probs.set_densel(densel); - + ProjMatrixElemsForOneDensel::iterator element_ptr = probs.begin(); - while (element_ptr != probs.end()) - { + while (element_ptr != probs.end()) { Bin c(*element_ptr); self::transform_bin_coordinates(c); *element_ptr = ProjMatrixElemsForOneDensel::value_type(c); ++element_ptr; } -} - +} void -SymmetryOperation_PET_CartesianGrid_swap_xy_yx_zq:: -transform_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& lor) const -{ +SymmetryOperation_PET_CartesianGrid_swap_xy_yx_zq::transform_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& lor) const { Bin bin = lor.get_bin(); transform_bin_coordinates(bin); lor.set_bin(bin); ProjMatrixElemsForOneBin::iterator element_ptr = lor.begin(); - while (element_ptr != lor.end()) - { + while (element_ptr != lor.end()) { Coordinate3D c(element_ptr->get_coords()); self::transform_image_coordinates(c); *element_ptr = ProjMatrixElemsForOneBin::value_type(c, element_ptr->get_value()); @@ -183,39 +151,30 @@ transform_proj_matrix_elems_for_one_bin( } } - -void -SymmetryOperation_PET_CartesianGrid_swap_xy_yx_zq:: -transform_proj_matrix_elems_for_one_densel( - ProjMatrixElemsForOneDensel& probs) const -{ +void +SymmetryOperation_PET_CartesianGrid_swap_xy_yx_zq::transform_proj_matrix_elems_for_one_densel( + ProjMatrixElemsForOneDensel& probs) const { Densel densel = probs.get_densel(); transform_image_coordinates(densel); probs.set_densel(densel); - + ProjMatrixElemsForOneDensel::iterator element_ptr = probs.begin(); - while (element_ptr != probs.end()) - { + while (element_ptr != probs.end()) { Bin c(*element_ptr); self::transform_bin_coordinates(c); *element_ptr = ProjMatrixElemsForOneDensel::value_type(c); ++element_ptr; } -} - +} void -SymmetryOperation_PET_CartesianGrid_swap_xmy_yx:: -transform_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& lor) const -{ +SymmetryOperation_PET_CartesianGrid_swap_xmy_yx::transform_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& lor) const { Bin bin = lor.get_bin(); transform_bin_coordinates(bin); lor.set_bin(bin); ProjMatrixElemsForOneBin::iterator element_ptr = lor.begin(); - while (element_ptr != lor.end()) - { + while (element_ptr != lor.end()) { Coordinate3D c(element_ptr->get_coords()); self::transform_image_coordinates(c); *element_ptr = ProjMatrixElemsForOneBin::value_type(c, element_ptr->get_value()); @@ -223,39 +182,30 @@ transform_proj_matrix_elems_for_one_bin( } } - -void -SymmetryOperation_PET_CartesianGrid_swap_xmy_yx:: -transform_proj_matrix_elems_for_one_densel( - ProjMatrixElemsForOneDensel& probs) const -{ +void +SymmetryOperation_PET_CartesianGrid_swap_xmy_yx::transform_proj_matrix_elems_for_one_densel( + ProjMatrixElemsForOneDensel& probs) const { Densel densel = probs.get_densel(); transform_image_coordinates(densel); probs.set_densel(densel); - + ProjMatrixElemsForOneDensel::iterator element_ptr = probs.begin(); - while (element_ptr != probs.end()) - { + while (element_ptr != probs.end()) { Bin c(*element_ptr); self::transform_bin_coordinates(c); *element_ptr = ProjMatrixElemsForOneDensel::value_type(c); ++element_ptr; } -} - +} void -SymmetryOperation_PET_CartesianGrid_swap_xy_yx:: -transform_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& lor) const -{ +SymmetryOperation_PET_CartesianGrid_swap_xy_yx::transform_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& lor) const { Bin bin = lor.get_bin(); transform_bin_coordinates(bin); lor.set_bin(bin); ProjMatrixElemsForOneBin::iterator element_ptr = lor.begin(); - while (element_ptr != lor.end()) - { + while (element_ptr != lor.end()) { Coordinate3D c(element_ptr->get_coords()); self::transform_image_coordinates(c); *element_ptr = ProjMatrixElemsForOneBin::value_type(c, element_ptr->get_value()); @@ -263,39 +213,30 @@ transform_proj_matrix_elems_for_one_bin( } } - -void -SymmetryOperation_PET_CartesianGrid_swap_xy_yx:: -transform_proj_matrix_elems_for_one_densel( - ProjMatrixElemsForOneDensel& probs) const -{ +void +SymmetryOperation_PET_CartesianGrid_swap_xy_yx::transform_proj_matrix_elems_for_one_densel( + ProjMatrixElemsForOneDensel& probs) const { Densel densel = probs.get_densel(); transform_image_coordinates(densel); probs.set_densel(densel); - + ProjMatrixElemsForOneDensel::iterator element_ptr = probs.begin(); - while (element_ptr != probs.end()) - { + while (element_ptr != probs.end()) { Bin c(*element_ptr); self::transform_bin_coordinates(c); *element_ptr = ProjMatrixElemsForOneDensel::value_type(c); ++element_ptr; } -} - +} void -SymmetryOperation_PET_CartesianGrid_swap_xmx:: -transform_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& lor) const -{ +SymmetryOperation_PET_CartesianGrid_swap_xmx::transform_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& lor) const { Bin bin = lor.get_bin(); transform_bin_coordinates(bin); lor.set_bin(bin); ProjMatrixElemsForOneBin::iterator element_ptr = lor.begin(); - while (element_ptr != lor.end()) - { + while (element_ptr != lor.end()) { Coordinate3D c(element_ptr->get_coords()); self::transform_image_coordinates(c); *element_ptr = ProjMatrixElemsForOneBin::value_type(c, element_ptr->get_value()); @@ -303,39 +244,30 @@ transform_proj_matrix_elems_for_one_bin( } } - -void -SymmetryOperation_PET_CartesianGrid_swap_xmx:: -transform_proj_matrix_elems_for_one_densel( - ProjMatrixElemsForOneDensel& probs) const -{ +void +SymmetryOperation_PET_CartesianGrid_swap_xmx::transform_proj_matrix_elems_for_one_densel( + ProjMatrixElemsForOneDensel& probs) const { Densel densel = probs.get_densel(); transform_image_coordinates(densel); probs.set_densel(densel); - + ProjMatrixElemsForOneDensel::iterator element_ptr = probs.begin(); - while (element_ptr != probs.end()) - { + while (element_ptr != probs.end()) { Bin c(*element_ptr); self::transform_bin_coordinates(c); *element_ptr = ProjMatrixElemsForOneDensel::value_type(c); ++element_ptr; } -} - +} void -SymmetryOperation_PET_CartesianGrid_swap_ymy:: -transform_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& lor) const -{ +SymmetryOperation_PET_CartesianGrid_swap_ymy::transform_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& lor) const { Bin bin = lor.get_bin(); transform_bin_coordinates(bin); lor.set_bin(bin); ProjMatrixElemsForOneBin::iterator element_ptr = lor.begin(); - while (element_ptr != lor.end()) - { + while (element_ptr != lor.end()) { Coordinate3D c(element_ptr->get_coords()); self::transform_image_coordinates(c); *element_ptr = ProjMatrixElemsForOneBin::value_type(c, element_ptr->get_value()); @@ -343,35 +275,26 @@ transform_proj_matrix_elems_for_one_bin( } } - -void -SymmetryOperation_PET_CartesianGrid_swap_ymy:: -transform_proj_matrix_elems_for_one_densel( - ProjMatrixElemsForOneDensel& probs) const -{ +void +SymmetryOperation_PET_CartesianGrid_swap_ymy::transform_proj_matrix_elems_for_one_densel( + ProjMatrixElemsForOneDensel& probs) const { Densel densel = probs.get_densel(); transform_image_coordinates(densel); probs.set_densel(densel); - + ProjMatrixElemsForOneDensel::iterator element_ptr = probs.begin(); - while (element_ptr != probs.end()) - { + while (element_ptr != probs.end()) { Bin c(*element_ptr); self::transform_bin_coordinates(c); *element_ptr = ProjMatrixElemsForOneDensel::value_type(c); ++element_ptr; } -} - +} void -SymmetryOperation_PET_CartesianGrid_swap_zq:: -transform_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& lor) const -{ +SymmetryOperation_PET_CartesianGrid_swap_zq::transform_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& lor) const { ProjMatrixElemsForOneBin::iterator element_ptr = lor.begin(); - while (element_ptr != lor.end()) - { + while (element_ptr != lor.end()) { Coordinate3D c(element_ptr->get_coords()); self::transform_image_coordinates(c); *element_ptr = ProjMatrixElemsForOneBin::value_type(c, element_ptr->get_value()); @@ -379,39 +302,31 @@ transform_proj_matrix_elems_for_one_bin( } } - -void -SymmetryOperation_PET_CartesianGrid_swap_zq:: -transform_proj_matrix_elems_for_one_densel( - ProjMatrixElemsForOneDensel& probs) const -{ +void +SymmetryOperation_PET_CartesianGrid_swap_zq::transform_proj_matrix_elems_for_one_densel( + ProjMatrixElemsForOneDensel& probs) const { Densel densel = probs.get_densel(); transform_image_coordinates(densel); probs.set_densel(densel); - + ProjMatrixElemsForOneDensel::iterator element_ptr = probs.begin(); - while (element_ptr != probs.end()) - { + while (element_ptr != probs.end()) { Bin c(*element_ptr); self::transform_bin_coordinates(c); *element_ptr = ProjMatrixElemsForOneDensel::value_type(c); ++element_ptr; } -} - +} void -SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy_zq:: -transform_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& lor) const -{ +SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy_zq::transform_proj_matrix_elems_for_one_bin( + ProjMatrixElemsForOneBin& lor) const { Bin bin = lor.get_bin(); transform_bin_coordinates(bin); lor.set_bin(bin); ProjMatrixElemsForOneBin::iterator element_ptr = lor.begin(); - while (element_ptr != lor.end()) - { + while (element_ptr != lor.end()) { Coordinate3D c(element_ptr->get_coords()); self::transform_image_coordinates(c); *element_ptr = ProjMatrixElemsForOneBin::value_type(c, element_ptr->get_value()); @@ -419,39 +334,30 @@ transform_proj_matrix_elems_for_one_bin( } } - -void -SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy_zq:: -transform_proj_matrix_elems_for_one_densel( - ProjMatrixElemsForOneDensel& probs) const -{ +void +SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy_zq::transform_proj_matrix_elems_for_one_densel( + ProjMatrixElemsForOneDensel& probs) const { Densel densel = probs.get_densel(); transform_image_coordinates(densel); probs.set_densel(densel); - + ProjMatrixElemsForOneDensel::iterator element_ptr = probs.begin(); - while (element_ptr != probs.end()) - { + while (element_ptr != probs.end()) { Bin c(*element_ptr); self::transform_bin_coordinates(c); *element_ptr = ProjMatrixElemsForOneDensel::value_type(c); ++element_ptr; } -} - +} void -SymmetryOperation_PET_CartesianGrid_swap_xy_ymx_zq:: -transform_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& lor) const -{ +SymmetryOperation_PET_CartesianGrid_swap_xy_ymx_zq::transform_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& lor) const { Bin bin = lor.get_bin(); transform_bin_coordinates(bin); lor.set_bin(bin); ProjMatrixElemsForOneBin::iterator element_ptr = lor.begin(); - while (element_ptr != lor.end()) - { + while (element_ptr != lor.end()) { Coordinate3D c(element_ptr->get_coords()); self::transform_image_coordinates(c); *element_ptr = ProjMatrixElemsForOneBin::value_type(c, element_ptr->get_value()); @@ -459,39 +365,30 @@ transform_proj_matrix_elems_for_one_bin( } } - -void -SymmetryOperation_PET_CartesianGrid_swap_xy_ymx_zq:: -transform_proj_matrix_elems_for_one_densel( - ProjMatrixElemsForOneDensel& probs) const -{ +void +SymmetryOperation_PET_CartesianGrid_swap_xy_ymx_zq::transform_proj_matrix_elems_for_one_densel( + ProjMatrixElemsForOneDensel& probs) const { Densel densel = probs.get_densel(); transform_image_coordinates(densel); probs.set_densel(densel); - + ProjMatrixElemsForOneDensel::iterator element_ptr = probs.begin(); - while (element_ptr != probs.end()) - { + while (element_ptr != probs.end()) { Bin c(*element_ptr); self::transform_bin_coordinates(c); *element_ptr = ProjMatrixElemsForOneDensel::value_type(c); ++element_ptr; } -} - +} void -SymmetryOperation_PET_CartesianGrid_swap_xy_ymx:: -transform_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& lor) const -{ +SymmetryOperation_PET_CartesianGrid_swap_xy_ymx::transform_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& lor) const { Bin bin = lor.get_bin(); transform_bin_coordinates(bin); lor.set_bin(bin); ProjMatrixElemsForOneBin::iterator element_ptr = lor.begin(); - while (element_ptr != lor.end()) - { + while (element_ptr != lor.end()) { Coordinate3D c(element_ptr->get_coords()); self::transform_image_coordinates(c); *element_ptr = ProjMatrixElemsForOneBin::value_type(c, element_ptr->get_value()); @@ -499,39 +396,30 @@ transform_proj_matrix_elems_for_one_bin( } } - -void -SymmetryOperation_PET_CartesianGrid_swap_xy_ymx:: -transform_proj_matrix_elems_for_one_densel( - ProjMatrixElemsForOneDensel& probs) const -{ +void +SymmetryOperation_PET_CartesianGrid_swap_xy_ymx::transform_proj_matrix_elems_for_one_densel( + ProjMatrixElemsForOneDensel& probs) const { Densel densel = probs.get_densel(); transform_image_coordinates(densel); probs.set_densel(densel); - + ProjMatrixElemsForOneDensel::iterator element_ptr = probs.begin(); - while (element_ptr != probs.end()) - { + while (element_ptr != probs.end()) { Bin c(*element_ptr); self::transform_bin_coordinates(c); *element_ptr = ProjMatrixElemsForOneDensel::value_type(c); ++element_ptr; } -} - +} void -SymmetryOperation_PET_CartesianGrid_swap_xmy_ymx:: -transform_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& lor) const -{ +SymmetryOperation_PET_CartesianGrid_swap_xmy_ymx::transform_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& lor) const { Bin bin = lor.get_bin(); transform_bin_coordinates(bin); lor.set_bin(bin); ProjMatrixElemsForOneBin::iterator element_ptr = lor.begin(); - while (element_ptr != lor.end()) - { + while (element_ptr != lor.end()) { Coordinate3D c(element_ptr->get_coords()); self::transform_image_coordinates(c); *element_ptr = ProjMatrixElemsForOneBin::value_type(c, element_ptr->get_value()); @@ -539,39 +427,30 @@ transform_proj_matrix_elems_for_one_bin( } } - -void -SymmetryOperation_PET_CartesianGrid_swap_xmy_ymx:: -transform_proj_matrix_elems_for_one_densel( - ProjMatrixElemsForOneDensel& probs) const -{ +void +SymmetryOperation_PET_CartesianGrid_swap_xmy_ymx::transform_proj_matrix_elems_for_one_densel( + ProjMatrixElemsForOneDensel& probs) const { Densel densel = probs.get_densel(); transform_image_coordinates(densel); probs.set_densel(densel); - + ProjMatrixElemsForOneDensel::iterator element_ptr = probs.begin(); - while (element_ptr != probs.end()) - { + while (element_ptr != probs.end()) { Bin c(*element_ptr); self::transform_bin_coordinates(c); *element_ptr = ProjMatrixElemsForOneDensel::value_type(c); ++element_ptr; } -} - +} void -SymmetryOperation_PET_CartesianGrid_swap_ymy_zq:: -transform_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& lor) const -{ +SymmetryOperation_PET_CartesianGrid_swap_ymy_zq::transform_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& lor) const { Bin bin = lor.get_bin(); transform_bin_coordinates(bin); lor.set_bin(bin); ProjMatrixElemsForOneBin::iterator element_ptr = lor.begin(); - while (element_ptr != lor.end()) - { + while (element_ptr != lor.end()) { Coordinate3D c(element_ptr->get_coords()); self::transform_image_coordinates(c); *element_ptr = ProjMatrixElemsForOneBin::value_type(c, element_ptr->get_value()); @@ -579,39 +458,30 @@ transform_proj_matrix_elems_for_one_bin( } } - -void -SymmetryOperation_PET_CartesianGrid_swap_ymy_zq:: -transform_proj_matrix_elems_for_one_densel( - ProjMatrixElemsForOneDensel& probs) const -{ +void +SymmetryOperation_PET_CartesianGrid_swap_ymy_zq::transform_proj_matrix_elems_for_one_densel( + ProjMatrixElemsForOneDensel& probs) const { Densel densel = probs.get_densel(); transform_image_coordinates(densel); probs.set_densel(densel); - + ProjMatrixElemsForOneDensel::iterator element_ptr = probs.begin(); - while (element_ptr != probs.end()) - { + while (element_ptr != probs.end()) { Bin c(*element_ptr); self::transform_bin_coordinates(c); *element_ptr = ProjMatrixElemsForOneDensel::value_type(c); ++element_ptr; } -} - +} void -SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy:: -transform_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& lor) const -{ +SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy::transform_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin& lor) const { Bin bin = lor.get_bin(); transform_bin_coordinates(bin); lor.set_bin(bin); ProjMatrixElemsForOneBin::iterator element_ptr = lor.begin(); - while (element_ptr != lor.end()) - { + while (element_ptr != lor.end()) { Coordinate3D c(element_ptr->get_coords()); self::transform_image_coordinates(c); *element_ptr = ProjMatrixElemsForOneBin::value_type(c, element_ptr->get_value()); @@ -619,38 +489,31 @@ transform_proj_matrix_elems_for_one_bin( } } -void -SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy:: -transform_proj_matrix_elems_for_one_densel( - ProjMatrixElemsForOneDensel& probs) const -{ +void +SymmetryOperation_PET_CartesianGrid_swap_xmx_ymy::transform_proj_matrix_elems_for_one_densel( + ProjMatrixElemsForOneDensel& probs) const { Densel densel = probs.get_densel(); transform_image_coordinates(densel); probs.set_densel(densel); - + ProjMatrixElemsForOneDensel::iterator element_ptr = probs.begin(); - while (element_ptr != probs.end()) - { + while (element_ptr != probs.end()) { Bin c(*element_ptr); self::transform_bin_coordinates(c); *element_ptr = ProjMatrixElemsForOneDensel::value_type(c); ++element_ptr; } -} - +} void -SymmetryOperation_PET_CartesianGrid_swap_xmy_ymx_zq:: -transform_proj_matrix_elems_for_one_bin( - ProjMatrixElemsForOneBin& lor) const -{ +SymmetryOperation_PET_CartesianGrid_swap_xmy_ymx_zq::transform_proj_matrix_elems_for_one_bin( + ProjMatrixElemsForOneBin& lor) const { Bin bin = lor.get_bin(); transform_bin_coordinates(bin); lor.set_bin(bin); ProjMatrixElemsForOneBin::iterator element_ptr = lor.begin(); - while (element_ptr != lor.end()) - { + while (element_ptr != lor.end()) { Coordinate3D c(element_ptr->get_coords()); self::transform_image_coordinates(c); *element_ptr = ProjMatrixElemsForOneBin::value_type(c, element_ptr->get_value()); @@ -658,26 +521,20 @@ transform_proj_matrix_elems_for_one_bin( } } - - -void -SymmetryOperation_PET_CartesianGrid_swap_xmy_ymx_zq:: -transform_proj_matrix_elems_for_one_densel( - ProjMatrixElemsForOneDensel& probs) const -{ +void +SymmetryOperation_PET_CartesianGrid_swap_xmy_ymx_zq::transform_proj_matrix_elems_for_one_densel( + ProjMatrixElemsForOneDensel& probs) const { Densel densel = probs.get_densel(); transform_image_coordinates(densel); probs.set_densel(densel); - + ProjMatrixElemsForOneDensel::iterator element_ptr = probs.begin(); - while (element_ptr != probs.end()) - { + while (element_ptr != probs.end()) { Bin c(*element_ptr); self::transform_bin_coordinates(c); *element_ptr = ProjMatrixElemsForOneDensel::value_type(c); ++element_ptr; } -} - +} END_NAMESPACE_STIR diff --git a/src/recon_buildblock/TrivialBinNormalisation.cxx b/src/recon_buildblock/TrivialBinNormalisation.cxx index d600f1d073..f5a8813a48 100644 --- a/src/recon_buildblock/TrivialBinNormalisation.cxx +++ b/src/recon_buildblock/TrivialBinNormalisation.cxx @@ -29,8 +29,6 @@ START_NAMESPACE_STIR -const char * const -TrivialBinNormalisation::registered_name = "None"; +const char* const TrivialBinNormalisation::registered_name = "None"; END_NAMESPACE_STIR - diff --git a/src/recon_buildblock/TrivialDataSymmetriesForBins.cxx b/src/recon_buildblock/TrivialDataSymmetriesForBins.cxx index 92ffbeeb6e..0101c6e7da 100644 --- a/src/recon_buildblock/TrivialDataSymmetriesForBins.cxx +++ b/src/recon_buildblock/TrivialDataSymmetriesForBins.cxx @@ -19,7 +19,7 @@ /*! \file \ingroup symmetries - \brief non-inline implementations for class + \brief non-inline implementations for class stir::TrivialDataSymmetriesForBins \author Kris Thielemans @@ -32,132 +32,91 @@ using std::vector; START_NAMESPACE_STIR -TrivialDataSymmetriesForBins:: -TrivialDataSymmetriesForBins -( - const shared_ptr& proj_data_info_ptr) - : DataSymmetriesForBins(proj_data_info_ptr) -{ -} - +TrivialDataSymmetriesForBins::TrivialDataSymmetriesForBins(const shared_ptr& proj_data_info_ptr) + : DataSymmetriesForBins(proj_data_info_ptr) {} #ifndef STIR_NO_COVARIANT_RETURN_TYPES - TrivialDataSymmetriesForBins * +TrivialDataSymmetriesForBins* #else - DataSymmetriesForViewSegmentNumbers * +DataSymmetriesForViewSegmentNumbers* #endif -TrivialDataSymmetriesForBins:: -clone() const -{ +TrivialDataSymmetriesForBins::clone() const { return new TrivialDataSymmetriesForBins(*this); } int -TrivialDataSymmetriesForBins::num_related_bins(const Bin& b) const -{ +TrivialDataSymmetriesForBins::num_related_bins(const Bin& b) const { return 1; } -bool TrivialDataSymmetriesForBins::find_basic_bin(Bin& b) const -{ +bool +TrivialDataSymmetriesForBins::find_basic_bin(Bin& b) const { return false; } - bool -TrivialDataSymmetriesForBins:: -is_basic(const Bin& b) const -{ +TrivialDataSymmetriesForBins::is_basic(const Bin& b) const { return true; } - void -TrivialDataSymmetriesForBins:: -get_related_bins_factorised(vector& axtan_pos_nums, const Bin& b, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num) const -{ - if (b.axial_pos_num() >= min_axial_pos_num && - b.axial_pos_num() <= max_axial_pos_num && - b.tangential_pos_num() >= min_tangential_pos_num && - b.tangential_pos_num() <= max_tangential_pos_num) - { - axtan_pos_nums.resize(1); - axtan_pos_nums[0] = - AxTangPosNumbers(b.axial_pos_num(), - b.tangential_pos_num()); - } - else - { - axtan_pos_nums.resize(0); - } +TrivialDataSymmetriesForBins::get_related_bins_factorised(vector& axtan_pos_nums, const Bin& b, + const int min_axial_pos_num, const int max_axial_pos_num, + const int min_tangential_pos_num, + const int max_tangential_pos_num) const { + if (b.axial_pos_num() >= min_axial_pos_num && b.axial_pos_num() <= max_axial_pos_num && + b.tangential_pos_num() >= min_tangential_pos_num && b.tangential_pos_num() <= max_tangential_pos_num) { + axtan_pos_nums.resize(1); + axtan_pos_nums[0] = AxTangPosNumbers(b.axial_pos_num(), b.tangential_pos_num()); + } else { + axtan_pos_nums.resize(0); + } } void -TrivialDataSymmetriesForBins:: -get_related_bins(vector& rel_b, const Bin& b, - const int min_axial_pos_num, const int max_axial_pos_num, - const int min_tangential_pos_num, const int max_tangential_pos_num, - const int min_timing_pos_num, const int max_timing_pos_num) const -{ - if (b.axial_pos_num() >= min_axial_pos_num && - b.axial_pos_num() <= max_axial_pos_num && - b.tangential_pos_num() >= min_tangential_pos_num && - b.tangential_pos_num() <= max_tangential_pos_num && - b.timing_pos_num() >= min_timing_pos_num && - b.timing_pos_num() <= max_timing_pos_num) - { - rel_b.resize(1); - rel_b[0] = b; - } - else - { - rel_b.resize(0); - } +TrivialDataSymmetriesForBins::get_related_bins(vector& rel_b, const Bin& b, const int min_axial_pos_num, + const int max_axial_pos_num, const int min_tangential_pos_num, + const int max_tangential_pos_num, const int min_timing_pos_num, + const int max_timing_pos_num) const { + if (b.axial_pos_num() >= min_axial_pos_num && b.axial_pos_num() <= max_axial_pos_num && + b.tangential_pos_num() >= min_tangential_pos_num && b.tangential_pos_num() <= max_tangential_pos_num && + b.timing_pos_num() >= min_timing_pos_num && b.timing_pos_num() <= max_timing_pos_num) { + rel_b.resize(1); + rel_b[0] = b; + } else { + rel_b.resize(0); + } } unique_ptr -TrivialDataSymmetriesForBins:: -find_symmetry_operation_from_basic_bin(Bin&) const -{ +TrivialDataSymmetriesForBins::find_symmetry_operation_from_basic_bin(Bin&) const { return unique_ptr(new TrivialSymmetryOperation); } unique_ptr -TrivialDataSymmetriesForBins:: -find_symmetry_operation_from_basic_view_segment_numbers(ViewSegmentNumbers& vs) const -{ +TrivialDataSymmetriesForBins::find_symmetry_operation_from_basic_view_segment_numbers(ViewSegmentNumbers& vs) const { return unique_ptr(new TrivialSymmetryOperation); } void -TrivialDataSymmetriesForBins:: -get_related_view_segment_numbers(vector& all, const ViewSegmentNumbers& v_s) const -{ +TrivialDataSymmetriesForBins::get_related_view_segment_numbers(vector& all, + const ViewSegmentNumbers& v_s) const { all.resize(1); all[0] = v_s; } - int -TrivialDataSymmetriesForBins:: -num_related_view_segment_numbers(const ViewSegmentNumbers&) const -{ +TrivialDataSymmetriesForBins::num_related_view_segment_numbers(const ViewSegmentNumbers&) const { return 1; } bool -TrivialDataSymmetriesForBins:: -find_basic_view_segment_numbers(ViewSegmentNumbers&) const -{ +TrivialDataSymmetriesForBins::find_basic_view_segment_numbers(ViewSegmentNumbers&) const { return false; } -bool -TrivialDataSymmetriesForBins:: -blindly_equals(const root_type * const) const -{ +bool +TrivialDataSymmetriesForBins::blindly_equals(const root_type* const) const { return true; } diff --git a/src/recon_buildblock/distributable.cxx b/src/recon_buildblock/distributable.cxx index b2095a1e6e..e530bd5d4a 100644 --- a/src/recon_buildblock/distributable.cxx +++ b/src/recon_buildblock/distributable.cxx @@ -23,8 +23,8 @@ \brief Implementation of stir::distributable_computation() and related functions - \author Kris Thielemans - \author Alexey Zverovich + \author Kris Thielemans + \author Alexey Zverovich \author Matthew Jacobson \author PARAPET project \author Tobias Beisel @@ -32,7 +32,7 @@ /* Modification history: KT 30/05/2002 get rid of dependence on specific symmetries (i.e. views up to 45 degrees) - + Tobias Beisel 16/06/2007 added MPI support added function for Cached MPI support @@ -60,319 +60,261 @@ #include #ifdef STIR_MPI -#include "stir/recon_buildblock/distributableMPICacheEnabled.h" -#include "stir/recon_buildblock/distributed_functions.h" -#include "stir/recon_buildblock/distributed_test_functions.h" -#include "stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.h" // needed for RPC functions +# include "stir/recon_buildblock/distributableMPICacheEnabled.h" +# include "stir/recon_buildblock/distributed_functions.h" +# include "stir/recon_buildblock/distributed_test_functions.h" +# include "stir/recon_buildblock/PoissonLogLikelihoodWithLinearModelForMeanAndProjData.h" // needed for RPC functions #endif #ifdef STIR_OPENMP # ifdef STIR_MPI # error Cannot use both OPENMP and MP # endif -#include +# include #endif #include "stir/num_threads.h" START_NAMESPACE_STIR -/* WARNING: the sequence of steps here has to match what is on the receiving end +/* WARNING: the sequence of steps here has to match what is on the receiving end in DistributedWorker */ -void setup_distributable_computation( - const shared_ptr& proj_pair_sptr, - const shared_ptr& exam_info_sptr, - const shared_ptr proj_data_info_sptr, - const shared_ptr >& target_sptr, - const bool zero_seg0_end_planes, - const bool distributed_cache_enabled) -{ +void +setup_distributable_computation(const shared_ptr& proj_pair_sptr, + const shared_ptr& exam_info_sptr, + const shared_ptr proj_data_info_sptr, + const shared_ptr>& target_sptr, + const bool zero_seg0_end_planes, const bool distributed_cache_enabled) { set_num_threads(); #ifdef STIR_OPENMP - info(boost::format("Using distributable_computation with %d threads on %d processors.") - % omp_get_max_threads() % omp_get_num_procs()); + info(boost::format("Using distributable_computation with %d threads on %d processors.") % omp_get_max_threads() % + omp_get_num_procs()); #endif #ifdef STIR_MPI distributed::first_iteration = true; - - //broadcast type of computation (currently only 1 available) + + // broadcast type of computation (currently only 1 available) distributed::send_int_value(task_setup_distributable_computation, -1); - //broadcast zero_seg0_end_planes + // broadcast zero_seg0_end_planes distributed::send_bool_value(zero_seg0_end_planes, -1, -1); - - //broadcast target_sptr + + // broadcast target_sptr distributed::send_image_parameters(target_sptr.get(), -1, -1); distributed::send_image_estimate(target_sptr.get(), -1); - - //sending Data_info + + // sending Data_info distributed::send_exam_and_proj_data_info(*exam_info_sptr, *proj_data_info_sptr, -1); - //send projector pair + // send projector pair distributed::send_projectors(proj_pair_sptr, -1); - //send configuration values for distributed computation + // send configuration values for distributed computation int configurations[4]; - configurations[0]=distributed::test?1:0; - configurations[1]=distributed::test_send_receive_times?1:0; - configurations[2]=distributed::rpc_time?1:0; - configurations[3]=distributed_cache_enabled?1:0; + configurations[0] = distributed::test ? 1 : 0; + configurations[1] = distributed::test_send_receive_times ? 1 : 0; + configurations[2] = distributed::rpc_time ? 1 : 0; + configurations[3] = distributed_cache_enabled ? 1 : 0; distributed::send_int_values(configurations, 4, distributed::STIR_MPI_CONF_TAG, -1); - + distributed::send_double_values(&distributed::min_threshold, 1, distributed::STIR_MPI_CONF_TAG, -1); - -#ifndef NDEBUG - //test sending parameter_info - if (distributed::test) + +# ifndef NDEBUG + // test sending parameter_info + if (distributed::test) distributed::test_parameter_info_master(proj_pair_sptr->stir::ParsingObject::parameter_info(), 1, "projector_pair_ptr"); -#endif +# endif #endif // STIR_MPI } -void end_distributable_computation() -{ +void +end_distributable_computation() { #ifdef STIR_MPI - int my_rank; - MPI_Comm_rank(MPI_COMM_WORLD, &my_rank) ; /*Gets the rank of the Processor*/ - if (my_rank==0) - { - //broadcast end of processing notification - distributed::send_int_value(task_stop_processing, -1); - } + int my_rank; + MPI_Comm_rank(MPI_COMM_WORLD, &my_rank); /*Gets the rank of the Processor*/ + if (my_rank == 0) { + // broadcast end of processing notification + distributed::send_int_value(task_stop_processing, -1); + } #endif - } template static void -zero_end_sinograms(ViewgramsPtr viewgrams_ptr) -{ - if (!is_null_ptr(viewgrams_ptr)) - { - const int min_ax_pos_num = viewgrams_ptr->get_min_axial_pos_num(); - const int max_ax_pos_num = viewgrams_ptr->get_max_axial_pos_num(); - for (RelatedViewgrams::iterator r_viewgrams_iter = viewgrams_ptr->begin(); - r_viewgrams_iter != viewgrams_ptr->end(); - ++r_viewgrams_iter) - { - (*r_viewgrams_iter)[min_ax_pos_num].fill(0); - (*r_viewgrams_iter)[max_ax_pos_num].fill(0); - } +zero_end_sinograms(ViewgramsPtr viewgrams_ptr) { + if (!is_null_ptr(viewgrams_ptr)) { + const int min_ax_pos_num = viewgrams_ptr->get_min_axial_pos_num(); + const int max_ax_pos_num = viewgrams_ptr->get_max_axial_pos_num(); + for (RelatedViewgrams::iterator r_viewgrams_iter = viewgrams_ptr->begin(); r_viewgrams_iter != viewgrams_ptr->end(); + ++r_viewgrams_iter) { + (*r_viewgrams_iter)[min_ax_pos_num].fill(0); + (*r_viewgrams_iter)[max_ax_pos_num].fill(0); } + } } -static -void get_viewgrams(shared_ptr >& y, - shared_ptr >& additive_binwise_correction_viewgrams, - shared_ptr >& mult_viewgrams_sptr, - const shared_ptr& proj_dat_ptr, - const bool read_from_proj_dat, - const bool zero_seg0_end_planes, - const shared_ptr& binwise_correction, - const shared_ptr& normalisation_sptr, - const double start_time_of_frame, - const double end_time_of_frame, - const shared_ptr& symmetries_ptr, - const ViewSegmentNumbers& view_segment_num, - const int timing_pos_num - ) -{ - if (!is_null_ptr(binwise_correction)) - { +static void +get_viewgrams(shared_ptr>& y, shared_ptr>& additive_binwise_correction_viewgrams, + shared_ptr>& mult_viewgrams_sptr, const shared_ptr& proj_dat_ptr, + const bool read_from_proj_dat, const bool zero_seg0_end_planes, const shared_ptr& binwise_correction, + const shared_ptr& normalisation_sptr, const double start_time_of_frame, + const double end_time_of_frame, const shared_ptr& symmetries_ptr, + const ViewSegmentNumbers& view_segment_num, const int timing_pos_num) { + if (!is_null_ptr(binwise_correction)) { #ifdef STIR_OPENMP -#pragma omp critical(ADDSINO) +# pragma omp critical(ADDSINO) #endif -#if !defined(_MSC_VER) || _MSC_VER>1300 - additive_binwise_correction_viewgrams.reset( - new RelatedViewgrams - (binwise_correction->get_related_viewgrams(view_segment_num, symmetries_ptr, false, timing_pos_num))); +#if !defined(_MSC_VER) || _MSC_VER > 1300 + additive_binwise_correction_viewgrams.reset(new RelatedViewgrams( + binwise_correction->get_related_viewgrams(view_segment_num, symmetries_ptr, false, timing_pos_num))); #else - RelatedViewgrams tmp(binwise_correction-> - get_related_viewgrams(view_segment_num, symmetries_ptr, false, timing_pos_num)); - additive_binwise_correction_viewgrams.reset(new RelatedViewgrams(tmp)); + RelatedViewgrams tmp( + binwise_correction->get_related_viewgrams(view_segment_num, symmetries_ptr, false, timing_pos_num)); + additive_binwise_correction_viewgrams.reset(new RelatedViewgrams(tmp)); #endif - } - - if (read_from_proj_dat) - { + } + + if (read_from_proj_dat) { #ifdef STIR_OPENMP -#pragma omp critical(VIEW) +# pragma omp critical(VIEW) #endif -#if !defined(_MSC_VER) || _MSC_VER>1300 - y.reset(new RelatedViewgrams - (proj_dat_ptr->get_related_viewgrams(view_segment_num, symmetries_ptr, false, timing_pos_num))); +#if !defined(_MSC_VER) || _MSC_VER > 1300 + y.reset(new RelatedViewgrams( + proj_dat_ptr->get_related_viewgrams(view_segment_num, symmetries_ptr, false, timing_pos_num))); #else - // workaround VC++ 6.0 bug - RelatedViewgrams tmp(proj_dat_ptr-> - get_related_viewgrams(view_segment_num, symmetries_ptr, false, timing_pos_num)); - y.reset(new RelatedViewgrams(tmp)); -#endif - } - else - { - y.reset(new RelatedViewgrams - (proj_dat_ptr->get_empty_related_viewgrams(view_segment_num, symmetries_ptr, false, timing_pos_num))); - } + // workaround VC++ 6.0 bug + RelatedViewgrams tmp(proj_dat_ptr->get_related_viewgrams(view_segment_num, symmetries_ptr, false, timing_pos_num)); + y.reset(new RelatedViewgrams(tmp)); +#endif + } else { + y.reset(new RelatedViewgrams( + proj_dat_ptr->get_empty_related_viewgrams(view_segment_num, symmetries_ptr, false, timing_pos_num))); + } // multiplicative correction - if (!is_null_ptr(normalisation_sptr) && !normalisation_sptr->is_trivial()) - { - mult_viewgrams_sptr.reset( - new RelatedViewgrams(proj_dat_ptr->get_empty_related_viewgrams(view_segment_num, symmetries_ptr, false, timing_pos_num))); - mult_viewgrams_sptr->fill(1.F); + if (!is_null_ptr(normalisation_sptr) && !normalisation_sptr->is_trivial()) { + mult_viewgrams_sptr.reset(new RelatedViewgrams( + proj_dat_ptr->get_empty_related_viewgrams(view_segment_num, symmetries_ptr, false, timing_pos_num))); + mult_viewgrams_sptr->fill(1.F); #ifdef STIR_OPENMP -#pragma omp critical(MULT) +# pragma omp critical(MULT) #endif - normalisation_sptr->undo(*mult_viewgrams_sptr,start_time_of_frame,end_time_of_frame); - } - - if (view_segment_num.segment_num()==0 && zero_seg0_end_planes) - { - zero_end_sinograms(y); - zero_end_sinograms(additive_binwise_correction_viewgrams); - zero_end_sinograms(mult_viewgrams_sptr); - } + normalisation_sptr->undo(*mult_viewgrams_sptr, start_time_of_frame, end_time_of_frame); + } + + if (view_segment_num.segment_num() == 0 && zero_seg0_end_planes) { + zero_end_sinograms(y); + zero_end_sinograms(additive_binwise_correction_viewgrams); + zero_end_sinograms(mult_viewgrams_sptr); + } } #ifdef STIR_MPI -void send_viewgrams(const shared_ptr >& y, - const shared_ptr >& additive_binwise_correction_viewgrams, - const shared_ptr >& mult_viewgrams_sptr, - const int next_receiver) -{ - distributed::send_view_segment_numbers( y->get_basic_view_segment_num(), NEW_VIEWGRAM_TAG, next_receiver); - distributed::send_int_value( y->get_basic_timing_pos_num(), next_receiver); - -#ifndef NDEBUG - //test sending related viegrams - shared_ptr - symmetries_sptr(y->get_symmetries_ptr()->clone()); - if (distributed::test && distributed::first_iteration==true && next_receiver==1) - distributed::test_related_viewgrams_master(y->get_proj_data_info_sptr()->create_shared_clone(), - symmetries_sptr, y.get(), next_receiver); -#endif +void +send_viewgrams(const shared_ptr>& y, + const shared_ptr>& additive_binwise_correction_viewgrams, + const shared_ptr>& mult_viewgrams_sptr, const int next_receiver) { + distributed::send_view_segment_numbers(y->get_basic_view_segment_num(), NEW_VIEWGRAM_TAG, next_receiver); + distributed::send_int_value(y->get_basic_timing_pos_num(), next_receiver); + +# ifndef NDEBUG + // test sending related viegrams + shared_ptr symmetries_sptr(y->get_symmetries_ptr()->clone()); + if (distributed::test && distributed::first_iteration == true && next_receiver == 1) + distributed::test_related_viewgrams_master(y->get_proj_data_info_sptr()->create_shared_clone(), symmetries_sptr, y.get(), + next_receiver); +# endif - //TODO: this could also be done by using MPI_Probe at the slave to find out what to recieve next - if (is_null_ptr(additive_binwise_correction_viewgrams)) - { //tell slaves that recieving additive_binwise_correction_viewgrams is not needed - distributed::send_bool_value(false, BINWISE_CORRECTION_TAG, next_receiver); - } - else - { - //tell slaves to receive additive_binwise_correction_viewgrams - distributed::send_bool_value(true, BINWISE_CORRECTION_TAG, next_receiver); - distributed::send_related_viewgrams(additive_binwise_correction_viewgrams.get(), next_receiver); - } - if (is_null_ptr(mult_viewgrams_sptr)) - { - //tell slaves that recieving mult_viewgrams is not needed - distributed::send_bool_value(false, BINWISE_MULT_TAG, next_receiver); - } - else - { - //tell slaves to receive mult_viewgrams - distributed::send_bool_value(true, BINWISE_MULT_TAG, next_receiver); - distributed::send_related_viewgrams(mult_viewgrams_sptr.get(), next_receiver); - } + // TODO: this could also be done by using MPI_Probe at the slave to find out what to recieve next + if (is_null_ptr(additive_binwise_correction_viewgrams)) { // tell slaves that recieving additive_binwise_correction_viewgrams is + // not needed + distributed::send_bool_value(false, BINWISE_CORRECTION_TAG, next_receiver); + } else { + // tell slaves to receive additive_binwise_correction_viewgrams + distributed::send_bool_value(true, BINWISE_CORRECTION_TAG, next_receiver); + distributed::send_related_viewgrams(additive_binwise_correction_viewgrams.get(), next_receiver); + } + if (is_null_ptr(mult_viewgrams_sptr)) { + // tell slaves that recieving mult_viewgrams is not needed + distributed::send_bool_value(false, BINWISE_MULT_TAG, next_receiver); + } else { + // tell slaves to receive mult_viewgrams + distributed::send_bool_value(true, BINWISE_MULT_TAG, next_receiver); + distributed::send_related_viewgrams(mult_viewgrams_sptr.get(), next_receiver); + } // send y distributed::send_related_viewgrams(y.get(), next_receiver); } #endif -void distributable_computation( - const shared_ptr& forward_projector_ptr, - const shared_ptr& back_projector_ptr, - const shared_ptr& symmetries_ptr, - DiscretisedDensity<3,float>* output_image_ptr, - const DiscretisedDensity<3,float>* input_image_ptr, - const shared_ptr& proj_dat_ptr, - const bool read_from_proj_dat, - int subset_num, int num_subsets, - int min_segment_num, int max_segment_num, - bool zero_seg0_end_planes, - double* log_likelihood_ptr, - const shared_ptr& binwise_correction, - const shared_ptr normalisation_sptr, - const double start_time_of_frame, - const double end_time_of_frame, - RPC_process_related_viewgrams_type * RPC_process_related_viewgrams, - DistributedCachingInformation* caching_info_ptr, - int min_timing_pos_num, int max_timing_pos_num) +void +distributable_computation(const shared_ptr& forward_projector_ptr, + const shared_ptr& back_projector_ptr, + const shared_ptr& symmetries_ptr, + DiscretisedDensity<3, float>* output_image_ptr, const DiscretisedDensity<3, float>* input_image_ptr, + const shared_ptr& proj_dat_ptr, const bool read_from_proj_dat, int subset_num, + int num_subsets, int min_segment_num, int max_segment_num, bool zero_seg0_end_planes, + double* log_likelihood_ptr, const shared_ptr& binwise_correction, + const shared_ptr normalisation_sptr, const double start_time_of_frame, + const double end_time_of_frame, RPC_process_related_viewgrams_type* RPC_process_related_viewgrams, + DistributedCachingInformation* caching_info_ptr, int min_timing_pos_num, int max_timing_pos_num) { -#ifdef STIR_MPI +#ifdef STIR_MPI // TODO need to differentiate depending on RPC_process_related_viewgrams int task_id; if (RPC_process_related_viewgrams == &RPC_process_related_viewgrams_accumulate_loglikelihood) - task_id=task_do_distributable_loglikelihood_computation; + task_id = task_do_distributable_loglikelihood_computation; else if (RPC_process_related_viewgrams == &RPC_process_related_viewgrams_gradient) - task_id=task_do_distributable_gradient_computation; + task_id = task_do_distributable_gradient_computation; else if (RPC_process_related_viewgrams == &RPC_process_related_viewgrams_sensitivity_computation) - task_id=task_do_distributable_sensitivity_computation; - /* else if (RPC_process_related_viewgrams == & - case - task_id=task_do_distributable_sensitivity_computation;break; - */ - else - { - error("distributable_computation: unknown RPC task"); - task_id = 0; // avoid compiler warning about "possibly unitialised" when using it - } + task_id = task_do_distributable_sensitivity_computation; + /* else if (RPC_process_related_viewgrams == & + case + task_id=task_do_distributable_sensitivity_computation;break; + */ + else { + error("distributable_computation: unknown RPC task"); + task_id = 0; // avoid compiler warning about "possibly unitialised" when using it + } distributed::send_int_value(task_id, -1); - if (caching_info_ptr != NULL) - { - distributable_computation_cache_enabled( - forward_projector_ptr, - back_projector_ptr, - symmetries_ptr, - output_image_ptr, - input_image_ptr, - proj_dat_ptr, - read_from_proj_dat, - subset_num, num_subsets, - min_segment_num, max_segment_num, - zero_seg0_end_planes, - log_likelihood_ptr, - binwise_correction, - normalisation_sptr, - start_time_of_frame, - end_time_of_frame, - RPC_process_related_viewgrams, - caching_info_ptr, - min_timing_pos_num, max_timing_pos_num); - return; - } + if (caching_info_ptr != NULL) { + distributable_computation_cache_enabled( + forward_projector_ptr, back_projector_ptr, symmetries_ptr, output_image_ptr, input_image_ptr, proj_dat_ptr, + read_from_proj_dat, subset_num, num_subsets, min_segment_num, max_segment_num, zero_seg0_end_planes, log_likelihood_ptr, + binwise_correction, normalisation_sptr, start_time_of_frame, end_time_of_frame, RPC_process_related_viewgrams, + caching_info_ptr, min_timing_pos_num, max_timing_pos_num); + return; + } + // test distributed functions (see DistributedTestFunctions.h for details) - //test distributed functions (see DistributedTestFunctions.h for details) +# ifndef NDEBUG + if (distributed::test && distributed::first_iteration) { + distributed::test_image_estimate_master(input_image_ptr, 1); + distributed::test_parameter_info_master(proj_dat_ptr->get_proj_data_info_sptr()->parameter_info(), 1, "proj_data_info"); + distributed::test_bool_value_master(true, 1); + distributed::test_int_value_master(444, 1); + distributed::test_int_values_master(1); -#ifndef NDEBUG - if (distributed::test && distributed::first_iteration) - { - distributed::test_image_estimate_master(input_image_ptr, 1); - distributed::test_parameter_info_master(proj_dat_ptr->get_proj_data_info_sptr()->parameter_info(), 1, "proj_data_info"); - distributed::test_bool_value_master(true, 1); - distributed::test_int_value_master(444, 1); - distributed::test_int_values_master(1); - - Viewgram viewgram(proj_dat_ptr->get_proj_data_info_sptr()->create_shared_clone(), 44, 0); - for ( int tang_pos = viewgram.get_min_tangential_pos_num(); tang_pos <= viewgram.get_max_tangential_pos_num() ;++tang_pos) - for ( int ax_pos = viewgram.get_min_axial_pos_num(); ax_pos <= viewgram.get_max_axial_pos_num() ;++ax_pos) - viewgram[ax_pos][tang_pos]= rand(); - - distributed::test_viewgram_master(viewgram, proj_dat_ptr->get_proj_data_info_sptr()->create_shared_clone()); - } -#endif - - //send if log_likelihood_ptr is valid and so needs to be accumulated - distributed::send_bool_value(!is_null_ptr(log_likelihood_ptr),USE_DOUBLE_ARG_TAG,-1); - //send the current image estimate + Viewgram viewgram(proj_dat_ptr->get_proj_data_info_sptr()->create_shared_clone(), 44, 0); + for (int tang_pos = viewgram.get_min_tangential_pos_num(); tang_pos <= viewgram.get_max_tangential_pos_num(); ++tang_pos) + for (int ax_pos = viewgram.get_min_axial_pos_num(); ax_pos <= viewgram.get_max_axial_pos_num(); ++ax_pos) + viewgram[ax_pos][tang_pos] = rand(); + + distributed::test_viewgram_master(viewgram, proj_dat_ptr->get_proj_data_info_sptr()->create_shared_clone()); + } +# endif + + // send if log_likelihood_ptr is valid and so needs to be accumulated + distributed::send_bool_value(!is_null_ptr(log_likelihood_ptr), USE_DOUBLE_ARG_TAG, -1); + // send the current image estimate distributed::send_image_estimate(input_image_ptr, -1); - //send if output_image_ptr is valid and so needs to be accumulated - distributed::send_bool_value(!is_null_ptr(output_image_ptr),USE_OUTPUT_IMAGE_ARG_TAG,-1); - + // send if output_image_ptr is valid and so needs to be accumulated + distributed::send_bool_value(!is_null_ptr(output_image_ptr), USE_OUTPUT_IMAGE_ARG_TAG, -1); + #endif CPUTimer CPU_timer; @@ -382,35 +324,32 @@ void distributable_computation( assert(min_segment_num <= max_segment_num); assert(min_timing_pos_num <= max_timing_pos_num); - assert(subset_num >=0); + assert(subset_num >= 0); assert(subset_num < num_subsets); - + assert(!is_null_ptr(proj_dat_ptr)); - + if (output_image_ptr != NULL) output_image_ptr->fill(0); - - if (log_likelihood_ptr != NULL) - { - (*log_likelihood_ptr) = 0.0; - }; + + if (log_likelihood_ptr != NULL) { + (*log_likelihood_ptr) = 0.0; + }; if (zero_seg0_end_planes) info("End-planes of segment 0 will be zeroed"); - const std::vector vs_nums_to_process = - detail::find_basic_vs_nums_in_subset(*proj_dat_ptr->get_proj_data_info_sptr(), *symmetries_ptr, - min_segment_num, max_segment_num, - subset_num, num_subsets); - - int count=0, count2=0; - + const std::vector vs_nums_to_process = detail::find_basic_vs_nums_in_subset( + *proj_dat_ptr->get_proj_data_info_sptr(), *symmetries_ptr, min_segment_num, max_segment_num, subset_num, num_subsets); + + int count = 0, count2 = 0; + #ifdef STIR_MPI - int sent_count=0; //counts the work packages sent - int working_slaves_count=0; //counts the number of slaves which are currently working - int next_receiver=1; //always stores the next slave to be provided with work + int sent_count = 0; // counts the work packages sent + int working_slaves_count = 0; // counts the number of slaves which are currently working + int next_receiver = 1; // always stores the next slave to be provided with work #endif - //double total_seq_rpc_time=0.0; //sums up times used for RPC_process_related_viewgrams + // double total_seq_rpc_time=0.0; //sums up times used for RPC_process_related_viewgrams forward_projector_ptr->set_input(*input_image_ptr); if (output_image_ptr != NULL) @@ -419,103 +358,90 @@ void distributable_computation( #ifdef STIR_OPENMP std::vector local_log_likelihoods; std::vector local_counts, local_count2s; -#pragma omp parallel shared(local_log_likelihoods, local_counts, local_count2s) +# pragma omp parallel shared(local_log_likelihoods, local_counts, local_count2s) #endif // start of threaded section if openmp - { + { #ifdef STIR_OPENMP -#pragma omp single +# pragma omp single { info(boost::format("Starting loop with %1% threads") % omp_get_num_threads(), 2); local_log_likelihoods.resize(omp_get_max_threads(), 0.); local_counts.resize(omp_get_max_threads(), 0); local_count2s.resize(omp_get_max_threads(), 0); } -#pragma omp for schedule(runtime) +# pragma omp for schedule(runtime) #endif - for (int timing_pos_num = min_timing_pos_num; timing_pos_num <= max_timing_pos_num; ++timing_pos_num) - { - // note: older versions of openmp need an int as loop - for (int i=0; i(vs_nums_to_process.size()); ++i) - { - const ViewSegmentNumbers view_segment_num=vs_nums_to_process[i]; - - shared_ptr > y; - shared_ptr > additive_binwise_correction_viewgrams; - shared_ptr > mult_viewgrams_sptr; - - get_viewgrams(y, additive_binwise_correction_viewgrams, mult_viewgrams_sptr, - proj_dat_ptr, read_from_proj_dat, - zero_seg0_end_planes, - binwise_correction, - normalisation_sptr, start_time_of_frame, end_time_of_frame, + for (int timing_pos_num = min_timing_pos_num; timing_pos_num <= max_timing_pos_num; ++timing_pos_num) { + // note: older versions of openmp need an int as loop + for (int i = 0; i < static_cast(vs_nums_to_process.size()); ++i) { + const ViewSegmentNumbers view_segment_num = vs_nums_to_process[i]; + + shared_ptr> y; + shared_ptr> additive_binwise_correction_viewgrams; + shared_ptr> mult_viewgrams_sptr; + + get_viewgrams(y, additive_binwise_correction_viewgrams, mult_viewgrams_sptr, proj_dat_ptr, read_from_proj_dat, + zero_seg0_end_planes, binwise_correction, normalisation_sptr, start_time_of_frame, end_time_of_frame, symmetries_ptr, view_segment_num, timing_pos_num); -#ifdef STIR_MPI - - //send viewgrams, the slave will immediatelly start calculation - send_viewgrams(y, additive_binwise_correction_viewgrams, mult_viewgrams_sptr, - next_receiver); - working_slaves_count++; - sent_count++; - - //give every slave some work before waiting for requests - if (sent_count < distributed::num_processors-1) // note: -1 as master doesn't get any viewgrams - next_receiver++; - else - { - //wait for available notification - int int_values[2]; - const MPI_Status status=distributed::receive_int_values(int_values, 2, AVAILABLE_NOTIFICATION_TAG); - next_receiver=status.MPI_SOURCE; - working_slaves_count--; - - //reduce count values - count+=int_values[0]; - count2+=int_values[1]; - } +#ifdef STIR_MPI + + // send viewgrams, the slave will immediatelly start calculation + send_viewgrams(y, additive_binwise_correction_viewgrams, mult_viewgrams_sptr, next_receiver); + working_slaves_count++; + sent_count++; + + // give every slave some work before waiting for requests + if (sent_count < distributed::num_processors - 1) // note: -1 as master doesn't get any viewgrams + next_receiver++; + else { + // wait for available notification + int int_values[2]; + const MPI_Status status = distributed::receive_int_values(int_values, 2, AVAILABLE_NOTIFICATION_TAG); + next_receiver = status.MPI_SOURCE; + working_slaves_count--; + + // reduce count values + count += int_values[0]; + count2 += int_values[1]; + } #else // STIR_MPI -#ifdef STIR_OPENMP - const int thread_num=omp_get_thread_num(); - info(boost::format("Thread %d/%d calculating segment_num: %d, view_num: %d, timing_pos_num: %d") - % thread_num % omp_get_num_threads() - % view_segment_num.segment_num() % view_segment_num.view_num() % timing_pos_num, 2); -#else - info(boost::format("calculating segment_num: %d, view_num: %d, timing_pos_num: %d") - % view_segment_num.segment_num() % view_segment_num.view_num() % timing_pos_num,2); -#endif +# ifdef STIR_OPENMP + const int thread_num = omp_get_thread_num(); + info(boost::format("Thread %d/%d calculating segment_num: %d, view_num: %d, timing_pos_num: %d") % thread_num % + omp_get_num_threads() % view_segment_num.segment_num() % view_segment_num.view_num() % timing_pos_num, + 2); +# else + info(boost::format("calculating segment_num: %d, view_num: %d, timing_pos_num: %d") % view_segment_num.segment_num() % + view_segment_num.view_num() % timing_pos_num, + 2); +# endif + +# ifdef STIR_OPENMP + RPC_process_related_viewgrams(forward_projector_ptr, back_projector_ptr, y.get(), local_counts[thread_num], + local_count2s[thread_num], + is_null_ptr(log_likelihood_ptr) ? NULL : &local_log_likelihoods[thread_num], + additive_binwise_correction_viewgrams.get(), mult_viewgrams_sptr.get()); + +# else + RPC_process_related_viewgrams(forward_projector_ptr, back_projector_ptr, y.get(), count, count2, log_likelihood_ptr, + additive_binwise_correction_viewgrams.get(), mult_viewgrams_sptr.get()); +# endif // OPENMP +#endif // MPI + } // end of for-loop + } // end of for-loop over timing_pos_num + } // end of parallel section of openmp -#ifdef STIR_OPENMP - RPC_process_related_viewgrams(forward_projector_ptr, - back_projector_ptr, - y.get(), - local_counts[thread_num], local_count2s[thread_num], - is_null_ptr(log_likelihood_ptr)? NULL : &local_log_likelihoods[thread_num], - additive_binwise_correction_viewgrams.get(), - mult_viewgrams_sptr.get()); - -#else - RPC_process_related_viewgrams(forward_projector_ptr, - back_projector_ptr, - y.get(), count, count2, log_likelihood_ptr, - additive_binwise_correction_viewgrams.get(), - mult_viewgrams_sptr.get()); -#endif // OPENMP -#endif // MPI - } // end of for-loop - } // end of for-loop over timing_pos_num - } // end of parallel section of openmp - #ifdef STIR_OPENMP // "reduce" data constructed by threads { - if (log_likelihood_ptr != NULL) - { - for (int i=0; i(local_log_likelihoods.size()); ++i) - *log_likelihood_ptr += local_log_likelihoods[i]; // accumulate all (as they were initialised to zero) - } + if (log_likelihood_ptr != NULL) { + for (int i = 0; i < static_cast(local_log_likelihoods.size()); ++i) + *log_likelihood_ptr += local_log_likelihoods[i]; // accumulate all (as they were initialised to zero) + } count += std::accumulate(local_counts.begin(), local_counts.end(), 0); count2 += std::accumulate(local_count2s.begin(), local_count2s.end(), 0); } @@ -523,61 +449,61 @@ void distributable_computation( if (output_image_ptr != NULL) back_projector_ptr->get_output(*output_image_ptr); #ifdef STIR_MPI - //end of iteration processing + // end of iteration processing // receive remaining available notifications { - while(working_slaves_count>0) - { - int int_values[2]; - distributed::receive_int_values(int_values, 2, AVAILABLE_NOTIFICATION_TAG); - working_slaves_count--; - - //reduce count values - count+=int_values[0]; - count2+=int_values[1]; - } + while (working_slaves_count > 0) { + int int_values[2]; + distributed::receive_int_values(int_values, 2, AVAILABLE_NOTIFICATION_TAG); + working_slaves_count--; + + // reduce count values + count += int_values[0]; + count2 += int_values[1]; + } } - distributed::first_iteration=false; - - //broadcast end of iteration notification - int int_values[2]; int_values[0]=3; int_values[1]=4; // values are ignored + distributed::first_iteration = false; + + // broadcast end of iteration notification + int int_values[2]; + int_values[0] = 3; + int_values[1] = 4; // values are ignored distributed::send_int_values(int_values, 2, END_ITERATION_TAG, -1); - - //reduce output image + + // reduce output image if (!is_null_ptr(output_image_ptr)) distributed::reduce_received_output_image(output_image_ptr, 0); // and log_likelihood - if (!is_null_ptr(log_likelihood_ptr)) - { - double buffer = 0.0; - MPI_Reduce(&buffer, log_likelihood_ptr, /*size*/1, MPI_DOUBLE, MPI_SUM, /*destination*/ 0, MPI_COMM_WORLD); - } + if (!is_null_ptr(log_likelihood_ptr)) { + double buffer = 0.0; + MPI_Reduce(&buffer, log_likelihood_ptr, /*size*/ 1, MPI_DOUBLE, MPI_SUM, /*destination*/ 0, MPI_COMM_WORLD); + } + + // reduce timed rpc-value + if (distributed::rpc_time) { + printf("Master: Reducing timer value\n"); + double send = 0; + double receive; + MPI_Reduce(&send, &receive, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); + distributed::total_rpc_time += (receive / (distributed::num_processors - 1)); + + printf("Average time used by slaves for RPC processing: %f secs\n", distributed::total_rpc_time); + distributed::total_rpc_time_slaves += receive; + } - //reduce timed rpc-value - if(distributed::rpc_time) - { - printf("Master: Reducing timer value\n"); - double send = 0; - double receive; - MPI_Reduce(&send, &receive, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); - distributed::total_rpc_time+=(receive/(distributed::num_processors-1)); - - printf("Average time used by slaves for RPC processing: %f secs\n", distributed::total_rpc_time); - distributed::total_rpc_time_slaves+=receive; - } - #endif { - // TODO this message relies on knowledge of count, count2 which might be inappropriate for + // TODO this message relies on knowledge of count, count2 which might be inappropriate for // the call-back function - info(boost::format("Number of (cancelled) singularities: %1%\nNumber of (cancelled) negative numerators: %2%") % count % count2); + info(boost::format("Number of (cancelled) singularities: %1%\nNumber of (cancelled) negative numerators: %2%") % count % + count2); } CPU_timer.stop(); wall_clock_timer.stop(); - info(boost::format("Computation times for distributable_computation, CPU %1%s, wall-clock %2%s") - % CPU_timer.value() % wall_clock_timer.value()); + info(boost::format("Computation times for distributable_computation, CPU %1%s, wall-clock %2%s") % CPU_timer.value() % + wall_clock_timer.value()); } END_NAMESPACE_STIR diff --git a/src/recon_buildblock/distributableMPICacheEnabled.cxx b/src/recon_buildblock/distributableMPICacheEnabled.cxx index 4c99c7815a..ad66adb4bc 100644 --- a/src/recon_buildblock/distributableMPICacheEnabled.cxx +++ b/src/recon_buildblock/distributableMPICacheEnabled.cxx @@ -24,8 +24,8 @@ \brief Implementation of stir::distributable_computation_cache_enabled() \todo merge with distributable.cxx - \author Kris Thielemans - \author Alexey Zverovich + \author Kris Thielemans + \author Alexey Zverovich \author Matthew Jacobson \author PARAPET project \author Tobias Beisel @@ -33,7 +33,7 @@ /* Modification history: KT 30/05/2002 get rid of dependence on specific symmetries (i.e. views up to 45 degrees) - + TB 30/06/2007 added MPI support KT 2011 @@ -53,9 +53,9 @@ #include "stir/recon_buildblock/find_basic_vs_nums_in_subsets.h" #include "stir/info.h" #ifdef STIR_MPI -#include "stir/recon_buildblock/distributed_functions.h" -#include "stir/recon_buildblock/distributed_test_functions.h" -#include "stir/recon_buildblock/DistributedCachingInformation.h" +# include "stir/recon_buildblock/distributed_functions.h" +# include "stir/recon_buildblock/distributed_test_functions.h" +# include "stir/recon_buildblock/DistributedCachingInformation.h" #endif START_NAMESPACE_STIR @@ -64,321 +64,267 @@ START_NAMESPACE_STIR template static void -zero_end_sinograms(ViewgramsPtr viewgrams_ptr) -{ - if (!is_null_ptr(viewgrams_ptr)) - { - const int min_ax_pos_num = viewgrams_ptr->get_min_axial_pos_num(); - const int max_ax_pos_num = viewgrams_ptr->get_max_axial_pos_num(); - for (RelatedViewgrams::iterator r_viewgrams_iter = viewgrams_ptr->begin(); - r_viewgrams_iter != viewgrams_ptr->end(); - ++r_viewgrams_iter) - { - (*r_viewgrams_iter)[min_ax_pos_num].fill(0); - (*r_viewgrams_iter)[max_ax_pos_num].fill(0); - } +zero_end_sinograms(ViewgramsPtr viewgrams_ptr) { + if (!is_null_ptr(viewgrams_ptr)) { + const int min_ax_pos_num = viewgrams_ptr->get_min_axial_pos_num(); + const int max_ax_pos_num = viewgrams_ptr->get_max_axial_pos_num(); + for (RelatedViewgrams::iterator r_viewgrams_iter = viewgrams_ptr->begin(); r_viewgrams_iter != viewgrams_ptr->end(); + ++r_viewgrams_iter) { + (*r_viewgrams_iter)[min_ax_pos_num].fill(0); + (*r_viewgrams_iter)[max_ax_pos_num].fill(0); } + } } -static -void get_viewgrams(shared_ptr >& y, - shared_ptr >& additive_binwise_correction_viewgrams, - shared_ptr >& mult_viewgrams_sptr, - const shared_ptr& proj_dat_ptr, - const bool read_from_proj_dat, - const bool zero_seg0_end_planes, - const shared_ptr& binwise_correction, - const shared_ptr& normalisation_sptr, - const double start_time_of_frame, - const double end_time_of_frame, - const shared_ptr& symmetries_ptr, - const ViewSegmentNumbers& view_segment_num, - const int timing_pos_num - ) -{ - if (!is_null_ptr(binwise_correction)) - { -#if !defined(_MSC_VER) || _MSC_VER>1300 - additive_binwise_correction_viewgrams.reset( - new RelatedViewgrams - (binwise_correction->get_related_viewgrams(view_segment_num, symmetries_ptr, false, timing_pos_num))); +static void +get_viewgrams(shared_ptr>& y, shared_ptr>& additive_binwise_correction_viewgrams, + shared_ptr>& mult_viewgrams_sptr, const shared_ptr& proj_dat_ptr, + const bool read_from_proj_dat, const bool zero_seg0_end_planes, const shared_ptr& binwise_correction, + const shared_ptr& normalisation_sptr, const double start_time_of_frame, + const double end_time_of_frame, const shared_ptr& symmetries_ptr, + const ViewSegmentNumbers& view_segment_num, const int timing_pos_num) { + if (!is_null_ptr(binwise_correction)) { +#if !defined(_MSC_VER) || _MSC_VER > 1300 + additive_binwise_correction_viewgrams.reset(new RelatedViewgrams( + binwise_correction->get_related_viewgrams(view_segment_num, symmetries_ptr, false, timing_pos_num))); #else - RelatedViewgrams tmp(binwise_correction-> - get_related_viewgrams(view_segment_num, symmetries_ptr, false, timing_pos_num)); - additive_binwise_correction_viewgrams = new RelatedViewgrams(tmp); -#endif - } - - if (read_from_proj_dat) - { -#if !defined(_MSC_VER) || _MSC_VER>1300 - y.reset(new RelatedViewgrams - (proj_dat_ptr->get_related_viewgrams(view_segment_num, symmetries_ptr, false, timing_pos_num))); + RelatedViewgrams tmp( + binwise_correction->get_related_viewgrams(view_segment_num, symmetries_ptr, false, timing_pos_num)); + additive_binwise_correction_viewgrams = new RelatedViewgrams(tmp); +#endif + } + + if (read_from_proj_dat) { +#if !defined(_MSC_VER) || _MSC_VER > 1300 + y.reset(new RelatedViewgrams( + proj_dat_ptr->get_related_viewgrams(view_segment_num, symmetries_ptr, false, timing_pos_num))); #else - // workaround VC++ 6.0 bug - RelatedViewgrams tmp(proj_dat_ptr-> - get_related_viewgrams(view_segment_num, symmetries_ptr, false, timing_pos_num)); - y = new RelatedViewgrams(tmp); -#endif - } - else - { - y.reset(new RelatedViewgrams - (proj_dat_ptr->get_empty_related_viewgrams(view_segment_num, symmetries_ptr, false, timing_pos_num))); - } + // workaround VC++ 6.0 bug + RelatedViewgrams tmp(proj_dat_ptr->get_related_viewgrams(view_segment_num, symmetries_ptr, false, timing_pos_num)); + y = new RelatedViewgrams(tmp); +#endif + } else { + y.reset(new RelatedViewgrams( + proj_dat_ptr->get_empty_related_viewgrams(view_segment_num, symmetries_ptr, false, timing_pos_num))); + } // multiplicative correction - if (!is_null_ptr(normalisation_sptr) && !normalisation_sptr->is_trivial()) - { - mult_viewgrams_sptr.reset( - new RelatedViewgrams(proj_dat_ptr->get_empty_related_viewgrams(view_segment_num, symmetries_ptr, false, timing_pos_num))); - mult_viewgrams_sptr->fill(1.F); - normalisation_sptr->undo(*mult_viewgrams_sptr,start_time_of_frame,end_time_of_frame); - } - - if (view_segment_num.segment_num()==0 && zero_seg0_end_planes) - { - zero_end_sinograms(y); - zero_end_sinograms(additive_binwise_correction_viewgrams); - zero_end_sinograms(mult_viewgrams_sptr); - } + if (!is_null_ptr(normalisation_sptr) && !normalisation_sptr->is_trivial()) { + mult_viewgrams_sptr.reset(new RelatedViewgrams( + proj_dat_ptr->get_empty_related_viewgrams(view_segment_num, symmetries_ptr, false, timing_pos_num))); + mult_viewgrams_sptr->fill(1.F); + normalisation_sptr->undo(*mult_viewgrams_sptr, start_time_of_frame, end_time_of_frame); + } + + if (view_segment_num.segment_num() == 0 && zero_seg0_end_planes) { + zero_end_sinograms(y); + zero_end_sinograms(additive_binwise_correction_viewgrams); + zero_end_sinograms(mult_viewgrams_sptr); + } } -static void send_viewgrams(const shared_ptr >& y, - const shared_ptr >& additive_binwise_correction_viewgrams, - const shared_ptr >& mult_viewgrams_sptr, - const int next_receiver) -{ - distributed::send_view_segment_numbers( y->get_basic_view_segment_num(), NEW_VIEWGRAM_TAG, next_receiver); - distributed::send_int_value( y->get_basic_timing_pos_num(), next_receiver); +static void +send_viewgrams(const shared_ptr>& y, + const shared_ptr>& additive_binwise_correction_viewgrams, + const shared_ptr>& mult_viewgrams_sptr, const int next_receiver) { + distributed::send_view_segment_numbers(y->get_basic_view_segment_num(), NEW_VIEWGRAM_TAG, next_receiver); + distributed::send_int_value(y->get_basic_timing_pos_num(), next_receiver); #ifndef NDEBUG - //test sending related viewgrams - shared_ptr - symmetries_sptr(y->get_symmetries_ptr()->clone()); - if (distributed::test && distributed::first_iteration==true && next_receiver==1) - distributed::test_related_viewgrams_master(y->get_proj_data_info_sptr()->create_shared_clone(), - symmetries_sptr, y.get(), next_receiver); + // test sending related viewgrams + shared_ptr symmetries_sptr(y->get_symmetries_ptr()->clone()); + if (distributed::test && distributed::first_iteration == true && next_receiver == 1) + distributed::test_related_viewgrams_master(y->get_proj_data_info_sptr()->create_shared_clone(), symmetries_sptr, y.get(), + next_receiver); #endif - //TODO: this could also be done by using MPI_Probe at the slave to find out what to recieve next - if (is_null_ptr(additive_binwise_correction_viewgrams)) - { //tell slaves that recieving additive_binwise_correction_viewgrams is not needed - distributed::send_bool_value(false, BINWISE_CORRECTION_TAG, next_receiver); - } - else - { - //tell slaves to receive additive_binwise_correction_viewgrams - distributed::send_bool_value(true, BINWISE_CORRECTION_TAG, next_receiver); - distributed::send_related_viewgrams(additive_binwise_correction_viewgrams.get(), next_receiver); - } - if (is_null_ptr(mult_viewgrams_sptr)) - { - //tell slaves that recieving mult_viewgrams is not needed - distributed::send_bool_value(false, BINWISE_MULT_TAG, next_receiver); - } - else - { - //tell slaves to receive mult_viewgrams - distributed::send_bool_value(true, BINWISE_MULT_TAG, next_receiver); - distributed::send_related_viewgrams(mult_viewgrams_sptr.get(), next_receiver); - } + // TODO: this could also be done by using MPI_Probe at the slave to find out what to recieve next + if (is_null_ptr(additive_binwise_correction_viewgrams)) { // tell slaves that recieving additive_binwise_correction_viewgrams is + // not needed + distributed::send_bool_value(false, BINWISE_CORRECTION_TAG, next_receiver); + } else { + // tell slaves to receive additive_binwise_correction_viewgrams + distributed::send_bool_value(true, BINWISE_CORRECTION_TAG, next_receiver); + distributed::send_related_viewgrams(additive_binwise_correction_viewgrams.get(), next_receiver); + } + if (is_null_ptr(mult_viewgrams_sptr)) { + // tell slaves that recieving mult_viewgrams is not needed + distributed::send_bool_value(false, BINWISE_MULT_TAG, next_receiver); + } else { + // tell slaves to receive mult_viewgrams + distributed::send_bool_value(true, BINWISE_MULT_TAG, next_receiver); + distributed::send_related_viewgrams(mult_viewgrams_sptr.get(), next_receiver); + } // send y distributed::send_related_viewgrams(y.get(), next_receiver); } -void distributable_computation_cache_enabled( - const shared_ptr& forward_projector_ptr, - const shared_ptr& back_projector_ptr, - const shared_ptr& symmetries_ptr, - DiscretisedDensity<3,float>* output_image_ptr, - const DiscretisedDensity<3,float>* input_image_ptr, - const shared_ptr& proj_dat_ptr, - const bool read_from_proj_dat, - int subset_num, int num_subsets, - int min_segment_num, int max_segment_num, - bool zero_seg0_end_planes, - double* log_likelihood_ptr, - const shared_ptr& binwise_correction, - const shared_ptr normalise_sptr, - const double start_time_of_frame, - const double end_time_of_frame, - RPC_process_related_viewgrams_type * RPC_process_related_viewgrams, - DistributedCachingInformation* caching_info_ptr, - int min_timing_pos_num, int max_timing_pos_num - ) -{ - //test distributed functions (see DistributedTestFunctions.h for details) +void +distributable_computation_cache_enabled( + const shared_ptr& forward_projector_ptr, const shared_ptr& back_projector_ptr, + const shared_ptr& symmetries_ptr, DiscretisedDensity<3, float>* output_image_ptr, + const DiscretisedDensity<3, float>* input_image_ptr, const shared_ptr& proj_dat_ptr, const bool read_from_proj_dat, + int subset_num, int num_subsets, int min_segment_num, int max_segment_num, bool zero_seg0_end_planes, + double* log_likelihood_ptr, const shared_ptr& binwise_correction, const shared_ptr normalise_sptr, + const double start_time_of_frame, const double end_time_of_frame, + RPC_process_related_viewgrams_type* RPC_process_related_viewgrams, DistributedCachingInformation* caching_info_ptr, + int min_timing_pos_num, int max_timing_pos_num) { + // test distributed functions (see DistributedTestFunctions.h for details) #ifndef NDEBUG - if (distributed::test && distributed::first_iteration) - { - distributed::test_image_estimate_master(input_image_ptr, 1); - distributed::test_parameter_info_master(proj_dat_ptr->get_proj_data_info_sptr()->parameter_info(), 1, "proj_data_info"); - distributed::test_bool_value_master(true, 1); - distributed::test_int_value_master(444, 1); - distributed::test_int_values_master(1); - - Viewgram viewgram(proj_dat_ptr->get_proj_data_info_sptr()->create_shared_clone(), 44, 0); - for ( int tang_pos = viewgram.get_min_tangential_pos_num(); tang_pos <= viewgram.get_max_tangential_pos_num() ;++tang_pos) - for ( int ax_pos = viewgram.get_min_axial_pos_num(); ax_pos <= viewgram.get_max_axial_pos_num() ;++ax_pos) - viewgram[ax_pos][tang_pos]= rand(); - - distributed::test_viewgram_master(viewgram, proj_dat_ptr->get_proj_data_info_sptr()->create_shared_clone()); - } + if (distributed::test && distributed::first_iteration) { + distributed::test_image_estimate_master(input_image_ptr, 1); + distributed::test_parameter_info_master(proj_dat_ptr->get_proj_data_info_sptr()->parameter_info(), 1, "proj_data_info"); + distributed::test_bool_value_master(true, 1); + distributed::test_int_value_master(444, 1); + distributed::test_int_values_master(1); + + Viewgram viewgram(proj_dat_ptr->get_proj_data_info_sptr()->create_shared_clone(), 44, 0); + for (int tang_pos = viewgram.get_min_tangential_pos_num(); tang_pos <= viewgram.get_max_tangential_pos_num(); ++tang_pos) + for (int ax_pos = viewgram.get_min_axial_pos_num(); ax_pos <= viewgram.get_max_axial_pos_num(); ++ax_pos) + viewgram[ax_pos][tang_pos] = rand(); + + distributed::test_viewgram_master(viewgram, proj_dat_ptr->get_proj_data_info_sptr()->create_shared_clone()); + } #endif - - //send if log_likelihood_ptr is valid and so needs to be accumulated - distributed::send_bool_value(!is_null_ptr(log_likelihood_ptr),USE_DOUBLE_ARG_TAG,-1); - //send the current image estimate + + // send if log_likelihood_ptr is valid and so needs to be accumulated + distributed::send_bool_value(!is_null_ptr(log_likelihood_ptr), USE_DOUBLE_ARG_TAG, -1); + // send the current image estimate distributed::send_image_estimate(input_image_ptr, -1); - //send if output_image_ptr is valid and so needs to be accumulated - distributed::send_bool_value(!is_null_ptr(output_image_ptr),USE_OUTPUT_IMAGE_ARG_TAG,-1); - + // send if output_image_ptr is valid and so needs to be accumulated + distributed::send_bool_value(!is_null_ptr(output_image_ptr), USE_OUTPUT_IMAGE_ARG_TAG, -1); + assert(min_segment_num <= max_segment_num); assert(min_timing_pos_num <= max_timing_pos_num); - assert(subset_num >=0); + assert(subset_num >= 0); assert(subset_num < num_subsets); - - assert(!is_null_ptr(proj_dat_ptr)); + + assert(!is_null_ptr(proj_dat_ptr)); if (output_image_ptr != NULL) output_image_ptr->fill(0); - - if (log_likelihood_ptr != NULL) - { - (*log_likelihood_ptr) = 0.0; - }; - + + if (log_likelihood_ptr != NULL) { + (*log_likelihood_ptr) = 0.0; + }; + // needed for several send/receive operations int int_values[2]; - - int working_slaves_count=0; //counts the number of slaves which are currently working - int next_receiver=1; //always stores the next slave to be provided with work - - int count=0, count2=0; - - const std::vector vs_nums_to_process = - detail::find_basic_vs_nums_in_subset(*proj_dat_ptr->get_proj_data_info_sptr(), *symmetries_ptr, - min_segment_num, max_segment_num, - subset_num, num_subsets); - - const std::size_t num_vs = vs_nums_to_process.size(); - + + int working_slaves_count = 0; // counts the number of slaves which are currently working + int next_receiver = 1; // always stores the next slave to be provided with work + + int count = 0, count2 = 0; + + const std::vector vs_nums_to_process = detail::find_basic_vs_nums_in_subset( + *proj_dat_ptr->get_proj_data_info_sptr(), *symmetries_ptr, min_segment_num, max_segment_num, subset_num, num_subsets); + + const std::size_t num_vs = vs_nums_to_process.size(); + CPUTimer iteration_timer; iteration_timer.start(); - - //initialize the caching values for the new iteration + + // initialize the caching values for the new iteration caching_info_ptr->initialise_new_subiteration(vs_nums_to_process); - - //while not all vs_nums are processed repeat - for (std::size_t processed_count = 1; processed_count <= num_vs; ++processed_count) - { - ViewSegmentNumbers view_segment_num; - - //check whether the slave will receive a new or an already cached viewgram - const bool new_viewgrams = - caching_info_ptr->get_unprocessed_vs_num(view_segment_num, next_receiver); - // view_segment_num = vs_nums_to_process[processed_count-1]; - - for (int timing_pos_num = min_timing_pos_num; timing_pos_num <= max_timing_pos_num; ++timing_pos_num) + + // while not all vs_nums are processed repeat + for (std::size_t processed_count = 1; processed_count <= num_vs; ++processed_count) { + ViewSegmentNumbers view_segment_num; + + // check whether the slave will receive a new or an already cached viewgram + const bool new_viewgrams = caching_info_ptr->get_unprocessed_vs_num(view_segment_num, next_receiver); + // view_segment_num = vs_nums_to_process[processed_count-1]; + + for (int timing_pos_num = min_timing_pos_num; timing_pos_num <= max_timing_pos_num; ++timing_pos_num) { + + // the slave has not yet processed this vs_num, so the viewgrams have to be sent + if (new_viewgrams == true) { + info(boost::format("Sending segment %1%, view %2%, TOF bin %3% to slave %4%\n") % view_segment_num.segment_num() % + view_segment_num.view_num() % timing_pos_num % next_receiver); + + shared_ptr> y; + shared_ptr> additive_binwise_correction_viewgrams; + shared_ptr> mult_viewgrams_sptr; + + get_viewgrams(y, additive_binwise_correction_viewgrams, mult_viewgrams_sptr, proj_dat_ptr, read_from_proj_dat, + zero_seg0_end_planes, binwise_correction, normalise_sptr, start_time_of_frame, end_time_of_frame, + symmetries_ptr, view_segment_num, timing_pos_num); + + // send viewgrams, the slave will immediatelly start calculation + send_viewgrams(y, additive_binwise_correction_viewgrams, mult_viewgrams_sptr, next_receiver); + } // if(new_viewgram) + else { + info(boost::format("Re-using segment %1%, view %2%, TOF bin %3% to slave %4%\n") % view_segment_num.segment_num() % + view_segment_num.view_num() % timing_pos_num % next_receiver); + // send vs_num with reuse-tag, the slave will immediatelly start calculation + distributed::send_view_segment_numbers(view_segment_num, REUSE_VIEWGRAM_TAG, next_receiver); + } + + working_slaves_count++; + + if (int(processed_count) < distributed::num_processors - 1) // note: -1 as master doesn't get any viewgrams { + // give every slave some work before waiting for requests + next_receiver++; + } else { + const MPI_Status status = distributed::receive_int_values(int_values, 2, AVAILABLE_NOTIFICATION_TAG); + next_receiver = status.MPI_SOURCE; + working_slaves_count--; - //the slave has not yet processed this vs_num, so the viewgrams have to be sent - if (new_viewgrams==true) - { - info(boost::format("Sending segment %1%, view %2%, TOF bin %3% to slave %4%\n") % view_segment_num.segment_num() % view_segment_num.view_num() % timing_pos_num % next_receiver); - - shared_ptr > y; - shared_ptr > additive_binwise_correction_viewgrams; - shared_ptr > mult_viewgrams_sptr; - - get_viewgrams(y, additive_binwise_correction_viewgrams, mult_viewgrams_sptr, - proj_dat_ptr, read_from_proj_dat, - zero_seg0_end_planes, - binwise_correction, - normalise_sptr, start_time_of_frame, end_time_of_frame, - symmetries_ptr, view_segment_num, timing_pos_num); - - //send viewgrams, the slave will immediatelly start calculation - send_viewgrams(y, additive_binwise_correction_viewgrams, mult_viewgrams_sptr, - next_receiver); - } // if(new_viewgram) - else - { - info(boost::format("Re-using segment %1%, view %2%, TOF bin %3% to slave %4%\n") % view_segment_num.segment_num() % view_segment_num.view_num() % timing_pos_num % next_receiver); - //send vs_num with reuse-tag, the slave will immediatelly start calculation - distributed::send_view_segment_numbers( view_segment_num, REUSE_VIEWGRAM_TAG, next_receiver); - } - - working_slaves_count++; - - if (int(processed_count) 0) - { - status=distributed::receive_int_values(int_values, 2, AVAILABLE_NOTIFICATION_TAG); - working_slaves_count--; - - //reduce count values - count+=int_values[0]; - count2+=int_values[1]; - } - + while (working_slaves_count > 0) { + status = distributed::receive_int_values(int_values, 2, AVAILABLE_NOTIFICATION_TAG); + working_slaves_count--; + + // reduce count values + count += int_values[0]; + count2 += int_values[1]; + } + // in the cache-enabled distributed case, this message is only printed once per iteration - // TODO this message relies on knowledge of count, count2 which might be inappropriate for + // TODO this message relies on knowledge of count, count2 which might be inappropriate for // the call-back function #ifndef STIR_NO_RUNNING_LOG - info(boost::format("\tNumber of (cancelled) singularities: %1%\n\tNumber of (cancelled) negative numerators: %2%\n\tIteration: %3%secs\n") % count % count2 % iteration_timer.value()); + info(boost::format("\tNumber of (cancelled) singularities: %1%\n\tNumber of (cancelled) negative numerators: %2%\n\tIteration: " + "%3%secs\n") % + count % count2 % iteration_timer.value()); #endif - - int_values[0]=3; - int_values[1]=4; - - //send end_iteration notification + + int_values[0] = 3; + int_values[1] = 4; + + // send end_iteration notification distributed::send_int_values(int_values, 2, END_ITERATION_TAG, -1); - - //reduce output image + + // reduce output image if (!is_null_ptr(output_image_ptr)) distributed::reduce_received_output_image(output_image_ptr, 0); // and log_likelihood - if (!is_null_ptr(log_likelihood_ptr)) - { - double buffer = 0.0; - MPI_Reduce(&buffer, log_likelihood_ptr, /*size*/1, MPI_DOUBLE, MPI_SUM, /*destination*/ 0, MPI_COMM_WORLD); - } - - //reduce timed rpc-value - if(distributed::rpc_time) - { - printf("Master: Reducing timer value\n"); - double send = 0; - double receive; - MPI_Reduce(&send, &receive, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); - distributed::total_rpc_time+=(receive/(distributed::num_processors-1)); - - printf("Average time used by slaves for RPC processing: %f secs\n", distributed::total_rpc_time); - distributed::total_rpc_time_slaves+=receive; - } - + if (!is_null_ptr(log_likelihood_ptr)) { + double buffer = 0.0; + MPI_Reduce(&buffer, log_likelihood_ptr, /*size*/ 1, MPI_DOUBLE, MPI_SUM, /*destination*/ 0, MPI_COMM_WORLD); + } + + // reduce timed rpc-value + if (distributed::rpc_time) { + printf("Master: Reducing timer value\n"); + double send = 0; + double receive; + MPI_Reduce(&send, &receive, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); + distributed::total_rpc_time += (receive / (distributed::num_processors - 1)); + + printf("Average time used by slaves for RPC processing: %f secs\n", distributed::total_rpc_time); + distributed::total_rpc_time_slaves += receive; + } } END_NAMESPACE_STIR diff --git a/src/recon_buildblock/distributed_functions.cxx b/src/recon_buildblock/distributed_functions.cxx index 0a2af14d55..d81b1425d3 100644 --- a/src/recon_buildblock/distributed_functions.cxx +++ b/src/recon_buildblock/distributed_functions.cxx @@ -22,7 +22,7 @@ \brief Implementation of functions in distributed namespace - \author Tobias Beisel + \author Tobias Beisel \author Kris Thielemans */ @@ -41,766 +41,885 @@ using std::ios; -namespace distributed -{ - //timings and tests - bool rpc_time=false; - bool test_send_receive_times=false; - - double total_rpc_time=0; - double min_threshold=0.1; - double total_rpc_time_slaves=0.0; - double total_rpc_time_2=0.0; - bool test=false; - - - //global variable often used - int num_processors; - int length; - - int processor; - bool first_iteration; - int iteration_counter=0; - int image_buffer_size; - MPI_Status status; - float parameters[6]; //array for receiving image parameters - int sizes[6]; //array for receiving image dimensions - - stir::HighResWallClockTimer t; - - - //--------------------------------------Send Operations------------------------------------- - - void send_int_value(int value, int destination) - { - int i = value; +namespace distributed { +// timings and tests +bool rpc_time = false; +bool test_send_receive_times = false; + +double total_rpc_time = 0; +double min_threshold = 0.1; +double total_rpc_time_slaves = 0.0; +double total_rpc_time_2 = 0.0; +bool test = false; + +// global variable often used +int num_processors; +int length; + +int processor; +bool first_iteration; +int iteration_counter = 0; +int image_buffer_size; +MPI_Status status; +float parameters[6]; // array for receiving image parameters +int sizes[6]; // array for receiving image dimensions -#ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) {t.reset(); t.start();} +stir::HighResWallClockTimer t; + +//--------------------------------------Send Operations------------------------------------- + +void +send_int_value(int value, int destination) { + int i = value; + +#ifdef STIR_MPI_TIMINGS + if (test_send_receive_times) { + t.reset(); + t.start(); + } #endif - - if (destination == -1) MPI_Bcast(&i, 1, MPI_INT, 0, MPI_COMM_WORLD); - else MPI_Send(&i, 1, MPI_INT, destination, INT_TAG, MPI_COMM_WORLD); -#ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) t.stop(); - if (test_send_receive_times && t.value()>min_threshold) std::cout << "Master/Slave: sending int value took " << t.value() << " seconds" << std::endl; + if (destination == -1) + MPI_Bcast(&i, 1, MPI_INT, 0, MPI_COMM_WORLD); + else + MPI_Send(&i, 1, MPI_INT, destination, INT_TAG, MPI_COMM_WORLD); + +#ifdef STIR_MPI_TIMINGS + if (test_send_receive_times) + t.stop(); + if (test_send_receive_times && t.value() > min_threshold) + std::cout << "Master/Slave: sending int value took " << t.value() << " seconds" << std::endl; #endif +} + +void +send_string(const std::string& str, int tag, int destination) { + // prepare sending parameter info + length = str.length() + 1; + char* buf = new char[length]; + strcpy(buf, str.c_str()); + + // send parameter info +#ifdef STIR_MPI_TIMINGS + if (test_send_receive_times) { + t.reset(); + t.start(); } - - void send_string(const std::string& str, int tag, int destination) - { - //prepare sending parameter info - length=str.length()+1; - char * buf= new char[length]; - strcpy(buf, str.c_str()); - - //send parameter info -#ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) {t.reset(); t.start();} -#endif - - if (destination == -1) - for (int processor=1; processormin_threshold) std::cout << "Master/Slave: sending string took " << t.value() << " seconds" << std::endl; -#endif - - delete[] buf; +#endif + + if (destination == -1) + for (int processor = 1; processor < num_processors; processor++) { + MPI_Send(&length, 1, MPI_INT, processor, tag, MPI_COMM_WORLD); + MPI_Send(buf, length, MPI_CHAR, processor, tag, MPI_COMM_WORLD); + } + else { + MPI_Send(&length, 1, MPI_INT, destination, tag, MPI_COMM_WORLD); + MPI_Send(buf, length, MPI_CHAR, destination, tag, MPI_COMM_WORLD); } - - void send_bool_value(bool value, int tag, int destination) - { - int i; - if (value==true) i=1; - else i=0; - + #ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) {t.reset(); t.start();} + if (test_send_receive_times) + t.stop(); + if (test_send_receive_times && t.value() > min_threshold) + std::cout << "Master/Slave: sending string took " << t.value() << " seconds" << std::endl; #endif - - if (destination==-1||tag ==-1) MPI_Bcast(&i, 1, MPI_INT, 0, MPI_COMM_WORLD); - else MPI_Send(&i, 1, MPI_INT, destination, tag, MPI_COMM_WORLD); - + + delete[] buf; +} + +void +send_bool_value(bool value, int tag, int destination) { + int i; + if (value == true) + i = 1; + else + i = 0; + +#ifdef STIR_MPI_TIMINGS + if (test_send_receive_times) { + t.reset(); + t.start(); + } +#endif + + if (destination == -1 || tag == -1) + MPI_Bcast(&i, 1, MPI_INT, 0, MPI_COMM_WORLD); + else + MPI_Send(&i, 1, MPI_INT, destination, tag, MPI_COMM_WORLD); + #ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) t.stop(); - if (test_send_receive_times && t.value()>min_threshold) std::cout << "Master/Slave: sending bool value took " << t.value() << " seconds" << std::endl; + if (test_send_receive_times) + t.stop(); + if (test_send_receive_times && t.value() > min_threshold) + std::cout << "Master/Slave: sending bool value took " << t.value() << " seconds" << std::endl; #endif +} +void +send_int_values(int* values, int count, int tag, int destination) { +#ifdef STIR_MPI_TIMINGS + if (test_send_receive_times) { + t.reset(); + t.start(); } - - void send_int_values(int * values, int count, int tag, int destination) - { +#endif + + if (destination == -1) { + for (processor = 1; processor < num_processors; processor++) + MPI_Send(values, count, MPI_INT, processor, tag, MPI_COMM_WORLD); + } else + MPI_Send(values, count, MPI_INT, destination, tag, MPI_COMM_WORLD); + #ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) {t.reset(); t.start();} + if (test_send_receive_times) + t.stop(); + int my_rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &my_rank); /*Gets the rank of the Processor*/ + if (test_send_receive_times && t.value() > min_threshold) + std::cout << "Master/Slave " << my_rank << ": sending int values took " << t.value() << " seconds" << std::endl; #endif - - if (destination == -1) - { - for (processor=1; processormin_threshold) std::cout << "Master/Slave " << my_rank << ": sending int values took " << t.value() << " seconds"<< std::endl; + if (test_send_receive_times) { + t.reset(); + t.start(); + } #endif + if (destination == -1) { + for (processor = 1; processor < num_processors; processor++) + MPI_Send(values, count, MPI_DOUBLE, processor, tag, MPI_COMM_WORLD); + } else + MPI_Send(values, count, MPI_DOUBLE, destination, tag, MPI_COMM_WORLD); + +#ifdef STIR_MPI_TIMINGS + if (test_send_receive_times) + t.stop(); + if (test_send_receive_times && t.value() > min_threshold) + std::cout << "Master: sending double values took " << t.value() << " seconds" << std::endl; +#endif +} + +void +send_view_segment_numbers(const stir::ViewSegmentNumbers& vs_num, int tag, int destination) { + int int_values[2]; + int_values[0] = vs_num.view_num(); + int_values[1] = vs_num.segment_num(); + distributed::send_int_values(int_values, 2, tag, destination); +} + +void +send_image_parameters(const stir::DiscretisedDensity<3, float>* input_image_ptr, int tag, int destination) { + // cast to allow getting image dimensions and grid_spacing + const stir::VoxelsOnCartesianGrid* image = dynamic_cast*>(input_image_ptr); + + // input_image dimensions + int sizes[6]; + sizes[0] = image->get_min_x(); + sizes[1] = image->get_min_y(); + sizes[2] = image->get_min_z(); + sizes[3] = image->get_max_x(); + sizes[4] = image->get_max_y(); + sizes[5] = image->get_max_z(); + + // buffer for input_image-array + image_buffer_size = (sizes[3] - sizes[0] + 1) * (sizes[4] - sizes[1] + 1) * (sizes[5] - sizes[2] + 1); + + // get origin of input_image_ptr + stir::CartesianCoordinate3D origin = input_image_ptr->get_origin(); + + // get grid_spacing of input_image_ptr + stir::CartesianCoordinate3D grid_spacing = image->get_grid_spacing(); + + float parameters[6]; + parameters[0] = origin.x(); + parameters[1] = origin.y(); + parameters[2] = origin.z(); + parameters[3] = grid_spacing.x(); + parameters[4] = grid_spacing.y(); + parameters[5] = grid_spacing.z(); + + // Sending image parameters +#ifdef STIR_MPI_TIMINGS + if (test_send_receive_times) { + t.reset(); + t.start(); } - - void send_double_values(double * values, int count, int tag, int destination) - { +#endif + if (tag == -1 || destination == -1) + MPI_Bcast(parameters, 6, MPI_FLOAT, 0, MPI_COMM_WORLD); + else + MPI_Send(parameters, 6, MPI_FLOAT, destination, tag, MPI_COMM_WORLD); + +#ifdef STIR_MPI_TIMINGS + if (test_send_receive_times) + t.stop(); + if (test_send_receive_times && t.value() > min_threshold) + std::cout << "Master/Slave: sending image parameters took " << t.value() << " seconds" << std::endl; +#endif + + // send dimensions to construct IndexRange-object #ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) {t.reset(); t.start();} + if (test_send_receive_times) { + t.reset(); + t.start(); + } #endif - - if (destination == -1) - { - for (processor=1; processormin_threshold) std::cout << "Master: sending double values took " << t.value() << " seconds" << std::endl; + if (test_send_receive_times) + t.stop(); + if (test_send_receive_times && t.value() > min_threshold) + std::cout << "Master/Slave: sending image dimensions took " << t.value() << " seconds" << std::endl; #endif +} + +void +send_image_estimate(const stir::DiscretisedDensity<3, float>* input_image_ptr, int destination) { + float* image_buf = new float[image_buffer_size]; + + // serialize input_image into 1-demnsional array + std::copy(input_image_ptr->begin_all(), input_image_ptr->end_all(), image_buf); + + // send input image +#ifdef STIR_MPI_TIMINGS + if (test_send_receive_times) { + t.reset(); + t.start(); } +#endif - void send_view_segment_numbers(const stir::ViewSegmentNumbers& vs_num, int tag, int destination) - { - int int_values[2]; - int_values[0]=vs_num.view_num(); - int_values[1]=vs_num.segment_num(); - distributed::send_int_values(int_values, 2, tag, destination); + if (destination == -1) { + MPI_Bcast(image_buf, image_buffer_size, MPI_FLOAT, 0, MPI_COMM_WORLD); + // for (int processor=1; processor min_threshold) + std::cout << "Master/Slave: sending image values took " << t.value() << " seconds" << std::endl; +#endif +} + +void +send_exam_and_proj_data_info(const stir::ExamInfo& exam_info, const stir::ProjDataInfo& proj_data_info, int destination) { + // KT TODO there must be a better way than writing to a temporary file on disk + stir::shared_ptr exam_info_sptr(new stir::ExamInfo(exam_info)); + stir::ProjDataInterfile projection_data_for_slave(exam_info_sptr, proj_data_info.create_shared_clone(), "for_slave"); + + std::ifstream is("for_slave.hs", ios::binary); + + // get length of file: + is.seekg(0, ios::end); + length = is.tellg(); + is.seekg(0, ios::beg); + + length++; + + char* file_buffer = new char[length]; + + // read data as a block: + is.read(file_buffer, length - 1); + is.close(); + file_buffer[length - 1] = '\0'; + +#ifdef STIR_MPI_TIMINGS + if (test_send_receive_times) { + t.reset(); + t.start(); } +#endif - void send_image_parameters(const stir::DiscretisedDensity<3,float>* input_image_ptr, int tag, int destination) - { - //cast to allow getting image dimensions and grid_spacing - const stir::VoxelsOnCartesianGrid* image = - dynamic_cast* >(input_image_ptr); - - //input_image dimensions - int sizes[6]; - sizes[0] = image->get_min_x(); - sizes[1] = image->get_min_y(); - sizes[2] = image->get_min_z(); - sizes[3] = image->get_max_x(); - sizes[4] = image->get_max_y(); - sizes[5] = image->get_max_z(); - - //buffer for input_image-array - image_buffer_size=(sizes[3]-sizes[0]+1)*(sizes[4]-sizes[1]+1)*(sizes[5]-sizes[2]+1); - - //get origin of input_image_ptr - stir::CartesianCoordinate3D origin = input_image_ptr->get_origin(); - - //get grid_spacing of input_image_ptr - stir::CartesianCoordinate3D grid_spacing = image->get_grid_spacing(); - - float parameters[6]; - parameters[0] = origin.x(); - parameters[1] = origin.y(); - parameters[2] = origin.z(); - parameters[3] = grid_spacing.x(); - parameters[4] = grid_spacing.y(); - parameters[5] = grid_spacing.z(); - - - //Sending image parameters -#ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) {t.reset(); t.start();} -#endif - - if (tag == -1 || destination == -1) MPI_Bcast(parameters, 6, MPI_FLOAT, 0, MPI_COMM_WORLD); - else MPI_Send(parameters, 6, MPI_FLOAT, destination, tag, MPI_COMM_WORLD); - -#ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) t.stop(); - if (test_send_receive_times && t.value()>min_threshold) std::cout << "Master/Slave: sending image parameters took " << t.value() << " seconds" << std::endl; -#endif - - //send dimensions to construct IndexRange-object -#ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) {t.reset(); t.start();} -#endif - - if (tag == -1 || destination == -1) MPI_Bcast(sizes, 6, MPI_INT, 0, MPI_COMM_WORLD); - else MPI_Send(sizes, 6, MPI_INT, destination, tag, MPI_COMM_WORLD); - -#ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) t.stop(); - if (test_send_receive_times && t.value()>min_threshold) std::cout << "Master/Slave: sending image dimensions took " << t.value() << " seconds" << std::endl; -#endif - + // send text_buffer + if (destination == -1) + for (processor = 1; processor < num_processors; processor++) { + MPI_Send(&length, 1, MPI_INT, processor, PROJECTION_DATA_INFO_TAG, MPI_COMM_WORLD); + MPI_Send(file_buffer, length, MPI_CHAR, processor, PROJECTION_DATA_INFO_TAG, MPI_COMM_WORLD); + } + else { + MPI_Send(&length, 1, MPI_INT, destination, PROJECTION_DATA_INFO_TAG, MPI_COMM_WORLD); + MPI_Send(file_buffer, length, MPI_CHAR, destination, PROJECTION_DATA_INFO_TAG, MPI_COMM_WORLD); } - - void send_image_estimate(const stir::DiscretisedDensity<3,float>* input_image_ptr, int destination) - { - float *image_buf = new float[image_buffer_size]; - - //serialize input_image into 1-demnsional array - std::copy(input_image_ptr->begin_all(), input_image_ptr->end_all(), image_buf); - - //send input image -#ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) {t.reset(); t.start();} -#endif - - if (destination == -1) - { - MPI_Bcast(image_buf, image_buffer_size, MPI_FLOAT, 0, MPI_COMM_WORLD); - //for (int processor=1; processormin_threshold) std::cout << "Master/Slave: sending image values took " << t.value() << " seconds" << std::endl; + if (test_send_receive_times) + t.stop(); + if (test_send_receive_times && t.value() > min_threshold) + std::cout << "Master: sending projection_data_info took " << t.value() << " seconds" << std::endl; #endif + + if (remove("for_slave.hs") != 0) + stir::warning("Error deleting temporary file"); + if (remove("for_slave.s") != 0) + stir::warning("Error deleting temporary file"); + + delete[] file_buffer; +} + +void +send_related_viewgrams(stir::RelatedViewgrams* viewgrams, int destination) { + // broadcast count of viewgrams to be received + int num_viewgrams = viewgrams->get_num_viewgrams(); + + // run through viewgrams + stir::RelatedViewgrams::iterator viewgrams_iter = viewgrams->begin(); + const stir::RelatedViewgrams::iterator viewgrams_end = viewgrams->end(); + + send_int_values(&num_viewgrams, 1, VIEWGRAM_COUNT_TAG, destination); + + while (viewgrams_iter != viewgrams_end) { + send_viewgram(*viewgrams_iter, destination); + ++viewgrams_iter; } - - void send_exam_and_proj_data_info(const stir::ExamInfo& exam_info, const stir::ProjDataInfo& proj_data_info, int destination) - { - // KT TODO there must be a better way than writing to a temporary file on disk - stir::shared_ptr exam_info_sptr(new stir::ExamInfo(exam_info)); - stir::ProjDataInterfile projection_data_for_slave(exam_info_sptr, proj_data_info.create_shared_clone(),"for_slave"); - - std::ifstream is("for_slave.hs", ios::binary ); - - // get length of file: - is.seekg (0, ios::end); - length = is.tellg(); - is.seekg (0, ios::beg); - - length++; - - char * file_buffer = new char[length]; - - // read data as a block: - is.read (file_buffer,length-1); - is.close(); - file_buffer[length-1]='\0'; - -#ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) {t.reset(); t.start();} -#endif - - //send text_buffer - if (destination==-1) - for (processor=1; processormin_threshold) std::cout << "Master: sending projection_data_info took " << t.value() << " seconds" << std::endl; -#endif - - - if( remove( "for_slave.hs" ) != 0 ) - stir::warning( "Error deleting temporary file" ); - if( remove( "for_slave.s" ) != 0 ) - stir::warning( "Error deleting temporary file" ); - - delete[] file_buffer; - } - - void send_related_viewgrams(stir::RelatedViewgrams* viewgrams, int destination) - { - //broadcast count of viewgrams to be received - int num_viewgrams=viewgrams->get_num_viewgrams(); - - //run through viewgrams - stir::RelatedViewgrams::iterator viewgrams_iter = viewgrams->begin(); - const stir::RelatedViewgrams::iterator viewgrams_end = viewgrams->end(); - - send_int_values(&num_viewgrams, 1, VIEWGRAM_COUNT_TAG, destination); - - while (viewgrams_iter!= viewgrams_end) - { - send_viewgram(*viewgrams_iter, destination); - ++viewgrams_iter; - } +} + +void +send_viewgram(const stir::Viewgram& viewgram, int destination) { + // send dimensions of viewgram (axial and tangential positions and the view and segment numbers) + int viewgram_values[7]; + viewgram_values[0] = viewgram.get_min_axial_pos_num(); + viewgram_values[1] = viewgram.get_max_axial_pos_num(); + viewgram_values[2] = viewgram.get_min_tangential_pos_num(); + viewgram_values[3] = viewgram.get_max_tangential_pos_num(); + viewgram_values[4] = viewgram.get_view_num(); + viewgram_values[5] = viewgram.get_segment_num(); + viewgram_values[6] = viewgram.get_timing_pos_num(); + + send_int_values(viewgram_values, 7, VIEWGRAM_DIMENSIONS_TAG, destination); + + // allocate send-buffer + int buffer_size = (viewgram_values[1] - viewgram_values[0] + 1) * (viewgram_values[3] - viewgram_values[2] + 1); + float* viewgram_buf = new float[buffer_size]; + std::copy(viewgram.begin_all(), viewgram.end_all(), viewgram_buf); + + // send array +#ifdef STIR_MPI_TIMINGS + if (test_send_receive_times) { + t.reset(); + t.start(); } - - void send_viewgram(const stir::Viewgram& viewgram, int destination) - { - //send dimensions of viewgram (axial and tangential positions and the view and segment numbers) - int viewgram_values[7]; - viewgram_values[0] = viewgram.get_min_axial_pos_num(); - viewgram_values[1] = viewgram.get_max_axial_pos_num(); - viewgram_values[2] = viewgram.get_min_tangential_pos_num(); - viewgram_values[3] = viewgram.get_max_tangential_pos_num(); - viewgram_values[4] = viewgram.get_view_num(); - viewgram_values[5] = viewgram.get_segment_num(); - viewgram_values[6] = viewgram.get_timing_pos_num(); - - send_int_values(viewgram_values, 7, VIEWGRAM_DIMENSIONS_TAG, destination); - - //allocate send-buffer - int buffer_size=(viewgram_values[1]-viewgram_values[0]+1)*(viewgram_values[3]-viewgram_values[2]+1); - float *viewgram_buf = new float[buffer_size]; - std::copy(viewgram.begin_all(), viewgram.end_all(), viewgram_buf); - - //send array -#ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) {t.reset(); t.start();} #endif - MPI_Send(viewgram_buf, buffer_size, MPI_FLOAT, destination, VIEWGRAM_TAG, MPI_COMM_WORLD); + MPI_Send(viewgram_buf, buffer_size, MPI_FLOAT, destination, VIEWGRAM_TAG, MPI_COMM_WORLD); #ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) t.stop(); - if (test_send_receive_times && t.value()>min_threshold) std::cout << "Master: sending viewgram took " << t.value() << " seconds" << std::endl; + if (test_send_receive_times) + t.stop(); + if (test_send_receive_times && t.value() > min_threshold) + std::cout << "Master: sending viewgram took " << t.value() << " seconds" << std::endl; #endif - - delete[] viewgram_buf; - } - void send_projectors(const stir::shared_ptr &proj_pair_sptr, int destination) - { - //send registered name of projector pair - distributed::send_string(proj_pair_sptr->get_registered_name(), REGISTERED_NAME_TAG, destination); - - //send parameter info of projector pair - distributed::send_string(proj_pair_sptr->stir::ParsingObject::parameter_info(), PARAMETER_INFO_TAG, destination); + delete[] viewgram_buf; +} - } - - //--------------------------------------Receive Operations------------------------------------- - - int receive_int_value(int source) - { - int i; +void +send_projectors(const stir::shared_ptr& proj_pair_sptr, int destination) { + // send registered name of projector pair + distributed::send_string(proj_pair_sptr->get_registered_name(), REGISTERED_NAME_TAG, destination); + + // send parameter info of projector pair + distributed::send_string(proj_pair_sptr->stir::ParsingObject::parameter_info(), PARAMETER_INFO_TAG, destination); +} + +//--------------------------------------Receive Operations------------------------------------- + +int +receive_int_value(int source) { + int i; #ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) {t.reset(); t.start();} + if (test_send_receive_times) { + t.reset(); + t.start(); + } #endif - - if (source==-1) MPI_Bcast(&i, 1, MPI_INT, 0, MPI_COMM_WORLD); - else MPI_Recv(&i, 1, MPI_INT, source, INT_TAG, MPI_COMM_WORLD, &status); - + + if (source == -1) + MPI_Bcast(&i, 1, MPI_INT, 0, MPI_COMM_WORLD); + else + MPI_Recv(&i, 1, MPI_INT, source, INT_TAG, MPI_COMM_WORLD, &status); + #ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) t.stop(); - if (test_send_receive_times && t.value()>min_threshold) std::cout << "Slave: received int value after " << t.value() << " seconds" << std::endl; + if (test_send_receive_times) + t.stop(); + if (test_send_receive_times && t.value() > min_threshold) + std::cout << "Slave: received int value after " << t.value() << " seconds" << std::endl; #endif - - return i; - } - - std::string receive_string(int tag, int source) - { + + return i; +} + +std::string +receive_string(int tag, int source) { #ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) {t.reset(); t.start();} + if (test_send_receive_times) { + t.reset(); + t.start(); + } #endif - - MPI_Recv(&length, 1, MPI_INT, source, tag, MPI_COMM_WORLD, &status); - char * buf= new char[length]; - MPI_Recv(buf, length, MPI_CHAR, source, tag, MPI_COMM_WORLD, & status); - + + MPI_Recv(&length, 1, MPI_INT, source, tag, MPI_COMM_WORLD, &status); + char* buf = new char[length]; + MPI_Recv(buf, length, MPI_CHAR, source, tag, MPI_COMM_WORLD, &status); + #ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) t.stop(); - if (test_send_receive_times && t.value()>min_threshold) std::cout << "Slave: received string value after " << t.value() << " seconds" << std::endl; + if (test_send_receive_times) + t.stop(); + if (test_send_receive_times && t.value() > min_threshold) + std::cout << "Slave: received string value after " << t.value() << " seconds" << std::endl; #endif - - //convert to string - std::string str(buf); - delete[] buf; - return str; - } - - void receive_and_initialize_projectors(stir::shared_ptr &projector_pair_ptr, int source) - { - //Receive projector-pair registered_keyword -#ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) {t.reset(); t.start();} -#endif - // KT TODO. use receive_string() to clean-up code - - MPI_Recv(&length, 1, MPI_INT, source, REGISTERED_NAME_TAG, MPI_COMM_WORLD, &status); - char * buf= new char[length]; - MPI_Recv(buf, length, MPI_CHAR, source, REGISTERED_NAME_TAG, MPI_COMM_WORLD, & status); - -#ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) t.stop(); - if (test_send_receive_times && t.value()>min_threshold) std::cout << "Slave: received REGISTERED_NAME_TAG value after " << t.value() << " seconds" << std::endl; -#endif - - //convert to string - std::string registered_name_proj_pair(buf); - delete[] buf; - - //Receive parameter info -#ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) {t.reset(); t.start();} -#endif - - MPI_Recv(&length, 1, MPI_INT, source, PARAMETER_INFO_TAG, MPI_COMM_WORLD, &status); - - char * buf3= new char[length]; - MPI_Recv(buf3, length, MPI_CHAR, source, PARAMETER_INFO_TAG, MPI_COMM_WORLD, & status); - -#ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) t.stop(); - if (test_send_receive_times && t.value()>min_threshold) std::cout << "Slave: received parameter info value after " << t.value() << " seconds" << std::endl; -#endif - - //convert to string - std::string parameter_info(buf3); - delete[] buf3; - - //construct new Backprojector and Forward Projector, projector_pair_ptr - std::istringstream parameter_info_stream(parameter_info); - - projector_pair_ptr. - reset(stir::RegisteredObject:: - read_registered_object(¶meter_info_stream, registered_name_proj_pair)); - } - - bool receive_bool_value(int tag, int source) - { - int i = 0; // initialise to avoid compiler warning - + + // convert to string + std::string str(buf); + delete[] buf; + return str; +} + +void +receive_and_initialize_projectors(stir::shared_ptr& projector_pair_ptr, int source) { + // Receive projector-pair registered_keyword #ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) {t.reset(); t.start();} + if (test_send_receive_times) { + t.reset(); + t.start(); + } #endif - - if (tag==-1 || source == -1) MPI_Bcast(&i, 1, MPI_INT, 0, MPI_COMM_WORLD); - else MPI_Recv(&i, 1, MPI_INT, source, tag, MPI_COMM_WORLD, &status); - + // KT TODO. use receive_string() to clean-up code + + MPI_Recv(&length, 1, MPI_INT, source, REGISTERED_NAME_TAG, MPI_COMM_WORLD, &status); + char* buf = new char[length]; + MPI_Recv(buf, length, MPI_CHAR, source, REGISTERED_NAME_TAG, MPI_COMM_WORLD, &status); + #ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) t.stop(); - if (test_send_receive_times && t.value()>min_threshold) std::cout << "Slave: received bool value after " << t.value() << " seconds" << std::endl; + if (test_send_receive_times) + t.stop(); + if (test_send_receive_times && t.value() > min_threshold) + std::cout << "Slave: received REGISTERED_NAME_TAG value after " << t.value() << " seconds" << std::endl; #endif - - return i!=0; - } - - MPI_Status receive_int_values(int * values, int count, int tag) - { + // convert to string + std::string registered_name_proj_pair(buf); + delete[] buf; + + // Receive parameter info #ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) {t.reset(); t.start();} + if (test_send_receive_times) { + t.reset(); + t.start(); + } #endif - if (tag == ARBITRARY_TAG) MPI_Recv(values, count, MPI_INT, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status); - else MPI_Recv(values, count, MPI_INT, MPI_ANY_SOURCE, tag, MPI_COMM_WORLD, &status); + MPI_Recv(&length, 1, MPI_INT, source, PARAMETER_INFO_TAG, MPI_COMM_WORLD, &status); + + char* buf3 = new char[length]; + MPI_Recv(buf3, length, MPI_CHAR, source, PARAMETER_INFO_TAG, MPI_COMM_WORLD, &status); #ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) t.stop(); - int my_rank=0; - MPI_Comm_rank(MPI_COMM_WORLD, &my_rank) ; /*Gets the rank of the Processor*/ - if (test_send_receive_times && t.value()>min_threshold) std::cout << "Master/Slave " << my_rank << ": received "<< count << " int values after " << t.value() << " seconds"<< std::endl; + if (test_send_receive_times) + t.stop(); + if (test_send_receive_times && t.value() > min_threshold) + std::cout << "Slave: received parameter info value after " << t.value() << " seconds" << std::endl; #endif - return status; + // convert to string + std::string parameter_info(buf3); + delete[] buf3; + + // construct new Backprojector and Forward Projector, projector_pair_ptr + std::istringstream parameter_info_stream(parameter_info); + + projector_pair_ptr.reset(stir::RegisteredObject::read_registered_object(¶meter_info_stream, + registered_name_proj_pair)); +} + +bool +receive_bool_value(int tag, int source) { + int i = 0; // initialise to avoid compiler warning + +#ifdef STIR_MPI_TIMINGS + if (test_send_receive_times) { + t.reset(); + t.start(); } - - MPI_Status receive_double_values(double * values, int count, int tag) - { +#endif + + if (tag == -1 || source == -1) + MPI_Bcast(&i, 1, MPI_INT, 0, MPI_COMM_WORLD); + else + MPI_Recv(&i, 1, MPI_INT, source, tag, MPI_COMM_WORLD, &status); + +#ifdef STIR_MPI_TIMINGS + if (test_send_receive_times) + t.stop(); + if (test_send_receive_times && t.value() > min_threshold) + std::cout << "Slave: received bool value after " << t.value() << " seconds" << std::endl; +#endif + + return i != 0; +} + +MPI_Status +receive_int_values(int* values, int count, int tag) { #ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) {t.reset(); t.start();} + if (test_send_receive_times) { + t.reset(); + t.start(); + } #endif - - if (tag == ARBITRARY_TAG) MPI_Recv(values, count, MPI_DOUBLE, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status); - else MPI_Recv(values, count, MPI_DOUBLE, MPI_ANY_SOURCE, tag, MPI_COMM_WORLD, &status); - + + if (tag == ARBITRARY_TAG) + MPI_Recv(values, count, MPI_INT, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status); + else + MPI_Recv(values, count, MPI_INT, MPI_ANY_SOURCE, tag, MPI_COMM_WORLD, &status); + #ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) t.stop(); - if (test_send_receive_times && t.value()>min_threshold) std::cout << "Slave: received double values after " << t.value() << " seconds" << std::endl; + if (test_send_receive_times) + t.stop(); + int my_rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &my_rank); /*Gets the rank of the Processor*/ + if (test_send_receive_times && t.value() > min_threshold) + std::cout << "Master/Slave " << my_rank << ": received " << count << " int values after " << t.value() << " seconds" + << std::endl; #endif - - return status; + + return status; +} + +MPI_Status +receive_double_values(double* values, int count, int tag) { + +#ifdef STIR_MPI_TIMINGS + if (test_send_receive_times) { + t.reset(); + t.start(); } +#endif - MPI_Status receive_view_segment_numbers(stir::ViewSegmentNumbers& vs_num, int tag) - { - int int_values[2]; - const MPI_Status status = distributed::receive_int_values(int_values, 2, tag); - vs_num.view_num() = int_values[0]; - vs_num.segment_num() = int_values[1]; - return status; + if (tag == ARBITRARY_TAG) + MPI_Recv(values, count, MPI_DOUBLE, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status); + else + MPI_Recv(values, count, MPI_DOUBLE, MPI_ANY_SOURCE, tag, MPI_COMM_WORLD, &status); + +#ifdef STIR_MPI_TIMINGS + if (test_send_receive_times) + t.stop(); + if (test_send_receive_times && t.value() > min_threshold) + std::cout << "Slave: received double values after " << t.value() << " seconds" << std::endl; +#endif + + return status; +} + +MPI_Status +receive_view_segment_numbers(stir::ViewSegmentNumbers& vs_num, int tag) { + int int_values[2]; + const MPI_Status status = distributed::receive_int_values(int_values, 2, tag); + vs_num.view_num() = int_values[0]; + vs_num.segment_num() = int_values[1]; + return status; +} + +void +receive_and_set_image_parameters(stir::shared_ptr>& image_ptr, int& buffer, int tag, + int source) { + // receive image parameters (origin and grid_spacing) +#ifdef STIR_MPI_TIMINGS + if (test_send_receive_times) { + t.reset(); + t.start(); } - - void receive_and_set_image_parameters(stir::shared_ptr > &image_ptr, int &buffer, int tag, int source) - { - //receive image parameters (origin and grid_spacing) -#ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) {t.reset(); t.start();} #endif - - if (tag==-1) MPI_Bcast(parameters, 6, MPI_FLOAT, source, MPI_COMM_WORLD); - else MPI_Recv(parameters, 6, MPI_FLOAT, source, tag, MPI_COMM_WORLD, &status); + + if (tag == -1) + MPI_Bcast(parameters, 6, MPI_FLOAT, source, MPI_COMM_WORLD); + else + MPI_Recv(parameters, 6, MPI_FLOAT, source, tag, MPI_COMM_WORLD, &status); #ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) t.stop(); - if (test_send_receive_times && t.value()>min_threshold) std::cout << "Slave: received origin and grid_spacing after " << t.value() << " seconds" << std::endl; - - //receive dimensions of Image-data - if (test_send_receive_times) {t.reset(); t.start();} + if (test_send_receive_times) + t.stop(); + if (test_send_receive_times && t.value() > min_threshold) + std::cout << "Slave: received origin and grid_spacing after " << t.value() << " seconds" << std::endl; + + // receive dimensions of Image-data + if (test_send_receive_times) { + t.reset(); + t.start(); + } #endif - - if (tag==-1) MPI_Bcast(sizes, 6, MPI_INT, source, MPI_COMM_WORLD); - else MPI_Recv(sizes, 6, MPI_INT, source, tag, MPI_COMM_WORLD, &status); -#ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) t.stop(); - if (test_send_receive_times && t.value()>min_threshold) std::cout << "Slave: received image dimensions after " << t.value() << " seconds" << std::endl; + if (tag == -1) + MPI_Bcast(sizes, 6, MPI_INT, source, MPI_COMM_WORLD); + else + MPI_Recv(sizes, 6, MPI_INT, source, tag, MPI_COMM_WORLD, &status); + +#ifdef STIR_MPI_TIMINGS + if (test_send_receive_times) + t.stop(); + if (test_send_receive_times && t.value() > min_threshold) + std::cout << "Slave: received image dimensions after " << t.value() << " seconds" << std::endl; #endif - - buffer=(sizes[3]-sizes[0]+1)*(sizes[4]-sizes[1]+1)*(sizes[5]-sizes[2]+1); - - //construct new index range from received values - stir::IndexRange<3> - range(stir::CartesianCoordinate3D(sizes[2],sizes[1],sizes[0]), - stir::CartesianCoordinate3D(sizes[5],sizes[4],sizes[3])); - - //construct new image object to save received input-image values - stir::CartesianCoordinate3D origin( parameters[2], parameters[1], parameters[0]); - stir::CartesianCoordinate3D grid_spacing(parameters[5], parameters[4], parameters[3]); + + buffer = (sizes[3] - sizes[0] + 1) * (sizes[4] - sizes[1] + 1) * (sizes[5] - sizes[2] + 1); + + // construct new index range from received values + stir::IndexRange<3> range(stir::CartesianCoordinate3D(sizes[2], sizes[1], sizes[0]), + stir::CartesianCoordinate3D(sizes[5], sizes[4], sizes[3])); + + // construct new image object to save received input-image values + stir::CartesianCoordinate3D origin(parameters[2], parameters[1], parameters[0]); + stir::CartesianCoordinate3D grid_spacing(parameters[5], parameters[4], parameters[3]); #if 0 stir::VoxelsOnCartesianGrid *voxels = new stir::VoxelsOnCartesianGrid(range, origin, grid_spacing); stir::shared_ptr > tmpPtr(voxels); //point pointer to newly created image_estimate - image_ptr = *(stir::shared_ptr >*)&tmpPtr; + image_ptr = *(stir::shared_ptr >*)&tmpPtr; #else - image_ptr.reset(new stir::VoxelsOnCartesianGrid(range, origin, grid_spacing)); + image_ptr.reset(new stir::VoxelsOnCartesianGrid(range, origin, grid_spacing)); #endif - } - - MPI_Status receive_image_values_and_fill_image_ptr(stir::shared_ptr > &image_ptr, int buffer_size, int source) - { - //buffer for input_image - float * buffer = new float[buffer_size]; - if (buffer == 0) - stir::error("Ran out of memory"); +} -#ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) {t.reset(); t.start();} +MPI_Status +receive_image_values_and_fill_image_ptr(stir::shared_ptr>& image_ptr, int buffer_size, + int source) { + // buffer for input_image + float* buffer = new float[buffer_size]; + if (buffer == 0) + stir::error("Ran out of memory"); + +#ifdef STIR_MPI_TIMINGS + if (test_send_receive_times) { + t.reset(); + t.start(); + } #endif - - MPI_Bcast(buffer, buffer_size, MPI_FLOAT, source, MPI_COMM_WORLD); - //else MPI_Recv(buffer, buffer_size, MPI_FLOAT, source, IMAGE_ESTIMATE_TAG, MPI_COMM_WORLD, & status); -#ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) t.stop(); - if (test_send_receive_times && t.value()>min_threshold) std::cout << "Slave: received image values after " << t.value() << " seconds" << std::endl; + MPI_Bcast(buffer, buffer_size, MPI_FLOAT, source, MPI_COMM_WORLD); + // else MPI_Recv(buffer, buffer_size, MPI_FLOAT, source, IMAGE_ESTIMATE_TAG, MPI_COMM_WORLD, & status); + +#ifdef STIR_MPI_TIMINGS + if (test_send_receive_times) + t.stop(); + if (test_send_receive_times && t.value() > min_threshold) + std::cout << "Slave: received image values after " << t.value() << " seconds" << std::endl; #endif - - std::copy(buffer, buffer + buffer_size, image_ptr->begin_all()); - delete[] buffer; - return status; - } - - void receive_and_construct_exam_and_proj_data_info_ptr(stir::shared_ptr& exam_info_sptr, - stir::shared_ptr& proj_data_info_sptr, - int source) - { - int len; - //receive projection data info pointer + std::copy(buffer, buffer + buffer_size, image_ptr->begin_all()); + delete[] buffer; + + return status; +} + +void +receive_and_construct_exam_and_proj_data_info_ptr(stir::shared_ptr& exam_info_sptr, + stir::shared_ptr& proj_data_info_sptr, int source) { + int len; + // receive projection data info pointer #ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) {t.reset(); t.start();} + if (test_send_receive_times) { + t.reset(); + t.start(); + } #endif - - MPI_Recv(&len, 1, MPI_INT, source, PROJECTION_DATA_INFO_TAG, MPI_COMM_WORLD, &status); - boost::shared_array proj_data_info_buf(new char[len]); - MPI_Recv(proj_data_info_buf.get(), len, MPI_CHAR, source, PROJECTION_DATA_INFO_TAG, MPI_COMM_WORLD, & status); -#ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) t.stop(); - if (test_send_receive_times && t.value()>min_threshold) std::cout << "Slave: received proj_data_info after " << t.value() << " seconds" << std::endl; + MPI_Recv(&len, 1, MPI_INT, source, PROJECTION_DATA_INFO_TAG, MPI_COMM_WORLD, &status); + boost::shared_array proj_data_info_buf(new char[len]); + MPI_Recv(proj_data_info_buf.get(), len, MPI_CHAR, source, PROJECTION_DATA_INFO_TAG, MPI_COMM_WORLD, &status); + +#ifdef STIR_MPI_TIMINGS + if (test_send_receive_times) + t.stop(); + if (test_send_receive_times && t.value() > min_threshold) + std::cout << "Slave: received proj_data_info after " << t.value() << " seconds" << std::endl; #endif - - //construct projector_info_ptr - std::istringstream projector_info_ptr_stream(proj_data_info_buf.get()); - - stir::InterfileHeader hdr; - std::ios::off_type offset = projector_info_ptr_stream.tellg(); - if (!hdr.parse(projector_info_ptr_stream, false)) // parse without warnings - { - stir::error("Error receiving projection data info. Text does not seem to be in Interfile format"); - } - projector_info_ptr_stream.seekg(offset); - exam_info_sptr = hdr.get_exam_info_sptr(); - if (hdr.get_exam_info().imaging_modality.get_modality() == - stir::ImagingModality::NM) - { - stir::InterfilePDFSHeaderSPECT hdr; - if (!hdr.parse(projector_info_ptr_stream)) - stir::error("Error receiving projection data info. Text does not seem to be in Interfile format"); - proj_data_info_sptr = - stir::shared_ptr (hdr.data_info_sptr->clone()); - } - else - { - stir::InterfilePDFSHeader hdr; - if (!hdr.parse(projector_info_ptr_stream)) - stir::error("Error receiving projection data info. Text does not seem to be in Interfile format"); - - proj_data_info_sptr = - stir::shared_ptr (hdr.data_info_ptr->clone()); - } - } - - void receive_and_construct_related_viewgrams(stir::RelatedViewgrams*& viewgrams, - const stir::shared_ptr& proj_data_info_ptr, - const stir::shared_ptr symmetries_sptr, - int source) + + // construct projector_info_ptr + std::istringstream projector_info_ptr_stream(proj_data_info_buf.get()); + + stir::InterfileHeader hdr; + std::ios::off_type offset = projector_info_ptr_stream.tellg(); + if (!hdr.parse(projector_info_ptr_stream, false)) // parse without warnings { - //receive count of viewgrams - int int_values[1]; - status=distributed::receive_int_values(int_values, 1, VIEWGRAM_COUNT_TAG); - - std::vector > viewgrams_vector; - - viewgrams_vector.reserve(int_values[0]); - - for (int i=0; i* vg = NULL; - - //receive a viewgram from Master - receive_and_construct_viewgram(vg, proj_data_info_ptr, source); - - //add viewgram to Viewgram-vector - viewgrams_vector.push_back(*vg); - delete vg; - } - - //use viewgram-vector and symmetries-pointer to construct related viewgrams element - viewgrams = new stir::RelatedViewgrams(viewgrams_vector, symmetries_sptr); + stir::error("Error receiving projection data info. Text does not seem to be in Interfile format"); } - - void receive_and_construct_viewgram(stir::Viewgram*& viewgram_ptr, - const stir::shared_ptr& proj_data_info_ptr, - int source) - { + projector_info_ptr_stream.seekg(offset); + exam_info_sptr = hdr.get_exam_info_sptr(); + if (hdr.get_exam_info().imaging_modality.get_modality() == stir::ImagingModality::NM) { + stir::InterfilePDFSHeaderSPECT hdr; + if (!hdr.parse(projector_info_ptr_stream)) + stir::error("Error receiving projection data info. Text does not seem to be in Interfile format"); + proj_data_info_sptr = stir::shared_ptr(hdr.data_info_sptr->clone()); + } else { + stir::InterfilePDFSHeader hdr; + if (!hdr.parse(projector_info_ptr_stream)) + stir::error("Error receiving projection data info. Text does not seem to be in Interfile format"); + + proj_data_info_sptr = stir::shared_ptr(hdr.data_info_ptr->clone()); + } +} + +void +receive_and_construct_related_viewgrams(stir::RelatedViewgrams*& viewgrams, + const stir::shared_ptr& proj_data_info_ptr, + const stir::shared_ptr symmetries_sptr, + int source) { + // receive count of viewgrams + int int_values[1]; + status = distributed::receive_int_values(int_values, 1, VIEWGRAM_COUNT_TAG); + + std::vector> viewgrams_vector; + + viewgrams_vector.reserve(int_values[0]); + + for (int i = 0; i < int_values[0]; i++) { + stir::Viewgram* vg = NULL; + + // receive a viewgram from Master + receive_and_construct_viewgram(vg, proj_data_info_ptr, source); + + // add viewgram to Viewgram-vector + viewgrams_vector.push_back(*vg); + delete vg; + } + + // use viewgram-vector and symmetries-pointer to construct related viewgrams element + viewgrams = new stir::RelatedViewgrams(viewgrams_vector, symmetries_sptr); +} + +void +receive_and_construct_viewgram(stir::Viewgram*& viewgram_ptr, + const stir::shared_ptr& proj_data_info_ptr, int source) { #ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) {t.reset(); t.start();} + if (test_send_receive_times) { + t.reset(); + t.start(); + } #endif - //receive dimension of viewgram (vlues 0-3) and view_num + segment_num (values 4-5) and timing_pos_num (value 6) - int viewgram_values[7]; - - status = receive_int_values(viewgram_values, 7, VIEWGRAM_DIMENSIONS_TAG); - - const int v_num = viewgram_values[4]; - const int s_num = viewgram_values[5]; - const int t_num = viewgram_values[6]; - - viewgram_ptr= new stir::Viewgram(proj_data_info_ptr, v_num, s_num, t_num); - - //allocate receive-buffer - const int buffer_size=(viewgram_values[1]-viewgram_values[0]+1)*(viewgram_values[3]-viewgram_values[2]+1); - float *viewgram_buf = new float[buffer_size]; - - //receive viewgram array + // receive dimension of viewgram (vlues 0-3) and view_num + segment_num (values 4-5) and timing_pos_num (value 6) + int viewgram_values[7]; + + status = receive_int_values(viewgram_values, 7, VIEWGRAM_DIMENSIONS_TAG); - MPI_Recv(viewgram_buf, buffer_size, MPI_FLOAT, source, VIEWGRAM_TAG, MPI_COMM_WORLD, &status); + const int v_num = viewgram_values[4]; + const int s_num = viewgram_values[5]; + const int t_num = viewgram_values[6]; - std::copy(viewgram_buf, viewgram_buf+buffer_size, viewgram_ptr->begin_all()); + viewgram_ptr = new stir::Viewgram(proj_data_info_ptr, v_num, s_num, t_num); - delete[] viewgram_buf; + // allocate receive-buffer + const int buffer_size = (viewgram_values[1] - viewgram_values[0] + 1) * (viewgram_values[3] - viewgram_values[2] + 1); + float* viewgram_buf = new float[buffer_size]; + + // receive viewgram array + + MPI_Recv(viewgram_buf, buffer_size, MPI_FLOAT, source, VIEWGRAM_TAG, MPI_COMM_WORLD, &status); + + std::copy(viewgram_buf, viewgram_buf + buffer_size, viewgram_ptr->begin_all()); + + delete[] viewgram_buf; #ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) t.stop(); - if (test_send_receive_times && t.value()>min_threshold) std::cout << "Slave: received viewgram_array after " << t.value() << " seconds" << std::endl; + if (test_send_receive_times) + t.stop(); + if (test_send_receive_times && t.value() > min_threshold) + std::cout << "Slave: received viewgram_array after " << t.value() << " seconds" << std::endl; #endif +} - } - - //--------------------------------------Reduce Operations------------------------------------- - - void reduce_received_output_image(stir::DiscretisedDensity<3,float>* output_image_ptr, int destination) - { - #ifdef STIR_MPI_TIMINGS - stir::HighResWallClockTimer fulltimer; - fulltimer.reset(); fulltimer.start(); -#endif - float *output_buf = new float[image_buffer_size]; - float *image_buf = new float[image_buffer_size]; - - //initialize output_buffer to zero. - //contributions from all slaves will be added into it - //KTXXXfor (int i=0; i* output_image_ptr, int destination) { #ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) {t.reset(); t.start();} + stir::HighResWallClockTimer fulltimer; + fulltimer.reset(); + fulltimer.start(); #endif + float* output_buf = new float[image_buffer_size]; + float* image_buf = new float[image_buffer_size]; - MPI_Reduce(image_buf, output_buf, image_buffer_size, MPI_FLOAT, MPI_SUM, destination, MPI_COMM_WORLD); - delete[] image_buf; + // initialize output_buffer to zero. + // contributions from all slaves will be added into it + // KTXXXfor (int i=0; imin_threshold) std::cout << "Master: reduced output_image after " << t.value() << " seconds" << std::endl; + if (test_send_receive_times) { + t.reset(); + t.start(); + } #endif - std::cout <<"Master: output_image reduced.\n"; - - //get input_image from 1-demnsional array - std::copy(output_buf, output_buf+image_buffer_size, output_image_ptr->begin_all()); - delete[] output_buf; + MPI_Reduce(image_buf, output_buf, image_buffer_size, MPI_FLOAT, MPI_SUM, destination, MPI_COMM_WORLD); + delete[] image_buf; + #ifdef STIR_MPI_TIMINGS - fulltimer.stop(); - if (test_send_receive_times /*&& fulltimer.value()>min_threshold*/) std::cout << "Master: reduced output_image total after " << fulltimer.value() << " seconds" << std::endl; + if (test_send_receive_times) + t.stop(); + if (test_send_receive_times && t.value() > min_threshold) + std::cout << "Master: reduced output_image after " << t.value() << " seconds" << std::endl; #endif - } - - void reduce_output_image(stir::shared_ptr > &output_image_ptr, int image_buffer_size, int my_rank_ignored, int destination) - { - float *output_buf = new float[image_buffer_size]; - float *image_buf = new float[image_buffer_size]; - - //serialize input_image into 1-demnsional array - //KTXXXstd::copy(output_image_ptr->begin_all(), output_image_ptr->end_all(), output_buf); - std::copy(output_image_ptr->begin_all(), output_image_ptr->end_all(), image_buf); - - //reduction of output_image at master + + std::cout << "Master: output_image reduced.\n"; + + // get input_image from 1-demnsional array + std::copy(output_buf, output_buf + image_buffer_size, output_image_ptr->begin_all()); + delete[] output_buf; #ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) {t.reset(); t.start();} + fulltimer.stop(); + if (test_send_receive_times /*&& fulltimer.value()>min_threshold*/) + std::cout << "Master: reduced output_image total after " << fulltimer.value() << " seconds" << std::endl; #endif +} - MPI_Reduce(image_buf, output_buf, image_buffer_size, MPI_FLOAT, MPI_SUM, destination, MPI_COMM_WORLD); - delete[] image_buf; +void +reduce_output_image(stir::shared_ptr>& output_image_ptr, int image_buffer_size, + int my_rank_ignored, int destination) { + float* output_buf = new float[image_buffer_size]; + float* image_buf = new float[image_buffer_size]; + // serialize input_image into 1-demnsional array + // KTXXXstd::copy(output_image_ptr->begin_all(), output_image_ptr->end_all(), output_buf); + std::copy(output_image_ptr->begin_all(), output_image_ptr->end_all(), image_buf); + + // reduction of output_image at master #ifdef STIR_MPI_TIMINGS - if (test_send_receive_times) t.stop(); - int my_rank=0; - MPI_Comm_rank(MPI_COMM_WORLD, &my_rank) ; /*Gets the rank of the Processor*/ - if (test_send_receive_times && t.value()>min_threshold) std::cout << "Slave " << my_rank << ": reduced output_image after " << t.value() << " seconds" << std::endl; -#endif - delete[] output_buf; + if (test_send_receive_times) { + t.reset(); + t.start(); } - +#endif + + MPI_Reduce(image_buf, output_buf, image_buffer_size, MPI_FLOAT, MPI_SUM, destination, MPI_COMM_WORLD); + delete[] image_buf; + +#ifdef STIR_MPI_TIMINGS + if (test_send_receive_times) + t.stop(); + int my_rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &my_rank); /*Gets the rank of the Processor*/ + if (test_send_receive_times && t.value() > min_threshold) + std::cout << "Slave " << my_rank << ": reduced output_image after " << t.value() << " seconds" << std::endl; +#endif + delete[] output_buf; } + +} // namespace distributed diff --git a/src/recon_buildblock/distributed_test_functions.cxx b/src/recon_buildblock/distributed_test_functions.cxx index a0344d22c9..2061b8c216 100644 --- a/src/recon_buildblock/distributed_test_functions.cxx +++ b/src/recon_buildblock/distributed_test_functions.cxx @@ -23,7 +23,7 @@ \brief Implementation of test functions in distributed namespace - \author Tobias Beisel + \author Tobias Beisel */ @@ -31,248 +31,242 @@ #include "stir/recon_buildblock/distributed_functions.h" #include "stir/VoxelsOnCartesianGrid.h" -namespace distributed -{ - void test_viewgram_slave(const stir::shared_ptr& proj_data_info_ptr) - { - printf("\n-----Slave startet Test for sending viewgram----------\n"); - - stir::Viewgram* vg= NULL; - receive_and_construct_viewgram(vg, proj_data_info_ptr, 0); - - send_viewgram(*vg, 0); - vg=NULL; - delete vg; - } - - void test_viewgram_master(stir::Viewgram viewgram, const stir::shared_ptr& proj_data_info_ptr) - { - printf("\n-----Running Test for sending viewgram----------\n"); - - send_viewgram(viewgram, 1); - - stir::Viewgram* vg= NULL; - receive_and_construct_viewgram(vg, proj_data_info_ptr, 1); - - assert(vg!=NULL); - assert(vg->has_same_characteristics(viewgram)); - assert(*vg==viewgram); - - for ( int tang_pos = viewgram.get_min_tangential_pos_num(); tang_pos <= viewgram.get_max_tangential_pos_num() ;++tang_pos) - for ( int ax_pos = viewgram.get_min_axial_pos_num(); ax_pos <= viewgram.get_max_axial_pos_num() ;++ax_pos) - { - assert(viewgram[ax_pos][tang_pos]==(*vg)[ax_pos][tang_pos]); - if (viewgram[ax_pos][tang_pos]!=(*vg)[ax_pos][tang_pos]) printf("-----Test sending viewgram failed!!!!-------------\n"); - } - assert(vg->get_view_num()==viewgram.get_view_num()); - if (vg->get_view_num()!=viewgram.get_view_num()) printf("-----Test sending viewgram failed!!!!-------------\n"); - assert(vg->get_segment_num()==viewgram.get_segment_num()); - if (vg->get_segment_num()!=viewgram.get_segment_num()) printf("-----Test sending viewgram failed!!!!-------------\n"); - assert(vg->get_timing_pos_num()==viewgram.get_timing_pos_num()); - if (vg->get_timing_pos_num()!=viewgram.get_timing_pos_num()) printf("-----Test sending viewgram failed!!!!-------------\n"); - - delete vg; - printf("\n-----Test sending viewgram done-----------\n"); - } - - void test_image_estimate_master(const stir::DiscretisedDensity<3,float>* input_image_ptr, int slave) - { - printf("\n-----Running Test for sending image estimate-----\n"); - - int image_buffer_size; - - send_image_parameters(input_image_ptr, 99, slave); - send_image_estimate(input_image_ptr, slave); - - stir::shared_ptr > target_image_sptr; - receive_and_set_image_parameters(target_image_sptr, image_buffer_size, 99, slave); - - receive_image_values_and_fill_image_ptr(target_image_sptr, image_buffer_size, slave); - - assert(target_image_sptr->has_same_characteristics(*input_image_ptr)); - assert(*input_image_ptr==*target_image_sptr); - - stir::DiscretisedDensity<3,float>::const_full_iterator density_iter = input_image_ptr->begin_all(); - stir::DiscretisedDensity<3,float>::const_full_iterator density_end = input_image_ptr->end_all(); - - stir::DiscretisedDensity<3,float>::full_iterator target_density_iter = target_image_sptr->begin_all(); - - while (density_iter!= density_end) - { - assert(*density_iter == *target_density_iter); - - density_iter++; - target_density_iter++; - } - - printf("\n-----Test sending image estimate done-----\n"); - } - - void test_image_estimate_slave() - { - printf("\n-----Slave startet Test for sending image estimate-----\n"); - - int buffer_size; - stir::shared_ptr > received_image_estimate; - - receive_and_set_image_parameters(received_image_estimate, buffer_size, 99, 0); - receive_image_values_and_fill_image_ptr(received_image_estimate, buffer_size, 0); - - send_image_parameters(received_image_estimate.get(), 99, 0); - send_image_estimate(received_image_estimate.get(), 0); +namespace distributed { +void +test_viewgram_slave(const stir::shared_ptr& proj_data_info_ptr) { + printf("\n-----Slave startet Test for sending viewgram----------\n"); + + stir::Viewgram* vg = NULL; + receive_and_construct_viewgram(vg, proj_data_info_ptr, 0); + + send_viewgram(*vg, 0); + vg = NULL; + delete vg; +} + +void +test_viewgram_master(stir::Viewgram viewgram, const stir::shared_ptr& proj_data_info_ptr) { + printf("\n-----Running Test for sending viewgram----------\n"); + + send_viewgram(viewgram, 1); + + stir::Viewgram* vg = NULL; + receive_and_construct_viewgram(vg, proj_data_info_ptr, 1); + + assert(vg != NULL); + assert(vg->has_same_characteristics(viewgram)); + assert(*vg == viewgram); + + for (int tang_pos = viewgram.get_min_tangential_pos_num(); tang_pos <= viewgram.get_max_tangential_pos_num(); ++tang_pos) + for (int ax_pos = viewgram.get_min_axial_pos_num(); ax_pos <= viewgram.get_max_axial_pos_num(); ++ax_pos) { + assert(viewgram[ax_pos][tang_pos] == (*vg)[ax_pos][tang_pos]); + if (viewgram[ax_pos][tang_pos] != (*vg)[ax_pos][tang_pos]) + printf("-----Test sending viewgram failed!!!!-------------\n"); + } + assert(vg->get_view_num() == viewgram.get_view_num()); + if (vg->get_view_num() != viewgram.get_view_num()) + printf("-----Test sending viewgram failed!!!!-------------\n"); + assert(vg->get_segment_num() == viewgram.get_segment_num()); + if (vg->get_segment_num() != viewgram.get_segment_num()) + printf("-----Test sending viewgram failed!!!!-------------\n"); + assert(vg->get_timing_pos_num() == viewgram.get_timing_pos_num()); + if (vg->get_timing_pos_num() != viewgram.get_timing_pos_num()) + printf("-----Test sending viewgram failed!!!!-------------\n"); + + delete vg; + printf("\n-----Test sending viewgram done-----------\n"); +} + +void +test_image_estimate_master(const stir::DiscretisedDensity<3, float>* input_image_ptr, int slave) { + printf("\n-----Running Test for sending image estimate-----\n"); + + int image_buffer_size; + + send_image_parameters(input_image_ptr, 99, slave); + send_image_estimate(input_image_ptr, slave); + + stir::shared_ptr> target_image_sptr; + receive_and_set_image_parameters(target_image_sptr, image_buffer_size, 99, slave); + + receive_image_values_and_fill_image_ptr(target_image_sptr, image_buffer_size, slave); + + assert(target_image_sptr->has_same_characteristics(*input_image_ptr)); + assert(*input_image_ptr == *target_image_sptr); + + stir::DiscretisedDensity<3, float>::const_full_iterator density_iter = input_image_ptr->begin_all(); + stir::DiscretisedDensity<3, float>::const_full_iterator density_end = input_image_ptr->end_all(); + + stir::DiscretisedDensity<3, float>::full_iterator target_density_iter = target_image_sptr->begin_all(); + + while (density_iter != density_end) { + assert(*density_iter == *target_density_iter); + + density_iter++; + target_density_iter++; } - - void test_related_viewgrams_master(const stir::shared_ptr& proj_data_info_ptr, - const stir::shared_ptr symmetries_sptr, - stir::RelatedViewgrams* y, int slave) - { - printf("\n-----Running Test for sending related viewgrams-----\n"); - - send_related_viewgrams(y, 1); - - stir::RelatedViewgrams* received_viewgrams=NULL; - - receive_and_construct_related_viewgrams(received_viewgrams, - proj_data_info_ptr, - symmetries_sptr, 1); - - assert(received_viewgrams!=NULL); - assert(received_viewgrams->has_same_characteristics(*y)); - assert(*received_viewgrams==*y); - - stir::RelatedViewgrams::iterator viewgrams_iter = y->begin(); - stir::RelatedViewgrams::iterator viewgrams_end = y->end(); - stir::RelatedViewgrams::iterator received_viewgrams_iter = received_viewgrams->begin(); - - int pos=0; - while (viewgrams_iter!= viewgrams_end) - { - for ( int tang_pos = (*viewgrams_iter).get_min_tangential_pos_num() ;tang_pos <= (*viewgrams_iter).get_max_tangential_pos_num() ;++tang_pos) - for ( int ax_pos = (*viewgrams_iter).get_min_axial_pos_num(); ax_pos <= (*viewgrams_iter).get_max_axial_pos_num() ;++ax_pos) - { - pos++; - assert(((*viewgrams_iter)[ax_pos][tang_pos])==(*received_viewgrams_iter)[ax_pos][tang_pos]); - } - viewgrams_iter++; - received_viewgrams_iter++; + + printf("\n-----Test sending image estimate done-----\n"); +} + +void +test_image_estimate_slave() { + printf("\n-----Slave startet Test for sending image estimate-----\n"); + + int buffer_size; + stir::shared_ptr> received_image_estimate; + + receive_and_set_image_parameters(received_image_estimate, buffer_size, 99, 0); + receive_image_values_and_fill_image_ptr(received_image_estimate, buffer_size, 0); + + send_image_parameters(received_image_estimate.get(), 99, 0); + send_image_estimate(received_image_estimate.get(), 0); +} + +void +test_related_viewgrams_master(const stir::shared_ptr& proj_data_info_ptr, + const stir::shared_ptr symmetries_sptr, + stir::RelatedViewgrams* y, int slave) { + printf("\n-----Running Test for sending related viewgrams-----\n"); + + send_related_viewgrams(y, 1); + + stir::RelatedViewgrams* received_viewgrams = NULL; + + receive_and_construct_related_viewgrams(received_viewgrams, proj_data_info_ptr, symmetries_sptr, 1); + + assert(received_viewgrams != NULL); + assert(received_viewgrams->has_same_characteristics(*y)); + assert(*received_viewgrams == *y); + + stir::RelatedViewgrams::iterator viewgrams_iter = y->begin(); + stir::RelatedViewgrams::iterator viewgrams_end = y->end(); + stir::RelatedViewgrams::iterator received_viewgrams_iter = received_viewgrams->begin(); + + int pos = 0; + while (viewgrams_iter != viewgrams_end) { + for (int tang_pos = (*viewgrams_iter).get_min_tangential_pos_num(); + tang_pos <= (*viewgrams_iter).get_max_tangential_pos_num(); ++tang_pos) + for (int ax_pos = (*viewgrams_iter).get_min_axial_pos_num(); ax_pos <= (*viewgrams_iter).get_max_axial_pos_num(); + ++ax_pos) { + pos++; + assert(((*viewgrams_iter)[ax_pos][tang_pos]) == (*received_viewgrams_iter)[ax_pos][tang_pos]); } - - received_viewgrams=NULL; - delete received_viewgrams; - printf("\n-----Test sending related viewgrams done-----\n"); - } - - void test_related_viewgrams_slave(const stir::shared_ptr& proj_data_info_ptr, - const stir::shared_ptr symmetries_sptr - ) - { - printf("\n-----Slave startet Test for sending related viewgrams-----\n"); - - stir::RelatedViewgrams* received_viewgrams=NULL; - - receive_and_construct_related_viewgrams(received_viewgrams, - proj_data_info_ptr, - symmetries_sptr, 0); - - assert(received_viewgrams!=NULL); - - send_related_viewgrams(received_viewgrams, 0); - - received_viewgrams=NULL; - delete received_viewgrams; - } - - void test_parameter_info_master(const std::string str, int slave, char const * const text) - { - printf("\n-----Running Test for sending %s-----\n", text); - - std::string slave_string= receive_string(88, slave); - - assert(str.compare(slave_string)==0); - - /*printf("\nReceived Parameter Info:\n"); - cerr<& proj_data_info_ptr, + const stir::shared_ptr symmetries_sptr) { + printf("\n-----Slave startet Test for sending related viewgrams-----\n"); + + stir::RelatedViewgrams* received_viewgrams = NULL; + + receive_and_construct_related_viewgrams(received_viewgrams, proj_data_info_ptr, symmetries_sptr, 0); + + assert(received_viewgrams != NULL); + + send_related_viewgrams(received_viewgrams, 0); + + received_viewgrams = NULL; + delete received_viewgrams; +} + +void +test_parameter_info_master(const std::string str, int slave, char const* const text) { + printf("\n-----Running Test for sending %s-----\n", text); + + std::string slave_string = receive_string(88, slave); + + assert(str.compare(slave_string) == 0); + + /*printf("\nReceived Parameter Info:\n"); + cerr< - find_basic_vs_nums_in_subset(const ProjDataInfo& proj_data_info, - const DataSymmetriesForViewSegmentNumbers& symmetries, - const int min_segment_num, const int max_segment_num, - const int subset_num, const int num_subsets) - { - std::vector vs_nums_to_process; - for (int segment_num = min_segment_num; segment_num <= max_segment_num; segment_num++) - { - for (int timing_pos_num = -proj_data_info.get_min_tof_pos_num(); - timing_pos_num<= proj_data_info.get_max_tof_pos_num(); - ++timing_pos_num) - { - for (int view = proj_data_info.get_min_view_num() + subset_num; - view <= proj_data_info.get_max_view_num(); - view += num_subsets) - { - const ViewSegmentNumbers view_segment_num(view, segment_num); +std::vector +find_basic_vs_nums_in_subset(const ProjDataInfo& proj_data_info, const DataSymmetriesForViewSegmentNumbers& symmetries, + const int min_segment_num, const int max_segment_num, const int subset_num, const int num_subsets) { + std::vector vs_nums_to_process; + for (int segment_num = min_segment_num; segment_num <= max_segment_num; segment_num++) { + for (int timing_pos_num = -proj_data_info.get_min_tof_pos_num(); timing_pos_num <= proj_data_info.get_max_tof_pos_num(); + ++timing_pos_num) { + for (int view = proj_data_info.get_min_view_num() + subset_num; view <= proj_data_info.get_max_view_num(); + view += num_subsets) { + const ViewSegmentNumbers view_segment_num(view, segment_num); - if (!symmetries.is_basic(view_segment_num)) - continue; + if (!symmetries.is_basic(view_segment_num)) + continue; + + vs_nums_to_process.push_back(view_segment_num); - vs_nums_to_process.push_back(view_segment_num); - #ifndef NDEBUG - // test if symmetries didn't take us out of the segment range - std::vector rel_vs; - symmetries.get_related_view_segment_numbers(rel_vs, view_segment_num); - for (std::vector::const_iterator iter = rel_vs.begin(); iter!= rel_vs.end(); ++iter) - { - assert(iter->segment_num() >= min_segment_num); - assert(iter->segment_num() <= max_segment_num); - } + // test if symmetries didn't take us out of the segment range + std::vector rel_vs; + symmetries.get_related_view_segment_numbers(rel_vs, view_segment_num); + for (std::vector::const_iterator iter = rel_vs.begin(); iter != rel_vs.end(); ++iter) { + assert(iter->segment_num() >= min_segment_num); + assert(iter->segment_num() <= max_segment_num); + } #endif - } - } } - return vs_nums_to_process; + } } - + return vs_nums_to_process; } +} // namespace detail + END_NAMESPACE_STIR diff --git a/src/recon_buildblock/recon_buildblock_registries.cxx b/src/recon_buildblock/recon_buildblock_registries.cxx index ed1f212bdc..4fc7b0c1da 100644 --- a/src/recon_buildblock/recon_buildblock_registries.cxx +++ b/src/recon_buildblock/recon_buildblock_registries.cxx @@ -68,31 +68,32 @@ #include "stir/OSSPS/OSSPSReconstruction.h" #ifdef HAVE_LLN_MATRIX -#include "stir/recon_buildblock/BinNormalisationFromECAT7.h" +# include "stir/recon_buildblock/BinNormalisationFromECAT7.h" #endif #include "stir/recon_buildblock/BinNormalisationFromECAT8.h" #ifdef HAVE_HDF5 -#include "stir/recon_buildblock/BinNormalisationFromGEHDF5.h" +# include "stir/recon_buildblock/BinNormalisationFromGEHDF5.h" #endif #include "stir/recon_buildblock/FourierRebinning.h" #ifdef STIR_WITH_NiftyPET_PROJECTOR -#include "stir/recon_buildblock/NiftyPET_projector/ForwardProjectorByBinNiftyPET.h" -#include "stir/recon_buildblock/NiftyPET_projector/BackProjectorByBinNiftyPET.h" -#include "stir/recon_buildblock/NiftyPET_projector/ProjectorByBinPairUsingNiftyPET.h" +# include "stir/recon_buildblock/NiftyPET_projector/ForwardProjectorByBinNiftyPET.h" +# include "stir/recon_buildblock/NiftyPET_projector/BackProjectorByBinNiftyPET.h" +# include "stir/recon_buildblock/NiftyPET_projector/ProjectorByBinPairUsingNiftyPET.h" #endif //#include "stir/IO/InputFileFormatRegistry.h" START_NAMESPACE_STIR -//static RegisterInputFileFormat idummy0(0); +// static RegisterInputFileFormat idummy0(0); -static PoissonLogLikelihoodWithLinearModelForMeanAndProjData >::RegisterIt dummy1; -static PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin >::RegisterIt dummy2; +static PoissonLogLikelihoodWithLinearModelForMeanAndProjData>::RegisterIt dummy1; +static PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin>::RegisterIt + dummy2; -static FilterRootPrior >::RegisterIt dummy4; +static FilterRootPrior>::RegisterIt dummy4; static QuadraticPrior::RegisterIt dummy5; static PLSPrior::RegisterIt dummyPLS; static RelativeDifferencePrior::RegisterIt dummyRelativeDifference; @@ -120,14 +121,14 @@ static BinNormalisationFromProjData::RegisterIt dummy93; static BinNormalisationFromAttenuationImage::RegisterIt dummy94; static BinNormalisationSPECT::RegisterIt dummy95; static PoissonLogLikelihoodWithLinearKineticModelAndDynamicProjectionData::RegisterIt Dummyxxx; -static PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion >::RegisterIt Dummyxxxzz; +static PoissonLogLikelihoodWithLinearModelForMeanAndGatedProjDataWithMotion>::RegisterIt Dummyxxxzz; static FBP2DReconstruction::RegisterIt dummy601; static FBP3DRPReconstruction::RegisterIt dummy602; -static OSMAPOSLReconstruction >::RegisterIt dummy603; -static KOSMAPOSLReconstruction >::RegisterIt dummyK ; -static OSSPSReconstruction >::RegisterIt dummy604; +static OSMAPOSLReconstruction>::RegisterIt dummy603; +static KOSMAPOSLReconstruction>::RegisterIt dummyK; +static OSSPSReconstruction>::RegisterIt dummy604; #ifdef STIR_WITH_NiftyPET_PROJECTOR static ForwardProjectorByBinNiftyPET::RegisterIt gpu_fwd; diff --git a/src/recon_test/bcktest.cxx b/src/recon_test/bcktest.cxx index 15286536bc..5b527c99c3 100644 --- a/src/recon_test/bcktest.cxx +++ b/src/recon_test/bcktest.cxx @@ -31,10 +31,10 @@ bcktest [output-filename [proj_data_file \ [template-image [backprojector-parfile ]]]] \endverbatim - If some command line parameter is not given, the program will ask + If some command line parameter is not given, the program will ask the user interactively. - The format of the parameter file to specifiy the backproejctor is + The format of the parameter file to specifiy the backproejctor is as follows: \verbatim Back Projector parameters:= @@ -80,321 +80,240 @@ using std::cerr; using std::endl; #endif - - - START_NAMESPACE_STIR void -do_segments(DiscretisedDensity<3,float>& image, - ProjData& proj_data_org, - const int start_timing_num, const int end_timing_num, - const int start_segment_num, const int end_segment_num, - const int start_axial_pos_num, const int end_axial_pos_num, - const int start_tang_pos_num,const int end_tang_pos_num, - const int start_view, const int end_view, - BackProjectorByBin* back_projector_ptr, - bool fill_with_1) -{ - - shared_ptr - symmetries_sptr(back_projector_ptr->get_symmetries_used()->clone()); - - +do_segments(DiscretisedDensity<3, float>& image, ProjData& proj_data_org, const int start_timing_num, const int end_timing_num, + const int start_segment_num, const int end_segment_num, const int start_axial_pos_num, const int end_axial_pos_num, + const int start_tang_pos_num, const int end_tang_pos_num, const int start_view, const int end_view, + BackProjectorByBin* back_projector_ptr, bool fill_with_1) { + + shared_ptr symmetries_sptr(back_projector_ptr->get_symmetries_used()->clone()); + list already_processed; back_projector_ptr->start_accumulating_in_new_target(); - for (int timing_num = start_timing_num; timing_num <= end_timing_num; ++timing_num) - { - already_processed.clear(); - for (int segment_num = start_segment_num; segment_num <= end_segment_num; ++segment_num) - for (int view = start_view; view <= end_view; view++) - { - ViewSegmentNumbers vs(view, segment_num); - symmetries_sptr->find_basic_view_segment_numbers(vs); - if (find(already_processed.begin(), already_processed.end(), vs) - != already_processed.end()) - continue; - - already_processed.push_back(vs); - - cerr << "Processing view " << vs.view_num() - << " of segment " << vs.segment_num() - << " of timing position index " << timing_num - << endl; - - if (fill_with_1) - { - RelatedViewgrams viewgrams_empty = - proj_data_org.get_empty_related_viewgrams(vs, symmetries_sptr, false, timing_num); - //proj_data_org.get_empty_related_viewgrams(vs.view_num(),vs.segment_num(), symmetries_sptr); - - RelatedViewgrams::iterator r_viewgrams_iter = viewgrams_empty.begin(); - while (r_viewgrams_iter != viewgrams_empty.end()) - { - Viewgram& single_viewgram = *r_viewgrams_iter; - if (start_view <= single_viewgram.get_view_num() && - single_viewgram.get_view_num() <= end_view && - single_viewgram.get_segment_num() >= start_segment_num && - single_viewgram.get_segment_num() <= end_segment_num) - { - single_viewgram.fill(1.F); - } - r_viewgrams_iter++; - } - - back_projector_ptr->back_project(viewgrams_empty, - std::max(start_axial_pos_num, viewgrams_empty.get_min_axial_pos_num()), - std::min(end_axial_pos_num, viewgrams_empty.get_max_axial_pos_num()), - start_tang_pos_num, end_tang_pos_num); - } - else - { - RelatedViewgrams viewgrams = - proj_data_org.get_related_viewgrams(vs, - //proj_data_org.get_related_viewgrams(vs.view_num(),vs.segment_num(), - symmetries_sptr, false, timing_num); - RelatedViewgrams::iterator r_viewgrams_iter = viewgrams.begin(); - - while (r_viewgrams_iter != viewgrams.end()) - { - Viewgram& single_viewgram = *r_viewgrams_iter; - { - if (start_view <= single_viewgram.get_view_num() && - single_viewgram.get_view_num() <= end_view && - single_viewgram.get_segment_num() >= start_segment_num && - single_viewgram.get_segment_num() <= end_segment_num) - { - // ok - } - else - { - // set to 0 to prevent it being backprojected - single_viewgram.fill(0); - } - } - ++r_viewgrams_iter; - } - - back_projector_ptr->back_project(viewgrams, - std::max(start_axial_pos_num, viewgrams.get_min_axial_pos_num()), - std::min(end_axial_pos_num, viewgrams.get_max_axial_pos_num()), - start_tang_pos_num, end_tang_pos_num); - } // fill - } // for view_num, segment_num - } // for timing_pos_num + for (int timing_num = start_timing_num; timing_num <= end_timing_num; ++timing_num) { + already_processed.clear(); + for (int segment_num = start_segment_num; segment_num <= end_segment_num; ++segment_num) + for (int view = start_view; view <= end_view; view++) { + ViewSegmentNumbers vs(view, segment_num); + symmetries_sptr->find_basic_view_segment_numbers(vs); + if (find(already_processed.begin(), already_processed.end(), vs) != already_processed.end()) + continue; + + already_processed.push_back(vs); + + cerr << "Processing view " << vs.view_num() << " of segment " << vs.segment_num() << " of timing position index " + << timing_num << endl; + + if (fill_with_1) { + RelatedViewgrams viewgrams_empty = + proj_data_org.get_empty_related_viewgrams(vs, symmetries_sptr, false, timing_num); + // proj_data_org.get_empty_related_viewgrams(vs.view_num(),vs.segment_num(), symmetries_sptr); + + RelatedViewgrams::iterator r_viewgrams_iter = viewgrams_empty.begin(); + while (r_viewgrams_iter != viewgrams_empty.end()) { + Viewgram& single_viewgram = *r_viewgrams_iter; + if (start_view <= single_viewgram.get_view_num() && single_viewgram.get_view_num() <= end_view && + single_viewgram.get_segment_num() >= start_segment_num && single_viewgram.get_segment_num() <= end_segment_num) { + single_viewgram.fill(1.F); + } + r_viewgrams_iter++; + } + + back_projector_ptr->back_project( + viewgrams_empty, std::max(start_axial_pos_num, viewgrams_empty.get_min_axial_pos_num()), + std::min(end_axial_pos_num, viewgrams_empty.get_max_axial_pos_num()), start_tang_pos_num, end_tang_pos_num); + } else { + RelatedViewgrams viewgrams = + proj_data_org.get_related_viewgrams(vs, + // proj_data_org.get_related_viewgrams(vs.view_num(),vs.segment_num(), + symmetries_sptr, false, timing_num); + RelatedViewgrams::iterator r_viewgrams_iter = viewgrams.begin(); + + while (r_viewgrams_iter != viewgrams.end()) { + Viewgram& single_viewgram = *r_viewgrams_iter; + { + if (start_view <= single_viewgram.get_view_num() && single_viewgram.get_view_num() <= end_view && + single_viewgram.get_segment_num() >= start_segment_num && + single_viewgram.get_segment_num() <= end_segment_num) { + // ok + } else { + // set to 0 to prevent it being backprojected + single_viewgram.fill(0); + } + } + ++r_viewgrams_iter; + } + + back_projector_ptr->back_project(viewgrams, std::max(start_axial_pos_num, viewgrams.get_min_axial_pos_num()), + std::min(end_axial_pos_num, viewgrams.get_max_axial_pos_num()), start_tang_pos_num, + end_tang_pos_num); + } // fill + } // for view_num, segment_num + } // for timing_pos_num } END_NAMESPACE_STIR - - USING_NAMESPACE_STIR int -main(int argc, char **argv) -{ - if (argc==1 || argc>7) - { - cerr <<"Usage: " << argv[0] << " \\\n" - << "\t[output-filename [proj_data_file [template-image [backprojector-parfile ]]]]\n"; - exit(EXIT_FAILURE); - } - const std::string output_filename= - argc>1? argv[1] : ask_string("Output filename"); +main(int argc, char** argv) { + if (argc == 1 || argc > 7) { + cerr << "Usage: " << argv[0] << " \\\n" + << "\t[output-filename [proj_data_file [template-image [backprojector-parfile ]]]]\n"; + exit(EXIT_FAILURE); + } + const std::string output_filename = argc > 1 ? argv[1] : ask_string("Output filename"); shared_ptr proj_data_ptr; bool fill; - if (argc>2) - { - proj_data_ptr = ProjData::read_from_file(argv[2]); - fill = ask("Do you want to backproject all 1s (Y) or the data (N) ?", true); - } - else - { - shared_ptr data_info(ProjDataInfo::ask_parameters()); - // create an empty ProjDataFromStream object - // such that we don't have to differentiate between code later on - shared_ptr exam_info_sptr(new ExamInfo); - proj_data_ptr.reset(new ProjDataFromStream (exam_info_sptr, data_info,shared_ptr())); - fill = true; - } + if (argc > 2) { + proj_data_ptr = ProjData::read_from_file(argv[2]); + fill = ask("Do you want to backproject all 1s (Y) or the data (N) ?", true); + } else { + shared_ptr data_info(ProjDataInfo::ask_parameters()); + // create an empty ProjDataFromStream object + // such that we don't have to differentiate between code later on + shared_ptr exam_info_sptr(new ExamInfo); + proj_data_ptr.reset(new ProjDataFromStream(exam_info_sptr, data_info, shared_ptr())); + fill = true; + } shared_ptr back_projector_ptr; const bool disp = ask("Display images ?", false); - + const bool save = ask("Save images ?", true); - + const bool save_profiles = ask("Save horizontal profiles ?", false); - // note: first check 4th parameter for historical reasons + // note: first check 4th parameter for historical reasons // (could be switched with 3rd without problems) - if (argc>4) - { - KeyParser parser; - parser.add_start_key("Back Projector parameters"); - parser.add_parsing_key("type", &back_projector_ptr); - parser.add_stop_key("END"); - parser.parse(argv[4]); - } + if (argc > 4) { + KeyParser parser; + parser.add_start_key("Back Projector parameters"); + parser.add_parsing_key("type", &back_projector_ptr); + parser.add_stop_key("END"); + parser.parse(argv[4]); + } - const shared_ptr proj_data_info_sptr = - proj_data_ptr->get_proj_data_info_sptr(); - - shared_ptr > image_sptr; + const shared_ptr proj_data_info_sptr = proj_data_ptr->get_proj_data_info_sptr(); + + shared_ptr> image_sptr; + + if (argc > 3) { + image_sptr = read_from_file>(argv[3]); + } else { + const float zoom = ask_num("Zoom factor (>1 means smaller voxels)", 0.F, 100.F, 1.F); + int xy_size = static_cast(proj_data_ptr->get_num_tangential_poss() * zoom); + xy_size = ask_num("Number of x,y pixels", 3, xy_size * 2, xy_size); + int z_size = 2 * proj_data_info_sptr->get_scanner_ptr()->get_num_rings() - 1; + z_size = ask_num("Number of z pixels", 1, 1000, z_size); + VoxelsOnCartesianGrid* vox_image_ptr = new VoxelsOnCartesianGrid( + *proj_data_info_sptr, zoom, Coordinate3D(0, 0, 0), Coordinate3D(z_size, xy_size, xy_size)); + const float z_origin = + ask_num("Shift z-origin (in pixels)", -vox_image_ptr->get_length() / 2, vox_image_ptr->get_length() / 2, 0) * + vox_image_ptr->get_voxel_size().z(); + vox_image_ptr->set_origin(Coordinate3D(z_origin, 0, 0)); + + image_sptr.reset(vox_image_ptr); + } - if (argc>3) - { - image_sptr = read_from_file >(argv[3]); - } - else - { - const float zoom = ask_num("Zoom factor (>1 means smaller voxels)",0.F,100.F,1.F); - int xy_size = static_cast(proj_data_ptr->get_num_tangential_poss()*zoom); - xy_size = ask_num("Number of x,y pixels",3,xy_size*2,xy_size); - int z_size = 2*proj_data_info_sptr->get_scanner_ptr()->get_num_rings()-1; - z_size = ask_num("Number of z pixels",1,1000,z_size); - VoxelsOnCartesianGrid * vox_image_ptr = - new VoxelsOnCartesianGrid(*proj_data_info_sptr, - zoom, - Coordinate3D(0,0,0), - Coordinate3D(z_size,xy_size,xy_size)); - const float z_origin = - ask_num("Shift z-origin (in pixels)", - -vox_image_ptr->get_length()/2, - vox_image_ptr->get_length()/2, - 0) - *vox_image_ptr->get_voxel_size().z(); - vox_image_ptr->set_origin(Coordinate3D(z_origin,0,0)); - - image_sptr.reset(vox_image_ptr); - } + while (is_null_ptr(back_projector_ptr)) { + back_projector_ptr.reset(BackProjectorByBin::ask_type_and_parameters()); + } - while (is_null_ptr(back_projector_ptr)) - { - back_projector_ptr.reset(BackProjectorByBin::ask_type_and_parameters()); - } + back_projector_ptr->set_up(proj_data_ptr->get_proj_data_info_sptr()->create_shared_clone(), image_sptr); + + do { + int min_timing_num = ask_num("Minimum timing position index to backproject", proj_data_info_sptr->get_min_tof_pos_num(), + proj_data_info_sptr->get_max_tof_pos_num(), proj_data_info_sptr->get_min_tof_pos_num()); + int max_timing_num = ask_num("Maximum timing position index to backproject", min_timing_num, + proj_data_info_sptr->get_max_tof_pos_num(), min_timing_num); + int min_segment_num = ask_num("Minimum segment number to backproject", proj_data_info_sptr->get_min_segment_num(), + proj_data_info_sptr->get_max_segment_num(), 0); + int max_segment_num = ask_num("Maximum segment number to backproject", min_segment_num, + proj_data_info_sptr->get_max_segment_num(), min_segment_num); - back_projector_ptr->set_up(proj_data_ptr->get_proj_data_info_sptr()->create_shared_clone(), - image_sptr); - - do - { - int min_timing_num = ask_num("Minimum timing position index to backproject", - proj_data_info_sptr->get_min_tof_pos_num(), proj_data_info_sptr->get_max_tof_pos_num(), - proj_data_info_sptr->get_min_tof_pos_num()); - int max_timing_num = ask_num("Maximum timing position index to backproject", - min_timing_num, proj_data_info_sptr->get_max_tof_pos_num(), - min_timing_num); - int min_segment_num = ask_num("Minimum segment number to backproject", - proj_data_info_sptr->get_min_segment_num(), proj_data_info_sptr->get_max_segment_num(), 0); - int max_segment_num = ask_num("Maximum segment number to backproject", - min_segment_num,proj_data_info_sptr->get_max_segment_num(), - min_segment_num); - // find max_axial_pos_num in the range of segments // TODO relies on axial_pos_num starting from 0 assert(proj_data_info_sptr->get_min_axial_pos_num(0) == 0); -#if 1 +#if 1 int max_axial_pos_num; - if (min_segment_num <= 0 && 0 <= max_segment_num) - { + if (min_segment_num <= 0 && 0 <= max_segment_num) { // all axial_poss are addressed for segment 0 max_axial_pos_num = proj_data_info_sptr->get_max_axial_pos_num(0); + } else { + if (min_segment_num > 0) // which implies max_segment_num>0 + max_axial_pos_num = proj_data_info_sptr->get_max_axial_pos_num(min_segment_num); + else // min_segment_num <= max_segment_num < 0 + max_axial_pos_num = proj_data_info_sptr->get_max_axial_pos_num(max_segment_num); } - else - { - if (min_segment_num>0) // which implies max_segment_num>0 - max_axial_pos_num = proj_data_info_sptr->get_max_axial_pos_num(min_segment_num); - else // min_segment_num <= max_segment_num < 0 - max_axial_pos_num = proj_data_info_sptr->get_max_axial_pos_num(max_segment_num); - } - - const int start_axial_pos_num = - ask_num("Start axial_pos", 0, max_axial_pos_num, 0); - const int end_axial_pos_num = - ask_num("End axial_pos", start_axial_pos_num, max_axial_pos_num, max_axial_pos_num); + + const int start_axial_pos_num = ask_num("Start axial_pos", 0, max_axial_pos_num, 0); + const int end_axial_pos_num = ask_num("End axial_pos", start_axial_pos_num, max_axial_pos_num, max_axial_pos_num); #else const int min_axial_pos_num = proj_data_info_sptr->get_min_axial_pos_num(segment_num); const int max_axial_pos_num = proj_data_info_sptr->get_max_axial_pos_num(segment_num); - const int start_axial_pos_num = - ask_num("Start axial_pos", min_axial_pos_num, max_axial_pos_num, min_axial_pos_num); - const int end_axial_pos_num = - ask_num("End axial_pos", start_axial_pos_num, max_axial_pos_num, max_axial_pos_num); + const int start_axial_pos_num = ask_num("Start axial_pos", min_axial_pos_num, max_axial_pos_num, min_axial_pos_num); + const int end_axial_pos_num = ask_num("End axial_pos", start_axial_pos_num, max_axial_pos_num, max_axial_pos_num); #endif - + // TODO this message is symmetry specific const int nviews = proj_data_info_sptr->get_num_views(); - cerr << "Special views are at 0, " - << nviews/4 <<", " << nviews/2 <<", " << nviews/4*3 << endl; - - int start_view = ask_num("Start view", 0, nviews-1, 0); - int end_view = ask_num("End view", 0, nviews-1, nviews-1); - - const int start_tang_pos_num = - ask_num("Start tang_pos", - proj_data_info_sptr->get_min_tangential_pos_num(), - proj_data_info_sptr->get_max_tangential_pos_num(), - proj_data_info_sptr->get_min_tangential_pos_num()); - const int end_tang_pos_num = - ask_num("End tang_pos", - start_tang_pos_num, - proj_data_info_sptr->get_max_tangential_pos_num(), - proj_data_info_sptr->get_max_tangential_pos_num()); - //const int start_tang_pos_num = -end_tang_pos_num; + cerr << "Special views are at 0, " << nviews / 4 << ", " << nviews / 2 << ", " << nviews / 4 * 3 << endl; + + int start_view = ask_num("Start view", 0, nviews - 1, 0); + int end_view = ask_num("End view", 0, nviews - 1, nviews - 1); + + const int start_tang_pos_num = + ask_num("Start tang_pos", proj_data_info_sptr->get_min_tangential_pos_num(), + proj_data_info_sptr->get_max_tangential_pos_num(), proj_data_info_sptr->get_min_tangential_pos_num()); + const int end_tang_pos_num = ask_num("End tang_pos", start_tang_pos_num, proj_data_info_sptr->get_max_tangential_pos_num(), + proj_data_info_sptr->get_max_tangential_pos_num()); + // const int start_tang_pos_num = -end_tang_pos_num; if (!ask("Add this backprojection to image of previous run ?", false)) image_sptr->fill(0); - + CPUTimer timer; timer.reset(); back_projector_ptr->reset_timers(); back_projector_ptr->start_timers(); timer.start(); - do_segments(*image_sptr, - *proj_data_ptr, - min_timing_num, max_timing_num, - min_segment_num, max_segment_num, - start_axial_pos_num,end_axial_pos_num, - start_tang_pos_num,end_tang_pos_num, - start_view, end_view, - back_projector_ptr.get(), - fill); - + do_segments(*image_sptr, *proj_data_ptr, min_timing_num, max_timing_num, min_segment_num, max_segment_num, + start_axial_pos_num, end_axial_pos_num, start_tang_pos_num, end_tang_pos_num, start_view, end_view, + back_projector_ptr.get(), fill); + timer.stop(); cerr << timer.value() << " s total CPU time\n"; - cerr << "of which " << back_projector_ptr->get_CPU_timer_value() - << " s is reported by backprojector\n"; - cerr << "min and max in image " << image_sptr->find_min() - << ", " << image_sptr->find_max() << endl; - + cerr << "of which " << back_projector_ptr->get_CPU_timer_value() << " s is reported by backprojector\n"; + cerr << "min and max in image " << image_sptr->find_min() << ", " << image_sptr->find_max() << endl; + if (disp) display(*image_sptr, image_sptr->find_max()); - - if (save) - { - cerr <<" - Saving " << output_filename << endl; - OutputFileFormat >::default_sptr()-> - write_to_file(output_filename, *image_sptr); - + + if (save) { + cerr << " - Saving " << output_filename << endl; + OutputFileFormat>::default_sptr()->write_to_file(output_filename, *image_sptr); } - if (save_profiles) - { - + if (save_profiles) { + cerr << "Writing horizontal profiles to bcktest.prof" << endl; ofstream profile("bcktest.prof"); - if (!profile) - { cerr << "Couldn't open " << "bcktest.prof"; } - - for (int z=image_sptr->get_min_index(); z<= image_sptr->get_max_index(); z++) - profile << (*image_sptr)[z][0] << '\n'; - } - - } - while (ask("One more ?", true)); + if (!profile) { + cerr << "Couldn't open " + << "bcktest.prof"; + } - return EXIT_SUCCESS; -} + for (int z = image_sptr->get_min_index(); z <= image_sptr->get_max_index(); z++) + profile << (*image_sptr)[z][0] << '\n'; + } + } while (ask("One more ?", true)); + return EXIT_SUCCESS; +} diff --git a/src/recon_test/fwdtest.cxx b/src/recon_test/fwdtest.cxx index 9f7e2567ad..31a7f09f0d 100644 --- a/src/recon_test/fwdtest.cxx +++ b/src/recon_test/fwdtest.cxx @@ -26,7 +26,7 @@ \author PARAPET project This program allows forward projection of a few segments/views - only, or of the full data set. + only, or of the full data set. \par Usage: \verbatim @@ -34,7 +34,7 @@ \endverbatim The template_proj_data_file will be used to get the scanner, mashing etc. details (its data will \e not be used, nor will it be overwritten). - If some of these parameters are not given, some questions are asked. + If some of these parameters are not given, some questions are asked. \par Example parameter file for specifying the forward projector \verbatim @@ -68,429 +68,303 @@ #include "stir/is_null_ptr.h" #include - using std::cerr; using std::cout; using std::endl; USING_NAMESPACE_STIR -//USING_NAMESPACE_STD - +// USING_NAMESPACE_STD /******************* Declarations local functions *******************/ -static void -do_segments(const VoxelsOnCartesianGrid& image, ProjData& s3d, - const int start_timing_pos_num, const int end_timing_pos_num, - const int start_segment_num, const int end_segment_num, - const int start_view, const int end_view, - const int start_tangential_pos_num, const int end_tangential_pos_num, - ForwardProjectorByBin&, - const bool disp); -static void -fill_cuboid(VoxelsOnCartesianGrid& image); -static void -fill_cylinder(VoxelsOnCartesianGrid& image); - - +static void do_segments(const VoxelsOnCartesianGrid& image, ProjData& s3d, const int start_timing_pos_num, + const int end_timing_pos_num, const int start_segment_num, const int end_segment_num, + const int start_view, const int end_view, const int start_tangential_pos_num, + const int end_tangential_pos_num, ForwardProjectorByBin&, const bool disp); +static void fill_cuboid(VoxelsOnCartesianGrid& image); +static void fill_cylinder(VoxelsOnCartesianGrid& image); /*************************** main ***********************************/ -int -main(int argc, char *argv[]) -{ - - if(argc<3 || argc>5) - { - std::cerr <<"Usage:\n" - << argv[0] << " \\\n" - << " output-filename template_proj_data_file [image_to_forward_project [forwardprojector-parfile ]]]\n" - <<"The template_proj_data_file will be used to get the scanner, mashing etc. details.\n"; +int +main(int argc, char* argv[]) { + + if (argc < 3 || argc > 5) { + std::cerr << "Usage:\n" + << argv[0] << " \\\n" + << " output-filename template_proj_data_file [image_to_forward_project [forwardprojector-parfile ]]]\n" + << "The template_proj_data_file will be used to get the scanner, mashing etc. details.\n"; exit(EXIT_FAILURE); } - + const std::string output_file_name = argv[1]; shared_ptr new_data_info_ptr; shared_ptr exam_info_sptr; - if(argc>=3) - { - shared_ptr proj_data_sptr = - ProjData::read_from_file(argv[2]); + if (argc >= 3) { + shared_ptr proj_data_sptr = ProjData::read_from_file(argv[2]); exam_info_sptr = proj_data_sptr->get_exam_info().create_shared_clone(); - new_data_info_ptr= proj_data_sptr->get_proj_data_info_sptr()->create_shared_clone(); - } - else - { + new_data_info_ptr = proj_data_sptr->get_proj_data_info_sptr()->create_shared_clone(); + } else { exam_info_sptr.reset(new ExamInfo); new_data_info_ptr.reset(ProjDataInfo::ask_parameters()); } - int limit_segments= - ask_num("Maximum absolute segment number to process: ", 0, - new_data_info_ptr->get_max_segment_num(), - new_data_info_ptr->get_max_segment_num() ); + int limit_segments = ask_num("Maximum absolute segment number to process: ", 0, new_data_info_ptr->get_max_segment_num(), + new_data_info_ptr->get_max_segment_num()); new_data_info_ptr->reduce_segment_range(-limit_segments, limit_segments); shared_ptr proj_data_ptr(new ProjDataInterfile(exam_info_sptr, new_data_info_ptr, output_file_name)); - cerr << "Output will be written to " << output_file_name - << " and its Interfile header\n"; + cerr << "Output will be written to " << output_file_name << " and its Interfile header\n"; - int dispstart = 0; int save = 0; - - shared_ptr > image_sptr; - VoxelsOnCartesianGrid * vox_image_ptr = 0; - - if (argc<4) - { - switch (int choice = ask_num("Start image is cuboid (1) or cylinder (2) or on file (3)",1,3,2)) - { - case 1: - case 2: - { - dispstart = - ask_num("Display start image ? no (0), yes (1)", 0,1,0); - - save = - ask_num("Save start images ? no (0), yes (1)", 0,1,0); - const float zoom = ask_num("Zoom factor (>1 means smaller voxels)",0.F,10.F,1.F); - int xy_size = static_cast(proj_data_ptr->get_num_tangential_poss()*zoom); - xy_size = ask_num("Number of x,y pixels",3,xy_size*2,xy_size); - const float zoom_z = ask_num("Zoom factor in z", 0.F, 10.F, 1.F); - int z_size = - stir::round((2*proj_data_ptr->get_proj_data_info_sptr()->get_scanner_ptr()->get_num_rings()-1)*zoom_z); - z_size = ask_num("Number of z pixels",1,1000,z_size); - vox_image_ptr = - new VoxelsOnCartesianGrid(*(proj_data_ptr->get_proj_data_info_sptr()), - zoom, - CartesianCoordinate3D(0,0,0), - Coordinate3D(z_size,xy_size,xy_size)); - image_sptr.reset(vox_image_ptr); - vox_image_ptr->set_grid_spacing( - vox_image_ptr->get_grid_spacing()/ - CartesianCoordinate3D(zoom_z,1.F,1.F)); - if (choice==1) - fill_cuboid(*vox_image_ptr); - else - fill_cylinder(*vox_image_ptr); - break; - } - case 3: - { - char filename[max_filename_length]; - - ask_filename_with_extension(filename, "Input file name ?", ".hv"); - - image_sptr = - read_from_file >(filename); - vox_image_ptr = dynamic_cast *> (image_sptr.get()); - - break; - } - } + + shared_ptr> image_sptr; + VoxelsOnCartesianGrid* vox_image_ptr = 0; + + if (argc < 4) { + switch (int choice = ask_num("Start image is cuboid (1) or cylinder (2) or on file (3)", 1, 3, 2)) { + case 1: + case 2: { + dispstart = ask_num("Display start image ? no (0), yes (1)", 0, 1, 0); + + save = ask_num("Save start images ? no (0), yes (1)", 0, 1, 0); + const float zoom = ask_num("Zoom factor (>1 means smaller voxels)", 0.F, 10.F, 1.F); + int xy_size = static_cast(proj_data_ptr->get_num_tangential_poss() * zoom); + xy_size = ask_num("Number of x,y pixels", 3, xy_size * 2, xy_size); + const float zoom_z = ask_num("Zoom factor in z", 0.F, 10.F, 1.F); + int z_size = stir::round((2 * proj_data_ptr->get_proj_data_info_sptr()->get_scanner_ptr()->get_num_rings() - 1) * zoom_z); + z_size = ask_num("Number of z pixels", 1, 1000, z_size); + vox_image_ptr = + new VoxelsOnCartesianGrid(*(proj_data_ptr->get_proj_data_info_sptr()), zoom, + CartesianCoordinate3D(0, 0, 0), Coordinate3D(z_size, xy_size, xy_size)); + image_sptr.reset(vox_image_ptr); + vox_image_ptr->set_grid_spacing(vox_image_ptr->get_grid_spacing() / CartesianCoordinate3D(zoom_z, 1.F, 1.F)); + if (choice == 1) + fill_cuboid(*vox_image_ptr); + else + fill_cylinder(*vox_image_ptr); + break; } - else - { - image_sptr = - read_from_file >(argv[3]); - vox_image_ptr = dynamic_cast *> (image_sptr.get()); + case 3: { + char filename[max_filename_length]; + + ask_filename_with_extension(filename, "Input file name ?", ".hv"); + + image_sptr = read_from_file>(filename); + vox_image_ptr = dynamic_cast*>(image_sptr.get()); + + break; } + } + } else { + image_sptr = read_from_file>(argv[3]); + vox_image_ptr = dynamic_cast*>(image_sptr.get()); + } + + const float z_origin = + ask_num("Shift z-origin (in pixels)", -vox_image_ptr->get_length() / 2, vox_image_ptr->get_length() / 2, 0) * + vox_image_ptr->get_voxel_size().z(); - const float z_origin = - ask_num("Shift z-origin (in pixels)", - -vox_image_ptr->get_length()/2, - vox_image_ptr->get_length()/2, - 0) *vox_image_ptr->get_voxel_size().z(); - - vox_image_ptr->set_origin(make_coordinate(z_origin,0.F,0.F) + vox_image_ptr->get_origin()); + vox_image_ptr->set_origin(make_coordinate(z_origin, 0.F, 0.F) + vox_image_ptr->get_origin()); // use shared_ptr such that it cleans up automatically shared_ptr forw_projector_ptr; - if (argc>=5) - { - KeyParser parser; - parser.add_start_key("Forward Projector parameters"); - parser.add_parsing_key("type", &forw_projector_ptr); - parser.add_stop_key("END"); - parser.parse(argv[4]); - } + if (argc >= 5) { + KeyParser parser; + parser.add_start_key("Forward Projector parameters"); + parser.add_parsing_key("type", &forw_projector_ptr); + parser.add_stop_key("END"); + parser.parse(argv[4]); + } - while (is_null_ptr(forw_projector_ptr)) - { - forw_projector_ptr.reset(ForwardProjectorByBin::ask_type_and_parameters()); - } + while (is_null_ptr(forw_projector_ptr)) { + forw_projector_ptr.reset(ForwardProjectorByBin::ask_type_and_parameters()); + } - forw_projector_ptr->set_up(proj_data_ptr->get_proj_data_info_sptr()->create_shared_clone(), - image_sptr); + forw_projector_ptr->set_up(proj_data_ptr->get_proj_data_info_sptr()->create_shared_clone(), image_sptr); cerr << forw_projector_ptr->parameter_info(); - if (dispstart) - { - cerr << "Displaying start image"; - display(*image_sptr, image_sptr->find_max()); - } - + if (dispstart) { + cerr << "Displaying start image"; + display(*image_sptr, image_sptr->find_max()); + } - if (save) - { + if (save) { cerr << "Saving start image to 'test_image'" << endl; - OutputFileFormat >::default_sptr()-> - write_to_file("test_image", *image_sptr); + OutputFileFormat>::default_sptr()->write_to_file("test_image", *image_sptr); } - + std::list already_processed; - if (ask("Do full forward projection ?", true)) - { + if (ask("Do full forward projection ?", true)) { CPUTimer timer; timer.reset(); timer.start(); - - do_segments(*vox_image_ptr, *proj_data_ptr, - proj_data_ptr->get_min_tof_pos_num(), proj_data_ptr->get_max_tof_pos_num(), - proj_data_ptr->get_min_segment_num(), proj_data_ptr->get_max_segment_num(), - proj_data_ptr->get_min_view_num(), - proj_data_ptr->get_max_view_num(), - proj_data_ptr->get_min_tangential_pos_num(), - proj_data_ptr->get_max_tangential_pos_num(), - *forw_projector_ptr, - false); - + do_segments(*vox_image_ptr, *proj_data_ptr, proj_data_ptr->get_min_tof_pos_num(), proj_data_ptr->get_max_tof_pos_num(), + proj_data_ptr->get_min_segment_num(), proj_data_ptr->get_max_segment_num(), proj_data_ptr->get_min_view_num(), + proj_data_ptr->get_max_view_num(), proj_data_ptr->get_min_tangential_pos_num(), + proj_data_ptr->get_max_tangential_pos_num(), *forw_projector_ptr, false); + timer.stop(); - cerr << timer.value() << " s CPU time"<get_min_tof_pos_num(); - timing_pos_num <= proj_data_ptr->get_max_tof_pos_num(); - ++timing_pos_num) - for (int segment_num = proj_data_ptr->get_min_segment_num(); - segment_num <= proj_data_ptr->get_max_segment_num(); - ++segment_num) - { - const SegmentByView segment = - proj_data_ptr->get_empty_segment_by_view(segment_num, false,timing_pos_num); - if (!(proj_data_ptr->set_segment(segment) == Succeeded::yes)) - warning("Error set_segment %d of timing position index %d\n", segment_num,timing_pos_num); - } - do - { + for (int timing_pos_num = proj_data_ptr->get_min_tof_pos_num(); timing_pos_num <= proj_data_ptr->get_max_tof_pos_num(); + ++timing_pos_num) + for (int segment_num = proj_data_ptr->get_min_segment_num(); segment_num <= proj_data_ptr->get_max_segment_num(); + ++segment_num) { + const SegmentByView segment = proj_data_ptr->get_empty_segment_by_view(segment_num, false, timing_pos_num); + if (!(proj_data_ptr->set_segment(segment) == Succeeded::yes)) + warning("Error set_segment %d of timing position index %d\n", segment_num, timing_pos_num); + } + do { CPUTimer timer; timer.reset(); timer.start(); - - const int timing_pos_num = - ask_num("Timing position index to forward project", - proj_data_ptr->get_min_tof_pos_num(), - proj_data_ptr->get_max_tof_pos_num(), - 0); - const int segment_num = - ask_num("Segment number to forward project (related segments will be done as well)", - proj_data_ptr->get_min_segment_num(), - proj_data_ptr->get_max_segment_num(), - 0); + + const int timing_pos_num = ask_num("Timing position index to forward project", proj_data_ptr->get_min_tof_pos_num(), + proj_data_ptr->get_max_tof_pos_num(), 0); + const int segment_num = ask_num("Segment number to forward project (related segments will be done as well)", + proj_data_ptr->get_min_segment_num(), proj_data_ptr->get_max_segment_num(), 0); const int min_view = proj_data_ptr->get_min_view_num(); const int max_view = proj_data_ptr->get_max_view_num(); - const int start_view = - ask_num("Start view (related views will be done as well)", min_view, max_view, min_view); - const int end_view = - ask_num("End view (related views will be done as well)", start_view, max_view, max_view); + const int start_view = ask_num("Start view (related views will be done as well)", min_view, max_view, min_view); + const int end_view = ask_num("End view (related views will be done as well)", start_view, max_view, max_view); const int min_tangential_pos_num = proj_data_ptr->get_min_tangential_pos_num(); const int max_tangential_pos_num = proj_data_ptr->get_max_tangential_pos_num(); - const int start_tangential_pos_num = - ask_num("Start tangential_pos_num", min_tangential_pos_num, max_tangential_pos_num, min_tangential_pos_num); - const int end_tangential_pos_num = - ask_num("End tangential_pos_num ", start_tangential_pos_num, max_tangential_pos_num, max_tangential_pos_num); - - do_segments(*vox_image_ptr,*proj_data_ptr, - timing_pos_num, timing_pos_num, - segment_num,segment_num, - start_view, end_view, - start_tangential_pos_num, end_tangential_pos_num, - *forw_projector_ptr, disp); - + const int start_tangential_pos_num = + ask_num("Start tangential_pos_num", min_tangential_pos_num, max_tangential_pos_num, min_tangential_pos_num); + const int end_tangential_pos_num = + ask_num("End tangential_pos_num ", start_tangential_pos_num, max_tangential_pos_num, max_tangential_pos_num); + + do_segments(*vox_image_ptr, *proj_data_ptr, timing_pos_num, timing_pos_num, segment_num, segment_num, start_view, end_view, + start_tangential_pos_num, end_tangential_pos_num, *forw_projector_ptr, disp); timer.stop(); - cerr << timer.value() << " s CPU time"<& image, - ProjData& proj_data, - const int start_timing_pos_num, const int end_timing_pos_num, - const int start_segment_num, const int end_segment_num, - const int start_view, const int end_view, - const int start_tangential_pos_num, const int end_tangential_pos_num, - ForwardProjectorByBin& forw_projector, - const bool disp) -{ - shared_ptr - symmetries_sptr(forw_projector.get_symmetries_used()->clone()); - - std::list already_processed; - for (int timing_pos_num = start_timing_pos_num; timing_pos_num <= end_timing_pos_num; ++timing_pos_num) - { - already_processed.clear(); - for (int segment_num = start_segment_num; segment_num <= end_segment_num; ++segment_num) - for (int view = start_view; view <= end_view; view++) - { - ViewSegmentNumbers vs(view, segment_num); - symmetries_sptr->find_basic_view_segment_numbers(vs); - if (find(already_processed.begin(), already_processed.end(), vs) - != already_processed.end()) - continue; - - already_processed.push_back(vs); - - cerr << "Processing view " << vs.view_num() - << " of segment " << vs.segment_num() - << " of timing position index " << timing_pos_num - << endl; - - RelatedViewgrams viewgrams = - proj_data.get_empty_related_viewgrams(vs, symmetries_sptr, false,timing_pos_num); - forw_projector.forward_project(viewgrams, - viewgrams.get_min_axial_pos_num(), - viewgrams.get_max_axial_pos_num(), - start_tangential_pos_num, end_tangential_pos_num); - if (disp) - display(viewgrams, viewgrams.find_max()); - if (!(proj_data.set_related_viewgrams(viewgrams) == Succeeded::yes)) - error("Error set_related_viewgrams\n"); - } - } -} +do_segments(const VoxelsOnCartesianGrid& image, ProjData& proj_data, const int start_timing_pos_num, + const int end_timing_pos_num, const int start_segment_num, const int end_segment_num, const int start_view, + const int end_view, const int start_tangential_pos_num, const int end_tangential_pos_num, + ForwardProjectorByBin& forw_projector, const bool disp) { + shared_ptr symmetries_sptr(forw_projector.get_symmetries_used()->clone()); + std::list already_processed; + for (int timing_pos_num = start_timing_pos_num; timing_pos_num <= end_timing_pos_num; ++timing_pos_num) { + already_processed.clear(); + for (int segment_num = start_segment_num; segment_num <= end_segment_num; ++segment_num) + for (int view = start_view; view <= end_view; view++) { + ViewSegmentNumbers vs(view, segment_num); + symmetries_sptr->find_basic_view_segment_numbers(vs); + if (find(already_processed.begin(), already_processed.end(), vs) != already_processed.end()) + continue; + + already_processed.push_back(vs); + + cerr << "Processing view " << vs.view_num() << " of segment " << vs.segment_num() << " of timing position index " + << timing_pos_num << endl; + + RelatedViewgrams viewgrams = proj_data.get_empty_related_viewgrams(vs, symmetries_sptr, false, timing_pos_num); + forw_projector.forward_project(viewgrams, viewgrams.get_min_axial_pos_num(), viewgrams.get_max_axial_pos_num(), + start_tangential_pos_num, end_tangential_pos_num); + if (disp) + display(viewgrams, viewgrams.find_max()); + if (!(proj_data.set_related_viewgrams(viewgrams) == Succeeded::yes)) + error("Error set_related_viewgrams\n"); + } + } +} +void +fill_cuboid(VoxelsOnCartesianGrid& image) { + const float voxel_value = ask_num("Voxel value", -10E10F, 10E10F, 1.F); + const int xs = ask_num("Start X coordinate", image.get_min_x(), image.get_max_x(), (image.get_min_x() + image.get_max_x()) / 2); + const int ys = ask_num("Start Y coordinate", image.get_min_y(), image.get_max_y(), (image.get_min_y() + image.get_max_y()) / 2); + const int zs = ask_num("Start Z coordinate", image.get_min_z(), image.get_max_z(), (image.get_min_z() + image.get_max_z()) / 2); -void fill_cuboid(VoxelsOnCartesianGrid& image) -{ - const float voxel_value = ask_num("Voxel value",-10E10F, 10E10F,1.F); - const int xs = ask_num("Start X coordinate", - image.get_min_x(), image.get_max_x(), - (image.get_min_x()+ image.get_max_x())/2); - const int ys = ask_num("Start Y coordinate", - image.get_min_y(), image.get_max_y(), - (image.get_min_y()+ image.get_max_y())/2); - const int zs = ask_num("Start Z coordinate", - image.get_min_z(), image.get_max_z(), - (image.get_min_z()+ image.get_max_z())/2); - const int xe = ask_num("End X coordinate", xs, image.get_max_x(), xs); const int ye = ask_num("End Y coordinate", ys, image.get_max_y(), ys); const int ze = ask_num("End Z coordinate", zs, image.get_max_z(), zs); - - - cerr << "Start coordinate: (x,y,z) = (" - << xs << ", " << ys << ", " << zs - << ")" << endl; - cerr << "End coordinate: (x,y,z) = (" - << xe << ", " << ye << ", " << ze - << ")" << endl; - + + cerr << "Start coordinate: (x,y,z) = (" << xs << ", " << ys << ", " << zs << ")" << endl; + cerr << "End coordinate: (x,y,z) = (" << xe << ", " << ye << ", " << ze << ")" << endl; + image.fill(0); - for (int z=zs; z<=ze; z++) - for (int y=ys; y <= ye; y++) - for (int x=xs; x<= xe; x++) - image[z][y][x] = voxel_value; + for (int z = zs; z <= ze; z++) + for (int y = ys; y <= ye; y++) + for (int x = xs; x <= xe; x++) + image[z][y][x] = voxel_value; } -void fill_cylinder(VoxelsOnCartesianGrid& image) -{ - const float voxel_value = ask_num("Voxel value",-10E10F, 10E10F,1.F); - - const double xc = - ask_num("Centre X coordinate", - (double)image.get_min_x(), (double)image.get_max_x(), - (image.get_min_x()+ image.get_max_x())/2.); - - const double yc = - ask_num("Centre Y coordinate", - (double)image.get_min_y(), (double)image.get_max_y(), - (image.get_min_y()+ image.get_max_y())/2.); - - const float zc = - ask_num("Centre Z coordinate", - (float)image.get_min_z(), (float)image.get_max_z(), - (image.get_min_z()+ image.get_max_z())/2.F); - - - const double Rcyl = - ask_num("Radius (pixels)", - .5, (image.get_max_x()- image.get_min_x())/2., - (image.get_max_x()- image.get_min_x())/4.); - +void +fill_cylinder(VoxelsOnCartesianGrid& image) { + const float voxel_value = ask_num("Voxel value", -10E10F, 10E10F, 1.F); + + const double xc = ask_num("Centre X coordinate", (double)image.get_min_x(), (double)image.get_max_x(), + (image.get_min_x() + image.get_max_x()) / 2.); + + const double yc = ask_num("Centre Y coordinate", (double)image.get_min_y(), (double)image.get_max_y(), + (image.get_min_y() + image.get_max_y()) / 2.); + + const float zc = ask_num("Centre Z coordinate", (float)image.get_min_z(), (float)image.get_max_z(), + (image.get_min_z() + image.get_max_z()) / 2.F); + + const double Rcyl = + ask_num("Radius (pixels)", .5, (image.get_max_x() - image.get_min_x()) / 2., (image.get_max_x() - image.get_min_x()) / 4.); + // Max length is num_planes+1 because of edges of voxels - const float Lcyl = - ask_num("Length (planes)", 1.F, (image.get_max_z()- image.get_min_z())+1.F, - (image.get_max_z()- image.get_min_z())+1.F); - - - cerr << "Centre coordinate: (x,y,z) = (" - << xc << ", " << yc << ", " << zc - << ")" << endl; - cerr << "Radius = " << Rcyl << ", Length = " << Lcyl << endl; - - const int num_samples = - ask_num("With how many points (in x,y direction) do I sample each voxel ?", - 1,100,5); - - Array<2,float> plane = image[0]; - - for (int y=image.get_min_y(); y<=image.get_max_y(); y++) - for (int x=image.get_min_x(); x<=image.get_max_x(); x++) - { - double value = 0; - - for (double ysmall=-(num_samples-1.)/num_samples/2.; - ysmall < 0.5; - ysmall+= 1./num_samples) - { - const double ytry = y-ysmall-yc; - - for (double xsmall=-(num_samples-1.)/num_samples/2.; - xsmall < 0.5; - xsmall+= 1./num_samples) - { - const double xtry = x-xsmall-xc; - - if (xtry*xtry + ytry*ytry <= Rcyl*Rcyl) - value++; - } - } - // update plane with normalised value (independent of num_samples) - plane[y][x] = static_cast(voxel_value*value/(num_samples*num_samples)); + const float Lcyl = ask_num("Length (planes)", 1.F, (image.get_max_z() - image.get_min_z()) + 1.F, + (image.get_max_z() - image.get_min_z()) + 1.F); + + cerr << "Centre coordinate: (x,y,z) = (" << xc << ", " << yc << ", " << zc << ")" << endl; + cerr << "Radius = " << Rcyl << ", Length = " << Lcyl << endl; + + const int num_samples = ask_num("With how many points (in x,y direction) do I sample each voxel ?", 1, 100, 5); + + Array<2, float> plane = image[0]; + + for (int y = image.get_min_y(); y <= image.get_max_y(); y++) + for (int x = image.get_min_x(); x <= image.get_max_x(); x++) { + double value = 0; + + for (double ysmall = -(num_samples - 1.) / num_samples / 2.; ysmall < 0.5; ysmall += 1. / num_samples) { + const double ytry = y - ysmall - yc; + + for (double xsmall = -(num_samples - 1.) / num_samples / 2.; xsmall < 0.5; xsmall += 1. / num_samples) { + const double xtry = x - xsmall - xc; + + if (xtry * xtry + ytry * ytry <= Rcyl * Rcyl) + value++; + } } - - for (int z=image.get_min_z(); z<=image.get_max_z(); z++) - { - // use 2. to make both args of min() and max() double - float zfactor = (std::min(z+.5F, zc+Lcyl/2.F) - std::max(z-.5F, zc-Lcyl/2.F)); - if (zfactor<0) zfactor = 0; - image[z] = plane; - image[z] *= zfactor; + // update plane with normalised value (independent of num_samples) + plane[y][x] = static_cast(voxel_value * value / (num_samples * num_samples)); } - -} + for (int z = image.get_min_z(); z <= image.get_max_z(); z++) { + // use 2. to make both args of min() and max() double + float zfactor = (std::min(z + .5F, zc + Lcyl / 2.F) - std::max(z - .5F, zc - Lcyl / 2.F)); + if (zfactor < 0) + zfactor = 0; + image[z] = plane; + image[z] *= zfactor; + } +} diff --git a/src/recon_test/recontest.cxx b/src/recon_test/recontest.cxx index 139d372126..2dc4d40667 100644 --- a/src/recon_test/recontest.cxx +++ b/src/recon_test/recontest.cxx @@ -14,7 +14,6 @@ */ - #include "stir/DiscretisedDensity.h" #include "stir/IO/read_from_file.h" #include "stir/recon_buildblock/Reconstruction.h" @@ -25,70 +24,66 @@ #include "stir/CPUTimer.h" #include "stir/HighResWallClockTimer.h" -static void print_usage_and_exit() -{ - std::cerr<<"This executable is able to reconstruct some data without calling a specific reconstruction method, from the code.\n"; - std::cerr<<"but specifing the method in the par file with the \"econstruction method\". \n"; - std::cerr<<"\nUsage:\nrecontest reconstuction.par\n"; - std::cerr<<"Example parameter file:\n\n" - <<"reconstruction method := OSMAPOSL\n" - <<"OSMAPOSLParameters := \n" - <<"objective function type:= PoissonLogLikelihoodWithLinearModelForMeanAndProjData\n" - <<"PoissonLogLikelihoodWithLinearModelForMeanAndProjData Parameters:=\n" - <<"input file := .hs\n" - <<"maximum absolute segment number to process := -1\n" - <<"projector pair type := Matrix\n" - <<"Projector Pair Using Matrix Parameters :=\n" - <<"Matrix type := Ray Tracing\n" - <<"Ray tracing matrix parameters :=\n" - <<"number of rays in tangential direction to trace for each bin:= 10\n" - <<"End Ray tracing matrix parameters :=\n" - <<"End Projector Pair Using Matrix Parameters :=\n" - <<"recompute sensitivity := 1\n" - <<"zoom := 1\n" - <<"end PoissonLogLikelihoodWithLinearModelForMeanAndProjData Parameters:=\n" - <<"enforce initial positivity condition:= 1 \n" - <<"number of subsets:= 1\n" - <<"number of subiterations:= 1 \n" - <<"save estimates at subiteration intervals:= 1\n" - <<"output filename prefix := output\n" - <<"end OSMAPOSLParameters := \n" - <<"end reconstruction := \n"; - exit(EXIT_FAILURE); +static void +print_usage_and_exit() { + std::cerr + << "This executable is able to reconstruct some data without calling a specific reconstruction method, from the code.\n"; + std::cerr << "but specifing the method in the par file with the \"econstruction method\". \n"; + std::cerr << "\nUsage:\nrecontest reconstuction.par\n"; + std::cerr << "Example parameter file:\n\n" + << "reconstruction method := OSMAPOSL\n" + << "OSMAPOSLParameters := \n" + << "objective function type:= PoissonLogLikelihoodWithLinearModelForMeanAndProjData\n" + << "PoissonLogLikelihoodWithLinearModelForMeanAndProjData Parameters:=\n" + << "input file := .hs\n" + << "maximum absolute segment number to process := -1\n" + << "projector pair type := Matrix\n" + << "Projector Pair Using Matrix Parameters :=\n" + << "Matrix type := Ray Tracing\n" + << "Ray tracing matrix parameters :=\n" + << "number of rays in tangential direction to trace for each bin:= 10\n" + << "End Ray tracing matrix parameters :=\n" + << "End Projector Pair Using Matrix Parameters :=\n" + << "recompute sensitivity := 1\n" + << "zoom := 1\n" + << "end PoissonLogLikelihoodWithLinearModelForMeanAndProjData Parameters:=\n" + << "enforce initial positivity condition:= 1 \n" + << "number of subsets:= 1\n" + << "number of subiterations:= 1 \n" + << "save estimates at subiteration intervals:= 1\n" + << "output filename prefix := output\n" + << "end OSMAPOSLParameters := \n" + << "end reconstruction := \n"; + exit(EXIT_FAILURE); } - /***********************************************************/ -int main(int argc, const char *argv[]) -{ - using namespace stir; +int +main(int argc, const char* argv[]) { + using namespace stir; - if (argc!=2) - print_usage_and_exit(); + if (argc != 2) + print_usage_and_exit(); - shared_ptr < Reconstruction < DiscretisedDensity < 3, float > > > - reconstruction_method_sptr; + shared_ptr>> reconstruction_method_sptr; - KeyParser parser; - parser.add_start_key("Reconstruction"); - parser.add_stop_key("End Reconstruction"); - parser.add_parsing_key("reconstruction method", &reconstruction_method_sptr); - parser.parse(argv[1]); + KeyParser parser; + parser.add_start_key("Reconstruction"); + parser.add_stop_key("End Reconstruction"); + parser.add_parsing_key("reconstruction method", &reconstruction_method_sptr); + parser.parse(argv[1]); - HighResWallClockTimer t; - t.reset(); - t.start(); + HighResWallClockTimer t; + t.reset(); + t.start(); - if (reconstruction_method_sptr->reconstruct() == Succeeded::yes) - { - t.stop(); - std::cout << "Total Wall clock time: " << t.value() << " seconds" << std::endl; - return EXIT_SUCCESS; - } - else - { - t.stop(); - return EXIT_FAILURE; - } + if (reconstruction_method_sptr->reconstruct() == Succeeded::yes) { + t.stop(); + std::cout << "Total Wall clock time: " << t.value() << " seconds" << std::endl; + return EXIT_SUCCESS; + } else { + t.stop(); + return EXIT_FAILURE; + } } diff --git a/src/recon_test/test_DataSymmetriesForBins_PET_CartesianGrid.cxx b/src/recon_test/test_DataSymmetriesForBins_PET_CartesianGrid.cxx index 367772845c..643af47fee 100644 --- a/src/recon_test/test_DataSymmetriesForBins_PET_CartesianGrid.cxx +++ b/src/recon_test/test_DataSymmetriesForBins_PET_CartesianGrid.cxx @@ -20,13 +20,13 @@ \file \ingroup test - + \brief Test program for stir::DataSymmetriesForBins_PET_CartesianGrid Uses stir::ProjMatrixByBinUsingRayTracing. - + \author Kris Thielemans - + */ #include "stir/VoxelsOnCartesianGrid.h" @@ -52,11 +52,9 @@ using std::cerr; START_NAMESPACE_STIR template -bool -coordinates_less(const BasicCoordinate<3,T>& el1, const BasicCoordinate<3,T>& el2) -{ - return el1[1]& el1, const BasicCoordinate<3, T>& el2) { + return el1[1] < el2[1] || (el1[1] == el2[1] && (el1[2] < el2[2] || (el1[2] == el2[2] && el1[3] < el2[3]))); } /*! @@ -68,39 +66,33 @@ coordinates_less(const BasicCoordinate<3,T>& el1, const BasicCoordinate<3,T>& el symmetries. Checks if results are independent of which symmetries we use. */ -class DataSymmetriesForBins_PET_CartesianGridTests : public RunTests -{ +class DataSymmetriesForBins_PET_CartesianGridTests : public RunTests { public: - DataSymmetriesForBins_PET_CartesianGridTests(char const * template_proj_data_filename = 0); + DataSymmetriesForBins_PET_CartesianGridTests(char const* template_proj_data_filename = 0); void run_tests(); + private: - char const * template_proj_data_filename; + char const* template_proj_data_filename; shared_ptr proj_data_info_sptr; - void run_tests_2_proj_matrices_1_bin(const ProjMatrixByBin& proj_matrix_no_symm, - const ProjMatrixByBin& proj_matrix_with_symm, - const Bin& bin); - void run_tests_2_proj_matrices(const ProjMatrixByBin& proj_matrix_no_symm, - const ProjMatrixByBin& proj_matrix_with_symm); + void run_tests_2_proj_matrices_1_bin(const ProjMatrixByBin& proj_matrix_no_symm, const ProjMatrixByBin& proj_matrix_with_symm, + const Bin& bin); + void run_tests_2_proj_matrices(const ProjMatrixByBin& proj_matrix_no_symm, const ProjMatrixByBin& proj_matrix_with_symm); void run_tests_all_symmetries(const shared_ptr& proj_data_info_sptr, - const shared_ptr >& density_sptr - ); + const shared_ptr>& density_sptr); void run_tests_for_1_projdata(const shared_ptr& proj_data_info_sptr); }; -DataSymmetriesForBins_PET_CartesianGridTests:: -DataSymmetriesForBins_PET_CartesianGridTests(char const * template_proj_data_filename) - : template_proj_data_filename(template_proj_data_filename) -{} +DataSymmetriesForBins_PET_CartesianGridTests::DataSymmetriesForBins_PET_CartesianGridTests( + char const* template_proj_data_filename) + : template_proj_data_filename(template_proj_data_filename) {} void -DataSymmetriesForBins_PET_CartesianGridTests:: -run_tests_2_proj_matrices_1_bin(const ProjMatrixByBin& proj_matrix_no_symm, - const ProjMatrixByBin& proj_matrix_with_symm, - const Bin& bin) -{ +DataSymmetriesForBins_PET_CartesianGridTests::run_tests_2_proj_matrices_1_bin(const ProjMatrixByBin& proj_matrix_no_symm, + const ProjMatrixByBin& proj_matrix_with_symm, + const Bin& bin) { #if 0 // SYM // assert(proj_matrix_with_symm.get_symmetries_ptr()->is_basic(bin)); // vector related_bins; @@ -122,487 +114,391 @@ run_tests_2_proj_matrices_1_bin(const ProjMatrixByBin& proj_matrix_no_symm, get_proj_matrix_elems_for_one_bin(elems_no_sym, *bin_iter); #else - ProjMatrixElemsForOneBin elems_no_sym; - ProjMatrixElemsForOneBin elems_with_sym; - { - proj_matrix_with_symm. - get_proj_matrix_elems_for_one_bin(elems_with_sym, bin); - proj_matrix_no_symm. - get_proj_matrix_elems_for_one_bin(elems_no_sym, bin); + ProjMatrixElemsForOneBin elems_no_sym; + ProjMatrixElemsForOneBin elems_with_sym; + { + proj_matrix_with_symm.get_proj_matrix_elems_for_one_bin(elems_with_sym, bin); + proj_matrix_no_symm.get_proj_matrix_elems_for_one_bin(elems_no_sym, bin); #endif - elems_no_sym.sort(); - elems_with_sym.sort(); - if (!check(elems_no_sym == elems_with_sym, "comparing lors")) - { - // SYM const Bin bin=*bin_iter; - cerr << "Current bin: segment = " << bin.segment_num() - << ", axial pos " << bin.axial_pos_num() - << ", view = " << bin.view_num() - << ", tangential_pos_num = " << bin.tangential_pos_num() - << ", timing position index = " << bin.timing_pos_num() << "\n"; - ProjMatrixElemsForOneBin::const_iterator no_sym_iter= elems_no_sym.begin(); - ProjMatrixElemsForOneBin::const_iterator with_sym_iter = elems_with_sym.begin(); - while (no_sym_iter!= elems_no_sym.end() || with_sym_iter!=elems_with_sym.end()) - { - if (no_sym_iter==elems_no_sym.end() || - with_sym_iter==elems_with_sym.end() || - no_sym_iter->get_coords()!= with_sym_iter->get_coords() || - fabs(no_sym_iter->get_value()/with_sym_iter->get_value() -1) > .01) - { - bool inc_no_sym_iter = false; - if (no_sym_iter!=elems_no_sym.end() && - (with_sym_iter==elems_with_sym.end() || - coordinates_less(no_sym_iter->get_coords(),with_sym_iter->get_coords()) || - no_sym_iter->get_coords()==with_sym_iter->get_coords())) - { - cerr << no_sym_iter->get_coords() - << ':' << no_sym_iter->get_value() - << " || "; - inc_no_sym_iter = true; - } - else - cerr << " || "; - if (with_sym_iter!=elems_with_sym.end() && - (no_sym_iter==elems_no_sym.end() || - !coordinates_less(no_sym_iter->get_coords(),with_sym_iter->get_coords()))) - { - cerr << with_sym_iter->get_coords() - << ':' << with_sym_iter->get_value(); - ++with_sym_iter; - } - if (inc_no_sym_iter) - ++no_sym_iter; - cerr << "\n"; - } - else - { - if (no_sym_iter!=elems_no_sym.end()) - ++no_sym_iter; - if (with_sym_iter!=elems_with_sym.end()) - ++with_sym_iter; - } - } - } + elems_no_sym.sort(); + elems_with_sym.sort(); + if (!check(elems_no_sym == elems_with_sym, "comparing lors")) { + // SYM const Bin bin=*bin_iter; + cerr << "Current bin: segment = " << bin.segment_num() << ", axial pos " << bin.axial_pos_num() + << ", view = " << bin.view_num() << ", tangential_pos_num = " << bin.tangential_pos_num() + << ", timing position index = " << bin.timing_pos_num() << "\n"; + ProjMatrixElemsForOneBin::const_iterator no_sym_iter = elems_no_sym.begin(); + ProjMatrixElemsForOneBin::const_iterator with_sym_iter = elems_with_sym.begin(); + while (no_sym_iter != elems_no_sym.end() || with_sym_iter != elems_with_sym.end()) { + if (no_sym_iter == elems_no_sym.end() || with_sym_iter == elems_with_sym.end() || + no_sym_iter->get_coords() != with_sym_iter->get_coords() || + fabs(no_sym_iter->get_value() / with_sym_iter->get_value() - 1) > .01) { + bool inc_no_sym_iter = false; + if (no_sym_iter != elems_no_sym.end() && + (with_sym_iter == elems_with_sym.end() || coordinates_less(no_sym_iter->get_coords(), with_sym_iter->get_coords()) || + no_sym_iter->get_coords() == with_sym_iter->get_coords())) { + cerr << no_sym_iter->get_coords() << ':' << no_sym_iter->get_value() << " || "; + inc_no_sym_iter = true; + } else + cerr << " || "; + if (with_sym_iter != elems_with_sym.end() && + (no_sym_iter == elems_no_sym.end() || !coordinates_less(no_sym_iter->get_coords(), with_sym_iter->get_coords()))) { + cerr << with_sym_iter->get_coords() << ':' << with_sym_iter->get_value(); + ++with_sym_iter; + } + if (inc_no_sym_iter) + ++no_sym_iter; + cerr << "\n"; + } else { + if (no_sym_iter != elems_no_sym.end()) + ++no_sym_iter; + if (with_sym_iter != elems_with_sym.end()) + ++with_sym_iter; + } } + } +} } void -DataSymmetriesForBins_PET_CartesianGridTests:: -run_tests_2_proj_matrices(const ProjMatrixByBin& proj_matrix_no_symm, - const ProjMatrixByBin& proj_matrix_with_symm) -{ +DataSymmetriesForBins_PET_CartesianGridTests::run_tests_2_proj_matrices(const ProjMatrixByBin& proj_matrix_no_symm, + const ProjMatrixByBin& proj_matrix_with_symm) { #if 1 - for (int s=-proj_data_info_sptr->get_max_segment_num(); s<=proj_data_info_sptr->get_max_segment_num(); ++s) - for (int v=proj_data_info_sptr->get_min_view_num(); - v <= proj_data_info_sptr->get_max_view_num(); - ++v) - for (int timing_pos=proj_data_info_sptr->get_min_tof_pos_num(); - timing_pos<=proj_data_info_sptr->get_max_tof_pos_num(); - ++timing_pos) - for (int a=proj_data_info_sptr->get_min_axial_pos_num(s); - a <= proj_data_info_sptr->get_max_axial_pos_num(s); - ++a) - for (int t=-6; t<=6; t+=3) - { - const Bin bin(s,v,a,t,timing_pos); - //SYM if (proj_matrix_with_symm.get_symmetries_ptr()->is_basic(bin)) - run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, - proj_matrix_with_symm, bin); - } + for (int s = -proj_data_info_sptr->get_max_segment_num(); s <= proj_data_info_sptr->get_max_segment_num(); ++s) + for (int v = proj_data_info_sptr->get_min_view_num(); v <= proj_data_info_sptr->get_max_view_num(); ++v) + for (int timing_pos = proj_data_info_sptr->get_min_tof_pos_num(); timing_pos <= proj_data_info_sptr->get_max_tof_pos_num(); + ++timing_pos) + for (int a = proj_data_info_sptr->get_min_axial_pos_num(s); a <= proj_data_info_sptr->get_max_axial_pos_num(s); ++a) + for (int t = -6; t <= 6; t += 3) { + const Bin bin(s, v, a, t, timing_pos); + // SYM if (proj_matrix_with_symm.get_symmetries_ptr()->is_basic(bin)) + run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, proj_matrix_with_symm, bin); + } #else const int oblique_seg_num = proj_data_info_sptr->get_max_segment_num(); - const int view45 = - proj_data_info_sptr->get_num_views()/4; - assert(fabs(proj_data_info_sptr-> - get_phi(Bin(0,view45,0,0)) - _PI/4)<.001); - + const int view45 = proj_data_info_sptr->get_num_views() / 4; + assert(fabs(proj_data_info_sptr->get_phi(Bin(0, view45, 0, 0)) - _PI / 4) < .001); + { - const Bin bin(oblique_seg_num,1,5,6); - run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, - proj_matrix_with_symm, bin); + const Bin bin(oblique_seg_num, 1, 5, 6); + run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, proj_matrix_with_symm, bin); } { - const Bin bin(oblique_seg_num,1,5,-6); - run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, - proj_matrix_with_symm, bin); + const Bin bin(oblique_seg_num, 1, 5, -6); + run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, proj_matrix_with_symm, bin); } { - const Bin bin(-oblique_seg_num,1,5,6); - run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, - proj_matrix_with_symm, bin); + const Bin bin(-oblique_seg_num, 1, 5, 6); + run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, proj_matrix_with_symm, bin); } { - const Bin bin(oblique_seg_num,1,5,-6); - run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, - proj_matrix_with_symm, bin); + const Bin bin(oblique_seg_num, 1, 5, -6); + run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, proj_matrix_with_symm, bin); } { - const Bin bin(oblique_seg_num,1,5,0); - run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, - proj_matrix_with_symm, bin); + const Bin bin(oblique_seg_num, 1, 5, 0); + run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, proj_matrix_with_symm, bin); } { - const Bin bin(-oblique_seg_num,1,5,0); - run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, - proj_matrix_with_symm, bin); + const Bin bin(-oblique_seg_num, 1, 5, 0); + run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, proj_matrix_with_symm, bin); } { - const Bin bin(0,1,5,6); - run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, - proj_matrix_with_symm, bin); + const Bin bin(0, 1, 5, 6); + run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, proj_matrix_with_symm, bin); } { - const Bin bin(0,1,5,0); - run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, - proj_matrix_with_symm, bin); + const Bin bin(0, 1, 5, 0); + run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, proj_matrix_with_symm, bin); } { - const Bin bin(oblique_seg_num,0,5,6); - run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, - proj_matrix_with_symm, bin); + const Bin bin(oblique_seg_num, 0, 5, 6); + run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, proj_matrix_with_symm, bin); } { - const Bin bin(-oblique_seg_num,0,5,6); - run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, - proj_matrix_with_symm, bin); + const Bin bin(-oblique_seg_num, 0, 5, 6); + run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, proj_matrix_with_symm, bin); } { - const Bin bin(oblique_seg_num,0,5,0); - run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, - proj_matrix_with_symm, bin); + const Bin bin(oblique_seg_num, 0, 5, 0); + run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, proj_matrix_with_symm, bin); } { - const Bin bin(-oblique_seg_num,0,5,0); - run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, - proj_matrix_with_symm, bin); + const Bin bin(-oblique_seg_num, 0, 5, 0); + run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, proj_matrix_with_symm, bin); } { - const Bin bin(0,0,5,6); - run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, - proj_matrix_with_symm, bin); + const Bin bin(0, 0, 5, 6); + run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, proj_matrix_with_symm, bin); } { - const Bin bin(0,0,5,-6); - run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, - proj_matrix_with_symm, bin); + const Bin bin(0, 0, 5, -6); + run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, proj_matrix_with_symm, bin); } { - const Bin bin(0,0,5,0); - run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, - proj_matrix_with_symm, bin); + const Bin bin(0, 0, 5, 0); + run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, proj_matrix_with_symm, bin); } { - const Bin bin(oblique_seg_num,view45,5,6); - run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, - proj_matrix_with_symm, bin); + const Bin bin(oblique_seg_num, view45, 5, 6); + run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, proj_matrix_with_symm, bin); } { - const Bin bin(oblique_seg_num,view45,5,-6); - run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, - proj_matrix_with_symm, bin); + const Bin bin(oblique_seg_num, view45, 5, -6); + run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, proj_matrix_with_symm, bin); } { - const Bin bin(-oblique_seg_num,view45,5,6); - run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, - proj_matrix_with_symm, bin); + const Bin bin(-oblique_seg_num, view45, 5, 6); + run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, proj_matrix_with_symm, bin); } { - const Bin bin(-oblique_seg_num,view45,5,-6); - run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, - proj_matrix_with_symm, bin); + const Bin bin(-oblique_seg_num, view45, 5, -6); + run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, proj_matrix_with_symm, bin); } { - const Bin bin(oblique_seg_num,view45,5,0); - run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, - proj_matrix_with_symm, bin); + const Bin bin(oblique_seg_num, view45, 5, 0); + run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, proj_matrix_with_symm, bin); } { - const Bin bin(-oblique_seg_num,view45,5,0); - run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, - proj_matrix_with_symm, bin); + const Bin bin(-oblique_seg_num, view45, 5, 0); + run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, proj_matrix_with_symm, bin); } { - const Bin bin(0,view45,5,6); - run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, - proj_matrix_with_symm, bin); + const Bin bin(0, view45, 5, 6); + run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, proj_matrix_with_symm, bin); } { - const Bin bin(0,view45,5,0); - run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, - proj_matrix_with_symm, bin); + const Bin bin(0, view45, 5, 0); + run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, proj_matrix_with_symm, bin); } - { - const Bin bin(oblique_seg_num,2*view45+1,5,6); - run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, - proj_matrix_with_symm, bin); + const Bin bin(oblique_seg_num, 2 * view45 + 1, 5, 6); + run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, proj_matrix_with_symm, bin); } { - const Bin bin(oblique_seg_num,2*view45+1,5,-6); - run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, - proj_matrix_with_symm, bin); + const Bin bin(oblique_seg_num, 2 * view45 + 1, 5, -6); + run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, proj_matrix_with_symm, bin); } { - const Bin bin(-oblique_seg_num,2*view45+1,5,6); - run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, - proj_matrix_with_symm, bin); + const Bin bin(-oblique_seg_num, 2 * view45 + 1, 5, 6); + run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, proj_matrix_with_symm, bin); } { - const Bin bin(-oblique_seg_num,2*view45+1,5,-6); - run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, - proj_matrix_with_symm, bin); + const Bin bin(-oblique_seg_num, 2 * view45 + 1, 5, -6); + run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, proj_matrix_with_symm, bin); } { - const Bin bin(oblique_seg_num,2*view45+1,5,0); - run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, - proj_matrix_with_symm, bin); + const Bin bin(oblique_seg_num, 2 * view45 + 1, 5, 0); + run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, proj_matrix_with_symm, bin); } { - const Bin bin(-oblique_seg_num,2*view45+1,5,0); - run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, - proj_matrix_with_symm, bin); + const Bin bin(-oblique_seg_num, 2 * view45 + 1, 5, 0); + run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, proj_matrix_with_symm, bin); } { - const Bin bin(0,2*view45+1,5,6); - run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, - proj_matrix_with_symm, bin); + const Bin bin(0, 2 * view45 + 1, 5, 6); + run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, proj_matrix_with_symm, bin); } { - const Bin bin(0,2*view45+1,5,0); - run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, - proj_matrix_with_symm, bin); + const Bin bin(0, 2 * view45 + 1, 5, 0); + run_tests_2_proj_matrices_1_bin(proj_matrix_no_symm, proj_matrix_with_symm, bin); } #endif } - void -DataSymmetriesForBins_PET_CartesianGridTests:: -run_tests_all_symmetries(const shared_ptr& proj_data_info_sptr, - const shared_ptr >& density_sptr - ) -{ - - ProjMatrixByBinUsingRayTracing proj_matrix_no_sym; - { - stringstream str; - str << - "Ray Tracing Matrix Parameters :=\n" - "restrict to cylindrical FOV := 1\n" - "number of rays in tangential direction to trace for each bin := 1\n" - "use actual detector boundaries := 0\n" - "do symmetry 90degrees min phi := 0\n" - "do symmetry 180degrees min phi := 0\n" - "do_symmetry_swap_segment := 0\n" - "do_symmetry_swap_s := 0\n" - "do_symmetry_shift_z := 0\n" - "End Ray Tracing Matrix Parameters :=\n"; - if (!check(proj_matrix_no_sym.parse(str), - "parsing projection matrix parameters")) - return; - proj_matrix_no_sym.set_up(proj_data_info_sptr, density_sptr); +DataSymmetriesForBins_PET_CartesianGridTests::run_tests_all_symmetries( + const shared_ptr& proj_data_info_sptr, const shared_ptr>& density_sptr) { + + ProjMatrixByBinUsingRayTracing proj_matrix_no_sym; + { + stringstream str; + str << "Ray Tracing Matrix Parameters :=\n" + "restrict to cylindrical FOV := 1\n" + "number of rays in tangential direction to trace for each bin := 1\n" + "use actual detector boundaries := 0\n" + "do symmetry 90degrees min phi := 0\n" + "do symmetry 180degrees min phi := 0\n" + "do_symmetry_swap_segment := 0\n" + "do_symmetry_swap_s := 0\n" + "do_symmetry_shift_z := 0\n" + "End Ray Tracing Matrix Parameters :=\n"; + if (!check(proj_matrix_no_sym.parse(str), "parsing projection matrix parameters")) + return; + proj_matrix_no_sym.set_up(proj_data_info_sptr, density_sptr); + } + + { + cerr << "\t\tTesting with all symmetries\n"; + ProjMatrixByBinUsingRayTracing proj_matrix_with_sym; + + stringstream str; + str << "Ray Tracing Matrix Parameters :=\n" + "restrict to cylindrical FOV := 1\n" + "number of rays in tangential direction to trace for each bin := 1\n" + "use actual detector boundaries := 0\n" + "do symmetry 90degrees min phi := 1\n" + "do symmetry 180degrees min phi := 1\n" + "do_symmetry_swap_segment := 1\n" + "do_symmetry_swap_s := 1\n" + "do_symmetry_shift_z := 1\n" + "End Ray Tracing Matrix Parameters :=\n"; + if (check(proj_matrix_with_sym.parse(str), "parsing projection matrix parameters")) { + proj_matrix_with_sym.set_up(proj_data_info_sptr, density_sptr); + run_tests_2_proj_matrices(proj_matrix_no_sym, proj_matrix_with_sym); } - - { - cerr << "\t\tTesting with all symmetries\n"; - ProjMatrixByBinUsingRayTracing proj_matrix_with_sym; - - stringstream str; - str << - "Ray Tracing Matrix Parameters :=\n" - "restrict to cylindrical FOV := 1\n" - "number of rays in tangential direction to trace for each bin := 1\n" - "use actual detector boundaries := 0\n" - "do symmetry 90degrees min phi := 1\n" - "do symmetry 180degrees min phi := 1\n" - "do_symmetry_swap_segment := 1\n" - "do_symmetry_swap_s := 1\n" - "do_symmetry_shift_z := 1\n" - "End Ray Tracing Matrix Parameters :=\n"; - if (check(proj_matrix_with_sym.parse(str), - "parsing projection matrix parameters")) - { - proj_matrix_with_sym.set_up(proj_data_info_sptr, density_sptr); - run_tests_2_proj_matrices(proj_matrix_no_sym, proj_matrix_with_sym); - } - } - { - cerr << "\t\tTesting with all symmetries except 90-phi\n"; - ProjMatrixByBinUsingRayTracing proj_matrix_with_sym; - - stringstream str; - str << - "Ray Tracing Matrix Parameters :=\n" - "restrict to cylindrical FOV := 1\n" - "number of rays in tangential direction to trace for each bin := 1\n" - "use actual detector boundaries := 0\n" - "do symmetry 90degrees min phi := 0\n" - "do symmetry 180degrees min phi := 1\n" - "do_symmetry_swap_segment := 1\n" - "do_symmetry_swap_s := 1\n" - "do_symmetry_shift_z := 1\n" - "End Ray Tracing Matrix Parameters :=\n"; - if (check(proj_matrix_with_sym.parse(str), - "parsing projection matrix parameters")) - { - proj_matrix_with_sym.set_up(proj_data_info_sptr, density_sptr); - run_tests_2_proj_matrices(proj_matrix_no_sym, proj_matrix_with_sym); - } + } + { + cerr << "\t\tTesting with all symmetries except 90-phi\n"; + ProjMatrixByBinUsingRayTracing proj_matrix_with_sym; + + stringstream str; + str << "Ray Tracing Matrix Parameters :=\n" + "restrict to cylindrical FOV := 1\n" + "number of rays in tangential direction to trace for each bin := 1\n" + "use actual detector boundaries := 0\n" + "do symmetry 90degrees min phi := 0\n" + "do symmetry 180degrees min phi := 1\n" + "do_symmetry_swap_segment := 1\n" + "do_symmetry_swap_s := 1\n" + "do_symmetry_shift_z := 1\n" + "End Ray Tracing Matrix Parameters :=\n"; + if (check(proj_matrix_with_sym.parse(str), "parsing projection matrix parameters")) { + proj_matrix_with_sym.set_up(proj_data_info_sptr, density_sptr); + run_tests_2_proj_matrices(proj_matrix_no_sym, proj_matrix_with_sym); } - { - cerr << "\t\tTesting with all symmetries except phi symms\n"; - ProjMatrixByBinUsingRayTracing proj_matrix_with_sym; - - stringstream str; - str << - "Ray Tracing Matrix Parameters :=\n" - "restrict to cylindrical FOV := 1\n" - "number of rays in tangential direction to trace for each bin := 1\n" - "use actual detector boundaries := 0\n" - "do symmetry 90degrees min phi := 0\n" - "do symmetry 180degrees min phi := 0\n" - "do_symmetry_swap_segment := 1\n" - "do_symmetry_swap_s := 1\n" - "do_symmetry_shift_z := 1\n" - "End Ray Tracing Matrix Parameters :=\n"; - if (check(proj_matrix_with_sym.parse(str), - "parsing projection matrix parameters")) - { - proj_matrix_with_sym.set_up(proj_data_info_sptr, density_sptr); - run_tests_2_proj_matrices(proj_matrix_no_sym, proj_matrix_with_sym); - } + } + { + cerr << "\t\tTesting with all symmetries except phi symms\n"; + ProjMatrixByBinUsingRayTracing proj_matrix_with_sym; + + stringstream str; + str << "Ray Tracing Matrix Parameters :=\n" + "restrict to cylindrical FOV := 1\n" + "number of rays in tangential direction to trace for each bin := 1\n" + "use actual detector boundaries := 0\n" + "do symmetry 90degrees min phi := 0\n" + "do symmetry 180degrees min phi := 0\n" + "do_symmetry_swap_segment := 1\n" + "do_symmetry_swap_s := 1\n" + "do_symmetry_shift_z := 1\n" + "End Ray Tracing Matrix Parameters :=\n"; + if (check(proj_matrix_with_sym.parse(str), "parsing projection matrix parameters")) { + proj_matrix_with_sym.set_up(proj_data_info_sptr, density_sptr); + run_tests_2_proj_matrices(proj_matrix_no_sym, proj_matrix_with_sym); } - { - cerr << "\t\tTesting with all symmetries except swap_segment\n"; - ProjMatrixByBinUsingRayTracing proj_matrix_with_sym; - - stringstream str; - str << - "Ray Tracing Matrix Parameters :=\n" - "restrict to cylindrical FOV := 1\n" - "number of rays in tangential direction to trace for each bin := 1\n" - "use actual detector boundaries := 0\n" - "do symmetry 90degrees min phi := 1\n" - "do symmetry 180degrees min phi := 1\n" - "do_symmetry_swap_segment := 0\n" - "do_symmetry_swap_s := 1\n" - "do_symmetry_shift_z := 1\n" - "End Ray Tracing Matrix Parameters :=\n"; - if (check(proj_matrix_with_sym.parse(str), - "parsing projection matrix parameters")) - { - proj_matrix_with_sym.set_up(proj_data_info_sptr, density_sptr); - run_tests_2_proj_matrices(proj_matrix_no_sym, proj_matrix_with_sym); - } + } + { + cerr << "\t\tTesting with all symmetries except swap_segment\n"; + ProjMatrixByBinUsingRayTracing proj_matrix_with_sym; + + stringstream str; + str << "Ray Tracing Matrix Parameters :=\n" + "restrict to cylindrical FOV := 1\n" + "number of rays in tangential direction to trace for each bin := 1\n" + "use actual detector boundaries := 0\n" + "do symmetry 90degrees min phi := 1\n" + "do symmetry 180degrees min phi := 1\n" + "do_symmetry_swap_segment := 0\n" + "do_symmetry_swap_s := 1\n" + "do_symmetry_shift_z := 1\n" + "End Ray Tracing Matrix Parameters :=\n"; + if (check(proj_matrix_with_sym.parse(str), "parsing projection matrix parameters")) { + proj_matrix_with_sym.set_up(proj_data_info_sptr, density_sptr); + run_tests_2_proj_matrices(proj_matrix_no_sym, proj_matrix_with_sym); } - { - cerr << "\t\tTesting with all symmetries except swap_s\n"; - ProjMatrixByBinUsingRayTracing proj_matrix_with_sym; - - stringstream str; - str << - "Ray Tracing Matrix Parameters :=\n" - "restrict to cylindrical FOV := 1\n" - "number of rays in tangential direction to trace for each bin := 1\n" - "use actual detector boundaries := 0\n" - "do symmetry 90degrees min phi := 1\n" - "do symmetry 180degrees min phi := 1\n" - "do_symmetry_swap_segment := 1\n" - "do_symmetry_swap_s := 0\n" - "do_symmetry_shift_z := 1\n" - "End Ray Tracing Matrix Parameters :=\n"; - if (check(proj_matrix_with_sym.parse(str), - "parsing projection matrix parameters")) - { - proj_matrix_with_sym.set_up(proj_data_info_sptr, density_sptr); - run_tests_2_proj_matrices(proj_matrix_no_sym, proj_matrix_with_sym); - } + } + { + cerr << "\t\tTesting with all symmetries except swap_s\n"; + ProjMatrixByBinUsingRayTracing proj_matrix_with_sym; + + stringstream str; + str << "Ray Tracing Matrix Parameters :=\n" + "restrict to cylindrical FOV := 1\n" + "number of rays in tangential direction to trace for each bin := 1\n" + "use actual detector boundaries := 0\n" + "do symmetry 90degrees min phi := 1\n" + "do symmetry 180degrees min phi := 1\n" + "do_symmetry_swap_segment := 1\n" + "do_symmetry_swap_s := 0\n" + "do_symmetry_shift_z := 1\n" + "End Ray Tracing Matrix Parameters :=\n"; + if (check(proj_matrix_with_sym.parse(str), "parsing projection matrix parameters")) { + proj_matrix_with_sym.set_up(proj_data_info_sptr, density_sptr); + run_tests_2_proj_matrices(proj_matrix_no_sym, proj_matrix_with_sym); } - { - cerr << "\t\tTesting with all symmetries except shift_z\n"; - ProjMatrixByBinUsingRayTracing proj_matrix_with_sym; - - stringstream str; - str << - "Ray Tracing Matrix Parameters :=\n" - "restrict to cylindrical FOV := 1\n" - "number of rays in tangential direction to trace for each bin := 1\n" - "use actual detector boundaries := 0\n" - "do symmetry 90degrees min phi := 1\n" - "do symmetry 180degrees min phi := 1\n" - "do_symmetry_swap_segment := 1\n" - "do_symmetry_swap_s := 1\n" - "do_symmetry_shift_z := 0\n" - "End Ray Tracing Matrix Parameters :=\n"; - if (check(proj_matrix_with_sym.parse(str), - "parsing projection matrix parameters")) - { - proj_matrix_with_sym.set_up(proj_data_info_sptr, density_sptr); - run_tests_2_proj_matrices(proj_matrix_no_sym, proj_matrix_with_sym); - } + } + { + cerr << "\t\tTesting with all symmetries except shift_z\n"; + ProjMatrixByBinUsingRayTracing proj_matrix_with_sym; + + stringstream str; + str << "Ray Tracing Matrix Parameters :=\n" + "restrict to cylindrical FOV := 1\n" + "number of rays in tangential direction to trace for each bin := 1\n" + "use actual detector boundaries := 0\n" + "do symmetry 90degrees min phi := 1\n" + "do symmetry 180degrees min phi := 1\n" + "do_symmetry_swap_segment := 1\n" + "do_symmetry_swap_s := 1\n" + "do_symmetry_shift_z := 0\n" + "End Ray Tracing Matrix Parameters :=\n"; + if (check(proj_matrix_with_sym.parse(str), "parsing projection matrix parameters")) { + proj_matrix_with_sym.set_up(proj_data_info_sptr, density_sptr); + run_tests_2_proj_matrices(proj_matrix_no_sym, proj_matrix_with_sym); } - { - cerr << "\t\tTesting with only shift_z\n"; - ProjMatrixByBinUsingRayTracing proj_matrix_with_sym; - - stringstream str; - str << - "Ray Tracing Matrix Parameters :=\n" - "restrict to cylindrical FOV := 1\n" - "number of rays in tangential direction to trace for each bin := 1\n" - "use actual detector boundaries := 0\n" - "do symmetry 90degrees min phi := 0\n" - "do symmetry 180degrees min phi := 0\n" - "do_symmetry_swap_segment := 0\n" - "do_symmetry_swap_s := 0\n" - "do_symmetry_shift_z := 1\n" - "End Ray Tracing Matrix Parameters :=\n"; - if (check(proj_matrix_with_sym.parse(str), - "parsing projection matrix parameters")) - { - proj_matrix_with_sym.set_up(proj_data_info_sptr, density_sptr); - run_tests_2_proj_matrices(proj_matrix_no_sym, proj_matrix_with_sym); - } + } + { + cerr << "\t\tTesting with only shift_z\n"; + ProjMatrixByBinUsingRayTracing proj_matrix_with_sym; + + stringstream str; + str << "Ray Tracing Matrix Parameters :=\n" + "restrict to cylindrical FOV := 1\n" + "number of rays in tangential direction to trace for each bin := 1\n" + "use actual detector boundaries := 0\n" + "do symmetry 90degrees min phi := 0\n" + "do symmetry 180degrees min phi := 0\n" + "do_symmetry_swap_segment := 0\n" + "do_symmetry_swap_s := 0\n" + "do_symmetry_shift_z := 1\n" + "End Ray Tracing Matrix Parameters :=\n"; + if (check(proj_matrix_with_sym.parse(str), "parsing projection matrix parameters")) { + proj_matrix_with_sym.set_up(proj_data_info_sptr, density_sptr); + run_tests_2_proj_matrices(proj_matrix_no_sym, proj_matrix_with_sym); } + } } void -DataSymmetriesForBins_PET_CartesianGridTests:: -run_tests_for_1_projdata(const shared_ptr& proj_data_info_sptr) -{ - CartesianCoordinate3D origin (0,0,0); - const float zoom=1.F; +DataSymmetriesForBins_PET_CartesianGridTests::run_tests_for_1_projdata(const shared_ptr& proj_data_info_sptr) { + CartesianCoordinate3D origin(0, 0, 0); + const float zoom = 1.F; cerr << "\tTests with usual image size\n"; - - shared_ptr > - density_sptr(new VoxelsOnCartesianGrid(*proj_data_info_sptr,zoom,origin)); - VoxelsOnCartesianGrid & image = - dynamic_cast&>(*density_sptr); + shared_ptr> density_sptr(new VoxelsOnCartesianGrid(*proj_data_info_sptr, zoom, origin)); + + VoxelsOnCartesianGrid& image = dynamic_cast&>(*density_sptr); run_tests_all_symmetries(proj_data_info_sptr, density_sptr); cerr << "\tTests with shifted origin\n"; - density_sptr->set_origin(image.get_grid_spacing()* - CartesianCoordinate3D(3,0,0)); + density_sptr->set_origin(image.get_grid_spacing() * CartesianCoordinate3D(3, 0, 0)); run_tests_all_symmetries(proj_data_info_sptr, density_sptr); - const int org_z_length = - density_sptr->get_length(); - const CartesianCoordinate3D org_voxel_size = - image.get_voxel_size(); + const int org_z_length = density_sptr->get_length(); + const CartesianCoordinate3D org_voxel_size = image.get_voxel_size(); - cerr << "\tTests with non-standard range of planes (larger)\n"; - density_sptr->set_origin(CartesianCoordinate3D(0,0,0)); - density_sptr->grow(IndexRange3D(-2, org_z_length+3, - image.get_min_y(), image.get_max_y(), - image.get_min_x(), image.get_max_x())); + cerr << "\tTests with non-standard range of planes (larger)\n"; + density_sptr->set_origin(CartesianCoordinate3D(0, 0, 0)); + density_sptr->grow( + IndexRange3D(-2, org_z_length + 3, image.get_min_y(), image.get_max_y(), image.get_min_x(), image.get_max_x())); run_tests_all_symmetries(proj_data_info_sptr, density_sptr); #if 0 if (org_z_length>2) @@ -614,7 +510,7 @@ run_tests_for_1_projdata(const shared_ptr& proj_data_info_sptr) image.get_min_x(), image.get_max_x())); run_tests_all_symmetries(proj_data_info_sptr, density_sptr); } -#if 1 +# if 1 // this test currently fails with the ray tracing projmatrix // (but not with the interpolation projmatrix) if (org_z_length>1) @@ -640,118 +536,97 @@ run_tests_for_1_projdata(const shared_ptr& proj_data_info_sptr) image.get_min_x(), image.get_max_x())); run_tests_all_symmetries(proj_data_info_sptr, density_sptr); } -#endif +# endif #endif { - cerr << "\tTests with z voxel size 3 times smaller than usual\n"; - density_sptr->set_origin(CartesianCoordinate3D(0,0,0)); - image.set_grid_spacing(org_voxel_size/ - CartesianCoordinate3D(2,1,1)); - density_sptr->grow(IndexRange3D(-2, org_z_length*2, // note: -2 because grow doesn't allow shrinking! - image.get_min_y(), image.get_max_y(), - image.get_min_x(), image.get_max_x())); + cerr << "\tTests with z voxel size 3 times smaller than usual\n"; + density_sptr->set_origin(CartesianCoordinate3D(0, 0, 0)); + image.set_grid_spacing(org_voxel_size / CartesianCoordinate3D(2, 1, 1)); + density_sptr->grow(IndexRange3D(-2, org_z_length * 2, // note: -2 because grow doesn't allow shrinking! + image.get_min_y(), image.get_max_y(), image.get_min_x(), image.get_max_x())); run_tests_all_symmetries(proj_data_info_sptr, density_sptr); } - if (proj_data_info_sptr->get_sampling_in_m(Bin(0,0,0,0))/org_voxel_size.z()>=1.999) + if (proj_data_info_sptr->get_sampling_in_m(Bin(0, 0, 0, 0)) / org_voxel_size.z() >= 1.999) { + // currently symmetries do not work when the voxel size is larger than the ring_spacing + // so we only perform these tests when they can work { - // currently symmetries do not work when the voxel size is larger than the ring_spacing - // so we only perform these tests when they can work - { - cerr << "\tTests with z voxel size 2 times larger than usual\n"; - density_sptr->set_origin(CartesianCoordinate3D(0,0,0)); - image.set_grid_spacing(org_voxel_size* - CartesianCoordinate3D(2,1,1)); - density_sptr->resize(IndexRange3D(0, (org_z_length+1)/2-1, - image.get_min_y(), image.get_max_y(), - image.get_min_x(), image.get_max_x())); - run_tests_all_symmetries(proj_data_info_sptr, density_sptr); - } - { - cerr << "\tTests with usual z voxel size 2 times larger, 1 extra plane\n"; - density_sptr->set_origin(CartesianCoordinate3D(0,0,0)); - image.set_grid_spacing(org_voxel_size* - CartesianCoordinate3D(2,1,1)); - density_sptr->grow(IndexRange3D(0, (org_z_length+1)/2, - image.get_min_y(), image.get_max_y(), - image.get_min_x(), image.get_max_x())); - run_tests_all_symmetries(proj_data_info_sptr, density_sptr); - } + cerr << "\tTests with z voxel size 2 times larger than usual\n"; + density_sptr->set_origin(CartesianCoordinate3D(0, 0, 0)); + image.set_grid_spacing(org_voxel_size * CartesianCoordinate3D(2, 1, 1)); + density_sptr->resize(IndexRange3D(0, (org_z_length + 1) / 2 - 1, image.get_min_y(), image.get_max_y(), image.get_min_x(), + image.get_max_x())); + run_tests_all_symmetries(proj_data_info_sptr, density_sptr); } - + { + cerr << "\tTests with usual z voxel size 2 times larger, 1 extra plane\n"; + density_sptr->set_origin(CartesianCoordinate3D(0, 0, 0)); + image.set_grid_spacing(org_voxel_size * CartesianCoordinate3D(2, 1, 1)); + density_sptr->grow( + IndexRange3D(0, (org_z_length + 1) / 2, image.get_min_y(), image.get_max_y(), image.get_min_x(), image.get_max_x())); + run_tests_all_symmetries(proj_data_info_sptr, density_sptr); + } + } } void -DataSymmetriesForBins_PET_CartesianGridTests::run_tests() -{ +DataSymmetriesForBins_PET_CartesianGridTests::run_tests() { cerr << "Tests for DataSymmetriesForBins_PET_CartesianGrid\n"; - if (template_proj_data_filename == 0) + if (template_proj_data_filename == 0) { { - { - cerr << "Testing span=1\n"; - shared_ptr scanner_sptr(new Scanner(Scanner::E953)); - proj_data_info_sptr.reset( - ProjDataInfo::ProjDataInfoCTI(scanner_sptr, - /*span=*/1, - /*max_delta=*/5, - /*num_views=*/8, - /*num_tang_poss=*/16)); - - run_tests_for_1_projdata(proj_data_info_sptr); - } - { - cerr << "Testing span=3\n"; - // warning: make sure that parameters are ok such that hard-wired - // bins above are fine (e.g. segment 3 should be allowed) - shared_ptr scanner_sptr(new Scanner(Scanner::E953)); - proj_data_info_sptr.reset( - ProjDataInfo::ProjDataInfoCTI(scanner_sptr, - /*span=*/3, - /*max_delta=*/12, - /*num_views=*/8, - /*num_tang_poss=*/16)); - - - run_tests_for_1_projdata(proj_data_info_sptr); - } - { - cerr << "Testing with proj_data_info with time-of-flight"; - // warning: make sure that parameters are ok such that hard-wired - // bins above are fine (e.g. segment 3 should be allowed) - shared_ptr scanner_sptr(new Scanner(Scanner::PETMR_Signa)); - proj_data_info_sptr.reset( - ProjDataInfo::ProjDataInfoCTI(scanner_sptr, - /*span=*/11, - /*max_delta=*/5, - /*num_views=*/scanner_sptr->get_num_detectors_per_ring()/8, - /*num_tang_poss=*/64, - /*arc_corrected*/false, - /*tof_mashing*/116)); + cerr << "Testing span=1\n"; + shared_ptr scanner_sptr(new Scanner(Scanner::E953)); + proj_data_info_sptr.reset(ProjDataInfo::ProjDataInfoCTI(scanner_sptr, + /*span=*/1, + /*max_delta=*/5, + /*num_views=*/8, + /*num_tang_poss=*/16)); + run_tests_for_1_projdata(proj_data_info_sptr); + } + { + cerr << "Testing span=3\n"; + // warning: make sure that parameters are ok such that hard-wired + // bins above are fine (e.g. segment 3 should be allowed) + shared_ptr scanner_sptr(new Scanner(Scanner::E953)); + proj_data_info_sptr.reset(ProjDataInfo::ProjDataInfoCTI(scanner_sptr, + /*span=*/3, + /*max_delta=*/12, + /*num_views=*/8, + /*num_tang_poss=*/16)); - run_tests_for_1_projdata(proj_data_info_sptr); - } + run_tests_for_1_projdata(proj_data_info_sptr); } - else { - shared_ptr proj_data_sptr = - ProjData::read_from_file(template_proj_data_filename); - proj_data_info_sptr = - proj_data_sptr->get_proj_data_info_sptr()->create_shared_clone(); + cerr << "Testing with proj_data_info with time-of-flight"; + // warning: make sure that parameters are ok such that hard-wired + // bins above are fine (e.g. segment 3 should be allowed) + shared_ptr scanner_sptr(new Scanner(Scanner::PETMR_Signa)); + proj_data_info_sptr.reset(ProjDataInfo::ProjDataInfoCTI(scanner_sptr, + /*span=*/11, + /*max_delta=*/5, + /*num_views=*/scanner_sptr->get_num_detectors_per_ring() / 8, + /*num_tang_poss=*/64, + /*arc_corrected*/ false, + /*tof_mashing*/ 116)); + run_tests_for_1_projdata(proj_data_info_sptr); } + } else { + shared_ptr proj_data_sptr = ProjData::read_from_file(template_proj_data_filename); + proj_data_info_sptr = proj_data_sptr->get_proj_data_info_sptr()->create_shared_clone(); + run_tests_for_1_projdata(proj_data_info_sptr); + } } END_NAMESPACE_STIR - USING_NAMESPACE_STIR - -int main(int argc, char **argv) -{ - DataSymmetriesForBins_PET_CartesianGridTests tests(argc==2? argv[1] : 0); +int +main(int argc, char** argv) { + DataSymmetriesForBins_PET_CartesianGridTests tests(argc == 2 ? argv[1] : 0); tests.run_tests(); return tests.main_return_value(); } diff --git a/src/recon_test/test_FBP2D.cxx b/src/recon_test/test_FBP2D.cxx index b5a1cd4fc6..39c266100e 100644 --- a/src/recon_test/test_FBP2D.cxx +++ b/src/recon_test/test_FBP2D.cxx @@ -24,41 +24,33 @@ START_NAMESPACE_STIR -typedef DiscretisedDensity<3,float> target_type; +typedef DiscretisedDensity<3, float> target_type; /*! \ingroup recon_test \ingroup FBP2D \brief Test class for FBP2D */ -class TestFBP2D : public ReconstructionTests -{ +class TestFBP2D : public ReconstructionTests { private: typedef ReconstructionTests base_type; + public: //! Constructor that can take some input data to run the test with - TestFBP2D(const std::string &proj_data_filename = "", - const std::string & density_filename = "") - : base_type(proj_data_filename, density_filename) - {} + TestFBP2D(const std::string& proj_data_filename = "", const std::string& density_filename = "") + : base_type(proj_data_filename, density_filename) {} virtual ~TestFBP2D() {} - virtual void construct_reconstructor(); void run_tests(); }; - void -TestFBP2D:: -construct_reconstructor() -{ +TestFBP2D::construct_reconstructor() { this->_recon_sptr.reset(new FBP2DReconstruction); } void -TestFBP2D:: -run_tests() -{ +TestFBP2D::run_tests() { std::cerr << "Tests for FBP2D\n"; try { @@ -67,54 +59,45 @@ run_tests() shared_ptr output_sptr(this->_input_density_sptr->get_empty_copy()); this->reconstruct(output_sptr); this->compare(output_sptr); + } catch (const std::exception& error) { + std::cerr << "\nHere's the error:\n\t" << error.what() << "\n\n"; + everything_ok = false; + } catch (...) { + everything_ok = false; } - catch(const std::exception &error) - { - std::cerr << "\nHere's the error:\n\t" << error.what() << "\n\n"; - everything_ok = false; - } - catch(...) - { - everything_ok = false; - } // see if it checks input parameters { FBP2DReconstruction fbp(this->_proj_data_sptr, /*alpha*/ -1.F); - try - { - std::cerr << "\nYou should now see an error about a wrong setting for alpha" << std::endl; - fbp.set_up(this->_input_density_sptr); - // we shouldn't get here - everything_ok = false; - } - catch (...) - { - } + try { + std::cerr << "\nYou should now see an error about a wrong setting for alpha" << std::endl; + fbp.set_up(this->_input_density_sptr); + // we shouldn't get here + everything_ok = false; + } catch (...) { + } } } END_NAMESPACE_STIR - USING_NAMESPACE_STIR +int +main(int argc, char** argv) { + if (argc < 1 || argc > 3) { + std::cerr << "\n\tUsage: " << argv[0] << " [template_proj_data [image]]\n" + << "template_proj_data (optional) will serve as a template, but is otherwise not used.\n" + << "Image (optional) has to be compatible with projection data and currently at zoom=1\n"; + return EXIT_FAILURE; + } -int main(int argc, char **argv) -{ - if (argc < 1 || argc > 3) { - std::cerr << "\n\tUsage: " << argv[0] << " [template_proj_data [image]]\n" - << "template_proj_data (optional) will serve as a template, but is otherwise not used.\n" - << "Image (optional) has to be compatible with projection data and currently at zoom=1\n"; - return EXIT_FAILURE; - } - - //set_default_num_threads(); + // set_default_num_threads(); - TestFBP2D test(argc>1 ? argv[1] : "", argc > 2 ? argv[2] : ""); + TestFBP2D test(argc > 1 ? argv[1] : "", argc > 2 ? argv[2] : ""); - if (test.is_everything_ok()) - test.run_tests(); + if (test.is_everything_ok()) + test.run_tests(); - return test.main_return_value(); + return test.main_return_value(); } diff --git a/src/recon_test/test_FBP3DRP.cxx b/src/recon_test/test_FBP3DRP.cxx index 124ba24543..2c493b7665 100644 --- a/src/recon_test/test_FBP3DRP.cxx +++ b/src/recon_test/test_FBP3DRP.cxx @@ -24,41 +24,33 @@ START_NAMESPACE_STIR -typedef DiscretisedDensity<3,float> target_type; +typedef DiscretisedDensity<3, float> target_type; /*! \ingroup recon_test \ingroup FBP3DRP \brief Test class for FBP3DRP */ -class TestFBP3DRP : public ReconstructionTests -{ +class TestFBP3DRP : public ReconstructionTests { private: typedef ReconstructionTests base_type; + public: //! Constructor that can take some input data to run the test with - TestFBP3DRP(const std::string &proj_data_filename = "", - const std::string & density_filename = "") - : base_type(proj_data_filename, density_filename) - {} + TestFBP3DRP(const std::string& proj_data_filename = "", const std::string& density_filename = "") + : base_type(proj_data_filename, density_filename) {} virtual ~TestFBP3DRP() {} - virtual void construct_reconstructor(); void run_tests(); }; - void -TestFBP3DRP:: -construct_reconstructor() -{ +TestFBP3DRP::construct_reconstructor() { this->_recon_sptr.reset(new FBP3DRPReconstruction); } void -TestFBP3DRP:: -run_tests() -{ +TestFBP3DRP::run_tests() { std::cerr << "Tests for FBP3DRP\n"; try { @@ -67,26 +59,20 @@ run_tests() shared_ptr output_sptr(this->_input_density_sptr->get_empty_copy()); this->reconstruct(output_sptr); this->compare(output_sptr); + } catch (const std::exception& error) { + std::cerr << "\nHere's the error:\n\t" << error.what() << "\n\n"; + everything_ok = false; + } catch (...) { + everything_ok = false; } - catch(const std::exception &error) - { - std::cerr << "\nHere's the error:\n\t" << error.what() << "\n\n"; - everything_ok = false; - } - catch(...) - { - everything_ok = false; - } } END_NAMESPACE_STIR - USING_NAMESPACE_STIR - -int main(int argc, char **argv) -{ +int +main(int argc, char** argv) { if (argc < 1 || argc > 3) { std::cerr << "\n\tUsage: " << argv[0] << " [template_proj_data [image]]\n" << "template_proj_data (optional) will serve as a template, but is otherwise not used.\n" @@ -94,9 +80,9 @@ int main(int argc, char **argv) return EXIT_FAILURE; } - //set_default_num_threads(); + // set_default_num_threads(); - TestFBP3DRP test(argc>1 ? argv[1] : "", argc > 2 ? argv[2] : ""); + TestFBP3DRP test(argc > 1 ? argv[1] : "", argc > 2 ? argv[2] : ""); if (test.is_everything_ok()) test.run_tests(); diff --git a/src/recon_test/test_OSMAPOSL.cxx b/src/recon_test/test_OSMAPOSL.cxx index 42080cdb7e..9338ae3b0d 100644 --- a/src/recon_test/test_OSMAPOSL.cxx +++ b/src/recon_test/test_OSMAPOSL.cxx @@ -24,38 +24,30 @@ START_NAMESPACE_STIR -typedef DiscretisedDensity<3,float> target_type; +typedef DiscretisedDensity<3, float> target_type; /*! \ingroup recon_test \ingroup OSMAPOSL \brief Test class for OSMAPOSL */ -class TestOSMAPOSL : public PoissonLLReconstructionTests -{ +class TestOSMAPOSL : public PoissonLLReconstructionTests { private: typedef PoissonLLReconstructionTests base_type; + public: //! Constructor that can take some input data to run the test with - TestOSMAPOSL(const std::string &proj_data_filename = "", - const std::string & density_filename = "") - : base_type(proj_data_filename, density_filename) - {} + TestOSMAPOSL(const std::string& proj_data_filename = "", const std::string& density_filename = "") + : base_type(proj_data_filename, density_filename) {} virtual ~TestOSMAPOSL() {} - virtual void construct_reconstructor(); - OSMAPOSLReconstruction& - recon() - { return dynamic_cast& >(*this->_recon_sptr); } + OSMAPOSLReconstruction& recon() { return dynamic_cast&>(*this->_recon_sptr); } void run_tests(); }; - void -TestOSMAPOSL:: -construct_reconstructor() -{ +TestOSMAPOSL::construct_reconstructor() { this->_recon_sptr.reset(new OSMAPOSLReconstruction); this->construct_log_likelihood(); this->recon().set_objective_function_sptr(this->_objective_function_sptr); @@ -64,9 +56,7 @@ construct_reconstructor() } void -TestOSMAPOSL:: -run_tests() -{ +TestOSMAPOSL::run_tests() { std::cerr << "Tests for OSMAPOSL\n"; try { @@ -76,54 +66,44 @@ run_tests() output_sptr->fill(1.F); this->reconstruct(output_sptr); this->compare(output_sptr); + } catch (const std::exception& error) { + std::cerr << "\nHere's the error:\n\t" << error.what() << "\n\n"; + everything_ok = false; + } catch (...) { + everything_ok = false; } - catch(const std::exception &error) - { - std::cerr << "\nHere's the error:\n\t" << error.what() << "\n\n"; - everything_ok = false; - } - catch(...) - { - everything_ok = false; - } - if (everything_ok) - { - // see if it checks input parameters - try - { - std::cerr << "\nYou should now see an error about a wrong setting for MAP model" << std::endl; - this->recon().set_MAP_model("a_wrong_value"); - // we shouldn't get here - everything_ok = false; - } - catch (...) - { - } + if (everything_ok) { + // see if it checks input parameters + try { + std::cerr << "\nYou should now see an error about a wrong setting for MAP model" << std::endl; + this->recon().set_MAP_model("a_wrong_value"); + // we shouldn't get here + everything_ok = false; + } catch (...) { } + } } END_NAMESPACE_STIR - USING_NAMESPACE_STIR +int +main(int argc, char** argv) { + if (argc < 1 || argc > 3) { + std::cerr << "\n\tUsage: " << argv[0] << " [template_proj_data [image]]\n" + << "template_proj_data (optional) will serve as a template, but is otherwise not used.\n" + << "Image (optional) has to be compatible with projection data and currently at zoom=1\n"; + return EXIT_FAILURE; + } -int main(int argc, char **argv) -{ - if (argc < 1 || argc > 3) { - std::cerr << "\n\tUsage: " << argv[0] << " [template_proj_data [image]]\n" - << "template_proj_data (optional) will serve as a template, but is otherwise not used.\n" - << "Image (optional) has to be compatible with projection data and currently at zoom=1\n"; - return EXIT_FAILURE; - } - - //set_default_num_threads(); + // set_default_num_threads(); - TestOSMAPOSL test(argc>1 ? argv[1] : "", argc > 2 ? argv[2] : ""); + TestOSMAPOSL test(argc > 1 ? argv[1] : "", argc > 2 ? argv[2] : ""); - if (test.is_everything_ok()) - test.run_tests(); + if (test.is_everything_ok()) + test.run_tests(); - return test.main_return_value(); + return test.main_return_value(); } diff --git a/src/recon_test/test_PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx b/src/recon_test/test_PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx index c1d9bb42a4..68de5ac640 100644 --- a/src/recon_test/test_PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx +++ b/src/recon_test/test_PoissonLogLikelihoodWithLinearModelForMeanAndProjData.cxx @@ -19,7 +19,7 @@ \file \ingroup recon_test - + \brief Test program for stir::PoissonLogLikelihoodWithLinearModelForMeanAndProjData \par Usage @@ -64,31 +64,29 @@ #include "stir/recon_buildblock/distributable_main.h" START_NAMESPACE_STIR - /*! \ingroup test \brief Test class for PoissonLogLikelihoodWithLinearModelForMeanAndProjData This is a somewhat preliminary implementation of a test that compares the result - of GeneralisedObjectiveFunction::compute_gradient - with a numerical gradient computed by using the + of GeneralisedObjectiveFunction::compute_gradient + with a numerical gradient computed by using the GeneralisedObjectiveFunction::compute_objective_function() function. - The trouble with this is that compute the gradient voxel by voxel is obviously - terribly slow. A solution (for the test) would be to compute it only in + The trouble with this is that compute the gradient voxel by voxel is obviously + terribly slow. A solution (for the test) would be to compute it only in a subset of voxels or so. We'll leave this for later. Note that the test only works if the objective function is well-defined. For example, if certain projections are non-zero, while the model estimates them to be zero, the Poisson objective function is in theory infinite. - PoissonLogLikelihoodWithLinearModelForMeanAndProjData uses some thresholds to try to + PoissonLogLikelihoodWithLinearModelForMeanAndProjData uses some thresholds to try to avoid overflow, but if there are too many of these bins, the total objective function will become infinite. The numerical gradient then becomes ill-defined (even in voxels that do not contribute to these bins). */ -class PoissonLogLikelihoodWithLinearModelForMeanAndProjDataTests : public RunTests -{ +class PoissonLogLikelihoodWithLinearModelForMeanAndProjDataTests : public RunTests { public: //! Constructor that can take some input data to run the test with /*! This makes it possible to run the test with your own data. However, beware that @@ -98,61 +96,59 @@ class PoissonLogLikelihoodWithLinearModelForMeanAndProjDataTests : public RunTes \todo it would be better to parse an objective function. That would allow us to set all parameters from the command line. */ - PoissonLogLikelihoodWithLinearModelForMeanAndProjDataTests(char const * const proj_data_filename = 0, char const * const density_filename = 0); - typedef DiscretisedDensity<3,float> target_type; + PoissonLogLikelihoodWithLinearModelForMeanAndProjDataTests(char const* const proj_data_filename = 0, + char const* const density_filename = 0); + typedef DiscretisedDensity<3, float> target_type; void construct_input_data(shared_ptr& density_sptr); void run_tests(); + protected: - char const * proj_data_filename; - char const * density_filename; - shared_ptr > objective_function_sptr; + char const* proj_data_filename; + char const* density_filename; + shared_ptr> objective_function_sptr; //! run the test /*! Note that this function is not specific to PoissonLogLikelihoodWithLinearModelForMeanAndProjData */ - void run_tests_for_objective_function(GeneralisedObjectiveFunction& objective_function, - target_type& target); + void run_tests_for_objective_function(GeneralisedObjectiveFunction& objective_function, target_type& target); }; -PoissonLogLikelihoodWithLinearModelForMeanAndProjDataTests:: -PoissonLogLikelihoodWithLinearModelForMeanAndProjDataTests(char const * proj_data_filename, char const * const density_filename) - : proj_data_filename(proj_data_filename), density_filename(density_filename) -{} +PoissonLogLikelihoodWithLinearModelForMeanAndProjDataTests::PoissonLogLikelihoodWithLinearModelForMeanAndProjDataTests( + char const* proj_data_filename, char const* const density_filename) + : proj_data_filename(proj_data_filename), density_filename(density_filename) {} void -PoissonLogLikelihoodWithLinearModelForMeanAndProjDataTests:: -run_tests_for_objective_function(GeneralisedObjectiveFunction& objective_function, - PoissonLogLikelihoodWithLinearModelForMeanAndProjDataTests::target_type& target) { +PoissonLogLikelihoodWithLinearModelForMeanAndProjDataTests::run_tests_for_objective_function( + GeneralisedObjectiveFunction& objective_function, + PoissonLogLikelihoodWithLinearModelForMeanAndProjDataTests::target_type& target) { shared_ptr gradient_sptr(target.get_empty_copy()); shared_ptr gradient_2_sptr(target.get_empty_copy()); const int subset_num = 0; info("Computing gradient"); objective_function.compute_sub_gradient(*gradient_sptr, target, subset_num); - this->set_tolerance(std::max(fabs(double(gradient_sptr->find_min())), double(gradient_sptr->find_max()))/1000); + this->set_tolerance(std::max(fabs(double(gradient_sptr->find_min())), double(gradient_sptr->find_max())) / 1000); info("Computing objective function at target"); const double value_at_target = objective_function.compute_objective_function(target, subset_num); - target_type::full_iterator target_iter=target.begin_all(); - target_type::full_iterator gradient_iter=gradient_sptr->begin_all(); - target_type::full_iterator gradient_2_iter=gradient_2_sptr->begin_all(); + target_type::full_iterator target_iter = target.begin_all(); + target_type::full_iterator gradient_iter = gradient_sptr->begin_all(); + target_type::full_iterator gradient_2_iter = gradient_2_sptr->begin_all(); const float eps = 1e-2F; bool testOK = true; info("Computing gradient of objective function by numerical differences (this will take a while)"); - while(target_iter!=target.end_all()) - { - *target_iter += eps; - const double value_at_inc = objective_function.compute_objective_function(target, subset_num); - *target_iter -= eps; - const float gradient_at_iter = static_cast((value_at_inc - value_at_target)/eps); - *gradient_2_iter++ = gradient_at_iter; - testOK = testOK && - this->check_if_equal(gradient_at_iter, *gradient_iter, "gradient"); - ++target_iter; ++ gradient_iter; - } - if (!testOK) - { - info("Writing diagnostic files gradient.hv, numerical_gradient.hv"); - write_to_file("gradient.hv", *gradient_sptr); - write_to_file("numerical_gradient.hv", *gradient_2_sptr); + while (target_iter != target.end_all()) { + *target_iter += eps; + const double value_at_inc = objective_function.compute_objective_function(target, subset_num); + *target_iter -= eps; + const float gradient_at_iter = static_cast((value_at_inc - value_at_target) / eps); + *gradient_2_iter++ = gradient_at_iter; + testOK = testOK && this->check_if_equal(gradient_at_iter, *gradient_iter, "gradient"); + ++target_iter; + ++gradient_iter; + } + if (!testOK) { + info("Writing diagnostic files gradient.hv, numerical_gradient.hv"); + write_to_file("gradient.hv", *gradient_sptr); + write_to_file("numerical_gradient.hv", *gradient_2_sptr); #if 0 write_to_file("subsens.hv", reinterpret_cast &>(objective_function).get_subset_sensitivity(subset_num)); @@ -161,175 +157,134 @@ run_tests_for_objective_function(GeneralisedObjectiveFunction& density_sptr) -{ +PoissonLogLikelihoodWithLinearModelForMeanAndProjDataTests::construct_input_data(shared_ptr& density_sptr) { shared_ptr proj_data_sptr; - if (this->proj_data_filename == 0) - { - // construct a small scanner and sinogram - shared_ptr scanner_sptr(new Scanner(Scanner::E953)); - scanner_sptr->set_num_rings(5); - shared_ptr proj_data_info_sptr( - ProjDataInfo::ProjDataInfoCTI(scanner_sptr, - /*span=*/3, - /*max_delta=*/4, - /*num_views=*/16, - /*num_tang_poss=*/16)); - shared_ptr exam_info_sptr(new ExamInfo); - proj_data_sptr.reset(new ProjDataInMemory (exam_info_sptr, proj_data_info_sptr)); - for (int seg_num=proj_data_sptr->get_min_segment_num(); - seg_num<=proj_data_sptr->get_max_segment_num(); - ++seg_num) - { - for (int timing_pos_num = proj_data_sptr->get_min_tof_pos_num(); - timing_pos_num <= proj_data_sptr->get_max_tof_pos_num(); - ++timing_pos_num) - { - SegmentByView segment = proj_data_sptr->get_empty_segment_by_view(seg_num,false,timing_pos_num); - // fill in some crazy values - float value=0; - for (SegmentByView::full_iterator iter = segment.begin_all(); - iter != segment.end_all(); - ++iter) - { - value = float(fabs((seg_num+.1)*value - 5)); // needs to be positive for Poisson - *iter = value; - } - proj_data_sptr->set_segment(segment); - } + if (this->proj_data_filename == 0) { + // construct a small scanner and sinogram + shared_ptr scanner_sptr(new Scanner(Scanner::E953)); + scanner_sptr->set_num_rings(5); + shared_ptr proj_data_info_sptr(ProjDataInfo::ProjDataInfoCTI(scanner_sptr, + /*span=*/3, + /*max_delta=*/4, + /*num_views=*/16, + /*num_tang_poss=*/16)); + shared_ptr exam_info_sptr(new ExamInfo); + proj_data_sptr.reset(new ProjDataInMemory(exam_info_sptr, proj_data_info_sptr)); + for (int seg_num = proj_data_sptr->get_min_segment_num(); seg_num <= proj_data_sptr->get_max_segment_num(); ++seg_num) { + for (int timing_pos_num = proj_data_sptr->get_min_tof_pos_num(); timing_pos_num <= proj_data_sptr->get_max_tof_pos_num(); + ++timing_pos_num) { + SegmentByView segment = proj_data_sptr->get_empty_segment_by_view(seg_num, false, timing_pos_num); + // fill in some crazy values + float value = 0; + for (SegmentByView::full_iterator iter = segment.begin_all(); iter != segment.end_all(); ++iter) { + value = float(fabs((seg_num + .1) * value - 5)); // needs to be positive for Poisson + *iter = value; } + proj_data_sptr->set_segment(segment); + } } - else - { - proj_data_sptr = - ProjData::read_from_file(this->proj_data_filename); - } + } else { + proj_data_sptr = ProjData::read_from_file(this->proj_data_filename); + } - if (this->density_filename == 0) - { - // construct a small image - - CartesianCoordinate3D origin (0,0,0); - const float zoom=1.F; - - density_sptr.reset(new VoxelsOnCartesianGrid(*proj_data_sptr->get_proj_data_info_sptr(),zoom,origin)); - // fill with random numbers between 0 and 1 - typedef boost::mt19937 base_generator_type; - // initialize by reproducible seed - static base_generator_type generator(boost::uint32_t(42)); - static boost::uniform_01 random01(generator); - for (target_type::full_iterator iter=density_sptr->begin_all(); iter!=density_sptr->end_all(); ++iter) - *iter = static_cast(random01()); + if (this->density_filename == 0) { + // construct a small image - } - else - { - shared_ptr aptr(read_from_file(this->density_filename)); - density_sptr = aptr; - } + CartesianCoordinate3D origin(0, 0, 0); + const float zoom = 1.F; + + density_sptr.reset(new VoxelsOnCartesianGrid(*proj_data_sptr->get_proj_data_info_sptr(), zoom, origin)); + // fill with random numbers between 0 and 1 + typedef boost::mt19937 base_generator_type; + // initialize by reproducible seed + static base_generator_type generator(boost::uint32_t(42)); + static boost::uniform_01 random01(generator); + for (target_type::full_iterator iter = density_sptr->begin_all(); iter != density_sptr->end_all(); ++iter) + *iter = static_cast(random01()); + + } else { + shared_ptr aptr(read_from_file(this->density_filename)); + density_sptr = aptr; + } // make odd to avoid difficulties with outer-bin that isn't filled-in when using symmetries { - BasicCoordinate<3,int> min_ind, max_ind; - if (density_sptr->get_regular_range(min_ind,max_ind)) - { - for (int d=2; d<=3; ++d) - { - min_ind[d]=std::min(min_ind[d], -max_ind[d]); - max_ind[d]=std::max(-min_ind[d], max_ind[d]); - } - density_sptr->grow(IndexRange<3>(min_ind,max_ind)); - } + BasicCoordinate<3, int> min_ind, max_ind; + if (density_sptr->get_regular_range(min_ind, max_ind)) { + for (int d = 2; d <= 3; ++d) { + min_ind[d] = std::min(min_ind[d], -max_ind[d]); + max_ind[d] = std::max(-min_ind[d], max_ind[d]); + } + density_sptr->grow(IndexRange<3>(min_ind, max_ind)); + } } - // multiplicative term shared_ptr bin_norm_sptr(new TrivialBinNormalisation()); { - shared_ptr - mult_proj_data_sptr(new ProjDataInMemory (proj_data_sptr->get_exam_info_sptr(), - proj_data_sptr->get_proj_data_info_sptr()->create_shared_clone())); - for (int seg_num=proj_data_sptr->get_min_segment_num(); - seg_num<=proj_data_sptr->get_max_segment_num(); - ++seg_num) - { - for (int timing_pos_num = proj_data_sptr->get_min_tof_pos_num(); - timing_pos_num <= proj_data_sptr->get_max_tof_pos_num(); - ++timing_pos_num) - { - SegmentByView segment = proj_data_sptr->get_empty_segment_by_view(seg_num, false, timing_pos_num); - // fill in some crazy values - float value =0; - for (SegmentByView::full_iterator iter = segment.begin_all(); - iter != segment.end_all(); - ++iter) - { - value = float(fabs(seg_num*value - .2)); // needs to be positive for Poisson - *iter = value; - } - mult_proj_data_sptr->set_segment(segment); - } + shared_ptr mult_proj_data_sptr(new ProjDataInMemory( + proj_data_sptr->get_exam_info_sptr(), proj_data_sptr->get_proj_data_info_sptr()->create_shared_clone())); + for (int seg_num = proj_data_sptr->get_min_segment_num(); seg_num <= proj_data_sptr->get_max_segment_num(); ++seg_num) { + for (int timing_pos_num = proj_data_sptr->get_min_tof_pos_num(); timing_pos_num <= proj_data_sptr->get_max_tof_pos_num(); + ++timing_pos_num) { + SegmentByView segment = proj_data_sptr->get_empty_segment_by_view(seg_num, false, timing_pos_num); + // fill in some crazy values + float value = 0; + for (SegmentByView::full_iterator iter = segment.begin_all(); iter != segment.end_all(); ++iter) { + value = float(fabs(seg_num * value - .2)); // needs to be positive for Poisson + *iter = value; + } + mult_proj_data_sptr->set_segment(segment); } + } bin_norm_sptr.reset(new BinNormalisationFromProjData(mult_proj_data_sptr)); } // additive term - shared_ptr add_proj_data_sptr(new ProjDataInMemory (proj_data_sptr->get_exam_info_sptr(), - -proj_data_sptr->get_proj_data_info_sptr()->create_shared_clone())); + shared_ptr add_proj_data_sptr(new ProjDataInMemory(proj_data_sptr->get_exam_info_sptr(), + + proj_data_sptr->get_proj_data_info_sptr()->create_shared_clone())); { - for (int seg_num=proj_data_sptr->get_min_segment_num(); - seg_num<=proj_data_sptr->get_max_segment_num(); - ++seg_num) - { - for (int timing_pos_num = proj_data_sptr->get_min_tof_pos_num(); - timing_pos_num <= proj_data_sptr->get_max_tof_pos_num(); - ++timing_pos_num) - { - SegmentByView segment = proj_data_sptr->get_empty_segment_by_view(seg_num, false, timing_pos_num); - // fill in some crazy values - float value =0; - for (SegmentByView::full_iterator iter = segment.begin_all(); - iter != segment.end_all(); - ++iter) - { - value = float(fabs(seg_num*value - .3)); // needs to be positive for Poisson - *iter = value; - } - add_proj_data_sptr->set_segment(segment); - } + for (int seg_num = proj_data_sptr->get_min_segment_num(); seg_num <= proj_data_sptr->get_max_segment_num(); ++seg_num) { + for (int timing_pos_num = proj_data_sptr->get_min_tof_pos_num(); timing_pos_num <= proj_data_sptr->get_max_tof_pos_num(); + ++timing_pos_num) { + SegmentByView segment = proj_data_sptr->get_empty_segment_by_view(seg_num, false, timing_pos_num); + // fill in some crazy values + float value = 0; + for (SegmentByView::full_iterator iter = segment.begin_all(); iter != segment.end_all(); ++iter) { + value = float(fabs(seg_num * value - .3)); // needs to be positive for Poisson + *iter = value; + } + add_proj_data_sptr->set_segment(segment); } + } } objective_function_sptr.reset(new PoissonLogLikelihoodWithLinearModelForMeanAndProjData); PoissonLogLikelihoodWithLinearModelForMeanAndProjData& objective_function = - reinterpret_cast< PoissonLogLikelihoodWithLinearModelForMeanAndProjData& >(*objective_function_sptr); + reinterpret_cast&>(*objective_function_sptr); objective_function.set_proj_data_sptr(proj_data_sptr); objective_function.set_use_subset_sensitivities(true); shared_ptr proj_matrix_sptr(new ProjMatrixByBinUsingRayTracing()); shared_ptr proj_pair_sptr(new ProjectorByBinPairUsingProjMatrixByBin(proj_matrix_sptr)); - objective_function.set_projector_pair_sptr(proj_pair_sptr) ; + objective_function.set_projector_pair_sptr(proj_pair_sptr); /* void set_frame_num(const int); void set_frame_definitions(const TimeFrameDefinitions&); */ objective_function.set_normalisation_sptr(bin_norm_sptr); objective_function.set_additive_proj_data_sptr(add_proj_data_sptr); - objective_function.set_num_subsets(proj_data_sptr->get_num_views()/2); - if (!check(objective_function.set_up(density_sptr)==Succeeded::yes, "set-up of objective function")) + objective_function.set_num_subsets(proj_data_sptr->get_num_views() / 2); + if (!check(objective_function.set_up(density_sptr) == Succeeded::yes, "set-up of objective function")) return; } void -PoissonLogLikelihoodWithLinearModelForMeanAndProjDataTests:: -run_tests() -{ +PoissonLogLikelihoodWithLinearModelForMeanAndProjDataTests::run_tests() { std::cerr << "Tests for PoissonLogLikelihoodWithLinearModelForMeanAndProjData\n"; #if 1 @@ -340,29 +295,28 @@ run_tests() // alternative that gets the objective function from an OSMAPOSL .par file // currently disabled OSMAPOSLReconstruction recon(proj_data_filename); // actually .par - shared_ptr > objective_function_sptr = recon.get_objective_function_sptr(); - if (!check(objective_function_sptr->set_up(recon.get_initial_data_ptr())==Succeeded::yes, "set-up of objective function")) + shared_ptr> objective_function_sptr = recon.get_objective_function_sptr(); + if (!check(objective_function_sptr->set_up(recon.get_initial_data_ptr()) == Succeeded::yes, "set-up of objective function")) return; - this->run_tests_for_objective_function(*objective_function_sptr, - *recon.get_initial_data_ptr()); + this->run_tests_for_objective_function(*objective_function_sptr, *recon.get_initial_data_ptr()); #endif -} +} END_NAMESPACE_STIR - USING_NAMESPACE_STIR #ifdef STIR_MPI -int stir::distributable_main(int argc, char **argv) +int +stir::distributable_main(int argc, char** argv) #else -int main(int argc, char **argv) +int +main(int argc, char** argv) #endif { set_default_num_threads(); - PoissonLogLikelihoodWithLinearModelForMeanAndProjDataTests tests(argc>1? argv[1] : 0, - argc>2? argv[2] : 0); + PoissonLogLikelihoodWithLinearModelForMeanAndProjDataTests tests(argc > 1 ? argv[1] : 0, argc > 2 ? argv[2] : 0); tests.run_tests(); return tests.main_return_value(); } diff --git a/src/recon_test/test_consistency_root.cxx b/src/recon_test/test_consistency_root.cxx index e34201c38c..23f3e7d0af 100644 --- a/src/recon_test/test_consistency_root.cxx +++ b/src/recon_test/test_consistency_root.cxx @@ -57,115 +57,100 @@ START_NAMESPACE_STIR * */ -class ROOTconsistency_Tests : public RunTests -{ +class ROOTconsistency_Tests : public RunTests { public: - ROOTconsistency_Tests(const std::string& in, const std::string& image) - : root_header_filename(in), image_filename(image) - {} - void run_tests(); - - // Class to store the coordinates and weights of the maxima of the Lines-of-Response - // used to calculate the centre of gravity (see below). - class LORMax{ - public: - LORMax() : voxel_centre(CartesianCoordinate3D(0.f,0.f,0.f)), value(0.f) - {} - CartesianCoordinate3D voxel_centre; - float value; - }; + ROOTconsistency_Tests(const std::string& in, const std::string& image) : root_header_filename(in), image_filename(image) {} + void run_tests(); + + // Class to store the coordinates and weights of the maxima of the Lines-of-Response + // used to calculate the centre of gravity (see below). + class LORMax { + public: + LORMax() : voxel_centre(CartesianCoordinate3D(0.f, 0.f, 0.f)), value(0.f) {} + CartesianCoordinate3D voxel_centre; + float value; + }; private: - //! Reads listmode event by event, computes the ProjMatrixElemsForOneBin (probabilities - //! along a bin LOR) and stores in a vector the coordinates and weights of the - //! LOR maxima (vector::LORMax) prior to computing the centre of mass of those. - void construct_list_of_LOR_max( - const shared_ptr >& test_discretised_density_sptr); - - //! Selects and stores the highest probability elements of ProjMatrixElemsForOneBin. - void get_LOR_of_max(const ProjMatrixElemsForOneBin& probabilities, - const shared_ptr >& test_discretised_density_sptr); - - //! Given a vector::LORMax, computes the centre of mass. - CartesianCoordinate3D compute_centre_of_mass(); - - //! Checks if original and calculated coordinates are close enough. - void compare_original_and_calculated_coordinates( - const CartesianCoordinate3D& original_coords, - const CartesianCoordinate3D& centre_of_mass, - const BasicCoordinate<3, float>& grid_spacing); - - //! Modified version of check_if_equal for this test - bool check_if_almost_equal(const double a, const double b, const std::string& str, const double tolerance); - - std::string root_header_filename; - std::string image_filename; - std::vector max_lor; + //! Reads listmode event by event, computes the ProjMatrixElemsForOneBin (probabilities + //! along a bin LOR) and stores in a vector the coordinates and weights of the + //! LOR maxima (vector::LORMax) prior to computing the centre of mass of those. + void construct_list_of_LOR_max(const shared_ptr>& test_discretised_density_sptr); + + //! Selects and stores the highest probability elements of ProjMatrixElemsForOneBin. + void get_LOR_of_max(const ProjMatrixElemsForOneBin& probabilities, + const shared_ptr>& test_discretised_density_sptr); + + //! Given a vector::LORMax, computes the centre of mass. + CartesianCoordinate3D compute_centre_of_mass(); + + //! Checks if original and calculated coordinates are close enough. + void compare_original_and_calculated_coordinates(const CartesianCoordinate3D& original_coords, + const CartesianCoordinate3D& centre_of_mass, + const BasicCoordinate<3, float>& grid_spacing); + + //! Modified version of check_if_equal for this test + bool check_if_almost_equal(const double a, const double b, const std::string& str, const double tolerance); + + std::string root_header_filename; + std::string image_filename; + std::vector max_lor; }; void -ROOTconsistency_Tests::run_tests() -{ - // DiscretisedDensity for original image - shared_ptr > discretised_density_sptr(DiscretisedDensity<3,float>::read_from_file(image_filename)); +ROOTconsistency_Tests::run_tests() { + // DiscretisedDensity for original image + shared_ptr> discretised_density_sptr(DiscretisedDensity<3, float>::read_from_file(image_filename)); - // needs to be cast to VoxelsOnCartesianGrid to be able to calculate the centre of gravity, - // hence the location of the original source, stored in test_original_coords. - const VoxelsOnCartesianGrid& discretised_cartesian_grid = - dynamic_cast &>(*discretised_density_sptr); - CartesianCoordinate3D original_coords = find_centre_of_gravity_in_mm(discretised_cartesian_grid); + // needs to be cast to VoxelsOnCartesianGrid to be able to calculate the centre of gravity, + // hence the location of the original source, stored in test_original_coords. + const VoxelsOnCartesianGrid& discretised_cartesian_grid = + dynamic_cast&>(*discretised_density_sptr); + CartesianCoordinate3D original_coords = find_centre_of_gravity_in_mm(discretised_cartesian_grid); - construct_list_of_LOR_max(discretised_density_sptr); + construct_list_of_LOR_max(discretised_density_sptr); - CartesianCoordinate3D centreofmass = compute_centre_of_mass(); + CartesianCoordinate3D centreofmass = compute_centre_of_mass(); - compare_original_and_calculated_coordinates(original_coords,centreofmass,discretised_cartesian_grid.get_grid_spacing()); + compare_original_and_calculated_coordinates(original_coords, centreofmass, discretised_cartesian_grid.get_grid_spacing()); } void -ROOTconsistency_Tests:: -construct_list_of_LOR_max(const shared_ptr >& discretised_density_sptr) -{ - shared_ptr lm_data_sptr(read_from_file(root_header_filename)); - - shared_ptr proj_matrix_sptr(new ProjMatrixByBinUsingRayTracing()); - - proj_matrix_sptr.get()->set_up(lm_data_sptr->get_proj_data_info_sptr(), - discretised_density_sptr); - proj_matrix_sptr->enable_tof(lm_data_sptr->get_proj_data_info_sptr()); - - ProjMatrixElemsForOneBin proj_matrix_row; - - { - // loop over all events in the listmode file - shared_ptr record_sptr = lm_data_sptr->get_empty_record_sptr(); - CListRecord& record = *record_sptr; - while (lm_data_sptr->get_next_record(record) == Succeeded::yes) - { - // only stores prompts - if (record.is_event() && record.event().is_prompt()) - { - Bin bin; - bin.set_bin_value(1.f); - // gets the bin corresponding to the event - record.event().get_bin(bin, *lm_data_sptr->get_proj_data_info_sptr()); - if ( bin.get_bin_value()>0 ) - { - // computes the TOF probabilities along the bin LOR - proj_matrix_sptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, bin); - // adds coordinates and weights of the elements with highest probability along LOR - get_LOR_of_max(proj_matrix_row, discretised_density_sptr); - } - } - } - } +ROOTconsistency_Tests::construct_list_of_LOR_max(const shared_ptr>& discretised_density_sptr) { + shared_ptr lm_data_sptr(read_from_file(root_header_filename)); + + shared_ptr proj_matrix_sptr(new ProjMatrixByBinUsingRayTracing()); + + proj_matrix_sptr.get()->set_up(lm_data_sptr->get_proj_data_info_sptr(), discretised_density_sptr); + proj_matrix_sptr->enable_tof(lm_data_sptr->get_proj_data_info_sptr()); + ProjMatrixElemsForOneBin proj_matrix_row; + + { + // loop over all events in the listmode file + shared_ptr record_sptr = lm_data_sptr->get_empty_record_sptr(); + CListRecord& record = *record_sptr; + while (lm_data_sptr->get_next_record(record) == Succeeded::yes) { + // only stores prompts + if (record.is_event() && record.event().is_prompt()) { + Bin bin; + bin.set_bin_value(1.f); + // gets the bin corresponding to the event + record.event().get_bin(bin, *lm_data_sptr->get_proj_data_info_sptr()); + if (bin.get_bin_value() > 0) { + // computes the TOF probabilities along the bin LOR + proj_matrix_sptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, bin); + // adds coordinates and weights of the elements with highest probability along LOR + get_LOR_of_max(proj_matrix_row, discretised_density_sptr); + } + } + } + } } void -ROOTconsistency_Tests::get_LOR_of_max(const ProjMatrixElemsForOneBin& probabilities, const shared_ptr >& test_discretised_density_sptr) -{ +ROOTconsistency_Tests::get_LOR_of_max(const ProjMatrixElemsForOneBin& probabilities, + const shared_ptr>& test_discretised_density_sptr) { std::stack tmp_max_lor; CartesianCoordinate3D voxel_centre; @@ -174,13 +159,10 @@ ROOTconsistency_Tests::get_LOR_of_max(const ProjMatrixElemsForOneBin& probabilit ProjMatrixElemsForOneBin::const_iterator element_ptr = probabilities.begin(); // iterative calculation of highest probability and corresponding elements along the LOR - while (element_ptr != probabilities.end()) - { - if (element_ptr->get_value() >= maxLOR) - { + while (element_ptr != probabilities.end()) { + if (element_ptr->get_value() >= maxLOR) { maxLOR = element_ptr->get_value(); - voxel_centre = - test_discretised_density_sptr->get_physical_coordinates_for_indices(element_ptr->get_coords()); + voxel_centre = test_discretised_density_sptr->get_physical_coordinates_for_indices(element_ptr->get_coords()); LORMax tmp; tmp.value = element_ptr->get_value(); tmp.voxel_centre = voxel_centre; @@ -190,130 +172,108 @@ ROOTconsistency_Tests::get_LOR_of_max(const ProjMatrixElemsForOneBin& probabilit } // only selects the elements on top of the stack, corresponding to the highest probability - if (maxLOR !=0) - { - while(!tmp_max_lor.empty()) - { - if (tmp_max_lor.top().value == maxLOR) - { + if (maxLOR != 0) { + while (!tmp_max_lor.empty()) { + if (tmp_max_lor.top().value == maxLOR) { max_lor.push_back(tmp_max_lor.top()); tmp_max_lor.pop(); - } - else break; + } else + break; } } } -CartesianCoordinate3D ROOTconsistency_Tests::compute_centre_of_mass() -{ +CartesianCoordinate3D +ROOTconsistency_Tests::compute_centre_of_mass() { // creation of a file with all LOR maxima, to be able to plot them std::ofstream myfile; - std::string file_name = image_filename.substr(0,image_filename.size()-3) + ".txt"; - myfile.open (file_name.c_str()); + std::string file_name = image_filename.substr(0, image_filename.size() - 3) + ".txt"; + myfile.open(file_name.c_str()); LORMax centreofmass; // computes centre of mass - for (std::vector::iterator lor_element_ptr=max_lor.begin(); - lor_element_ptr != max_lor.end();++lor_element_ptr) - { - centreofmass.voxel_centre.x() += lor_element_ptr->voxel_centre.x()*lor_element_ptr->value; - centreofmass.voxel_centre.y() += lor_element_ptr->voxel_centre.y()*lor_element_ptr->value; - centreofmass.voxel_centre.z() += lor_element_ptr->voxel_centre.z()*lor_element_ptr->value; + for (std::vector::iterator lor_element_ptr = max_lor.begin(); lor_element_ptr != max_lor.end(); ++lor_element_ptr) { + centreofmass.voxel_centre.x() += lor_element_ptr->voxel_centre.x() * lor_element_ptr->value; + centreofmass.voxel_centre.y() += lor_element_ptr->voxel_centre.y() * lor_element_ptr->value; + centreofmass.voxel_centre.z() += lor_element_ptr->voxel_centre.z() * lor_element_ptr->value; centreofmass.value += lor_element_ptr->value; - myfile << lor_element_ptr->voxel_centre.x() << " " - << lor_element_ptr->voxel_centre.y() << " " - << lor_element_ptr->voxel_centre.z() << " " - << lor_element_ptr->value << std::endl; - + myfile << lor_element_ptr->voxel_centre.x() << " " << lor_element_ptr->voxel_centre.y() << " " + << lor_element_ptr->voxel_centre.z() << " " << lor_element_ptr->value << std::endl; } // needs to divide by the weights - if (centreofmass.value != 0) - { - centreofmass.voxel_centre.x()=centreofmass.voxel_centre.x()/centreofmass.value; - centreofmass.voxel_centre.y()=centreofmass.voxel_centre.y()/centreofmass.value; - centreofmass.voxel_centre.z()=centreofmass.voxel_centre.z()/centreofmass.value; - } - else - { + if (centreofmass.value != 0) { + centreofmass.voxel_centre.x() = centreofmass.voxel_centre.x() / centreofmass.value; + centreofmass.voxel_centre.y() = centreofmass.voxel_centre.y() / centreofmass.value; + centreofmass.voxel_centre.z() = centreofmass.voxel_centre.z() / centreofmass.value; + } else { warning("Total weight of the centre of mass equal to 0. Please check your data."); - centreofmass.voxel_centre.x()=0; - centreofmass.voxel_centre.y()=0; - centreofmass.voxel_centre.z()=0; + centreofmass.voxel_centre.x() = 0; + centreofmass.voxel_centre.y() = 0; + centreofmass.voxel_centre.z() = 0; } - cerr << "Centre of gravity coordinates: " << centreofmass.voxel_centre.x() << " " - << centreofmass.voxel_centre.y() << " " << centreofmass.voxel_centre.z() << std::endl; + cerr << "Centre of gravity coordinates: " << centreofmass.voxel_centre.x() << " " << centreofmass.voxel_centre.y() << " " + << centreofmass.voxel_centre.z() << std::endl; myfile.close(); return centreofmass.voxel_centre; - } // TODO change this -void ROOTconsistency_Tests::compare_original_and_calculated_coordinates(const CartesianCoordinate3D& original_coords, - const CartesianCoordinate3D& centre_of_mass, const BasicCoordinate<3, float>& grid_spacing) -{ - check_if_almost_equal(static_cast(original_coords.x()),static_cast(centre_of_mass.x()),"x",grid_spacing[1]); - check_if_almost_equal(static_cast(original_coords.y()),static_cast(centre_of_mass.y()),"y",grid_spacing[2]); - check_if_almost_equal(static_cast(original_coords.z()),static_cast(centre_of_mass.z()),"z",grid_spacing[3]); - - cerr << "Original coordinates: " << original_coords.x() << " " - << original_coords.y() << " " << original_coords.z() << std::endl; +void +ROOTconsistency_Tests::compare_original_and_calculated_coordinates(const CartesianCoordinate3D& original_coords, + const CartesianCoordinate3D& centre_of_mass, + const BasicCoordinate<3, float>& grid_spacing) { + check_if_almost_equal(static_cast(original_coords.x()), static_cast(centre_of_mass.x()), "x", grid_spacing[1]); + check_if_almost_equal(static_cast(original_coords.y()), static_cast(centre_of_mass.y()), "y", grid_spacing[2]); + check_if_almost_equal(static_cast(original_coords.z()), static_cast(centre_of_mass.z()), "z", grid_spacing[3]); + + cerr << "Original coordinates: " << original_coords.x() << " " << original_coords.y() << " " << original_coords.z() + << std::endl; } bool -ROOTconsistency_Tests::check_if_almost_equal(const double a, const double b, std::string str, const double tolerance) -{ - if ((fabs(a-b) > tolerance)) - { - std::cerr << "Error : unequal values are " << a << " and " << b - << ". " << str << std::endl; +ROOTconsistency_Tests::check_if_almost_equal(const double a, const double b, std::string str, const double tolerance) { + if ((fabs(a - b) > tolerance)) { + std::cerr << "Error : unequal values are " << a << " and " << b << ". " << str << std::endl; everything_ok = false; return false; - } - else + } else return true; } END_NAMESPACE_STIR -int main(int argc, char **argv) -{ +int +main(int argc, char** argv) { USING_NAMESPACE_STIR - if (argc != 3) - { - cerr << "Usage : " << argv[1] << " filename " - << argv[2] << "original image \n" + if (argc != 3) { + cerr << "Usage : " << argv[1] << " filename " << argv[2] << "original image \n" << "See source file for the format of this file.\n\n"; return EXIT_FAILURE; } - ifstream in(argv[1]); - if (!in) - { - cerr << argv[0] - << ": Error opening root file " << argv[1] << "\nExiting.\n"; + if (!in) { + cerr << argv[0] << ": Error opening root file " << argv[1] << "\nExiting.\n"; return EXIT_FAILURE; } ifstream in2(argv[2]); - if (!in2) - { - cerr << argv[0] - << ": Error opening original image " << argv[2] << "\nExiting.\n"; + if (!in2) { + cerr << argv[0] << ": Error opening original image " << argv[2] << "\nExiting.\n"; return EXIT_FAILURE; } std::string filename(argv[1]); std::string image(argv[2]); - ROOTconsistency_Tests tests(filename,image); - tests.run_tests(); - return tests.main_return_value(); + ROOTconsistency_Tests tests(filename, image); + tests.run_tests(); + return tests.main_return_value(); } diff --git a/src/recon_test/test_data_processor_projectors.cxx b/src/recon_test/test_data_processor_projectors.cxx index 52e5afd325..0b135298ec 100644 --- a/src/recon_test/test_data_processor_projectors.cxx +++ b/src/recon_test/test_data_processor_projectors.cxx @@ -33,188 +33,171 @@ START_NAMESPACE_STIR \ingroup test \brief Test class for GPU projectors */ -class TestDataProcessorProjectors : public RunTests -{ +class TestDataProcessorProjectors : public RunTests { public: - //! Constructor that can take some input data to run the test with - TestDataProcessorProjectors(const std::string &sinogram_filename, const float fwhm); + //! Constructor that can take some input data to run the test with + TestDataProcessorProjectors(const std::string& sinogram_filename, const float fwhm); - virtual ~TestDataProcessorProjectors() {} + virtual ~TestDataProcessorProjectors() {} + + void run_tests(); - void run_tests(); protected: - std::string _sinogram_filename; - float _fwhm; - shared_ptr _input_sino_sptr; - const std::vector > > post_data_processor_bck_proj(); - const std::vector > pre_data_processor_fwd_proj(const DiscretisedDensity<3,float> &input_image); + std::string _sinogram_filename; + float _fwhm; + shared_ptr _input_sino_sptr; + const std::vector>> post_data_processor_bck_proj(); + const std::vector> pre_data_processor_fwd_proj(const DiscretisedDensity<3, float>& input_image); }; -TestDataProcessorProjectors::TestDataProcessorProjectors(const std::string &sinogram_filename, const float fwhm) : - _sinogram_filename(sinogram_filename), - _fwhm(fwhm) -{ -} +TestDataProcessorProjectors::TestDataProcessorProjectors(const std::string& sinogram_filename, const float fwhm) + : _sinogram_filename(sinogram_filename), _fwhm(fwhm) {} -static -Succeeded -compare_arrays(const std::vector &vec1, const std::vector &vec2) -{ - // Subtract - std::vector diff = vec1; - std::transform(vec1.begin(), vec1.end(), vec2.begin(), diff.begin(), std::minus()); +static Succeeded +compare_arrays(const std::vector& vec1, const std::vector& vec2) { + // Subtract + std::vector diff = vec1; + std::transform(vec1.begin(), vec1.end(), vec2.begin(), diff.begin(), std::minus()); - // Get max difference - const float max_diff = *std::max_element(diff.begin(), diff.end()); + // Get max difference + const float max_diff = *std::max_element(diff.begin(), diff.end()); - std::cout << "Min array 1 / array 2 = " << *std::min_element(vec1.begin(),vec1.end()) << " / " << *std::min_element(vec2.begin(),vec2.end()) << "\n"; - std::cout << "Max array 1 / array 2 = " << *std::max_element(vec1.begin(),vec1.end()) << " / " << *std::max_element(vec2.begin(),vec2.end()) << "\n"; - std::cout << "Sum array 1 / array 2 = " << std::accumulate(vec1.begin(),vec1.end(),0.f) << " / " << std::accumulate(vec2.begin(),vec2.end(),0.f) << "\n"; - std::cout << "Max diff = " << max_diff << "\n\n"; + std::cout << "Min array 1 / array 2 = " << *std::min_element(vec1.begin(), vec1.end()) << " / " + << *std::min_element(vec2.begin(), vec2.end()) << "\n"; + std::cout << "Max array 1 / array 2 = " << *std::max_element(vec1.begin(), vec1.end()) << " / " + << *std::max_element(vec2.begin(), vec2.end()) << "\n"; + std::cout << "Sum array 1 / array 2 = " << std::accumulate(vec1.begin(), vec1.end(), 0.f) << " / " + << std::accumulate(vec2.begin(), vec2.end(), 0.f) << "\n"; + std::cout << "Max diff = " << max_diff << "\n\n"; - return (std::abs(max_diff) < 1e-3f ? Succeeded::yes : Succeeded::no); + return (std::abs(max_diff) < 1e-3f ? Succeeded::yes : Succeeded::no); } -static -void -compare_images(bool &everything_ok, const DiscretisedDensity<3,float> &im_1, const DiscretisedDensity<3,float> &im_2) -{ - std::cout << "\nComparing images...\n"; - - if (!im_1.has_same_characteristics(im_2)) { - std::cout << "\nImages have different characteristics!\n"; - everything_ok = false; - } - - Coordinate3D min_indices, max_indices; - - im_1.get_regular_range(min_indices, max_indices); - unsigned num_elements = 1; - for (int i=0; i<3; ++i) - num_elements *= unsigned(max_indices[i + 1] - min_indices[i + 1] + 1); - - std::vector arr_1(num_elements), arr_2(num_elements); - - DiscretisedDensity<3,float>::const_full_iterator im_1_iter = im_1.begin_all_const(); - DiscretisedDensity<3,float>::const_full_iterator im_2_iter = im_2.begin_all_const(); - std::vector::iterator arr_1_iter = arr_1.begin(); - std::vector::iterator arr_2_iter = arr_2.begin(); - while (im_1_iter!=im_1.end_all_const()) { - *arr_1_iter = *im_1_iter; - *arr_2_iter = *im_2_iter; - ++im_1_iter; - ++im_2_iter; - ++arr_1_iter; - ++arr_2_iter; - } - - // Compare values - if (compare_arrays(arr_1,arr_2) == Succeeded::yes) - std::cout << "Images match!\n"; - else { - std::cout << "Images don't match!\n"; - everything_ok = false; - } +static void +compare_images(bool& everything_ok, const DiscretisedDensity<3, float>& im_1, const DiscretisedDensity<3, float>& im_2) { + std::cout << "\nComparing images...\n"; + + if (!im_1.has_same_characteristics(im_2)) { + std::cout << "\nImages have different characteristics!\n"; + everything_ok = false; + } + + Coordinate3D min_indices, max_indices; + + im_1.get_regular_range(min_indices, max_indices); + unsigned num_elements = 1; + for (int i = 0; i < 3; ++i) + num_elements *= unsigned(max_indices[i + 1] - min_indices[i + 1] + 1); + + std::vector arr_1(num_elements), arr_2(num_elements); + + DiscretisedDensity<3, float>::const_full_iterator im_1_iter = im_1.begin_all_const(); + DiscretisedDensity<3, float>::const_full_iterator im_2_iter = im_2.begin_all_const(); + std::vector::iterator arr_1_iter = arr_1.begin(); + std::vector::iterator arr_2_iter = arr_2.begin(); + while (im_1_iter != im_1.end_all_const()) { + *arr_1_iter = *im_1_iter; + *arr_2_iter = *im_2_iter; + ++im_1_iter; + ++im_2_iter; + ++arr_1_iter; + ++arr_2_iter; + } + + // Compare values + if (compare_arrays(arr_1, arr_2) == Succeeded::yes) + std::cout << "Images match!\n"; + else { + std::cout << "Images don't match!\n"; + everything_ok = false; + } } -static -void -compare_sinos(bool &everything_ok, const ProjData &proj_data_1, const ProjData &proj_data_2) -{ - std::cout << "\nComparing sinograms...\n"; - - if (*proj_data_1.get_proj_data_info_sptr() != *proj_data_2.get_proj_data_info_sptr()) { - std::cout << "\nSinogram proj data info don't match\n"; - everything_ok = false; - } - - int min_segment_num = proj_data_1.get_min_segment_num(); - int max_segment_num = proj_data_1.get_max_segment_num(); - - // Get number of elements - unsigned num_elements(0); - for (int segment_num = min_segment_num; segment_num<= max_segment_num; ++segment_num) - num_elements += unsigned(proj_data_1.get_max_axial_pos_num(segment_num) - proj_data_1.get_min_axial_pos_num(segment_num)) + 1; - num_elements *= unsigned(proj_data_1.get_max_view_num() - proj_data_1.get_min_view_num()) + 1; - num_elements *= unsigned(proj_data_1.get_max_tangential_pos_num() - proj_data_1.get_min_tangential_pos_num()) + 1; - - // Create arrays - std::vector arr_1(num_elements), arr_2(num_elements); - proj_data_1.copy_to(arr_1.begin()); - proj_data_2.copy_to(arr_2.begin()); - - // Compare values - if (compare_arrays(arr_1,arr_2) == Succeeded::yes) - std::cout << "Sinograms match!\n"; - else { - std::cout << "Sinograms don't match!\n"; - everything_ok = false; - } +static void +compare_sinos(bool& everything_ok, const ProjData& proj_data_1, const ProjData& proj_data_2) { + std::cout << "\nComparing sinograms...\n"; + + if (*proj_data_1.get_proj_data_info_sptr() != *proj_data_2.get_proj_data_info_sptr()) { + std::cout << "\nSinogram proj data info don't match\n"; + everything_ok = false; + } + + int min_segment_num = proj_data_1.get_min_segment_num(); + int max_segment_num = proj_data_1.get_max_segment_num(); + + // Get number of elements + unsigned num_elements(0); + for (int segment_num = min_segment_num; segment_num <= max_segment_num; ++segment_num) + num_elements += unsigned(proj_data_1.get_max_axial_pos_num(segment_num) - proj_data_1.get_min_axial_pos_num(segment_num)) + 1; + num_elements *= unsigned(proj_data_1.get_max_view_num() - proj_data_1.get_min_view_num()) + 1; + num_elements *= unsigned(proj_data_1.get_max_tangential_pos_num() - proj_data_1.get_min_tangential_pos_num()) + 1; + + // Create arrays + std::vector arr_1(num_elements), arr_2(num_elements); + proj_data_1.copy_to(arr_1.begin()); + proj_data_2.copy_to(arr_2.begin()); + + // Compare values + if (compare_arrays(arr_1, arr_2) == Succeeded::yes) + std::cout << "Sinograms match!\n"; + else { + std::cout << "Sinograms don't match!\n"; + everything_ok = false; + } } void -TestDataProcessorProjectors:: -run_tests() -{ - try { - // Open sinogram - _input_sino_sptr = ProjData::read_from_file(_sinogram_filename); - - // Back project - std::cerr << "Tests for post-data-processor back projection\n"; - const std::vector > > bck_projected_ims = - this->post_data_processor_bck_proj(); - - // Forward project - std::cerr << "Tests for pre-data-processor forward projection\n"; - const std::vector > fwd_projected_sinos = - this->pre_data_processor_fwd_proj(*bck_projected_ims[0]); - - // Compare back projections - compare_images(everything_ok, *bck_projected_ims[0],*bck_projected_ims[1]); - - // Compare forward projections - compare_sinos(everything_ok, *fwd_projected_sinos[0],*fwd_projected_sinos[1]); - } - catch(const std::exception &error) { - std::cerr << "\nHere's the error:\n\t" << error.what() << "\n\n"; - everything_ok = false; - } - catch(...) { - everything_ok = false; - } +TestDataProcessorProjectors::run_tests() { + try { + // Open sinogram + _input_sino_sptr = ProjData::read_from_file(_sinogram_filename); + + // Back project + std::cerr << "Tests for post-data-processor back projection\n"; + const std::vector>> bck_projected_ims = this->post_data_processor_bck_proj(); + + // Forward project + std::cerr << "Tests for pre-data-processor forward projection\n"; + const std::vector> fwd_projected_sinos = this->pre_data_processor_fwd_proj(*bck_projected_ims[0]); + + // Compare back projections + compare_images(everything_ok, *bck_projected_ims[0], *bck_projected_ims[1]); + + // Compare forward projections + compare_sinos(everything_ok, *fwd_projected_sinos[0], *fwd_projected_sinos[1]); + } catch (const std::exception& error) { + std::cerr << "\nHere's the error:\n\t" << error.what() << "\n\n"; + everything_ok = false; + } catch (...) { + everything_ok = false; + } } -static -void -get_data_processor(shared_ptr > > &data_processor_sptr, const float fwhm) -{ - data_processor_sptr.reset(new SeparableCartesianMetzImageFilter); - std::string buffer; - std::stringstream parameterstream(buffer); - - parameterstream << "Separable Cartesian Metz Filter Parameters :=\n" - << "x-dir filter FWHM (in mm):= " << fwhm << "\n" - << "y-dir filter FWHM (in mm):= " << fwhm << "\n" - << "z-dir filter FWHM (in mm):= " << fwhm << "\n" - << "x-dir filter Metz power:= .0\n" - << "y-dir filter Metz power:= .0\n" - << "z-dir filter Metz power:=.0\n" - << "END Separable Cartesian Metz Filter Parameters :=\n"; - data_processor_sptr->parse(parameterstream); +static void +get_data_processor(shared_ptr>>& data_processor_sptr, const float fwhm) { + data_processor_sptr.reset(new SeparableCartesianMetzImageFilter); + std::string buffer; + std::stringstream parameterstream(buffer); + + parameterstream << "Separable Cartesian Metz Filter Parameters :=\n" + << "x-dir filter FWHM (in mm):= " << fwhm << "\n" + << "y-dir filter FWHM (in mm):= " << fwhm << "\n" + << "z-dir filter FWHM (in mm):= " << fwhm << "\n" + << "x-dir filter Metz power:= .0\n" + << "y-dir filter Metz power:= .0\n" + << "z-dir filter Metz power:=.0\n" + << "END Separable Cartesian Metz Filter Parameters :=\n"; + data_processor_sptr->parse(parameterstream); } -static -shared_ptr -get_back_projector_via_parser(const float fwhm = -1.f) -{ - std::string buffer; - std::stringstream parameterstream(buffer); - - parameterstream << "Back Projector parameters:=\n"; - if (fwhm > 0) - parameterstream - << "Post Data Processor := Separable Cartesian Metz\n" +static shared_ptr +get_back_projector_via_parser(const float fwhm = -1.f) { + std::string buffer; + std::stringstream parameterstream(buffer); + + parameterstream << "Back Projector parameters:=\n"; + if (fwhm > 0) + parameterstream << "Post Data Processor := Separable Cartesian Metz\n" << "Separable Cartesian Metz Filter Parameters :=\n" << " x-dir filter FWHM (in mm):= " << fwhm << "\n" << " y-dir filter FWHM (in mm):= " << fwhm << "\n" @@ -223,29 +206,25 @@ get_back_projector_via_parser(const float fwhm = -1.f) << " y-dir filter Metz power:= .0\n" << " z-dir filter Metz power:=.0\n" << "END Separable Cartesian Metz Filter Parameters :=\n"; - parameterstream << "End Back Projector Parameters:=\n"; + parameterstream << "End Back Projector Parameters:=\n"; - shared_ptr PM_sptr(new ProjMatrixByBinUsingRayTracing); + shared_ptr PM_sptr(new ProjMatrixByBinUsingRayTracing); - shared_ptr back_proj_sptr = - MAKE_SHARED(PM_sptr); - back_proj_sptr->parse(parameterstream); + shared_ptr back_proj_sptr = MAKE_SHARED(PM_sptr); + back_proj_sptr->parse(parameterstream); - return back_proj_sptr; + return back_proj_sptr; } -static -shared_ptr -get_forward_projector_via_parser(const float fwhm = -1.f) -{ - shared_ptr fwd_proj; - std::string buffer; - std::stringstream parameterstream(buffer); - - parameterstream << "Forward Projector parameters:=\n"; - if (fwhm > 0) - parameterstream - << "Pre Data Processor := Separable Cartesian Metz\n" +static shared_ptr +get_forward_projector_via_parser(const float fwhm = -1.f) { + shared_ptr fwd_proj; + std::string buffer; + std::stringstream parameterstream(buffer); + + parameterstream << "Forward Projector parameters:=\n"; + if (fwhm > 0) + parameterstream << "Pre Data Processor := Separable Cartesian Metz\n" << "Separable Cartesian Metz Filter Parameters :=\n" << " x-dir filter FWHM (in mm):= " << fwhm << "\n" << " y-dir filter FWHM (in mm):= " << fwhm << "\n" @@ -254,122 +233,114 @@ get_forward_projector_via_parser(const float fwhm = -1.f) << " y-dir filter Metz power:= .0\n" << " z-dir filter Metz power:=.0\n" << "END Separable Cartesian Metz Filter Parameters :=\n"; - parameterstream << "End Forward Projector Parameters:=\n"; + parameterstream << "End Forward Projector Parameters:=\n"; - shared_ptr PM_sptr(new ProjMatrixByBinUsingRayTracing); + shared_ptr PM_sptr(new ProjMatrixByBinUsingRayTracing); - shared_ptr fwd_proj_sptr = - MAKE_SHARED(PM_sptr); - fwd_proj_sptr->parse(parameterstream); + shared_ptr fwd_proj_sptr = MAKE_SHARED(PM_sptr); + fwd_proj_sptr->parse(parameterstream); - return fwd_proj_sptr; + return fwd_proj_sptr; } -const std::vector > -TestDataProcessorProjectors:: -pre_data_processor_fwd_proj(const DiscretisedDensity<3,float> &input_image) -{ - // Create two sinograms, images and forward projectors. - // One for pre-data processor forward projection, - // the other for data processor then forward projection - std::vector > sinos(2); - std::vector > > images(2); - std::vector > projectors(2); +const std::vector> +TestDataProcessorProjectors::pre_data_processor_fwd_proj(const DiscretisedDensity<3, float>& input_image) { + // Create two sinograms, images and forward projectors. + // One for pre-data processor forward projection, + // the other for data processor then forward projection + std::vector> sinos(2); + std::vector>> images(2); + std::vector> projectors(2); - // Loop over twice! - for (unsigned i=0; i(*_input_sino_sptr); - sinos[i]->fill(0.f); + // Copy the sinogram and fill with zeros + sinos[i] = MAKE_SHARED(*_input_sino_sptr); + sinos[i]->fill(0.f); - // Copy input image - images[i].reset(input_image.clone()); + // Copy input image + images[i].reset(input_image.clone()); - // The first time, use the data processor during the forward projection - projectors[i] = get_forward_projector_via_parser(i==0 ? _fwhm : -1); + // The first time, use the data processor during the forward projection + projectors[i] = get_forward_projector_via_parser(i == 0 ? _fwhm : -1); + projectors[i]->set_up(_input_sino_sptr->get_proj_data_info_sptr()->create_shared_clone(), images[i]); - projectors[i]->set_up(_input_sino_sptr->get_proj_data_info_sptr()->create_shared_clone(),images[i]); - - // The second time, use the data processor before the forward projection - if (i!=0) { - // Set up the data processor - shared_ptr > > data_processor_sptr; - get_data_processor(data_processor_sptr, _fwhm); - data_processor_sptr->apply(*images[i]); - } - - // Forward project - projectors[i]->set_input(*images[i]); - projectors[i]->forward_project(*sinos[i]); + // The second time, use the data processor before the forward projection + if (i != 0) { + // Set up the data processor + shared_ptr>> data_processor_sptr; + get_data_processor(data_processor_sptr, _fwhm); + data_processor_sptr->apply(*images[i]); } - return sinos; + // Forward project + projectors[i]->set_input(*images[i]); + projectors[i]->forward_project(*sinos[i]); + } + + return sinos; } -const std::vector > > -TestDataProcessorProjectors:: -post_data_processor_bck_proj() -{ - // Create two images and two back projectors. - // One for pre-data processor back projection, - // the other for data processor then back projection - std::vector > > images(2); - std::vector > projectors(2); - - // Loop over twice! - for (unsigned i=0; i >(*_input_sino_sptr->get_proj_data_info_sptr()); - images[i]->fill(0.f); - - // The first time, use the data processor during the back projection - projectors[i] = get_back_projector_via_parser(i==0 ? _fwhm : -1); - - projectors[i]->set_up(_input_sino_sptr->get_proj_data_info_sptr()->create_shared_clone(),images[i]); - - // Back project - projectors[i]->start_accumulating_in_new_target(); - projectors[i]->back_project(*_input_sino_sptr); - projectors[i]->get_output(*images[i]); - - // The second time, use the data processor after the back projection - if (i!=0) { - // Set up the data processor - shared_ptr > > data_processor_sptr; - get_data_processor(data_processor_sptr, _fwhm); - data_processor_sptr->apply(*images[i]); - } +const std::vector>> +TestDataProcessorProjectors::post_data_processor_bck_proj() { + // Create two images and two back projectors. + // One for pre-data processor back projection, + // the other for data processor then back projection + std::vector>> images(2); + std::vector> projectors(2); + + // Loop over twice! + for (unsigned i = 0; i < images.size(); ++i) { + + // Copy images and fill with zeros + images[i] = MAKE_SHARED>(*_input_sino_sptr->get_proj_data_info_sptr()); + images[i]->fill(0.f); + + // The first time, use the data processor during the back projection + projectors[i] = get_back_projector_via_parser(i == 0 ? _fwhm : -1); + + projectors[i]->set_up(_input_sino_sptr->get_proj_data_info_sptr()->create_shared_clone(), images[i]); + + // Back project + projectors[i]->start_accumulating_in_new_target(); + projectors[i]->back_project(*_input_sino_sptr); + projectors[i]->get_output(*images[i]); + + // The second time, use the data processor after the back projection + if (i != 0) { + // Set up the data processor + shared_ptr>> data_processor_sptr; + get_data_processor(data_processor_sptr, _fwhm); + data_processor_sptr->apply(*images[i]); } + } - return images; + return images; } END_NAMESPACE_STIR - USING_NAMESPACE_STIR +int +main(int argc, char** argv) { + if (argc < 2 || argc > 3) { + std::cerr << "\n\tUsage: " << argv[0] << " sinogram [fwhm]\n"; + return EXIT_FAILURE; + } -int main(int argc, char **argv) -{ - if (argc < 2 || argc > 3) { - std::cerr << "\n\tUsage: " << argv[0] << " sinogram [fwhm]\n"; - return EXIT_FAILURE; - } - - float fwhm = 5.f; - if (argc == 3) - fwhm = float(atof(argv[2])); + float fwhm = 5.f; + if (argc == 3) + fwhm = float(atof(argv[2])); - set_default_num_threads(); + set_default_num_threads(); - TestDataProcessorProjectors test(argv[1], fwhm); + TestDataProcessorProjectors test(argv[1], fwhm); - if (test.is_everything_ok()) - test.run_tests(); + if (test.is_everything_ok()) + test.run_tests(); - return test.main_return_value(); + return test.main_return_value(); } \ No newline at end of file diff --git a/src/recon_test/test_priors.cxx b/src/recon_test/test_priors.cxx index 0abce86eaf..065b63f543 100644 --- a/src/recon_test/test_priors.cxx +++ b/src/recon_test/test_priors.cxx @@ -19,13 +19,13 @@ \file \ingroup recon_test - + \brief Test program for stir::QuadraticPrior, RelativeDifferencePrior, and LogcoshPrior \par Usage
    -  test_priors [ density_filename ] 
    +  test_priors [ density_filename ]
       
    where the argument is optional. See the class documentation for more info. @@ -53,55 +53,48 @@ START_NAMESPACE_STIR - /*! \ingroup test \brief Test class for QuadraticPrior, RelativeDifferencePrior, and LogcoshPrior This test compares the result of GeneralisedPrior::compute_gradient() - with a numerical gradient computed by using the + with a numerical gradient computed by using the GeneralisedPrior::compute_value() function. */ -class GeneralisedPriorTests : public RunTests -{ +class GeneralisedPriorTests : public RunTests { public: //! Constructor that can take some input data to run the test with /*! This makes it possible to run the test with your own data. However, beware that - it is very easy to set up a very long computation. + it is very easy to set up a very long computation. \todo it would be better to parse an objective function. That would allow us to set all parameters from the command line. */ - GeneralisedPriorTests(char const * const density_filename = 0); - typedef DiscretisedDensity<3,float> target_type; + GeneralisedPriorTests(char const* const density_filename = 0); + typedef DiscretisedDensity<3, float> target_type; void construct_input_data(shared_ptr& density_sptr); void run_tests(); + protected: - char const * density_filename; - shared_ptr > objective_function_sptr; + char const* density_filename; + shared_ptr> objective_function_sptr; //! run the test /*! Note that this function is not specific to a particular prior */ - void run_tests_for_objective_function(const std::string& test_name, - GeneralisedPrior& objective_function, + void run_tests_for_objective_function(const std::string& test_name, GeneralisedPrior& objective_function, shared_ptr target_sptr); }; -GeneralisedPriorTests:: -GeneralisedPriorTests(char const * const density_filename) - : density_filename(density_filename) -{} +GeneralisedPriorTests::GeneralisedPriorTests(char const* const density_filename) : density_filename(density_filename) {} void -GeneralisedPriorTests:: -run_tests_for_objective_function(const std::string& test_name, - GeneralisedPrior& objective_function, - shared_ptr target_sptr) -{ +GeneralisedPriorTests::run_tests_for_objective_function(const std::string& test_name, + GeneralisedPrior& objective_function, + shared_ptr target_sptr) { std::cerr << "----- test " << test_name << '\n'; - if (!check(objective_function.set_up(target_sptr)==Succeeded::yes, "set-up of objective function")) + if (!check(objective_function.set_up(target_sptr) == Succeeded::yes, "set-up of objective function")) return; // setup images @@ -109,81 +102,73 @@ run_tests_for_objective_function(const std::string& test_name, shared_ptr gradient_sptr(target.get_empty_copy()); shared_ptr gradient_2_sptr(target.get_empty_copy()); - info("Computing gradient",3); + info("Computing gradient", 3); objective_function.compute_gradient(*gradient_sptr, target); - this->set_tolerance(std::max(fabs(double(gradient_sptr->find_min())), fabs(double(gradient_sptr->find_max()))) /1000); + this->set_tolerance(std::max(fabs(double(gradient_sptr->find_min())), fabs(double(gradient_sptr->find_max()))) / 1000); - info("Computing objective function at target",3); + info("Computing objective function at target", 3); const double value_at_target = objective_function.compute_value(target); - target_type::full_iterator target_iter=target.begin_all(); - target_type::full_iterator gradient_iter=gradient_sptr->begin_all(); - target_type::full_iterator gradient_2_iter=gradient_2_sptr->begin_all(); + target_type::full_iterator target_iter = target.begin_all(); + target_type::full_iterator gradient_iter = gradient_sptr->begin_all(); + target_type::full_iterator gradient_2_iter = gradient_2_sptr->begin_all(); // setup perturbation response const float eps = 1e-3F; bool testOK = true; - info("Computing gradient of objective function by numerical differences (this will take a while)",3); - while(target_iter!=target.end_all())// && testOK) - { - const float org_image_value = *target_iter; - *target_iter += eps; // perturb current voxel - const double value_at_inc = objective_function.compute_value(target); - *target_iter = org_image_value; // restore - const float ngradient_at_iter = static_cast((value_at_inc - value_at_target)/eps); - *gradient_2_iter = ngradient_at_iter; - testOK = testOK && this->check_if_equal(ngradient_at_iter, *gradient_iter, "gradient"); - //for (int i=0; i<5 && target_iter!=target.end_all(); ++i) - { - ++gradient_2_iter; ++target_iter; ++ gradient_iter; - } - } - if (!testOK) + info("Computing gradient of objective function by numerical differences (this will take a while)", 3); + while (target_iter != target.end_all()) // && testOK) + { + const float org_image_value = *target_iter; + *target_iter += eps; // perturb current voxel + const double value_at_inc = objective_function.compute_value(target); + *target_iter = org_image_value; // restore + const float ngradient_at_iter = static_cast((value_at_inc - value_at_target) / eps); + *gradient_2_iter = ngradient_at_iter; + testOK = testOK && this->check_if_equal(ngradient_at_iter, *gradient_iter, "gradient"); + // for (int i=0; i<5 && target_iter!=target.end_all(); ++i) { - info("Writing diagnostic files gradient" + test_name + ".hv, numerical_gradient" + test_name + ".hv"); - write_to_file("gradient" + test_name + ".hv", *gradient_sptr); - write_to_file("numerical_gradient" + test_name + ".hv", *gradient_2_sptr); + ++gradient_2_iter; + ++target_iter; + ++gradient_iter; } - + } + if (!testOK) { + info("Writing diagnostic files gradient" + test_name + ".hv, numerical_gradient" + test_name + ".hv"); + write_to_file("gradient" + test_name + ".hv", *gradient_sptr); + write_to_file("numerical_gradient" + test_name + ".hv", *gradient_2_sptr); + } } void -GeneralisedPriorTests:: -construct_input_data(shared_ptr& density_sptr) -{ - if (this->density_filename == 0) - { - // construct a small image with random voxel values between 0 and 1 - - shared_ptr exam_info_sptr(new ExamInfo); - exam_info_sptr->imaging_modality = ImagingModality::PT; - CartesianCoordinate3D origin (0,0,0); - CartesianCoordinate3D voxel_size(2.F,3.F,3.F); - - density_sptr.reset(new VoxelsOnCartesianGrid(exam_info_sptr, - IndexRange<3>(make_coordinate(10,9,8)), - origin, voxel_size)); - // fill with random numbers between 0 and 1 - typedef boost::mt19937 base_generator_type; - // initialize by reproducible seed - static base_generator_type generator(boost::uint32_t(42)); - static boost::uniform_01 random01(generator); - for (target_type::full_iterator iter=density_sptr->begin_all(); iter!=density_sptr->end_all(); ++iter) - *iter = static_cast(random01()); - - } - else - { - // load image from file - shared_ptr aptr(read_from_file(this->density_filename)); - density_sptr = aptr; - } - return; +GeneralisedPriorTests::construct_input_data(shared_ptr& density_sptr) { + if (this->density_filename == 0) { + // construct a small image with random voxel values between 0 and 1 + + shared_ptr exam_info_sptr(new ExamInfo); + exam_info_sptr->imaging_modality = ImagingModality::PT; + CartesianCoordinate3D origin(0, 0, 0); + CartesianCoordinate3D voxel_size(2.F, 3.F, 3.F); + + density_sptr.reset( + new VoxelsOnCartesianGrid(exam_info_sptr, IndexRange<3>(make_coordinate(10, 9, 8)), origin, voxel_size)); + // fill with random numbers between 0 and 1 + typedef boost::mt19937 base_generator_type; + // initialize by reproducible seed + static base_generator_type generator(boost::uint32_t(42)); + static boost::uniform_01 random01(generator); + for (target_type::full_iterator iter = density_sptr->begin_all(); iter != density_sptr->end_all(); ++iter) + *iter = static_cast(random01()); + + } else { + // load image from file + shared_ptr aptr(read_from_file(this->density_filename)); + density_sptr = aptr; + } + return; } void -GeneralisedPriorTests:: -run_tests() -{ +GeneralisedPriorTests::run_tests() { shared_ptr density_sptr; construct_input_data(density_sptr); @@ -199,32 +184,31 @@ run_tests() this->run_tests_for_objective_function("RDP_no_kappa", objective_function, density_sptr); } // Disabled PLS due to known issue -// std::cerr << "Tests for PLSPrior\n"; -// { -// PLSPrior objective_function(false, 1.F); -// shared_ptr > anatomical_image_sptr(density_sptr->get_empty_copy()); -// anatomical_image_sptr->fill(1.F); -// objective_function.set_anatomical_image_sptr(anatomical_image_sptr); -// this->run_tests_for_objective_function("PLS_no_kappa_flat_anatomical", objective_function, density_sptr); -// } + // std::cerr << "Tests for PLSPrior\n"; + // { + // PLSPrior objective_function(false, 1.F); + // shared_ptr > anatomical_image_sptr(density_sptr->get_empty_copy()); + // anatomical_image_sptr->fill(1.F); + // objective_function.set_anatomical_image_sptr(anatomical_image_sptr); + // this->run_tests_for_objective_function("PLS_no_kappa_flat_anatomical", objective_function, density_sptr); + // } std::cerr << "Tests for Logcosh Prior\n"; { // scalar is off LogcoshPrior objective_function(false, 1.F, 1.F); this->run_tests_for_objective_function("Logcosh_no_kappa", objective_function, density_sptr); } -} +} END_NAMESPACE_STIR - USING_NAMESPACE_STIR -int main(int argc, char **argv) -{ +int +main(int argc, char** argv) { set_default_num_threads(); - GeneralisedPriorTests tests(argc>1? argv[1] : 0); + GeneralisedPriorTests tests(argc > 1 ? argv[1] : 0); tests.run_tests(); return tests.main_return_value(); } diff --git a/src/scatter_buildblock/CreateTailMaskFromACFs.cxx b/src/scatter_buildblock/CreateTailMaskFromACFs.cxx index 4249f51f99..3abae056b9 100644 --- a/src/scatter_buildblock/CreateTailMaskFromACFs.cxx +++ b/src/scatter_buildblock/CreateTailMaskFromACFs.cxx @@ -23,179 +23,122 @@ START_NAMESPACE_STIR void -CreateTailMaskFromACFs:: -set_input_projdata_sptr(shared_ptr & arg) -{ - this->ACF_sptr = arg; +CreateTailMaskFromACFs::set_input_projdata_sptr(shared_ptr& arg) { + this->ACF_sptr = arg; } void -CreateTailMaskFromACFs:: -set_input_projdata(std::string& arg) -{ - this->ACF_sptr = - ProjData::read_from_file(arg); +CreateTailMaskFromACFs::set_input_projdata(std::string& arg) { + this->ACF_sptr = ProjData::read_from_file(arg); } void -CreateTailMaskFromACFs:: -set_output_projdata_sptr(shared_ptr& arg) -{ - this->mask_proj_data = arg; +CreateTailMaskFromACFs::set_output_projdata_sptr(shared_ptr& arg) { + this->mask_proj_data = arg; } void -CreateTailMaskFromACFs:: -set_output_projdata(std::string& arg) -{ - this->mask_proj_data.reset(new ProjDataInterfile(ACF_sptr->get_exam_info_sptr(), - ACF_sptr->get_proj_data_info_sptr()->create_shared_clone(), - arg)); +CreateTailMaskFromACFs::set_output_projdata(std::string& arg) { + this->mask_proj_data.reset( + new ProjDataInterfile(ACF_sptr->get_exam_info_sptr(), ACF_sptr->get_proj_data_info_sptr()->create_shared_clone(), arg)); } - shared_ptr -CreateTailMaskFromACFs:: -get_output_projdata_sptr() -{ - return this->mask_proj_data; +CreateTailMaskFromACFs::get_output_projdata_sptr() { + return this->mask_proj_data; } void -CreateTailMaskFromACFs:: -set_defaults() -{ - ACF_threshold = 1.1F; - safety_margin = 4; +CreateTailMaskFromACFs::set_defaults() { + ACF_threshold = 1.1F; + safety_margin = 4; } -CreateTailMaskFromACFs:: -CreateTailMaskFromACFs() -{ - this->set_defaults(); -} +CreateTailMaskFromACFs::CreateTailMaskFromACFs() { this->set_defaults(); } void -CreateTailMaskFromACFs:: -initialise_keymap() -{ - this->parser.add_start_key("CreateTailMaskFromACFs"); - this->parser.add_stop_key("END CreateTailMaskFromACFs"); - this->parser.add_key("ACF-filename", - &_input_filename); - this->parser.add_key("output-filename", - &_output_filename); - this->parser.add_key("ACF-threshold", - &ACF_threshold); - this->parser.add_key("safety-margin", - &safety_margin); +CreateTailMaskFromACFs::initialise_keymap() { + this->parser.add_start_key("CreateTailMaskFromACFs"); + this->parser.add_stop_key("END CreateTailMaskFromACFs"); + this->parser.add_key("ACF-filename", &_input_filename); + this->parser.add_key("output-filename", &_output_filename); + this->parser.add_key("ACF-threshold", &ACF_threshold); + this->parser.add_key("safety-margin", &safety_margin); } bool -CreateTailMaskFromACFs:: -post_processing() -{ - if (ACF_threshold<=1) - error("ACF-threshold should be larger than 1"); +CreateTailMaskFromACFs::post_processing() { + if (ACF_threshold <= 1) + error("ACF-threshold should be larger than 1"); - if(this->_input_filename.size() > 0) - this->set_input_projdata(this->_input_filename); + if (this->_input_filename.size() > 0) + this->set_input_projdata(this->_input_filename); + if (this->_output_filename.size() > 0) + this->set_output_projdata(this->_output_filename); - if(this->_output_filename.size() > 0) - this->set_output_projdata(this->_output_filename); - - return false; + return false; } Succeeded -CreateTailMaskFromACFs:: -process_data() -{ - if (is_null_ptr(this->ACF_sptr)) - error("Check the attenuation_correct_factors file"); - - if (is_null_ptr(this->mask_proj_data)) - error("Please set output file"); - - Bin bin; - { - for (bin.segment_num()=this->mask_proj_data->get_min_segment_num(); - bin.segment_num()<=this->mask_proj_data->get_max_segment_num(); - ++bin.segment_num()) - for (bin.axial_pos_num()= - this->mask_proj_data->get_min_axial_pos_num(bin.segment_num()); - bin.axial_pos_num()<=this->mask_proj_data->get_max_axial_pos_num(bin.segment_num()); - ++bin.axial_pos_num()) - { - const Sinogram att_sinogram - (this->ACF_sptr->get_sinogram(bin.axial_pos_num(),bin.segment_num())); - Sinogram mask_sinogram - (this->mask_proj_data->get_empty_sinogram(bin.axial_pos_num(),bin.segment_num())); - - std::size_t count=0; - for (bin.view_num()=this->mask_proj_data->get_min_view_num(); - bin.view_num()<=this->mask_proj_data->get_max_view_num(); - ++bin.view_num()) - { +CreateTailMaskFromACFs::process_data() { + if (is_null_ptr(this->ACF_sptr)) + error("Check the attenuation_correct_factors file"); + + if (is_null_ptr(this->mask_proj_data)) + error("Please set output file"); + + Bin bin; + { + for (bin.segment_num() = this->mask_proj_data->get_min_segment_num(); + bin.segment_num() <= this->mask_proj_data->get_max_segment_num(); ++bin.segment_num()) + for (bin.axial_pos_num() = this->mask_proj_data->get_min_axial_pos_num(bin.segment_num()); + bin.axial_pos_num() <= this->mask_proj_data->get_max_axial_pos_num(bin.segment_num()); ++bin.axial_pos_num()) { + const Sinogram att_sinogram(this->ACF_sptr->get_sinogram(bin.axial_pos_num(), bin.segment_num())); + Sinogram mask_sinogram(this->mask_proj_data->get_empty_sinogram(bin.axial_pos_num(), bin.segment_num())); + + std::size_t count = 0; + for (bin.view_num() = this->mask_proj_data->get_min_view_num(); + bin.view_num() <= this->mask_proj_data->get_max_view_num(); ++bin.view_num()) { #ifdef SCFOLD - for (bin.tangential_pos_num()= - mask_proj_data.get_min_tangential_pos_num(); - bin.tangential_pos_num()<= - mask_proj_data.get_max_tangential_pos_num(); - ++bin.tangential_pos_num()) - if (att_sinogram[bin.view_num()][bin.tangential_pos_num()]= std::fabs(scatter_proj_data.get_proj_data_info_sptr()->get_s(bin)))) - { - ++count; - mask_sinogram[bin.view_num()][bin.tangential_pos_num()]=1; - } - else - mask_sinogram[bin.view_num()][bin.tangential_pos_num()]=0; + for (bin.tangential_pos_num() = mask_proj_data.get_min_tangential_pos_num(); + bin.tangential_pos_num() <= mask_proj_data.get_max_tangential_pos_num(); ++bin.tangential_pos_num()) + if (att_sinogram[bin.view_num()][bin.tangential_pos_num()] < ACF_threshold && + (mask_radius_in_mm < 0 || + mask_radius_in_mm >= std::fabs(scatter_proj_data.get_proj_data_info_sptr()->get_s(bin)))) { + ++count; + mask_sinogram[bin.view_num()][bin.tangential_pos_num()] = 1; + } else + mask_sinogram[bin.view_num()][bin.tangential_pos_num()] = 0; #else - const Array<1,float>& att_line=att_sinogram[bin.view_num()]; - - using boost::lambda::_1; - - // find left and right mask sizes - // sorry: a load of ugly casting to make sure we allow all datatypes - std::size_t mask_left_size = - static_cast( - std::max(0, - static_cast - (std::find_if(att_line.begin(), att_line.end(), - _1>=ACF_threshold) - - att_line.begin()) - - safety_margin) - ); - std::size_t mask_right_size = - static_cast( - std::max(0, - static_cast - (std::find_if(att_line.rbegin(), att_line.rend() - mask_left_size, - _1>=ACF_threshold) - - att_line.rbegin()) - - safety_margin) - ); -#if 0 + const Array<1, float>& att_line = att_sinogram[bin.view_num()]; + + using boost::lambda::_1; + + // find left and right mask sizes + // sorry: a load of ugly casting to make sure we allow all datatypes + std::size_t mask_left_size = static_cast(std::max( + 0, static_cast(std::find_if(att_line.begin(), att_line.end(), _1 >= ACF_threshold) - att_line.begin()) - + safety_margin)); + std::size_t mask_right_size = static_cast(std::max( + 0, static_cast(std::find_if(att_line.rbegin(), att_line.rend() - mask_left_size, _1 >= ACF_threshold) - + att_line.rbegin()) - + safety_margin)); +# if 0 std::cout << "mask sizes " << mask_left_size << ", " << mask_right_size << '\n'; +# endif + std::fill(mask_sinogram[bin.view_num()].begin(), mask_sinogram[bin.view_num()].begin() + mask_left_size, 1.F); + std::fill(mask_sinogram[bin.view_num()].rbegin(), mask_sinogram[bin.view_num()].rbegin() + mask_right_size, 1.F); + count += mask_left_size + mask_right_size; #endif - std::fill(mask_sinogram[bin.view_num()].begin(), - mask_sinogram[bin.view_num()].begin() + mask_left_size, - 1.F); - std::fill(mask_sinogram[bin.view_num()].rbegin(), - mask_sinogram[bin.view_num()].rbegin() + mask_right_size, - 1.F); - count += mask_left_size + mask_right_size; -#endif - } - std::cout << count << " bins in mask for sinogram at segment " - << bin.segment_num() << ", axial_pos " << bin.axial_pos_num() << "\n"; - if (this->mask_proj_data->set_sinogram(mask_sinogram) != Succeeded::yes) - return Succeeded::no; - } - } - return Succeeded::yes; + } + std::cout << count << " bins in mask for sinogram at segment " << bin.segment_num() << ", axial_pos " + << bin.axial_pos_num() << "\n"; + if (this->mask_proj_data->set_sinogram(mask_sinogram) != Succeeded::yes) + return Succeeded::no; + } + } + return Succeeded::yes; } END_NAMESPACE_STIR diff --git a/src/scatter_buildblock/ScatterEstimation.cxx b/src/scatter_buildblock/ScatterEstimation.cxx index 43ac79c35e..5599a53abb 100644 --- a/src/scatter_buildblock/ScatterEstimation.cxx +++ b/src/scatter_buildblock/ScatterEstimation.cxx @@ -57,273 +57,208 @@ START_NAMESPACE_STIR void -ScatterEstimation:: -set_defaults() -{ - this->_already_setup = false; - this->scatter_simulation_sptr.reset(new SingleScatterSimulation); - this->recompute_atten_projdata = true; - this->recompute_mask_image = true; - { - // image masking - this->masking_parameters.min_threshold = .003F; - shared_ptr > filter_sptr(new SeparableGaussianImageFilter); - filter_sptr->set_fwhms(make_coordinate(15.F, 20.F, 20.F)); - this->masking_parameters.filter_sptr.reset(new PostFiltering >); - this->masking_parameters.filter_sptr->set_filter_sptr(filter_sptr); - } - this->recompute_mask_projdata = true; - this->run_in_2d_projdata = true; - this->do_average_at_2 = true; - this->export_scatter_estimates_of_each_iteration = false; - this->run_debug_mode = false; - this->override_scanner_template = true; - this->override_density_image = true; - this->downsample_scanner_bool = true; - this->remove_interleaving = true; - this->atten_image_filename = ""; - this->atten_coeff_filename = ""; - this->norm_3d_sptr.reset(); - this->multiplicative_binnorm_sptr.reset(); - this->output_scatter_estimate_prefix = ""; - this->output_additive_estimate_prefix = ""; - this->num_scatter_iterations = 5; - this->min_scale_value = 0.4f; - this->max_scale_value = 100.f; - this->half_filter_width = 3; +ScatterEstimation::set_defaults() { + this->_already_setup = false; + this->scatter_simulation_sptr.reset(new SingleScatterSimulation); + this->recompute_atten_projdata = true; + this->recompute_mask_image = true; + { + // image masking + this->masking_parameters.min_threshold = .003F; + shared_ptr> filter_sptr(new SeparableGaussianImageFilter); + filter_sptr->set_fwhms(make_coordinate(15.F, 20.F, 20.F)); + this->masking_parameters.filter_sptr.reset(new PostFiltering>); + this->masking_parameters.filter_sptr->set_filter_sptr(filter_sptr); + } + this->recompute_mask_projdata = true; + this->run_in_2d_projdata = true; + this->do_average_at_2 = true; + this->export_scatter_estimates_of_each_iteration = false; + this->run_debug_mode = false; + this->override_scanner_template = true; + this->override_density_image = true; + this->downsample_scanner_bool = true; + this->remove_interleaving = true; + this->atten_image_filename = ""; + this->atten_coeff_filename = ""; + this->norm_3d_sptr.reset(); + this->multiplicative_binnorm_sptr.reset(); + this->output_scatter_estimate_prefix = ""; + this->output_additive_estimate_prefix = ""; + this->num_scatter_iterations = 5; + this->min_scale_value = 0.4f; + this->max_scale_value = 100.f; + this->half_filter_width = 3; } void -ScatterEstimation:: -initialise_keymap() -{ - this->parser.add_start_key("Scatter Estimation Parameters"); - this->parser.add_stop_key("end Scatter Estimation Parameters"); - - this->parser.add_key("run in debug mode", - &this->run_debug_mode); - this->parser.add_key("input file", - &this->input_projdata_filename); - this->parser.add_key("attenuation image filename", - &this->atten_image_filename); - - // MASK parameters - this->parser.add_key("recompute mask image", - &this->recompute_mask_image); - this->parser.add_key("mask image filename", - &this->mask_image_filename); - this->parser.add_key("mask attenuation image filter filename", - &this->masking_parameters.filter_filename); - this->parser.add_key("mask attenuation image min threshold", - &this->masking_parameters.min_threshold); - this->parser.add_key("recompute mask projdata", - &this->recompute_mask_projdata); - this->parser.add_key("mask projdata filename", - &this->mask_projdata_filename); - this->parser.add_key("tail fitting parameter filename", - &this->tail_mask_par_filename); - // END MASK - this->parser.add_key("background projdata filename", - &this->back_projdata_filename); - this->parser.add_parsing_key("Normalisation type", - &this->norm_3d_sptr); - this->parser.add_key("attenuation correction factors filename", - &this->atten_coeff_filename); - this->parser.add_parsing_key("Bin Normalisation type", - &this->multiplicative_binnorm_sptr); - - // RECONSTRUCTION RELATED - this->parser.add_key("reconstruction parameter filename", - &this->recon_template_par_filename); - this->parser.add_parsing_key("reconstruction type", - &this->reconstruction_template_sptr); - // END RECONSTRUCTION RELATED - - this->parser.add_key("number of scatter iterations", - &this->num_scatter_iterations); - //Scatter simulation - this->parser.add_parsing_key("Scatter Simulation type", - &this->scatter_simulation_sptr); - this->parser.add_key("scatter simulation parameter filename", - &this->scatter_sim_par_filename); - this->parser.add_key("use scanner downsampling in scatter simulation", - &this->downsample_scanner_bool); - - this->parser.add_key("override attenuation image", - &this->override_density_image); - this->parser.add_key("override scanner template", - &this->override_scanner_template); - - // END Scatter simulation - - this->parser.add_key("export scatter estimates of each iteration", - &this->export_scatter_estimates_of_each_iteration); - this->parser.add_key("output scatter estimate name prefix", - &this->output_scatter_estimate_prefix); - this->parser.add_key("output additive estimate name prefix", - &this->output_additive_estimate_prefix); - this->parser.add_key("do average at 2", - &this->do_average_at_2); - this->parser.add_key("maximum scatter scaling factor", - &this->max_scale_value); - this->parser.add_key("minimum scatter scaling factor", - &this->min_scale_value); - this->parser.add_key("upsampling half filter width", - &this->half_filter_width); - this->parser.add_key("remove interleaving before upsampling", - &this->remove_interleaving); - this->parser.add_key("run in 2d projdata", - &this->run_in_2d_projdata); +ScatterEstimation::initialise_keymap() { + this->parser.add_start_key("Scatter Estimation Parameters"); + this->parser.add_stop_key("end Scatter Estimation Parameters"); + + this->parser.add_key("run in debug mode", &this->run_debug_mode); + this->parser.add_key("input file", &this->input_projdata_filename); + this->parser.add_key("attenuation image filename", &this->atten_image_filename); + + // MASK parameters + this->parser.add_key("recompute mask image", &this->recompute_mask_image); + this->parser.add_key("mask image filename", &this->mask_image_filename); + this->parser.add_key("mask attenuation image filter filename", &this->masking_parameters.filter_filename); + this->parser.add_key("mask attenuation image min threshold", &this->masking_parameters.min_threshold); + this->parser.add_key("recompute mask projdata", &this->recompute_mask_projdata); + this->parser.add_key("mask projdata filename", &this->mask_projdata_filename); + this->parser.add_key("tail fitting parameter filename", &this->tail_mask_par_filename); + // END MASK + this->parser.add_key("background projdata filename", &this->back_projdata_filename); + this->parser.add_parsing_key("Normalisation type", &this->norm_3d_sptr); + this->parser.add_key("attenuation correction factors filename", &this->atten_coeff_filename); + this->parser.add_parsing_key("Bin Normalisation type", &this->multiplicative_binnorm_sptr); + + // RECONSTRUCTION RELATED + this->parser.add_key("reconstruction parameter filename", &this->recon_template_par_filename); + this->parser.add_parsing_key("reconstruction type", &this->reconstruction_template_sptr); + // END RECONSTRUCTION RELATED + + this->parser.add_key("number of scatter iterations", &this->num_scatter_iterations); + // Scatter simulation + this->parser.add_parsing_key("Scatter Simulation type", &this->scatter_simulation_sptr); + this->parser.add_key("scatter simulation parameter filename", &this->scatter_sim_par_filename); + this->parser.add_key("use scanner downsampling in scatter simulation", &this->downsample_scanner_bool); + + this->parser.add_key("override attenuation image", &this->override_density_image); + this->parser.add_key("override scanner template", &this->override_scanner_template); + + // END Scatter simulation + + this->parser.add_key("export scatter estimates of each iteration", &this->export_scatter_estimates_of_each_iteration); + this->parser.add_key("output scatter estimate name prefix", &this->output_scatter_estimate_prefix); + this->parser.add_key("output additive estimate name prefix", &this->output_additive_estimate_prefix); + this->parser.add_key("do average at 2", &this->do_average_at_2); + this->parser.add_key("maximum scatter scaling factor", &this->max_scale_value); + this->parser.add_key("minimum scatter scaling factor", &this->min_scale_value); + this->parser.add_key("upsampling half filter width", &this->half_filter_width); + this->parser.add_key("remove interleaving before upsampling", &this->remove_interleaving); + this->parser.add_key("run in 2d projdata", &this->run_in_2d_projdata); } -ScatterEstimation:: -ScatterEstimation() -{ - this->set_defaults(); -} +ScatterEstimation::ScatterEstimation() { this->set_defaults(); } -ScatterEstimation:: -ScatterEstimation(const std::string& parameter_filename) -{ +ScatterEstimation::ScatterEstimation(const std::string& parameter_filename) { this->set_defaults(); - if (!this->parse(parameter_filename.c_str())) - { - error("ScatterEstimation: Error parsing input file %s. Aborting.", parameter_filename.c_str()); - } + if (!this->parse(parameter_filename.c_str())) { + error("ScatterEstimation: Error parsing input file %s. Aborting.", parameter_filename.c_str()); + } } bool -ScatterEstimation:: -post_processing() -{ - if (!this->input_projdata_filename.empty()) - { - info("ScatterEstimation: Loading input projdata...", 3); - this->input_projdata_sptr = - ProjData::read_from_file(this->input_projdata_filename); - } - // If the reconstruction_template_sptr is null then, we need to parse it from another - // file. I prefer this implementation since makes smaller modular files. - if (!this->recon_template_par_filename.empty()) - { - KeyParser local_parser; - local_parser.add_start_key("Reconstruction Parameters"); - local_parser.add_stop_key("End Reconstruction Parameters"); - local_parser.add_parsing_key("reconstruction type", &this->reconstruction_template_sptr); - if (!local_parser.parse(this->recon_template_par_filename.c_str())) - { - warning(boost::format("ScatterEstimation: Error parsing reconstruction parameters file %1%. Aborting.") - %this->recon_template_par_filename); - return true; - } +ScatterEstimation::post_processing() { + if (!this->input_projdata_filename.empty()) { + info("ScatterEstimation: Loading input projdata...", 3); + this->input_projdata_sptr = ProjData::read_from_file(this->input_projdata_filename); + } + // If the reconstruction_template_sptr is null then, we need to parse it from another + // file. I prefer this implementation since makes smaller modular files. + if (!this->recon_template_par_filename.empty()) { + KeyParser local_parser; + local_parser.add_start_key("Reconstruction Parameters"); + local_parser.add_stop_key("End Reconstruction Parameters"); + local_parser.add_parsing_key("reconstruction type", &this->reconstruction_template_sptr); + if (!local_parser.parse(this->recon_template_par_filename.c_str())) { + warning(boost::format("ScatterEstimation: Error parsing reconstruction parameters file %1%. Aborting.") % + this->recon_template_par_filename); + return true; } + } + + if (!this->atten_image_filename.empty()) { + info("ScatterEstimation: Loading attenuation image...", 3); + this->atten_image_sptr = read_from_file>(this->atten_image_filename); + } + if (!this->atten_coeff_filename.empty()) { + info("ScatterEstimation: Loading attenuation coefficients projdata...", 3); + shared_ptr atten_coef_sptr = ProjData::read_from_file(this->atten_coeff_filename); + this->set_attenuation_correction_proj_data_sptr(atten_coef_sptr); + } + if (!is_null_ptr(multiplicative_binnorm_sptr)) { + warning("ScatterEstimation: looks like you set a combined norm via the 'bin normalisation type' keyword\n" + "This is deprecated and will be removed in a future version (5.0?).\n" + "Use 'normalisation type' (for the norm factors) and 'attenuation correction factors filename' instead."); + } + + if (!this->back_projdata_filename.empty()) { + info("ScatterEstimation: Loading background projdata...", 3); + this->back_projdata_sptr = ProjData::read_from_file(this->back_projdata_filename); + } + + // if(!this->recompute_initial_activity_image ) // This image can be used as a template + // { + // info("ScatterEstimation: Loading initial activity image ..."); + // if(this->initial_activity_image_filename.size() > 0 ) + // this->current_activity_image_lowres_sptr = + // read_from_file >(this->initial_activity_image_filename); + // else + // { + // warning("ScatterEstimation: Recompute initial activity image was set to false and" + // "no filename was set. Aborting."); + // return true; + // } + // } + + if (!this->masking_parameters.filter_filename.empty()) { + this->masking_parameters.filter_sptr.reset(new PostFiltering>); + + if (!masking_parameters.filter_sptr->parse(this->masking_parameters.filter_filename.c_str())) { + warning(boost::format("ScatterEstimation: Error parsing post filter parameters file %1%. Aborting.") % + this->masking_parameters.filter_filename); + return true; + } + } - if (!this->atten_image_filename.empty()) - { - info("ScatterEstimation: Loading attenuation image...", 3); - this->atten_image_sptr = - read_from_file >(this->atten_image_filename); - } - if (!this->atten_coeff_filename.empty()) - { - info("ScatterEstimation: Loading attenuation coefficients projdata...", 3); - shared_ptr atten_coef_sptr = - ProjData::read_from_file(this->atten_coeff_filename); - this->set_attenuation_correction_proj_data_sptr(atten_coef_sptr); - } - if(!is_null_ptr(multiplicative_binnorm_sptr)) - { - warning("ScatterEstimation: looks like you set a combined norm via the 'bin normalisation type' keyword\n" - "This is deprecated and will be removed in a future version (5.0?).\n" - "Use 'normalisation type' (for the norm factors) and 'attenuation correction factors filename' instead."); - } - - if (!this->back_projdata_filename.empty()) - { - info("ScatterEstimation: Loading background projdata...", 3); - this->back_projdata_sptr = - ProjData::read_from_file(this->back_projdata_filename); - } - - // if(!this->recompute_initial_activity_image ) // This image can be used as a template - // { - // info("ScatterEstimation: Loading initial activity image ..."); - // if(this->initial_activity_image_filename.size() > 0 ) - // this->current_activity_image_lowres_sptr = - // read_from_file >(this->initial_activity_image_filename); - // else - // { - // warning("ScatterEstimation: Recompute initial activity image was set to false and" - // "no filename was set. Aborting."); - // return true; - // } - // } - - if(!this->masking_parameters.filter_filename.empty()) + if (!this->scatter_sim_par_filename.empty()) { + info("ScatterEstimation: Initialising Scatter Simulation ...", 3); + // Parse locally { - this->masking_parameters.filter_sptr.reset(new PostFiltering >); - - if(!masking_parameters.filter_sptr->parse(this->masking_parameters.filter_filename.c_str())) - { - warning(boost::format("ScatterEstimation: Error parsing post filter parameters file %1%. Aborting.") - %this->masking_parameters.filter_filename); - return true; - } + KeyParser local_parser; + local_parser.add_start_key("Scatter Simulation Parameters"); + local_parser.add_stop_key("End Scatter Simulation Parameters"); + local_parser.add_parsing_key("Scatter Simulation type", &this->scatter_simulation_sptr); + if (!local_parser.parse(this->scatter_sim_par_filename.c_str())) + error("ScatterEstimation: Error parsing scatter simulation parameters."); } - - if (!this->scatter_sim_par_filename.empty()) - { - info ("ScatterEstimation: Initialising Scatter Simulation ...", 3); - // Parse locally - { - KeyParser local_parser; - local_parser.add_start_key("Scatter Simulation Parameters"); - local_parser.add_stop_key("End Scatter Simulation Parameters"); - local_parser.add_parsing_key("Scatter Simulation type", &this->scatter_simulation_sptr); - if (!local_parser.parse(this->scatter_sim_par_filename.c_str())) - error("ScatterEstimation: Error parsing scatter simulation parameters."); - } - } - - // There is no output in this case - if (this->output_scatter_estimate_prefix.empty() && this->output_additive_estimate_prefix.empty()) - { - // This is ok when running from Python or so, but not when running from the command line. - // As we don't know, we just write a warning - warning("ScatterEstimation: no filename prefix set for either the scatter estimate or the additive.\n" - "This is probably not what you want."); - } - - if(!this->recompute_mask_projdata) - { - if (!this->mask_projdata_filename.empty()) - this->mask_projdata_sptr = - ProjData::read_from_file(this->mask_projdata_filename); - } - else - { - if (!this->recompute_mask_image && !this->mask_image_filename.empty()) - this->mask_image_sptr = - read_from_file >(this->mask_image_filename); - } - - return false; + } + + // There is no output in this case + if (this->output_scatter_estimate_prefix.empty() && this->output_additive_estimate_prefix.empty()) { + // This is ok when running from Python or so, but not when running from the command line. + // As we don't know, we just write a warning + warning("ScatterEstimation: no filename prefix set for either the scatter estimate or the additive.\n" + "This is probably not what you want."); + } + + if (!this->recompute_mask_projdata) { + if (!this->mask_projdata_filename.empty()) + this->mask_projdata_sptr = ProjData::read_from_file(this->mask_projdata_filename); + } else { + if (!this->recompute_mask_image && !this->mask_image_filename.empty()) + this->mask_image_sptr = read_from_file>(this->mask_image_filename); + } + + return false; } -shared_ptr -ScatterEstimation::get_output() const -{ - return scatter_estimate_sptr; +shared_ptr +ScatterEstimation::get_output() const { + return scatter_estimate_sptr; } #if STIR_VERSION < 050000 -void ScatterEstimation::set_input_data(const shared_ptr& data) -{ +void +ScatterEstimation::set_input_data(const shared_ptr& data) { this->set_input_proj_data_sptr(data); } #else -void ScatterEstimation::set_input_data(const shared_ptr& data) -{ +void +ScatterEstimation::set_input_data(const shared_ptr& data) { // C++-11 auto sptr = std::dynamic_pointer_cast(data); if (!sptr) @@ -333,1077 +268,881 @@ void ScatterEstimation::set_input_data(const shared_ptr& data) } #endif -shared_ptr ScatterEstimation::get_input_data() const -{ +shared_ptr +ScatterEstimation::get_input_data() const { return this->input_projdata_sptr; } -shared_ptr > -ScatterEstimation::get_estimated_activity_image_sptr() const -{ +shared_ptr> +ScatterEstimation::get_estimated_activity_image_sptr() const { return this->current_activity_image_sptr; } -void ScatterEstimation::set_output_scatter_estimate_prefix(const std::string& arg) -{ +void +ScatterEstimation::set_output_scatter_estimate_prefix(const std::string& arg) { this->output_scatter_estimate_prefix = arg; } -void ScatterEstimation::set_export_scatter_estimates_of_each_iteration(bool arg) -{ +void +ScatterEstimation::set_export_scatter_estimates_of_each_iteration(bool arg) { this->export_scatter_estimates_of_each_iteration = arg; } - void -ScatterEstimation:: -set_attenuation_correction_proj_data_sptr(const shared_ptr arg) -{ +ScatterEstimation::set_attenuation_correction_proj_data_sptr(const shared_ptr arg) { this->_already_setup = false; this->atten_norm_3d_sptr.reset(new BinNormalisationFromProjData(arg)); this->multiplicative_binnorm_sptr.reset(); } void -ScatterEstimation:: -set_normalisation_sptr(const shared_ptr arg) -{ +ScatterEstimation::set_normalisation_sptr(const shared_ptr arg) { this->_already_setup = false; this->norm_3d_sptr = arg; this->multiplicative_binnorm_sptr.reset(); } -bool ScatterEstimation::already_setup() const -{ +bool +ScatterEstimation::already_setup() const { return this->_already_setup; } Succeeded -ScatterEstimation:: -set_up() -{ - if (this->run_debug_mode) - { - info("ScatterEstimation: Debugging mode is activated."); - this->export_scatter_estimates_of_each_iteration = true; +ScatterEstimation::set_up() { + if (this->run_debug_mode) { + info("ScatterEstimation: Debugging mode is activated."); + this->export_scatter_estimates_of_each_iteration = true; - // Create extras folder in this location - FilePath current_full_path(FilePath::get_current_working_directory()); - extras_path = current_full_path.append("extras"); - } + // Create extras folder in this location + FilePath current_full_path(FilePath::get_current_working_directory()); + extras_path = current_full_path.append("extras"); + } - if (is_null_ptr(this->atten_image_sptr)) - error("ScatterEstimation: No attenuation image has been set. Aborting."); + if (is_null_ptr(this->atten_image_sptr)) + error("ScatterEstimation: No attenuation image has been set. Aborting."); - if (is_null_ptr(this->input_projdata_sptr)) - error("ScatterEstimation: No input proj_data have been set. Aborting."); + if (is_null_ptr(this->input_projdata_sptr)) + error("ScatterEstimation: No input proj_data have been set. Aborting."); - if (is_null_ptr(this->scatter_simulation_sptr)) - error("ScatterEstimation: Please define a scatter simulation method. Aborting."); + if (is_null_ptr(this->scatter_simulation_sptr)) + error("ScatterEstimation: Please define a scatter simulation method. Aborting."); - if (!run_in_2d_projdata) - error("ScatterEstimation: Currently, only running the estimation in 2D is supported."); + if (!run_in_2d_projdata) + error("ScatterEstimation: Currently, only running the estimation in 2D is supported."); - if(!this->recompute_mask_projdata) - { - if (is_null_ptr(this->mask_projdata_sptr)) - error("ScatterEstimation: Please set mask proj_data (or enable computing it)"); - } - else if (!this->recompute_mask_image) - { - if (is_null_ptr(this->mask_image_sptr)) - error("ScatterEstimation: Please set a mask image (or enable computing it)"); - } + if (!this->recompute_mask_projdata) { + if (is_null_ptr(this->mask_projdata_sptr)) + error("ScatterEstimation: Please set mask proj_data (or enable computing it)"); + } else if (!this->recompute_mask_image) { + if (is_null_ptr(this->mask_image_sptr)) + error("ScatterEstimation: Please set a mask image (or enable computing it)"); + } - if (this->_already_setup) - return Succeeded::yes; + if (this->_already_setup) + return Succeeded::yes; - info("Scatter Estimation Parameters (objects that are not set by parsing will not be listed correctly)\n" + this->parameter_info() + "\n\n", 1); + info("Scatter Estimation Parameters (objects that are not set by parsing will not be listed correctly)\n" + + this->parameter_info() + "\n\n", + 1); - this->create_multiplicative_binnorm_sptr(); - this->multiplicative_binnorm_sptr->set_up(this->input_projdata_sptr->get_exam_info_sptr(), this->input_projdata_sptr->get_proj_data_info_sptr()); + this->create_multiplicative_binnorm_sptr(); + this->multiplicative_binnorm_sptr->set_up(this->input_projdata_sptr->get_exam_info_sptr(), + this->input_projdata_sptr->get_proj_data_info_sptr()); #if 1 - // Calculate the SSRB - if (input_projdata_sptr->get_num_segments() > 1) - { - info("ScatterEstimation: Running SSRB on input data..."); - shared_ptr proj_data_info_2d_sptr( - SSRB(*this->input_projdata_sptr->get_proj_data_info_sptr(), - this->input_projdata_sptr->get_num_segments(), 1, false)); + // Calculate the SSRB + if (input_projdata_sptr->get_num_segments() > 1) { + info("ScatterEstimation: Running SSRB on input data..."); + shared_ptr proj_data_info_2d_sptr( + SSRB(*this->input_projdata_sptr->get_proj_data_info_sptr(), this->input_projdata_sptr->get_num_segments(), 1, false)); - this->input_projdata_2d_sptr.reset(new ProjDataInMemory(this->input_projdata_sptr->get_exam_info_sptr(), - proj_data_info_2d_sptr)); + this->input_projdata_2d_sptr.reset( + new ProjDataInMemory(this->input_projdata_sptr->get_exam_info_sptr(), proj_data_info_2d_sptr)); - SSRB(*this->input_projdata_2d_sptr, - *input_projdata_sptr,false); - } - else - { - input_projdata_2d_sptr = input_projdata_sptr; - } + SSRB(*this->input_projdata_2d_sptr, *input_projdata_sptr, false); + } else { + input_projdata_2d_sptr = input_projdata_sptr; + } #else - { - std::string tmp_input2D = "./extras/nema_proj_f1g1d0b0.hs_2d.hs"; - this->input_projdata_2d_sptr = - ProjData::read_from_file(tmp_input2D); - } + { + std::string tmp_input2D = "./extras/nema_proj_f1g1d0b0.hs_2d.hs"; + this->input_projdata_2d_sptr = ProjData::read_from_file(tmp_input2D); + } #endif - info("ScatterEstimation: Setting up reconstruction method ..."); - - if(is_null_ptr(this->reconstruction_template_sptr)) - { - warning("ScatterEstimation: Reconstruction method has not been initialised. Aborting."); - return Succeeded::no; + info("ScatterEstimation: Setting up reconstruction method ..."); + + if (is_null_ptr(this->reconstruction_template_sptr)) { + warning("ScatterEstimation: Reconstruction method has not been initialised. Aborting."); + return Succeeded::no; + } + + // We have to check which reconstruction method we are going to use ... + shared_ptr tmp_analytic = + dynamic_pointer_cast(this->reconstruction_template_sptr); + shared_ptr>> tmp_iterative = + dynamic_pointer_cast>>(reconstruction_template_sptr); + + if (!is_null_ptr(tmp_analytic)) { + if (set_up_analytic() == Succeeded::no) { + warning("ScatterEstimation: set_up_analytic reconstruction failed. Aborting."); + return Succeeded::no; } - // We have to check which reconstruction method we are going to use ... - shared_ptr tmp_analytic = - dynamic_pointer_cast(this->reconstruction_template_sptr); - shared_ptr > > tmp_iterative = - dynamic_pointer_cast > >(reconstruction_template_sptr); - - if (!is_null_ptr(tmp_analytic)) - { - if(set_up_analytic() == Succeeded::no) - { - warning("ScatterEstimation: set_up_analytic reconstruction failed. Aborting."); - return Succeeded::no; - } - - this->iterative_method = false; + this->iterative_method = false; + } else if (!is_null_ptr(tmp_iterative)) { + if (set_up_iterative(tmp_iterative) == Succeeded::no) { + warning("ScatterEstimation: set_up_iterative reconstruction failed. Aborting."); + return Succeeded::no; } - else if (!is_null_ptr(tmp_iterative)) - { - if(set_up_iterative(tmp_iterative) == Succeeded::no) - { - warning("ScatterEstimation: set_up_iterative reconstruction failed. Aborting."); - return Succeeded::no; - } - this->iterative_method = true; - } - else - { - warning("ScatterEstimation: Failure to detect a method of reconstruction. Aborting."); - return Succeeded::no; + this->iterative_method = true; + } else { + warning("ScatterEstimation: Failure to detect a method of reconstruction. Aborting."); + return Succeeded::no; + } + + if (iterative_method) + this->current_activity_image_sptr.reset(tmp_iterative->get_initial_data_ptr()); + + this->current_activity_image_sptr->fill(1.0); + + // + // ScatterSimulation + // + + info("ScatterEstimation: Setting up Scatter Simulation method ..."); + // The images are passed to the simulation. + // and it will override anything that the ScatterSimulation.par file has done. + if (this->override_density_image) { + info( + "ScatterEstimation: Over-riding attenuation image! (The file and settings set in the simulation par file are discarded)"); + this->scatter_simulation_sptr->set_density_image_sptr(this->atten_image_sptr); + } + + // if(this->override_initial_activity_image) + // { + // info("ScatterEstimation: Over-riding activity image! (The file and settings set in the simulation par file are + // discarded)"); this->scatter_simulation_sptr->set_activity_image_sptr(this->current_activity_image_sptr); + // } + + if (this->override_scanner_template) { + info("ScatterEstimation: Over-riding the scanner template! (The file and settings set in the simulation par file are " + "discarded)"); + if (run_in_2d_projdata) { + this->scatter_simulation_sptr->set_template_proj_data_info(*this->input_projdata_2d_sptr->get_proj_data_info_sptr()); + this->scatter_simulation_sptr->set_exam_info(this->input_projdata_2d_sptr->get_exam_info()); + } else { + this->scatter_simulation_sptr->set_template_proj_data_info(*this->input_projdata_sptr->get_proj_data_info_sptr()); + this->scatter_simulation_sptr->set_exam_info(this->input_projdata_sptr->get_exam_info()); } - - if(iterative_method) - this->current_activity_image_sptr.reset(tmp_iterative->get_initial_data_ptr()); - - this->current_activity_image_sptr->fill(1.0); - - // - // ScatterSimulation - // - - info("ScatterEstimation: Setting up Scatter Simulation method ..."); - // The images are passed to the simulation. - // and it will override anything that the ScatterSimulation.par file has done. - if(this->override_density_image) - { - info("ScatterEstimation: Over-riding attenuation image! (The file and settings set in the simulation par file are discarded)"); - this->scatter_simulation_sptr->set_density_image_sptr(this->atten_image_sptr); + } + + if (this->downsample_scanner_bool) + this->scatter_simulation_sptr->downsample_scanner(); + + // Check if Load a mask proj_data + + if (is_null_ptr(this->mask_projdata_sptr) || this->recompute_mask_projdata) { + if (is_null_ptr(this->mask_image_sptr) || this->recompute_mask_image) { + // Applying mask + // 1. Clone from the original image. + // 2. Apply to the new clone. + auto mask_image_ptr(this->atten_image_sptr->clone()); + this->apply_mask_in_place(*mask_image_ptr, this->masking_parameters); + this->mask_image_sptr.reset(mask_image_ptr); + if (this->mask_image_filename.size() > 0) + OutputFileFormat>::default_sptr()->write_to_file(this->mask_image_filename, + *this->mask_image_sptr); } - // if(this->override_initial_activity_image) - // { - // info("ScatterEstimation: Over-riding activity image! (The file and settings set in the simulation par file are discarded)"); - // this->scatter_simulation_sptr->set_activity_image_sptr(this->current_activity_image_sptr); - // } - - - if(this->override_scanner_template) - { - info("ScatterEstimation: Over-riding the scanner template! (The file and settings set in the simulation par file are discarded)"); - if (run_in_2d_projdata) - { - this->scatter_simulation_sptr->set_template_proj_data_info(*this->input_projdata_2d_sptr->get_proj_data_info_sptr()); - this->scatter_simulation_sptr->set_exam_info(this->input_projdata_2d_sptr->get_exam_info()); - } - else - { - this->scatter_simulation_sptr->set_template_proj_data_info(*this->input_projdata_sptr->get_proj_data_info_sptr()); - this->scatter_simulation_sptr->set_exam_info(this->input_projdata_sptr->get_exam_info()); - } - + if (project_mask_image() == Succeeded::no) { + warning("ScatterEstimation: Unsuccessful to fwd project the mask image. Aborting."); + return Succeeded::no; } + } - if (this->downsample_scanner_bool) - this->scatter_simulation_sptr->downsample_scanner(); - - // Check if Load a mask proj_data - - if(is_null_ptr(this->mask_projdata_sptr) || this->recompute_mask_projdata) - { - if(is_null_ptr(this->mask_image_sptr) || this->recompute_mask_image) - { - // Applying mask - // 1. Clone from the original image. - // 2. Apply to the new clone. - auto mask_image_ptr(this->atten_image_sptr->clone()); - this->apply_mask_in_place(*mask_image_ptr, this->masking_parameters); - this->mask_image_sptr.reset(mask_image_ptr); - if (this->mask_image_filename.size() > 0 ) - OutputFileFormat >::default_sptr()-> - write_to_file(this->mask_image_filename, *this->mask_image_sptr); - } - - if(project_mask_image() == Succeeded::no) - { - warning("ScatterEstimation: Unsuccessful to fwd project the mask image. Aborting."); - return Succeeded::no; - } - } - - this->_already_setup = true; - info("ScatterEstimation: >>>>Set up finished successfully!!<<<<"); - return Succeeded::yes; + this->_already_setup = true; + info("ScatterEstimation: >>>>Set up finished successfully!!<<<<"); + return Succeeded::yes; } Succeeded -ScatterEstimation:: -set_up_iterative(shared_ptr > > iterative_object) -{ - info("ScatterEstimation: Setting up iterative reconstruction ..."); - - if(run_in_2d_projdata) - { - iterative_object->set_input_data(this->input_projdata_2d_sptr); - } - else - iterative_object->set_input_data(this->input_projdata_sptr); +ScatterEstimation::set_up_iterative(shared_ptr>> iterative_object) { + info("ScatterEstimation: Setting up iterative reconstruction ..."); - const double start_time = this->input_projdata_sptr->get_exam_info_sptr()->get_time_frame_definitions().get_start_time(); - const double end_time =this->input_projdata_sptr->get_exam_info_sptr()->get_time_frame_definitions().get_end_time(); + if (run_in_2d_projdata) { + iterative_object->set_input_data(this->input_projdata_2d_sptr); + } else + iterative_object->set_input_data(this->input_projdata_sptr); + const double start_time = this->input_projdata_sptr->get_exam_info_sptr()->get_time_frame_definitions().get_start_time(); + const double end_time = this->input_projdata_sptr->get_exam_info_sptr()->get_time_frame_definitions().get_end_time(); - // - // Multiplicative projdata - // - shared_ptr tmp_atten_projdata_sptr = - this->get_attenuation_correction_factors_sptr(this->multiplicative_binnorm_sptr); - shared_ptr atten_projdata_2d_sptr; + // + // Multiplicative projdata + // + shared_ptr tmp_atten_projdata_sptr = this->get_attenuation_correction_factors_sptr(this->multiplicative_binnorm_sptr); + shared_ptr atten_projdata_2d_sptr; - info("ScatterEstimation: 3.Calculating the attenuation projection data..."); + info("ScatterEstimation: 3.Calculating the attenuation projection data..."); - if( tmp_atten_projdata_sptr->get_num_segments() > 1) - { - info("ScatterEstimation: Running SSRB on attenuation correction coefficients ..."); + if (tmp_atten_projdata_sptr->get_num_segments() > 1) { + info("ScatterEstimation: Running SSRB on attenuation correction coefficients ..."); - std::string out_filename = "tmp_atten_sino_2d.hs"; + std::string out_filename = "tmp_atten_sino_2d.hs"; - atten_projdata_2d_sptr = create_new_proj_data(out_filename, this->input_projdata_2d_sptr->get_exam_info_sptr(), - this->input_projdata_2d_sptr->get_proj_data_info_sptr()->create_shared_clone()); + atten_projdata_2d_sptr = create_new_proj_data(out_filename, this->input_projdata_2d_sptr->get_exam_info_sptr(), + this->input_projdata_2d_sptr->get_proj_data_info_sptr()->create_shared_clone()); - SSRB(*atten_projdata_2d_sptr, - *tmp_atten_projdata_sptr, true); - } - else - { - // TODO: this needs more work. -- Setting directly 2D proj_data is buggy right now. - atten_projdata_2d_sptr = tmp_atten_projdata_sptr; - } - - info("ScatterEstimation: 4.Calculating the normalisation data..."); - { - if (run_in_2d_projdata) - { - shared_ptr norm3d_sptr = - this->get_normalisation_object_sptr(this->multiplicative_binnorm_sptr); - shared_ptr norm_coeff_2d_sptr; + SSRB(*atten_projdata_2d_sptr, *tmp_atten_projdata_sptr, true); + } else { + // TODO: this needs more work. -- Setting directly 2D proj_data is buggy right now. + atten_projdata_2d_sptr = tmp_atten_projdata_sptr; + } - if ( input_projdata_sptr->get_num_segments() > 1) - { - // Some BinNormalisation classes don't know about SSRB. - // we need to get norm2d=1/SSRB(1/norm3d)) + info("ScatterEstimation: 4.Calculating the normalisation data..."); + { + if (run_in_2d_projdata) { + shared_ptr norm3d_sptr = this->get_normalisation_object_sptr(this->multiplicative_binnorm_sptr); + shared_ptr norm_coeff_2d_sptr; - info("ScatterEstimation: Constructing 2D normalisation coefficients ..."); + if (input_projdata_sptr->get_num_segments() > 1) { + // Some BinNormalisation classes don't know about SSRB. + // we need to get norm2d=1/SSRB(1/norm3d)) - std::string out_filename = "tmp_inverted_normdata.hs"; - shared_ptr inv_projdata_3d_sptr = create_new_proj_data(out_filename, - this->input_projdata_sptr->get_exam_info_sptr(), - this->input_projdata_sptr->get_proj_data_info_sptr()->create_shared_clone()); - inv_projdata_3d_sptr->fill(1.f); + info("ScatterEstimation: Constructing 2D normalisation coefficients ..."); - out_filename = "tmp_normdata_2d.hs"; - shared_ptr norm_projdata_2d_sptr = create_new_proj_data(out_filename, - this->input_projdata_2d_sptr->get_exam_info_sptr(), - this->input_projdata_2d_sptr->get_proj_data_info_sptr()->create_shared_clone()); - norm_projdata_2d_sptr->fill(0.f); + std::string out_filename = "tmp_inverted_normdata.hs"; + shared_ptr inv_projdata_3d_sptr = + create_new_proj_data(out_filename, this->input_projdata_sptr->get_exam_info_sptr(), + this->input_projdata_sptr->get_proj_data_info_sptr()->create_shared_clone()); + inv_projdata_3d_sptr->fill(1.f); - // Essentially since inv_projData_sptr is 1s then this is an inversion. - // inv_projdata_sptr = 1/norm3d - norm3d_sptr->undo(*inv_projdata_3d_sptr, start_time, end_time); + out_filename = "tmp_normdata_2d.hs"; + shared_ptr norm_projdata_2d_sptr = + create_new_proj_data(out_filename, this->input_projdata_2d_sptr->get_exam_info_sptr(), + this->input_projdata_2d_sptr->get_proj_data_info_sptr()->create_shared_clone()); + norm_projdata_2d_sptr->fill(0.f); - info("ScatterEstimation: Performing SSRB on efficiency factors ..."); + // Essentially since inv_projData_sptr is 1s then this is an inversion. + // inv_projdata_sptr = 1/norm3d + norm3d_sptr->undo(*inv_projdata_3d_sptr, start_time, end_time); - SSRB(*norm_projdata_2d_sptr, - *inv_projdata_3d_sptr,false); + info("ScatterEstimation: Performing SSRB on efficiency factors ..."); - // Crucial: Avoid divisions by zero!! - // This should be resolved after https://github.com/UCL/STIR/issues/348 - pow_times_add min_threshold (0.0f, 1.0f, 1.0f, 1E-20f, NumericInfo().max_value()); - apply_to_proj_data(*norm_projdata_2d_sptr, min_threshold); + SSRB(*norm_projdata_2d_sptr, *inv_projdata_3d_sptr, false); - pow_times_add invert (0.0f, 1.0f, -1.0f, NumericInfo().min_value(), NumericInfo().max_value()); - apply_to_proj_data(*norm_projdata_2d_sptr, invert); + // Crucial: Avoid divisions by zero!! + // This should be resolved after https://github.com/UCL/STIR/issues/348 + pow_times_add min_threshold(0.0f, 1.0f, 1.0f, 1E-20f, NumericInfo().max_value()); + apply_to_proj_data(*norm_projdata_2d_sptr, min_threshold); - norm_coeff_2d_sptr.reset(new BinNormalisationFromProjData(norm_projdata_2d_sptr)); - } - else - { - norm_coeff_2d_sptr = norm3d_sptr; - } + pow_times_add invert(0.0f, 1.0f, -1.0f, NumericInfo().min_value(), NumericInfo().max_value()); + apply_to_proj_data(*norm_projdata_2d_sptr, invert); - shared_ptratten_coeff_2d_sptr(new BinNormalisationFromProjData(atten_projdata_2d_sptr)); - this->multiplicative_binnorm_2d_sptr.reset( - new ChainedBinNormalisation(norm_coeff_2d_sptr, atten_coeff_2d_sptr)); - - this->multiplicative_binnorm_2d_sptr->set_up(this->back_projdata_sptr->get_exam_info_sptr(), this->input_projdata_2d_sptr->get_proj_data_info_sptr()->create_shared_clone()); - iterative_object->get_objective_function_sptr()->set_normalisation_sptr(multiplicative_binnorm_2d_sptr); - } - else // run_in_2d_projdata - iterative_object->get_objective_function_sptr()->set_normalisation_sptr(multiplicative_binnorm_sptr); - } - info("ScatterEstimation: Done normalisation coefficients."); - - // - // Set background (randoms) projdata - // - info("ScatterEstimation: 5.Calculating the background data and data_to_fit for the scaling..."); - - if (!is_null_ptr(this->back_projdata_sptr)) - { - if( back_projdata_sptr->get_num_segments() > 1) - { - info("ScatterEstimation: Running SSRB on the background data ..."); - - this->back_projdata_2d_sptr.reset(new ProjDataInMemory(this->input_projdata_2d_sptr->get_exam_info_sptr(), - this->input_projdata_2d_sptr->get_proj_data_info_sptr()->create_shared_clone())); + norm_coeff_2d_sptr.reset(new BinNormalisationFromProjData(norm_projdata_2d_sptr)); + } else { + norm_coeff_2d_sptr = norm3d_sptr; + } - SSRB(*this->back_projdata_2d_sptr, - *this->back_projdata_sptr, false); - } - else - { - this->back_projdata_2d_sptr = back_projdata_sptr; - } + shared_ptr atten_coeff_2d_sptr(new BinNormalisationFromProjData(atten_projdata_2d_sptr)); + this->multiplicative_binnorm_2d_sptr.reset(new ChainedBinNormalisation(norm_coeff_2d_sptr, atten_coeff_2d_sptr)); + + this->multiplicative_binnorm_2d_sptr->set_up( + this->back_projdata_sptr->get_exam_info_sptr(), + this->input_projdata_2d_sptr->get_proj_data_info_sptr()->create_shared_clone()); + iterative_object->get_objective_function_sptr()->set_normalisation_sptr(multiplicative_binnorm_2d_sptr); + } else // run_in_2d_projdata + iterative_object->get_objective_function_sptr()->set_normalisation_sptr(multiplicative_binnorm_sptr); + } + info("ScatterEstimation: Done normalisation coefficients."); + + // + // Set background (randoms) projdata + // + info("ScatterEstimation: 5.Calculating the background data and data_to_fit for the scaling..."); + + if (!is_null_ptr(this->back_projdata_sptr)) { + if (back_projdata_sptr->get_num_segments() > 1) { + info("ScatterEstimation: Running SSRB on the background data ..."); + + this->back_projdata_2d_sptr.reset( + new ProjDataInMemory(this->input_projdata_2d_sptr->get_exam_info_sptr(), + this->input_projdata_2d_sptr->get_proj_data_info_sptr()->create_shared_clone())); + + SSRB(*this->back_projdata_2d_sptr, *this->back_projdata_sptr, false); + } else { + this->back_projdata_2d_sptr = back_projdata_sptr; } - else // We will need a background for the scatter, so let's create a simple empty ProjData - { - if (run_in_2d_projdata) - { - std::string out_filename = "tmp_background_data_2d.hs"; - - this->back_projdata_2d_sptr = create_new_proj_data(out_filename, - this->input_projdata_2d_sptr->get_exam_info_sptr(), - this->input_projdata_2d_sptr->get_proj_data_info_sptr()->create_shared_clone()); - this->back_projdata_2d_sptr->fill(0.0f); - } - else - { - std::string out_filename = "tmp_background_data.hs"; - - this->back_projdata_sptr = create_new_proj_data(out_filename, - this->input_projdata_sptr->get_exam_info_sptr(), - this->input_projdata_sptr->get_proj_data_info_sptr()->create_shared_clone()); - this->back_projdata_sptr->fill(0.0f); - } + } else // We will need a background for the scatter, so let's create a simple empty ProjData + { + if (run_in_2d_projdata) { + std::string out_filename = "tmp_background_data_2d.hs"; + + this->back_projdata_2d_sptr = + create_new_proj_data(out_filename, this->input_projdata_2d_sptr->get_exam_info_sptr(), + this->input_projdata_2d_sptr->get_proj_data_info_sptr()->create_shared_clone()); + this->back_projdata_2d_sptr->fill(0.0f); + } else { + std::string out_filename = "tmp_background_data.hs"; + + this->back_projdata_sptr = + create_new_proj_data(out_filename, this->input_projdata_sptr->get_exam_info_sptr(), + this->input_projdata_sptr->get_proj_data_info_sptr()->create_shared_clone()); + this->back_projdata_sptr->fill(0.0f); } + } + if (run_in_2d_projdata) { + // Normalise in order to get the additive component + std::stringstream convert; // stream used for the conversion + convert << output_additive_estimate_prefix << "_0_2d.hs"; + std::string out_filename = convert.str(); // extras_path.get_path() +"/"+ output_background_estimate_prefix + ""; + add_projdata_2d_sptr = create_new_proj_data(out_filename, this->input_projdata_2d_sptr->get_exam_info_sptr(), + this->input_projdata_2d_sptr->get_proj_data_info_sptr()->create_shared_clone()); + add_projdata_2d_sptr->fill(*back_projdata_2d_sptr); + this->multiplicative_binnorm_2d_sptr->apply(*this->add_projdata_2d_sptr); - if (run_in_2d_projdata) - { - // Normalise in order to get the additive component - std::stringstream convert; // stream used for the conversion - convert << output_additive_estimate_prefix << "_0_2d.hs"; - - std::string out_filename = convert.str(); //extras_path.get_path() +"/"+ output_background_estimate_prefix + ""; - add_projdata_2d_sptr = create_new_proj_data(out_filename, - this->input_projdata_2d_sptr->get_exam_info_sptr(), - this->input_projdata_2d_sptr->get_proj_data_info_sptr()->create_shared_clone()); - add_projdata_2d_sptr->fill(*back_projdata_2d_sptr); - this->multiplicative_binnorm_2d_sptr->apply(*this->add_projdata_2d_sptr); - - iterative_object->get_objective_function_sptr()->set_additive_proj_data_sptr(this->add_projdata_2d_sptr); - - out_filename ="data_to_fit_2d.hs"; - data_to_fit_projdata_sptr = create_new_proj_data(out_filename, - this->input_projdata_2d_sptr->get_exam_info_sptr(), - this->input_projdata_2d_sptr->get_proj_data_info_sptr()->create_shared_clone()); - - data_to_fit_projdata_sptr->fill(*input_projdata_2d_sptr); - subtract_proj_data(*data_to_fit_projdata_sptr, *this->back_projdata_2d_sptr); - } - else - { - // Normalise in order to get the additive component - std::string out_filename = output_additive_estimate_prefix + "_0.hs"; - add_projdata_sptr = create_new_proj_data(out_filename, - this->input_projdata_sptr->get_exam_info_sptr(), - this->input_projdata_sptr->get_proj_data_info_sptr()->create_shared_clone()); - add_projdata_sptr->fill(*back_projdata_sptr); - this->multiplicative_binnorm_sptr->apply(*this->add_projdata_sptr); - - iterative_object->get_objective_function_sptr()->set_additive_proj_data_sptr(this->add_projdata_sptr); - - out_filename = "data_to_fit.hs"; - data_to_fit_projdata_sptr = create_new_proj_data(out_filename, - this->input_projdata_sptr->get_exam_info_sptr(), - this->input_projdata_sptr->get_proj_data_info_sptr()->create_shared_clone()); - data_to_fit_projdata_sptr->fill(*input_projdata_sptr); - subtract_proj_data(*data_to_fit_projdata_sptr, *this->back_projdata_sptr); - } + iterative_object->get_objective_function_sptr()->set_additive_proj_data_sptr(this->add_projdata_2d_sptr); - return Succeeded::yes; + out_filename = "data_to_fit_2d.hs"; + data_to_fit_projdata_sptr = + create_new_proj_data(out_filename, this->input_projdata_2d_sptr->get_exam_info_sptr(), + this->input_projdata_2d_sptr->get_proj_data_info_sptr()->create_shared_clone()); + + data_to_fit_projdata_sptr->fill(*input_projdata_2d_sptr); + subtract_proj_data(*data_to_fit_projdata_sptr, *this->back_projdata_2d_sptr); + } else { + // Normalise in order to get the additive component + std::string out_filename = output_additive_estimate_prefix + "_0.hs"; + add_projdata_sptr = create_new_proj_data(out_filename, this->input_projdata_sptr->get_exam_info_sptr(), + this->input_projdata_sptr->get_proj_data_info_sptr()->create_shared_clone()); + add_projdata_sptr->fill(*back_projdata_sptr); + this->multiplicative_binnorm_sptr->apply(*this->add_projdata_sptr); + + iterative_object->get_objective_function_sptr()->set_additive_proj_data_sptr(this->add_projdata_sptr); + + out_filename = "data_to_fit.hs"; + data_to_fit_projdata_sptr = create_new_proj_data(out_filename, this->input_projdata_sptr->get_exam_info_sptr(), + this->input_projdata_sptr->get_proj_data_info_sptr()->create_shared_clone()); + data_to_fit_projdata_sptr->fill(*input_projdata_sptr); + subtract_proj_data(*data_to_fit_projdata_sptr, *this->back_projdata_sptr); + } + + return Succeeded::yes; } Succeeded -ScatterEstimation:: -set_up_analytic() -{ - //TODO : I have most stuff in tmp. - error("Analytic recon not implemented yet"); - return Succeeded::yes; +ScatterEstimation::set_up_analytic() { + // TODO : I have most stuff in tmp. + error("Analytic recon not implemented yet"); + return Succeeded::yes; } Succeeded -ScatterEstimation:: -process_data() -{ +ScatterEstimation::process_data() { if (!this->_already_setup) error("ScatterEstimation: set_up needs to be called before process_data()"); - float local_min_scale_value = 0.5f; - float local_max_scale_value = 0.5f; - - stir::BSpline::BSplineType spline_type = stir::BSpline::quadratic; - - // This has been set to 2D or 3D in the set_up() - shared_ptr unscaled_est_projdata_sptr(new ProjDataInMemory(this->scatter_simulation_sptr->get_exam_info_sptr(), - this->scatter_simulation_sptr->get_template_proj_data_info_sptr()->create_shared_clone())); - scatter_simulation_sptr->set_output_proj_data_sptr(unscaled_est_projdata_sptr); - - // Here the scaled scatter data will be stored. - // Wether 2D or 3D depends on how the ScatterSimulation was initialised - shared_ptr scaled_est_projdata_sptr; + float local_min_scale_value = 0.5f; + float local_max_scale_value = 0.5f; + + stir::BSpline::BSplineType spline_type = stir::BSpline::quadratic; + + // This has been set to 2D or 3D in the set_up() + shared_ptr unscaled_est_projdata_sptr( + new ProjDataInMemory(this->scatter_simulation_sptr->get_exam_info_sptr(), + this->scatter_simulation_sptr->get_template_proj_data_info_sptr()->create_shared_clone())); + scatter_simulation_sptr->set_output_proj_data_sptr(unscaled_est_projdata_sptr); + + // Here the scaled scatter data will be stored. + // Wether 2D or 3D depends on how the ScatterSimulation was initialised + shared_ptr scaled_est_projdata_sptr; + + shared_ptr normalisation_factors_sptr = this->get_normalisation_object_sptr( + run_in_2d_projdata ? this->multiplicative_binnorm_2d_sptr : this->multiplicative_binnorm_sptr); + if (run_in_2d_projdata) { + scaled_est_projdata_sptr.reset( + new ProjDataInMemory(this->input_projdata_2d_sptr->get_exam_info_sptr(), + this->input_projdata_2d_sptr->get_proj_data_info_sptr()->create_shared_clone())); + scaled_est_projdata_sptr->fill(0.F); + } else { + scaled_est_projdata_sptr.reset( + new ProjDataInMemory(this->input_projdata_sptr->get_exam_info_sptr(), + this->input_projdata_sptr->get_proj_data_info_sptr()->create_shared_clone())); + scaled_est_projdata_sptr->fill(0.F); + } + + info("ScatterEstimation: Start processing..."); + shared_ptr> act_image_for_averaging; + + // Recompute the initial y image if the max is equal to the min. +#if 1 + if (this->current_activity_image_sptr->find_max() == this->current_activity_image_sptr->find_min()) { + info("ScatterEstimation: The max and the min values of the current activity image are equal." + "We deduce that it has been initialised to some value, therefore we will run an initial " + "reconstruction ..."); - shared_ptr normalisation_factors_sptr = - this->get_normalisation_object_sptr(run_in_2d_projdata - ? this->multiplicative_binnorm_2d_sptr - : this->multiplicative_binnorm_sptr); - if(run_in_2d_projdata) - { - scaled_est_projdata_sptr.reset(new ProjDataInMemory(this->input_projdata_2d_sptr->get_exam_info_sptr(), - this->input_projdata_2d_sptr->get_proj_data_info_sptr()->create_shared_clone())); - scaled_est_projdata_sptr->fill(0.F); - } + if (iterative_method) + reconstruct_iterative(0, this->current_activity_image_sptr); else - { - scaled_est_projdata_sptr.reset(new ProjDataInMemory(this->input_projdata_sptr->get_exam_info_sptr(), - this->input_projdata_sptr->get_proj_data_info_sptr()->create_shared_clone())); - scaled_est_projdata_sptr->fill(0.F); - } - - info("ScatterEstimation: Start processing..."); - shared_ptr > act_image_for_averaging; + reconstruct_analytic(0, this->current_activity_image_sptr); - //Recompute the initial y image if the max is equal to the min. -#if 1 - if( this->current_activity_image_sptr->find_max() == this->current_activity_image_sptr->find_min() ) - { - info("ScatterEstimation: The max and the min values of the current activity image are equal." - "We deduce that it has been initialised to some value, therefore we will run an initial " - "reconstruction ..."); - - if (iterative_method) - reconstruct_iterative(0, this->current_activity_image_sptr); - else - reconstruct_analytic(0, this->current_activity_image_sptr); - - if ( run_debug_mode ) - { - std::string out_filename = extras_path.get_path() + "initial_activity_image"; - OutputFileFormat >::default_sptr()-> - write_to_file(out_filename, *this->current_activity_image_sptr); - } + if (run_debug_mode) { + std::string out_filename = extras_path.get_path() + "initial_activity_image"; + OutputFileFormat>::default_sptr()->write_to_file(out_filename, + *this->current_activity_image_sptr); } + } #else - { - std::string filename = extras_path.get_path() + "recon_0.hv"; - current_activity_image_sptr = read_from_file >(filename); - } + { + std::string filename = extras_path.get_path() + "recon_0.hv"; + current_activity_image_sptr = read_from_file>(filename); + } #endif - // Set the first activity image - scatter_simulation_sptr->set_activity_image_sptr(current_activity_image_sptr); + // Set the first activity image + scatter_simulation_sptr->set_activity_image_sptr(current_activity_image_sptr); - if (this->do_average_at_2) - act_image_for_averaging.reset(this->current_activity_image_sptr->clone()); + if (this->do_average_at_2) + act_image_for_averaging.reset(this->current_activity_image_sptr->clone()); - // - // Begin the estimation process... - // - info("ScatterEstimation: Begin the estimation process..."); - for (int i_scat_iter = 1; - i_scat_iter <= this->num_scatter_iterations; - i_scat_iter++) - { + // + // Begin the estimation process... + // + info("ScatterEstimation: Begin the estimation process..."); + for (int i_scat_iter = 1; i_scat_iter <= this->num_scatter_iterations; i_scat_iter++) { + if (this->do_average_at_2) { + if (i_scat_iter == 2) // do average 0 and 1 + { + if (is_null_ptr(act_image_for_averaging)) + error("Storing the first activity estimate has failed at some point."); - if ( this->do_average_at_2) - { - if (i_scat_iter == 2) // do average 0 and 1 - { - if (is_null_ptr(act_image_for_averaging)) - error("Storing the first activity estimate has failed at some point."); + *this->current_activity_image_sptr += *act_image_for_averaging; + *this->current_activity_image_sptr /= 2.f; + } + } - *this->current_activity_image_sptr += *act_image_for_averaging; - *this->current_activity_image_sptr /= 2.f; - } - } + if (!iterative_method) { + // Threshold + } - if (!iterative_method) - { - // Threshold - } + info("ScatterEstimation: Scatter simulation in progress..."); + if (this->scatter_simulation_sptr->set_up() == Succeeded::no) + error("ScatterEstimation: Failure at set_up() of the Scatter Simulation."); - info("ScatterEstimation: Scatter simulation in progress..."); - if (this->scatter_simulation_sptr->set_up() == Succeeded::no) - error("ScatterEstimation: Failure at set_up() of the Scatter Simulation."); - - if (this->scatter_simulation_sptr->process_data() == Succeeded::no) - error("ScatterEstimation: Scatter simulation failed"); - info("ScatterEstimation: Scatter simulation done..."); - - if(this->run_debug_mode) // Write unscaled scatter sinogram - { - std::stringstream convert; // stream used for the conversion - convert << "unscaled_" << i_scat_iter; - FilePath tmp(convert.str(),false); - tmp.prepend_directory_name(extras_path.get_path()); - unscaled_est_projdata_sptr->write_to_file(tmp.get_string()); - } + if (this->scatter_simulation_sptr->process_data() == Succeeded::no) + error("ScatterEstimation: Scatter simulation failed"); + info("ScatterEstimation: Scatter simulation done..."); - // Set the min and max scale factors - // We're going to assume that the first iteration starts from an image without scatter correction, and therefore - // overestimates scatter. This could be inaccurate, but is the case most of the time. - // TODO introduce a variable to control this behaviour - if (i_scat_iter > 0) - { - local_max_scale_value = this->max_scale_value; - local_min_scale_value = this->min_scale_value; - } + if (this->run_debug_mode) // Write unscaled scatter sinogram + { + std::stringstream convert; // stream used for the conversion + convert << "unscaled_" << i_scat_iter; + FilePath tmp(convert.str(), false); + tmp.prepend_directory_name(extras_path.get_path()); + unscaled_est_projdata_sptr->write_to_file(tmp.get_string()); + } - scaled_est_projdata_sptr->fill(0.F); - - upsample_and_fit_scatter_estimate(*scaled_est_projdata_sptr, *data_to_fit_projdata_sptr, - *unscaled_est_projdata_sptr, - *normalisation_factors_sptr, - *this->mask_projdata_sptr, local_min_scale_value, - local_max_scale_value, this->half_filter_width, - spline_type, true); - - if(this->run_debug_mode) - { - std::stringstream convert; // stream used for the conversion - convert << "scaled_" << i_scat_iter; - FilePath tmp(convert.str(),false); - tmp.prepend_directory_name(extras_path.get_path()); - scaled_est_projdata_sptr->write_to_file(tmp.get_string()); - } + // Set the min and max scale factors + // We're going to assume that the first iteration starts from an image without scatter correction, and therefore + // overestimates scatter. This could be inaccurate, but is the case most of the time. + // TODO introduce a variable to control this behaviour + if (i_scat_iter > 0) { + local_max_scale_value = this->max_scale_value; + local_min_scale_value = this->min_scale_value; + } + scaled_est_projdata_sptr->fill(0.F); - // When saving we need to go 3D. - if (this->export_scatter_estimates_of_each_iteration || - i_scat_iter == this->num_scatter_iterations ) - { - - shared_ptr temp_scatter_projdata; - - if(run_in_2d_projdata) - { - info("ScatterEstimation: upsampling scatter to 3D"); - //this is complicated as the 2d scatter estimate was - //"unnormalised" (divided by norm2d), so we need to undo this 2D norm, and put a 3D norm in. - //unfortunately, currently the values in the gaps in the - //scatter estimate are not quite zero (just very small) - //so we have to first make sure that they are zero before - //we do any of this, otherwise the values after normalisation will be garbage - //we do this by min-thresholding and then subtracting the threshold. - //as long as the threshold is tiny, this will be ok - - // At the same time we are going to save to a temp projdata file - - shared_ptr temp_projdata ( new ProjDataInMemory (scaled_est_projdata_sptr->get_exam_info_sptr(), - scaled_est_projdata_sptr->get_proj_data_info_sptr())); - temp_projdata->fill(*scaled_est_projdata_sptr); - pow_times_add min_threshold (0.0f, 1.0f, 1.0f, 1e-9f, NumericInfo().max_value()); - pow_times_add add_scalar (-1e-9f, 1.0f, 1.0f, NumericInfo().min_value(), NumericInfo().max_value()); - apply_to_proj_data(*temp_projdata, min_threshold); - apply_to_proj_data(*temp_projdata, add_scalar); - // threshold back to 0 to avoid getting tiny negatives (due to numerical precision errors) - pow_times_add min_threshold_zero (0.0f, 1.0f, 1.0f, 0.f, NumericInfo().max_value()); - apply_to_proj_data(*temp_projdata, min_threshold_zero); - - // ok, we can multiply with the norm - normalisation_factors_sptr->apply(*temp_projdata); - - // Create proj_data to save the 3d scatter estimate - if(!this->output_scatter_estimate_prefix.empty()) - { - std::stringstream convert; - convert << this->output_scatter_estimate_prefix << "_" << i_scat_iter; - std::string output_scatter_filename = convert.str(); - - scatter_estimate_sptr.reset( - new ProjDataInterfile(this->input_projdata_sptr->get_exam_info_sptr(), - this->input_projdata_sptr->get_proj_data_info_sptr() , - output_scatter_filename, - std::ios::in | std::ios::out | std::ios::trunc)); - } - else - { - // TODO should check if we have one already from previous iteration - scatter_estimate_sptr.reset( - new ProjDataInMemory(this->input_projdata_sptr->get_exam_info_sptr(), - this->input_projdata_sptr->get_proj_data_info_sptr())); - } - scatter_estimate_sptr->fill(0.0); - - // Upsample to 3D - //we're currently not doing the tail fitting in this step, but keeping the same scale as determined in 2D - //Note that most of the arguments here are ignored because we fix the scale to 1 - shared_ptr normalisation_factors_3d_sptr = - this->get_normalisation_object_sptr(this->multiplicative_binnorm_sptr); - - upsample_and_fit_scatter_estimate(*scatter_estimate_sptr, - *this->input_projdata_sptr, - *temp_projdata, - *normalisation_factors_3d_sptr, - *this->input_projdata_sptr, - 1.0f, 1.0f, 1, spline_type, - false); - } - else - { - scatter_estimate_sptr = scaled_est_projdata_sptr; - } - - if(!this->output_additive_estimate_prefix.empty()) - { - info("ScatterEstimation: constructing additive sinogram"); - // Now save the full background term. - std::stringstream convert; - convert << this->output_additive_estimate_prefix << "_" << - i_scat_iter; - std::string output_additive_filename = convert.str(); - - shared_ptr temp_additive_projdata( - new ProjDataInterfile(this->input_projdata_sptr->get_exam_info_sptr(), - this->input_projdata_sptr->get_proj_data_info_sptr() , - output_additive_filename, - std::ios::in | std::ios::out | std::ios::trunc)); - - temp_additive_projdata->fill(*scatter_estimate_sptr); - if (!is_null_ptr(this->back_projdata_sptr)) - { - add_proj_data(*temp_additive_projdata, *this->back_projdata_sptr); - } - - this->multiplicative_binnorm_sptr->apply(*temp_additive_projdata); - } - } + upsample_and_fit_scatter_estimate(*scaled_est_projdata_sptr, *data_to_fit_projdata_sptr, *unscaled_est_projdata_sptr, + *normalisation_factors_sptr, *this->mask_projdata_sptr, local_min_scale_value, + local_max_scale_value, this->half_filter_width, spline_type, true); - // In the additive put the scaled scatter estimate - // If we have randoms, then add them to the scaled scatter estimate - // Then normalise - if(run_in_2d_projdata) - { - this->add_projdata_2d_sptr->fill(*scaled_est_projdata_sptr); - - if (!is_null_ptr(this->back_projdata_2d_sptr)) - { - add_proj_data(*add_projdata_2d_sptr, *this->back_projdata_2d_sptr); - } - this->multiplicative_binnorm_2d_sptr->apply(*add_projdata_2d_sptr); - } - else - { - // TODO restructure code to move additive_projdata code from above - error("ScatterEstimation: You should not be here. This is not 2D."); + if (this->run_debug_mode) { + std::stringstream convert; // stream used for the conversion + convert << "scaled_" << i_scat_iter; + FilePath tmp(convert.str(), false); + tmp.prepend_directory_name(extras_path.get_path()); + scaled_est_projdata_sptr->write_to_file(tmp.get_string()); + } + + // When saving we need to go 3D. + if (this->export_scatter_estimates_of_each_iteration || i_scat_iter == this->num_scatter_iterations) { + + shared_ptr temp_scatter_projdata; + + if (run_in_2d_projdata) { + info("ScatterEstimation: upsampling scatter to 3D"); + // this is complicated as the 2d scatter estimate was + //"unnormalised" (divided by norm2d), so we need to undo this 2D norm, and put a 3D norm in. + // unfortunately, currently the values in the gaps in the + // scatter estimate are not quite zero (just very small) + // so we have to first make sure that they are zero before + // we do any of this, otherwise the values after normalisation will be garbage + // we do this by min-thresholding and then subtracting the threshold. + // as long as the threshold is tiny, this will be ok + + // At the same time we are going to save to a temp projdata file + + shared_ptr temp_projdata(new ProjDataInMemory(scaled_est_projdata_sptr->get_exam_info_sptr(), + scaled_est_projdata_sptr->get_proj_data_info_sptr())); + temp_projdata->fill(*scaled_est_projdata_sptr); + pow_times_add min_threshold(0.0f, 1.0f, 1.0f, 1e-9f, NumericInfo().max_value()); + pow_times_add add_scalar(-1e-9f, 1.0f, 1.0f, NumericInfo().min_value(), NumericInfo().max_value()); + apply_to_proj_data(*temp_projdata, min_threshold); + apply_to_proj_data(*temp_projdata, add_scalar); + // threshold back to 0 to avoid getting tiny negatives (due to numerical precision errors) + pow_times_add min_threshold_zero(0.0f, 1.0f, 1.0f, 0.f, NumericInfo().max_value()); + apply_to_proj_data(*temp_projdata, min_threshold_zero); + + // ok, we can multiply with the norm + normalisation_factors_sptr->apply(*temp_projdata); + + // Create proj_data to save the 3d scatter estimate + if (!this->output_scatter_estimate_prefix.empty()) { + std::stringstream convert; + convert << this->output_scatter_estimate_prefix << "_" << i_scat_iter; + std::string output_scatter_filename = convert.str(); + + scatter_estimate_sptr.reset(new ProjDataInterfile( + this->input_projdata_sptr->get_exam_info_sptr(), this->input_projdata_sptr->get_proj_data_info_sptr(), + output_scatter_filename, std::ios::in | std::ios::out | std::ios::trunc)); + } else { + // TODO should check if we have one already from previous iteration + scatter_estimate_sptr.reset(new ProjDataInMemory(this->input_projdata_sptr->get_exam_info_sptr(), + this->input_projdata_sptr->get_proj_data_info_sptr())); } - current_activity_image_sptr->fill(1.f); + scatter_estimate_sptr->fill(0.0); - iterative_method ? reconstruct_iterative(i_scat_iter, - this->current_activity_image_sptr): - reconstruct_analytic(i_scat_iter, this->current_activity_image_sptr); + // Upsample to 3D + // we're currently not doing the tail fitting in this step, but keeping the same scale as determined in 2D + // Note that most of the arguments here are ignored because we fix the scale to 1 + shared_ptr normalisation_factors_3d_sptr = + this->get_normalisation_object_sptr(this->multiplicative_binnorm_sptr); - scatter_simulation_sptr->set_activity_image_sptr(current_activity_image_sptr); + upsample_and_fit_scatter_estimate(*scatter_estimate_sptr, *this->input_projdata_sptr, *temp_projdata, + *normalisation_factors_3d_sptr, *this->input_projdata_sptr, 1.0f, 1.0f, 1, spline_type, + false); + } else { + scatter_estimate_sptr = scaled_est_projdata_sptr; + } - } + if (!this->output_additive_estimate_prefix.empty()) { + info("ScatterEstimation: constructing additive sinogram"); + // Now save the full background term. + std::stringstream convert; + convert << this->output_additive_estimate_prefix << "_" << i_scat_iter; + std::string output_additive_filename = convert.str(); - info("ScatterEstimation: Scatter Estimation finished !!!"); + shared_ptr temp_additive_projdata(new ProjDataInterfile( + this->input_projdata_sptr->get_exam_info_sptr(), this->input_projdata_sptr->get_proj_data_info_sptr(), + output_additive_filename, std::ios::in | std::ios::out | std::ios::trunc)); - return Succeeded::yes; -} + temp_additive_projdata->fill(*scatter_estimate_sptr); + if (!is_null_ptr(this->back_projdata_sptr)) { + add_proj_data(*temp_additive_projdata, *this->back_projdata_sptr); + } -void -ScatterEstimation:: -reconstruct_iterative(int _current_iter_num, - shared_ptr > & _current_estimate_sptr) -{ + this->multiplicative_binnorm_sptr->apply(*temp_additive_projdata); + } + } - shared_ptr > > tmp_iterative = - dynamic_pointer_cast > >(reconstruction_template_sptr); + // In the additive put the scaled scatter estimate + // If we have randoms, then add them to the scaled scatter estimate + // Then normalise + if (run_in_2d_projdata) { + this->add_projdata_2d_sptr->fill(*scaled_est_projdata_sptr); - // - // Now, we can call Reconstruction::set_up(). - if (tmp_iterative->set_up(this->current_activity_image_sptr) == Succeeded::no) - { - error("ScatterEstimation: Failure at set_up() of the reconstruction method. Aborting."); + if (!is_null_ptr(this->back_projdata_2d_sptr)) { + add_proj_data(*add_projdata_2d_sptr, *this->back_projdata_2d_sptr); + } + this->multiplicative_binnorm_2d_sptr->apply(*add_projdata_2d_sptr); + } else { + // TODO restructure code to move additive_projdata code from above + error("ScatterEstimation: You should not be here. This is not 2D."); } + current_activity_image_sptr->fill(1.f); - // return iterative_object->reconstruct(this->activity_image_lowres_sptr); - tmp_iterative->reconstruct(this->current_activity_image_sptr); + iterative_method ? reconstruct_iterative(i_scat_iter, this->current_activity_image_sptr) + : reconstruct_analytic(i_scat_iter, this->current_activity_image_sptr); - if(this->run_debug_mode) - { - std::stringstream convert; // stream used for the conversion - convert << "recon_" << _current_iter_num; - FilePath tmp(convert.str(),false); - tmp.prepend_directory_name(extras_path.get_path()); - OutputFileFormat >::default_sptr()-> - write_to_file(tmp.get_string(), *_current_estimate_sptr); + scatter_simulation_sptr->set_activity_image_sptr(current_activity_image_sptr); + } - } + info("ScatterEstimation: Scatter Estimation finished !!!"); + + return Succeeded::yes; } void -ScatterEstimation:: -reconstruct_analytic(int _current_iter_num, - shared_ptr > & _current_estimate_sptr) -{ - AnalyticReconstruction* analytic_object = - dynamic_cast (this->reconstruction_template_sptr.get()); - analytic_object->reconstruct(this->current_activity_image_sptr); - - if(this->run_debug_mode) - { - std::stringstream convert; // stream used for the conversion - convert << "recon_analytic_"<< _current_iter_num; - FilePath tmp(convert.str(),false); - tmp.prepend_directory_name(extras_path.get_path()); - OutputFileFormat >::default_sptr()-> - write_to_file(tmp.get_string(), *_current_estimate_sptr); - - } +ScatterEstimation::reconstruct_iterative(int _current_iter_num, + shared_ptr>& _current_estimate_sptr) { + + shared_ptr>> tmp_iterative = + dynamic_pointer_cast>>(reconstruction_template_sptr); + + // + // Now, we can call Reconstruction::set_up(). + if (tmp_iterative->set_up(this->current_activity_image_sptr) == Succeeded::no) { + error("ScatterEstimation: Failure at set_up() of the reconstruction method. Aborting."); + } + + // return iterative_object->reconstruct(this->activity_image_lowres_sptr); + tmp_iterative->reconstruct(this->current_activity_image_sptr); + + if (this->run_debug_mode) { + std::stringstream convert; // stream used for the conversion + convert << "recon_" << _current_iter_num; + FilePath tmp(convert.str(), false); + tmp.prepend_directory_name(extras_path.get_path()); + OutputFileFormat>::default_sptr()->write_to_file(tmp.get_string(), *_current_estimate_sptr); + } +} - //TODO: threshold ... to cut the negative values +void +ScatterEstimation::reconstruct_analytic(int _current_iter_num, shared_ptr>& _current_estimate_sptr) { + AnalyticReconstruction* analytic_object = dynamic_cast(this->reconstruction_template_sptr.get()); + analytic_object->reconstruct(this->current_activity_image_sptr); + + if (this->run_debug_mode) { + std::stringstream convert; // stream used for the conversion + convert << "recon_analytic_" << _current_iter_num; + FilePath tmp(convert.str(), false); + tmp.prepend_directory_name(extras_path.get_path()); + OutputFileFormat>::default_sptr()->write_to_file(tmp.get_string(), *_current_estimate_sptr); + } + + // TODO: threshold ... to cut the negative values } /****************** functions to help **********************/ void -ScatterEstimation:: -add_proj_data(ProjData& first_addend, const ProjData& second_addend) -{ - assert(first_addend.get_min_segment_num() == second_addend.get_min_segment_num()); - assert(first_addend.get_max_segment_num() == second_addend.get_max_segment_num()); - for (int segment_num = first_addend.get_min_segment_num(); - segment_num <= first_addend.get_max_segment_num(); - ++segment_num) - { - SegmentByView first_segment_by_view = - first_addend.get_segment_by_view(segment_num); +ScatterEstimation::add_proj_data(ProjData& first_addend, const ProjData& second_addend) { + assert(first_addend.get_min_segment_num() == second_addend.get_min_segment_num()); + assert(first_addend.get_max_segment_num() == second_addend.get_max_segment_num()); + for (int segment_num = first_addend.get_min_segment_num(); segment_num <= first_addend.get_max_segment_num(); ++segment_num) { + SegmentByView first_segment_by_view = first_addend.get_segment_by_view(segment_num); - SegmentByView sec_segment_by_view = - second_addend.get_segment_by_view(segment_num); + SegmentByView sec_segment_by_view = second_addend.get_segment_by_view(segment_num); - first_segment_by_view += sec_segment_by_view; + first_segment_by_view += sec_segment_by_view; - if (!(first_addend.set_segment(first_segment_by_view) == Succeeded::yes)) - { - error("Error set_segment %d", segment_num); - } + if (!(first_addend.set_segment(first_segment_by_view) == Succeeded::yes)) { + error("Error set_segment %d", segment_num); } + } } void -ScatterEstimation:: -subtract_proj_data(ProjData& minuend, const ProjData& subtracted) -{ - assert(minuend.get_min_segment_num() == subtracted.get_min_segment_num()); - assert(minuend.get_max_segment_num() == subtracted.get_max_segment_num()); - for (int segment_num = minuend.get_min_segment_num(); - segment_num <= minuend.get_max_segment_num(); - ++segment_num) - { - SegmentByView first_segment_by_view = - minuend.get_segment_by_view(segment_num); +ScatterEstimation::subtract_proj_data(ProjData& minuend, const ProjData& subtracted) { + assert(minuend.get_min_segment_num() == subtracted.get_min_segment_num()); + assert(minuend.get_max_segment_num() == subtracted.get_max_segment_num()); + for (int segment_num = minuend.get_min_segment_num(); segment_num <= minuend.get_max_segment_num(); ++segment_num) { + SegmentByView first_segment_by_view = minuend.get_segment_by_view(segment_num); - SegmentByView sec_segment_by_view = - subtracted.get_segment_by_view(segment_num); + SegmentByView sec_segment_by_view = subtracted.get_segment_by_view(segment_num); - first_segment_by_view -= sec_segment_by_view; + first_segment_by_view -= sec_segment_by_view; - if (!(minuend.set_segment(first_segment_by_view) == Succeeded::yes)) - { - error("ScatterEstimation: Error set_segment %d", segment_num); - } + if (!(minuend.set_segment(first_segment_by_view) == Succeeded::yes)) { + error("ScatterEstimation: Error set_segment %d", segment_num); } + } - // Filter negative values: - // pow_times_add zero_threshold (0.0f, 1.0f, 1.0f, 0.0f, NumericInfo().max_value()); - // apply_to_proj_data(minuend, zero_threshold); + // Filter negative values: + // pow_times_add zero_threshold (0.0f, 1.0f, 1.0f, 0.0f, NumericInfo().max_value()); + // apply_to_proj_data(minuend, zero_threshold); } void -ScatterEstimation:: -apply_to_proj_data(ProjData& data, const pow_times_add& func) -{ - for (int segment_num = data.get_min_segment_num(); - segment_num <= data.get_max_segment_num(); - ++segment_num) - { - SegmentByView segment_by_view = - data.get_segment_by_view(segment_num); +ScatterEstimation::apply_to_proj_data(ProjData& data, const pow_times_add& func) { + for (int segment_num = data.get_min_segment_num(); segment_num <= data.get_max_segment_num(); ++segment_num) { + SegmentByView segment_by_view = data.get_segment_by_view(segment_num); - in_place_apply_function(segment_by_view, - func); + in_place_apply_function(segment_by_view, func); - if (!(data.set_segment(segment_by_view) == Succeeded::yes)) - { - error("ScatterEstimation: Error set_segment %d", segment_num); - } + if (!(data.set_segment(segment_by_view) == Succeeded::yes)) { + error("ScatterEstimation: Error set_segment %d", segment_num); } + } } Succeeded -ScatterEstimation::project_mask_image() -{ - if (is_null_ptr(this->mask_image_sptr)) - { - warning("You cannot forward project if you have not set the mask image. Aborting."); - return Succeeded::no; +ScatterEstimation::project_mask_image() { + if (is_null_ptr(this->mask_image_sptr)) { + warning("You cannot forward project if you have not set the mask image. Aborting."); + return Succeeded::no; + } + + if (run_in_2d_projdata) { + if (is_null_ptr(this->input_projdata_2d_sptr)) { + warning("No 2D proj_data have been initialised. Aborting."); + return Succeeded::no; } - - if (run_in_2d_projdata) - { - if (is_null_ptr(this->input_projdata_2d_sptr)) - { - warning("No 2D proj_data have been initialised. Aborting."); - return Succeeded::no; - } - } - else - { - if (is_null_ptr(this->input_projdata_sptr)) - { - warning("No 3D proj_data have been initialised. Aborting."); - return Succeeded::no; - } + } else { + if (is_null_ptr(this->input_projdata_sptr)) { + warning("No 3D proj_data have been initialised. Aborting."); + return Succeeded::no; } + } - shared_ptr forw_projector_sptr; - shared_ptr PM(new ProjMatrixByBinUsingRayTracing()); - forw_projector_sptr.reset(new ForwardProjectorByBinUsingProjMatrixByBin(PM)); - info(boost::format("ScatterEstimation: Forward projector used for the calculation of " - "the tail mask: %1%") % forw_projector_sptr->parameter_info()); + shared_ptr forw_projector_sptr; + shared_ptr PM(new ProjMatrixByBinUsingRayTracing()); + forw_projector_sptr.reset(new ForwardProjectorByBinUsingProjMatrixByBin(PM)); + info(boost::format("ScatterEstimation: Forward projector used for the calculation of " + "the tail mask: %1%") % + forw_projector_sptr->parameter_info()); - shared_ptr mask_projdata; - if(run_in_2d_projdata) - { - forw_projector_sptr->set_up(this->input_projdata_2d_sptr->get_proj_data_info_sptr(), - this->mask_image_sptr ); + shared_ptr mask_projdata; + if (run_in_2d_projdata) { + forw_projector_sptr->set_up(this->input_projdata_2d_sptr->get_proj_data_info_sptr(), this->mask_image_sptr); - mask_projdata.reset(new ProjDataInMemory(this->input_projdata_2d_sptr->get_exam_info_sptr(), - this->input_projdata_2d_sptr->get_proj_data_info_sptr())); - } - else - { - forw_projector_sptr->set_up(this->input_projdata_sptr->get_proj_data_info_sptr(), - this->mask_image_sptr ); + mask_projdata.reset(new ProjDataInMemory(this->input_projdata_2d_sptr->get_exam_info_sptr(), + this->input_projdata_2d_sptr->get_proj_data_info_sptr())); + } else { + forw_projector_sptr->set_up(this->input_projdata_sptr->get_proj_data_info_sptr(), this->mask_image_sptr); - mask_projdata.reset(new ProjDataInMemory(this->input_projdata_sptr->get_exam_info_sptr(), - this->input_projdata_sptr->get_proj_data_info_sptr())); - } + mask_projdata.reset(new ProjDataInMemory(this->input_projdata_sptr->get_exam_info_sptr(), + this->input_projdata_sptr->get_proj_data_info_sptr())); + } - forw_projector_sptr->forward_project(*mask_projdata, *this->mask_image_sptr); + forw_projector_sptr->forward_project(*mask_projdata, *this->mask_image_sptr); - //add 1 to be able to use create_tail_mask_from_ACFs (which expects ACFs, - //so complains if the threshold is too low) + // add 1 to be able to use create_tail_mask_from_ACFs (which expects ACFs, + // so complains if the threshold is too low) - pow_times_add pow_times_add_object(1.0f, 1.0f, 1.0f,NumericInfo().min_value(), - NumericInfo().max_value()); + pow_times_add pow_times_add_object(1.0f, 1.0f, 1.0f, NumericInfo().min_value(), NumericInfo().max_value()); - // I have only one segment I could remove this. - for (int segment_num = mask_projdata->get_min_segment_num(); - segment_num <= mask_projdata->get_max_segment_num(); - ++segment_num) - { - SegmentByView segment_by_view = - mask_projdata->get_segment_by_view(segment_num); + // I have only one segment I could remove this. + for (int segment_num = mask_projdata->get_min_segment_num(); segment_num <= mask_projdata->get_max_segment_num(); + ++segment_num) { + SegmentByView segment_by_view = mask_projdata->get_segment_by_view(segment_num); - in_place_apply_function(segment_by_view, - pow_times_add_object); + in_place_apply_function(segment_by_view, pow_times_add_object); - if (!(mask_projdata->set_segment(segment_by_view) == Succeeded::yes)) - { - warning("ScatterEstimation: Error set_segment %d", segment_num); - return Succeeded::no; - } + if (!(mask_projdata->set_segment(segment_by_view) == Succeeded::yes)) { + warning("ScatterEstimation: Error set_segment %d", segment_num); + return Succeeded::no; } + } - if (this->mask_projdata_filename.size() > 0) - this->mask_projdata_sptr.reset(new ProjDataInterfile(mask_projdata->get_exam_info_sptr(), - mask_projdata->get_proj_data_info_sptr(), - this->mask_projdata_filename, - std::ios::in | std::ios::out | std::ios::trunc)); - else - this->mask_projdata_sptr.reset(new ProjDataInMemory(mask_projdata->get_exam_info_sptr(), - mask_projdata->get_proj_data_info_sptr())); - - CreateTailMaskFromACFs create_tail_mask_from_acfs; - - if(this->tail_mask_par_filename.empty()) - { - create_tail_mask_from_acfs.ACF_threshold = 1.1; - create_tail_mask_from_acfs.safety_margin = 4; - } - else - { - if(!create_tail_mask_from_acfs.parse(this->tail_mask_par_filename.c_str())) - error(boost::format("Error parsing parameters file %1%, for creating mask tails from ACFs.") - %this->tail_mask_par_filename); - } - - create_tail_mask_from_acfs.set_input_projdata_sptr(mask_projdata); - create_tail_mask_from_acfs.set_output_projdata_sptr(this->mask_projdata_sptr); - return create_tail_mask_from_acfs.process_data(); + if (this->mask_projdata_filename.size() > 0) + this->mask_projdata_sptr.reset(new ProjDataInterfile(mask_projdata->get_exam_info_sptr(), + mask_projdata->get_proj_data_info_sptr(), this->mask_projdata_filename, + std::ios::in | std::ios::out | std::ios::trunc)); + else + this->mask_projdata_sptr.reset( + new ProjDataInMemory(mask_projdata->get_exam_info_sptr(), mask_projdata->get_proj_data_info_sptr())); + + CreateTailMaskFromACFs create_tail_mask_from_acfs; + + if (this->tail_mask_par_filename.empty()) { + create_tail_mask_from_acfs.ACF_threshold = 1.1; + create_tail_mask_from_acfs.safety_margin = 4; + } else { + if (!create_tail_mask_from_acfs.parse(this->tail_mask_par_filename.c_str())) + error(boost::format("Error parsing parameters file %1%, for creating mask tails from ACFs.") % + this->tail_mask_par_filename); + } + + create_tail_mask_from_acfs.set_input_projdata_sptr(mask_projdata); + create_tail_mask_from_acfs.set_output_projdata_sptr(this->mask_projdata_sptr); + return create_tail_mask_from_acfs.process_data(); } void -ScatterEstimation:: -apply_mask_in_place(DiscretisedDensity<3, float>& arg, - const MaskingParameters& masking_parameters) -{ - if (!is_null_ptr(masking_parameters.filter_sptr)) - { - masking_parameters.filter_sptr->process_data(arg); - } +ScatterEstimation::apply_mask_in_place(DiscretisedDensity<3, float>& arg, const MaskingParameters& masking_parameters) { + if (!is_null_ptr(masking_parameters.filter_sptr)) { + masking_parameters.filter_sptr->process_data(arg); + } // min threshold - for (DiscretisedDensity<3,float>::full_iterator iter = arg.begin_all(); iter != arg.end_all(); ++iter) - { - if (*iter < masking_parameters.min_threshold) - *iter = 0.F; - else - *iter = 1.F; - } + for (DiscretisedDensity<3, float>::full_iterator iter = arg.begin_all(); iter != arg.end_all(); ++iter) { + if (*iter < masking_parameters.min_threshold) + *iter = 0.F; + else + *iter = 1.F; + } } -int ScatterEstimation::get_num_iterations() const -{ - return num_scatter_iterations; +int +ScatterEstimation::get_num_iterations() const { + return num_scatter_iterations; } // deprecated version -int ScatterEstimation::get_iterations_num() const -{ - return num_scatter_iterations; +int +ScatterEstimation::get_iterations_num() const { + return num_scatter_iterations; } void -ScatterEstimation::create_multiplicative_binnorm_sptr() -{ - if (!is_null_ptr(this->multiplicative_binnorm_sptr)) - { - if (!is_null_ptr(this->norm_3d_sptr)) - error("ScatterEstimation: cannot handle having both norm and 'combined norm' initialised"); - if (!is_null_ptr(this->atten_norm_3d_sptr)) - error("ScatterEstimation: cannot handle having both attenuation and 'combined norm' initialised"); +ScatterEstimation::create_multiplicative_binnorm_sptr() { + if (!is_null_ptr(this->multiplicative_binnorm_sptr)) { + if (!is_null_ptr(this->norm_3d_sptr)) + error("ScatterEstimation: cannot handle having both norm and 'combined norm' initialised"); + if (!is_null_ptr(this->atten_norm_3d_sptr)) + error("ScatterEstimation: cannot handle having both attenuation and 'combined norm' initialised"); + } else { + if (is_null_ptr(this->atten_norm_3d_sptr)) { + error("ScatterEstimation: need attenuation correction factors to be set (sorry)"); } - else - { - if (is_null_ptr(this->atten_norm_3d_sptr)) - { - error("ScatterEstimation: need attenuation correction factors to be set (sorry)"); - } - if (is_null_ptr(this->norm_3d_sptr)) - { - warning("ScatterEstimation: no normalisation data set. This would only be appropriate for simple simulations."); - this->norm_3d_sptr = this->atten_norm_3d_sptr; - } - else - { - this->multiplicative_binnorm_sptr.reset(new ChainedBinNormalisation(norm_3d_sptr, atten_norm_3d_sptr)); - } + if (is_null_ptr(this->norm_3d_sptr)) { + warning("ScatterEstimation: no normalisation data set. This would only be appropriate for simple simulations."); + this->norm_3d_sptr = this->atten_norm_3d_sptr; + } else { + this->multiplicative_binnorm_sptr.reset(new ChainedBinNormalisation(norm_3d_sptr, atten_norm_3d_sptr)); } + } } shared_ptr -ScatterEstimation::get_normalisation_object_sptr(const shared_ptr& combined_norm_sptr) const -{ - const ChainedBinNormalisation* tmp_chain_norm_sptr = - dynamic_cast(combined_norm_sptr.get()); - - if (!is_null_ptr(tmp_chain_norm_sptr )) - { - return tmp_chain_norm_sptr->get_first_norm(); - } - else //Just trivial, then .. - { - shared_ptr normalisation_factors_sptr(new TrivialBinNormalisation()); - normalisation_factors_sptr->set_up(this->input_projdata_sptr->get_exam_info_sptr(), this->input_projdata_sptr->get_proj_data_info_sptr()); - return normalisation_factors_sptr; - } +ScatterEstimation::get_normalisation_object_sptr(const shared_ptr& combined_norm_sptr) const { + const ChainedBinNormalisation* tmp_chain_norm_sptr = dynamic_cast(combined_norm_sptr.get()); + + if (!is_null_ptr(tmp_chain_norm_sptr)) { + return tmp_chain_norm_sptr->get_first_norm(); + } else // Just trivial, then .. + { + shared_ptr normalisation_factors_sptr(new TrivialBinNormalisation()); + normalisation_factors_sptr->set_up(this->input_projdata_sptr->get_exam_info_sptr(), + this->input_projdata_sptr->get_proj_data_info_sptr()); + return normalisation_factors_sptr; + } } shared_ptr -ScatterEstimation::get_attenuation_correction_factors_sptr(const shared_ptr& combined_norm_sptr) const -{ - const ChainedBinNormalisation* tmp_chain_norm_sptr = - dynamic_cast(combined_norm_sptr.get()); +ScatterEstimation::get_attenuation_correction_factors_sptr(const shared_ptr& combined_norm_sptr) const { + const ChainedBinNormalisation* tmp_chain_norm_sptr = dynamic_cast(combined_norm_sptr.get()); shared_ptr atten_norm_sptr; - if (!is_null_ptr(tmp_chain_norm_sptr )) - { - atten_norm_sptr = tmp_chain_norm_sptr->get_second_norm(); - } - else - { - atten_norm_sptr = combined_norm_sptr; - } - - return - dynamic_cast (atten_norm_sptr.get())->get_norm_proj_data_sptr(); + if (!is_null_ptr(tmp_chain_norm_sptr)) { + atten_norm_sptr = tmp_chain_norm_sptr->get_second_norm(); + } else { + atten_norm_sptr = combined_norm_sptr; + } + return dynamic_cast(atten_norm_sptr.get())->get_norm_proj_data_sptr(); } -shared_ptr ScatterEstimation::create_new_proj_data(const std::string& filename, - const shared_ptr exam_info_sptr, - const shared_ptr proj_data_info_sptr) const -{ - shared_ptr pd_sptr; - if (run_debug_mode) - { - FilePath tmp(filename, false); - tmp = tmp.get_filename(); // get rid of any folder info - tmp.prepend_directory_name(extras_path.get_path()); - pd_sptr.reset(new ProjDataInterfile(exam_info_sptr, - proj_data_info_sptr, - tmp.get_string(), - std::ios::in | std::ios::out | std::ios::trunc)); - } - else - { - pd_sptr.reset(new ProjDataInMemory(exam_info_sptr, proj_data_info_sptr)); - } - return pd_sptr; +shared_ptr +ScatterEstimation::create_new_proj_data(const std::string& filename, const shared_ptr exam_info_sptr, + const shared_ptr proj_data_info_sptr) const { + shared_ptr pd_sptr; + if (run_debug_mode) { + FilePath tmp(filename, false); + tmp = tmp.get_filename(); // get rid of any folder info + tmp.prepend_directory_name(extras_path.get_path()); + pd_sptr.reset(new ProjDataInterfile(exam_info_sptr, proj_data_info_sptr, tmp.get_string(), + std::ios::in | std::ios::out | std::ios::trunc)); + } else { + pd_sptr.reset(new ProjDataInMemory(exam_info_sptr, proj_data_info_sptr)); + } + return pd_sptr; } END_NAMESPACE_STIR diff --git a/src/scatter_buildblock/ScatterSimulation.cxx b/src/scatter_buildblock/ScatterSimulation.cxx index 71bb183cd6..1ef62c204a 100644 --- a/src/scatter_buildblock/ScatterSimulation.cxx +++ b/src/scatter_buildblock/ScatterSimulation.cxx @@ -53,565 +53,472 @@ START_NAMESPACE_STIR -ScatterSimulation:: -ScatterSimulation() -{ - this->set_defaults(); -} +ScatterSimulation::ScatterSimulation() { this->set_defaults(); } -ScatterSimulation:: -~ScatterSimulation() -{ - // Sometimes I get a segfault without this line. - scatt_points_vector.clear(); +ScatterSimulation::~ScatterSimulation() { + // Sometimes I get a segfault without this line. + scatt_points_vector.clear(); } Succeeded -ScatterSimulation:: -process_data() -{ - if (!this->_already_set_up) - error("ScatterSimulation: need to call set_up() first"); - if(is_null_ptr(output_proj_data_sptr)) - error("ScatterSimulation: output projection data not set. Aborting."); - - // this is useful in the scatter estimation process. - this->output_proj_data_sptr->fill(0.f); - // check if output has same info as templates - { - if ((*output_proj_data_sptr->get_proj_data_info_sptr()) != - (*this->get_template_proj_data_info_sptr())) - error("ScatterSimulation: output projection data incompatible with what was used for set_up()"); - // TODO enable check on exam_info but this has no operator== yet - } - info("ScatterSimulator: Running Scatter Simulation ..."); - info("ScatterSimulator: Initialising ..."); - - ViewSegmentNumbers vs_num; - /* ////////////////// SCATTER ESTIMATION TIME //////////////// */ - CPUTimer bin_timer; - bin_timer.start(); - // variables to report (remaining) time - HighResWallClockTimer wall_clock_timer; - double previous_timer = 0 ; - int previous_bin_count = 0 ; - int bin_counter = 0; - int axial_bins = 0 ; - wall_clock_timer.start(); - - for (vs_num.segment_num() = this->proj_data_info_cyl_noarc_cor_sptr->get_min_segment_num(); - vs_num.segment_num() <= this->proj_data_info_cyl_noarc_cor_sptr->get_max_segment_num(); - ++vs_num.segment_num()) - axial_bins += this->proj_data_info_cyl_noarc_cor_sptr->get_num_axial_poss(vs_num.segment_num()); - - const int total_bins = - this->proj_data_info_cyl_noarc_cor_sptr->get_num_views() * axial_bins * - this->proj_data_info_cyl_noarc_cor_sptr->get_num_tangential_poss(); - /* ////////////////// end SCATTER ESTIMATION TIME //////////////// */ - float total_scatter = 0 ; - - info("ScatterSimulator: Initialization finished ..."); - - for (vs_num.segment_num() = this->proj_data_info_cyl_noarc_cor_sptr->get_min_segment_num(); - vs_num.segment_num() <= this->proj_data_info_cyl_noarc_cor_sptr->get_max_segment_num(); - ++vs_num.segment_num()) - { - for (vs_num.view_num() = this->proj_data_info_cyl_noarc_cor_sptr->get_min_view_num(); - vs_num.view_num() <= this->proj_data_info_cyl_noarc_cor_sptr->get_max_view_num(); - ++vs_num.view_num()) - { - total_scatter += this->process_data_for_view_segment_num(vs_num); - bin_counter += - this->proj_data_info_cyl_noarc_cor_sptr->get_num_axial_poss(vs_num.segment_num()) * - this->proj_data_info_cyl_noarc_cor_sptr->get_num_tangential_poss(); - /* ////////////////// SCATTER ESTIMATION TIME ////////////////*/ - { - wall_clock_timer.stop(); // must be stopped before getting the value - info(boost::format("%1$5u / %2% bins done. Total time elapsed %3$5.2f secs, remaining about %4$5.2f mins (ignoring caching).") - % bin_counter % total_bins - % wall_clock_timer.value() - % ((wall_clock_timer.value() - previous_timer) - * (total_bins - bin_counter) / (bin_counter - previous_bin_count) / 60), - /* verbosity level*/ 3); - previous_timer = wall_clock_timer.value() ; - previous_bin_count = bin_counter ; - wall_clock_timer.start(); - } - /* ////////////////// end SCATTER ESTIMATION TIME //////////////// */ - } +ScatterSimulation::process_data() { + if (!this->_already_set_up) + error("ScatterSimulation: need to call set_up() first"); + if (is_null_ptr(output_proj_data_sptr)) + error("ScatterSimulation: output projection data not set. Aborting."); + + // this is useful in the scatter estimation process. + this->output_proj_data_sptr->fill(0.f); + // check if output has same info as templates + { + if ((*output_proj_data_sptr->get_proj_data_info_sptr()) != (*this->get_template_proj_data_info_sptr())) + error("ScatterSimulation: output projection data incompatible with what was used for set_up()"); + // TODO enable check on exam_info but this has no operator== yet + } + info("ScatterSimulator: Running Scatter Simulation ..."); + info("ScatterSimulator: Initialising ..."); + + ViewSegmentNumbers vs_num; + /* ////////////////// SCATTER ESTIMATION TIME //////////////// */ + CPUTimer bin_timer; + bin_timer.start(); + // variables to report (remaining) time + HighResWallClockTimer wall_clock_timer; + double previous_timer = 0; + int previous_bin_count = 0; + int bin_counter = 0; + int axial_bins = 0; + wall_clock_timer.start(); + + for (vs_num.segment_num() = this->proj_data_info_cyl_noarc_cor_sptr->get_min_segment_num(); + vs_num.segment_num() <= this->proj_data_info_cyl_noarc_cor_sptr->get_max_segment_num(); ++vs_num.segment_num()) + axial_bins += this->proj_data_info_cyl_noarc_cor_sptr->get_num_axial_poss(vs_num.segment_num()); + + const int total_bins = this->proj_data_info_cyl_noarc_cor_sptr->get_num_views() * axial_bins * + this->proj_data_info_cyl_noarc_cor_sptr->get_num_tangential_poss(); + /* ////////////////// end SCATTER ESTIMATION TIME //////////////// */ + float total_scatter = 0; + + info("ScatterSimulator: Initialization finished ..."); + + for (vs_num.segment_num() = this->proj_data_info_cyl_noarc_cor_sptr->get_min_segment_num(); + vs_num.segment_num() <= this->proj_data_info_cyl_noarc_cor_sptr->get_max_segment_num(); ++vs_num.segment_num()) { + for (vs_num.view_num() = this->proj_data_info_cyl_noarc_cor_sptr->get_min_view_num(); + vs_num.view_num() <= this->proj_data_info_cyl_noarc_cor_sptr->get_max_view_num(); ++vs_num.view_num()) { + total_scatter += this->process_data_for_view_segment_num(vs_num); + bin_counter += this->proj_data_info_cyl_noarc_cor_sptr->get_num_axial_poss(vs_num.segment_num()) * + this->proj_data_info_cyl_noarc_cor_sptr->get_num_tangential_poss(); + /* ////////////////// SCATTER ESTIMATION TIME ////////////////*/ + { + wall_clock_timer.stop(); // must be stopped before getting the value + info(boost::format( + "%1$5u / %2% bins done. Total time elapsed %3$5.2f secs, remaining about %4$5.2f mins (ignoring caching).") % + bin_counter % total_bins % wall_clock_timer.value() % + ((wall_clock_timer.value() - previous_timer) * (total_bins - bin_counter) / (bin_counter - previous_bin_count) / + 60), + /* verbosity level*/ 3); + previous_timer = wall_clock_timer.value(); + previous_bin_count = bin_counter; + wall_clock_timer.start(); + } + /* ////////////////// end SCATTER ESTIMATION TIME //////////////// */ } + } - bin_timer.stop(); - wall_clock_timer.stop(); + bin_timer.stop(); + wall_clock_timer.stop(); - if (detection_points_vector.size() != static_cast(total_detectors)) - { - warning("Expected num detectors: %d, but found %d\n", - total_detectors, detection_points_vector.size()); - return Succeeded::no; - } + if (detection_points_vector.size() != static_cast(total_detectors)) { + warning("Expected num detectors: %d, but found %d\n", total_detectors, detection_points_vector.size()); + return Succeeded::no; + } - info(boost::format("TOTAL SCATTER counts before upsampling and norm = %g") % total_scatter); - this->write_log(wall_clock_timer.value(), total_scatter); - return Succeeded::yes; + info(boost::format("TOTAL SCATTER counts before upsampling and norm = %g") % total_scatter); + this->write_log(wall_clock_timer.value(), total_scatter); + return Succeeded::yes; } double -ScatterSimulation:: -process_data_for_view_segment_num(const ViewSegmentNumbers& vs_num) -{ - // First construct a vector of all bins that we'll process. - // The reason for making this list before the actual calculation is that we can then parallelise over all bins - // without having to think about double loops. - std::vector all_bins; - { - Bin bin(vs_num.segment_num(), vs_num.view_num(), 0, 0); - - for (bin.axial_pos_num() = this->proj_data_info_cyl_noarc_cor_sptr->get_min_axial_pos_num(bin.segment_num()); - bin.axial_pos_num() <= this->proj_data_info_cyl_noarc_cor_sptr->get_max_axial_pos_num(bin.segment_num()); - ++bin.axial_pos_num()) - { - for (bin.tangential_pos_num() = this->proj_data_info_cyl_noarc_cor_sptr->get_min_tangential_pos_num(); - bin.tangential_pos_num() <= this->proj_data_info_cyl_noarc_cor_sptr->get_max_tangential_pos_num(); - ++bin.tangential_pos_num()) - { - all_bins.push_back(bin); - } - } +ScatterSimulation::process_data_for_view_segment_num(const ViewSegmentNumbers& vs_num) { + // First construct a vector of all bins that we'll process. + // The reason for making this list before the actual calculation is that we can then parallelise over all bins + // without having to think about double loops. + std::vector all_bins; + { + Bin bin(vs_num.segment_num(), vs_num.view_num(), 0, 0); + + for (bin.axial_pos_num() = this->proj_data_info_cyl_noarc_cor_sptr->get_min_axial_pos_num(bin.segment_num()); + bin.axial_pos_num() <= this->proj_data_info_cyl_noarc_cor_sptr->get_max_axial_pos_num(bin.segment_num()); + ++bin.axial_pos_num()) { + for (bin.tangential_pos_num() = this->proj_data_info_cyl_noarc_cor_sptr->get_min_tangential_pos_num(); + bin.tangential_pos_num() <= this->proj_data_info_cyl_noarc_cor_sptr->get_max_tangential_pos_num(); + ++bin.tangential_pos_num()) { + all_bins.push_back(bin); + } } + } - // now compute scatter for all bins - double total_scatter = 0.; - Viewgram viewgram = - this->output_proj_data_sptr->get_empty_viewgram(vs_num.view_num(), vs_num.segment_num()); + // now compute scatter for all bins + double total_scatter = 0.; + Viewgram viewgram = this->output_proj_data_sptr->get_empty_viewgram(vs_num.view_num(), vs_num.segment_num()); #ifdef STIR_OPENMP -#pragma omp parallel for reduction(+:total_scatter) schedule(dynamic) +# pragma omp parallel for reduction(+ : total_scatter) schedule(dynamic) #endif - for (int i = 0; i < static_cast(all_bins.size()); ++i) - { - const Bin bin = all_bins[i]; - const double scatter_ratio = scatter_estimate(bin); + for (int i = 0; i < static_cast(all_bins.size()); ++i) { + const Bin bin = all_bins[i]; + const double scatter_ratio = scatter_estimate(bin); -#if defined STIR_OPENMP +#if defined STIR_OPENMP # if _OPENMP >= 201107 # pragma omp atomic write # else # pragma omp critical(ScatterSimulationByBin_process_data_for_view_segment_num) # endif #endif - viewgram[bin.axial_pos_num()][bin.tangential_pos_num()] = - static_cast(scatter_ratio); - total_scatter += static_cast(scatter_ratio); - } // end loop over bins + viewgram[bin.axial_pos_num()][bin.tangential_pos_num()] = static_cast(scatter_ratio); + total_scatter += static_cast(scatter_ratio); + } // end loop over bins - if (this->output_proj_data_sptr->set_viewgram(viewgram) == Succeeded::no) - error("ScatterSimulation: error writing viewgram"); + if (this->output_proj_data_sptr->set_viewgram(viewgram) == Succeeded::no) + error("ScatterSimulation: error writing viewgram"); - return total_scatter; + return total_scatter; } void -ScatterSimulation::set_defaults() -{ - this->attenuation_threshold = 0.01f ; - this->randomly_place_scatter_points = true; - this->use_cache = true; - this->zoom_xy = -1.f; - this->zoom_z = -1.f; - this->zoom_size_xy = -1; - this->zoom_size_z = -1; - this->downsample_scanner_bool = false; - this->downsample_scanner_dets = 64; - this->downsample_scanner_rings = -1; - this->density_image_filename = ""; - this->activity_image_filename = ""; - this->density_image_for_scatter_points_output_filename =""; - this->density_image_for_scatter_points_filename = ""; - this->template_proj_data_filename = ""; - this->remove_cache_for_integrals_over_activity(); - this->remove_cache_for_integrals_over_attenuation(); - this->_already_set_up = false; +ScatterSimulation::set_defaults() { + this->attenuation_threshold = 0.01f; + this->randomly_place_scatter_points = true; + this->use_cache = true; + this->zoom_xy = -1.f; + this->zoom_z = -1.f; + this->zoom_size_xy = -1; + this->zoom_size_z = -1; + this->downsample_scanner_bool = false; + this->downsample_scanner_dets = 64; + this->downsample_scanner_rings = -1; + this->density_image_filename = ""; + this->activity_image_filename = ""; + this->density_image_for_scatter_points_output_filename = ""; + this->density_image_for_scatter_points_filename = ""; + this->template_proj_data_filename = ""; + this->remove_cache_for_integrals_over_activity(); + this->remove_cache_for_integrals_over_attenuation(); + this->_already_set_up = false; } void -ScatterSimulation:: -ask_parameters() -{ - this->attenuation_threshold = ask_num("attenuation threshold(cm^-1)",0.0f, 5.0f, 0.01f); - this->randomly_place_scatter_points = ask_num("random place scatter points?",0, 1, 1); - this->use_cache = ask_num(" Use cache?",0, 1, 1); - this->density_image_filename = ask_string("density image filename", ""); - this->activity_image_filename = ask_string("activity image filename", ""); - //this->density_image_for_scatter_points_filename = ask_string("density image for scatter points filename", ""); - this->template_proj_data_filename = ask_string("Scanner ProjData filename", ""); +ScatterSimulation::ask_parameters() { + this->attenuation_threshold = ask_num("attenuation threshold(cm^-1)", 0.0f, 5.0f, 0.01f); + this->randomly_place_scatter_points = ask_num("random place scatter points?", 0, 1, 1); + this->use_cache = ask_num(" Use cache?", 0, 1, 1); + this->density_image_filename = ask_string("density image filename", ""); + this->activity_image_filename = ask_string("activity image filename", ""); + // this->density_image_for_scatter_points_filename = ask_string("density image for scatter points filename", ""); + this->template_proj_data_filename = ask_string("Scanner ProjData filename", ""); } void -ScatterSimulation::initialise_keymap() -{ - - // this->parser.add_start_key("Scatter Simulation Parameters"); - // this->parser.add_stop_key("end Scatter Simulation Parameters"); - this->parser.add_key("template projdata filename", - &this->template_proj_data_filename); - this->parser.add_key("attenuation image filename", - &this->density_image_filename); - this->parser.add_key("attenuation image for scatter points filename", - &this->density_image_for_scatter_points_filename); - this->parser.add_key("zoom XY for attenuation image for scatter points", - &this->zoom_xy); - this->parser.add_key("zoom Z for attenuation image for scatter points", - &this->zoom_z); - this->parser.add_key("XY size of downsampled image for scatter points", - &this->zoom_size_xy); - this->parser.add_key("Z size of downsampled image for scatter points", - &this->zoom_size_z); - this->parser.add_key("attenuation image for scatter points output filename", - &this->density_image_for_scatter_points_output_filename); - this->parser.add_key("downsampled scanner number of detectors per ring", - &this->downsample_scanner_dets); - this->parser.add_key("downsampled scanner number of rings", - &this->downsample_scanner_rings); - this->parser.add_key("activity image filename", - &this->activity_image_filename); - this->parser.add_key("attenuation threshold", - &this->attenuation_threshold); - this->parser.add_key("output filename prefix", - &this->output_proj_data_filename); - this->parser.add_key("downsample scanner", - &this->downsample_scanner_bool); - this->parser.add_key("randomly place scatter points", &this->randomly_place_scatter_points); - this->parser.add_key("use cache", &this->use_cache); +ScatterSimulation::initialise_keymap() { + + // this->parser.add_start_key("Scatter Simulation Parameters"); + // this->parser.add_stop_key("end Scatter Simulation Parameters"); + this->parser.add_key("template projdata filename", &this->template_proj_data_filename); + this->parser.add_key("attenuation image filename", &this->density_image_filename); + this->parser.add_key("attenuation image for scatter points filename", &this->density_image_for_scatter_points_filename); + this->parser.add_key("zoom XY for attenuation image for scatter points", &this->zoom_xy); + this->parser.add_key("zoom Z for attenuation image for scatter points", &this->zoom_z); + this->parser.add_key("XY size of downsampled image for scatter points", &this->zoom_size_xy); + this->parser.add_key("Z size of downsampled image for scatter points", &this->zoom_size_z); + this->parser.add_key("attenuation image for scatter points output filename", + &this->density_image_for_scatter_points_output_filename); + this->parser.add_key("downsampled scanner number of detectors per ring", &this->downsample_scanner_dets); + this->parser.add_key("downsampled scanner number of rings", &this->downsample_scanner_rings); + this->parser.add_key("activity image filename", &this->activity_image_filename); + this->parser.add_key("attenuation threshold", &this->attenuation_threshold); + this->parser.add_key("output filename prefix", &this->output_proj_data_filename); + this->parser.add_key("downsample scanner", &this->downsample_scanner_bool); + this->parser.add_key("randomly place scatter points", &this->randomly_place_scatter_points); + this->parser.add_key("use cache", &this->use_cache); } - bool -ScatterSimulation:: -post_processing() -{ +ScatterSimulation::post_processing() { - if (this->template_proj_data_filename.size() > 0) - this->set_template_proj_data_info(this->template_proj_data_filename); + if (this->template_proj_data_filename.size() > 0) + this->set_template_proj_data_info(this->template_proj_data_filename); - if (this->activity_image_filename.size() > 0) - this->set_activity_image(this->activity_image_filename); + if (this->activity_image_filename.size() > 0) + this->set_activity_image(this->activity_image_filename); - if (this->density_image_filename.size() > 0) - this->set_density_image(this->density_image_filename); + if (this->density_image_filename.size() > 0) + this->set_density_image(this->density_image_filename); - if(this->density_image_for_scatter_points_filename.size() > 0) - this->set_density_image_for_scatter_points(this->density_image_for_scatter_points_filename); + if (this->density_image_for_scatter_points_filename.size() > 0) + this->set_density_image_for_scatter_points(this->density_image_for_scatter_points_filename); - if (this->output_proj_data_filename.size() > 0) - this->set_output_proj_data(this->output_proj_data_filename); + if (this->output_proj_data_filename.size() > 0) + this->set_output_proj_data(this->output_proj_data_filename); - return false; + return false; } Succeeded -ScatterSimulation:: -set_up() -{ - if (is_null_ptr(proj_data_info_cyl_noarc_cor_sptr)) - error("ScatterSimulation: projection data info not set. Aborting."); - - if (!proj_data_info_cyl_noarc_cor_sptr->has_energy_information()) - error("ScatterSimulation: scanner energy resolution information not set. Aborting."); - - if (is_null_ptr(template_exam_info_sptr)) - error("ScatterSimulation: projection data info not set. Aborting."); - - if(!template_exam_info_sptr->has_energy_information()) - error("ScatterSimulation: template energy window information not set. Aborting."); - - if(is_null_ptr(activity_image_sptr)) - error("ScatterSimulation: activity image not set. Aborting."); - - if(is_null_ptr(density_image_sptr)) - error("ScatterSimulation: density image not set. Aborting."); - - if(downsample_scanner_bool) - { - if (this->_already_set_up) - error("ScatterSimulation: set_up() called twice. This is currently not supported."); - - downsample_scanner(); - } - - if(is_null_ptr(density_image_for_scatter_points_sptr)) - { - if (this->_already_set_up) - error("ScatterSimulation: set_up() called twice. This is currently not supported."); - downsample_density_image_for_scatter_points(zoom_xy, zoom_z, zoom_size_xy, zoom_size_z); - } - -// { -// this->output_proj_data_sptr.reset(new ProjDataInMemory(this->template_exam_info_sptr, -// this->proj_data_info_cyl_noarc_cor_sptr->create_shared_clone())); -// this->output_proj_data_sptr->fill(0.0); -// info("ScatterSimulation: output projection data created."); -// } - - - // Note: horrible shift used for detection_points_vector - /* Currently, proj_data_info.find_cartesian_coordinates_of_detection() returns - coordinate in a coordinate system where z=0 in the first ring of the scanner. - We want to shift this to a coordinate system where z=0 in the middle - of the scanner. - We can use get_m() as that uses the 'middle of the scanner' system. - (sorry) - */ +ScatterSimulation::set_up() { + if (is_null_ptr(proj_data_info_cyl_noarc_cor_sptr)) + error("ScatterSimulation: projection data info not set. Aborting."); + + if (!proj_data_info_cyl_noarc_cor_sptr->has_energy_information()) + error("ScatterSimulation: scanner energy resolution information not set. Aborting."); + + if (is_null_ptr(template_exam_info_sptr)) + error("ScatterSimulation: projection data info not set. Aborting."); + + if (!template_exam_info_sptr->has_energy_information()) + error("ScatterSimulation: template energy window information not set. Aborting."); + + if (is_null_ptr(activity_image_sptr)) + error("ScatterSimulation: activity image not set. Aborting."); + + if (is_null_ptr(density_image_sptr)) + error("ScatterSimulation: density image not set. Aborting."); + + if (downsample_scanner_bool) { + if (this->_already_set_up) + error("ScatterSimulation: set_up() called twice. This is currently not supported."); + + downsample_scanner(); + } + + if (is_null_ptr(density_image_for_scatter_points_sptr)) { + if (this->_already_set_up) + error("ScatterSimulation: set_up() called twice. This is currently not supported."); + downsample_density_image_for_scatter_points(zoom_xy, zoom_z, zoom_size_xy, zoom_size_z); + } + + // { + // this->output_proj_data_sptr.reset(new ProjDataInMemory(this->template_exam_info_sptr, + // this->proj_data_info_cyl_noarc_cor_sptr->create_shared_clone())); + // this->output_proj_data_sptr->fill(0.0); + // info("ScatterSimulation: output projection data created."); + // } + + // Note: horrible shift used for detection_points_vector + /* Currently, proj_data_info.find_cartesian_coordinates_of_detection() returns + coordinate in a coordinate system where z=0 in the first ring of the scanner. + We want to shift this to a coordinate system where z=0 in the middle + of the scanner. + We can use get_m() as that uses the 'middle of the scanner' system. + (sorry) +*/ #ifndef NDEBUG - { - CartesianCoordinate3D detector_coord_A, detector_coord_B; - // check above statement - this->proj_data_info_cyl_noarc_cor_sptr->find_cartesian_coordinates_of_detection( - detector_coord_A, detector_coord_B, Bin(0, 0, 0, 0)); - assert(detector_coord_A.z() == 0); - assert(detector_coord_B.z() == 0); - // check that get_m refers to the middle of the scanner - const float m_first = - this->proj_data_info_cyl_noarc_cor_sptr->get_m(Bin(0, 0, this->proj_data_info_cyl_noarc_cor_sptr->get_min_axial_pos_num(0), 0)); - const float m_last = - this->proj_data_info_cyl_noarc_cor_sptr->get_m(Bin(0, 0, this->proj_data_info_cyl_noarc_cor_sptr->get_max_axial_pos_num(0), 0)); - assert(fabs(m_last + m_first) < m_last * 10E-4); - } + { + CartesianCoordinate3D detector_coord_A, detector_coord_B; + // check above statement + this->proj_data_info_cyl_noarc_cor_sptr->find_cartesian_coordinates_of_detection(detector_coord_A, detector_coord_B, + Bin(0, 0, 0, 0)); + assert(detector_coord_A.z() == 0); + assert(detector_coord_B.z() == 0); + // check that get_m refers to the middle of the scanner + const float m_first = this->proj_data_info_cyl_noarc_cor_sptr->get_m( + Bin(0, 0, this->proj_data_info_cyl_noarc_cor_sptr->get_min_axial_pos_num(0), 0)); + const float m_last = this->proj_data_info_cyl_noarc_cor_sptr->get_m( + Bin(0, 0, this->proj_data_info_cyl_noarc_cor_sptr->get_max_axial_pos_num(0), 0)); + assert(fabs(m_last + m_first) < m_last * 10E-4); + } #endif - this->shift_detector_coordinates_to_origin = - CartesianCoordinate3D(this->proj_data_info_cyl_noarc_cor_sptr->get_m(Bin(0, 0, 0, 0)), 0, 0); + this->shift_detector_coordinates_to_origin = + CartesianCoordinate3D(this->proj_data_info_cyl_noarc_cor_sptr->get_m(Bin(0, 0, 0, 0)), 0, 0); #if 1 - // checks on image zooming to avvoid getting incorrect results - { - check_z_to_middle_consistent(*this->activity_image_sptr, "activity"); - check_z_to_middle_consistent(*this->density_image_sptr, "attenuation"); - check_z_to_middle_consistent(*this->density_image_for_scatter_points_sptr, "scatter-point"); - } + // checks on image zooming to avvoid getting incorrect results + { + check_z_to_middle_consistent(*this->activity_image_sptr, "activity"); + check_z_to_middle_consistent(*this->density_image_sptr, "attenuation"); + check_z_to_middle_consistent(*this->density_image_for_scatter_points_sptr, "scatter-point"); + } #endif - this->initialise_cache_for_scattpoint_det_integrals_over_attenuation(); - this->initialise_cache_for_scattpoint_det_integrals_over_activity(); + this->initialise_cache_for_scattpoint_det_integrals_over_attenuation(); + this->initialise_cache_for_scattpoint_det_integrals_over_activity(); - this->_already_set_up = true; + this->_already_set_up = true; - return Succeeded::yes; + return Succeeded::yes; } void -ScatterSimulation:: -check_z_to_middle_consistent(const DiscretisedDensity<3,float>& _image, const std::string& name) const -{ - const VoxelsOnCartesianGrid & image = dynamic_cast const& >(_image); - const float z_to_middle = - (image.get_max_index() + image.get_min_index())*image.get_voxel_size().z()/2.F; - -# if 0 +ScatterSimulation::check_z_to_middle_consistent(const DiscretisedDensity<3, float>& _image, const std::string& name) const { + const VoxelsOnCartesianGrid& image = dynamic_cast const&>(_image); + const float z_to_middle = (image.get_max_index() + image.get_min_index()) * image.get_voxel_size().z() / 2.F; + +#if 0 const Scanner& scanner = *this->proj_data_info_cyl_noarc_cor_sptr->get_scanner_ptr(); const float z_to_middle_standard = (scanner.get_num_rings()-1) * scanner.get_ring_spacing()/2; #endif - const VoxelsOnCartesianGrid & act_image = - dynamic_cast const& >(*this->activity_image_sptr); + const VoxelsOnCartesianGrid& act_image = dynamic_cast const&>(*this->activity_image_sptr); const float z_to_middle_standard = - (act_image.get_max_index() + act_image.get_min_index())*act_image.get_voxel_size().z()/2.F; + (act_image.get_max_index() + act_image.get_min_index()) * act_image.get_voxel_size().z() / 2.F; if (abs(z_to_middle - z_to_middle_standard) > .1) error(boost::format("ScatterSimulation: limitation in #planes and voxel-size for the %1% image.\n" "This would cause a shift of %2%mm w.r.t. the activity image.\n" - "(see https://github.com/UCL/STIR/issues/495.") - % name % (z_to_middle - z_to_middle_standard)); + "(see https://github.com/UCL/STIR/issues/495.") % + name % (z_to_middle - z_to_middle_standard)); } void -ScatterSimulation:: -set_activity_image_sptr(const shared_ptr > arg) -{ - if (is_null_ptr(arg) ) - error("ScatterSimulation: Unable to set the activity image"); +ScatterSimulation::set_activity_image_sptr(const shared_ptr> arg) { + if (is_null_ptr(arg)) + error("ScatterSimulation: Unable to set the activity image"); - this->activity_image_sptr = arg; - this->remove_cache_for_integrals_over_activity(); - this->_already_set_up = false; + this->activity_image_sptr = arg; + this->remove_cache_for_integrals_over_activity(); + this->_already_set_up = false; } void -ScatterSimulation:: -set_activity_image(const std::string& filename) -{ - this->activity_image_filename = filename; - shared_ptr > sptr(read_from_file >(filename)); - this->set_activity_image_sptr(sptr); +ScatterSimulation::set_activity_image(const std::string& filename) { + this->activity_image_filename = filename; + shared_ptr> sptr(read_from_file>(filename)); + this->set_activity_image_sptr(sptr); } void -ScatterSimulation:: -set_density_image_sptr(const shared_ptr > arg) -{ - if (is_null_ptr(arg) ) - error("ScatterSimulation: Unable to set the density image"); - this->density_image_sptr=arg; - // make sure that we're not re-using a previously interpolated image for scatter points - this->density_image_for_scatter_points_sptr.reset(); - this->remove_cache_for_integrals_over_attenuation(); - this->_already_set_up = false; +ScatterSimulation::set_density_image_sptr(const shared_ptr> arg) { + if (is_null_ptr(arg)) + error("ScatterSimulation: Unable to set the density image"); + this->density_image_sptr = arg; + // make sure that we're not re-using a previously interpolated image for scatter points + this->density_image_for_scatter_points_sptr.reset(); + this->remove_cache_for_integrals_over_attenuation(); + this->_already_set_up = false; } void -ScatterSimulation:: -set_density_image(const std::string& filename) -{ - this->density_image_filename=filename; - shared_ptr > sptr(read_from_file >(filename)); - this->set_density_image_sptr(sptr); +ScatterSimulation::set_density_image(const std::string& filename) { + this->density_image_filename = filename; + shared_ptr> sptr(read_from_file>(filename)); + this->set_density_image_sptr(sptr); } void -ScatterSimulation:: -set_density_image_for_scatter_points_sptr(shared_ptr > arg) -{ - if (is_null_ptr(arg) ) - error("ScatterSimulation: Unable to set the density image for scatter points."); - this->density_image_for_scatter_points_sptr.reset( - new VoxelsOnCartesianGrid(*dynamic_cast *>(arg.get()))); - this->sample_scatter_points(); - this->remove_cache_for_integrals_over_attenuation(); - this->_already_set_up = false; +ScatterSimulation::set_density_image_for_scatter_points_sptr(shared_ptr> arg) { + if (is_null_ptr(arg)) + error("ScatterSimulation: Unable to set the density image for scatter points."); + this->density_image_for_scatter_points_sptr.reset( + new VoxelsOnCartesianGrid(*dynamic_cast*>(arg.get()))); + this->sample_scatter_points(); + this->remove_cache_for_integrals_over_attenuation(); + this->_already_set_up = false; } -const DiscretisedDensity<3,float>& -ScatterSimulation:: -get_activity_image() const -{ - return *activity_image_sptr; +const DiscretisedDensity<3, float>& +ScatterSimulation::get_activity_image() const { + return *activity_image_sptr; } -const DiscretisedDensity<3,float>& -ScatterSimulation:: -get_attenuation_image() const -{ - return *density_image_sptr; +const DiscretisedDensity<3, float>& +ScatterSimulation::get_attenuation_image() const { + return *density_image_sptr; } -const DiscretisedDensity<3,float>& -ScatterSimulation:: -get_attenuation_image_for_scatter_points() const -{ - return *density_image_for_scatter_points_sptr; +const DiscretisedDensity<3, float>& +ScatterSimulation::get_attenuation_image_for_scatter_points() const { + return *density_image_for_scatter_points_sptr; } -shared_ptr > -ScatterSimulation:: -get_density_image_for_scatter_points_sptr() const -{ - return density_image_for_scatter_points_sptr; +shared_ptr> +ScatterSimulation::get_density_image_for_scatter_points_sptr() const { + return density_image_for_scatter_points_sptr; } void -ScatterSimulation:: -set_density_image_for_scatter_points(const std::string& filename) -{ - this->density_image_for_scatter_points_filename=filename; - shared_ptr > sptr(read_from_file >(filename)); - this->set_density_image_for_scatter_points_sptr(sptr); - this->_already_set_up = false; +ScatterSimulation::set_density_image_for_scatter_points(const std::string& filename) { + this->density_image_for_scatter_points_filename = filename; + shared_ptr> sptr(read_from_file>(filename)); + this->set_density_image_for_scatter_points_sptr(sptr); + this->_already_set_up = false; } void -ScatterSimulation:: -set_image_downsample_factors(float _zoom_xy, float _zoom_z, - int _size_zoom_xy, int _size_zoom_z) -{ - if (_zoom_xy<0.F || _zoom_z<0.F) - error("ScatterSimulation: at least one zoom factor for the scatter-point image is negative"); - zoom_xy = _zoom_xy; - zoom_z = _zoom_z; - zoom_size_xy = _size_zoom_xy; - zoom_size_z = _size_zoom_z; - _already_set_up = false; +ScatterSimulation::set_image_downsample_factors(float _zoom_xy, float _zoom_z, int _size_zoom_xy, int _size_zoom_z) { + if (_zoom_xy < 0.F || _zoom_z < 0.F) + error("ScatterSimulation: at least one zoom factor for the scatter-point image is negative"); + zoom_xy = _zoom_xy; + zoom_z = _zoom_z; + zoom_size_xy = _size_zoom_xy; + zoom_size_z = _size_zoom_z; + _already_set_up = false; } void -ScatterSimulation:: -downsample_density_image_for_scatter_points(float _zoom_xy, float _zoom_z, - int _size_xy, int _size_z) -{ - if (is_null_ptr(this->density_image_sptr)) - error("ScatterSimulation: downsampling function called before attenuation image is set"); - - const VoxelsOnCartesianGrid & tmp_att = dynamic_cast& >(*this->density_image_sptr); - - const int old_x = tmp_att.get_x_size(); - const int old_y = tmp_att.get_y_size(); - const int old_z = tmp_att.get_z_size(); - - if (_zoom_xy < 0 || _zoom_z < 0) - { - VoxelsOnCartesianGrid tmpl_density(this->density_image_sptr->get_exam_info_sptr(), *proj_data_info_cyl_noarc_cor_sptr); - info(boost::format("ScatterSimulation: template density to find zoom factors: voxel-sizes %1%, size %2%, product %3%") - % tmpl_density.get_voxel_size() - % tmpl_density.get_lengths() - % (tmpl_density.get_voxel_size() * BasicCoordinate<3,float>(tmpl_density.get_lengths())), - 3); - if (_zoom_xy < 0) - _zoom_xy = tmp_att.get_voxel_size().x() / tmpl_density.get_voxel_size().x(); - - const float z_length = - std::max((old_z+1)*tmp_att.get_voxel_size().z(), - (tmpl_density.get_z_size()+1)*tmpl_density.get_voxel_size().z()); - if (_zoom_z < 0) - { - if (_size_z < 0) - _size_z = (tmpl_density.get_z_size()+1)/2; - zoom_z = tmp_att.get_voxel_size().z() / (z_length/_size_z); - } - else - zoom_z = _zoom_z; - } - else +ScatterSimulation::downsample_density_image_for_scatter_points(float _zoom_xy, float _zoom_z, int _size_xy, int _size_z) { + if (is_null_ptr(this->density_image_sptr)) + error("ScatterSimulation: downsampling function called before attenuation image is set"); + + const VoxelsOnCartesianGrid& tmp_att = dynamic_cast&>(*this->density_image_sptr); + + const int old_x = tmp_att.get_x_size(); + const int old_y = tmp_att.get_y_size(); + const int old_z = tmp_att.get_z_size(); + + if (_zoom_xy < 0 || _zoom_z < 0) { + VoxelsOnCartesianGrid tmpl_density(this->density_image_sptr->get_exam_info_sptr(), *proj_data_info_cyl_noarc_cor_sptr); + info(boost::format("ScatterSimulation: template density to find zoom factors: voxel-sizes %1%, size %2%, product %3%") % + tmpl_density.get_voxel_size() % tmpl_density.get_lengths() % + (tmpl_density.get_voxel_size() * BasicCoordinate<3, float>(tmpl_density.get_lengths())), + 3); + if (_zoom_xy < 0) + _zoom_xy = tmp_att.get_voxel_size().x() / tmpl_density.get_voxel_size().x(); + + const float z_length = + std::max((old_z + 1) * tmp_att.get_voxel_size().z(), (tmpl_density.get_z_size() + 1) * tmpl_density.get_voxel_size().z()); + if (_zoom_z < 0) { + if (_size_z < 0) + _size_z = (tmpl_density.get_z_size() + 1) / 2; + zoom_z = tmp_att.get_voxel_size().z() / (z_length / _size_z); + } else zoom_z = _zoom_z; + } else + zoom_z = _zoom_z; - set_image_downsample_factors(_zoom_xy, zoom_z, _size_xy, _size_z); - - int new_x = zoom_size_xy == -1 ? static_cast(old_x * zoom_xy + 1) : zoom_size_xy; - int new_y = zoom_size_xy == -1 ? static_cast(old_y * zoom_xy + 1) : zoom_size_xy; - const int new_z = zoom_size_z == -1 ? static_cast(old_z * zoom_z + 1) : zoom_size_z; - - // make sizes odd to avoid edge effects and half-voxel shifts - if (new_x%2 == 0) - new_x++; - if (new_y%2 == 0) - new_y++; - - // adjust zoom_z to cope with ugly "shift to middle of scanner" problem - { - // see http://github.com/UCL/STIR/issues/495 - zoom_z = static_cast(new_z-1)/(old_z-1); - if (_zoom_z>0 && abs(zoom_z - _zoom_z)>.1) - error(boost::format("Current limitation in ScatterSimulation: use zoom_z==-1 or %1%") - % zoom_z); - } - - const CartesianCoordinate3D new_voxel_size = - tmp_att.get_voxel_size() / make_coordinate(zoom_z, zoom_xy, zoom_xy); - // create new image of appropriate size - shared_ptr > - vox_sptr(new VoxelsOnCartesianGrid(tmp_att.get_exam_info_sptr(), - IndexRange3D(0, new_z-1, - -new_y/2, -new_y/2+new_y-1, - -new_x/2, -new_x/2+new_x-1), - tmp_att.get_origin(), - new_voxel_size - )); - // assign to class member - this->density_image_for_scatter_points_sptr = vox_sptr; - info(boost::format("ScatterSimulation: scatter-point image: voxel-sizes %1%, size %2%, total-length %3%") - % vox_sptr->get_voxel_size() - % vox_sptr->get_lengths() - % (vox_sptr->get_voxel_size() * (BasicCoordinate<3,float>(vox_sptr->get_lengths()+1.F))), - 2); - // fill values from original attenuation image - ZoomOptions scaling(ZoomOptions::preserve_values); - zoom_image( *vox_sptr, tmp_att, scaling); + set_image_downsample_factors(_zoom_xy, zoom_z, _size_xy, _size_z); + + int new_x = zoom_size_xy == -1 ? static_cast(old_x * zoom_xy + 1) : zoom_size_xy; + int new_y = zoom_size_xy == -1 ? static_cast(old_y * zoom_xy + 1) : zoom_size_xy; + const int new_z = zoom_size_z == -1 ? static_cast(old_z * zoom_z + 1) : zoom_size_z; + + // make sizes odd to avoid edge effects and half-voxel shifts + if (new_x % 2 == 0) + new_x++; + if (new_y % 2 == 0) + new_y++; + + // adjust zoom_z to cope with ugly "shift to middle of scanner" problem + { + // see http://github.com/UCL/STIR/issues/495 + zoom_z = static_cast(new_z - 1) / (old_z - 1); + if (_zoom_z > 0 && abs(zoom_z - _zoom_z) > .1) + error(boost::format("Current limitation in ScatterSimulation: use zoom_z==-1 or %1%") % zoom_z); + } + + const CartesianCoordinate3D new_voxel_size = tmp_att.get_voxel_size() / make_coordinate(zoom_z, zoom_xy, zoom_xy); + // create new image of appropriate size + shared_ptr> vox_sptr(new VoxelsOnCartesianGrid( + tmp_att.get_exam_info_sptr(), + IndexRange3D(0, new_z - 1, -new_y / 2, -new_y / 2 + new_y - 1, -new_x / 2, -new_x / 2 + new_x - 1), tmp_att.get_origin(), + new_voxel_size)); + // assign to class member + this->density_image_for_scatter_points_sptr = vox_sptr; + info(boost::format("ScatterSimulation: scatter-point image: voxel-sizes %1%, size %2%, total-length %3%") % + vox_sptr->get_voxel_size() % vox_sptr->get_lengths() % + (vox_sptr->get_voxel_size() * (BasicCoordinate<3, float>(vox_sptr->get_lengths() + 1.F))), + 2); + // fill values from original attenuation image + ZoomOptions scaling(ZoomOptions::preserve_values); + zoom_image(*vox_sptr, tmp_att, scaling); #if 0 // do some checks @@ -631,324 +538,257 @@ downsample_density_image_for_scatter_points(float _zoom_xy, float _zoom_z, } #endif - if(this->density_image_for_scatter_points_output_filename.size()>0) - OutputFileFormat >::default_sptr()-> - write_to_file(density_image_for_scatter_points_output_filename, - *this->density_image_for_scatter_points_sptr); + if (this->density_image_for_scatter_points_output_filename.size() > 0) + OutputFileFormat>::default_sptr()->write_to_file( + density_image_for_scatter_points_output_filename, *this->density_image_for_scatter_points_sptr); - this->sample_scatter_points(); - this->remove_cache_for_integrals_over_attenuation(); - this->_already_set_up = false; + this->sample_scatter_points(); + this->remove_cache_for_integrals_over_attenuation(); + this->_already_set_up = false; } - void -ScatterSimulation:: -set_output_proj_data_sptr(const shared_ptr _exam, - const shared_ptr _info, - const std::string & filename) -{ - if (filename.size() > 0 ) - this->output_proj_data_sptr.reset(new ProjDataInterfile(_exam, - _info, - filename, - std::ios::in | std::ios::out | std::ios::trunc)); - else - this->output_proj_data_sptr.reset( new ProjDataInMemory(_exam, - _info)); +ScatterSimulation::set_output_proj_data_sptr(const shared_ptr _exam, const shared_ptr _info, + const std::string& filename) { + if (filename.size() > 0) + this->output_proj_data_sptr.reset( + new ProjDataInterfile(_exam, _info, filename, std::ios::in | std::ios::out | std::ios::trunc)); + else + this->output_proj_data_sptr.reset(new ProjDataInMemory(_exam, _info)); } shared_ptr -ScatterSimulation:: -get_output_proj_data_sptr() const -{ +ScatterSimulation::get_output_proj_data_sptr() const { - if(is_null_ptr(this->output_proj_data_sptr)) - { - error("ScatterSimulation: No output ProjData set. Aborting."); - } + if (is_null_ptr(this->output_proj_data_sptr)) { + error("ScatterSimulation: No output ProjData set. Aborting."); + } - return this->output_proj_data_sptr; + return this->output_proj_data_sptr; } void -ScatterSimulation:: -set_output_proj_data(const std::string& filename) -{ +ScatterSimulation::set_output_proj_data(const std::string& filename) { - if(is_null_ptr(this->proj_data_info_cyl_noarc_cor_sptr)) - { - error("ScatterSimulation: Template ProjData has not been set. Aborting."); - } + if (is_null_ptr(this->proj_data_info_cyl_noarc_cor_sptr)) { + error("ScatterSimulation: Template ProjData has not been set. Aborting."); + } - this->output_proj_data_filename = filename; - shared_ptr tmp_sptr; + this->output_proj_data_filename = filename; + shared_ptr tmp_sptr; - if (is_null_ptr(this->template_exam_info_sptr)) - { - shared_ptr exam_info_sptr(new ExamInfo); - tmp_sptr.reset(new ProjDataInterfile(exam_info_sptr, - this->proj_data_info_cyl_noarc_cor_sptr->create_shared_clone(), - this->output_proj_data_filename,std::ios::in | std::ios::out | std::ios::trunc)); - } - else - { - tmp_sptr.reset(new ProjDataInterfile(this->template_exam_info_sptr, - this->proj_data_info_cyl_noarc_cor_sptr->create_shared_clone(), - this->output_proj_data_filename,std::ios::in | std::ios::out | std::ios::trunc)); + if (is_null_ptr(this->template_exam_info_sptr)) { + shared_ptr exam_info_sptr(new ExamInfo); + tmp_sptr.reset(new ProjDataInterfile(exam_info_sptr, this->proj_data_info_cyl_noarc_cor_sptr->create_shared_clone(), + this->output_proj_data_filename, std::ios::in | std::ios::out | std::ios::trunc)); + } else { + tmp_sptr.reset(new ProjDataInterfile(this->template_exam_info_sptr, + this->proj_data_info_cyl_noarc_cor_sptr->create_shared_clone(), + this->output_proj_data_filename, std::ios::in | std::ios::out | std::ios::trunc)); + } - } - - set_output_proj_data_sptr(tmp_sptr); + set_output_proj_data_sptr(tmp_sptr); } - void -ScatterSimulation:: -set_output_proj_data_sptr(shared_ptr arg) -{ - this->output_proj_data_sptr = arg; +ScatterSimulation::set_output_proj_data_sptr(shared_ptr arg) { + this->output_proj_data_sptr = arg; } shared_ptr -ScatterSimulation:: -get_template_proj_data_info_sptr() const -{ - return this->proj_data_info_cyl_noarc_cor_sptr; +ScatterSimulation::get_template_proj_data_info_sptr() const { + return this->proj_data_info_cyl_noarc_cor_sptr; } shared_ptr -ScatterSimulation::get_exam_info_sptr() const -{ - return this->template_exam_info_sptr; +ScatterSimulation::get_exam_info_sptr() const { + return this->template_exam_info_sptr; } void -ScatterSimulation:: -set_template_proj_data_info(const std::string& filename) -{ - this->template_proj_data_filename = filename; - shared_ptr template_proj_data_sptr(ProjData::read_from_file(this->template_proj_data_filename)); +ScatterSimulation::set_template_proj_data_info(const std::string& filename) { + this->template_proj_data_filename = filename; + shared_ptr template_proj_data_sptr(ProjData::read_from_file(this->template_proj_data_filename)); - this->set_exam_info(template_proj_data_sptr->get_exam_info()); + this->set_exam_info(template_proj_data_sptr->get_exam_info()); - this->set_template_proj_data_info(*template_proj_data_sptr->get_proj_data_info_sptr()); + this->set_template_proj_data_info(*template_proj_data_sptr->get_proj_data_info_sptr()); } void -ScatterSimulation::set_template_proj_data_info(const ProjDataInfo& arg) -{ - this->_already_set_up = false; - this->proj_data_info_cyl_noarc_cor_sptr.reset(dynamic_cast(arg.clone())); +ScatterSimulation::set_template_proj_data_info(const ProjDataInfo& arg) { + this->_already_set_up = false; + this->proj_data_info_cyl_noarc_cor_sptr.reset(dynamic_cast(arg.clone())); - if (is_null_ptr(this->proj_data_info_cyl_noarc_cor_sptr)) - error("ScatterSimulation: Can only handle non-arccorrected data"); + if (is_null_ptr(this->proj_data_info_cyl_noarc_cor_sptr)) + error("ScatterSimulation: Can only handle non-arccorrected data"); - // find final size of detection_points_vector - this->total_detectors = - this->proj_data_info_cyl_noarc_cor_sptr->get_scanner_ptr()->get_num_rings()* - this->proj_data_info_cyl_noarc_cor_sptr->get_scanner_ptr()->get_num_detectors_per_ring (); + // find final size of detection_points_vector + this->total_detectors = this->proj_data_info_cyl_noarc_cor_sptr->get_scanner_ptr()->get_num_rings() * + this->proj_data_info_cyl_noarc_cor_sptr->get_scanner_ptr()->get_num_detectors_per_ring(); - // get rid of any previously stored points - this->detection_points_vector.clear(); - // reserve space to avoid reallocation, but the actual size will grow dynamically - this->detection_points_vector.reserve(static_cast(this->total_detectors)); + // get rid of any previously stored points + this->detection_points_vector.clear(); + // reserve space to avoid reallocation, but the actual size will grow dynamically + this->detection_points_vector.reserve(static_cast(this->total_detectors)); - // set to negative value such that this will be recomputed - this->detector_efficiency_no_scatter = -1.F; + // set to negative value such that this will be recomputed + this->detector_efficiency_no_scatter = -1.F; - // remove any cached values as they'd be incorrect if the sizes changes - this->remove_cache_for_integrals_over_attenuation(); - this->remove_cache_for_integrals_over_activity(); + // remove any cached values as they'd be incorrect if the sizes changes + this->remove_cache_for_integrals_over_attenuation(); + this->remove_cache_for_integrals_over_activity(); } void -ScatterSimulation:: -set_exam_info(const ExamInfo& arg) -{ +ScatterSimulation::set_exam_info(const ExamInfo& arg) { this->_already_set_up = false; this->template_exam_info_sptr = arg.create_shared_clone(); } void -ScatterSimulation:: -set_exam_info_sptr(const shared_ptr arg) -{ - this->_already_set_up = false; - this->template_exam_info_sptr = arg->create_shared_clone(); +ScatterSimulation::set_exam_info_sptr(const shared_ptr arg) { + this->_already_set_up = false; + this->template_exam_info_sptr = arg->create_shared_clone(); } Succeeded -ScatterSimulation::downsample_scanner(int new_num_rings, int new_num_dets) -{ - if (new_num_rings <= 0) - { - if(downsample_scanner_rings > 0) - new_num_rings = downsample_scanner_rings; - else if (!is_null_ptr(proj_data_info_cyl_noarc_cor_sptr)) - { - const float total_axial_length = proj_data_info_cyl_noarc_cor_sptr->get_scanner_sptr()->get_num_rings() * - proj_data_info_cyl_noarc_cor_sptr->get_scanner_sptr()->get_ring_spacing(); - - new_num_rings = round(total_axial_length / 20.F + 0.5F); - } - else - return Succeeded::no; - } - if (new_num_dets <= 0) - { - if(downsample_scanner_dets > 0) - new_num_dets = downsample_scanner_dets; - else - return Succeeded::no; - } +ScatterSimulation::downsample_scanner(int new_num_rings, int new_num_dets) { + if (new_num_rings <= 0) { + if (downsample_scanner_rings > 0) + new_num_rings = downsample_scanner_rings; + else if (!is_null_ptr(proj_data_info_cyl_noarc_cor_sptr)) { + const float total_axial_length = proj_data_info_cyl_noarc_cor_sptr->get_scanner_sptr()->get_num_rings() * + proj_data_info_cyl_noarc_cor_sptr->get_scanner_sptr()->get_ring_spacing(); + + new_num_rings = round(total_axial_length / 20.F + 0.5F); + } else + return Succeeded::no; + } + if (new_num_dets <= 0) { + if (downsample_scanner_dets > 0) + new_num_dets = downsample_scanner_dets; + else + return Succeeded::no; + } - const Scanner *const old_scanner_ptr = this->proj_data_info_cyl_noarc_cor_sptr->get_scanner_ptr(); - shared_ptr new_scanner_sptr( new Scanner(*old_scanner_ptr)); + const Scanner* const old_scanner_ptr = this->proj_data_info_cyl_noarc_cor_sptr->get_scanner_ptr(); + shared_ptr new_scanner_sptr(new Scanner(*old_scanner_ptr)); - // preserve the length of the scanner - float scanner_length = new_scanner_sptr->get_num_rings()* new_scanner_sptr->get_ring_spacing(); + // preserve the length of the scanner + float scanner_length = new_scanner_sptr->get_num_rings() * new_scanner_sptr->get_ring_spacing(); - new_scanner_sptr->set_num_rings(new_num_rings); - new_scanner_sptr->set_num_detectors_per_ring(new_num_dets); - new_scanner_sptr->set_ring_spacing(static_cast(scanner_length/new_scanner_sptr->get_num_rings())); - const float approx_num_non_arccorrected_bins = - old_scanner_ptr->get_max_num_non_arccorrected_bins() * - (float(new_num_dets) / old_scanner_ptr->get_num_detectors_per_ring()) - + 5; // add 5 to avoid strange edge-effects, certainly with B-splines - new_scanner_sptr->set_max_num_non_arccorrected_bins(round(approx_num_non_arccorrected_bins+.5F)); - new_scanner_sptr->set_default_bin_size(new_scanner_sptr->get_effective_ring_radius() * _PI / new_num_dets); // approx new detector size + new_scanner_sptr->set_num_rings(new_num_rings); + new_scanner_sptr->set_num_detectors_per_ring(new_num_dets); + new_scanner_sptr->set_ring_spacing(static_cast(scanner_length / new_scanner_sptr->get_num_rings())); + const float approx_num_non_arccorrected_bins = old_scanner_ptr->get_max_num_non_arccorrected_bins() * + (float(new_num_dets) / old_scanner_ptr->get_num_detectors_per_ring()) + + 5; // add 5 to avoid strange edge-effects, certainly with B-splines + new_scanner_sptr->set_max_num_non_arccorrected_bins(round(approx_num_non_arccorrected_bins + .5F)); + new_scanner_sptr->set_default_bin_size(new_scanner_sptr->get_effective_ring_radius() * _PI / + new_num_dets); // approx new detector size - // Find how much is the delta ring - // If the previous projdatainfo had max segment == 1 then should be from SSRB - // in ScatterEstimation. Otherwise use the max possible. - int delta_ring = proj_data_info_cyl_noarc_cor_sptr->get_num_segments() == 1 ? 0 : - new_scanner_sptr->get_num_rings()-1; + // Find how much is the delta ring + // If the previous projdatainfo had max segment == 1 then should be from SSRB + // in ScatterEstimation. Otherwise use the max possible. + int delta_ring = proj_data_info_cyl_noarc_cor_sptr->get_num_segments() == 1 ? 0 : new_scanner_sptr->get_num_rings() - 1; - shared_ptr templ_proj_data_info_sptr( - ProjDataInfo::ProjDataInfoCTI(new_scanner_sptr, - 1, delta_ring, - new_scanner_sptr->get_num_detectors_per_ring()/2, - new_scanner_sptr->get_max_num_non_arccorrected_bins(), - false)); + shared_ptr templ_proj_data_info_sptr( + ProjDataInfo::ProjDataInfoCTI(new_scanner_sptr, 1, delta_ring, new_scanner_sptr->get_num_detectors_per_ring() / 2, + new_scanner_sptr->get_max_num_non_arccorrected_bins(), false)); - info(boost::format("ScatterSimulation: down-sampled scanner info:\n%1%") - % templ_proj_data_info_sptr->parameter_info(), - 3); - this->set_template_proj_data_info(*templ_proj_data_info_sptr); + info(boost::format("ScatterSimulation: down-sampled scanner info:\n%1%") % templ_proj_data_info_sptr->parameter_info(), 3); + this->set_template_proj_data_info(*templ_proj_data_info_sptr); - return Succeeded::yes; + return Succeeded::yes; } -Succeeded ScatterSimulation::downsample_images_to_scanner_size() -{ - if(is_null_ptr(proj_data_info_cyl_noarc_cor_sptr)) - return Succeeded::no; +Succeeded +ScatterSimulation::downsample_images_to_scanner_size() { + if (is_null_ptr(proj_data_info_cyl_noarc_cor_sptr)) + return Succeeded::no; - // Downsample the activity and attenuation images - shared_ptr > tmpl_image( new VoxelsOnCartesianGrid(*proj_data_info_cyl_noarc_cor_sptr)); + // Downsample the activity and attenuation images + shared_ptr> tmpl_image(new VoxelsOnCartesianGrid(*proj_data_info_cyl_noarc_cor_sptr)); - if(!is_null_ptr(activity_image_sptr)) - { - const VoxelsOnCartesianGrid* tmp_act = dynamic_cast* >(activity_image_sptr.get()); - VoxelsOnCartesianGrid* tmp = tmpl_image->get_empty_copy(); + if (!is_null_ptr(activity_image_sptr)) { + const VoxelsOnCartesianGrid* tmp_act = dynamic_cast*>(activity_image_sptr.get()); + VoxelsOnCartesianGrid* tmp = tmpl_image->get_empty_copy(); - ZoomOptions scaling(ZoomOptions::preserve_projections); - zoom_image(*tmp, *tmp_act, scaling); - activity_image_sptr.reset(tmp); + ZoomOptions scaling(ZoomOptions::preserve_projections); + zoom_image(*tmp, *tmp_act, scaling); + activity_image_sptr.reset(tmp); - this->remove_cache_for_integrals_over_activity(); - this->_already_set_up = false; - } + this->remove_cache_for_integrals_over_activity(); + this->_already_set_up = false; + } - if(!is_null_ptr(density_image_sptr)) - { - const VoxelsOnCartesianGrid* tmp_att = dynamic_cast* >(density_image_sptr.get()); - VoxelsOnCartesianGrid* tmp = tmpl_image->get_empty_copy(); + if (!is_null_ptr(density_image_sptr)) { + const VoxelsOnCartesianGrid* tmp_att = dynamic_cast*>(density_image_sptr.get()); + VoxelsOnCartesianGrid* tmp = tmpl_image->get_empty_copy(); - ZoomOptions scaling(ZoomOptions::preserve_values); - zoom_image(*tmp, *tmp_att, scaling); - density_image_sptr.reset(tmp); + ZoomOptions scaling(ZoomOptions::preserve_values); + zoom_image(*tmp, *tmp_att, scaling); + density_image_sptr.reset(tmp); - this->remove_cache_for_integrals_over_attenuation(); - this->_already_set_up = false; - } + this->remove_cache_for_integrals_over_attenuation(); + this->_already_set_up = false; + } - // zooming of density_image_for_scatter_points_sptr will happen in set_up + // zooming of density_image_for_scatter_points_sptr will happen in set_up - return Succeeded::yes; + return Succeeded::yes; } void -ScatterSimulation:: -set_attenuation_threshold(const float arg) -{ - attenuation_threshold = arg; - this->_already_set_up = false; +ScatterSimulation::set_attenuation_threshold(const float arg) { + attenuation_threshold = arg; + this->_already_set_up = false; } void -ScatterSimulation:: -set_randomly_place_scatter_points(const bool arg) -{ - randomly_place_scatter_points = arg; - this->_already_set_up = false; +ScatterSimulation::set_randomly_place_scatter_points(const bool arg) { + randomly_place_scatter_points = arg; + this->_already_set_up = false; } void -ScatterSimulation:: -set_cache_enabled(const bool arg) -{ - use_cache = arg; +ScatterSimulation::set_cache_enabled(const bool arg) { + use_cache = arg; } void -ScatterSimulation:: -write_log(const double simulation_time, - const float total_scatter) -{ - if (this->output_proj_data_filename.empty()) - return; - - std::string log_filename = - this->output_proj_data_filename + ".log"; - std::ofstream mystream(log_filename.c_str()); - - if (!mystream) - { - warning("Cannot open log file '%s'", log_filename.c_str()) ; - return; - } - - int axial_bins = 0 ; - - for (int segment_num = this->output_proj_data_sptr->get_min_segment_num(); - segment_num <= this->output_proj_data_sptr->get_max_segment_num(); - ++segment_num) - axial_bins += this->output_proj_data_sptr->get_num_axial_poss(segment_num); - - const int total_bins = - this->output_proj_data_sptr->get_num_views() * axial_bins * - this->output_proj_data_sptr->get_num_tangential_poss(); - mystream << this->parameter_info() - << "\nTotal simulation time elapsed: " - << simulation_time / 60 << "min" - << "\nTotal Scatter Points : " << scatt_points_vector.size() - << "\nTotal Scatter Counts (before upsampling and norm) : " << total_scatter - << "\nActivity image SIZE: " - << (*this->activity_image_sptr).size() << " * " - << (*this->activity_image_sptr)[0].size() << " * " // TODO relies on 0 index - << (*this->activity_image_sptr)[0][0].size() - << "\nAttenuation image for scatter points SIZE: " - << (*this->density_image_for_scatter_points_sptr).size() << " * " - << (*this->density_image_for_scatter_points_sptr)[0].size() << " * " - << (*this->density_image_for_scatter_points_sptr)[0][0].size() - << "\nTotal bins : " << total_bins << " = " - << this->output_proj_data_sptr->get_num_views() - << " view_bins * " - << axial_bins << " axial_bins * " - << this->output_proj_data_sptr->get_num_tangential_poss() - << " tangential_bins\n"; +ScatterSimulation::write_log(const double simulation_time, const float total_scatter) { + if (this->output_proj_data_filename.empty()) + return; + + std::string log_filename = this->output_proj_data_filename + ".log"; + std::ofstream mystream(log_filename.c_str()); + + if (!mystream) { + warning("Cannot open log file '%s'", log_filename.c_str()); + return; + } + + int axial_bins = 0; + + for (int segment_num = this->output_proj_data_sptr->get_min_segment_num(); + segment_num <= this->output_proj_data_sptr->get_max_segment_num(); ++segment_num) + axial_bins += this->output_proj_data_sptr->get_num_axial_poss(segment_num); + + const int total_bins = + this->output_proj_data_sptr->get_num_views() * axial_bins * this->output_proj_data_sptr->get_num_tangential_poss(); + mystream << this->parameter_info() << "\nTotal simulation time elapsed: " << simulation_time / 60 << "min" + << "\nTotal Scatter Points : " << scatt_points_vector.size() + << "\nTotal Scatter Counts (before upsampling and norm) : " << total_scatter + << "\nActivity image SIZE: " << (*this->activity_image_sptr).size() << " * " << (*this->activity_image_sptr)[0].size() + << " * " // TODO relies on 0 index + << (*this->activity_image_sptr)[0][0].size() + << "\nAttenuation image for scatter points SIZE: " << (*this->density_image_for_scatter_points_sptr).size() << " * " + << (*this->density_image_for_scatter_points_sptr)[0].size() << " * " + << (*this->density_image_for_scatter_points_sptr)[0][0].size() << "\nTotal bins : " << total_bins << " = " + << this->output_proj_data_sptr->get_num_views() << " view_bins * " << axial_bins << " axial_bins * " + << this->output_proj_data_sptr->get_num_tangential_poss() << " tangential_bins\n"; } END_NAMESPACE_STIR diff --git a/src/scatter_buildblock/SingleScatterSimulation.cxx b/src/scatter_buildblock/SingleScatterSimulation.cxx index 91c81e5feb..867a61118b 100644 --- a/src/scatter_buildblock/SingleScatterSimulation.cxx +++ b/src/scatter_buildblock/SingleScatterSimulation.cxx @@ -18,104 +18,67 @@ START_NAMESPACE_STIR -const char * const -SingleScatterSimulation::registered_name = - "PET Single Scatter Simulation"; +const char* const SingleScatterSimulation::registered_name = "PET Single Scatter Simulation"; +SingleScatterSimulation::SingleScatterSimulation() : base_type() { this->set_defaults(); } -SingleScatterSimulation:: -SingleScatterSimulation() : - base_type() -{ - this->set_defaults(); -} - -SingleScatterSimulation:: -SingleScatterSimulation(const std::string& parameter_filename) -{ - this->initialise(parameter_filename); -} - -SingleScatterSimulation:: -~SingleScatterSimulation() -{} +SingleScatterSimulation::SingleScatterSimulation(const std::string& parameter_filename) { this->initialise(parameter_filename); } +SingleScatterSimulation::~SingleScatterSimulation() {} void -SingleScatterSimulation:: -initialise_keymap() -{ - base_type::initialise_keymap(); - this->parser.add_start_key("PET Single Scatter Simulation Parameters"); - this->parser.add_stop_key("end PET Single Scatter Simulation Parameters"); +SingleScatterSimulation::initialise_keymap() { + base_type::initialise_keymap(); + this->parser.add_start_key("PET Single Scatter Simulation Parameters"); + this->parser.add_stop_key("end PET Single Scatter Simulation Parameters"); } void -SingleScatterSimulation:: -initialise(const std::string& parameter_filename) -{ - if (parameter_filename.size() == 0) - { - this->set_defaults(); - this->ask_parameters(); - } - else - { - this->set_defaults(); - if (!this->parse(parameter_filename.c_str())) - { - error("Error parsing input file %s, exiting", parameter_filename.c_str()); - } +SingleScatterSimulation::initialise(const std::string& parameter_filename) { + if (parameter_filename.size() == 0) { + this->set_defaults(); + this->ask_parameters(); + } else { + this->set_defaults(); + if (!this->parse(parameter_filename.c_str())) { + error("Error parsing input file %s, exiting", parameter_filename.c_str()); } + } } void -SingleScatterSimulation:: -set_defaults() -{ - base_type::set_defaults(); +SingleScatterSimulation::set_defaults() { + base_type::set_defaults(); } Succeeded -SingleScatterSimulation:: -set_up() -{ - // set to negative value such that this will be recomputed - this->max_single_scatter_cos_angle = -1.F; +SingleScatterSimulation::set_up() { + // set to negative value such that this will be recomputed + this->max_single_scatter_cos_angle = -1.F; - return base_type::set_up(); + return base_type::set_up(); } Succeeded -SingleScatterSimulation:: -process_data() -{ - return base_type::process_data(); +SingleScatterSimulation::process_data() { + return base_type::process_data(); } void -SingleScatterSimulation:: -ask_parameters() -{ - base_type::ask_parameters(); +SingleScatterSimulation::ask_parameters() { + base_type::ask_parameters(); } bool -SingleScatterSimulation:: -post_processing() -{ - if (!base_type::post_processing()) - return false; - return true; +SingleScatterSimulation::post_processing() { + if (!base_type::post_processing()) + return false; + return true; } std::string -SingleScatterSimulation:: -method_info() const -{ - return this->registered_name; +SingleScatterSimulation::method_info() const { + return this->registered_name; } - END_NAMESPACE_STIR - diff --git a/src/scatter_buildblock/cached_single_scatter_integrals.cxx b/src/scatter_buildblock/cached_single_scatter_integrals.cxx index 02757aa724..054110b174 100644 --- a/src/scatter_buildblock/cached_single_scatter_integrals.cxx +++ b/src/scatter_buildblock/cached_single_scatter_integrals.cxx @@ -11,7 +11,7 @@ This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. + GNU Lesser General Public License for more details. See STIR/LICENSE.txt for details */ /*! @@ -19,7 +19,7 @@ \ingroup scatter \brief Implementations of functions defined in stir::ScatterEstimationByBin - Functions calculate the integral along LOR in an image (attenuation or emission). + Functions calculate the integral along LOR in an image (attenuation or emission). (from scatter point to detector coordinate). \author Charalampos Tsoumpas @@ -27,7 +27,7 @@ \author Kris Thielemans */ #include "stir/scatter/ScatterSimulation.h" -#include "stir/IndexRange.h" +#include "stir/IndexRange.h" #include "stir/Coordinate2D.h" START_NAMESPACE_STIR @@ -35,47 +35,36 @@ START_NAMESPACE_STIR const float cache_init_value = -1234567.89E10F; // an arbitrary value that should never occur void -ScatterSimulation:: -remove_cache_for_integrals_over_attenuation() -{ +ScatterSimulation::remove_cache_for_integrals_over_attenuation() { this->cached_attenuation_integral_scattpoint_det.recycle(); } void -ScatterSimulation:: -remove_cache_for_integrals_over_activity() -{ +ScatterSimulation::remove_cache_for_integrals_over_activity() { this->cached_activity_integral_scattpoint_det.recycle(); } - void -ScatterSimulation:: -initialise_cache_for_scattpoint_det_integrals_over_attenuation() -{ +ScatterSimulation::initialise_cache_for_scattpoint_det_integrals_over_attenuation() { if (!this->use_cache) return; - const IndexRange<2> range (Coordinate2D (0,0), - Coordinate2D (static_cast(this->scatt_points_vector.size()-1), - this->total_detectors-1)); + const IndexRange<2> range(Coordinate2D(0, 0), + Coordinate2D(static_cast(this->scatt_points_vector.size() - 1), this->total_detectors - 1)); if (this->cached_attenuation_integral_scattpoint_det.get_index_range() == range) - return; // keep cache if correct size + return; // keep cache if correct size this->cached_attenuation_integral_scattpoint_det.resize(range); this->cached_attenuation_integral_scattpoint_det.fill(cache_init_value); } void -ScatterSimulation:: -initialise_cache_for_scattpoint_det_integrals_over_activity() -{ +ScatterSimulation::initialise_cache_for_scattpoint_det_integrals_over_activity() { if (!this->use_cache) return; - const IndexRange<2> range (Coordinate2D (0,0), - Coordinate2D (static_cast(this->scatt_points_vector.size()-1), - this->total_detectors-1)); + const IndexRange<2> range(Coordinate2D(0, 0), + Coordinate2D(static_cast(this->scatt_points_vector.size() - 1), this->total_detectors - 1)); if (this->cached_activity_integral_scattpoint_det.get_index_range() == range) return; // keep cache if correct size @@ -84,15 +73,10 @@ initialise_cache_for_scattpoint_det_integrals_over_activity() this->cached_activity_integral_scattpoint_det.fill(cache_init_value); } -float -ScatterSimulation:: -cached_integral_over_activity_image_between_scattpoint_det(const unsigned scatter_point_num, - const unsigned det_num) -{ - float * location_in_cache = - this->use_cache - ? &cached_activity_integral_scattpoint_det[scatter_point_num][det_num] - : 0; +float +ScatterSimulation::cached_integral_over_activity_image_between_scattpoint_det(const unsigned scatter_point_num, + const unsigned det_num) { + float* location_in_cache = this->use_cache ? &cached_activity_integral_scattpoint_det[scatter_point_num][det_num] : 0; /* OPENMP note: We use atomic read/write to get at the cache. This should ensure validity. @@ -102,96 +86,79 @@ cached_integral_over_activity_image_between_scattpoint_det(const unsigned scatte */ float value; #if defined(STIR_OPENMP) -# if _OPENMP >=201012 -# pragma omp atomic read -# else -# pragma omp critical(STIRSCATTERESTIMATIONCACHE) +# if _OPENMP >= 201012 +# pragma omp atomic read +# else +# pragma omp critical(STIRSCATTERESTIMATIONCACHE) { -# endif +# endif #endif value = *location_in_cache; -#if defined(STIR_OPENMP) && (_OPENMP <201012) - } +#if defined(STIR_OPENMP) && (_OPENMP < 201012) +} #endif - if (this->use_cache && value!=cache_init_value) - { - return value; - } - else - { - const float result = - integral_over_activity_image_between_scattpoint_det - (scatt_points_vector[scatter_point_num].coord, - detection_points_vector[det_num] - ); - if (this->use_cache) +if (this->use_cache && value != cache_init_value) { + return value; +} else { + const float result = integral_over_activity_image_between_scattpoint_det(scatt_points_vector[scatter_point_num].coord, + detection_points_vector[det_num]); + if (this->use_cache) #ifdef STIR_OPENMP -# if _OPENMP >=201012 -# pragma omp atomic write -# else -# pragma omp critical(STIRSCATTERESTIMATIONCACHE) - { -# endif +# if _OPENMP >= 201012 +# pragma omp atomic write +# else +# pragma omp critical(STIRSCATTERESTIMATIONCACHE) + { +# endif #endif - *location_in_cache=result; -#if defined(STIR_OPENMP) && (_OPENMP <201012) - } + *location_in_cache = result; +#if defined(STIR_OPENMP) && (_OPENMP < 201012) +} #endif - return result; - } +return result; +} } -float -ScatterSimulation:: -cached_exp_integral_over_attenuation_image_between_scattpoint_det(const unsigned scatter_point_num, - const unsigned det_num) -{ - float * location_in_cache = - this->use_cache - ? &cached_attenuation_integral_scattpoint_det[scatter_point_num][det_num] - : 0; +float +ScatterSimulation::cached_exp_integral_over_attenuation_image_between_scattpoint_det(const unsigned scatter_point_num, + const unsigned det_num) { + float* location_in_cache = this->use_cache ? &cached_attenuation_integral_scattpoint_det[scatter_point_num][det_num] : 0; float value; #if defined(STIR_OPENMP) -# if _OPENMP >=201012 -# pragma omp atomic read -# else -# pragma omp critical(STIRSCATTERESTIMATIONREADCACHEATTENINT) +# if _OPENMP >= 201012 +# pragma omp atomic read +# else +# pragma omp critical(STIRSCATTERESTIMATIONREADCACHEATTENINT) { -# endif +# endif #endif value = *location_in_cache; -#if defined(STIR_OPENMP) && (_OPENMP <201012) - } +#if defined(STIR_OPENMP) && (_OPENMP < 201012) +} #endif - if (this->use_cache && value!=cache_init_value) - { - return *location_in_cache; - } - else - { - const float result = - exp_integral_over_attenuation_image_between_scattpoint_det - (scatt_points_vector[scatter_point_num].coord, - detection_points_vector[det_num] - ); - if (this->use_cache) +if (this->use_cache && value != cache_init_value) { + return *location_in_cache; +} else { + const float result = exp_integral_over_attenuation_image_between_scattpoint_det(scatt_points_vector[scatter_point_num].coord, + detection_points_vector[det_num]); + if (this->use_cache) #ifdef STIR_OPENMP -# if _OPENMP >=201012 -# pragma omp atomic write -# else -# pragma omp critical(STIRSCATTERESTIMATIONREADCACHEATTENINT) - { -# endif +# if _OPENMP >= 201012 +# pragma omp atomic write +# else +# pragma omp critical(STIRSCATTERESTIMATIONREADCACHEATTENINT) + { +# endif #endif - *location_in_cache=result; -#if defined(STIR_OPENMP) && (_OPENMP <201012) - } + *location_in_cache = result; +#if defined(STIR_OPENMP) && (_OPENMP < 201012) +} #endif - return result; - } +return result; +} } - + END_NAMESPACE_STIR diff --git a/src/scatter_buildblock/extradebug.cxx b/src/scatter_buildblock/extradebug.cxx index 3672aaa60a..56b1fcfe7e 100644 --- a/src/scatter_buildblock/extradebug.cxx +++ b/src/scatter_buildblock/extradebug.cxx @@ -1,61 +1,55 @@ #ifndef NDEBUGXXX - { - { - const VoxelsOnCartesianGrid& image = dynamic_cast&>(*activity_image_sptr); - const CartesianCoordinate3D voxel_size = image.get_grid_spacing(); - CartesianCoordinate3D origin = - image.get_origin(); - const float z_to_middle = - (image.get_max_index() + image.get_min_index())*voxel_size.z()/2.F; - origin.z() -= z_to_middle; - /* TODO replace with image.get_index_coordinates_for_physical_coordinates */ - info(boost::format("first/last z for activity image after shift: %1%/%2%") - % (origin.z() + image.get_min_index()*voxel_size.z()) % (origin.z() + image.get_max_index()*voxel_size.z())); - } - { - const VoxelsOnCartesianGrid& image = dynamic_cast&>(*density_image_sptr); - const CartesianCoordinate3D voxel_size = image.get_grid_spacing(); - CartesianCoordinate3D origin = - image.get_origin(); - const float z_to_middle = - (image.get_max_index() + image.get_min_index())*voxel_size.z()/2.F; - origin.z() -= z_to_middle; - /* TODO replace with image.get_index_coordinates_for_physical_coordinates */ - info(boost::format("first/last z for attenuation image after shift: %1%/%2%") - % (origin.z() + image.get_min_index()*voxel_size.z()) % (origin.z() + image.get_max_index()*voxel_size.z())); - } - { - const VoxelsOnCartesianGrid& image = dynamic_cast&>(*get_density_image_for_scatter_points_sptr()); - const CartesianCoordinate3D voxel_size = image.get_grid_spacing(); - CartesianCoordinate3D origin = - image.get_origin(); - const float z_to_middle = - (image.get_max_index() + image.get_min_index())*voxel_size.z()/2.F; - origin.z() -= z_to_middle; - /* TODO replace with image.get_index_coordinates_for_physical_coordinates */ - info(boost::format("first/last z for scatter-point image after shift: %1%/%2%") - % (origin.z() + image.get_min_index()*voxel_size.z()) % (origin.z() + image.get_max_index()*voxel_size.z())); - } - { - unsigned det_num_A, det_num_B; - find_detectors(det_num_A, det_num_B, Bin(0,0,this->proj_data_info_cyl_noarc_cor_sptr->get_min_axial_pos_num(0),0)); - const float first = detection_points_vector[det_num_A].z(); - find_detectors(det_num_A, det_num_B, Bin(0,0,this->proj_data_info_cyl_noarc_cor_sptr->get_max_axial_pos_num(0),0)); - const float last = detection_points_vector[det_num_A].z(); - info(boost::format("first/last z for detectors after shift: %1%/%2%") - % first % last); - } - } +{ + { + const VoxelsOnCartesianGrid& image = dynamic_cast&>(*activity_image_sptr); + const CartesianCoordinate3D voxel_size = image.get_grid_spacing(); + CartesianCoordinate3D origin = image.get_origin(); + const float z_to_middle = (image.get_max_index() + image.get_min_index()) * voxel_size.z() / 2.F; + origin.z() -= z_to_middle; + /* TODO replace with image.get_index_coordinates_for_physical_coordinates */ + info(boost::format("first/last z for activity image after shift: %1%/%2%") % + (origin.z() + image.get_min_index() * voxel_size.z()) % (origin.z() + image.get_max_index() * voxel_size.z())); + } + { + const VoxelsOnCartesianGrid& image = dynamic_cast&>(*density_image_sptr); + const CartesianCoordinate3D voxel_size = image.get_grid_spacing(); + CartesianCoordinate3D origin = image.get_origin(); + const float z_to_middle = (image.get_max_index() + image.get_min_index()) * voxel_size.z() / 2.F; + origin.z() -= z_to_middle; + /* TODO replace with image.get_index_coordinates_for_physical_coordinates */ + info(boost::format("first/last z for attenuation image after shift: %1%/%2%") % + (origin.z() + image.get_min_index() * voxel_size.z()) % (origin.z() + image.get_max_index() * voxel_size.z())); + } + { + const VoxelsOnCartesianGrid& image = + dynamic_cast&>(*get_density_image_for_scatter_points_sptr()); + const CartesianCoordinate3D voxel_size = image.get_grid_spacing(); + CartesianCoordinate3D origin = image.get_origin(); + const float z_to_middle = (image.get_max_index() + image.get_min_index()) * voxel_size.z() / 2.F; + origin.z() -= z_to_middle; + /* TODO replace with image.get_index_coordinates_for_physical_coordinates */ + info(boost::format("first/last z for scatter-point image after shift: %1%/%2%") % + (origin.z() + image.get_min_index() * voxel_size.z()) % (origin.z() + image.get_max_index() * voxel_size.z())); + } + { + unsigned det_num_A, det_num_B; + find_detectors(det_num_A, det_num_B, Bin(0, 0, this->proj_data_info_cyl_noarc_cor_sptr->get_min_axial_pos_num(0), 0)); + const float first = detection_points_vector[det_num_A].z(); + find_detectors(det_num_A, det_num_B, Bin(0, 0, this->proj_data_info_cyl_noarc_cor_sptr->get_max_axial_pos_num(0), 0)); + const float last = detection_points_vector[det_num_A].z(); + info(boost::format("first/last z for detectors after shift: %1%/%2%") % first % last); + } +} #endif - - sample #ifndef NDEBUG - { - const CartesianCoordinate3D first = voxel_size*convert_int_to_float(min_index) + origin; - const CartesianCoordinate3D last = voxel_size*convert_int_to_float(max_index) + origin; - info(boost::format("Coordinates of centre of first and last voxel of scatter-point image after shifting to centre of scanner: %1% / %2% centre %3%") - % first % last % ((first+last)/2)); - } +{ + const CartesianCoordinate3D first = voxel_size * convert_int_to_float(min_index) + origin; + const CartesianCoordinate3D last = voxel_size * convert_int_to_float(max_index) + origin; + info(boost::format("Coordinates of centre of first and last voxel of scatter-point image after shifting to centre of scanner: " + "%1% / %2% centre %3%") % + first % last % ((first + last) / 2)); +} #endif diff --git a/src/scatter_buildblock/sample_scatter_points.cxx b/src/scatter_buildblock/sample_scatter_points.cxx index b27c5c257c..ec5d84154a 100644 --- a/src/scatter_buildblock/sample_scatter_points.cxx +++ b/src/scatter_buildblock/sample_scatter_points.cxx @@ -12,8 +12,8 @@ This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - + GNU Lesser General Public License for more details. + See STIR/LICENSE.txt for details */ /*! @@ -34,68 +34,58 @@ using namespace std; START_NAMESPACE_STIR -static inline float random_point(const float low, const float high) -{ +static inline float +random_point(const float low, const float high) { /* returns a pseudo random number which holds in the bounds low and high */ - const float result= (rand()*(high-low))/RAND_MAX + low; + const float result = (rand() * (high - low)) / RAND_MAX + low; assert(low <= result); assert(high >= result); return result; } void -ScatterSimulation:: -sample_scatter_points() -{ +ScatterSimulation::sample_scatter_points() { - const DiscretisedDensityOnCartesianGrid<3,float>& attenuation_map = - dynamic_cast& > - (*this->density_image_for_scatter_points_sptr); + const DiscretisedDensityOnCartesianGrid<3, float>& attenuation_map = + dynamic_cast&>(*this->density_image_for_scatter_points_sptr); - BasicCoordinate<3,int> min_index, max_index ; + BasicCoordinate<3, int> min_index, max_index; CartesianCoordinate3D coord; - if(!this->density_image_for_scatter_points_sptr->get_regular_range(min_index, max_index)) - error("scatter points sampling works only on regular ranges, at the moment\n"); - const VoxelsOnCartesianGrid& image = - dynamic_cast&>(attenuation_map); - const CartesianCoordinate3D voxel_size = image.get_voxel_size(); - CartesianCoordinate3D origin = image.get_origin(); + if (!this->density_image_for_scatter_points_sptr->get_regular_range(min_index, max_index)) + error("scatter points sampling works only on regular ranges, at the moment\n"); + const VoxelsOnCartesianGrid& image = dynamic_cast&>(attenuation_map); + const CartesianCoordinate3D voxel_size = image.get_voxel_size(); + CartesianCoordinate3D origin = image.get_origin(); // shift origin such that we refer to the middle of the scanner // this is to be consistent with projector conventions // TODO use class function once it exists - const float z_to_middle = - (image.get_max_index() + image.get_min_index())*voxel_size.z()/2.F; + const float z_to_middle = (image.get_max_index() + image.get_min_index()) * voxel_size.z() / 2.F; origin.z() -= z_to_middle; - this->scatter_volume = voxel_size[1]*voxel_size[2]*voxel_size[3]; + this->scatter_volume = voxel_size[1] * voxel_size[2] * voxel_size[3]; - if(this->randomly_place_scatter_points) - { // Initialize Pseudo Random Number generator using time - srand((unsigned)time( NULL )); - } + if (this->randomly_place_scatter_points) { // Initialize Pseudo Random Number generator using time + srand((unsigned)time(NULL)); + } this->scatt_points_vector.resize(0); // make sure we don't keep scatter points from a previous run - this->scatt_points_vector.reserve(1000); + this->scatt_points_vector.reserve(1000); - // coord[] is in voxels units - for(coord[1]=min_index[1];coord[1]<=max_index[1];++coord[1]) - for(coord[2]=min_index[2];coord[2]<=max_index[2];++coord[2]) - for(coord[3]=min_index[3];coord[3]<=max_index[3];++coord[3]) - if(attenuation_map[coord] >= this->attenuation_threshold) - { - ScatterPoint scatter_point; - scatter_point.coord = convert_int_to_float(coord); - if (randomly_place_scatter_points) - scatter_point.coord += - CartesianCoordinate3D(random_point(-.5,.5), - random_point(-.5,.5), - random_point(-.5,.5)); - scatter_point.coord = - voxel_size*scatter_point.coord + origin; - scatter_point.mu_value = attenuation_map[coord]; - this->scatt_points_vector.push_back(scatter_point); - } + // coord[] is in voxels units + for (coord[1] = min_index[1]; coord[1] <= max_index[1]; ++coord[1]) + for (coord[2] = min_index[2]; coord[2] <= max_index[2]; ++coord[2]) + for (coord[3] = min_index[3]; coord[3] <= max_index[3]; ++coord[3]) + if (attenuation_map[coord] >= this->attenuation_threshold) { + ScatterPoint scatter_point; + scatter_point.coord = convert_int_to_float(coord); + if (randomly_place_scatter_points) + scatter_point.coord += + CartesianCoordinate3D(random_point(-.5, .5), random_point(-.5, .5), random_point(-.5, .5)); + scatter_point.coord = voxel_size * scatter_point.coord + origin; + scatter_point.mu_value = attenuation_map[coord]; + this->scatt_points_vector.push_back(scatter_point); + } this->remove_cache_for_integrals_over_activity(); this->remove_cache_for_integrals_over_attenuation(); info(boost::format("ScatterSimulation: using %1% scatter points") % this->scatt_points_vector.size(), 2); } -END_NAMESPACE_STIR +END_NAMESPACE_STIR diff --git a/src/scatter_buildblock/scatter_detection_modelling.cxx b/src/scatter_buildblock/scatter_detection_modelling.cxx index ad6124560f..f4f8f10d89 100644 --- a/src/scatter_buildblock/scatter_detection_modelling.cxx +++ b/src/scatter_buildblock/scatter_detection_modelling.cxx @@ -12,7 +12,7 @@ This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. + GNU Lesser General Public License for more details. See STIR/LICENSE.txt for details */ @@ -35,155 +35,112 @@ #include START_NAMESPACE_STIR -unsigned -ScatterSimulation:: -find_in_detection_points_vector(const CartesianCoordinate3D& coord) const -{ +unsigned +ScatterSimulation::find_in_detection_points_vector(const CartesianCoordinate3D& coord) const { #ifndef NDEBUG if (!this->_already_set_up) - error("ScatterSimulation::find_detectors: need to call set_up() first"); + error("ScatterSimulation::find_detectors: need to call set_up() first"); #endif unsigned int ret_value = 0; #pragma omp critical(SCATTERESTIMATIONFINDDETECTIONPOINTS) { - std::vector >::const_iterator iter= - std::find(detection_points_vector.begin(), - detection_points_vector.end(), - coord); - if (iter != detection_points_vector.end()) - { - ret_value = iter-detection_points_vector.begin(); - } - else - { - if (detection_points_vector.size()==static_cast(this->total_detectors)) + std::vector>::const_iterator iter = + std::find(detection_points_vector.begin(), detection_points_vector.end(), coord); + if (iter != detection_points_vector.end()) { + ret_value = iter - detection_points_vector.begin(); + } else { + if (detection_points_vector.size() == static_cast(this->total_detectors)) error("More detection points than we think there are!\n"); detection_points_vector.push_back(coord); - ret_value = detection_points_vector.size()-1; + ret_value = detection_points_vector.size() - 1; } } return ret_value; } void -ScatterSimulation:: -find_detectors(unsigned& det_num_A, unsigned& det_num_B, const Bin& bin) const -{ +ScatterSimulation::find_detectors(unsigned& det_num_A, unsigned& det_num_B, const Bin& bin) const { #ifndef NDEBUG if (!this->_already_set_up) - error("ScatterSimulation::find_detectors: need to call set_up() first"); + error("ScatterSimulation::find_detectors: need to call set_up() first"); #endif CartesianCoordinate3D detector_coord_A, detector_coord_B; - this->proj_data_info_cyl_noarc_cor_sptr-> - find_cartesian_coordinates_of_detection( - detector_coord_A,detector_coord_B,bin); - det_num_A = - this->find_in_detection_points_vector(detector_coord_A + - this->shift_detector_coordinates_to_origin); - det_num_B = - this->find_in_detection_points_vector(detector_coord_B + - this->shift_detector_coordinates_to_origin); + this->proj_data_info_cyl_noarc_cor_sptr->find_cartesian_coordinates_of_detection(detector_coord_A, detector_coord_B, bin); + det_num_A = this->find_in_detection_points_vector(detector_coord_A + this->shift_detector_coordinates_to_origin); + det_num_B = this->find_in_detection_points_vector(detector_coord_B + this->shift_detector_coordinates_to_origin); } float -ScatterSimulation:: -compute_emis_to_det_points_solid_angle_factor( - const CartesianCoordinate3D& emis_point, - const CartesianCoordinate3D& detector_coord) -{ - - const CartesianCoordinate3D dist_vector = emis_point - detector_coord ; - +ScatterSimulation::compute_emis_to_det_points_solid_angle_factor(const CartesianCoordinate3D& emis_point, + const CartesianCoordinate3D& detector_coord) { + + const CartesianCoordinate3D dist_vector = emis_point - detector_coord; const float dist_emis_det_squared = norm_squared(dist_vector); - const float emis_det_solid_angle_factor = 1.F/ dist_emis_det_squared ; + const float emis_det_solid_angle_factor = 1.F / dist_emis_det_squared; - return emis_det_solid_angle_factor ; + return emis_det_solid_angle_factor; } float -ScatterSimulation:: -detection_efficiency(const float energy) const -{ +ScatterSimulation::detection_efficiency(const float energy) const { #ifndef NDEBUG if (!this->_already_set_up) - error("ScatterSimulation::find_detectors: need to call set_up() first"); + error("ScatterSimulation::find_detectors: need to call set_up() first"); #endif // factor 2.35482 is used to convert FWHM to sigma - const float sigma_times_sqrt2= - sqrt(2.*energy*this->proj_data_info_cyl_noarc_cor_sptr->get_scanner_ptr()->get_reference_energy())* - this->proj_data_info_cyl_noarc_cor_sptr->get_scanner_ptr()->get_energy_resolution()/2.35482f; // 2.35482=2 * sqrt( 2 * ( log(2) ) - - // sigma_times_sqrt2= sqrt(2) * sigma // resolution proportional to FWHM - - const float efficiency = - 0.5f*( erf((this->template_exam_info_sptr->get_high_energy_thres()-energy)/sigma_times_sqrt2) - - erf((this->template_exam_info_sptr->get_low_energy_thres()-energy)/sigma_times_sqrt2 )); + const float sigma_times_sqrt2 = + sqrt(2. * energy * this->proj_data_info_cyl_noarc_cor_sptr->get_scanner_ptr()->get_reference_energy()) * + this->proj_data_info_cyl_noarc_cor_sptr->get_scanner_ptr()->get_energy_resolution() / + 2.35482f; // 2.35482=2 * sqrt( 2 * ( log(2) ) + + // sigma_times_sqrt2= sqrt(2) * sigma // resolution proportional to FWHM + + const float efficiency = 0.5f * (erf((this->template_exam_info_sptr->get_high_energy_thres() - energy) / sigma_times_sqrt2) - + erf((this->template_exam_info_sptr->get_low_energy_thres() - energy) / sigma_times_sqrt2)); /* Maximum efficiency is 1.*/ return efficiency; } float -ScatterSimulation:: -max_cos_angle(const float low, const float approx, const float resolution_at_511keV) -{ - return - 2.f - (8176.*log(2.))/(square(approx*resolution_at_511keV)*(511. + (16.*low*log(2.))/square(approx*resolution_at_511keV) - - sqrt(511.)*sqrt(511. + (32.*low*log(2.))/square(approx*resolution_at_511keV)))) ; +ScatterSimulation::max_cos_angle(const float low, const float approx, const float resolution_at_511keV) { + return 2.f - (8176. * log(2.)) / (square(approx * resolution_at_511keV) * + (511. + (16. * low * log(2.)) / square(approx * resolution_at_511keV) - + sqrt(511.) * sqrt(511. + (32. * low * log(2.)) / square(approx * resolution_at_511keV)))); } - float -ScatterSimulation:: -energy_lower_limit(const float low, const float approx, const float resolution_at_511keV) -{ - return - low + (approx*resolution_at_511keV)*(approx*resolution_at_511keV)*(46.0761 - 2.03829*sqrt(22.1807*low/square(approx*resolution_at_511keV)+511.)); +ScatterSimulation::energy_lower_limit(const float low, const float approx, const float resolution_at_511keV) { + return low + (approx * resolution_at_511keV) * (approx * resolution_at_511keV) * + (46.0761 - 2.03829 * sqrt(22.1807 * low / square(approx * resolution_at_511keV) + 511.)); } double -ScatterSimulation:: -detection_efficiency_no_scatter(const unsigned det_num_A, - const unsigned det_num_B) const -{ +ScatterSimulation::detection_efficiency_no_scatter(const unsigned det_num_A, const unsigned det_num_B) const { #ifndef NDEBUG if (!this->_already_set_up) - error("ScatterSimulation::find_detectors: need to call set_up() first"); + error("ScatterSimulation::find_detectors: need to call set_up() first"); #endif if (detector_efficiency_no_scatter <= 0.F) // set to negative value by set_up(), so recompute - { - detector_efficiency_no_scatter = - detection_efficiency(511.F) > 0 - ? detection_efficiency(511.F) - : (info("Zero detection efficiency for 511. Will normalise to 1"), 1.F); - } - const CartesianCoordinate3D& detector_coord_A = - detection_points_vector[det_num_A]; - const CartesianCoordinate3D& detector_coord_B = - detection_points_vector[det_num_B]; - const CartesianCoordinate3D - detA_to_ring_center(0,-detector_coord_A[2],-detector_coord_A[3]); - const CartesianCoordinate3D - detB_to_ring_center(0,-detector_coord_B[2],-detector_coord_B[3]); - const float rAB_squared=static_cast(norm_squared(detector_coord_A-detector_coord_B)); - const float cos_incident_angle_A = static_cast( - cos_angle(detector_coord_B - detector_coord_A, - detA_to_ring_center)) ; - const float cos_incident_angle_B = static_cast( - cos_angle(detector_coord_A - detector_coord_B, - detB_to_ring_center)) ; - - //0.75 is due to the volume of the pyramid approximation! - return - 1./( 0.75/2./_PI * - rAB_squared - /detector_efficiency_no_scatter/ - (cos_incident_angle_A* - cos_incident_angle_B)); + { + detector_efficiency_no_scatter = detection_efficiency(511.F) > 0 + ? detection_efficiency(511.F) + : (info("Zero detection efficiency for 511. Will normalise to 1"), 1.F); + } + const CartesianCoordinate3D& detector_coord_A = detection_points_vector[det_num_A]; + const CartesianCoordinate3D& detector_coord_B = detection_points_vector[det_num_B]; + const CartesianCoordinate3D detA_to_ring_center(0, -detector_coord_A[2], -detector_coord_A[3]); + const CartesianCoordinate3D detB_to_ring_center(0, -detector_coord_B[2], -detector_coord_B[3]); + const float rAB_squared = static_cast(norm_squared(detector_coord_A - detector_coord_B)); + const float cos_incident_angle_A = static_cast(cos_angle(detector_coord_B - detector_coord_A, detA_to_ring_center)); + const float cos_incident_angle_B = static_cast(cos_angle(detector_coord_A - detector_coord_B, detB_to_ring_center)); + + // 0.75 is due to the volume of the pyramid approximation! + return 1. / (0.75 / 2. / _PI * rAB_squared / detector_efficiency_no_scatter / (cos_incident_angle_A * cos_incident_angle_B)); } END_NAMESPACE_STIR diff --git a/src/scatter_buildblock/scatter_estimate_for_one_scatter_point.cxx b/src/scatter_buildblock/scatter_estimate_for_one_scatter_point.cxx index 37067ba4df..f6ee335768 100644 --- a/src/scatter_buildblock/scatter_estimate_for_one_scatter_point.cxx +++ b/src/scatter_buildblock/scatter_estimate_for_one_scatter_point.cxx @@ -13,8 +13,8 @@ This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - + GNU Lesser General Public License for more details. + See STIR/LICENSE.txt for details */ /*! @@ -32,7 +32,7 @@ #include "stir/scatter/ScatterSimulation.h" #ifndef NDEBUG // currently necessary for assert below -#include "stir/VoxelsOnCartesianGrid.h" +# include "stir/VoxelsOnCartesianGrid.h" #endif #include "stir/round.h" @@ -40,116 +40,80 @@ using namespace std; START_NAMESPACE_STIR -static const float total_Compton_cross_section_511keV = -ScatterSimulation:: - total_Compton_cross_section(511.F); +static const float total_Compton_cross_section_511keV = ScatterSimulation::total_Compton_cross_section(511.F); float -SingleScatterSimulation:: - simulate_for_one_scatter_point( - const std::size_t scatter_point_num, - const unsigned det_num_A, - const unsigned det_num_B) -{ +SingleScatterSimulation::simulate_for_one_scatter_point(const std::size_t scatter_point_num, const unsigned det_num_A, + const unsigned det_num_B) { if (this->max_single_scatter_cos_angle <= 0.F) // set to negative value by set_up(), so recompute - { - this->max_single_scatter_cos_angle=max_cos_angle(this->template_exam_info_sptr->get_low_energy_thres(), - 2.f, - this->proj_data_info_cyl_noarc_cor_sptr->get_scanner_ptr()->get_energy_resolution()); - } - - //static const float min_energy=energy_lower_limit(lower_energy_threshold,2.,energy_resolution); - - const CartesianCoordinate3D& scatter_point = - this->scatt_points_vector[scatter_point_num].coord; - const CartesianCoordinate3D& detector_coord_A = - this->detection_points_vector[det_num_A]; - const CartesianCoordinate3D& detector_coord_B = - this->detection_points_vector[det_num_B]; + { + this->max_single_scatter_cos_angle = + max_cos_angle(this->template_exam_info_sptr->get_low_energy_thres(), 2.f, + this->proj_data_info_cyl_noarc_cor_sptr->get_scanner_ptr()->get_energy_resolution()); + } + + // static const float min_energy=energy_lower_limit(lower_energy_threshold,2.,energy_resolution); + + const CartesianCoordinate3D& scatter_point = this->scatt_points_vector[scatter_point_num].coord; + const CartesianCoordinate3D& detector_coord_A = this->detection_points_vector[det_num_A]; + const CartesianCoordinate3D& detector_coord_B = this->detection_points_vector[det_num_B]; // note: costheta is -cos_angle such that it is 1 for zero scatter angle - const float costheta = static_cast( - -cos_angle(detector_coord_A - scatter_point, - detector_coord_B - scatter_point)); + const float costheta = static_cast(-cos_angle(detector_coord_A - scatter_point, detector_coord_B - scatter_point)); // note: costheta is identical for scatter to A or scatter to B // Hence, the Compton_cross_section and energy are identical for both cases as well. - if(this->max_single_scatter_cos_angle>costheta) + if (this->max_single_scatter_cos_angle > costheta) return 0; - const float new_energy = - photon_energy_after_Compton_scatter_511keV(costheta); + const float new_energy = photon_energy_after_Compton_scatter_511keV(costheta); - const float detection_efficiency_scatter = - detection_efficiency(new_energy); - if (detection_efficiency_scatter==0) + const float detection_efficiency_scatter = detection_efficiency(new_energy); + if (detection_efficiency_scatter == 0) return 0; const float emiss_to_detA = - cached_integral_over_activity_image_between_scattpoint_det - (static_cast (scatter_point_num), - det_num_A); - const float emiss_to_detB = - cached_integral_over_activity_image_between_scattpoint_det - (static_cast (scatter_point_num), - det_num_B); - if (emiss_to_detA==0 && emiss_to_detB==0) - return 0; - const float atten_to_detA = - cached_exp_integral_over_attenuation_image_between_scattpoint_det - (scatter_point_num, - det_num_A); - const float atten_to_detB = - cached_exp_integral_over_attenuation_image_between_scattpoint_det - (scatter_point_num, - det_num_B); - - const float dif_Compton_cross_section_value = - dif_Compton_cross_section(costheta, 511.F); - - const float rA_squared=static_cast(norm_squared(scatter_point-detector_coord_A)); - const float rB_squared=static_cast(norm_squared(scatter_point-detector_coord_B)); - - const float scatter_point_mu= - scatt_points_vector[scatter_point_num].mu_value; + cached_integral_over_activity_image_between_scattpoint_det(static_cast(scatter_point_num), det_num_A); + const float emiss_to_detB = + cached_integral_over_activity_image_between_scattpoint_det(static_cast(scatter_point_num), det_num_B); + if (emiss_to_detA == 0 && emiss_to_detB == 0) + return 0; + const float atten_to_detA = cached_exp_integral_over_attenuation_image_between_scattpoint_det(scatter_point_num, det_num_A); + const float atten_to_detB = cached_exp_integral_over_attenuation_image_between_scattpoint_det(scatter_point_num, det_num_B); + + const float dif_Compton_cross_section_value = dif_Compton_cross_section(costheta, 511.F); + + const float rA_squared = static_cast(norm_squared(scatter_point - detector_coord_A)); + const float rB_squared = static_cast(norm_squared(scatter_point - detector_coord_B)); + + const float scatter_point_mu = scatt_points_vector[scatter_point_num].mu_value; #ifndef NDEBUG - { + { // check if mu-value ok // currently terribly shift needed as in sample_scatter_points (TODO) const VoxelsOnCartesianGrid& image = - dynamic_cast&>(*this->get_density_image_for_scatter_points_sptr()); - const CartesianCoordinate3D voxel_size = image.get_voxel_size(); - const float z_to_middle = - (image.get_max_index() + image.get_min_index())*voxel_size.z()/2.F; - CartesianCoordinate3D shifted=scatter_point; + dynamic_cast&>(*this->get_density_image_for_scatter_points_sptr()); + const CartesianCoordinate3D voxel_size = image.get_voxel_size(); + const float z_to_middle = (image.get_max_index() + image.get_min_index()) * voxel_size.z() / 2.F; + CartesianCoordinate3D shifted = scatter_point; shifted.z() += z_to_middle; - assert(scatter_point_mu== - (*this->get_density_image_for_scatter_points_sptr())[this->get_density_image_for_scatter_points_sptr()->get_indices_closest_to_physical_coordinates(shifted)]); + assert(scatter_point_mu == + (*this->get_density_image_for_scatter_points_sptr())[this->get_density_image_for_scatter_points_sptr() + ->get_indices_closest_to_physical_coordinates(shifted)]); } #endif - float scatter_ratio=0 ; - - scatter_ratio= - (emiss_to_detA*(1.F/rB_squared)*pow(atten_to_detB,total_Compton_cross_section_relative_to_511keV(new_energy)-1) - +emiss_to_detB*(1.F/rA_squared)*pow(atten_to_detA,total_Compton_cross_section_relative_to_511keV(new_energy)-1)) - *atten_to_detB - *atten_to_detA - *scatter_point_mu - *detection_efficiency_scatter; - - - const CartesianCoordinate3D - detA_to_ring_center(0,-detector_coord_A[2],-detector_coord_A[3]); - const CartesianCoordinate3D - detB_to_ring_center(0,-detector_coord_B[2],-detector_coord_B[3]); - const float cos_incident_angle_AS = static_cast( - cos_angle(scatter_point - detector_coord_A, - detA_to_ring_center)) ; - const float cos_incident_angle_BS = static_cast( - cos_angle(scatter_point - detector_coord_B, - detB_to_ring_center)) ; - - return scatter_ratio*cos_incident_angle_AS*cos_incident_angle_BS*dif_Compton_cross_section_value; - + float scatter_ratio = 0; + + scatter_ratio = + (emiss_to_detA * (1.F / rB_squared) * pow(atten_to_detB, total_Compton_cross_section_relative_to_511keV(new_energy) - 1) + + emiss_to_detB * (1.F / rA_squared) * pow(atten_to_detA, total_Compton_cross_section_relative_to_511keV(new_energy) - 1)) * + atten_to_detB * atten_to_detA * scatter_point_mu * detection_efficiency_scatter; + + const CartesianCoordinate3D detA_to_ring_center(0, -detector_coord_A[2], -detector_coord_A[3]); + const CartesianCoordinate3D detB_to_ring_center(0, -detector_coord_B[2], -detector_coord_B[3]); + const float cos_incident_angle_AS = static_cast(cos_angle(scatter_point - detector_coord_A, detA_to_ring_center)); + const float cos_incident_angle_BS = static_cast(cos_angle(scatter_point - detector_coord_B, detB_to_ring_center)); + + return scatter_ratio * cos_incident_angle_AS * cos_incident_angle_BS * dif_Compton_cross_section_value; } END_NAMESPACE_STIR diff --git a/src/scatter_buildblock/single_scatter_estimate.cxx b/src/scatter_buildblock/single_scatter_estimate.cxx index 0c5e1f964b..b5276e5ce1 100644 --- a/src/scatter_buildblock/single_scatter_estimate.cxx +++ b/src/scatter_buildblock/single_scatter_estimate.cxx @@ -11,7 +11,7 @@ This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. + GNU Lesser General Public License for more details. See STIR/LICENSE.txt for details */ @@ -27,47 +27,30 @@ */ #include "stir/scatter/SingleScatterSimulation.h" START_NAMESPACE_STIR -static const float total_Compton_cross_section_511keV = -ScatterSimulation:: - total_Compton_cross_section(511.F); +static const float total_Compton_cross_section_511keV = ScatterSimulation::total_Compton_cross_section(511.F); double -SingleScatterSimulation:: -scatter_estimate(const Bin& bin) -{ +SingleScatterSimulation::scatter_estimate(const Bin& bin) { double scatter_ratio_singles = 0; unsigned det_num_A = 0; // initialise to avoid compiler warnings unsigned det_num_B = 0; this->find_detectors(det_num_A, det_num_B, bin); - this->actual_scatter_estimate(scatter_ratio_singles, - det_num_A, - det_num_B); + this->actual_scatter_estimate(scatter_ratio_singles, det_num_A, det_num_B); - return scatter_ratio_singles; + return scatter_ratio_singles; } - void -SingleScatterSimulation:: -actual_scatter_estimate(double& scatter_ratio_singles, - const unsigned det_num_A, - const unsigned det_num_B) -{ +SingleScatterSimulation::actual_scatter_estimate(double& scatter_ratio_singles, const unsigned det_num_A, + const unsigned det_num_B) { scatter_ratio_singles = 0; - - for(std::size_t scatter_point_num =0; - scatter_point_num < this->scatt_points_vector.size(); - ++scatter_point_num) - { - scatter_ratio_singles += - simulate_for_one_scatter_point( - scatter_point_num, - det_num_A, det_num_B); - - } + + for (std::size_t scatter_point_num = 0; scatter_point_num < this->scatt_points_vector.size(); ++scatter_point_num) { + scatter_ratio_singles += simulate_for_one_scatter_point(scatter_point_num, det_num_A, det_num_B); + } // we will divide by the effiency of the detector pair for unscattered photons // (computed with the same detection model as used in the scatter code) @@ -78,8 +61,7 @@ actual_scatter_estimate(double& scatter_ratio_singles, // the factors total_Compton_cross_section_511keV should probably be moved to the scatter_computation code const double common_factor = - 1/detection_efficiency_no_scatter(det_num_A, det_num_B) * - scatter_volume/total_Compton_cross_section_511keV; + 1 / detection_efficiency_no_scatter(det_num_A, det_num_B) * scatter_volume / total_Compton_cross_section_511keV; scatter_ratio_singles *= common_factor; } diff --git a/src/scatter_buildblock/single_scatter_integrals.cxx b/src/scatter_buildblock/single_scatter_integrals.cxx index 843f692068..54f5ccef43 100644 --- a/src/scatter_buildblock/single_scatter_integrals.cxx +++ b/src/scatter_buildblock/single_scatter_integrals.cxx @@ -13,8 +13,8 @@ This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - + GNU Lesser General Public License for more details. + See STIR/LICENSE.txt for details */ /*! @@ -22,13 +22,13 @@ \ingroup scatter \brief Implementations of integrating functions in stir::ScatterEstimationByBin - Functions calculates the integral along LOR in an image (attenuation or emission). + Functions calculates the integral along LOR in an image (attenuation or emission). (from scatter point to detector coordinate) - + \author Pablo Aguiar \author Charalampos Tsoumpas \author Kris Thielemans - + */ #include "stir/scatter/ScatterSimulation.h" #include "stir/VoxelsOnCartesianGrid.h" @@ -36,111 +36,79 @@ #include "stir/recon_buildblock/RayTraceVoxelsOnCartesianGrid.h" START_NAMESPACE_STIR -float -ScatterSimulation:: -exp_integral_over_attenuation_image_between_scattpoint_det (const CartesianCoordinate3D& scatter_point, - const CartesianCoordinate3D& detector_coord) -{ -#ifndef NEWSCALE - /* projectors work in pixel units, so convert attenuation data +float +ScatterSimulation::exp_integral_over_attenuation_image_between_scattpoint_det( + const CartesianCoordinate3D& scatter_point, const CartesianCoordinate3D& detector_coord) { +#ifndef NEWSCALE + /* projectors work in pixel units, so convert attenuation data from cm^-1 to pixel_units^-1 */ - const float rescale = - dynamic_cast &>(*density_image_sptr). - get_grid_spacing()[3]/10; + const float rescale = + dynamic_cast&>(*density_image_sptr).get_grid_spacing()[3] / 10; #else - const float rescale = - 0.1F; + const float rescale = 0.1F; #endif - return - exp(-rescale* - integral_between_2_points(*density_image_sptr, - scatter_point, - detector_coord) - ); + return exp(-rescale * integral_between_2_points(*density_image_sptr, scatter_point, detector_coord)); } - float -ScatterSimulation:: -integral_over_activity_image_between_scattpoint_det (const CartesianCoordinate3D& scatter_point, - const CartesianCoordinate3D& detector_coord) -{ +ScatterSimulation::integral_over_activity_image_between_scattpoint_det(const CartesianCoordinate3D& scatter_point, + const CartesianCoordinate3D& detector_coord) { { - const CartesianCoordinate3D dist_vector = scatter_point - detector_coord ; + const CartesianCoordinate3D dist_vector = scatter_point - detector_coord; const float dist_sp1_det_squared = norm_squared(dist_vector); - const float solid_angle_factor = - std::min(static_cast(_PI/2), 1.F / dist_sp1_det_squared) ; - - return - solid_angle_factor * - integral_between_2_points(*activity_image_sptr, - scatter_point, - detector_coord); + const float solid_angle_factor = std::min(static_cast(_PI / 2), 1.F / dist_sp1_det_squared); + + return solid_angle_factor * integral_between_2_points(*activity_image_sptr, scatter_point, detector_coord); } } -float -ScatterSimulation:: -integral_between_2_points(const DiscretisedDensity<3,float>& density, - const CartesianCoordinate3D& scatter_point, - const CartesianCoordinate3D& detector_coord) -{ +float +ScatterSimulation::integral_between_2_points(const DiscretisedDensity<3, float>& density, + const CartesianCoordinate3D& scatter_point, + const CartesianCoordinate3D& detector_coord) { + const VoxelsOnCartesianGrid& image = dynamic_cast&>(density); - const VoxelsOnCartesianGrid& image = - dynamic_cast& > - (density); - const CartesianCoordinate3D voxel_size = image.get_grid_spacing(); - - CartesianCoordinate3D origin = - image.get_origin(); - const float z_to_middle = - (image.get_max_index() + image.get_min_index())*voxel_size.z()/2.F; + + CartesianCoordinate3D origin = image.get_origin(); + const float z_to_middle = (image.get_max_index() + image.get_min_index()) * voxel_size.z() / 2.F; origin.z() -= z_to_middle; /* TODO replace with image.get_index_coordinates_for_physical_coordinates */ ProjMatrixElemsForOneBin lor; - RayTraceVoxelsOnCartesianGrid(lor, - (scatter_point-origin)/voxel_size, // should be in voxel units - (detector_coord-origin)/voxel_size, // should be in voxel units - voxel_size, //should be in mm + RayTraceVoxelsOnCartesianGrid(lor, + (scatter_point - origin) / voxel_size, // should be in voxel units + (detector_coord - origin) / voxel_size, // should be in voxel units + voxel_size, // should be in mm #ifdef NEWSCALE 1.F // normalise to mm #else - 1/voxel_size.x() // normalise to some kind of 'pixel units' + 1 / voxel_size.x() // normalise to some kind of 'pixel units' #endif - ); + ); lor.sort(); - float sum = 0; // add up values along LOR - { - ProjMatrixElemsForOneBin::iterator element_ptr =lor.begin() ; + float sum = 0; // add up values along LOR + { + ProjMatrixElemsForOneBin::iterator element_ptr = lor.begin(); bool we_have_been_within_the_image = false; - while (element_ptr != lor.end()) - { - const BasicCoordinate<3,int> coords = element_ptr->get_coords(); - if (coords[1] >= image.get_min_index() && - coords[1] <= image.get_max_index() && - coords[2] >= image[coords[1]].get_min_index() && - coords[2] <= image[coords[1]].get_max_index() && - coords[3] >= image[coords[1]][coords[2]].get_min_index() && - coords[3] <= image[coords[1]][coords[2]].get_max_index()) - { - we_have_been_within_the_image = true; - sum += image[coords] * element_ptr->get_value(); - } - else if (we_have_been_within_the_image) - { - // we jump out of the loop as we are now at the other side of - // the image - // break; - } - ++element_ptr; - } - } - return sum; -} + while (element_ptr != lor.end()) { + const BasicCoordinate<3, int> coords = element_ptr->get_coords(); + if (coords[1] >= image.get_min_index() && coords[1] <= image.get_max_index() && + coords[2] >= image[coords[1]].get_min_index() && coords[2] <= image[coords[1]].get_max_index() && + coords[3] >= image[coords[1]][coords[2]].get_min_index() && coords[3] <= image[coords[1]][coords[2]].get_max_index()) { + we_have_been_within_the_image = true; + sum += image[coords] * element_ptr->get_value(); + } else if (we_have_been_within_the_image) { + // we jump out of the loop as we are now at the other side of + // the image + // break; + } + ++element_ptr; + } + } + return sum; +} END_NAMESPACE_STIR - diff --git a/src/scatter_buildblock/upsample_and_fit_scatter_estimate.cxx b/src/scatter_buildblock/upsample_and_fit_scatter_estimate.cxx index 4560fdb5f9..009bc1fbdc 100644 --- a/src/scatter_buildblock/upsample_and_fit_scatter_estimate.cxx +++ b/src/scatter_buildblock/upsample_and_fit_scatter_estimate.cxx @@ -11,8 +11,8 @@ This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - + GNU Lesser General Public License for more details. + See STIR/LICENSE.txt for details */ /*! @@ -33,7 +33,7 @@ #include "stir/recon_buildblock/BinNormalisation.h" #include "stir/interpolate_projdata.h" #include "stir/utilities.h" -#include "stir/IndexRange2D.h" +#include "stir/IndexRange2D.h" #include "stir/stream.h" #include "stir/Succeeded.h" #include "stir/thresholding.h" @@ -46,106 +46,70 @@ START_NAMESPACE_STIR -void -ScatterEstimation:: -upsample_and_fit_scatter_estimate(ProjData& scaled_scatter_proj_data, - const ProjData& emission_proj_data, - const ProjData& scatter_proj_data, - BinNormalisation& scatter_normalisation, - const ProjData& weights_proj_data, - const float min_scale_factor, - const float max_scale_factor, - const unsigned half_filter_width, - BSpline::BSplineType spline_type, - const bool remove_interleaving) -{ - shared_ptr - interpolated_direct_scatter_proj_data_info_sptr(emission_proj_data.get_proj_data_info_sptr()->clone()); - interpolated_direct_scatter_proj_data_info_sptr->reduce_segment_range(0,0); +void +ScatterEstimation::upsample_and_fit_scatter_estimate(ProjData& scaled_scatter_proj_data, const ProjData& emission_proj_data, + const ProjData& scatter_proj_data, BinNormalisation& scatter_normalisation, + const ProjData& weights_proj_data, const float min_scale_factor, + const float max_scale_factor, const unsigned half_filter_width, + BSpline::BSplineType spline_type, const bool remove_interleaving) { + shared_ptr interpolated_direct_scatter_proj_data_info_sptr(emission_proj_data.get_proj_data_info_sptr()->clone()); + interpolated_direct_scatter_proj_data_info_sptr->reduce_segment_range(0, 0); info("upsample_and_fit_scatter_estimate: Interpolating scatter estimate to size of emission data"); ProjDataInMemory interpolated_direct_scatter(emission_proj_data.get_exam_info_sptr(), - interpolated_direct_scatter_proj_data_info_sptr); + interpolated_direct_scatter_proj_data_info_sptr); interpolate_projdata(interpolated_direct_scatter, scatter_proj_data, spline_type, remove_interleaving); - const TimeFrameDefinitions& time_frame_defs = - emission_proj_data.get_exam_info_sptr()->time_frame_definitions; - - if (min_scale_factor != 1 || max_scale_factor != 1 || !scatter_normalisation.is_trivial()) - { - ProjDataInMemory interpolated_scatter(emission_proj_data.get_exam_info_sptr(), - emission_proj_data.get_proj_data_info_sptr()->create_shared_clone()); - inverse_SSRB(interpolated_scatter, interpolated_direct_scatter); - - scatter_normalisation.set_up(emission_proj_data.get_exam_info_sptr(), emission_proj_data.get_proj_data_info_sptr()->create_shared_clone()); - scatter_normalisation.undo(interpolated_scatter, - time_frame_defs.get_start_time(), time_frame_defs.get_end_time()); - Array<2,float> scale_factors; - - if (min_scale_factor == max_scale_factor) - { - if (min_scale_factor == 1.F) - { - scaled_scatter_proj_data.fill(interpolated_scatter); - return; // all done - } - - const ProjDataInfo& proj_data_info = *emission_proj_data.get_proj_data_info_sptr(); - IndexRange2D sinogram_range(proj_data_info.get_min_segment_num(),proj_data_info.get_max_segment_num(),0,0); - for (int segment_num=proj_data_info.get_min_segment_num(); - segment_num<=proj_data_info.get_max_segment_num(); - ++segment_num) - { - sinogram_range[segment_num].resize( - proj_data_info.get_min_axial_pos_num(segment_num), - proj_data_info.get_max_axial_pos_num(segment_num) ); - } - scale_factors.grow(sinogram_range); - scale_factors.fill(min_scale_factor); - } - else - { - info("upsample_and_fit_scatter_estimate: Finding scale factors by sinogram", 3); - scale_factors = get_scale_factors_per_sinogram( - emission_proj_data, - interpolated_scatter, - weights_proj_data); - - info(boost::format("upsample_and_fit_scatter_estimate: scale factors before thresholding:\n%1%") % - scale_factors, - 2); - - threshold_lower(scale_factors.begin_all(), - scale_factors.end_all(), - min_scale_factor); - threshold_upper(scale_factors.begin_all(), - scale_factors.end_all(), - max_scale_factor); - info(boost::format("upsample_and_fit_scatter_estimate: scale factors after thresholding:\n%1%") % - scale_factors, - 2); - VectorWithOffset kernel(-static_cast(half_filter_width),half_filter_width); - kernel.fill(1.F/(2*half_filter_width+1)); - ArrayFilter1DUsingConvolution lowpass_filter(kernel, BoundaryConditions::constant); - std::for_each(scale_factors.begin(), - scale_factors.end(), - lowpass_filter); - info(boost::format("upsample_and_fit_scatter_estimate: scale factors after filtering:\n%1%") % - scale_factors, - 2); - } - info("upsample_and_fit_scatter_estimate: applying scale factors", 3); - if (scale_sinograms(scaled_scatter_proj_data, - interpolated_scatter, - scale_factors) != Succeeded::yes) - { - error("upsample_and_fit_scatter_estimate: writing of scaled sinograms failed"); - } + const TimeFrameDefinitions& time_frame_defs = emission_proj_data.get_exam_info_sptr()->time_frame_definitions; + + if (min_scale_factor != 1 || max_scale_factor != 1 || !scatter_normalisation.is_trivial()) { + ProjDataInMemory interpolated_scatter(emission_proj_data.get_exam_info_sptr(), + emission_proj_data.get_proj_data_info_sptr()->create_shared_clone()); + inverse_SSRB(interpolated_scatter, interpolated_direct_scatter); + + scatter_normalisation.set_up(emission_proj_data.get_exam_info_sptr(), + emission_proj_data.get_proj_data_info_sptr()->create_shared_clone()); + scatter_normalisation.undo(interpolated_scatter, time_frame_defs.get_start_time(), time_frame_defs.get_end_time()); + Array<2, float> scale_factors; + + if (min_scale_factor == max_scale_factor) { + if (min_scale_factor == 1.F) { + scaled_scatter_proj_data.fill(interpolated_scatter); + return; // all done + } + + const ProjDataInfo& proj_data_info = *emission_proj_data.get_proj_data_info_sptr(); + IndexRange2D sinogram_range(proj_data_info.get_min_segment_num(), proj_data_info.get_max_segment_num(), 0, 0); + for (int segment_num = proj_data_info.get_min_segment_num(); segment_num <= proj_data_info.get_max_segment_num(); + ++segment_num) { + sinogram_range[segment_num].resize(proj_data_info.get_min_axial_pos_num(segment_num), + proj_data_info.get_max_axial_pos_num(segment_num)); + } + scale_factors.grow(sinogram_range); + scale_factors.fill(min_scale_factor); + } else { + info("upsample_and_fit_scatter_estimate: Finding scale factors by sinogram", 3); + scale_factors = get_scale_factors_per_sinogram(emission_proj_data, interpolated_scatter, weights_proj_data); + + info(boost::format("upsample_and_fit_scatter_estimate: scale factors before thresholding:\n%1%") % scale_factors, 2); + + threshold_lower(scale_factors.begin_all(), scale_factors.end_all(), min_scale_factor); + threshold_upper(scale_factors.begin_all(), scale_factors.end_all(), max_scale_factor); + info(boost::format("upsample_and_fit_scatter_estimate: scale factors after thresholding:\n%1%") % scale_factors, 2); + VectorWithOffset kernel(-static_cast(half_filter_width), half_filter_width); + kernel.fill(1.F / (2 * half_filter_width + 1)); + ArrayFilter1DUsingConvolution lowpass_filter(kernel, BoundaryConditions::constant); + std::for_each(scale_factors.begin(), scale_factors.end(), lowpass_filter); + info(boost::format("upsample_and_fit_scatter_estimate: scale factors after filtering:\n%1%") % scale_factors, 2); } - else // min/max_scale_factor equal to 1 and no norm - { - inverse_SSRB(scaled_scatter_proj_data, interpolated_direct_scatter); + info("upsample_and_fit_scatter_estimate: applying scale factors", 3); + if (scale_sinograms(scaled_scatter_proj_data, interpolated_scatter, scale_factors) != Succeeded::yes) { + error("upsample_and_fit_scatter_estimate: writing of scaled sinograms failed"); } + } else // min/max_scale_factor equal to 1 and no norm + { + inverse_SSRB(scaled_scatter_proj_data, interpolated_direct_scatter); + } } END_NAMESPACE_STIR diff --git a/src/scatter_utilities/create_tail_mask_from_ACFs.cxx b/src/scatter_utilities/create_tail_mask_from_ACFs.cxx index 5849f594c1..9ae09f7473 100644 --- a/src/scatter_utilities/create_tail_mask_from_ACFs.cxx +++ b/src/scatter_utilities/create_tail_mask_from_ACFs.cxx @@ -12,7 +12,7 @@ This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. + GNU Lesser General Public License for more details. See STIR/LICENSE.txt for details */ @@ -28,7 +28,7 @@ \author Nikos Efthimiou \author Kris Thielemans - + \par Usage: \verbatim @@ -62,106 +62,89 @@ #include "stir/scatter/CreateTailMaskFromACFs.h" - -/***********************************************************/ +/***********************************************************/ static void -print_usage_and_exit(const char * const prog_name) -{ - std::cerr << "\nUsage:\n" << prog_name << "\n" - << "\t--ACF-filename \n" - << "\t--output-filename \n" - << "\t[--ACF-threshold ]\n" - << "\t[--safety-margin ]\n" - << "ACF-threshold defaults to 1.1, safety-margin to 4\n" - << "Alternative Usage:\n" - << "Create_tail_mask_from_ACFs parameters.par\n" - << "Example par file:"; - exit(EXIT_FAILURE); +print_usage_and_exit(const char* const prog_name) { + std::cerr << "\nUsage:\n" + << prog_name << "\n" + << "\t--ACF-filename \n" + << "\t--output-filename \n" + << "\t[--ACF-threshold ]\n" + << "\t[--safety-margin ]\n" + << "ACF-threshold defaults to 1.1, safety-margin to 4\n" + << "Alternative Usage:\n" + << "Create_tail_mask_from_ACFs parameters.par\n" + << "Example par file:"; + exit(EXIT_FAILURE); } -int main(int argc, const char *argv[]) -{ - USING_NAMESPACE_STIR; - const char * const prog_name = argv[0]; - - CreateTailMaskFromACFs create_tail_mask_from_ACFs; - - - - // If one arg is supplied and it is a par file - // then use the Create_tail_mask_from_ACFs to parse the - // file. Otherwise continue the old way. - if (argc == 2) - { - std::stringstream hdr_stream(argv[1]); - std::string sargv = hdr_stream.str(); - - size_t lastindex = sargv.find_last_of("."); - std::string extension = sargv.substr(lastindex); - std::string par_ext = ".par"; - if ( extension.compare(par_ext) != 0 ) - error("Please provide a valid par file."); - - if (create_tail_mask_from_ACFs.parse(argv[1]) == false) - { - warning("Create_tail_mask_from_ACFs aborting because error in parsing. Not writing any output"); - return EXIT_FAILURE; - } +int +main(int argc, const char* argv[]) { + USING_NAMESPACE_STIR; + const char* const prog_name = argv[0]; + + CreateTailMaskFromACFs create_tail_mask_from_ACFs; + + // If one arg is supplied and it is a par file + // then use the Create_tail_mask_from_ACFs to parse the + // file. Otherwise continue the old way. + if (argc == 2) { + std::stringstream hdr_stream(argv[1]); + std::string sargv = hdr_stream.str(); + + size_t lastindex = sargv.find_last_of("."); + std::string extension = sargv.substr(lastindex); + std::string par_ext = ".par"; + if (extension.compare(par_ext) != 0) + error("Please provide a valid par file."); + + if (create_tail_mask_from_ACFs.parse(argv[1]) == false) { + warning("Create_tail_mask_from_ACFs aborting because error in parsing. Not writing any output"); + return EXIT_FAILURE; + } + } else { + // option processing + float ACF_threshold = 1.1F; + int safety_margin = 4; + std::string ACF_filename; + std::string output_filename; + + while (argc > 2 && argv[1][1] == '-') { + if (strcmp(argv[1], "--ACF-filename") == 0) { + ACF_filename = (argv[2]); + argc -= 2; + argv += 2; + } else if (strcmp(argv[1], "--output-filename") == 0) { + output_filename = (argv[2]); + argc -= 2; + argv += 2; + } else if (strcmp(argv[1], "--ACF-threshold") == 0) { + ACF_threshold = float(atof(argv[2])); + argc -= 2; + argv += 2; + } else if (strcmp(argv[1], "--safety-margin") == 0) { + safety_margin = atoi(argv[2]); + argc -= 2; + argv += 2; + } else { + std::cerr << "\nUnknown option: " << argv[1]; + print_usage_and_exit(prog_name); + } } - else - { - // option processing - float ACF_threshold = 1.1F; - int safety_margin=4; - std::string ACF_filename; - std::string output_filename; - - while (argc>2 && argv[1][1] == '-') - { - if (strcmp(argv[1], "--ACF-filename")==0) - { - ACF_filename = (argv[2]); - argc-=2; argv +=2; - } - else if (strcmp(argv[1], "--output-filename")==0) - { - output_filename = (argv[2]); - argc-=2; argv +=2; - } - else if (strcmp(argv[1], "--ACF-threshold")==0) - { - ACF_threshold = float(atof(argv[2])); - argc-=2; argv +=2; - } - else if (strcmp(argv[1], "--safety-margin")==0) - { - safety_margin = atoi(argv[2]); - argc-=2; argv +=2; - } - else - { - std::cerr << "\nUnknown option: " << argv[1]; - print_usage_and_exit(prog_name); - } - } - - if (argc!=1 || ACF_filename.size()==0 || output_filename.size()==0) - { - print_usage_and_exit(prog_name); - } - - // Use CreateTailMaskFromACFs::set_up to parse the parameters - create_tail_mask_from_ACFs.set_input_projdata(ACF_filename); - create_tail_mask_from_ACFs.set_output_projdata(output_filename); - create_tail_mask_from_ACFs.ACF_threshold = ACF_threshold; - create_tail_mask_from_ACFs.safety_margin = safety_margin; + if (argc != 1 || ACF_filename.size() == 0 || output_filename.size() == 0) { + print_usage_and_exit(prog_name); } - // Onwards the new class will do the job ... + // Use CreateTailMaskFromACFs::set_up to parse the parameters + create_tail_mask_from_ACFs.set_input_projdata(ACF_filename); + create_tail_mask_from_ACFs.set_output_projdata(output_filename); + create_tail_mask_from_ACFs.ACF_threshold = ACF_threshold; + create_tail_mask_from_ACFs.safety_margin = safety_margin; + } - return create_tail_mask_from_ACFs.process_data() == stir::Succeeded::yes ? - EXIT_SUCCESS : EXIT_FAILURE; -} - + // Onwards the new class will do the job ... + + return create_tail_mask_from_ACFs.process_data() == stir::Succeeded::yes ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/src/scatter_utilities/estimate_scatter.cxx b/src/scatter_utilities/estimate_scatter.cxx index 95418e8043..f471f4ad84 100644 --- a/src/scatter_utilities/estimate_scatter.cxx +++ b/src/scatter_utilities/estimate_scatter.cxx @@ -12,7 +12,7 @@ This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. + GNU Lesser General Public License for more details. See STIR/LICENSE.txt for details */ @@ -23,8 +23,8 @@ \brief Estimates a coarse scatter sinogram \author Kris Thielemans - - + + \par Usage: \code estimate_scatter parfile @@ -35,33 +35,28 @@ #include "stir/scatter/ScatterEstimation.h" #include "stir/Succeeded.h" -/***********************************************************/ +/***********************************************************/ -static void print_usage_and_exit() -{ - std::cerr<<"This executable runs a Scatter simulation method based on the options " +static void +print_usage_and_exit() { + std::cerr << "This executable runs a Scatter simulation method based on the options " "in a parameter file"; - std::cerr<<"\nUsage:\n simulate_scatter scatter_simulation.par\n"; - std::cerr<<"Example parameter file can be found in the samples folder :\n" - << std::endl; - exit(EXIT_FAILURE); + std::cerr << "\nUsage:\n simulate_scatter scatter_simulation.par\n"; + std::cerr << "Example parameter file can be found in the samples folder :\n" << std::endl; + exit(EXIT_FAILURE); } /***********************************************************/ -int main(int argc, const char *argv[]) -{ - stir::ScatterEstimation scatter_estimation; - - if (argc==2) - { - if (scatter_estimation.parse(argv[1]) == false) - return EXIT_FAILURE; - } - else - print_usage_and_exit(); - - return - (scatter_estimation.set_up() == stir::Succeeded::yes) - && (scatter_estimation.process_data() == stir::Succeeded::yes) ? - EXIT_SUCCESS : EXIT_FAILURE; +int +main(int argc, const char* argv[]) { + stir::ScatterEstimation scatter_estimation; + + if (argc == 2) { + if (scatter_estimation.parse(argv[1]) == false) + return EXIT_FAILURE; + } else + print_usage_and_exit(); + + return (scatter_estimation.set_up() == stir::Succeeded::yes) && (scatter_estimation.process_data() == stir::Succeeded::yes) + ? EXIT_SUCCESS + : EXIT_FAILURE; } - diff --git a/src/scatter_utilities/simulate_scatter.cxx b/src/scatter_utilities/simulate_scatter.cxx index 6f3ba89a27..38c9fb0d40 100644 --- a/src/scatter_utilities/simulate_scatter.cxx +++ b/src/scatter_utilities/simulate_scatter.cxx @@ -41,12 +41,12 @@ using std::cerr; using std::cout; using std::endl; -static void print_usage_and_exit() -{ - std::cerr<<"This executable runs a Scatter simulation method based on the options " +static void +print_usage_and_exit() { + std::cerr << "This executable runs a Scatter simulation method based on the options " "in a parameter file"; - std::cerr<<"\nUsage:\n simulate_scatter scatter_simulation.par\n"; - std::cerr<<"Example minimal parameter file:\n\n" + std::cerr << "\nUsage:\n simulate_scatter scatter_simulation.par\n"; + std::cerr << "Example minimal parameter file:\n\n" "Scatter Simulation Parameters :=\n" "Simulation type := PET Single Scatter Simulation\n" "PET Single Scatter Simulation Parameters :=\n" @@ -55,45 +55,45 @@ static void print_usage_and_exit() "activity image filename :=\n" "output filename prefix := \n" "End PET Single Scatter Simulation Parameters :=\n" - "End Scatter Simulation Parameters:="<< std::endl; - exit(EXIT_FAILURE); + "End Scatter Simulation Parameters:=" + << std::endl; + exit(EXIT_FAILURE); } /***********************************************************/ -int main(int argc, const char *argv[]) -{ - USING_NAMESPACE_STIR - - HighResWallClockTimer t; - t.reset(); - t.start(); - - if (argc!=2) - print_usage_and_exit(); - - shared_ptr < ScatterSimulation > - simulation_method_sptr; - - KeyParser parser; - parser.add_start_key("Scatter Simulation Parameters"); - parser.add_stop_key("End Scatter Simulation Parameters"); - parser.add_parsing_key("Scatter Simulation type", &simulation_method_sptr); - if (!parser.parse(argv[1])) - { t.stop(); return EXIT_FAILURE; } - - if(simulation_method_sptr->set_up() == Succeeded::no) - { t.stop(); return EXIT_FAILURE; } - - if(simulation_method_sptr->process_data() == stir::Succeeded::yes) - { - t.stop(); - cout << "Total Wall clock time: " << t.value() << " seconds" << endl; - return EXIT_SUCCESS; - } - else - { - t.stop(); - return EXIT_FAILURE; - } +int +main(int argc, const char* argv[]) { + USING_NAMESPACE_STIR + + HighResWallClockTimer t; + t.reset(); + t.start(); + + if (argc != 2) + print_usage_and_exit(); + + shared_ptr simulation_method_sptr; + + KeyParser parser; + parser.add_start_key("Scatter Simulation Parameters"); + parser.add_stop_key("End Scatter Simulation Parameters"); + parser.add_parsing_key("Scatter Simulation type", &simulation_method_sptr); + if (!parser.parse(argv[1])) { + t.stop(); + return EXIT_FAILURE; + } + + if (simulation_method_sptr->set_up() == Succeeded::no) { + t.stop(); + return EXIT_FAILURE; + } + + if (simulation_method_sptr->process_data() == stir::Succeeded::yes) { + t.stop(); + cout << "Total Wall clock time: " << t.value() << " seconds" << endl; + return EXIT_SUCCESS; + } else { + t.stop(); + return EXIT_FAILURE; + } } - diff --git a/src/scatter_utilities/upsample_and_fit_single_scatter.cxx b/src/scatter_utilities/upsample_and_fit_single_scatter.cxx index d192ccfe77..7a5ae557dc 100644 --- a/src/scatter_utilities/upsample_and_fit_single_scatter.cxx +++ b/src/scatter_utilities/upsample_and_fit_single_scatter.cxx @@ -11,7 +11,7 @@ This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. + GNU Lesser General Public License for more details. See STIR/LICENSE.txt for details */ @@ -25,20 +25,20 @@ \author Charalampos Tsoumpas \author Kris Thielemans - + \par Usage: \code [--min-scale-factor ] [--max-scale-factor ] [--remove-interleaving <1|0>] [--half-filter-width ] - --output-filename + --output-filename --data-to-fit --data-to-scale --norm --weights \endcode - \a remove_interleaving defaults to 1\, \a min-scale-factor to 1e-5, + \a remove_interleaving defaults to 1\, \a min-scale-factor to 1e-5, and \a max-scale-factor to 1e5. The norm parameter expects the filename of a .par file with the following keywords @@ -59,17 +59,17 @@ #include "stir/is_null_ptr.h" #include #include -/***********************************************************/ +/***********************************************************/ static void -print_usage_and_exit(const char * const prog_name) -{ - std::cerr << "\nUsage:\n" << prog_name << "\\\n" +print_usage_and_exit(const char* const prog_name) { + std::cerr << "\nUsage:\n" + << prog_name << "\\\n" << "\t[--min-scale-factor ]\\\n" << "\t[--max-scale-factor ]\\\n" << "\t[--remove-interleaving <1|0>]\\\n" << "\t[--half-filter-width ]\\\n" - << "\t--output-filename \\\n" + << "\t--output-filename \\\n" << "\t--data-to-fit \\\n" << "\t--data-to-scale \\\n" << "\t--norm \\\n" @@ -85,15 +85,15 @@ print_usage_and_exit(const char * const prog_name) exit(EXIT_FAILURE); } -int main(int argc, const char *argv[]) -{ - const char * const prog_name = argv[0]; +int +main(int argc, const char* argv[]) { + const char* const prog_name = argv[0]; float min_scale_factor = 1.E-5F; float max_scale_factor = 1.E5F; bool remove_interleaving = true; unsigned int half_filter_width = 1; - stir::BSpline::BSplineType spline_type = stir::BSpline::linear; + stir::BSpline::BSplineType spline_type = stir::BSpline::linear; std::string data_to_fit_filename; std::string data_to_scale_filename; std::string weights_filename; @@ -101,112 +101,88 @@ int main(int argc, const char *argv[]) stir::shared_ptr normalisation_sptr; // option processing - while (argc>1 && argv[1][1] == '-') - { - if (strcmp(argv[1], "--min-scale-factor")==0) - { - min_scale_factor = static_cast(atof(argv[2])); - argc-=2; argv +=2; - } - else if (strcmp(argv[1], "--max-scale-factor")==0) - { - max_scale_factor = static_cast(atof(argv[2])); - argc-=2; argv +=2; - } - else if (strcmp(argv[1], "--BSpline-type")==0) - { - spline_type = (stir::BSpline::BSplineType)atoi(argv[2]); - argc-=2; argv +=2; - } - else if (strcmp(argv[1], "--half-filter-width")==0) - { - half_filter_width = (unsigned)atoi(argv[2]); - argc-=2; argv +=2; - } - else if (strcmp(argv[1], "--remove-interleaving")==0) - { - remove_interleaving = atoi(argv[2])!=0; - argc-=2; argv +=2; - } - else if (strcmp(argv[1], "--output-filename")==0) - { - output_filename = (argv[2]); - argc-=2; argv +=2; - } - else if (strcmp(argv[1], "--data-to-fit")==0) - { - data_to_fit_filename = argv[2]; - argc-=2; argv +=2; - } - else if (strcmp(argv[1], "--data-to-scale")==0) - { - data_to_scale_filename = argv[2]; - argc-=2; argv +=2; - } - else if (strcmp(argv[1], "--weights")==0) - { - weights_filename = argv[2]; - argc-=2; argv +=2; - } - else if (strcmp(argv[1], "--norm")==0) - { - stir::KeyParser parser; - parser.add_start_key("Bin Normalisation parameters"); - parser.add_parsing_key("type", &normalisation_sptr); - parser.add_stop_key("END"); - if (!parser.parse(argv[2])) - { return EXIT_FAILURE; } - argc-=2; argv +=2; - } - else - { - std::cerr << "\nUnknown option: " << argv[1]; - print_usage_and_exit(prog_name); - } - } - - if (argc> 1) - { - std::cerr << "Command line should contain only options\n"; - print_usage_and_exit(prog_name); - } - - if (data_to_fit_filename.size()==0 || data_to_scale_filename.size() == 0 || - weights_filename.size()==0 || output_filename.size()==0) - { - std::cerr << "One of the required filenames has not been specified\n"; + while (argc > 1 && argv[1][1] == '-') { + if (strcmp(argv[1], "--min-scale-factor") == 0) { + min_scale_factor = static_cast(atof(argv[2])); + argc -= 2; + argv += 2; + } else if (strcmp(argv[1], "--max-scale-factor") == 0) { + max_scale_factor = static_cast(atof(argv[2])); + argc -= 2; + argv += 2; + } else if (strcmp(argv[1], "--BSpline-type") == 0) { + spline_type = (stir::BSpline::BSplineType)atoi(argv[2]); + argc -= 2; + argv += 2; + } else if (strcmp(argv[1], "--half-filter-width") == 0) { + half_filter_width = (unsigned)atoi(argv[2]); + argc -= 2; + argv += 2; + } else if (strcmp(argv[1], "--remove-interleaving") == 0) { + remove_interleaving = atoi(argv[2]) != 0; + argc -= 2; + argv += 2; + } else if (strcmp(argv[1], "--output-filename") == 0) { + output_filename = (argv[2]); + argc -= 2; + argv += 2; + } else if (strcmp(argv[1], "--data-to-fit") == 0) { + data_to_fit_filename = argv[2]; + argc -= 2; + argv += 2; + } else if (strcmp(argv[1], "--data-to-scale") == 0) { + data_to_scale_filename = argv[2]; + argc -= 2; + argv += 2; + } else if (strcmp(argv[1], "--weights") == 0) { + weights_filename = argv[2]; + argc -= 2; + argv += 2; + } else if (strcmp(argv[1], "--norm") == 0) { + stir::KeyParser parser; + parser.add_start_key("Bin Normalisation parameters"); + parser.add_parsing_key("type", &normalisation_sptr); + parser.add_stop_key("END"); + if (!parser.parse(argv[2])) { + return EXIT_FAILURE; + } + argc -= 2; + argv += 2; + } else { + std::cerr << "\nUnknown option: " << argv[1]; print_usage_and_exit(prog_name); } + } + + if (argc > 1) { + std::cerr << "Command line should contain only options\n"; + print_usage_and_exit(prog_name); + } + + if (data_to_fit_filename.size() == 0 || data_to_scale_filename.size() == 0 || weights_filename.size() == 0 || + output_filename.size() == 0) { + std::cerr << "One of the required filenames has not been specified\n"; + print_usage_and_exit(prog_name); + } using stir::ProjData; - const stir::shared_ptr< ProjData > weights_proj_data_sptr = - ProjData::read_from_file(weights_filename); - const stir::shared_ptr data_to_fit_proj_data_sptr = - ProjData::read_from_file(data_to_fit_filename); - const stir::shared_ptr data_to_scale_proj_data_sptr = - ProjData::read_from_file(data_to_scale_filename); - - if (stir::is_null_ptr(normalisation_sptr)) - { - normalisation_sptr.reset(new stir::TrivialBinNormalisation); - normalisation_sptr->set_up(data_to_fit_proj_data_sptr->get_exam_info_sptr(), data_to_fit_proj_data_sptr->get_proj_data_info_sptr()->create_shared_clone()); - } + const stir::shared_ptr weights_proj_data_sptr = ProjData::read_from_file(weights_filename); + const stir::shared_ptr data_to_fit_proj_data_sptr = ProjData::read_from_file(data_to_fit_filename); + const stir::shared_ptr data_to_scale_proj_data_sptr = ProjData::read_from_file(data_to_scale_filename); + + if (stir::is_null_ptr(normalisation_sptr)) { + normalisation_sptr.reset(new stir::TrivialBinNormalisation); + normalisation_sptr->set_up(data_to_fit_proj_data_sptr->get_exam_info_sptr(), + data_to_fit_proj_data_sptr->get_proj_data_info_sptr()->create_shared_clone()); + } stir::shared_ptr data_to_fit_proj_data_info_sptr = - data_to_fit_proj_data_sptr->get_proj_data_info_sptr()->create_shared_clone(); - - stir::ProjDataInterfile output_proj_data(data_to_fit_proj_data_sptr->get_exam_info_sptr(), - data_to_fit_proj_data_info_sptr, output_filename); - - stir::ScatterEstimation:: - upsample_and_fit_scatter_estimate(output_proj_data, - *data_to_fit_proj_data_sptr, - *data_to_scale_proj_data_sptr, - *normalisation_sptr, - *weights_proj_data_sptr, - min_scale_factor, - max_scale_factor, - half_filter_width, - spline_type, - remove_interleaving); + data_to_fit_proj_data_sptr->get_proj_data_info_sptr()->create_shared_clone(); + + stir::ProjDataInterfile output_proj_data(data_to_fit_proj_data_sptr->get_exam_info_sptr(), data_to_fit_proj_data_info_sptr, + output_filename); + + stir::ScatterEstimation::upsample_and_fit_scatter_estimate( + output_proj_data, *data_to_fit_proj_data_sptr, *data_to_scale_proj_data_sptr, *normalisation_sptr, *weights_proj_data_sptr, + min_scale_factor, max_scale_factor, half_filter_width, spline_type, remove_interleaving); return EXIT_SUCCESS; -} +} diff --git a/src/spatial_transformation_buildblock/GatedSpatialTransformation.cxx b/src/spatial_transformation_buildblock/GatedSpatialTransformation.cxx index 49e3717d74..0524f9313d 100644 --- a/src/spatial_transformation_buildblock/GatedSpatialTransformation.cxx +++ b/src/spatial_transformation_buildblock/GatedSpatialTransformation.cxx @@ -2,20 +2,20 @@ /* Copyright (C) 2009 - 2013, King's College London This file is part of STIR. - + This file is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.3 of the License, or (at your option) any later version. - + This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - + See STIR/LICENSE.txt for details */ -/*! +/*! \file \ingroup spatial_transformation \brief Implementations of inline functions of class stir::GatedSpatialTransformation @@ -32,45 +32,36 @@ START_NAMESPACE_STIR void -GatedSpatialTransformation:: -set_defaults() -{ +GatedSpatialTransformation::set_defaults() { base_type::set_defaults(); - this->_transformation_filename_prefix=""; - this->_spline_type=static_cast (1);; + this->_transformation_filename_prefix = ""; + this->_spline_type = static_cast(1); + ; } -const char * const -GatedSpatialTransformation::registered_name = "Gated Spatial Transformation"; +const char* const GatedSpatialTransformation::registered_name = "Gated Spatial Transformation"; //! default constructor -GatedSpatialTransformation::GatedSpatialTransformation() -{ - this->set_defaults(); -} +GatedSpatialTransformation::GatedSpatialTransformation() { this->set_defaults(); } -GatedSpatialTransformation::~GatedSpatialTransformation() //!< default destructor -{ } +GatedSpatialTransformation::~GatedSpatialTransformation() //!< default destructor +{} -Succeeded -GatedSpatialTransformation::set_up() -{ - if (this->_spatial_transformations_are_stored==true) +Succeeded +GatedSpatialTransformation::set_up() { + if (this->_spatial_transformations_are_stored == true) return Succeeded::yes; else return Succeeded::no; } -const TimeGateDefinitions & -GatedSpatialTransformation::get_time_gate_definitions() const -{ +const TimeGateDefinitions& +GatedSpatialTransformation::get_time_gate_definitions() const { return this->_spatial_transformation_x.get_time_gate_definitions(); } void -GatedSpatialTransformation:: -initialise_keymap() -{ +GatedSpatialTransformation::initialise_keymap() { base_type::initialise_keymap(); this->parser.add_start_key("Gated Spatial Transformation Parameters"); this->parser.add_key("Gated Spatial Transformation Filenames Prefix", &this->_transformation_filename_prefix); @@ -78,158 +69,158 @@ initialise_keymap() } bool -GatedSpatialTransformation:: -post_processing() -{ +GatedSpatialTransformation::post_processing() { if (base_type::post_processing() == true) return true; - if(this->_transformation_filename_prefix=="0") - { - warning("You need to specify a prefix for three files with transformation information."); - return true; - } - else - { - GatedSpatialTransformation::read_from_files(this->_transformation_filename_prefix); - this->_spatial_transformations_are_stored=true; - } + if (this->_transformation_filename_prefix == "0") { + warning("You need to specify a prefix for three files with transformation information."); + return true; + } else { + GatedSpatialTransformation::read_from_files(this->_transformation_filename_prefix); + this->_spatial_transformations_are_stored = true; + } // Always linear interpolation for the moment - this->_spline_type=static_cast (1); + this->_spline_type = static_cast(1); return false; } -//! Implementation to read the transformation vectors will be moved to the IO directory because it should be general. For example it can be in ECAT7 image formant +//! Implementation to read the transformation vectors will be moved to the IO directory because it should be general. For example +//! it can be in ECAT7 image formant void -GatedSpatialTransformation::read_from_files(const std::string input_string) -{ - const std::string gate_defs_input_string=input_string + ".gdef"; +GatedSpatialTransformation::read_from_files(const std::string input_string) { + const std::string gate_defs_input_string = input_string + ".gdef"; - if (gate_defs_input_string.size()!=0) - this->_gate_defs=TimeGateDefinitions(gate_defs_input_string); + if (gate_defs_input_string.size() != 0) + this->_gate_defs = TimeGateDefinitions(gate_defs_input_string); else { error("No Time Gates Definitions available!!!\n "); } - const shared_ptr spatial_transformation_z_sptr (GatedDiscretisedDensity::read_from_files(input_string,"d1")); - const GatedDiscretisedDensity & spatial_transformation_z(*spatial_transformation_z_sptr); - - const shared_ptr spatial_transformation_y_sptr(GatedDiscretisedDensity::read_from_files(input_string,"d2")); - const GatedDiscretisedDensity & spatial_transformation_y(*spatial_transformation_y_sptr); - - const shared_ptr spatial_transformation_x_sptr (GatedDiscretisedDensity::read_from_files(input_string,"d3")); - const GatedDiscretisedDensity & spatial_transformation_x(*spatial_transformation_x_sptr); - - const TimeGateDefinitions gate_defs(gate_defs_input_string);//This is not necessary as the defs are necessary for all the files and it should be one file... Think how to do this. - - this->_spatial_transformation_z= spatial_transformation_z; this->_spatial_transformation_y= spatial_transformation_y; this->_spatial_transformation_x= spatial_transformation_x; - this->_spatial_transformations_are_stored=true; -} + const shared_ptr spatial_transformation_z_sptr( + GatedDiscretisedDensity::read_from_files(input_string, "d1")); + const GatedDiscretisedDensity& spatial_transformation_z(*spatial_transformation_z_sptr); + + const shared_ptr spatial_transformation_y_sptr( + GatedDiscretisedDensity::read_from_files(input_string, "d2")); + const GatedDiscretisedDensity& spatial_transformation_y(*spatial_transformation_y_sptr); + + const shared_ptr spatial_transformation_x_sptr( + GatedDiscretisedDensity::read_from_files(input_string, "d3")); + const GatedDiscretisedDensity& spatial_transformation_x(*spatial_transformation_x_sptr); + + const TimeGateDefinitions gate_defs(gate_defs_input_string); // This is not necessary as the defs are necessary for all the + // files and it should be one file... Think how to do this. + + this->_spatial_transformation_z = spatial_transformation_z; + this->_spatial_transformation_y = spatial_transformation_y; + this->_spatial_transformation_x = spatial_transformation_x; + this->_spatial_transformations_are_stored = true; +} //! Implementation to write the transformation vectors void -GatedSpatialTransformation::write_to_files(const std::string output_string) -{ - (this->_spatial_transformation_z).write_to_files(output_string,"d1"); - (this->_spatial_transformation_y).write_to_files(output_string,"d2"); - (this->_spatial_transformation_x).write_to_files(output_string,"d3"); +GatedSpatialTransformation::write_to_files(const std::string output_string) { + (this->_spatial_transformation_z).write_to_files(output_string, "d1"); + (this->_spatial_transformation_y).write_to_files(output_string, "d2"); + (this->_spatial_transformation_x).write_to_files(output_string, "d3"); // return Succeeded::yes; // add a no case if you cannot write -} +} -void -GatedSpatialTransformation::warp_image(GatedDiscretisedDensity & new_gated_image, - const GatedDiscretisedDensity & gated_image) const -{ +void +GatedSpatialTransformation::warp_image(GatedDiscretisedDensity& new_gated_image, + const GatedDiscretisedDensity& gated_image) const { std::string explanation; - if (!(gated_image.get_densities()[0])->has_same_characteristics(*(gated_image.get_densities()[0]), explanation)){ - error(boost::format("GatedSpatialTransformation::warp_image needs the same sizes for input and output images: %1%") % explanation); + if (!(gated_image.get_densities()[0])->has_same_characteristics(*(gated_image.get_densities()[0]), explanation)) { + error(boost::format("GatedSpatialTransformation::warp_image needs the same sizes for input and output images: %1%") % + explanation); } new_gated_image.set_time_gate_definitions(this->_gate_defs); - assert(gated_image.get_time_gate_definitions().get_num_gates()==this->_spatial_transformation_x.get_time_gate_definitions().get_num_gates()); + assert(gated_image.get_time_gate_definitions().get_num_gates() == + this->_spatial_transformation_x.get_time_gate_definitions().get_num_gates()); new_gated_image.fill_with_zero(); if (this->_spatial_transformations_are_stored) - for(unsigned int gate_num=1 ; gate_num<=gated_image.get_time_gate_definitions().get_num_gates() ; ++gate_num) - new_gated_image[gate_num]=stir::warp_image((gated_image.get_densities())[gate_num-1], - (this->_spatial_transformation_x.get_densities())[gate_num-1], - (this->_spatial_transformation_y.get_densities())[gate_num-1], - (this->_spatial_transformation_z.get_densities())[gate_num-1], - BSpline::linear, false); + for (unsigned int gate_num = 1; gate_num <= gated_image.get_time_gate_definitions().get_num_gates(); ++gate_num) + new_gated_image[gate_num] = stir::warp_image( + (gated_image.get_densities())[gate_num - 1], (this->_spatial_transformation_x.get_densities())[gate_num - 1], + (this->_spatial_transformation_y.get_densities())[gate_num - 1], + (this->_spatial_transformation_z.get_densities())[gate_num - 1], BSpline::linear, false); else error("The transformation fields haven't been set properly yet.\n"); } void -GatedSpatialTransformation::warp_image(DiscretisedDensity<3, float> & new_reference_image, - const GatedDiscretisedDensity & gated_image) const -{ +GatedSpatialTransformation::warp_image(DiscretisedDensity<3, float>& new_reference_image, + const GatedDiscretisedDensity& gated_image) const { new_reference_image.fill(0.F); this->accumulate_warp_image(new_reference_image, gated_image); } void -GatedSpatialTransformation::accumulate_warp_image(DiscretisedDensity<3, float> & new_reference_image, - const GatedDiscretisedDensity & gated_image) const -{ +GatedSpatialTransformation::accumulate_warp_image(DiscretisedDensity<3, float>& new_reference_image, + const GatedDiscretisedDensity& gated_image) const { GatedDiscretisedDensity new_gated_image(gated_image); new_gated_image.fill_with_zero(); - this->warp_image(new_gated_image,gated_image); - //!todo This is not implemented as sum (or should it be the average?) - for(unsigned int gate_num = 1;gate_num<=gated_image.get_time_gate_definitions().get_num_gates() ; ++gate_num) + this->warp_image(new_gated_image, gated_image); + //! todo This is not implemented as sum (or should it be the average?) + for (unsigned int gate_num = 1; gate_num <= gated_image.get_time_gate_definitions().get_num_gates(); ++gate_num) new_reference_image += new_gated_image[gate_num]; // new_reference_image /= gated_image.get_time_gate_definitions().get_num_gates(); } -void -GatedSpatialTransformation::warp_image(GatedDiscretisedDensity & gated_image, - const DiscretisedDensity<3, float> & reference_image) const -{ - if ((gated_image.get_densities())[0]->size_all()!=reference_image.size_all()){ +void +GatedSpatialTransformation::warp_image(GatedDiscretisedDensity& gated_image, + const DiscretisedDensity<3, float>& reference_image) const { + if ((gated_image.get_densities())[0]->size_all() != reference_image.size_all()) { error("GatedSpatialTransformation::warp_image needs the same sizes for input and output images.\n"); } - if ((gated_image.get_densities())[0]->size_all()!=(this->_spatial_transformation_y.get_densities())[0]->size_all()){ + if ((gated_image.get_densities())[0]->size_all() != (this->_spatial_transformation_y.get_densities())[0]->size_all()) { info(boost::format("Number of voxels in one gated image: %1%") % (gated_image.get_densities())[0]->size_all()); - info(boost::format("Number of voxels in one motion vector gated image: %1%") % (this->_spatial_transformation_y.get_densities())[0]->size_all()); + info(boost::format("Number of voxels in one motion vector gated image: %1%") % + (this->_spatial_transformation_y.get_densities())[0]->size_all()); error("GatedSpatialTransformation::warp_image needs the same sizes for motion vectors and input/output images.\n"); } - const shared_ptr > reference_image_sptr( reference_image.clone()); + const shared_ptr> reference_image_sptr(reference_image.clone()); gated_image.resize_densities(this->_gate_defs); - + if (this->_spatial_transformations_are_stored) - for(unsigned int gate_num = 1 ; gate_num<=gated_image.get_time_gate_definitions().get_num_gates() ; ++gate_num) - { - const VoxelsOnCartesianGrid density = stir::warp_image(reference_image_sptr, - (this->_spatial_transformation_x.get_densities())[gate_num-1], - (this->_spatial_transformation_y.get_densities())[gate_num-1], - (this->_spatial_transformation_z.get_densities())[gate_num-1], - BSpline::linear, false); - const shared_ptr > density_sptr(density.clone()); - gated_image.set_density_sptr(density_sptr,gate_num); - } + for (unsigned int gate_num = 1; gate_num <= gated_image.get_time_gate_definitions().get_num_gates(); ++gate_num) { + const VoxelsOnCartesianGrid density = + stir::warp_image(reference_image_sptr, (this->_spatial_transformation_x.get_densities())[gate_num - 1], + (this->_spatial_transformation_y.get_densities())[gate_num - 1], + (this->_spatial_transformation_z.get_densities())[gate_num - 1], BSpline::linear, false); + const shared_ptr> density_sptr(density.clone()); + gated_image.set_density_sptr(density_sptr, gate_num); + } else - error("The transformation fields haven't been set properly yet."); + error("The transformation fields haven't been set properly yet."); } void -GatedSpatialTransformation:: -set_spatial_transformations(const GatedDiscretisedDensity & transformation_z, - const GatedDiscretisedDensity & transformation_y, - const GatedDiscretisedDensity & transformation_x) -{ - this->_spatial_transformation_z=transformation_z; - this->_spatial_transformation_y=transformation_y; - this->_spatial_transformation_x=transformation_x; - this->_spatial_transformations_are_stored=true; -} - -void -GatedSpatialTransformation::set_gate_defs(const TimeGateDefinitions & gate_defs) -{ this->_gate_defs=gate_defs; } - -GatedDiscretisedDensity GatedSpatialTransformation::get_spatial_transformation_z() const -{ return this->_spatial_transformation_z; } -GatedDiscretisedDensity GatedSpatialTransformation::get_spatial_transformation_y() const -{ return this->_spatial_transformation_y; } -GatedDiscretisedDensity GatedSpatialTransformation::get_spatial_transformation_x() const -{ return this->_spatial_transformation_x; } +GatedSpatialTransformation::set_spatial_transformations(const GatedDiscretisedDensity& transformation_z, + const GatedDiscretisedDensity& transformation_y, + const GatedDiscretisedDensity& transformation_x) { + this->_spatial_transformation_z = transformation_z; + this->_spatial_transformation_y = transformation_y; + this->_spatial_transformation_x = transformation_x; + this->_spatial_transformations_are_stored = true; +} +void +GatedSpatialTransformation::set_gate_defs(const TimeGateDefinitions& gate_defs) { + this->_gate_defs = gate_defs; +} + +GatedDiscretisedDensity +GatedSpatialTransformation::get_spatial_transformation_z() const { + return this->_spatial_transformation_z; +} +GatedDiscretisedDensity +GatedSpatialTransformation::get_spatial_transformation_y() const { + return this->_spatial_transformation_y; +} +GatedDiscretisedDensity +GatedSpatialTransformation::get_spatial_transformation_x() const { + return this->_spatial_transformation_x; +} END_NAMESPACE_STIR diff --git a/src/spatial_transformation_buildblock/InvertAxis.cxx b/src/spatial_transformation_buildblock/InvertAxis.cxx index 5a4b5d99a2..b1124eb260 100644 --- a/src/spatial_transformation_buildblock/InvertAxis.cxx +++ b/src/spatial_transformation_buildblock/InvertAxis.cxx @@ -2,19 +2,19 @@ /* Copyright (C) 2019 National Physical Laboratory This file is part of STIR. - + This file is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.3 of the License, or (at your option) any later version. - + This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - + See STIR/LICENSE.txt for details - */ + */ /*! \file \ingroup buildblock @@ -27,72 +27,64 @@ START_NAMESPACE_STIR void -InvertAxis::invert_axis(DiscretisedDensity<3,float> & inverted_image, - const DiscretisedDensity<3,float> & input_image, - const std::string &axis_name){ -//change all the pointers - const int min_z = input_image.get_min_index(); - const int max_z = input_image.get_max_index(); - - - for (int z=min_z; z<=max_z; z++){ - - const int min_y = input_image[z].get_min_index(); - const int max_y = input_image[z].get_max_index(); - - for (int y=min_y;y<= max_y;y++){ - - const int min_x = input_image[z][y].get_min_index(); - const int max_x = input_image[z][y].get_max_index(); - - for (int x=min_x;x<= max_x;x++){ - - if (axis_name=="x"){ -// checking whether the size is odd - if(((max_x-min_x+1) % 2)==0) - inverted_image[z][y][x]=input_image[z][y][-x-1]; - else - inverted_image[z][y][x]=input_image[z][y][-x]; - } - - else if (axis_name=="y"){ - if(((max_y-min_y+1) % 2)==0) - inverted_image[z][y][x]=input_image[z][-y-1][x]; - else - inverted_image[z][y][x]=input_image[z][-y][x]; - } - - else if (axis_name=="z"){ - inverted_image[z][y][x]=input_image[max_z-z][y][x]; - } - } - } - } - } +InvertAxis::invert_axis(DiscretisedDensity<3, float>& inverted_image, const DiscretisedDensity<3, float>& input_image, + const std::string& axis_name) { + // change all the pointers + const int min_z = input_image.get_min_index(); + const int max_z = input_image.get_max_index(); -int -InvertAxis::invert_axis_index(const int input_index, - const int size, - const std::string& axis_name){ + for (int z = min_z; z <= max_z; z++) { - if (axis_name=="x" || axis_name=="y"){ + const int min_y = input_image[z].get_min_index(); + const int max_y = input_image[z].get_max_index(); -// checking whether the size is odd - if((size % 2)==0) - return -input_index -1; - else - return -input_index; + for (int y = min_y; y <= max_y; y++) { + + const int min_x = input_image[z][y].get_min_index(); + const int max_x = input_image[z][y].get_max_index(); + + for (int x = min_x; x <= max_x; x++) { + + if (axis_name == "x") { + // checking whether the size is odd + if (((max_x - min_x + 1) % 2) == 0) + inverted_image[z][y][x] = input_image[z][y][-x - 1]; + else + inverted_image[z][y][x] = input_image[z][y][-x]; } - else if (axis_name=="z") - return size -1 -input_index; - else - { - error("InvertAxis: invalid axis name: " + axis_name); - return 0; // to avoid compiler warning + else if (axis_name == "y") { + if (((max_y - min_y + 1) % 2) == 0) + inverted_image[z][y][x] = input_image[z][-y - 1][x]; + else + inverted_image[z][y][x] = input_image[z][-y][x]; + } + + else if (axis_name == "z") { + inverted_image[z][y][x] = input_image[max_z - z][y][x]; + } } - + } + } +} +int +InvertAxis::invert_axis_index(const int input_index, const int size, const std::string& axis_name) { + + if (axis_name == "x" || axis_name == "y") { + + // checking whether the size is odd + if ((size % 2) == 0) + return -input_index - 1; + else + return -input_index; + } + + else if (axis_name == "z") + return size - 1 - input_index; + else { + error("InvertAxis: invalid axis name: " + axis_name); + return 0; // to avoid compiler warning + } } END_NAMESPACE_STIR - diff --git a/src/spatial_transformation_buildblock/SpatialTransformation.cxx b/src/spatial_transformation_buildblock/SpatialTransformation.cxx index e1d1f562f3..6997b03a3c 100644 --- a/src/spatial_transformation_buildblock/SpatialTransformation.cxx +++ b/src/spatial_transformation_buildblock/SpatialTransformation.cxx @@ -15,30 +15,27 @@ See STIR/LICENSE.txt for details */ - /*! - \file - \ingroup spatial_transformation - \brief Implementations of inline functions of class stir::SpatialTransformation +/*! + \file + \ingroup spatial_transformation + \brief Implementations of inline functions of class stir::SpatialTransformation - \author Charalampos Tsoumpas + \author Charalampos Tsoumpas - This is the most basic class for including Motion Fields. + This is the most basic class for including Motion Fields. */ - #include "stir/spatial_transformation/SpatialTransformation.h" - START_NAMESPACE_STIR -const char * const -SpatialTransformation::registered_name = "Motion Field Type"; +const char* const SpatialTransformation::registered_name = "Motion Field Type"; -SpatialTransformation::SpatialTransformation() //!< default constructor -{ } +SpatialTransformation::SpatialTransformation() //!< default constructor +{} -SpatialTransformation::~SpatialTransformation() //!< default destructor -{ } +SpatialTransformation::~SpatialTransformation() //!< default destructor +{} END_NAMESPACE_STIR diff --git a/src/spatial_transformation_buildblock/spatial_transformation_registries.cxx b/src/spatial_transformation_buildblock/spatial_transformation_registries.cxx index 5bce94a319..6a5bd23b57 100644 --- a/src/spatial_transformation_buildblock/spatial_transformation_registries.cxx +++ b/src/spatial_transformation_buildblock/spatial_transformation_registries.cxx @@ -1,27 +1,27 @@ // - /* - Copyright (C) 2009 -2013, King's College London - This file is part of STIR. - - This file is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.3 of the License, or - (at your option) any later version. - - This file is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - See STIR/LICENSE.txt for details -*/ +/* + Copyright (C) 2009 -2013, King's College London + This file is part of STIR. + + This file is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.3 of the License, or + (at your option) any later version. + + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + See STIR/LICENSE.txt for details +*/ /*! \file \ingroup spatial_transformation \brief File that registers all stir::RegisterObject children in spatial_transformation \author Charalampos Tsoumpas - + */ #include "stir/spatial_transformation/GatedSpatialTransformation.h" @@ -31,4 +31,3 @@ START_NAMESPACE_STIR static GatedSpatialTransformation::RegisterIt dummy1199; END_NAMESPACE_STIR - diff --git a/src/spatial_transformation_buildblock/warp_image.cxx b/src/spatial_transformation_buildblock/warp_image.cxx index ccc55d391c..338b709af5 100644 --- a/src/spatial_transformation_buildblock/warp_image.cxx +++ b/src/spatial_transformation_buildblock/warp_image.cxx @@ -2,50 +2,50 @@ /* Copyright (C) 2009 - 2013, King's College London This file is part of STIR. - + This file is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.3 of the License, or (at your option) any later version. - + This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - + See STIR/LICENSE.txt for details - */ + */ /*! \file \ingroup buildblock - \brief Implementation of function stir::warp_image + \brief Implementation of function stir::warp_image \author Charalampos Tsoumpas */ #include "stir/spatial_transformation/warp_image.h" START_NAMESPACE_STIR -//using namespace BSpline; +// using namespace BSpline; - -VoxelsOnCartesianGrid -warp_image(const shared_ptr > & density_sptr, - const shared_ptr > & motion_x_sptr, - const shared_ptr > & motion_y_sptr, - const shared_ptr > & motion_z_sptr, - const BSpline::BSplineType spline_type, const bool extend_borders) -{ - const DiscretisedDensityOnCartesianGrid <3,float>* density_cartesian_sptr = - dynamic_cast< DiscretisedDensityOnCartesianGrid<3,float>* > (density_sptr.get()); - const BasicCoordinate<3,float> grid_spacing=density_cartesian_sptr->get_grid_spacing(); - const CartesianCoordinate3D origin=density_cartesian_sptr->get_origin(); +VoxelsOnCartesianGrid +warp_image(const shared_ptr>& density_sptr, + const shared_ptr>& motion_x_sptr, + const shared_ptr>& motion_y_sptr, + const shared_ptr>& motion_z_sptr, const BSpline::BSplineType spline_type, + const bool extend_borders) { + const DiscretisedDensityOnCartesianGrid<3, float>* density_cartesian_sptr = + dynamic_cast*>(density_sptr.get()); + const BasicCoordinate<3, float> grid_spacing = density_cartesian_sptr->get_grid_spacing(); + const CartesianCoordinate3D origin = density_cartesian_sptr->get_origin(); const BSpline::BSplinesRegularGrid<3, float> density_interpolation(*density_sptr, spline_type); - - BasicCoordinate<3,int> min; BasicCoordinate<3,int> max; - const IndexRange<3> range=density_sptr->get_index_range(); - if (!range.get_regular_range(min,max)) + + BasicCoordinate<3, int> min; + BasicCoordinate<3, int> max; + const IndexRange<3> range = density_sptr->get_index_range(); + if (!range.get_regular_range(min, max)) error("image is not in regular grid.\n"); - const BasicCoordinate<3,int> out_min=min; const BasicCoordinate<3,int> out_max=max; + const BasicCoordinate<3, int> out_min = min; + const BasicCoordinate<3, int> out_max = max; #if 0 if (extend_borders==1) { out_min[1]-=abs(voxel_shift_z) ; @@ -56,30 +56,32 @@ warp_image(const shared_ptr > & density_sptr, out_max[3]+=abs(voxel_shift_x) ; } #endif - const IndexRange<3> out_range(out_min,out_max); - VoxelsOnCartesianGrid out_density(out_range,origin,grid_spacing); + const IndexRange<3> out_range(out_min, out_max); + VoxelsOnCartesianGrid out_density(out_range, origin, grid_spacing); - BasicCoordinate<3,int> c; - BasicCoordinate<3,double> d, l; - for (c[1]=min[1]; c[1]<=max[1]; ++c[1]) - for (c[2]=min[2]; c[2]<=max[2]; ++c[2]) - for (c[3]=min[3]; c[3]<=max[3]; ++c[3]) - { - l[1] = static_cast ((*motion_z_sptr)[c]/grid_spacing[1]); - l[2] = static_cast ((*motion_y_sptr)[c]/grid_spacing[2]); - l[3] = static_cast ((*motion_x_sptr)[c]/grid_spacing[3]); - d[1] = static_cast (c[1]) + l[1]; // for the IRTK version I had c-l, but for Christian's it seems to work as c+l - d[2] = static_cast (c[2]) + l[2]; - d[3] = static_cast (c[3]) + l[3]; - // Temporary fix such that when radioactivity comes from outside is set to 0. - // To fix this properly we need to modify the B-Splines interpolation method by changing the periodicity extrapolation. - if ( (d[1]<=static_cast(min[1])) || (d[1]>=static_cast(max[1])) || // I'm not considering the last plane if linear - (d[2]<=static_cast(min[2])) || (d[2]>=static_cast(max[2])) || // because it's going to use extrapolated data - (d[3]<=static_cast(min[3])) || (d[3]>=static_cast(max[3])) ) // I haven't implemented anything for higher order - out_density[c] = 0.F; - else - out_density[c] = density_interpolation(d); - } + BasicCoordinate<3, int> c; + BasicCoordinate<3, double> d, l; + for (c[1] = min[1]; c[1] <= max[1]; ++c[1]) + for (c[2] = min[2]; c[2] <= max[2]; ++c[2]) + for (c[3] = min[3]; c[3] <= max[3]; ++c[3]) { + l[1] = static_cast((*motion_z_sptr)[c] / grid_spacing[1]); + l[2] = static_cast((*motion_y_sptr)[c] / grid_spacing[2]); + l[3] = static_cast((*motion_x_sptr)[c] / grid_spacing[3]); + d[1] = static_cast(c[1]) + l[1]; // for the IRTK version I had c-l, but for Christian's it seems to work as c+l + d[2] = static_cast(c[2]) + l[2]; + d[3] = static_cast(c[3]) + l[3]; + // Temporary fix such that when radioactivity comes from outside is set to 0. + // To fix this properly we need to modify the B-Splines interpolation method by changing the periodicity extrapolation. + if ((d[1] <= static_cast(min[1])) || + (d[1] >= static_cast(max[1])) || // I'm not considering the last plane if linear + (d[2] <= static_cast(min[2])) || + (d[2] >= static_cast(max[2])) || // because it's going to use extrapolated data + (d[3] <= static_cast(min[3])) || + (d[3] >= static_cast(max[3]))) // I haven't implemented anything for higher order + out_density[c] = 0.F; + else + out_density[c] = density_interpolation(d); + } return out_density; } diff --git a/src/test/IO/test_IO_DiscretisedDensity.cxx b/src/test/IO/test_IO_DiscretisedDensity.cxx index 690968b669..68aac135cf 100644 --- a/src/test/IO/test_IO_DiscretisedDensity.cxx +++ b/src/test/IO/test_IO_DiscretisedDensity.cxx @@ -70,70 +70,63 @@ START_NAMESPACE_STIR \todo Delete STIRtmp.* files, but that's a bit difficult as we don't know which ones are written. */ -class IOTests_DiscretisedDensity : public IOTests > -{ +class IOTests_DiscretisedDensity : public IOTests> { public: - explicit IOTests_DiscretisedDensity(istream& in) : IOTests(in) {} + explicit IOTests_DiscretisedDensity(istream& in) : IOTests(in) {} protected: - - void create_image(); - void read_image(); - void check_result(); + void create_image(); + void read_image(); + void check_result(); }; -void IOTests_DiscretisedDensity::create_image() -{ - _image_to_write_sptr = create_single_image(); +void +IOTests_DiscretisedDensity::create_image() { + _image_to_write_sptr = create_single_image(); } -void IOTests_DiscretisedDensity::read_image() -{ - // now read it back - unique_ptr > - density_ptr = read_from_file >(_filename); +void +IOTests_DiscretisedDensity::read_image() { + // now read it back + unique_ptr> density_ptr = read_from_file>(_filename); - if(!check(!is_null_ptr(density_ptr), "failed reading")) - return; + if (!check(!is_null_ptr(density_ptr), "failed reading")) + return; - _image_to_read_sptr.reset(density_ptr->clone()); + _image_to_read_sptr.reset(density_ptr->clone()); - if(!check(!is_null_ptr(_image_to_read_sptr), "failed reading")) - return; + if (!check(!is_null_ptr(_image_to_read_sptr), "failed reading")) + return; } -void IOTests_DiscretisedDensity::check_result() -{ - // Cast the discretised density to voxels on cartesian grids to check grid spacing - VoxelsOnCartesianGrid *image_to_write_ptr = dynamic_cast *>(_image_to_write_sptr.get()); - VoxelsOnCartesianGrid *image_to_read_ptr = dynamic_cast *>(_image_to_read_sptr.get()); +void +IOTests_DiscretisedDensity::check_result() { + // Cast the discretised density to voxels on cartesian grids to check grid spacing + VoxelsOnCartesianGrid* image_to_write_ptr = dynamic_cast*>(_image_to_write_sptr.get()); + VoxelsOnCartesianGrid* image_to_read_ptr = dynamic_cast*>(_image_to_read_sptr.get()); - compare_images(*image_to_write_ptr, *image_to_read_ptr); + compare_images(*image_to_write_ptr, *image_to_read_ptr); - // Check TimeFrameDefinitions in ExamInfo. Not all formats support this. Skip if ITK - if (_output_file_format_sptr->get_registered_name() != "ITK") - check_exam_info(image_to_write_ptr->get_exam_info(), image_to_read_ptr->get_exam_info()); + // Check TimeFrameDefinitions in ExamInfo. Not all formats support this. Skip if ITK + if (_output_file_format_sptr->get_registered_name() != "ITK") + check_exam_info(image_to_write_ptr->get_exam_info(), image_to_read_ptr->get_exam_info()); } END_NAMESPACE_STIR USING_NAMESPACE_STIR -int main(int argc, char **argv) -{ - if (argc != 2) - { +int +main(int argc, char** argv) { + if (argc != 2) { cerr << "Usage : " << argv[0] << " filename\n" << "See source file for the format of this file.\n\n"; return EXIT_FAILURE; } - ifstream in(argv[1]); - if (!in) - { - cerr << argv[0] - << ": Error opening input file " << argv[1] << "\nExiting.\n"; + if (!in) { + cerr << argv[0] << ": Error opening input file " << argv[1] << "\nExiting.\n"; return EXIT_FAILURE; } diff --git a/src/test/IO/test_IO_DynamicDiscretisedDensity.cxx b/src/test/IO/test_IO_DynamicDiscretisedDensity.cxx index 48bc419e80..6022d1b036 100644 --- a/src/test/IO/test_IO_DynamicDiscretisedDensity.cxx +++ b/src/test/IO/test_IO_DynamicDiscretisedDensity.cxx @@ -71,98 +71,97 @@ START_NAMESPACE_STIR \todo Delete STIRtmp.* files, but that's a bit difficult as we don't know which ones are written. */ -class IOTests_DynamicDiscretisedDensity : public IOTests -{ +class IOTests_DynamicDiscretisedDensity : public IOTests { public: - explicit IOTests_DynamicDiscretisedDensity(istream& in) : IOTests(in) {} + explicit IOTests_DynamicDiscretisedDensity(istream& in) : IOTests(in) {} protected: - - void create_image(); - void check_result(); + void create_image(); + void check_result(); }; -void IOTests_DynamicDiscretisedDensity::create_image() -{ - double im_1_start = 20; - double im_1_end = 45; - double im_2_start = 50; - double im_2_end = 93; - - shared_ptr > dyn_im_1_sptr = create_single_image(); - shared_ptr > dyn_im_2_sptr = create_single_image(); - shared_ptr > dummy_im_sptr = create_single_image(); - - dyn_im_1_sptr->fill(2.); - dyn_im_2_sptr->fill(1.); - ExamInfo exam_info = dyn_im_1_sptr->get_exam_info(); - exam_info.time_frame_definitions.set_time_frame(1,im_1_start, im_1_end); - dyn_im_1_sptr->set_exam_info(exam_info); - exam_info.time_frame_definitions.set_time_frame(1,im_2_start, im_2_end); - dyn_im_2_sptr->set_exam_info(exam_info); - - // Create a scanner (any will do) - shared_ptr scanner_sptr(new Scanner(Scanner::Advance)); - - TimeFrameDefinitions tdefs; - tdefs.set_num_time_frames(2); - tdefs.set_time_frame(1,im_1_start,im_1_end); - tdefs.set_time_frame(2,im_2_start,im_2_end); - - _image_to_write_sptr.reset(new DynamicDiscretisedDensity(tdefs,dummy_im_sptr->get_exam_info().start_time_in_secs_since_1970,scanner_sptr,dummy_im_sptr)); - _image_to_write_sptr->set_density(*dyn_im_1_sptr,1); - _image_to_write_sptr->set_density(*dyn_im_2_sptr,2); - exam_info = _image_to_write_sptr->get_exam_info(); - exam_info.set_high_energy_thres(dummy_im_sptr->get_exam_info().get_high_energy_thres()); - exam_info.set_low_energy_thres(dummy_im_sptr->get_exam_info().get_low_energy_thres()); - _image_to_write_sptr->set_exam_info(exam_info); +void +IOTests_DynamicDiscretisedDensity::create_image() { + double im_1_start = 20; + double im_1_end = 45; + double im_2_start = 50; + double im_2_end = 93; + + shared_ptr> dyn_im_1_sptr = create_single_image(); + shared_ptr> dyn_im_2_sptr = create_single_image(); + shared_ptr> dummy_im_sptr = create_single_image(); + + dyn_im_1_sptr->fill(2.); + dyn_im_2_sptr->fill(1.); + ExamInfo exam_info = dyn_im_1_sptr->get_exam_info(); + exam_info.time_frame_definitions.set_time_frame(1, im_1_start, im_1_end); + dyn_im_1_sptr->set_exam_info(exam_info); + exam_info.time_frame_definitions.set_time_frame(1, im_2_start, im_2_end); + dyn_im_2_sptr->set_exam_info(exam_info); + + // Create a scanner (any will do) + shared_ptr scanner_sptr(new Scanner(Scanner::Advance)); + + TimeFrameDefinitions tdefs; + tdefs.set_num_time_frames(2); + tdefs.set_time_frame(1, im_1_start, im_1_end); + tdefs.set_time_frame(2, im_2_start, im_2_end); + + _image_to_write_sptr.reset(new DynamicDiscretisedDensity(tdefs, dummy_im_sptr->get_exam_info().start_time_in_secs_since_1970, + scanner_sptr, dummy_im_sptr)); + _image_to_write_sptr->set_density(*dyn_im_1_sptr, 1); + _image_to_write_sptr->set_density(*dyn_im_2_sptr, 2); + exam_info = _image_to_write_sptr->get_exam_info(); + exam_info.set_high_energy_thres(dummy_im_sptr->get_exam_info().get_high_energy_thres()); + exam_info.set_low_energy_thres(dummy_im_sptr->get_exam_info().get_low_energy_thres()); + _image_to_write_sptr->set_exam_info(exam_info); } -void IOTests_DynamicDiscretisedDensity::check_result() -{ - set_tolerance(.00001); +void +IOTests_DynamicDiscretisedDensity::check_result() { + set_tolerance(.00001); + + check_if_equal(_image_to_read_sptr->get_densities().size(), _image_to_write_sptr->get_densities().size(), + "test number of dynamic images"); - check_if_equal(_image_to_read_sptr->get_densities().size(),_image_to_write_sptr->get_densities().size(), "test number of dynamic images"); - - // Check the exam info - std::cerr << "\tChecking the exam info...\n"; - check_exam_info(_image_to_write_sptr->get_exam_info(),_image_to_read_sptr->get_exam_info()); + // Check the exam info + std::cerr << "\tChecking the exam info...\n"; + check_exam_info(_image_to_write_sptr->get_exam_info(), _image_to_read_sptr->get_exam_info()); - for (int i=1; i<=_image_to_read_sptr->get_densities().size(); ++i) { + for (int i = 1; i <= _image_to_read_sptr->get_densities().size(); ++i) { - std::cerr << "\t\tChecking dynamic image " << i << "...\n"; - - // Cast the discretised density to voxels on cartesian grids to check grid spacing - VoxelsOnCartesianGrid *image_to_write_ptr = dynamic_cast *>(&_image_to_write_sptr->get_density(i)); - VoxelsOnCartesianGrid *image_to_read_ptr = dynamic_cast *>(&_image_to_read_sptr->get_density(i)); + std::cerr << "\t\tChecking dynamic image " << i << "...\n"; - if (is_null_ptr(image_to_write_ptr) || is_null_ptr(image_to_read_ptr)) { - everything_ok = false; - return; - } + // Cast the discretised density to voxels on cartesian grids to check grid spacing + VoxelsOnCartesianGrid* image_to_write_ptr = + dynamic_cast*>(&_image_to_write_sptr->get_density(i)); + VoxelsOnCartesianGrid* image_to_read_ptr = + dynamic_cast*>(&_image_to_read_sptr->get_density(i)); - compare_images(*image_to_write_ptr, *image_to_read_ptr); + if (is_null_ptr(image_to_write_ptr) || is_null_ptr(image_to_read_ptr)) { + everything_ok = false; + return; } + + compare_images(*image_to_write_ptr, *image_to_read_ptr); + } } END_NAMESPACE_STIR USING_NAMESPACE_STIR -int main(int argc, char **argv) -{ - if (argc != 2) - { +int +main(int argc, char** argv) { + if (argc != 2) { cerr << "Usage : " << argv[0] << " filename\n" << "See source file for the format of this file.\n\n"; return EXIT_FAILURE; } ifstream in(argv[1]); - if (!in) - { - cerr << argv[0] - << ": Error opening input file " << argv[1] << "\nExiting.\n"; + if (!in) { + cerr << argv[0] << ": Error opening input file " << argv[1] << "\nExiting.\n"; return EXIT_FAILURE; } diff --git a/src/test/IO/test_IO_ITKMulticomponent.cxx b/src/test/IO/test_IO_ITKMulticomponent.cxx index 0f4d3c4a93..0fb8740c3d 100644 --- a/src/test/IO/test_IO_ITKMulticomponent.cxx +++ b/src/test/IO/test_IO_ITKMulticomponent.cxx @@ -40,60 +40,57 @@ START_NAMESPACE_STIR \ingroup test \brief A simple class to test the reading of multicomponent ITK images. */ -class IOTests_ITKMulticomponent : public RunTests -{ +class IOTests_ITKMulticomponent : public RunTests { public: - explicit IOTests_ITKMulticomponent(const std::string &multi) : - _multi(multi) {} + explicit IOTests_ITKMulticomponent(const std::string& multi) : _multi(multi) {} - void run_tests(); + void run_tests(); protected: - - std::string _multi; + std::string _multi; }; -void IOTests_ITKMulticomponent::run_tests() -{ - typedef VoxelsOnCartesianGrid > VoxelsCoords; +void +IOTests_ITKMulticomponent::run_tests() { + typedef VoxelsOnCartesianGrid> VoxelsCoords; - // Read the files - std::cerr << "\nReading: " << _multi << "\n"; + // Read the files + std::cerr << "\nReading: " << _multi << "\n"; - try { - shared_ptr voxels_coords(read_from_file(_multi)); + try { + shared_ptr voxels_coords(read_from_file(_multi)); - check(!is_null_ptr(voxels_coords), "failed reading %s"); + check(!is_null_ptr(voxels_coords), "failed reading %s"); - // Check sizes - check_if_equal(voxels_coords->size(), 64); - check_if_equal(voxels_coords->at(0).size(), 62); - check_if_equal(voxels_coords->at(0).at(0).size(), 63); - check_if_equal(voxels_coords->at(0).at(0).at(0).size(), 3); + // Check sizes + check_if_equal(voxels_coords->size(), 64); + check_if_equal(voxels_coords->at(0).size(), 62); + check_if_equal(voxels_coords->at(0).at(0).size(), 63); + check_if_equal(voxels_coords->at(0).at(0).at(0).size(), 3); - // Check voxel sizes - check_if_equal(voxels_coords->get_voxel_size()[1], 4.0625F); - check_if_equal(voxels_coords->get_voxel_size()[2], 4.0625F); - check_if_equal(voxels_coords->get_voxel_size()[3], 4.0625F); + // Check voxel sizes + check_if_equal(voxels_coords->get_voxel_size()[1], 4.0625F); + check_if_equal(voxels_coords->get_voxel_size()[2], 4.0625F); + check_if_equal(voxels_coords->get_voxel_size()[3], 4.0625F); - } catch(...) { - everything_ok = false; - } + } catch (...) { + everything_ok = false; + } } END_NAMESPACE_STD USING_NAMESPACE_STIR -int main(int argc, char **argv) -{ - if (argc != 2) { - std::cerr << "Usage : " << argv[0] << " filename\n"; - return EXIT_FAILURE; - } +int +main(int argc, char** argv) { + if (argc != 2) { + std::cerr << "Usage : " << argv[0] << " filename\n"; + return EXIT_FAILURE; + } - IOTests_ITKMulticomponent tests(argv[1]); + IOTests_ITKMulticomponent tests(argv[1]); - tests.run_tests(); + tests.run_tests(); - return tests.main_return_value(); + return tests.main_return_value(); } diff --git a/src/test/IO/test_IO_ParametricDiscretisedDensity.cxx b/src/test/IO/test_IO_ParametricDiscretisedDensity.cxx index ba4a42775c..86b0f7df8f 100644 --- a/src/test/IO/test_IO_ParametricDiscretisedDensity.cxx +++ b/src/test/IO/test_IO_ParametricDiscretisedDensity.cxx @@ -72,102 +72,105 @@ START_NAMESPACE_STIR \todo Delete STIRtmp.* files, but that's a bit difficult as we don't know which ones are written. */ -class IOTests_ParametricDiscretisedDensity : public IOTests -{ +class IOTests_ParametricDiscretisedDensity : public IOTests { public: - explicit IOTests_ParametricDiscretisedDensity(istream& in) : IOTests(in) {} + explicit IOTests_ParametricDiscretisedDensity(istream& in) : IOTests(in) {} protected: - void create_image(); - void check_result(); + void create_image(); + void check_result(); }; -void IOTests_ParametricDiscretisedDensity::create_image() -{ - shared_ptr > param_1_sptr = create_single_image(); - shared_ptr > param_2_sptr = create_single_image(); - shared_ptr > dummy_im_sptr = create_single_image(); - - //! Setup the scanner details first - const Scanner::Type test_scanner=Scanner::E966; - const shared_ptr scanner_sptr(new Scanner(test_scanner)); - VectorWithOffset num_axial_pos_per_segment; num_axial_pos_per_segment.resize(0,0); num_axial_pos_per_segment[0]=48; - VectorWithOffset min_ring_diff; min_ring_diff.resize(0,0); min_ring_diff[0]=0; - VectorWithOffset max_ring_diff; max_ring_diff.resize(0,0); max_ring_diff[0]=0; - const int num_views=144; const int num_tangential_poss=144; - - const float zoom=1.F; - - const CartesianCoordinate3D sizes ( - dummy_im_sptr->get_z_size(), - dummy_im_sptr->get_y_size(), - dummy_im_sptr->get_x_size()); - - ProjDataInfoCylindricalNoArcCorr proj_data_info(scanner_sptr,num_axial_pos_per_segment,min_ring_diff,max_ring_diff,num_views,num_tangential_poss); - - _image_to_write_sptr.reset(new ParametricVoxelsOnCartesianGrid(ParametricVoxelsOnCartesianGridBaseType(proj_data_info,zoom,dummy_im_sptr->get_grid_spacing(),sizes))); - - // Fill the first param - param_2_sptr->fill(2.F); - _image_to_write_sptr->update_parametric_image(*param_1_sptr,1); - - // Fill the second param with 1's - param_2_sptr->fill(1.F); - _image_to_write_sptr->update_parametric_image(*param_2_sptr,2); - - // Set the time definitions - ExamInfo exam_info = _image_to_write_sptr->get_exam_info(); - exam_info.set_time_frame_definitions(dummy_im_sptr->get_exam_info().get_time_frame_definitions()); - _image_to_write_sptr->set_exam_info(exam_info); +void +IOTests_ParametricDiscretisedDensity::create_image() { + shared_ptr> param_1_sptr = create_single_image(); + shared_ptr> param_2_sptr = create_single_image(); + shared_ptr> dummy_im_sptr = create_single_image(); + + //! Setup the scanner details first + const Scanner::Type test_scanner = Scanner::E966; + const shared_ptr scanner_sptr(new Scanner(test_scanner)); + VectorWithOffset num_axial_pos_per_segment; + num_axial_pos_per_segment.resize(0, 0); + num_axial_pos_per_segment[0] = 48; + VectorWithOffset min_ring_diff; + min_ring_diff.resize(0, 0); + min_ring_diff[0] = 0; + VectorWithOffset max_ring_diff; + max_ring_diff.resize(0, 0); + max_ring_diff[0] = 0; + const int num_views = 144; + const int num_tangential_poss = 144; + + const float zoom = 1.F; + + const CartesianCoordinate3D sizes(dummy_im_sptr->get_z_size(), dummy_im_sptr->get_y_size(), dummy_im_sptr->get_x_size()); + + ProjDataInfoCylindricalNoArcCorr proj_data_info(scanner_sptr, num_axial_pos_per_segment, min_ring_diff, max_ring_diff, + num_views, num_tangential_poss); + + _image_to_write_sptr.reset(new ParametricVoxelsOnCartesianGrid( + ParametricVoxelsOnCartesianGridBaseType(proj_data_info, zoom, dummy_im_sptr->get_grid_spacing(), sizes))); + + // Fill the first param + param_2_sptr->fill(2.F); + _image_to_write_sptr->update_parametric_image(*param_1_sptr, 1); + + // Fill the second param with 1's + param_2_sptr->fill(1.F); + _image_to_write_sptr->update_parametric_image(*param_2_sptr, 2); + + // Set the time definitions + ExamInfo exam_info = _image_to_write_sptr->get_exam_info(); + exam_info.set_time_frame_definitions(dummy_im_sptr->get_exam_info().get_time_frame_definitions()); + _image_to_write_sptr->set_exam_info(exam_info); } -void IOTests_ParametricDiscretisedDensity::check_result() -{ - set_tolerance(.00001); +void +IOTests_ParametricDiscretisedDensity::check_result() { + set_tolerance(.00001); - if(!check_if_equal(_image_to_read_sptr->get_num_params(),_image_to_write_sptr->get_num_params(), "test number of dynamic images")) - return; + if (!check_if_equal(_image_to_read_sptr->get_num_params(), _image_to_write_sptr->get_num_params(), + "test number of dynamic images")) + return; - // Check the exam info - std::cerr << "\tChecking the exam info...\n"; - check_exam_info(_image_to_write_sptr->get_exam_info(),_image_to_read_sptr->get_exam_info()); + // Check the exam info + std::cerr << "\tChecking the exam info...\n"; + check_exam_info(_image_to_write_sptr->get_exam_info(), _image_to_read_sptr->get_exam_info()); - for (int i=1; i<=_image_to_read_sptr->get_num_params(); ++i) { + for (int i = 1; i <= _image_to_read_sptr->get_num_params(); ++i) { - std::cerr << "\t\tChecking kinetic parameter " << i << "...\n"; + std::cerr << "\t\tChecking kinetic parameter " << i << "...\n"; - const VoxelsOnCartesianGrid &image_to_write = - _image_to_write_sptr->construct_single_density(i); + const VoxelsOnCartesianGrid& image_to_write = _image_to_write_sptr->construct_single_density(i); - const VoxelsOnCartesianGrid &image_to_read = - _image_to_read_sptr->construct_single_density(i); + const VoxelsOnCartesianGrid& image_to_read = _image_to_read_sptr->construct_single_density(i); - compare_images(image_to_write, image_to_read); - } + compare_images(image_to_write, image_to_read); + } } END_NAMESPACE_STIR USING_NAMESPACE_STIR -int main(int argc, char **argv) -{ - if (argc != 2) { - cerr << "Usage : " << argv[0] << " filename\n" - << "See source file for the format of this file.\n\n"; - return EXIT_FAILURE; - } +int +main(int argc, char** argv) { + if (argc != 2) { + cerr << "Usage : " << argv[0] << " filename\n" + << "See source file for the format of this file.\n\n"; + return EXIT_FAILURE; + } - ifstream in(argv[1]); - if (!in) { - cerr << argv[0] - << ": Error opening input file " << argv[1] << "\nExiting.\n"; - return EXIT_FAILURE; - } + ifstream in(argv[1]); + if (!in) { + cerr << argv[0] << ": Error opening input file " << argv[1] << "\nExiting.\n"; + return EXIT_FAILURE; + } - IOTests_ParametricDiscretisedDensity tests(in); + IOTests_ParametricDiscretisedDensity tests(in); - if (tests.is_everything_ok()) - tests.run_tests(); + if (tests.is_everything_ok()) + tests.run_tests(); - return tests.main_return_value(); + return tests.main_return_value(); } diff --git a/src/test/NiftyPET_projector/test_ProjectorNiftyPET_adjoint.cxx b/src/test/NiftyPET_projector/test_ProjectorNiftyPET_adjoint.cxx index f18bfb1482..6620643e89 100644 --- a/src/test/NiftyPET_projector/test_ProjectorNiftyPET_adjoint.cxx +++ b/src/test/NiftyPET_projector/test_ProjectorNiftyPET_adjoint.cxx @@ -42,411 +42,364 @@ using namespace std; \ingroup test \brief Test class for GPU projectors */ -class TestGPUProjectors : public RunTests -{ +class TestGPUProjectors : public RunTests { public: - //! Constructor - explicit TestGPUProjectors(const unsigned num_attempts) - : _num_attempts(num_attempts), - _time_fwrd(0), _time_back(0) {} + //! Constructor + explicit TestGPUProjectors(const unsigned num_attempts) : _num_attempts(num_attempts), _time_fwrd(0), _time_back(0) {} - /// Destructor - virtual ~TestGPUProjectors() {} + /// Destructor + virtual ~TestGPUProjectors() {} - /// Run tests - void run_tests(); + /// Run tests + void run_tests(); protected: - - /// Set up - void set_up(); - /// Set up the image - void set_up_image(); - /// Set up the sinogram - void set_up_sino(); - /// test the adjoint multiple times - void test_adjoints(); - - const unsigned _num_attempts; - ForwardProjectorByBinNiftyPET _projector_fwrd; - BackProjectorByBinNiftyPET _projector_back; - shared_ptr > _image_sptr; - shared_ptr _sino_sptr; - shared_ptr > _projected_image_sptr; - shared_ptr _projected_sino_sptr; - double _time_fwrd, _time_back; - std::vector _results; + /// Set up + void set_up(); + /// Set up the image + void set_up_image(); + /// Set up the sinogram + void set_up_sino(); + /// test the adjoint multiple times + void test_adjoints(); + + const unsigned _num_attempts; + ForwardProjectorByBinNiftyPET _projector_fwrd; + BackProjectorByBinNiftyPET _projector_back; + shared_ptr> _image_sptr; + shared_ptr _sino_sptr; + shared_ptr> _projected_image_sptr; + shared_ptr _projected_sino_sptr; + double _time_fwrd, _time_back; + std::vector _results; }; -static int get_rand(const int lower, const int upper) -{ - return rand() % upper + lower; +static int +get_rand(const int lower, const int upper) { + return rand() % upper + lower; } -static float get_rand(const float lower, const float upper) -{ - return lower + static_cast(rand()) / (static_cast(RAND_MAX/(upper-lower))); +static float +get_rand(const float lower, const float upper) { + return lower + static_cast(rand()) / (static_cast(RAND_MAX / (upper - lower))); } -static -CartesianCoordinate3D -get_rand_point(const CartesianCoordinate3D &min, const CartesianCoordinate3D &max) -{ - return CartesianCoordinate3D( - get_rand(min.at(1), max.at(1)), - get_rand(min.at(2), max.at(2)), - get_rand(min.at(3), max.at(3))); +static CartesianCoordinate3D +get_rand_point(const CartesianCoordinate3D& min, const CartesianCoordinate3D& max) { + return CartesianCoordinate3D(get_rand(min.at(1), max.at(1)), get_rand(min.at(2), max.at(2)), + get_rand(min.at(3), max.at(3))); } -static -CartesianCoordinate3D -get_rand_point(const float min, const float max) -{ - return CartesianCoordinate3D( - get_rand(min, max), - get_rand(min, max), - get_rand(min, max)); +static CartesianCoordinate3D +get_rand_point(const float min, const float max) { + return CartesianCoordinate3D(get_rand(min, max), get_rand(min, max), get_rand(min, max)); } -static float find_max(const ProjDataInMemory &prj) -{ - // Get number of elements - unsigned num_elements = prj.size_all(); +static float +find_max(const ProjDataInMemory& prj) { + // Get number of elements + unsigned num_elements = prj.size_all(); - // Create arrays - std::vector arr(num_elements); - prj.copy_to(arr.begin()); - return *std::max_element(arr.begin(),arr.end()); + // Create arrays + std::vector arr(num_elements); + prj.copy_to(arr.begin()); + return *std::max_element(arr.begin(), arr.end()); } void -TestGPUProjectors:: -set_up() -{ - std::cerr << "Setting up...\n"; - - // random seed - srand(time(NULL)); - - set_up_sino(); - set_up_image(); - - std::cerr << "\tSetting up projectors...\n"; - _projector_fwrd.set_verbosity(false); - _projector_fwrd.set_up(_sino_sptr->get_proj_data_info_sptr(),_image_sptr); - _projector_back.set_verbosity(false); - _projector_back.set_up(_sino_sptr->get_proj_data_info_sptr(),_image_sptr); +TestGPUProjectors::set_up() { + std::cerr << "Setting up...\n"; + + // random seed + srand(time(NULL)); + + set_up_sino(); + set_up_image(); + + std::cerr << "\tSetting up projectors...\n"; + _projector_fwrd.set_verbosity(false); + _projector_fwrd.set_up(_sino_sptr->get_proj_data_info_sptr(), _image_sptr); + _projector_back.set_verbosity(false); + _projector_back.set_up(_sino_sptr->get_proj_data_info_sptr(), _image_sptr); } void -TestGPUProjectors:: -set_up_image() -{ - std::cerr << "\tSetting up images...\n"; - - BasicCoordinate<3, int> min_image_indices(make_coordinate(0, -160, -160)); - BasicCoordinate<3, int> max_image_indices(make_coordinate(126, 159, 159)); - IndexRange<3> range = IndexRange<3>(min_image_indices,max_image_indices); - - _image_sptr = MAKE_SHARED >( - _sino_sptr->get_exam_info_sptr(), - range, - CartesianCoordinate3D(0.f,0.f,0.f), - CartesianCoordinate3D(2.03125f, 2.08626f, 2.08626f)); - - // Fill - _image_sptr->fill(0.f); - - // Make projected image a copy - _projected_image_sptr.reset(_image_sptr->clone()); +TestGPUProjectors::set_up_image() { + std::cerr << "\tSetting up images...\n"; + + BasicCoordinate<3, int> min_image_indices(make_coordinate(0, -160, -160)); + BasicCoordinate<3, int> max_image_indices(make_coordinate(126, 159, 159)); + IndexRange<3> range = IndexRange<3>(min_image_indices, max_image_indices); + + _image_sptr = MAKE_SHARED>(_sino_sptr->get_exam_info_sptr(), range, + CartesianCoordinate3D(0.f, 0.f, 0.f), + CartesianCoordinate3D(2.03125f, 2.08626f, 2.08626f)); + + // Fill + _image_sptr->fill(0.f); + + // Make projected image a copy + _projected_image_sptr.reset(_image_sptr->clone()); } void -TestGPUProjectors:: -set_up_sino() -{ - std::cerr << "\tSetting up sinograms...\n"; - // Create scanner - shared_ptr scanner_sptr(new Scanner(Scanner::Siemens_mMR)); - - // ExamInfo - shared_ptr exam_info_sptr(new ExamInfo); - exam_info_sptr->imaging_modality = ImagingModality::PT; - - shared_ptr proj_data_info_sptr( - ProjDataInfo::construct_proj_data_info( - scanner_sptr, - 11, // span - /* mMR needs maxDelta of */60, - scanner_sptr->get_num_detectors_per_ring()/2, - scanner_sptr->get_max_num_non_arccorrected_bins(), - /* arc_correction*/false)); - - _sino_sptr = MAKE_SHARED(exam_info_sptr,proj_data_info_sptr); - - // Create vector to be able to fill sinogram - const size_t num_elements = _sino_sptr->size_all(); - std::vector arr(num_elements); - for (unsigned i=0; ifill_from(arr.begin()); - - _projected_sino_sptr = MAKE_SHARED(*_sino_sptr); +TestGPUProjectors::set_up_sino() { + std::cerr << "\tSetting up sinograms...\n"; + // Create scanner + shared_ptr scanner_sptr(new Scanner(Scanner::Siemens_mMR)); + + // ExamInfo + shared_ptr exam_info_sptr(new ExamInfo); + exam_info_sptr->imaging_modality = ImagingModality::PT; + + shared_ptr proj_data_info_sptr( + ProjDataInfo::construct_proj_data_info(scanner_sptr, + 11, // span + /* mMR needs maxDelta of */ 60, scanner_sptr->get_num_detectors_per_ring() / 2, + scanner_sptr->get_max_num_non_arccorrected_bins(), + /* arc_correction*/ false)); + + _sino_sptr = MAKE_SHARED(exam_info_sptr, proj_data_info_sptr); + + // Create vector to be able to fill sinogram + const size_t num_elements = _sino_sptr->size_all(); + std::vector arr(num_elements); + for (unsigned i = 0; i < num_elements; ++i) + arr[i] = float(i); + _sino_sptr->fill_from(arr.begin()); + + _projected_sino_sptr = MAKE_SHARED(*_sino_sptr); } -static -void -get_random_sino(ProjData &sino) -{ - std::cerr << "Getting random sinogram...\n"; - - const size_t num_elements = sino.size_all(); - std::vector arr(num_elements,0.f); - - // Number of voxels to fill - const unsigned num_rand_voxels = get_rand(100,2000); - for (unsigned j=0; j arr(num_elements, 0.f); + + // Number of voxels to fill + const unsigned num_rand_voxels = get_rand(100, 2000); + for (unsigned j = 0; j < num_rand_voxels; ++j) { + // Get random index + const unsigned idx = get_rand(0, num_elements - 1); + const float val = get_rand(1.f, 100.f); + arr[idx] = val; + } + sino.fill_from(arr.begin()); } -static -void -get_random_image(VoxelsOnCartesianGrid &im) -{ - std::cerr << "Getting random image...\n"; - - im.fill(0.f); - auto temp = *im.clone(); - - CartesianCoordinate3D min = im.get_physical_coordinates_for_indices(im.get_min_indices()); - CartesianCoordinate3D max = im.get_physical_coordinates_for_indices(im.get_max_indices()); - CartesianCoordinate3D num_samles = {10,10,10}; - const float min_radius = 20.f; - const float max_radius = 100.f; - const float min_intensity = 10.f; - const float max_intensity = 100.f; - - // Keep looping until random image contains non-zeroes - // (in case all ellipsoids were in part that got truncated) - while (true) { - - const unsigned num_ellipsoids = get_rand(1,5); - - for (unsigned j=0; j 1e-4f) - break; - else - std::cout << "\nRandom image contains all zeroes, regenerating...\n"; +static void +get_random_image(VoxelsOnCartesianGrid& im) { + std::cerr << "Getting random image...\n"; + + im.fill(0.f); + auto temp = *im.clone(); + + CartesianCoordinate3D min = im.get_physical_coordinates_for_indices(im.get_min_indices()); + CartesianCoordinate3D max = im.get_physical_coordinates_for_indices(im.get_max_indices()); + CartesianCoordinate3D num_samles = {10, 10, 10}; + const float min_radius = 20.f; + const float max_radius = 100.f; + const float min_intensity = 10.f; + const float max_intensity = 100.f; + + // Keep looping until random image contains non-zeroes + // (in case all ellipsoids were in part that got truncated) + while (true) { + + const unsigned num_ellipsoids = get_rand(1, 5); + + for (unsigned j = 0; j < num_ellipsoids; ++j) { + + // Get radii, centre and intensity of ellipsoid + const auto radii = get_rand_point(min_radius, max_radius); + const auto centre = get_rand_point(min, max); + const float intensity = get_rand(min_intensity, max_intensity); + // Create shape + Ellipsoid ellipsoid(radii, centre); + // Get shape as image + temp.fill(0.f); + ellipsoid.construct_volume(temp, num_samles); + temp *= intensity; + // Add to output image + im += temp; } + + // Truncate it to a small cylinder + truncate_rim(im, 17); + + // Check that image contains non-zeros. + if (im.find_max() > 1e-4f) + break; + else + std::cout << "\nRandom image contains all zeroes, regenerating...\n"; + } } -static -void -forward_project(ProjData &sino, const VoxelsOnCartesianGrid &im, ForwardProjectorByBinNiftyPET &projector, double &time) -{ - std::cerr << "Forward projecting...\n"; - sino.fill(0.f); +static void +forward_project(ProjData& sino, const VoxelsOnCartesianGrid& im, ForwardProjectorByBinNiftyPET& projector, double& time) { + std::cerr << "Forward projecting...\n"; + sino.fill(0.f); - CPUTimer timer; - timer.start(); + CPUTimer timer; + timer.start(); - projector.set_input(im); - projector.forward_project(sino); + projector.set_input(im); + projector.forward_project(sino); - timer.stop(); - time += timer.value(); + timer.stop(); + time += timer.value(); } -static -void -back_project(VoxelsOnCartesianGrid &im, const ProjData &sino, BackProjectorByBinNiftyPET &projector, double &time) -{ - std::cerr << "Back projecting...\n"; - im.fill(0.f); +static void +back_project(VoxelsOnCartesianGrid& im, const ProjData& sino, BackProjectorByBinNiftyPET& projector, double& time) { + std::cerr << "Back projecting...\n"; + im.fill(0.f); - CPUTimer timer; - timer.start(); + CPUTimer timer; + timer.start(); - projector.start_accumulating_in_new_target(); - projector.back_project(sino); - projector.get_output(im); + projector.start_accumulating_in_new_target(); + projector.back_project(sino); + projector.get_output(im); - timer.stop(); - time += timer.value(); + timer.stop(); + time += timer.value(); } -static float get_inner_product( - const VoxelsOnCartesianGrid &im1, - const VoxelsOnCartesianGrid &im2) -{ - return std::inner_product(im1.begin_all(),im1.end_all(),im2.begin_all(),0.f); +static float +get_inner_product(const VoxelsOnCartesianGrid& im1, const VoxelsOnCartesianGrid& im2) { + return std::inner_product(im1.begin_all(), im1.end_all(), im2.begin_all(), 0.f); } -static float get_inner_product( - const ProjDataInMemory &proj1, - const ProjDataInMemory &proj2) -{ - // Get number of elements - const size_t num_elements = proj1.size_all(); +static float +get_inner_product(const ProjDataInMemory& proj1, const ProjDataInMemory& proj2) { + // Get number of elements + const size_t num_elements = proj1.size_all(); - // Create arrays - std::vector arr1(num_elements), arr2(num_elements); - proj1.copy_to(arr1.begin()); - proj2.copy_to(arr2.begin()); + // Create arrays + std::vector arr1(num_elements), arr2(num_elements); + proj1.copy_to(arr1.begin()); + proj2.copy_to(arr2.begin()); - return std::inner_product(arr1.begin(),arr1.end(),arr2.begin(),0.f); + return std::inner_product(arr1.begin(), arr1.end(), arr2.begin(), 0.f); } -static -float -test_inner_product(const VoxelsOnCartesianGrid &im, const ProjData &sino, - const VoxelsOnCartesianGrid &im_proj, const ProjData &sino_proj) -{ - std::cerr << "Checking inner products...\n"; - const float inner_product_images = get_inner_product(im,im_proj); - const float inner_product_sinos = get_inner_product(sino, sino_proj); - - std::cout << "\tinner product between images = " << inner_product_images << "\n"; - std::cout << "\tinner product between sinograms = " << inner_product_sinos << "\n"; - - if (std::abs(inner_product_images) + std::abs(inner_product_sinos) < 1e-4f) { - std::cout << "\n\t\tCan't perform adjoint test as both equal zero...\n"; - std::cout << "\t\tmax in input image = " << im.find_max() << "\n"; - std::cout << "\t\tmax in projected image = " << im_proj.find_max() << "\n"; - std::cout << "\t\tmax in input image = " << find_max(sino) << "\n"; - std::cout << "\t\tmax in projected image = " << find_max(sino_proj) << "\n"; - return -1.f; - } - - float adjoint_test = - std::abs(inner_product_images - inner_product_sinos) / - (0.5f * (std::abs(inner_product_images) + std::abs(inner_product_sinos))); - std::cout << "\t| - | / 0.5*(||+||) = " << adjoint_test << "\n"; - return adjoint_test; +static float +test_inner_product(const VoxelsOnCartesianGrid& im, const ProjData& sino, const VoxelsOnCartesianGrid& im_proj, + const ProjData& sino_proj) { + std::cerr << "Checking inner products...\n"; + const float inner_product_images = get_inner_product(im, im_proj); + const float inner_product_sinos = get_inner_product(sino, sino_proj); + + std::cout << "\tinner product between images = " << inner_product_images << "\n"; + std::cout << "\tinner product between sinograms = " << inner_product_sinos << "\n"; + + if (std::abs(inner_product_images) + std::abs(inner_product_sinos) < 1e-4f) { + std::cout << "\n\t\tCan't perform adjoint test as both equal zero...\n"; + std::cout << "\t\tmax in input image = " << im.find_max() << "\n"; + std::cout << "\t\tmax in projected image = " << im_proj.find_max() << "\n"; + std::cout << "\t\tmax in input image = " << find_max(sino) << "\n"; + std::cout << "\t\tmax in projected image = " << find_max(sino_proj) << "\n"; + return -1.f; + } + + float adjoint_test = std::abs(inner_product_images - inner_product_sinos) / + (0.5f * (std::abs(inner_product_images) + std::abs(inner_product_sinos))); + std::cout << "\t| - | / 0.5*(||+||) = " << adjoint_test << "\n"; + return adjoint_test; } void -TestGPUProjectors:: -test_adjoints() -{ - set_up(); - - unsigned num_unsuccessful(0); - - while(_results.size() < _num_attempts) { - - unsigned i = _results.size(); - - std::cout << "\nPerforming test " << i+1 << " of " << _num_attempts << "\n"; - - // Even iterations, modify the image - if (i%2==0) { - get_random_image(*_image_sptr); - forward_project(*_projected_sino_sptr, *_image_sptr, _projector_fwrd, _time_fwrd); - } - // Odd iterations (and first), modify the sinogram - if (i==0 || i%2==1) { - get_random_sino(*_sino_sptr); - back_project(*_projected_image_sptr, *_sino_sptr, _projector_back, _time_back); - } - - const float adjoint_test = - test_inner_product(*_image_sptr, *_sino_sptr, *_projected_image_sptr, *_projected_sino_sptr); - if (adjoint_test > 0.f) { - _results.push_back(adjoint_test); - std::cout << "\tAvg. test result = " << std::accumulate(_results.begin(), _results.end(), 0.0) /double(i+1) << - " (number of tests = " << i+1 << "), avg. time forward projecting = " << _time_fwrd/double(i+1) << " s, " << - "avg. time back projecting = " << _time_back/double(i+1) << " s.\n\n"; - - // Check the result - if (adjoint_test > 1e-4f) - error("Adjoint test greater than threshold, failed!"); - - // Reset unsuccessful counter - num_unsuccessful = 0; - } - else { - ++num_unsuccessful; - if (num_unsuccessful==5) - error("Too many (5) unsuccessful comparisons"); - } - } -} +TestGPUProjectors::test_adjoints() { + set_up(); -void -TestGPUProjectors:: -run_tests() -{ - try { - cerr << "Testing whether forward and back projectors are adjoint...\n"; - this->test_adjoints(); + unsigned num_unsuccessful(0); + + while (_results.size() < _num_attempts) { + + unsigned i = _results.size(); + + std::cout << "\nPerforming test " << i + 1 << " of " << _num_attempts << "\n"; + + // Even iterations, modify the image + if (i % 2 == 0) { + get_random_image(*_image_sptr); + forward_project(*_projected_sino_sptr, *_image_sptr, _projector_fwrd, _time_fwrd); } - catch(const std::exception &error) { - std::cerr << "\nHere's the error:\n\t" << error.what() << "\n\n"; - everything_ok = false; + // Odd iterations (and first), modify the sinogram + if (i == 0 || i % 2 == 1) { + get_random_sino(*_sino_sptr); + back_project(*_projected_image_sptr, *_sino_sptr, _projector_back, _time_back); } - catch(...) { - everything_ok = false; + + const float adjoint_test = test_inner_product(*_image_sptr, *_sino_sptr, *_projected_image_sptr, *_projected_sino_sptr); + if (adjoint_test > 0.f) { + _results.push_back(adjoint_test); + std::cout << "\tAvg. test result = " << std::accumulate(_results.begin(), _results.end(), 0.0) / double(i + 1) + << " (number of tests = " << i + 1 << "), avg. time forward projecting = " << _time_fwrd / double(i + 1) << " s, " + << "avg. time back projecting = " << _time_back / double(i + 1) << " s.\n\n"; + + // Check the result + if (adjoint_test > 1e-4f) + error("Adjoint test greater than threshold, failed!"); + + // Reset unsuccessful counter + num_unsuccessful = 0; + } else { + ++num_unsuccessful; + if (num_unsuccessful == 5) + error("Too many (5) unsuccessful comparisons"); } + } } -END_NAMESPACE_STIR +void +TestGPUProjectors::run_tests() { + try { + cerr << "Testing whether forward and back projectors are adjoint...\n"; + this->test_adjoints(); + } catch (const std::exception& error) { + std::cerr << "\nHere's the error:\n\t" << error.what() << "\n\n"; + everything_ok = false; + } catch (...) { + everything_ok = false; + } +} +END_NAMESPACE_STIR USING_NAMESPACE_STIR -void print_usage() -{ - std::cerr << "\n\tUsage: test_ProjectorNiftyPET_adjoint [-h] \n"; +void +print_usage() { + std::cerr << "\n\tUsage: test_ProjectorNiftyPET_adjoint [-h] \n"; } -int main(int argc, char **argv) -{ - set_default_num_threads(); - Verbosity::set(0); +int +main(int argc, char** argv) { + set_default_num_threads(); + Verbosity::set(0); - // Require a single argument - if (argc != 2) { - print_usage(); - return EXIT_SUCCESS; - } + // Require a single argument + if (argc != 2) { + print_usage(); + return EXIT_SUCCESS; + } - // If help desired - if (strcmp(argv[1],"-h") ==0) { - print_usage(); - return EXIT_SUCCESS; - } + // If help desired + if (strcmp(argv[1], "-h") == 0) { + print_usage(); + return EXIT_SUCCESS; + } - const unsigned num_attempts = std::stoi(argv[1]); + const unsigned num_attempts = std::stoi(argv[1]); - TestGPUProjectors test(num_attempts); + TestGPUProjectors test(num_attempts); - if (test.is_everything_ok()) - test.run_tests(); + if (test.is_everything_ok()) + test.run_tests(); - return test.main_return_value(); + return test.main_return_value(); } diff --git a/src/test/modelling/test_ParametricDiscretisedDensity.cxx b/src/test/modelling/test_ParametricDiscretisedDensity.cxx index 9c0955cc4c..4d4da7cad1 100644 --- a/src/test/modelling/test_ParametricDiscretisedDensity.cxx +++ b/src/test/modelling/test_ParametricDiscretisedDensity.cxx @@ -1,31 +1,31 @@ /* Copyright (C) 2006- 2011, Hammersmith Imanet Ltd This file is part of STIR. - + This file is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. - + This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - + See STIR/LICENSE.txt for details */ -/*! - +/*! + \file \ingroup test - + \brief testing stir::ParametricDiscretisedDensity class - + \author T. Borgeaud -\author Charalampos Tsoumpas - - - +\author Charalampos Tsoumpas + + + */ #include @@ -60,112 +60,111 @@ using std::endl; START_NAMESPACE_STIR -class ParametricDiscretisedDensityTests : public RunTests -{ +class ParametricDiscretisedDensityTests : public RunTests { public: - ParametricDiscretisedDensityTests() - {} + ParametricDiscretisedDensityTests() {} void run_tests(); - //private: + // private: }; -void ParametricDiscretisedDensityTests::run_tests() -{ +void +ParametricDiscretisedDensityTests::run_tests() { set_tolerance(0.000000000000001); const unsigned num_params = ParametricVoxelsOnCartesianGrid::get_num_params(); //! Setup the scanner details first - const Scanner::Type test_scanner=Scanner::E966; + const Scanner::Type test_scanner = Scanner::E966; const shared_ptr scanner_sptr(new Scanner(test_scanner)); - VectorWithOffset num_axial_pos_per_segment; num_axial_pos_per_segment.resize(0,0); num_axial_pos_per_segment[0]=48; - VectorWithOffset min_ring_diff; min_ring_diff.resize(0,0); min_ring_diff[0]=0; - VectorWithOffset max_ring_diff; max_ring_diff.resize(0,0); max_ring_diff[0]=0; - const int num_views=144; const int num_tangential_poss=144; - ProjDataInfoCylindricalNoArcCorr proj_data_info(scanner_sptr,num_axial_pos_per_segment,min_ring_diff,max_ring_diff,num_views,num_tangential_poss); + VectorWithOffset num_axial_pos_per_segment; + num_axial_pos_per_segment.resize(0, 0); + num_axial_pos_per_segment[0] = 48; + VectorWithOffset min_ring_diff; + min_ring_diff.resize(0, 0); + min_ring_diff[0] = 0; + VectorWithOffset max_ring_diff; + max_ring_diff.resize(0, 0); + max_ring_diff[0] = 0; + const int num_views = 144; + const int num_tangential_poss = 144; + ProjDataInfoCylindricalNoArcCorr proj_data_info(scanner_sptr, num_axial_pos_per_segment, min_ring_diff, max_ring_diff, + num_views, num_tangential_poss); //! Setup some of the image details - const CartesianCoordinate3D< float > origin (0.F,0.F,0.F); - const CartesianCoordinate3D grid_spacing (1.F,1.F,1.F); - const float zoom=1.F; + const CartesianCoordinate3D origin(0.F, 0.F, 0.F); + const CartesianCoordinate3D grid_spacing(1.F, 1.F, 1.F); + const float zoom = 1.F; { cerr << "Testing ParametricDiscretisedDensity class for one voxel..." << endl; - const CartesianCoordinate3D sizes (1,1,1); - - const shared_ptr parametric_image_sptr - ( - new ParametricVoxelsOnCartesianGrid( - ParametricVoxelsOnCartesianGridBaseType(proj_data_info, - zoom,grid_spacing,sizes))); - ParametricVoxelsOnCartesianGrid & parametric_image = *parametric_image_sptr; - parametric_image[0][0][0][1]=1.F; parametric_image[0][0][0][2]=2.F; - - check_if_equal(parametric_image[0][0][0][1],1.F,"check ParametricVoxelsOnCartesianGrid class implementation"); - check_if_equal(parametric_image[0][0][0][2],2.F,"check ParametricVoxelsOnCartesianGrid class implementation"); + const CartesianCoordinate3D sizes(1, 1, 1); + + const shared_ptr parametric_image_sptr( + new ParametricVoxelsOnCartesianGrid(ParametricVoxelsOnCartesianGridBaseType(proj_data_info, zoom, grid_spacing, sizes))); + ParametricVoxelsOnCartesianGrid& parametric_image = *parametric_image_sptr; + parametric_image[0][0][0][1] = 1.F; + parametric_image[0][0][0][2] = 2.F; + + check_if_equal(parametric_image[0][0][0][1], 1.F, "check ParametricVoxelsOnCartesianGrid class implementation"); + check_if_equal(parametric_image[0][0][0][2], 2.F, "check ParametricVoxelsOnCartesianGrid class implementation"); } { - // Test of two frame images, read voxel + // Test of two frame images, read voxel cerr << "Testing ParametricDiscretisedDensity class for more voxels: 63x128x128..." << endl; - const CartesianCoordinate3D sizes (63,128,128); - - //const shared_ptr parametric_image_sptr = + const CartesianCoordinate3D sizes(63, 128, 128); + + // const shared_ptr parametric_image_sptr = // new ParametricVoxelsOnCartesianGrid(ParametricVoxelsOnCartesianGridBaseType(proj_data_info,zoom,grid_spacing,sizes)); - //ParametricVoxelsOnCartesianGrid & parametric_image = *parametric_image_sptr; + // ParametricVoxelsOnCartesianGrid & parametric_image = *parametric_image_sptr; - //ParametricVoxelsOnCartesianGrid & parametric_image = *parametric_image_sptr; - ParametricVoxelsOnCartesianGrid parametric_image(ParametricVoxelsOnCartesianGridBaseType(proj_data_info,zoom,grid_spacing,sizes)); - for(int k=0;k<63;++k) - for(int j=-64;j<63;++j) - for(int i=-64;i<63;++i) - for(unsigned int par_num=1; par_num<=num_params; ++par_num) - parametric_image[k][j][i][par_num] = static_cast (par_num*(i*1.F+j*5.F-k*10.F)); + // ParametricVoxelsOnCartesianGrid & parametric_image = *parametric_image_sptr; + ParametricVoxelsOnCartesianGrid parametric_image( + ParametricVoxelsOnCartesianGridBaseType(proj_data_info, zoom, grid_spacing, sizes)); + for (int k = 0; k < 63; ++k) + for (int j = -64; j < 63; ++j) + for (int i = -64; i < 63; ++i) + for (unsigned int par_num = 1; par_num <= num_params; ++par_num) + parametric_image[k][j][i][par_num] = static_cast(par_num * (i * 1.F + j * 5.F - k * 10.F)); cerr << "- Checking the [] operator. " << endl; - for(int k=0;k<63;++k) - for(int j=-64;j<63;++j) - for(int i=-64;i<63;++i) - for(unsigned int par_num=1; par_num<=num_params; ++par_num) - check_if_equal(parametric_image[k][j][i][par_num],static_cast (par_num*(i*1.F+j*5.F-k*10.F)), - "Please, check the [] operator implementation"); - + for (int k = 0; k < 63; ++k) + for (int j = -64; j < 63; ++j) + for (int i = -64; i < 63; ++i) + for (unsigned int par_num = 1; par_num <= num_params; ++par_num) + check_if_equal(parametric_image[k][j][i][par_num], static_cast(par_num * (i * 1.F + j * 5.F - k * 10.F)), + "Please, check the [] operator implementation"); cerr << "- Checking construct_single_density(param_num) implementation." << endl; // TODO tests need to be rewritten to accomodate for other num_params!=2 - if (num_params!=2) - { - warning("test_ParametricDiscretisedDensity test only tests first 2 maps"); - } + if (num_params != 2) { + warning("test_ParametricDiscretisedDensity test only tests first 2 maps"); + } ParametricVoxelsOnCartesianGrid::SingleDiscretisedDensityType single_density_1 = parametric_image.construct_single_density(1); ParametricVoxelsOnCartesianGrid::SingleDiscretisedDensityType single_density_2 = parametric_image.construct_single_density(2); - for(int k=0;k<63;++k) - for(int j=-64;j<63;++j) - for(int i=-64;i<63;++i) - { - check_if_equal(parametric_image[k][j][i][1],single_density_1[k][j][i], - "Please, check construct_single_density(param_num) implementation"); - check_if_equal(parametric_image[k][j][i][2],single_density_2[k][j][i], - "Please, check construct_single_density(param_num) implementation"); - for(unsigned int par_num=1; par_num<=num_params; ++par_num) - check_if_equal(parametric_image[k][j][i][par_num],static_cast (par_num*(i*1.F+j*5.F-k*10.F)), - "Please, check the construct_single_density(param_num) implementation"); - } - - ParametricVoxelsOnCartesianGrid::SingleDiscretisedDensityType::full_iterator cur_iter_1 = - single_density_1.begin_all(); - ParametricVoxelsOnCartesianGrid::SingleDiscretisedDensityType::full_iterator cur_iter_2 = - single_density_2.begin_all(); - for (; cur_iter_1!=single_density_1.end_all() && - cur_iter_2!=single_density_2.end_all(); - ++cur_iter_1, ++cur_iter_2) - check_if_equal(*cur_iter_1*2.F, *cur_iter_2, - "Please, check construct_single_density(param_num) implementation"); + for (int k = 0; k < 63; ++k) + for (int j = -64; j < 63; ++j) + for (int i = -64; i < 63; ++i) { + check_if_equal(parametric_image[k][j][i][1], single_density_1[k][j][i], + "Please, check construct_single_density(param_num) implementation"); + check_if_equal(parametric_image[k][j][i][2], single_density_2[k][j][i], + "Please, check construct_single_density(param_num) implementation"); + for (unsigned int par_num = 1; par_num <= num_params; ++par_num) + check_if_equal(parametric_image[k][j][i][par_num], static_cast(par_num * (i * 1.F + j * 5.F - k * 10.F)), + "Please, check the construct_single_density(param_num) implementation"); + } + + ParametricVoxelsOnCartesianGrid::SingleDiscretisedDensityType::full_iterator cur_iter_1 = single_density_1.begin_all(); + ParametricVoxelsOnCartesianGrid::SingleDiscretisedDensityType::full_iterator cur_iter_2 = single_density_2.begin_all(); + for (; cur_iter_1 != single_density_1.end_all() && cur_iter_2 != single_density_2.end_all(); ++cur_iter_1, ++cur_iter_2) + check_if_equal(*cur_iter_1 * 2.F, *cur_iter_2, "Please, check construct_single_density(param_num) implementation"); std::fill(single_density_1.begin_all(), single_density_1.end_all(), 1.F); std::fill(single_density_2.begin_all(), single_density_2.end_all(), 2.F); - VectorWithOffset > v_test; - v_test.resize(1,2); v_test[1].reset(single_density_1.clone()); v_test[2].reset(single_density_2.clone()); + VectorWithOffset> v_test; + v_test.resize(1, 2); + v_test[1].reset(single_density_1.clone()); + v_test[2].reset(single_density_2.clone()); #if 0 // test disabled as function currently removed from class cerr << "- Checking constructor from(VectorWithOffset single_densities) implementation." << endl; @@ -213,19 +212,19 @@ void ParametricDiscretisedDensityTests::run_tests() } #endif cerr << "- Checking update_parametric_image(single_density,param_num) implementation." << endl; - parametric_image.update_parametric_image(single_density_1,2); + parametric_image.update_parametric_image(single_density_1, 2); #if 1 - parametric_image.update_parametric_image(single_density_2,1); + parametric_image.update_parametric_image(single_density_2, 1); // Check if the result has been reversed. { bool still_equal = true; - for(int k=0;still_equal && k<63;++k) - for(int j=-64;still_equal && j<63;++j) - for(int i=-64;still_equal && i<63;++i) - for(unsigned int par_num=1;still_equal && par_num<=num_params;++par_num) - still_equal = check_if_equal(parametric_image[k][j][i][par_num],static_cast (2+1-par_num), - "Please, check update_parametric_image(VectorWithOffset) implementation"); + for (int k = 0; still_equal && k < 63; ++k) + for (int j = -64; still_equal && j < 63; ++j) + for (int i = -64; still_equal && i < 63; ++i) + for (unsigned int par_num = 1; still_equal && par_num <= num_params; ++par_num) + still_equal = check_if_equal(parametric_image[k][j][i][par_num], static_cast(2 + 1 - par_num), + "Please, check update_parametric_image(VectorWithOffset) implementation"); } #endif } @@ -234,13 +233,12 @@ void ParametricDiscretisedDensityTests::run_tests() END_NAMESPACE_STIR USING_NAMESPACE_STIR -int main(int argc, char **argv) -{ - if (argc != 1) - { - cerr << "Usage : " << argv[0] << " \n"; - return EXIT_FAILURE; - } +int +main(int argc, char** argv) { + if (argc != 1) { + cerr << "Usage : " << argv[0] << " \n"; + return EXIT_FAILURE; + } ParametricDiscretisedDensityTests tests; tests.run_tests(); return tests.main_return_value(); diff --git a/src/test/modelling/test_modelling.cxx b/src/test/modelling/test_modelling.cxx index 77ab5c07fa..54054f364a 100644 --- a/src/test/modelling/test_modelling.cxx +++ b/src/test/modelling/test_modelling.cxx @@ -1,7 +1,7 @@ // // /*! - \file + \file \ingroup test \brief tests parts of the modelling implementation @@ -25,7 +25,6 @@ See STIR/LICENSE.txt for details */ - #include "stir/RunTests.h" #include "stir/modelling/PatlakPlot.h" #include "stir/modelling/ModelMatrix.h" @@ -41,173 +40,194 @@ START_NAMESPACE_STIR \ingroup test \brief A simple class to test modelling functions. */ -class modellingTests : public RunTests -{ +class modellingTests : public RunTests { public: explicit modellingTests(const std::string& directory); void run_tests(); + private: - //istream& in; + // istream& in; std::string directory; boost::shared_array full_filename_sptr; std::string add_directory(const std::string& filename); }; -modellingTests:: -modellingTests(const std::string& directory_v) - : directory(directory_v), - full_filename_sptr(new char[directory_v.length() + 100]) -{} +modellingTests::modellingTests(const std::string& directory_v) + : directory(directory_v), full_filename_sptr(new char[directory_v.length() + 100]) {} -std::string -modellingTests:: -add_directory(const std::string& filename) -{ +std::string +modellingTests::add_directory(const std::string& filename) { strcpy(this->full_filename_sptr.get(), filename.c_str()); - prepend_directory_name(this->full_filename_sptr.get(),this->directory.c_str()); + prepend_directory_name(this->full_filename_sptr.get(), this->directory.c_str()); return std::string(this->full_filename_sptr.get()); } -void modellingTests::run_tests() -{ +void +modellingTests::run_tests() { std::cerr << "Testing basic modelling functions..." << std::endl; set_tolerance(0.004); - + { - std::cerr << "Testing the reading of PlasmaData ..." << std::endl; - - PlasmaData file_plasma_data, testing_plasma_data; - file_plasma_data.read_plasma_data(this->add_directory("triple_plasma.if")); - std::vector this_plasma_blood_plot; - const PlasmaSample sample_1(0.5F,.999947F,.0999947F); - const PlasmaSample sample_2(7573.3F,.450739F,.0450739F); - const PlasmaSample sample_3(30292.2F,.0412893F,.00412893F); - this_plasma_blood_plot.push_back(sample_1); - this_plasma_blood_plot.push_back(sample_2); - this_plasma_blood_plot.push_back(sample_3); - testing_plasma_data.set_plot(this_plasma_blood_plot); - - - PlasmaData::const_iterator cur_iter_1, cur_iter_2; - - for (cur_iter_1=file_plasma_data.begin(), cur_iter_2=testing_plasma_data.begin(); - cur_iter_1!=file_plasma_data.end() && cur_iter_2!=testing_plasma_data.end(); - ++cur_iter_1, ++cur_iter_2) - { - check_if_equal((*cur_iter_1).get_time_in_s(),(*cur_iter_2).get_time_in_s(), "Check Reading Time of PlasmaData "); - check_if_equal((*cur_iter_1).get_plasma_counts_in_kBq(),(*cur_iter_2).get_plasma_counts_in_kBq(), "Check Reading Plasma of PlasmaData "); - check_if_equal((*cur_iter_1).get_blood_counts_in_kBq(),(*cur_iter_2).get_blood_counts_in_kBq(), "Check Reading Blood of PlasmaData "); - } + std::cerr << "Testing the reading of PlasmaData ..." << std::endl; + + PlasmaData file_plasma_data, testing_plasma_data; + file_plasma_data.read_plasma_data(this->add_directory("triple_plasma.if")); + std::vector this_plasma_blood_plot; + const PlasmaSample sample_1(0.5F, .999947F, .0999947F); + const PlasmaSample sample_2(7573.3F, .450739F, .0450739F); + const PlasmaSample sample_3(30292.2F, .0412893F, .00412893F); + this_plasma_blood_plot.push_back(sample_1); + this_plasma_blood_plot.push_back(sample_2); + this_plasma_blood_plot.push_back(sample_3); + testing_plasma_data.set_plot(this_plasma_blood_plot); + + PlasmaData::const_iterator cur_iter_1, cur_iter_2; + + for (cur_iter_1 = file_plasma_data.begin(), cur_iter_2 = testing_plasma_data.begin(); + cur_iter_1 != file_plasma_data.end() && cur_iter_2 != testing_plasma_data.end(); ++cur_iter_1, ++cur_iter_2) { + check_if_equal((*cur_iter_1).get_time_in_s(), (*cur_iter_2).get_time_in_s(), "Check Reading Time of PlasmaData "); + check_if_equal((*cur_iter_1).get_plasma_counts_in_kBq(), (*cur_iter_2).get_plasma_counts_in_kBq(), + "Check Reading Plasma of PlasmaData "); + check_if_equal((*cur_iter_1).get_blood_counts_in_kBq(), (*cur_iter_2).get_blood_counts_in_kBq(), + "Check Reading Blood of PlasmaData "); + } } { - std::cerr << "Testing the reading and writing of the ModelMatrix ..." << std::endl; - - ModelMatrix<2> file_model_matrix, correct_model_matrix; - file_model_matrix.read_from_file(this->add_directory("model_array.in")); - file_model_matrix.write_to_file(this->add_directory("model_array.out")); - - BasicCoordinate<2,int> min_range; - BasicCoordinate<2,int> max_range; - min_range[1]=1; min_range[2]=23; - max_range[1]=2; max_range[2]=28; - IndexRange<2> data_range(min_range,max_range); - Array<2,float> correct_model_array(data_range); - correct_model_array[1][23]=1; correct_model_array[2][23]=2; - correct_model_array[1][24]=11; correct_model_array[2][24]=22; - correct_model_array[1][25]=111; correct_model_array[2][25]=222; - correct_model_array[1][26]=1111; correct_model_array[2][26]=2222; - correct_model_array[1][27]=11111; correct_model_array[2][27]=22222; - correct_model_array[1][28]=111111; correct_model_array[2][28]=222222; - - correct_model_matrix.set_model_array(correct_model_array); - - Array<2,float> file_model_array=file_model_matrix.get_model_array(); - Array<2,float> get_correct_model_array=correct_model_matrix.get_model_array(); - - for (unsigned int param_num=1;param_num<=2;++param_num) - for(unsigned int frame_num=23;frame_num<=28;++frame_num) - { - check_if_equal(file_model_array[param_num][frame_num],get_correct_model_array[param_num][frame_num],"Check ModelMatrix reading. "); - check_if_equal(file_model_array[param_num][frame_num],correct_model_array[param_num][frame_num],"Check ModelMatrix reading. "); - } + std::cerr << "Testing the reading and writing of the ModelMatrix ..." << std::endl; + + ModelMatrix<2> file_model_matrix, correct_model_matrix; + file_model_matrix.read_from_file(this->add_directory("model_array.in")); + file_model_matrix.write_to_file(this->add_directory("model_array.out")); + + BasicCoordinate<2, int> min_range; + BasicCoordinate<2, int> max_range; + min_range[1] = 1; + min_range[2] = 23; + max_range[1] = 2; + max_range[2] = 28; + IndexRange<2> data_range(min_range, max_range); + Array<2, float> correct_model_array(data_range); + correct_model_array[1][23] = 1; + correct_model_array[2][23] = 2; + correct_model_array[1][24] = 11; + correct_model_array[2][24] = 22; + correct_model_array[1][25] = 111; + correct_model_array[2][25] = 222; + correct_model_array[1][26] = 1111; + correct_model_array[2][26] = 2222; + correct_model_array[1][27] = 11111; + correct_model_array[2][27] = 22222; + correct_model_array[1][28] = 111111; + correct_model_array[2][28] = 222222; + + correct_model_matrix.set_model_array(correct_model_array); + + Array<2, float> file_model_array = file_model_matrix.get_model_array(); + Array<2, float> get_correct_model_array = correct_model_matrix.get_model_array(); + + for (unsigned int param_num = 1; param_num <= 2; ++param_num) + for (unsigned int frame_num = 23; frame_num <= 28; ++frame_num) { + check_if_equal(file_model_array[param_num][frame_num], get_correct_model_array[param_num][frame_num], + "Check ModelMatrix reading. "); + check_if_equal(file_model_array[param_num][frame_num], correct_model_array[param_num][frame_num], + "Check ModelMatrix reading. "); + } } { // This tests uses the results from the Mathematica. The used plasma and frame files are parts of the t00196 scan. - std::cerr << "\nTesting the sampling of PlasmaData into frames ..." << std::endl; + std::cerr << "\nTesting the sampling of PlasmaData into frames ..." << std::endl; PlasmaData file_plasma_data, testing_plasma_data; file_plasma_data.read_plasma_data(this->add_directory("plasma.if")); std::vector this_plasma_blood_plot; - TimeFrameDefinitions time_frame_def(this->add_directory("time.fdef")); + TimeFrameDefinitions time_frame_def(this->add_directory("time.fdef")); file_plasma_data.set_isotope_halflife(6586.2F); PlasmaData sample_plasma_data_in_frames = file_plasma_data.get_sample_data_in_frames(time_frame_def); - const PlasmaSample sample_17(1, 11.4776, 10.7832); const PlasmaSample sample_18(1, 10.7523, 10.1135); const PlasmaSample sample_19(1, 10.0841, 9.50239); - const PlasmaSample sample_20(1, 9.24207, 8.7949); const PlasmaSample sample_21(1, 8.39741, 8.04141); const PlasmaSample sample_22(1, 7.74369, 7.36121); - const PlasmaSample sample_23(1, 7.18224, 6.78764); const PlasmaSample sample_24(1, 6.67699, 6.3266); const PlasmaSample sample_25(1, 6.23402, 5.93635); - const PlasmaSample sample_26(1, 5.8495, 5.593); const PlasmaSample sample_27(1, 5.50858, 5.29071); const PlasmaSample sample_28(1, 5.19509, 5.02458); - - TimeFrameDefinitions plasma_fdef=sample_plasma_data_in_frames.get_time_frame_definitions(); - - this_plasma_blood_plot.push_back(sample_17); this_plasma_blood_plot.push_back(sample_18); this_plasma_blood_plot.push_back(sample_19); - this_plasma_blood_plot.push_back(sample_20); this_plasma_blood_plot.push_back(sample_21); this_plasma_blood_plot.push_back(sample_22); - this_plasma_blood_plot.push_back(sample_23); this_plasma_blood_plot.push_back(sample_24); this_plasma_blood_plot.push_back(sample_25); - this_plasma_blood_plot.push_back(sample_26); this_plasma_blood_plot.push_back(sample_27); this_plasma_blood_plot.push_back(sample_28); + const PlasmaSample sample_17(1, 11.4776, 10.7832); + const PlasmaSample sample_18(1, 10.7523, 10.1135); + const PlasmaSample sample_19(1, 10.0841, 9.50239); + const PlasmaSample sample_20(1, 9.24207, 8.7949); + const PlasmaSample sample_21(1, 8.39741, 8.04141); + const PlasmaSample sample_22(1, 7.74369, 7.36121); + const PlasmaSample sample_23(1, 7.18224, 6.78764); + const PlasmaSample sample_24(1, 6.67699, 6.3266); + const PlasmaSample sample_25(1, 6.23402, 5.93635); + const PlasmaSample sample_26(1, 5.8495, 5.593); + const PlasmaSample sample_27(1, 5.50858, 5.29071); + const PlasmaSample sample_28(1, 5.19509, 5.02458); + + TimeFrameDefinitions plasma_fdef = sample_plasma_data_in_frames.get_time_frame_definitions(); + + this_plasma_blood_plot.push_back(sample_17); + this_plasma_blood_plot.push_back(sample_18); + this_plasma_blood_plot.push_back(sample_19); + this_plasma_blood_plot.push_back(sample_20); + this_plasma_blood_plot.push_back(sample_21); + this_plasma_blood_plot.push_back(sample_22); + this_plasma_blood_plot.push_back(sample_23); + this_plasma_blood_plot.push_back(sample_24); + this_plasma_blood_plot.push_back(sample_25); + this_plasma_blood_plot.push_back(sample_26); + this_plasma_blood_plot.push_back(sample_27); + this_plasma_blood_plot.push_back(sample_28); testing_plasma_data.set_plot(this_plasma_blood_plot); testing_plasma_data.set_isotope_halflife(6586.2F); testing_plasma_data.decay_correct_PlasmaData(); PlasmaData::const_iterator cur_iter_1, cur_iter_2; - - for (cur_iter_1=sample_plasma_data_in_frames.begin()+16, cur_iter_2=testing_plasma_data.begin(); - cur_iter_1!=sample_plasma_data_in_frames.end() && cur_iter_2!=testing_plasma_data.end(); - ++cur_iter_1, ++cur_iter_2) - { - check_if_equal((*cur_iter_1).get_plasma_counts_in_kBq(),(*cur_iter_2).get_plasma_counts_in_kBq(),"Check Plasma when sampling PlasmaData into frames"); - check_if_equal((*cur_iter_1).get_blood_counts_in_kBq(),(*cur_iter_2).get_blood_counts_in_kBq(),"Check Blood when sampling PlasmaData into frames"); - } - assert(time_frame_def.get_num_frames()==plasma_fdef.get_num_frames()); - for (unsigned int frame_num=17 ; frame_num<=time_frame_def.get_num_frames() && frame_num<=plasma_fdef.get_num_frames(); ++frame_num) - { - check_if_equal(time_frame_def.get_start_time(frame_num),plasma_fdef.get_start_time(frame_num),"Check start time when sampling PlasmaData into frames"); - check_if_equal(time_frame_def.get_duration(frame_num),plasma_fdef.get_duration(frame_num),"Check duration when sampling PlasmaData into frames"); - check_if_equal(time_frame_def.get_end_time(frame_num),plasma_fdef.get_end_time(frame_num),"Check duration when sampling PlasmaData into frames"); - } - std::cerr << "\nTesting the creation of Model Matrix based on Plasma Data..." << std::endl; - PatlakPlot patlak_plot; - const unsigned int starting_frame=23; - patlak_plot._plasma_frame_data=sample_plasma_data_in_frames; - patlak_plot._frame_defs=time_frame_def; - patlak_plot._starting_frame=starting_frame; - patlak_plot._cal_factor=10.0F; - patlak_plot.set_up(); - ModelMatrix<2> stir_model_matrix=(patlak_plot.get_model_matrix()); - ModelMatrix<2> mathematica_model_matrix; - mathematica_model_matrix.read_from_file(this->add_directory("math_model_matrix.in")); - // stir_model_matrix.convert_to_total_frame_counts(time_frame_def); - Array<2,float> stir_model_array=stir_model_matrix.get_model_array(); - Array<2,float> mathematica_model_array=mathematica_model_matrix.get_model_array(); - - for(unsigned int frame_num=23;frame_num<=28;++frame_num) - { - check_if_equal(mathematica_model_array[1][frame_num]/patlak_plot._cal_factor,stir_model_array[1][frame_num],"Check _model_array-1st column in ModelMatrix"); - check_if_equal(mathematica_model_array[2][frame_num]/patlak_plot._cal_factor,stir_model_array[2][frame_num],"Check _model_array-2nd column in ModelMatrix"); - } - } + for (cur_iter_1 = sample_plasma_data_in_frames.begin() + 16, cur_iter_2 = testing_plasma_data.begin(); + cur_iter_1 != sample_plasma_data_in_frames.end() && cur_iter_2 != testing_plasma_data.end(); + ++cur_iter_1, ++cur_iter_2) { + check_if_equal((*cur_iter_1).get_plasma_counts_in_kBq(), (*cur_iter_2).get_plasma_counts_in_kBq(), + "Check Plasma when sampling PlasmaData into frames"); + check_if_equal((*cur_iter_1).get_blood_counts_in_kBq(), (*cur_iter_2).get_blood_counts_in_kBq(), + "Check Blood when sampling PlasmaData into frames"); + } + assert(time_frame_def.get_num_frames() == plasma_fdef.get_num_frames()); + for (unsigned int frame_num = 17; frame_num <= time_frame_def.get_num_frames() && frame_num <= plasma_fdef.get_num_frames(); + ++frame_num) { + check_if_equal(time_frame_def.get_start_time(frame_num), plasma_fdef.get_start_time(frame_num), + "Check start time when sampling PlasmaData into frames"); + check_if_equal(time_frame_def.get_duration(frame_num), plasma_fdef.get_duration(frame_num), + "Check duration when sampling PlasmaData into frames"); + check_if_equal(time_frame_def.get_end_time(frame_num), plasma_fdef.get_end_time(frame_num), + "Check duration when sampling PlasmaData into frames"); + } + std::cerr << "\nTesting the creation of Model Matrix based on Plasma Data..." << std::endl; + PatlakPlot patlak_plot; + const unsigned int starting_frame = 23; + patlak_plot._plasma_frame_data = sample_plasma_data_in_frames; + patlak_plot._frame_defs = time_frame_def; + patlak_plot._starting_frame = starting_frame; + patlak_plot._cal_factor = 10.0F; + patlak_plot.set_up(); + ModelMatrix<2> stir_model_matrix = (patlak_plot.get_model_matrix()); + ModelMatrix<2> mathematica_model_matrix; + mathematica_model_matrix.read_from_file(this->add_directory("math_model_matrix.in")); + // stir_model_matrix.convert_to_total_frame_counts(time_frame_def); + Array<2, float> stir_model_array = stir_model_matrix.get_model_array(); + Array<2, float> mathematica_model_array = mathematica_model_matrix.get_model_array(); + + for (unsigned int frame_num = 23; frame_num <= 28; ++frame_num) { + check_if_equal(mathematica_model_array[1][frame_num] / patlak_plot._cal_factor, stir_model_array[1][frame_num], + "Check _model_array-1st column in ModelMatrix"); + check_if_equal(mathematica_model_array[2][frame_num] / patlak_plot._cal_factor, stir_model_array[2][frame_num], + "Check _model_array-2nd column in ModelMatrix"); + } + } } - END_NAMESPACE_STIR USING_NAMESPACE_STIR -int main(int argc, char **argv) -{ - if (argc != 2) - { +int +main(int argc, char** argv) { + if (argc != 2) { std::cerr << "Usage : " << argv[0] << " \n"; return EXIT_FAILURE; } diff --git a/src/test/numerics/BSplines_timing.cxx b/src/test/numerics/BSplines_timing.cxx index 7ed4efcb66..005565d815 100644 --- a/src/test/numerics/BSplines_timing.cxx +++ b/src/test/numerics/BSplines_timing.cxx @@ -8,23 +8,23 @@ it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. - + This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - + See STIR/LICENSE.txt for details */ /*! -\file +\file \ingroup numerics_test \brief Allows executing timing tests for the stir::BSpline::BSplinesRegularGrid class \author Tim Borgeaud \author Kris Thielemans - -*/ + +*/ //#define MYLINEAR #include "stir/Array.h" @@ -37,7 +37,6 @@ #include "stir/numerics/BSplines.h" #include "stir/numerics/BSplinesRegularGrid.h" - #include #include #include @@ -48,12 +47,9 @@ using std::cerr; using namespace stir; using namespace BSpline; - - const int MAX_DIMS = 4; const int DEF_DIMS = 1; - const int MAX_NUM = 10000000; const int MIN_SIZE = 5; const int MAX_TOTAL_GRID_ELEMENTS = 10000000; @@ -61,115 +57,103 @@ const int MAX_TOTAL_GRID_ELEMENTS = 10000000; const int DEF_NUM = 1000; const int DEF_SIZE = 10; +void +usage(char* name) { -void usage(char *name) { - cerr << "A program to test the BSplines interpolation implementation\n\n"; - + cerr << "Usage: " << name << " [-n number of interpolations] [-s grid size]\n"; cerr << " [-i interpolator] [-d 2]\n\n"; - + cerr << " interpolator: near, linear, quadratic, cubic, omoms\n\n"; - } +template +double +interpolate(int num, int size, BSplineType bs_type, Array grid) { - -template -double interpolate(int num, int size, BSplineType bs_type, Array grid) { - // Create interpolator. - + struct timeval start; - gettimeofday(&start, NULL); + gettimeofday(&start, NULL); BSplinesRegularGrid bsi(grid, bs_type); - + struct timeval end; gettimeofday(&end, NULL); - - double total = (end.tv_sec - start.tv_sec) + ((end.tv_usec - start.tv_usec)/1000000.0); - + + double total = (end.tv_sec - start.tv_sec) + ((end.tv_usec - start.tv_usec) / 1000000.0); + cout << "Constructed " << dims << " dimensional, " << size; - for (int d = 1 ; d < dims ; d++ ) { + for (int d = 1; d < dims; d++) { cout << " x " << size; } cout << " element grid.\n"; cout << "Time: " << total << " seconds\n"; cout << "\n"; - BasicCoordinate pos; - - gettimeofday(&start, NULL); double foo = 0.0; // Now try some interpolation. - for (int i = 0 ; i < num ; i++ ) { + for (int i = 0; i < num; i++) { // Set position - for (int d = 1 ; d < dims + 1 ; d++ ) { - pos[d] = (static_cast(size - 1)/(RAND_MAX)) * random(); - //cout << pos[d] << "\n"; + for (int d = 1; d < dims + 1; d++) { + pos[d] = (static_cast(size - 1) / (RAND_MAX)) * random(); + // cout << pos[d] << "\n"; } // Get interpolation. foo += bsi(pos); - } - + } gettimeofday(&end, NULL); - total = (end.tv_sec - start.tv_sec) + ((end.tv_usec - start.tv_usec)/1000000.0); - + total = (end.tv_sec - start.tv_sec) + ((end.tv_usec - start.tv_usec) / 1000000.0); cout << "Interpolations: " << num << "\n"; cout << "Time: " << total << " seconds\n"; cout << "\n"; ////////////// -#ifdef MYLINEAR +#ifdef MYLINEAR gettimeofday(&start, NULL); foo = 0.0; // Now try some interpolation. - for (int i = 0 ; i < num ; i++ ) { + for (int i = 0; i < num; i++) { // Set position - for (int d = 1 ; d < dims + 1 ; d++ ) { - pos[d] = (static_cast(size - 1)/(RAND_MAX)) * random(); - //cout << pos[d] << "\n"; + for (int d = 1; d < dims + 1; d++) { + pos[d] = (static_cast(size - 1) / (RAND_MAX)) * random(); + // cout << pos[d] << "\n"; } // Get interpolation. - foo += pull_linear_interpolate(grid,pos); - } + foo += pull_linear_interpolate(grid, pos); + } gettimeofday(&end, NULL); - total = (end.tv_sec - start.tv_sec) + ((end.tv_usec - start.tv_usec)/1000000.0); - + total = (end.tv_sec - start.tv_sec) + ((end.tv_usec - start.tv_usec) / 1000000.0); cout << "linear Time: " << total << " seconds\n"; cout << "\n"; -#endif //MYLINEAR +#endif // MYLINEAR ////////////// cout << "foo: " << foo << "\n\n"; - return(total); + return (total); } - - - - - -int main(int argc, char **argv) { +int +main(int argc, char** argv) { int grid_size = DEF_SIZE; BSplineType bs_type = near_n; @@ -180,146 +164,133 @@ int main(int argc, char **argv) { // Options. while ((opt_ch = getopt(argc, argv, "n:s:i:d:h")) != -1) - - switch (opt_ch) { - case 'h': - usage(basename(argv[0])); - exit(EXIT_SUCCESS); - - case 'n': - // Number of interpolations. - num = atoi(optarg); - break; - - case 's': - // Grid_Size - grid_size = atoi(optarg); - break; - - case 'i': - // Interpolation. - - if ( strcasecmp(optarg, "near") == 0 ) { - bs_type = near_n; - } else if ( strcasecmp(optarg, "linear") == 0 ) { - bs_type = linear; - } else if ( strcasecmp(optarg, "quadratic") == 0 ) { - bs_type = quadratic; - } else if ( strcasecmp(optarg, "cubic") == 0 ) { - bs_type = cubic; - } else if ( strcasecmp(optarg, "quartic") == 0 ) { - bs_type = quartic; - } else if ( strcasecmp(optarg, "quintic") == 0 ) { - bs_type = quintic; - } else if ( strcasecmp(optarg, "omoms") == 0 ) { - bs_type = oMoms; - } - - break; - - case 'd': - dimensions = atoi(optarg); - break; - - case '?': - default: - usage(basename(argv[0])); - exit(0); - break; - } - + + switch (opt_ch) { + case 'h': + usage(basename(argv[0])); + exit(EXIT_SUCCESS); + + case 'n': + // Number of interpolations. + num = atoi(optarg); + break; + + case 's': + // Grid_Size + grid_size = atoi(optarg); + break; + + case 'i': + // Interpolation. + + if (strcasecmp(optarg, "near") == 0) { + bs_type = near_n; + } else if (strcasecmp(optarg, "linear") == 0) { + bs_type = linear; + } else if (strcasecmp(optarg, "quadratic") == 0) { + bs_type = quadratic; + } else if (strcasecmp(optarg, "cubic") == 0) { + bs_type = cubic; + } else if (strcasecmp(optarg, "quartic") == 0) { + bs_type = quartic; + } else if (strcasecmp(optarg, "quintic") == 0) { + bs_type = quintic; + } else if (strcasecmp(optarg, "omoms") == 0) { + bs_type = oMoms; + } + + break; + + case 'd': + dimensions = atoi(optarg); + break; + + case '?': + default: + usage(basename(argv[0])); + exit(0); + break; + } + argc -= optind; - - if ( argc > 0 ) { + + if (argc > 0) { usage(basename(argv[0])); exit(EXIT_FAILURE); } - - // Sanity check on grid dimensions and number of interpolations to carry out. - if ( num < 1 || num > MAX_NUM ) { + if (num < 1 || num > MAX_NUM) { num = DEF_NUM; cerr << "Number of interpolations out of range. Reset to: " << num << "\n"; } - - - if ( grid_size < MIN_SIZE ) { + if (grid_size < MIN_SIZE) { grid_size = DEF_SIZE; cerr << "Grid size too small. Reset to: " << grid_size << "\n"; } else { - + int total_elements = grid_size; - - for (int d = 1 ; d < dimensions ; d++) { + + for (int d = 1; d < dimensions; d++) { total_elements *= grid_size; } - - if ( total_elements > MAX_TOTAL_GRID_ELEMENTS ) { + + if (total_elements > MAX_TOTAL_GRID_ELEMENTS) { grid_size = DEF_SIZE; - cerr << "Grid size too large for " << dimensions << " dimensions. Reset to: " - << grid_size << "\n"; + cerr << "Grid size too large for " << dimensions << " dimensions. Reset to: " << grid_size << "\n"; } } - - - if ( dimensions < 1 || dimensions > MAX_DIMS ) { + if (dimensions < 1 || dimensions > MAX_DIMS) { dimensions = DEF_DIMS; cerr << "Dimensions out of range. Reset to: " << dimensions << "\n"; } - int size[4] = {0, 0, 0, 0}; - for (int d = 0 ; d < dimensions ; d++) { + for (int d = 0; d < dimensions; d++) { size[d] = grid_size; } - + // Test arrays - Double precision. Array<1, float> data_1(size[0]); IndexRange2D range2(size[1], size[1]); Array<2, float> data_2(range2); - + IndexRange3D range3(size[2], size[2], size[2]); Array<3, float> data_3(range3); IndexRange4D range4(size[3], size[3], size[3], size[3]); Array<4, float> data_4(range4); - - + // Fill test arrays. - for (int i = 0 ; i < size[0] ; i++ ) { + for (int i = 0; i < size[0]; i++) { data_1[i] = i; - - for (int j = 0 ; j < size[1] ; j++ ) { + + for (int j = 0; j < size[1]; j++) { data_2[i][j] = i * j; - - - for (int k = 0 ; k < size[2] ; k++) { + + for (int k = 0; k < size[2]; k++) { data_3[i][j][k] = i * j * k; - - for (int l = 0 ; l < size[3] ; l++) { + + for (int l = 0; l < size[3]; l++) { data_4[i][j][k][l] = i * j * k * l; } } } } - - - - switch ( dimensions ) { + switch (dimensions) { #ifndef MYLINEAR case 1: interpolate(num, size[0], bs_type, data_1); break; - + case 2: interpolate(num, size[1], bs_type, data_2); break; -#endif +#endif case 3: interpolate(num, size[2], bs_type, data_3); break; @@ -331,10 +302,8 @@ int main(int argc, char **argv) { default: // Do nothing. break; - } - - return(0); -} /* End of Main */ + return (0); +} /* End of Main */ diff --git a/src/test/numerics/test_BSplines.cxx b/src/test/numerics/test_BSplines.cxx index ab87bff4ae..cabcf525d3 100644 --- a/src/test/numerics/test_BSplines.cxx +++ b/src/test/numerics/test_BSplines.cxx @@ -17,13 +17,13 @@ See STIR/LICENSE.txt for details */ /*! - \file + \file \ingroup numerics_test - \brief tests the BSplines + \brief tests the BSplines \author Charalampos Tsoumpas \author Kris Thielemans -*/ +*/ #include "stir/RunTests.h" #include "stir/Array.h" #include "stir/IndexRange2D.h" @@ -45,410 +45,385 @@ using std::endl; #endif START_NAMESPACE_STIR namespace BSpline { - /*! - \ingroup test - \brief A simple class to test the BSplines function. - */ - class BSplines_Tests : public RunTests +/*! + \ingroup test + \brief A simple class to test the BSplines function. +*/ +class BSplines_Tests : public RunTests { +public: + BSplines_Tests() {} + void run_tests(); + +private: + template + bool check_at_sample_points(const std::vector& v, BSplines1DRegularGrid& interpolator, + const char* const message) { + std::vector out; + for (std::size_t i = 0, imax = v.size(); i < imax; ++i) + out.push_back(interpolator.BSplines(static_cast(i))); + std::cout << "IN: " << v << "OUT: " << out; + return check_if_equal(v, out, message); + } +}; +void +BSplines_Tests::run_tests() { + cerr << "Testing BSplines set of functions..." << endl; + set_tolerance(0.001); + typedef double elemT; + static std::vector pre_input_sample; + // pre_input_sample.push_back(-5); + pre_input_sample.push_back(-14); + pre_input_sample.push_back(8); + pre_input_sample.push_back(-1); + pre_input_sample.push_back(13); + pre_input_sample.push_back(-1); + pre_input_sample.push_back(-2); + pre_input_sample.push_back(11); + pre_input_sample.push_back(1); + pre_input_sample.push_back(-8); + pre_input_sample.push_back(6); + pre_input_sample.push_back(11); + pre_input_sample.push_back(-14); + pre_input_sample.push_back(6); + pre_input_sample.push_back(-3); + pre_input_sample.push_back(10); + pre_input_sample.push_back(1); + pre_input_sample.push_back(7); + pre_input_sample.push_back(-2); + pre_input_sample.push_back(-5); + pre_input_sample.push_back(-9); + pre_input_sample.push_back(-9); + pre_input_sample.push_back(6); + pre_input_sample.push_back(-5); + pre_input_sample.push_back(2); + pre_input_sample.push_back(-10); + pre_input_sample.push_back(6); + pre_input_sample.push_back(-3); + pre_input_sample.push_back(11); + pre_input_sample.push_back(11); + pre_input_sample.push_back(3); + { + cerr << "Testing BSplines_weights cubic function..." << endl; + std::vector BSplines_weights_STIR_vector_3, BSplines_weights_correct_vector_3; + BSplines1DRegularGrid BSplines1DRegularGridTests; + for (elemT i = 0.3; i <= 3; ++i) + BSplines_weights_STIR_vector_3.push_back(BSplines_weights(i, cubic)); + BSplines_weights_STIR_vector_3.push_back(BSplines_weights(0., cubic)); + + BSplines_weights_correct_vector_3.push_back(0.590167); // 1 + BSplines_weights_correct_vector_3.push_back(0.0571667); // 2 + BSplines_weights_correct_vector_3.push_back(0.); // 3 + BSplines_weights_correct_vector_3.push_back(0.666667); // 4 + + std::vector::iterator cur_iter_stir_out_3 = BSplines_weights_STIR_vector_3.begin(), + cur_iter_test_3 = BSplines_weights_correct_vector_3.begin(); + for (; cur_iter_stir_out_3 != BSplines_weights_STIR_vector_3.end() && + cur_iter_test_3 != BSplines_weights_correct_vector_3.end(); + ++cur_iter_stir_out_3, ++cur_iter_test_3) + check_if_equal(*cur_iter_stir_out_3, *cur_iter_test_3, "check cubic BSplines_weights implementation"); + } + { + cerr << "Testing BSplines_weights quadratic function..." << endl; + std::vector BSplines_weights_STIR_vector_2, BSplines_weights_correct_vector_2; + BSplines1DRegularGrid BSplines1DRegularGridTests; + for (elemT i = 0.3; i <= 3; ++i) + BSplines_weights_STIR_vector_2.push_back(BSplines_weights(i, quadratic)); + BSplines_weights_STIR_vector_2.push_back(BSplines_weights(0., quadratic)); + BSplines_weights_correct_vector_2.push_back(0.66); // 1 + BSplines_weights_correct_vector_2.push_back(0.02); // 2 + BSplines_weights_correct_vector_2.push_back(0.00); // 3 + BSplines_weights_correct_vector_2.push_back(0.75); // 4 + + std::vector::iterator cur_iter_stir_out_2 = BSplines_weights_STIR_vector_2.begin(), + cur_iter_test_2 = BSplines_weights_correct_vector_2.begin(); + for (; cur_iter_stir_out_2 != BSplines_weights_STIR_vector_2.end() && + cur_iter_test_2 != BSplines_weights_correct_vector_2.end(); + ++cur_iter_stir_out_2, ++cur_iter_test_2) + check_if_equal(*cur_iter_stir_out_2, *cur_iter_test_2, "check BSplines_weights quadratic implementation"); + } + { + cerr << "Testing BSplines_weights quintic function..." << endl; + std::vector BSplines_weights_STIR_vector_5, BSplines_weights_correct_vector_5; + BSplines1DRegularGrid BSplines1DRegularGridTests; + for (elemT i = 0.3; i <= 3; ++i) + BSplines_weights_STIR_vector_5.push_back(BSplines_weights(i, quintic)); + BSplines_weights_STIR_vector_5.push_back(BSplines_weights(0., quintic)); + BSplines_weights_correct_vector_5.push_back(0.506823); // 1 + BSplines_weights_correct_vector_5.push_back(0.109918); // 2 + BSplines_weights_correct_vector_5.push_back(0.00140058); // 3 + BSplines_weights_correct_vector_5.push_back(0.55); // 4 + std::vector::iterator cur_iter_stir_out = BSplines_weights_STIR_vector_5.begin(), + cur_iter_test = BSplines_weights_correct_vector_5.begin(); + for (; cur_iter_stir_out != BSplines_weights_STIR_vector_5.end() && cur_iter_test != BSplines_weights_correct_vector_5.end(); + ++cur_iter_stir_out, ++cur_iter_test) + check_if_equal(*cur_iter_stir_out, *cur_iter_test, "check BSplines_weights quintic implementation"); + } + { + BSplines1DRegularGrid BSplines1DRegularGridTests; + cerr << "Testing oMoms_weight function..." << endl; + std::vector oMoms_weight_STIR_vector, oMoms_weight_correct_vector; + oMoms_weight_STIR_vector.push_back(oMoms_weight(0.)); + for (elemT i = 0.3; i <= 3; ++i) + oMoms_weight_STIR_vector.push_back(oMoms_weight(i)); + oMoms_weight_correct_vector.push_back(0.619048); // 1 + oMoms_weight_correct_vector.push_back(0.563976); // 2 + oMoms_weight_correct_vector.push_back(0.0738333); // 3 + oMoms_weight_correct_vector.push_back(0.); // 4 + std::vector::iterator cur_iter_stir_out = oMoms_weight_STIR_vector.begin(), + cur_iter_test = oMoms_weight_correct_vector.begin(); + for (; cur_iter_stir_out != oMoms_weight_STIR_vector.end() && cur_iter_test != oMoms_weight_correct_vector.end(); + ++cur_iter_stir_out, ++cur_iter_test) + check_if_equal(*cur_iter_stir_out, *cur_iter_test, "check oMoms_weight implementation"); + } + { + cerr << "Testing BSplines_1st_der_weight function..." << endl; + BSplines1DRegularGrid BSplines1DRegularGridTests; + std::vector BSplines_1st_der_weight_STIR_vector, BSplines_1st_der_weight_correct_vector, + BSplines_1st_der_weight_est_vector; + + BSplines_1st_der_weight_STIR_vector.push_back(BSplines_1st_der_weight(0., cubic)); + + for (elemT i = 0.3; i <= 3; ++i) + BSplines_1st_der_weight_STIR_vector.push_back(BSplines_1st_der_weight(i, cubic)); + BSplines_1st_der_weight_correct_vector.push_back(0.); // 1 + BSplines_1st_der_weight_correct_vector.push_back(-0.465); // 2 + BSplines_1st_der_weight_correct_vector.push_back(-0.245); // 3 + BSplines_1st_der_weight_correct_vector.push_back(0.); // 4 + for (std::vector::iterator cur_iter_stir_out = BSplines_1st_der_weight_STIR_vector.begin(), + cur_iter_test = BSplines_1st_der_weight_correct_vector.begin(); + cur_iter_stir_out != BSplines_1st_der_weight_STIR_vector.end() && + cur_iter_test != BSplines_1st_der_weight_correct_vector.end(); + ++cur_iter_stir_out, ++cur_iter_test) + check_if_equal(*cur_iter_stir_out, *cur_iter_test, "check BSplines_1st_der_weight implementation"); + } + { + cerr << "Testing BSplines 1st Derivative analytically..." << endl; + std::vector BSplines_1st_der_STIR_vector, BSplines_1st_der_est_vector, new_input_sample(15, 1); + new_input_sample[5] = 7.; + new_input_sample[6] = 9.; + new_input_sample[10] = 91.; + BSplines1DRegularGrid BSplines1DRegularGridTest(new_input_sample, cubic); + const double epsilon = .0001; + for (double i = 0; i <= new_input_sample.size() + 3; ++i) { + BSplines_1st_der_STIR_vector.push_back(BSplines1DRegularGridTest.BSplines_1st_der(i)); + BSplines_1st_der_est_vector.push_back((BSplines1DRegularGridTest(i + epsilon) - BSplines1DRegularGridTest(i - epsilon)) / + (2 * epsilon)); + } + for (std::vector::iterator cur_iter_stir_out = BSplines_1st_der_est_vector.begin(), + cur_iter_test = BSplines_1st_der_STIR_vector.begin(); + cur_iter_test != BSplines_1st_der_STIR_vector.end(); ++cur_iter_stir_out, ++cur_iter_test) + check_if_equal(*cur_iter_stir_out, *cur_iter_test, "check cubic BSplines_1st_der_est or cubic BSplines implementation"); + } { - public: - BSplines_Tests() - {} - void run_tests(); - private: - template - bool check_at_sample_points(const std::vector& v, - BSplines1DRegularGrid& interpolator, - const char * const message) + cerr << "Testing BSplines: Nearest Neighbour values and constructor using a vector as input..." << endl; { - std::vector out; - for (std::size_t i=0, imax=v.size(); i(i))); - std::cout << "IN: " << v << "OUT: " << out; - return - check_if_equal(v, out, message); + const std::vector const_input_sample(10, 1); + BSplines1DRegularGrid BSplines1DRegularGridTest1(const_input_sample.begin(), const_input_sample.end(), + near_n); + check_at_sample_points(const_input_sample, BSplines1DRegularGridTest1, + "check BSplines implementation for nearest interpolation"); } - }; - void BSplines_Tests::run_tests() - { - cerr << "Testing BSplines set of functions..." << endl; - set_tolerance(0.001); - typedef double elemT; - static std::vector pre_input_sample; - //pre_input_sample.push_back(-5); - pre_input_sample.push_back(-14); pre_input_sample.push_back(8); pre_input_sample.push_back(-1); - pre_input_sample.push_back(13); pre_input_sample.push_back(-1); pre_input_sample.push_back(-2); - pre_input_sample.push_back(11); pre_input_sample.push_back(1); pre_input_sample.push_back(-8); - pre_input_sample.push_back(6); pre_input_sample.push_back(11); pre_input_sample.push_back(-14); - pre_input_sample.push_back(6); pre_input_sample.push_back(-3); pre_input_sample.push_back(10); - pre_input_sample.push_back(1); pre_input_sample.push_back(7); pre_input_sample.push_back(-2); - pre_input_sample.push_back(-5); pre_input_sample.push_back(-9); pre_input_sample.push_back(-9); - pre_input_sample.push_back(6); pre_input_sample.push_back(-5); pre_input_sample.push_back(2); - pre_input_sample.push_back(-10); pre_input_sample.push_back(6); pre_input_sample.push_back(-3); - pre_input_sample.push_back(11); pre_input_sample.push_back(11); pre_input_sample.push_back(3); { - cerr << "Testing BSplines_weights cubic function..." << endl; - std::vector BSplines_weights_STIR_vector_3, BSplines_weights_correct_vector_3; - BSplines1DRegularGrid BSplines1DRegularGridTests; - for(elemT i=0.3; i<=3 ;++i) - BSplines_weights_STIR_vector_3.push_back(BSplines_weights(i,cubic)); - BSplines_weights_STIR_vector_3.push_back(BSplines_weights(0.,cubic)); + std::vector linear_input; + for (elemT i = 0, imax = 10; i < imax; ++i) + linear_input.push_back(i); + BSplines1DRegularGrid BSplines1DRegularGridTesti(linear_input, near_n); - BSplines_weights_correct_vector_3.push_back(0.590167); //1 - BSplines_weights_correct_vector_3.push_back(0.0571667); //2 - BSplines_weights_correct_vector_3.push_back(0.); //3 - BSplines_weights_correct_vector_3.push_back(0.666667); //4 - - std::vector:: iterator cur_iter_stir_out_3= BSplines_weights_STIR_vector_3.begin() - , cur_iter_test_3= BSplines_weights_correct_vector_3.begin() ; - for (; cur_iter_stir_out_3!=BSplines_weights_STIR_vector_3.end() && - cur_iter_test_3!=BSplines_weights_correct_vector_3.end(); - ++cur_iter_stir_out_3, ++cur_iter_test_3) - check_if_equal(*cur_iter_stir_out_3, *cur_iter_test_3, - "check cubic BSplines_weights implementation"); + check_at_sample_points(linear_input, BSplines1DRegularGridTesti, "check BSplines implementation for nearest interpolation"); } { - cerr << "Testing BSplines_weights quadratic function..." << endl; - std::vector BSplines_weights_STIR_vector_2, BSplines_weights_correct_vector_2; - BSplines1DRegularGrid BSplines1DRegularGridTests; - for(elemT i=0.3; i<=3 ;++i) - BSplines_weights_STIR_vector_2.push_back(BSplines_weights(i,quadratic)); - BSplines_weights_STIR_vector_2.push_back(BSplines_weights(0.,quadratic)); - BSplines_weights_correct_vector_2.push_back(0.66); //1 - BSplines_weights_correct_vector_2.push_back(0.02); //2 - BSplines_weights_correct_vector_2.push_back(0.00); //3 - BSplines_weights_correct_vector_2.push_back(0.75); //4 - - std::vector:: iterator cur_iter_stir_out_2= BSplines_weights_STIR_vector_2.begin() - , cur_iter_test_2= BSplines_weights_correct_vector_2.begin() ; - for (; cur_iter_stir_out_2!=BSplines_weights_STIR_vector_2.end() && - cur_iter_test_2!=BSplines_weights_correct_vector_2.end(); - ++cur_iter_stir_out_2, ++cur_iter_test_2) - check_if_equal(*cur_iter_stir_out_2, *cur_iter_test_2, - "check BSplines_weights quadratic implementation"); + BSplines1DRegularGrid BSplines1DRegularGridTest(pre_input_sample, near_n); + check_at_sample_points(pre_input_sample, BSplines1DRegularGridTest, + "check BSplines implementation for nearest interpolation"); } + } + { + cerr << "Testing BSplines: Linear Interpolation values and constructor using a vector as input..." << endl; { - cerr << "Testing BSplines_weights quintic function..." << endl; - std::vector BSplines_weights_STIR_vector_5, BSplines_weights_correct_vector_5; - BSplines1DRegularGrid BSplines1DRegularGridTests; - for(elemT i=0.3; i<=3 ;++i) - BSplines_weights_STIR_vector_5.push_back(BSplines_weights(i,quintic)); - BSplines_weights_STIR_vector_5.push_back(BSplines_weights(0.,quintic)); - BSplines_weights_correct_vector_5.push_back(0.506823); //1 - BSplines_weights_correct_vector_5.push_back(0.109918); //2 - BSplines_weights_correct_vector_5.push_back(0.00140058); //3 - BSplines_weights_correct_vector_5.push_back(0.55); //4 - std::vector:: iterator cur_iter_stir_out= BSplines_weights_STIR_vector_5.begin() - , cur_iter_test= BSplines_weights_correct_vector_5.begin() ; - for (; cur_iter_stir_out!=BSplines_weights_STIR_vector_5.end() && - cur_iter_test!=BSplines_weights_correct_vector_5.end(); - ++cur_iter_stir_out, ++cur_iter_test) - check_if_equal(*cur_iter_stir_out, *cur_iter_test, - "check BSplines_weights quintic implementation"); + const std::vector const_input_sample(10, 1); + BSplines1DRegularGrid BSplines1DRegularGridTest1(const_input_sample.begin(), const_input_sample.end(), + linear); + check_at_sample_points(const_input_sample, BSplines1DRegularGridTest1, + "check BSplines implementation for linear interpolation"); } { - BSplines1DRegularGrid BSplines1DRegularGridTests; - cerr << "Testing oMoms_weight function..." << endl; - std::vector oMoms_weight_STIR_vector, oMoms_weight_correct_vector; - oMoms_weight_STIR_vector.push_back(oMoms_weight(0.)); - for(elemT i=0.3; i<=3 ;++i) - oMoms_weight_STIR_vector.push_back(oMoms_weight(i)); - oMoms_weight_correct_vector.push_back(0.619048);//1 - oMoms_weight_correct_vector.push_back(0.563976);//2 - oMoms_weight_correct_vector.push_back(0.0738333);//3 - oMoms_weight_correct_vector.push_back(0.); //4 - std::vector:: iterator cur_iter_stir_out= oMoms_weight_STIR_vector.begin() - , cur_iter_test= oMoms_weight_correct_vector.begin() ; - for (; cur_iter_stir_out!= oMoms_weight_STIR_vector.end() && - cur_iter_test!= oMoms_weight_correct_vector.end(); - ++cur_iter_stir_out, ++cur_iter_test) - check_if_equal(*cur_iter_stir_out, *cur_iter_test, - "check oMoms_weight implementation"); - } - { - cerr << "Testing BSplines_1st_der_weight function..." << endl; - BSplines1DRegularGrid BSplines1DRegularGridTests; - std::vector BSplines_1st_der_weight_STIR_vector, BSplines_1st_der_weight_correct_vector, - BSplines_1st_der_weight_est_vector; - - BSplines_1st_der_weight_STIR_vector.push_back(BSplines_1st_der_weight(0.,cubic)); + std::vector linear_input; + for (elemT i = 0, imax = 10; i < imax; ++i) + linear_input.push_back(i); + BSplines1DRegularGrid BSplines1DRegularGridTesti(linear_input, linear); - for(elemT i=0.3; i<=3 ;++i) - BSplines_1st_der_weight_STIR_vector.push_back(BSplines_1st_der_weight(i,cubic)); - BSplines_1st_der_weight_correct_vector.push_back(0.); //1 - BSplines_1st_der_weight_correct_vector.push_back(-0.465); //2 - BSplines_1st_der_weight_correct_vector.push_back(-0.245); //3 - BSplines_1st_der_weight_correct_vector.push_back(0.); //4 - for ( std::vector:: iterator cur_iter_stir_out= BSplines_1st_der_weight_STIR_vector.begin() - , cur_iter_test= BSplines_1st_der_weight_correct_vector.begin(); - cur_iter_stir_out!=BSplines_1st_der_weight_STIR_vector.end() && - cur_iter_test!=BSplines_1st_der_weight_correct_vector.end(); - ++cur_iter_stir_out, ++cur_iter_test) - check_if_equal(*cur_iter_stir_out, *cur_iter_test, - "check BSplines_1st_der_weight implementation"); + check_at_sample_points(linear_input, BSplines1DRegularGridTesti, "check BSplines implementation for linear interpolation"); } - { - cerr << "Testing BSplines 1st Derivative analytically..." << endl; - std::vector BSplines_1st_der_STIR_vector, - BSplines_1st_der_est_vector, new_input_sample(15,1); - new_input_sample[5]=7.; - new_input_sample[6]=9.; - new_input_sample[10]=91.; - BSplines1DRegularGrid BSplines1DRegularGridTest(new_input_sample,cubic); - const double epsilon = .0001; - for(double i=0; i<=new_input_sample.size()+3 ;++i) - { - BSplines_1st_der_STIR_vector.push_back(BSplines1DRegularGridTest.BSplines_1st_der(i)); - BSplines_1st_der_est_vector.push_back( - (BSplines1DRegularGridTest(i+epsilon) - - BSplines1DRegularGridTest(i-epsilon)) /(2*epsilon)); - } - for ( std::vector:: iterator cur_iter_stir_out= BSplines_1st_der_est_vector.begin() - , cur_iter_test=BSplines_1st_der_STIR_vector.begin(); - cur_iter_test!=BSplines_1st_der_STIR_vector.end(); - ++cur_iter_stir_out, ++cur_iter_test) - check_if_equal(*cur_iter_stir_out, *cur_iter_test, - "check cubic BSplines_1st_der_est or cubic BSplines implementation"); - } { - cerr << "Testing BSplines: Nearest Neighbour values and constructor using a vector as input..." << endl; - { - const std::vector const_input_sample(10,1); - BSplines1DRegularGrid BSplines1DRegularGridTest1( - const_input_sample.begin(), const_input_sample.end(), near_n); - check_at_sample_points(const_input_sample, BSplines1DRegularGridTest1, - "check BSplines implementation for nearest interpolation"); - } - { - std::vector linear_input; - for (elemT i=0, imax=10; i - BSplines1DRegularGridTesti(linear_input, near_n); - - check_at_sample_points(linear_input, BSplines1DRegularGridTesti, - "check BSplines implementation for nearest interpolation"); - } - { - BSplines1DRegularGrid - BSplines1DRegularGridTest(pre_input_sample, near_n); - check_at_sample_points(pre_input_sample, BSplines1DRegularGridTest, - "check BSplines implementation for nearest interpolation"); - } + BSplines1DRegularGrid BSplines1DRegularGridTest(pre_input_sample, linear); + check_at_sample_points(pre_input_sample, BSplines1DRegularGridTest, + "check BSplines implementation for linear interpolation"); } + } + { + cerr << "Testing BSplines: Quadratic Interpolation values and constructor using a vector as input..." << endl; { - cerr << "Testing BSplines: Linear Interpolation values and constructor using a vector as input..." << endl; - { - const std::vector const_input_sample(10,1); - BSplines1DRegularGrid BSplines1DRegularGridTest1( - const_input_sample.begin(), const_input_sample.end(), linear); - check_at_sample_points(const_input_sample, BSplines1DRegularGridTest1, - "check BSplines implementation for linear interpolation"); - } - { - std::vector linear_input; - for (elemT i=0, imax=10; i - BSplines1DRegularGridTesti(linear_input, linear); - - check_at_sample_points(linear_input, BSplines1DRegularGridTesti, - "check BSplines implementation for linear interpolation"); - } - { - BSplines1DRegularGrid - BSplines1DRegularGridTest(pre_input_sample, linear); - check_at_sample_points(pre_input_sample, BSplines1DRegularGridTest, - "check BSplines implementation for linear interpolation"); - } + const std::vector const_input_sample(10, 1); + BSplines1DRegularGrid BSplines1DRegularGridTest1(const_input_sample.begin(), const_input_sample.end(), + quadratic); + check_at_sample_points(const_input_sample, BSplines1DRegularGridTest1, + "check BSplines implementation for quadratic interpolation"); } { - cerr << "Testing BSplines: Quadratic Interpolation values and constructor using a vector as input..." << endl; - { - const std::vector const_input_sample(10,1); - BSplines1DRegularGrid BSplines1DRegularGridTest1( - const_input_sample.begin(), const_input_sample.end(), quadratic); - check_at_sample_points(const_input_sample, BSplines1DRegularGridTest1, - "check BSplines implementation for quadratic interpolation"); - } - { - std::vector linear_input; - for (elemT i=0, imax=10; i - BSplines1DRegularGridTesti(linear_input, quadratic); - - check_at_sample_points(linear_input, BSplines1DRegularGridTesti, - "check BSplines implementation for quadratic interpolation"); - } - { - BSplines1DRegularGrid - BSplines1DRegularGridTest(pre_input_sample, quadratic); - check_at_sample_points(pre_input_sample, BSplines1DRegularGridTest, - "check BSplines implementation for quadratic interpolation"); - } - } - { - cerr << "Testing BSplines: Cubic Interpolation values and constructor using a vector as input..." << endl; - { - const std::vector const_input_sample(10,1); - BSplines1DRegularGrid BSplines1DRegularGridTest1( - const_input_sample.begin(), const_input_sample.end(), cubic); - check_at_sample_points(const_input_sample, BSplines1DRegularGridTest1, - "check BSplines implementation for cubic interpolation"); - } - { - std::vector linear_input; - for (elemT i=0, imax=10; i - BSplines1DRegularGridTesti(linear_input, cubic); - - check_at_sample_points(linear_input, BSplines1DRegularGridTesti, - "check BSplines implementation for cubic interpolation"); - } - { - BSplines1DRegularGrid - BSplines1DRegularGridTest(pre_input_sample, cubic); - check_at_sample_points(pre_input_sample, BSplines1DRegularGridTest, - "check BSplines implementation for cubic interpolation"); - } - } + std::vector linear_input; + for (elemT i = 0, imax = 10; i < imax; ++i) + linear_input.push_back(i); + BSplines1DRegularGrid BSplines1DRegularGridTesti(linear_input, quadratic); + + check_at_sample_points(linear_input, BSplines1DRegularGridTesti, + "check BSplines implementation for quadratic interpolation"); + } { - cerr << "Testing BSplines: o-Moms Interpolation values and constructor using a vector as input..." << endl; - { - const std::vector const_input_sample(10,1); - BSplines1DRegularGrid BSplines1DRegularGridTest1( - const_input_sample.begin(), const_input_sample.end(), oMoms); - check_at_sample_points(const_input_sample, BSplines1DRegularGridTest1, - "check BSplines implementation for o-Moms interpolation"); - } - { - std::vector linear_input; - for (elemT i=0, imax=10; i - BSplines1DRegularGridTesti(linear_input, oMoms); - - check_at_sample_points(linear_input, BSplines1DRegularGridTesti, - "check BSplines implementation for o-Moms interpolation"); - } - } + BSplines1DRegularGrid BSplines1DRegularGridTest(pre_input_sample, quadratic); + check_at_sample_points(pre_input_sample, BSplines1DRegularGridTest, + "check BSplines implementation for quadratic interpolation"); + } + } + { + cerr << "Testing BSplines: Cubic Interpolation values and constructor using a vector as input..." << endl; { - cerr << "Testing BSplines Continuity..." << endl; - std::vector new_input_sample(12,1) , STIR_right_output_sample, - STIR_left_output_sample; - BSplines1DRegularGrid BSplines1DRegularGridTests( - new_input_sample.begin(), new_input_sample.end()); - // test if shifted copy of the B-spline functions add to 1 - for (double inc=0; inc<1; inc+=.1) - check_if_equal( - BSplines_weights(+inc,cubic)+ - BSplines_weights(+inc+1,cubic)+ - BSplines_weights(+inc+2,cubic)+ - BSplines_weights(+inc-1,cubic)+ - BSplines_weights(+inc-2,cubic), - 1., "test on cubic B-spline function"); - std::cerr << '\n'; - const elemT epsilon = 0.01; - for (elemT i=1, imax=10; i:: iterator cur_iter_stir_left_out= STIR_left_output_sample.begin(), - cur_iter_stir_right_out= STIR_right_output_sample.begin(); - for (; cur_iter_stir_left_out!=STIR_left_output_sample.end() && - cur_iter_stir_right_out!=STIR_right_output_sample.end(); - ++cur_iter_stir_left_out, ++cur_iter_stir_right_out) - check_if_equal(*cur_iter_stir_left_out, *cur_iter_stir_right_out, - "check BSplines implementation"); + const std::vector const_input_sample(10, 1); + BSplines1DRegularGrid BSplines1DRegularGridTest1(const_input_sample.begin(), const_input_sample.end(), cubic); + check_at_sample_points(const_input_sample, BSplines1DRegularGridTest1, + "check BSplines implementation for cubic interpolation"); } { - cerr << "Testing BSplines 1st Derivative Continuity..." << endl; - std::vector new_input_sample, STIR_right_output_sample, STIR_left_output_sample; + std::vector linear_input; + for (elemT i = 0, imax = 10; i < imax; ++i) + linear_input.push_back(i); + BSplines1DRegularGrid BSplines1DRegularGridTesti(linear_input, cubic); - for (elemT i=0, imax=14; i BSplines1DRegularGridTests( - new_input_sample.begin(), new_input_sample.end()); - - std::cerr << '\n'; - const elemT epsilon = 0.0001; - for (elemT i=1, imax=13; i:: iterator cur_iter_stir_left_out= STIR_left_output_sample.begin(), - cur_iter_stir_right_out= STIR_right_output_sample.begin(); - for (; cur_iter_stir_left_out!=STIR_left_output_sample.end() && - cur_iter_stir_right_out!=STIR_right_output_sample.end(); - ++cur_iter_stir_left_out, ++cur_iter_stir_right_out) - check_if_equal(*cur_iter_stir_left_out, *cur_iter_stir_right_out, - "check BSplines implementation"); + check_at_sample_points(linear_input, BSplines1DRegularGridTesti, "check BSplines implementation for cubic interpolation"); } { - cerr << "Testing BSplines values giving a vector as input..." << endl; - std::vector input_sample(10,1), output_sample_position, STIR_output_sample; - - BSplines1DRegularGrid BSplines1DRegularGridTests(input_sample); - for (elemT i=0, imax=23; i BSplines1DRegularGridTest(pre_input_sample, cubic); + check_at_sample_points(pre_input_sample, BSplines1DRegularGridTest, + "check BSplines implementation for cubic interpolation"); + } + } + { + cerr << "Testing BSplines: o-Moms Interpolation values and constructor using a vector as input..." << endl; + { + const std::vector const_input_sample(10, 1); + BSplines1DRegularGrid BSplines1DRegularGridTest1(const_input_sample.begin(), const_input_sample.end(), oMoms); + check_at_sample_points(const_input_sample, BSplines1DRegularGridTest1, + "check BSplines implementation for o-Moms interpolation"); + } + { + std::vector linear_input; + for (elemT i = 0, imax = 10; i < imax; ++i) + linear_input.push_back(i); + BSplines1DRegularGrid BSplines1DRegularGridTesti(linear_input, oMoms); - std::vector:: iterator cur_iter_stir_out = STIR_output_sample.begin(); + check_at_sample_points(linear_input, BSplines1DRegularGridTesti, "check BSplines implementation for o-Moms interpolation"); + } + } + { + cerr << "Testing BSplines Continuity..." << endl; + std::vector new_input_sample(12, 1), STIR_right_output_sample, STIR_left_output_sample; + BSplines1DRegularGrid BSplines1DRegularGridTests(new_input_sample.begin(), new_input_sample.end()); + // test if shifted copy of the B-spline functions add to 1 + for (double inc = 0; inc < 1; inc += .1) + check_if_equal(BSplines_weights(+inc, cubic) + BSplines_weights(+inc + 1, cubic) + BSplines_weights(+inc + 2, cubic) + + BSplines_weights(+inc - 1, cubic) + BSplines_weights(+inc - 2, cubic), + 1., "test on cubic B-spline function"); + std::cerr << '\n'; + const elemT epsilon = 0.01; + for (elemT i = 1, imax = 10; i < imax; ++i) { + STIR_left_output_sample.push_back(BSplines1DRegularGridTests(i - epsilon)); + STIR_right_output_sample.push_back(BSplines1DRegularGridTests(i + epsilon)); + } + std::vector::iterator cur_iter_stir_left_out = STIR_left_output_sample.begin(), + cur_iter_stir_right_out = STIR_right_output_sample.begin(); + for (; cur_iter_stir_left_out != STIR_left_output_sample.end() && cur_iter_stir_right_out != STIR_right_output_sample.end(); + ++cur_iter_stir_left_out, ++cur_iter_stir_right_out) + check_if_equal(*cur_iter_stir_left_out, *cur_iter_stir_right_out, "check BSplines implementation"); + } + { + cerr << "Testing BSplines 1st Derivative Continuity..." << endl; + std::vector new_input_sample, STIR_right_output_sample, STIR_left_output_sample; + + for (elemT i = 0, imax = 14; i < imax; ++i) + new_input_sample.push_back(i); + + BSplines1DRegularGrid BSplines1DRegularGridTests(new_input_sample.begin(), new_input_sample.end()); + + std::cerr << '\n'; + const elemT epsilon = 0.0001; + for (elemT i = 1, imax = 13; i < imax; ++i) { + STIR_left_output_sample.push_back(BSplines1DRegularGridTests.BSplines_1st_der(i - epsilon)); + STIR_right_output_sample.push_back(BSplines1DRegularGridTests.BSplines_1st_der(i + epsilon)); + } + std::vector::iterator cur_iter_stir_left_out = STIR_left_output_sample.begin(), + cur_iter_stir_right_out = STIR_right_output_sample.begin(); + for (; cur_iter_stir_left_out != STIR_left_output_sample.end() && cur_iter_stir_right_out != STIR_right_output_sample.end(); + ++cur_iter_stir_left_out, ++cur_iter_stir_right_out) + check_if_equal(*cur_iter_stir_left_out, *cur_iter_stir_right_out, "check BSplines implementation"); + } + { + cerr << "Testing BSplines values giving a vector as input..." << endl; + std::vector input_sample(10, 1), output_sample_position, STIR_output_sample; + + BSplines1DRegularGrid BSplines1DRegularGridTests(input_sample); + for (elemT i = 0, imax = 23; i < imax; ++i) + output_sample_position.push_back((i + 0.5) / 2.4); - for (; cur_iter_stir_out!=STIR_output_sample.end(); ++cur_iter_stir_out) - check_if_equal(*cur_iter_stir_out, (elemT)1, - "check BSplines implementation"); - } - /* { - cerr << "Testing interpolation results. Look at the text files!" << endl; - std::vector exp_input_sample; - std::vector STIR_output_sample; - int imax =30; - for(int i=0; i:: iterator cur_iter_stir_out= STIR_output_sample.begin(); - BSplines1DRegularGrid BSplines1DRegularGridTest( - pre_input_sample.begin(), pre_input_sample.end(), linear); - string output_string; - output_string += "gaussian"; //"noisy_inter" ; - ofstream out(output_string.c_str()); //output file // - - if(!out) - { - cout << "Cannot open text file.\n" ; - //return EXIT_FAILURE; - } - - - // STIR_output_sample.push_back(BSplines1DRegularGridTest(i)); - // cout << STIR_output_sample; - out << BSplines1DRegularGridTest.spline_type <<". B-Spline \n"; - out << "Position " << "\t" << " Value \n" ; - for (elemT i=0; i::iterator cur_iter_stir_out = STIR_output_sample.begin(); + + for (; cur_iter_stir_out != STIR_output_sample.end(); ++cur_iter_stir_out) + check_if_equal(*cur_iter_stir_out, (elemT)1, "check BSplines implementation"); } + /* { + cerr << "Testing interpolation results. Look at the text files!" << endl; + std::vector exp_input_sample; + std::vector STIR_output_sample; + int imax =30; + for(int i=0; i:: iterator cur_iter_stir_out= STIR_output_sample.begin(); + BSplines1DRegularGrid BSplines1DRegularGridTest( + pre_input_sample.begin(), pre_input_sample.end(), linear); + string output_string; + output_string += "gaussian"; //"noisy_inter" ; + ofstream out(output_string.c_str()); //output file // + + if(!out) + { + cout << "Cannot open text file.\n" ; + //return EXIT_FAILURE; + } + + + // STIR_output_sample.push_back(BSplines1DRegularGridTest(i)); + // cout << STIR_output_sample; + out << BSplines1DRegularGridTest.spline_type <<". B-Spline \n"; + out << "Position " << "\t" << " Value \n" ; + for (elemT i=0; i - bool check_at_sample_points(const Array<2,elemT>& v, - const BSplinesRegularGrid<2, elemT, elemT>& interpolator, - const char * const message) - { - Array<2,elemT> out(v.get_index_range()); - BasicCoordinate<2, elemT> relative_positions; - for (int j=out.get_min_index() ; j<=out.get_max_index() ; ++j) - for (int i=out[j].get_min_index() ; i<=out[j].get_max_index() ; ++i) - { - relative_positions[1]=j; - relative_positions[2]=i; - out[j][i]=interpolator(relative_positions); - } - // cout << "IN: \n" << v << "OUT: \n" << out; - return - check_if_equal(v, out, message); - } - template - bool check_near_sample_points(const Array<2,elemT>& v, - const BSplinesRegularGrid<2, elemT, elemT>& interpolator, - const BasicCoordinate<2, elemT>& epsilon, - const char * const message) - { - Array<2,elemT> out_near(v.get_index_range()); - BasicCoordinate<2, elemT> relative_positions; - for (int j=out_near.get_min_index() ; j<=out_near.get_max_index() ; ++j) - for (int i=out_near[j].get_min_index() ; i<=out_near[j].get_max_index() ; ++i) - { - relative_positions[1]=j+epsilon[1]; - relative_positions[2]=i+epsilon[2]; - out_near[j][i]=interpolator(relative_positions); - } - // cout << "IN: \n" << v << "out_near: \n" << out_near; - return - check_if_equal(v, out_near, message); - } - - template - bool check_at_half_way(const Array<2,elemT>& v, - const Array<2,elemT>& v_at_half, - const BSplinesRegularGrid<2, elemT, elemT>& interpolator, - const char * const message) - { - Array<2,elemT> out_at_half(v_at_half.get_index_range()), dv(v_at_half.get_index_range()); - BasicCoordinate<2, elemT> relative_positions; - for (int j=out_at_half.get_min_index() ; j<=out_at_half.get_max_index() ; ++j) - for (int i=out_at_half[j].get_min_index() ; i<=out_at_half[j].get_max_index() ; ++i) - { - relative_positions[1]=j+0.5; - relative_positions[2]=i+0.5; - out_at_half[j][i]=interpolator(relative_positions); - } - dv = (out_at_half - v_at_half)/v_at_half.sum(); - dv *= dv; - - // cout << "Checking BSplines implementation at half way:\n" ; - // cout << "IN: \n" << v_at_half << "OUT: \n" << out_at_half; - std::cout << "The mean deviation from the correct value is: " - << sqrt(dv.sum()/dv.size_all()) << endl; - return - check_if_zero(sqrt(dv.sum()/dv.size_all()), message); - } +/*! + \ingroup numerics_test + \brief A simple class to BSplinesRegularGrid for 2D arrays. +*/ +class BSplinesRegularGrid_Tests : public RunTests { +public: + BSplinesRegularGrid_Tests() {} + void run_tests(); - template - bool check_at_half_way(const Array<2,elemT>& v, - const BSplinesRegularGrid<2, elemT, elemT>& interpolator) - { - Array<2,elemT> out(v.get_index_range()); - BasicCoordinate<2, elemT> relative_positions; - for (int j=out.get_min_index() ; j<=out.get_max_index() ; ++j) - for (int i=out[j].get_min_index() ; i<=out[j].get_max_index() ; ++i) - { - relative_positions[1]=j+0.5; - relative_positions[2]=i+0.5; - out[j][i]=interpolator(relative_positions); - } - std::cout << "BSplines implementation at half way:\n" ; - std::cout << "IN: \n" << v << "OUT: \n" << out; - return true; - } +private: + template + bool check_at_sample_points(const Array<2, elemT>& v, const BSplinesRegularGrid<2, elemT, elemT>& interpolator, + const char* const message) { + Array<2, elemT> out(v.get_index_range()); + BasicCoordinate<2, elemT> relative_positions; + for (int j = out.get_min_index(); j <= out.get_max_index(); ++j) + for (int i = out[j].get_min_index(); i <= out[j].get_max_index(); ++i) { + relative_positions[1] = j; + relative_positions[2] = i; + out[j][i] = interpolator(relative_positions); + } + // cout << "IN: \n" << v << "OUT: \n" << out; + return check_if_equal(v, out, message); + } + template + bool check_near_sample_points(const Array<2, elemT>& v, const BSplinesRegularGrid<2, elemT, elemT>& interpolator, + const BasicCoordinate<2, elemT>& epsilon, const char* const message) { + Array<2, elemT> out_near(v.get_index_range()); + BasicCoordinate<2, elemT> relative_positions; + for (int j = out_near.get_min_index(); j <= out_near.get_max_index(); ++j) + for (int i = out_near[j].get_min_index(); i <= out_near[j].get_max_index(); ++i) { + relative_positions[1] = j + epsilon[1]; + relative_positions[2] = i + epsilon[2]; + out_near[j][i] = interpolator(relative_positions); + } + // cout << "IN: \n" << v << "out_near: \n" << out_near; + return check_if_equal(v, out_near, message); + } - - template - bool check_coefficients(const Array<2,elemT>& v, - const BSplinesRegularGrid<2, elemT, elemT>& interpolator, - const char * const message) - { - const Array<2,elemT> out=interpolator.get_coefficients(); - // cout << "IN: \n" << v << "Coefficients: \n" << out; - return - check_if_equal(v, out, message); - } + template + bool check_at_half_way(const Array<2, elemT>& v, const Array<2, elemT>& v_at_half, + const BSplinesRegularGrid<2, elemT, elemT>& interpolator, const char* const message) { + Array<2, elemT> out_at_half(v_at_half.get_index_range()), dv(v_at_half.get_index_range()); + BasicCoordinate<2, elemT> relative_positions; + for (int j = out_at_half.get_min_index(); j <= out_at_half.get_max_index(); ++j) + for (int i = out_at_half[j].get_min_index(); i <= out_at_half[j].get_max_index(); ++i) { + relative_positions[1] = j + 0.5; + relative_positions[2] = i + 0.5; + out_at_half[j][i] = interpolator(relative_positions); + } + dv = (out_at_half - v_at_half) / v_at_half.sum(); + dv *= dv; + + // cout << "Checking BSplines implementation at half way:\n" ; + // cout << "IN: \n" << v_at_half << "OUT: \n" << out_at_half; + std::cout << "The mean deviation from the correct value is: " << sqrt(dv.sum() / dv.size_all()) << endl; + return check_if_zero(sqrt(dv.sum() / dv.size_all()), message); + } - template - bool check_gradient(const BSplinesRegularGrid& interpolator, - const BasicCoordinate& p, - const char * const message) - { - const elemT epsilon = static_cast(1.E-4); - BasicCoordinate gradient = - interpolator.gradient(p); - const elemT value = - interpolator(p); - BasicCoordinate numerical_gradient; - BasicCoordinate multidim_epsilon; - for (int d=1; d<=num_dimensions; ++d) - { - assign(multidim_epsilon,0); - multidim_epsilon[d]=epsilon; - numerical_gradient[d] = - (interpolator( p+multidim_epsilon) - value) / epsilon; - } - return - this->check_if_equal(gradient, numerical_gradient, message); + template + bool check_at_half_way(const Array<2, elemT>& v, const BSplinesRegularGrid<2, elemT, elemT>& interpolator) { + Array<2, elemT> out(v.get_index_range()); + BasicCoordinate<2, elemT> relative_positions; + for (int j = out.get_min_index(); j <= out.get_max_index(); ++j) + for (int i = out[j].get_min_index(); i <= out[j].get_max_index(); ++i) { + relative_positions[1] = j + 0.5; + relative_positions[2] = i + 0.5; + out[j][i] = interpolator(relative_positions); + } + std::cout << "BSplines implementation at half way:\n"; + std::cout << "IN: \n" << v << "OUT: \n" << out; + return true; + } + + template + bool check_coefficients(const Array<2, elemT>& v, const BSplinesRegularGrid<2, elemT, elemT>& interpolator, + const char* const message) { + const Array<2, elemT> out = interpolator.get_coefficients(); + // cout << "IN: \n" << v << "Coefficients: \n" << out; + return check_if_equal(v, out, message); + } + + template + bool check_gradient(const BSplinesRegularGrid& interpolator, + const BasicCoordinate& p, const char* const message) { + const elemT epsilon = static_cast(1.E-4); + BasicCoordinate gradient = interpolator.gradient(p); + const elemT value = interpolator(p); + BasicCoordinate numerical_gradient; + BasicCoordinate multidim_epsilon; + for (int d = 1; d <= num_dimensions; ++d) { + assign(multidim_epsilon, 0); + multidim_epsilon[d] = epsilon; + numerical_gradient[d] = (interpolator(p + multidim_epsilon) - value) / epsilon; } + return this->check_if_equal(gradient, numerical_gradient, message); + } + template + bool check_continuity_of_gradient(const BSplinesRegularGrid& interpolator, + const BasicCoordinate& p, const char* const message) { + // TODO + return true; + } +}; - template - bool check_continuity_of_gradient(const BSplinesRegularGrid& interpolator, - const BasicCoordinate& p, - const char * const message) - { - // TODO - return true; +void +BSplinesRegularGrid_Tests::run_tests() { + cerr << "\nTesting BSplinesRegularGrid class..." << endl; + double test_tolerance = 0.001; + set_tolerance(test_tolerance); + typedef double elemT; + BasicCoordinate<2, elemT> epsilon_1; + epsilon_1[1] = -test_tolerance / 50; + epsilon_1[2] = test_tolerance / 50; + BasicCoordinate<2, elemT> epsilon_2; + epsilon_2[1] = -test_tolerance / 50; + epsilon_2[2] = -test_tolerance / 50; + BasicCoordinate<2, elemT> epsilon_3; + epsilon_3[1] = test_tolerance / 50; + epsilon_3[2] = -test_tolerance / 50; + BasicCoordinate<2, elemT> epsilon_4; + epsilon_4[1] = test_tolerance / 50; + epsilon_4[2] = test_tolerance / 50; + + Array<1, elemT> const_1D = make_1d_array(1., 1., 1., 1., 1., 1.); + Array<1, elemT> linear_1D = make_1d_array(1., 2., 3., 4., 5., 6., 7., 8., 9., 10.); + Array<1, elemT> random_1D_1 = make_1d_array(-14., 8., -1., 13., -1., -2., 11., 1., -8.); + Array<1, elemT> random_1D_2 = make_1d_array(6., 11., -14., 6., -3., 10., 1., 7., -2.); + Array<1, elemT> random_1D_3 = make_1d_array(-5., -9., -9., 6., -5., 2., -10., 6., -3.); + Array<1, elemT> random_1D_4 = make_1d_array(11., 8., -1., -1., 12., 11., 11., 3., 1.); + Array<1, elemT> random_1D_5 = make_1d_array(8., -1., 13., -1., -2., 11., 1., -8., -14.); + Array<1, elemT> random_1D_6 = make_1d_array(-14., 8., -1., 13., -1., -2., 1., -8., 11.); + Array<1, elemT> random_1D_7 = make_1d_array(13., -1., -2., -14., 11., 1., -8., 8., -1.); + + Array<2, elemT> const_input_sample = make_array(const_1D, const_1D, const_1D, const_1D, const_1D, const_1D); + Array<2, elemT> linear_const_input_sample = make_array(linear_1D, linear_1D, linear_1D, linear_1D, linear_1D, linear_1D); + Array<2, elemT> random_input_sample = + make_array(random_1D_1, random_1D_2, random_1D_3, random_1D_4, random_1D_5, random_1D_6, random_1D_7); + + const int jmax = 30, imax = 30; + BasicCoordinate<2, int> gauss_min, gauss_max, gauss_min_less, gauss_max_less; + gauss_min[1] = 0; + gauss_max[1] = jmax; + gauss_min[2] = 0; + gauss_max[2] = imax; + gauss_min_less[1] = 0; + gauss_max_less[1] = jmax - 1; + gauss_min_less[2] = 0; + gauss_max_less[2] = imax - 1; + + IndexRange<2> gaussian_input_sample_range(gauss_min, gauss_max); + Array<2, elemT> gaussian_input_sample(gaussian_input_sample_range); + + IndexRange<2> gaussian_input_sample_range_less(gauss_min_less, gauss_max_less); + Array<2, elemT> gaussian_check_sample(gaussian_input_sample_range_less); + + for (int j = gauss_min[1]; j <= gauss_max[1]; ++j) + for (int i = gauss_min[2]; i <= gauss_max[2]; ++i) { + gaussian_input_sample[j][i] = exp(-((static_cast(i) - 10.) * (static_cast(i) - 10.) + + (static_cast(j) - 10.) * (static_cast(j) - 10.)) / + 400.); + if (j > gauss_max_less[1] || i > gauss_max_less[2]) + continue; + else + gaussian_check_sample[j][i] = exp(-((static_cast(i) + 0.5 - 10.) * (static_cast(i) + 0.5 - 10.) + + (static_cast(j) + 0.5 - 10.) * (static_cast(j) + 0.5 - 10.)) / + 400.); } - }; - - - void BSplinesRegularGrid_Tests::run_tests() - { - cerr << "\nTesting BSplinesRegularGrid class..." << endl; - double test_tolerance = 0.001; - set_tolerance(test_tolerance); - typedef double elemT; - BasicCoordinate<2, elemT> epsilon_1; epsilon_1[1]=-test_tolerance/50; epsilon_1[2]=test_tolerance/50; - BasicCoordinate<2, elemT> epsilon_2; epsilon_2[1]=-test_tolerance/50; epsilon_2[2]=-test_tolerance/50; - BasicCoordinate<2, elemT> epsilon_3; epsilon_3[1]=test_tolerance/50; epsilon_3[2]=-test_tolerance/50; - BasicCoordinate<2, elemT> epsilon_4; epsilon_4[1]=test_tolerance/50; epsilon_4[2]=test_tolerance/50; - - Array<1,elemT> const_1D = make_1d_array(1., 1., 1., 1., 1., 1.); - Array<1,elemT> linear_1D = make_1d_array(1., 2., 3., 4., 5., 6.,7.,8.,9.,10.); - Array<1,elemT> random_1D_1 = make_1d_array(-14., 8., -1., 13., -1., -2., 11., 1., -8.); - Array<1,elemT> random_1D_2 = make_1d_array(6., 11., -14., 6., -3., 10., 1., 7., -2.); - Array<1,elemT> random_1D_3 = make_1d_array(-5., -9., -9., 6., -5., 2., -10., 6., -3.); - Array<1,elemT> random_1D_4 = make_1d_array(11., 8., -1., -1., 12., 11., 11., 3., 1.); - Array<1,elemT> random_1D_5 = make_1d_array(8., -1., 13., -1., -2., 11., 1., -8., -14.); - Array<1,elemT> random_1D_6 = make_1d_array(-14., 8., -1., 13., -1., -2., 1., -8., 11.); - Array<1,elemT> random_1D_7 = make_1d_array(13., -1., -2., -14., 11., 1., -8., 8., -1.); - - Array<2,elemT> const_input_sample = make_array(const_1D, - const_1D, - const_1D, - const_1D, - const_1D, - const_1D); - Array<2,elemT> linear_const_input_sample = make_array(linear_1D, - linear_1D, - linear_1D, - linear_1D, - linear_1D, - linear_1D); - Array<2,elemT> random_input_sample = make_array(random_1D_1, - random_1D_2, - random_1D_3, - random_1D_4, - random_1D_5, - random_1D_6, - random_1D_7); - - - - const int jmax=30, imax=30; - BasicCoordinate<2,int> gauss_min, gauss_max, gauss_min_less, gauss_max_less ; - gauss_min[1]=0; gauss_max[1]=jmax; gauss_min[2]=0; gauss_max[2]=imax; - gauss_min_less[1]=0; gauss_max_less[1]=jmax-1; gauss_min_less[2]=0; gauss_max_less[2]=imax-1; - - IndexRange<2> gaussian_input_sample_range(gauss_min,gauss_max); - Array<2,elemT> gaussian_input_sample(gaussian_input_sample_range); - - IndexRange<2> gaussian_input_sample_range_less(gauss_min_less,gauss_max_less); - Array<2,elemT> gaussian_check_sample(gaussian_input_sample_range_less); - - for (int j=gauss_min[1] ; j<=gauss_max[1] ; ++j) - for (int i=gauss_min[2] ; i<=gauss_max[2] ; ++i) - { - gaussian_input_sample[j][i]= - exp(-((static_cast(i)-10.)*(static_cast(i)-10.)+ - (static_cast(j)-10.)*(static_cast(j)-10.))/400.); - if (j>gauss_max_less[1] || i>gauss_max_less[2]) - continue; - else - gaussian_check_sample[j][i]= - exp(-((static_cast(i)+0.5-10.)*(static_cast(i)+0.5-10.)+ - (static_cast(j)+0.5-10.)*(static_cast(j)+0.5-10.))/400.); - - } + { + BSplinesRegularGrid<1, elemT> BSplinesRegularGridTest_const(const_1D, cubic); + BSplinesRegularGrid<1, elemT> BSplinesRegularGridTest_linear(linear_1D, cubic); + BSplinesRegularGrid<1, elemT> BSplinesRegularGridTest_random(random_1D_1, cubic); + BSplinesRegularGrid<1, elemT> BSplinesRegularGridTest_gaussian(gaussian_input_sample[14], cubic); + //////// gradient { - BSplinesRegularGrid<1, elemT> BSplinesRegularGridTest_const(const_1D, cubic); - BSplinesRegularGrid<1, elemT> BSplinesRegularGridTest_linear(linear_1D, cubic); - BSplinesRegularGrid<1, elemT> BSplinesRegularGridTest_random(random_1D_1, cubic); - BSplinesRegularGrid<1, elemT> BSplinesRegularGridTest_gaussian(gaussian_input_sample[14], cubic); - //////// gradient - { - BasicCoordinate<1,pos_type> p; - p[1] = 8.4; check_gradient(BSplinesRegularGridTest_gaussian, p, "gradient of cubic B-spline with Gaussian input"); - p[1] = 4.4; check_gradient(BSplinesRegularGridTest_gaussian, p, "gradient of cubic B-spline with Gaussian input"); - p[1] = 3.4; check_gradient(BSplinesRegularGridTest_linear, p, "gradient of cubic B-spline with linear input"); - p[1] = 2.4; check_gradient(BSplinesRegularGridTest_const, p, "gradient of cubic B-spline with const input"); - } - + BasicCoordinate<1, pos_type> p; + p[1] = 8.4; + check_gradient(BSplinesRegularGridTest_gaussian, p, "gradient of cubic B-spline with Gaussian input"); + p[1] = 4.4; + check_gradient(BSplinesRegularGridTest_gaussian, p, "gradient of cubic B-spline with Gaussian input"); + p[1] = 3.4; + check_gradient(BSplinesRegularGridTest_linear, p, "gradient of cubic B-spline with linear input"); + p[1] = 2.4; + check_gradient(BSplinesRegularGridTest_const, p, "gradient of cubic B-spline with const input"); } + } + { + cerr << "\nTesting BSplinesRegularGrid: Nearest Neighbour values and constructor using a 2D array as input..." << endl; { - cerr << "\nTesting BSplinesRegularGrid: Nearest Neighbour values and constructor using a 2D array as input..." << endl; - { - BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_const( - const_input_sample, near_n); - BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_linear_const( - linear_const_input_sample, near_n); - BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_random( - random_input_sample, near_n); - - check_coefficients(const_input_sample, BSplinesRegularGridTest_const, - "check BSplines implementation for nearest interpolation"); - check_at_sample_points(const_input_sample, BSplinesRegularGridTest_const, - "check BSplines implementation for nearest interpolation"); - // check_at_half_way(const_input_sample, BSplinesRegularGridTest_const); - check_at_sample_points(linear_const_input_sample, BSplinesRegularGridTest_linear_const, - "check BSplines implementation for nearest interpolation"); - // check_at_half_way(linear_const_input_sample, BSplinesRegularGridTest_linear_const); - check_at_sample_points(random_input_sample, BSplinesRegularGridTest_random, - "check BSplines implementation for nearest interpolation"); - // check_at_half_way(random_input_sample, BSplinesRegularGridTest_random); - check_near_sample_points(random_input_sample, BSplinesRegularGridTest_random, - epsilon_1, "Check BSplines implementation for nearest neighbour interpolation near samples: random test case 1"); - check_near_sample_points(random_input_sample, BSplinesRegularGridTest_random, - epsilon_2, "Check BSplines implementation for nearest neighbour interpolation near samples: random test case 2"); - check_near_sample_points(random_input_sample, BSplinesRegularGridTest_random, - epsilon_3, "Check BSplines implementation for nearest neighbour interpolation near samples: random test case 3"); - check_near_sample_points(random_input_sample, BSplinesRegularGridTest_random, - epsilon_4, "Check BSplines implementation for nearest neighbour interpolation near samples: random test case 4"); - } + BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_const(const_input_sample, near_n); + BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_linear_const(linear_const_input_sample, near_n); + BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_random(random_input_sample, near_n); + + check_coefficients(const_input_sample, BSplinesRegularGridTest_const, + "check BSplines implementation for nearest interpolation"); + check_at_sample_points(const_input_sample, BSplinesRegularGridTest_const, + "check BSplines implementation for nearest interpolation"); + // check_at_half_way(const_input_sample, BSplinesRegularGridTest_const); + check_at_sample_points(linear_const_input_sample, BSplinesRegularGridTest_linear_const, + "check BSplines implementation for nearest interpolation"); + // check_at_half_way(linear_const_input_sample, BSplinesRegularGridTest_linear_const); + check_at_sample_points(random_input_sample, BSplinesRegularGridTest_random, + "check BSplines implementation for nearest interpolation"); + // check_at_half_way(random_input_sample, BSplinesRegularGridTest_random); + check_near_sample_points( + random_input_sample, BSplinesRegularGridTest_random, epsilon_1, + "Check BSplines implementation for nearest neighbour interpolation near samples: random test case 1"); + check_near_sample_points( + random_input_sample, BSplinesRegularGridTest_random, epsilon_2, + "Check BSplines implementation for nearest neighbour interpolation near samples: random test case 2"); + check_near_sample_points( + random_input_sample, BSplinesRegularGridTest_random, epsilon_3, + "Check BSplines implementation for nearest neighbour interpolation near samples: random test case 3"); + check_near_sample_points( + random_input_sample, BSplinesRegularGridTest_random, epsilon_4, + "Check BSplines implementation for nearest neighbour interpolation near samples: random test case 4"); } + } + { + cerr << "\nTesting BSplinesRegularGrid: linear interpolation values and constructor using a 2D array as input..." << endl; { - cerr << "\nTesting BSplinesRegularGrid: linear interpolation values and constructor using a 2D array as input..." << endl; - { - BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_const( - const_input_sample, linear); - BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_linear_const( - linear_const_input_sample, linear); - BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_random( - random_input_sample, linear); - - check_coefficients(const_input_sample, BSplinesRegularGridTest_const, - "check BSplines implementation for linear interpolation"); - check_at_sample_points(const_input_sample, BSplinesRegularGridTest_const, - "check BSplines implementation for linear interpolation"); - // check_at_half_way(const_input_sample, BSplinesRegularGridTest_const); - check_at_sample_points(linear_const_input_sample, BSplinesRegularGridTest_linear_const, - "check BSplines implementation for linear interpolation"); - // check_at_half_way(linear_const_input_sample, BSplinesRegularGridTest_linear_const); - check_at_sample_points(random_input_sample, BSplinesRegularGridTest_random, - "check BSplines implementation for linear interpolation"); - // check_at_half_way(random_input_sample, BSplinesRegularGridTest_random); - check_near_sample_points(random_input_sample, BSplinesRegularGridTest_random, - epsilon_1, "Check BSplines implementation for linear interpolation near samples: random test case 1"); - check_near_sample_points(random_input_sample, BSplinesRegularGridTest_random, - epsilon_2, "Check BSplines implementation for linear interpolation near samples: random test case 2"); - check_near_sample_points(random_input_sample, BSplinesRegularGridTest_random, - epsilon_3, "Check BSplines implementation for linear interpolation near samples: random test case 3"); - check_near_sample_points(random_input_sample, BSplinesRegularGridTest_random, - epsilon_4, "Check BSplines implementation for linear interpolation near samples: random test case 4"); - } + BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_const(const_input_sample, linear); + BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_linear_const(linear_const_input_sample, linear); + BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_random(random_input_sample, linear); + + check_coefficients(const_input_sample, BSplinesRegularGridTest_const, + "check BSplines implementation for linear interpolation"); + check_at_sample_points(const_input_sample, BSplinesRegularGridTest_const, + "check BSplines implementation for linear interpolation"); + // check_at_half_way(const_input_sample, BSplinesRegularGridTest_const); + check_at_sample_points(linear_const_input_sample, BSplinesRegularGridTest_linear_const, + "check BSplines implementation for linear interpolation"); + // check_at_half_way(linear_const_input_sample, BSplinesRegularGridTest_linear_const); + check_at_sample_points(random_input_sample, BSplinesRegularGridTest_random, + "check BSplines implementation for linear interpolation"); + // check_at_half_way(random_input_sample, BSplinesRegularGridTest_random); + check_near_sample_points(random_input_sample, BSplinesRegularGridTest_random, epsilon_1, + "Check BSplines implementation for linear interpolation near samples: random test case 1"); + check_near_sample_points(random_input_sample, BSplinesRegularGridTest_random, epsilon_2, + "Check BSplines implementation for linear interpolation near samples: random test case 2"); + check_near_sample_points(random_input_sample, BSplinesRegularGridTest_random, epsilon_3, + "Check BSplines implementation for linear interpolation near samples: random test case 3"); + check_near_sample_points(random_input_sample, BSplinesRegularGridTest_random, epsilon_4, + "Check BSplines implementation for linear interpolation near samples: random test case 4"); } + } + { + cerr << "\nTesting BSplinesRegularGrid: quadratic interpolation values and constructor using a 2D array as input..." << endl; { - cerr << "\nTesting BSplinesRegularGrid: quadratic interpolation values and constructor using a 2D array as input..." << endl; - { - BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_const( - const_input_sample, quadratic); - BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_linear_const( - linear_const_input_sample, quadratic); - BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_random( - random_input_sample, quadratic); - BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_gaussian( - gaussian_input_sample, quadratic); - - check_coefficients(const_input_sample, BSplinesRegularGridTest_const, - "check BSplines implementation for quadratic interpolation"); - check_at_sample_points(const_input_sample, BSplinesRegularGridTest_const, - "check BSplines implementation for quadratic interpolation"); - // check_at_half_way(const_input_sample, BSplinesRegularGridTest_const); - check_at_sample_points(linear_const_input_sample, BSplinesRegularGridTest_linear_const, - "check BSplines implementation for quadratic interpolation"); - // check_at_half_way(linear_const_input_sample, BSplinesRegularGridTest_linear_const); - check_at_sample_points(random_input_sample, BSplinesRegularGridTest_random, - "check BSplines implementation for quadratic interpolation"); - // check_at_half_way(random_input_sample, BSplinesRegularGridTest_random); - check_at_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, - "check BSplines implementation for quadratic interpolation"); - - check_near_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, - epsilon_1, "Check BSplines implementation for quadratic interpolation near samples"); - check_near_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, - epsilon_2, "Check BSplines implementation for quadratic interpolation near samples"); - check_near_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, - epsilon_3, "Check BSplines implementation for quadratic interpolation near samples"); - check_near_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, - epsilon_4, "Check BSplines implementation for quadratic interpolation near samples"); - check_at_half_way(gaussian_input_sample, gaussian_check_sample, - BSplinesRegularGridTest_gaussian, - "check BSplines implementation for quadratic interpolation.\nProblems at half way!"); - } + BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_const(const_input_sample, quadratic); + BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_linear_const(linear_const_input_sample, quadratic); + BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_random(random_input_sample, quadratic); + BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_gaussian(gaussian_input_sample, quadratic); + + check_coefficients(const_input_sample, BSplinesRegularGridTest_const, + "check BSplines implementation for quadratic interpolation"); + check_at_sample_points(const_input_sample, BSplinesRegularGridTest_const, + "check BSplines implementation for quadratic interpolation"); + // check_at_half_way(const_input_sample, BSplinesRegularGridTest_const); + check_at_sample_points(linear_const_input_sample, BSplinesRegularGridTest_linear_const, + "check BSplines implementation for quadratic interpolation"); + // check_at_half_way(linear_const_input_sample, BSplinesRegularGridTest_linear_const); + check_at_sample_points(random_input_sample, BSplinesRegularGridTest_random, + "check BSplines implementation for quadratic interpolation"); + // check_at_half_way(random_input_sample, BSplinesRegularGridTest_random); + check_at_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, + "check BSplines implementation for quadratic interpolation"); + + check_near_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, epsilon_1, + "Check BSplines implementation for quadratic interpolation near samples"); + check_near_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, epsilon_2, + "Check BSplines implementation for quadratic interpolation near samples"); + check_near_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, epsilon_3, + "Check BSplines implementation for quadratic interpolation near samples"); + check_near_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, epsilon_4, + "Check BSplines implementation for quadratic interpolation near samples"); + check_at_half_way(gaussian_input_sample, gaussian_check_sample, BSplinesRegularGridTest_gaussian, + "check BSplines implementation for quadratic interpolation.\nProblems at half way!"); } + } + { + cerr << "\nTesting BSplinesRegularGrid: Cubic interpolation values and constructor using a 2D array as input..." << endl; { - cerr << "\nTesting BSplinesRegularGrid: Cubic interpolation values and constructor using a 2D array as input..." << endl; - { - BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_const( - const_input_sample, cubic); - BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_linear_const( - linear_const_input_sample, cubic); - BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_random( - random_input_sample, cubic); - BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_gaussian( - gaussian_input_sample, cubic); - - check_coefficients(const_input_sample, BSplinesRegularGridTest_const, - "check BSplines implementation for cubic interpolation"); - check_at_sample_points(const_input_sample, BSplinesRegularGridTest_const, - "check BSplines implementation for cubic interpolation"); - // check_at_half_way(const_input_sample, BSplinesRegularGridTest_const); - check_at_sample_points(linear_const_input_sample, BSplinesRegularGridTest_linear_const, - "check BSplines implementation for cubic interpolation"); - // check_at_half_way(linear_const_input_sample, BSplinesRegularGridTest_linear_const); - check_at_sample_points(random_input_sample, BSplinesRegularGridTest_random, - "check BSplines implementation for cubic interpolation"); - // check_at_half_way(random_input_sample, BSplinesRegularGridTest_random); - - check_at_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, - "check BSplines implementation for cubic interpolation"); - - check_near_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, - epsilon_1, "Check BSplines implementation for cubic interpolation near samples"); - check_near_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, - epsilon_2, "Check BSplines implementation for cubic interpolation near samples"); - check_near_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, - epsilon_3, "Check BSplines implementation for cubic interpolation near samples"); - check_near_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, - epsilon_4, "Check BSplines implementation for cubic interpolation near samples"); - - check_at_half_way(gaussian_input_sample, gaussian_check_sample, - BSplinesRegularGridTest_gaussian, - "check BSplines implementation for cubic interpolation.\nProblems at half way!"); - - - //////// gradient - { - - check_gradient(BSplinesRegularGridTest_gaussian, Coordinate2D(1.1,2.2), "cubic-spline gradient: Gaussian test-case 1"); - check_gradient(BSplinesRegularGridTest_gaussian, Coordinate2D(3.1,4.2), "cubic-spline gradient: Gaussian test-case 2"); - check_gradient(BSplinesRegularGridTest_linear_const, Coordinate2D(3.1,4.2), "cubic-spline gradient: test-case linear const"); - } - - } + BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_const(const_input_sample, cubic); + BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_linear_const(linear_const_input_sample, cubic); + BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_random(random_input_sample, cubic); + BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_gaussian(gaussian_input_sample, cubic); + + check_coefficients(const_input_sample, BSplinesRegularGridTest_const, + "check BSplines implementation for cubic interpolation"); + check_at_sample_points(const_input_sample, BSplinesRegularGridTest_const, + "check BSplines implementation for cubic interpolation"); + // check_at_half_way(const_input_sample, BSplinesRegularGridTest_const); + check_at_sample_points(linear_const_input_sample, BSplinesRegularGridTest_linear_const, + "check BSplines implementation for cubic interpolation"); + // check_at_half_way(linear_const_input_sample, BSplinesRegularGridTest_linear_const); + check_at_sample_points(random_input_sample, BSplinesRegularGridTest_random, + "check BSplines implementation for cubic interpolation"); + // check_at_half_way(random_input_sample, BSplinesRegularGridTest_random); + + check_at_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, + "check BSplines implementation for cubic interpolation"); + + check_near_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, epsilon_1, + "Check BSplines implementation for cubic interpolation near samples"); + check_near_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, epsilon_2, + "Check BSplines implementation for cubic interpolation near samples"); + check_near_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, epsilon_3, + "Check BSplines implementation for cubic interpolation near samples"); + check_near_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, epsilon_4, + "Check BSplines implementation for cubic interpolation near samples"); + + check_at_half_way(gaussian_input_sample, gaussian_check_sample, BSplinesRegularGridTest_gaussian, + "check BSplines implementation for cubic interpolation.\nProblems at half way!"); + + //////// gradient + { + + check_gradient(BSplinesRegularGridTest_gaussian, Coordinate2D(1.1, 2.2), + "cubic-spline gradient: Gaussian test-case 1"); + check_gradient(BSplinesRegularGridTest_gaussian, Coordinate2D(3.1, 4.2), + "cubic-spline gradient: Gaussian test-case 2"); + check_gradient(BSplinesRegularGridTest_linear_const, Coordinate2D(3.1, 4.2), + "cubic-spline gradient: test-case linear const"); + } } + } + { + cerr << "\nTesting BSplinesRegularGrid: oMoms interpolation values and constructor using a 2D array as input..." << endl; { - cerr << "\nTesting BSplinesRegularGrid: oMoms interpolation values and constructor using a 2D array as input..." << endl; - { - BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_const( - const_input_sample, oMoms); - BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_linear_const( - linear_const_input_sample, oMoms); - BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_random( - random_input_sample, oMoms); - BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_gaussian( - gaussian_input_sample, oMoms); - - check_coefficients(const_input_sample, BSplinesRegularGridTest_const, - "check BSplines implementation for oMoms interpolation"); - check_at_sample_points(const_input_sample, BSplinesRegularGridTest_const, - "check BSplines implementation for oMoms interpolation"); - // check_at_half_way(const_input_sample, BSplinesRegularGridTest_const); - check_at_sample_points(linear_const_input_sample, BSplinesRegularGridTest_linear_const, - "check BSplines implementation for oMoms interpolation"); - // check_at_half_way(linear_const_input_sample, BSplinesRegularGridTest_linear_const); - check_at_sample_points(random_input_sample, BSplinesRegularGridTest_random, - "check BSplines implementation for oMoms interpolation"); - // check_at_half_way(random_input_sample, BSplinesRegularGridTest_random); - - check_at_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, - "check BSplines implementation for oMoms interpolation"); - check_near_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, - epsilon_1, "Check BSplines implementation for oMoms interpolation near samples"); - check_near_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, - epsilon_2, "Check BSplines implementation for oMoms interpolation near samples"); - check_near_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, - epsilon_3, "Check BSplines implementation for oMoms interpolation near samples"); - check_near_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, - epsilon_4, "Check BSplines implementation for oMoms interpolation near samples"); - - check_at_half_way(gaussian_input_sample, gaussian_check_sample, - BSplinesRegularGridTest_gaussian, - "check BSplines implementation for oMoms interpolation.\nProblems at half way!"); - } + BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_const(const_input_sample, oMoms); + BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_linear_const(linear_const_input_sample, oMoms); + BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_random(random_input_sample, oMoms); + BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_gaussian(gaussian_input_sample, oMoms); + + check_coefficients(const_input_sample, BSplinesRegularGridTest_const, + "check BSplines implementation for oMoms interpolation"); + check_at_sample_points(const_input_sample, BSplinesRegularGridTest_const, + "check BSplines implementation for oMoms interpolation"); + // check_at_half_way(const_input_sample, BSplinesRegularGridTest_const); + check_at_sample_points(linear_const_input_sample, BSplinesRegularGridTest_linear_const, + "check BSplines implementation for oMoms interpolation"); + // check_at_half_way(linear_const_input_sample, BSplinesRegularGridTest_linear_const); + check_at_sample_points(random_input_sample, BSplinesRegularGridTest_random, + "check BSplines implementation for oMoms interpolation"); + // check_at_half_way(random_input_sample, BSplinesRegularGridTest_random); + + check_at_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, + "check BSplines implementation for oMoms interpolation"); + check_near_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, epsilon_1, + "Check BSplines implementation for oMoms interpolation near samples"); + check_near_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, epsilon_2, + "Check BSplines implementation for oMoms interpolation near samples"); + check_near_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, epsilon_3, + "Check BSplines implementation for oMoms interpolation near samples"); + check_near_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, epsilon_4, + "Check BSplines implementation for oMoms interpolation near samples"); + + check_at_half_way(gaussian_input_sample, gaussian_check_sample, BSplinesRegularGridTest_gaussian, + "check BSplines implementation for oMoms interpolation.\nProblems at half way!"); } + } + { + cerr << "\nTesting BSplinesRegularGrid: Linear-Cubic interpolation values and constructor using a 2D array as input..." + << endl; { - cerr << "\nTesting BSplinesRegularGrid: Linear-Cubic interpolation values and constructor using a 2D array as input..." << endl; - { - BasicCoordinate<2,BSpline::BSplineType> linear_cubic; - - linear_cubic[1]=linear; - linear_cubic[2]=cubic; - - BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_const( - const_input_sample, linear_cubic); - BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_linear_const( - linear_const_input_sample, linear_cubic); - BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_random( - random_input_sample, linear_cubic); - BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_gaussian( - gaussian_input_sample, linear_cubic); - - check_coefficients(const_input_sample, BSplinesRegularGridTest_const, - "check BSplines implementation for linear_cubic interpolation"); - check_at_sample_points(const_input_sample, BSplinesRegularGridTest_const, - "check BSplines implementation for linear_cubic interpolation"); - // check_at_half_way(const_input_sample, BSplinesRegularGridTest_const); - check_at_sample_points(linear_const_input_sample, BSplinesRegularGridTest_linear_const, - "check BSplines implementation for linear_cubic interpolation"); - // check_at_half_way(linear_const_input_sample, BSplinesRegularGridTest_linear_const); - check_at_sample_points(random_input_sample, BSplinesRegularGridTest_random, - "check BSplines implementation for linear_cubic interpolation"); - // check_at_half_way(random_input_sample, BSplinesRegularGridTest_random); - - check_at_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, - "check BSplines implementation for linear_cubic interpolation"); - check_near_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, - epsilon_1, "Check BSplines implementation for linear-cubic interpolation near samples: gaussian test 1"); - check_near_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, - epsilon_2, "Check BSplines implementation for linear-cubic interpolation near samples: gaussian test 2"); - check_near_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, - epsilon_3, "Check BSplines implementation for linear-cubic interpolation near samples: gaussian test 3"); - check_near_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, - epsilon_4, "Check BSplines implementation for linear-cubic interpolation near samples: gaussian test 4"); - - check_at_half_way(gaussian_input_sample, gaussian_check_sample, - BSplinesRegularGridTest_gaussian, - "check BSplines implementation for linear_cubic interpolation.\nProblems at half way!"); - } - } - /* { - cerr << "\nTesting BSplinesRegularGrid: Linear interpolation values and constructor using a 2D diamond array as input..." << endl; - Array<1,elemT> random_1D_1 = make_1d_array(-14., 8., -1., 13., -1., -2., 11., 1., -8.); - Array<1,elemT> random_1D_2 = make_1d_array(6., 11., -14., 6., -3., 10., 1.); - Array<1,elemT> random_1D_3 = make_1d_array(-5., -9., -9., 6., -5.); - Array<2,elemT> nonsquare = make_array(random_1D_1,random_1D_2,random_1D_3); - BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest( - nonsquare, linear); - - check_at_sample_points(const_input_sample, BSplinesRegularGridTest, - "check BSplines implementation for cubic interpolation no square"); - }*/ - } + BasicCoordinate<2, BSpline::BSplineType> linear_cubic; + + linear_cubic[1] = linear; + linear_cubic[2] = cubic; + + BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_const(const_input_sample, linear_cubic); + BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_linear_const(linear_const_input_sample, linear_cubic); + BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_random(random_input_sample, linear_cubic); + BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest_gaussian(gaussian_input_sample, linear_cubic); + + check_coefficients(const_input_sample, BSplinesRegularGridTest_const, + "check BSplines implementation for linear_cubic interpolation"); + check_at_sample_points(const_input_sample, BSplinesRegularGridTest_const, + "check BSplines implementation for linear_cubic interpolation"); + // check_at_half_way(const_input_sample, BSplinesRegularGridTest_const); + check_at_sample_points(linear_const_input_sample, BSplinesRegularGridTest_linear_const, + "check BSplines implementation for linear_cubic interpolation"); + // check_at_half_way(linear_const_input_sample, BSplinesRegularGridTest_linear_const); + check_at_sample_points(random_input_sample, BSplinesRegularGridTest_random, + "check BSplines implementation for linear_cubic interpolation"); + // check_at_half_way(random_input_sample, BSplinesRegularGridTest_random); + + check_at_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, + "check BSplines implementation for linear_cubic interpolation"); + check_near_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, epsilon_1, + "Check BSplines implementation for linear-cubic interpolation near samples: gaussian test 1"); + check_near_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, epsilon_2, + "Check BSplines implementation for linear-cubic interpolation near samples: gaussian test 2"); + check_near_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, epsilon_3, + "Check BSplines implementation for linear-cubic interpolation near samples: gaussian test 3"); + check_near_sample_points(gaussian_input_sample, BSplinesRegularGridTest_gaussian, epsilon_4, + "Check BSplines implementation for linear-cubic interpolation near samples: gaussian test 4"); + + check_at_half_way(gaussian_input_sample, gaussian_check_sample, BSplinesRegularGridTest_gaussian, + "check BSplines implementation for linear_cubic interpolation.\nProblems at half way!"); + } + } + /* { + cerr << "\nTesting BSplinesRegularGrid: Linear interpolation values and constructor using a 2D diamond array as input..." << + endl; Array<1,elemT> random_1D_1 = make_1d_array(-14., 8., -1., 13., -1., -2., 11., 1., -8.); Array<1,elemT> random_1D_2 = + make_1d_array(6., 11., -14., 6., -3., 10., 1.); Array<1,elemT> random_1D_3 = make_1d_array(-5., -9., -9., 6., -5.); + Array<2,elemT> nonsquare = make_array(random_1D_1,random_1D_2,random_1D_3); + BSplinesRegularGrid<2, elemT, elemT> BSplinesRegularGridTest( + nonsquare, linear); + + check_at_sample_points(const_input_sample, BSplinesRegularGridTest, + "check BSplines implementation for cubic interpolation no square"); + }*/ +} } // end namespace BSpline @@ -523,14 +466,13 @@ END_NAMESPACE_STIR USING_NAMESPACE_STIR -int main(int argc, char **argv) -{ - if (argc != 1) - { - cerr << "Usage : " << argv[0] << " \n"; - return EXIT_FAILURE; - } - BSpline::BSplinesRegularGrid_Tests tests; - tests.run_tests(); - return tests.main_return_value(); +int +main(int argc, char** argv) { + if (argc != 1) { + cerr << "Usage : " << argv[0] << " \n"; + return EXIT_FAILURE; + } + BSpline::BSplinesRegularGrid_Tests tests; + tests.run_tests(); + return tests.main_return_value(); } diff --git a/src/test/numerics/test_BSplinesRegularGrid1D.cxx b/src/test/numerics/test_BSplinesRegularGrid1D.cxx index e06f5914cf..7d0615588c 100644 --- a/src/test/numerics/test_BSplinesRegularGrid1D.cxx +++ b/src/test/numerics/test_BSplinesRegularGrid1D.cxx @@ -6,22 +6,22 @@ it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. - + This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - + See STIR/LICENSE.txt for details */ /*! - \file + \file \ingroup numerics_test \brief tests the stir::BSplinesRegularGrid class for the stir::Array 1D case \author Charalampos Tsoumpas \author Kris Thielemans -*/ +*/ #include "stir/RunTests.h" #include "stir/Array.h" #include "stir/make_array.h" @@ -36,251 +36,217 @@ using std::endl; #endif START_NAMESPACE_STIR namespace BSpline { - /*! - \ingroup numerics_test - \brief A simple class to test the BSplinesRegularGrid class for 1D arrays - */ - class BSplinesRegularGrid1D_Tests : public RunTests - { - public: - BSplinesRegularGrid1D_Tests() - {} - void run_tests(); - private: - template - bool check_at_sample_points(const Array<1,elemT>& v, - BSplinesRegularGrid<1, elemT, elemT>& interpolator, - const char * const message) - { - IndexRange<1> out_range(static_cast(v.size())); - Array<1,elemT> out(out_range); - BasicCoordinate<1, elemT> relative_positions; - for (int i=0, imax=static_cast(v.size()); i + bool check_at_sample_points(const Array<1, elemT>& v, BSplinesRegularGrid<1, elemT, elemT>& interpolator, + const char* const message) { + IndexRange<1> out_range(static_cast(v.size())); + Array<1, elemT> out(out_range); + BasicCoordinate<1, elemT> relative_positions; + for (int i = 0, imax = static_cast(v.size()); i < imax; ++i) { + relative_positions[1] = i; + out[i] = interpolator(relative_positions); } - - template - bool check_coefficients(const Array<1,elemT>& v, - BSplinesRegularGrid<1, elemT, elemT>& interpolator, - const char * const message) - { - IndexRange<1> out_range(static_cast(v.size())); - Array<1,elemT> out(out_range); - out=interpolator.get_coefficients(); - std::cout << "IN: " << v << "Coefficients: " << out; - return - check_if_equal(v, out, message); - } - }; + std::cout << "IN: " << v << "OUT: " << out; + return check_if_equal(v, out, message); + } + + template + bool check_coefficients(const Array<1, elemT>& v, BSplinesRegularGrid<1, elemT, elemT>& interpolator, + const char* const message) { + IndexRange<1> out_range(static_cast(v.size())); + Array<1, elemT> out(out_range); + out = interpolator.get_coefficients(); + std::cout << "IN: " << v << "Coefficients: " << out; + return check_if_equal(v, out, message); + } +}; - void BSplinesRegularGrid1D_Tests::run_tests() - { - cerr << "\nTesting BSplinesRegularGrid class..." << endl; - set_tolerance(0.001); - typedef double elemT; - Array<1,elemT> const_input_sample = make_1d_array(1., 1., 1., 1., 1., 1.); - Array<1,elemT> linear_input_sample = make_1d_array(1., 2., 3., 4., 5., 6.); - Array<1,elemT> random_input_sample = make_1d_array(-14., 8., -1., 13., -1., -2., 11., 1., -8.); +void +BSplinesRegularGrid1D_Tests::run_tests() { + cerr << "\nTesting BSplinesRegularGrid class..." << endl; + set_tolerance(0.001); + typedef double elemT; + Array<1, elemT> const_input_sample = make_1d_array(1., 1., 1., 1., 1., 1.); + Array<1, elemT> linear_input_sample = make_1d_array(1., 2., 3., 4., 5., 6.); + Array<1, elemT> random_input_sample = make_1d_array(-14., 8., -1., 13., -1., -2., 11., 1., -8.); + { + cerr << "\nTesting BSplinesRegularGrid: Nearest Neighbour values and constructor using an 1D array as input..." << endl; { - cerr << "\nTesting BSplinesRegularGrid: Nearest Neighbour values and constructor using an 1D array as input..." << endl; - { - BSplinesRegularGrid<1, elemT, elemT> BSplinesRegularGridTest_const( - const_input_sample, near_n); - BSplinesRegularGrid<1, elemT, elemT> BSplinesRegularGridTest_linear( - linear_input_sample, near_n); - BSplinesRegularGrid<1, elemT, elemT> BSplinesRegularGridTest_random( - random_input_sample, near_n); - BasicCoordinate<1,elemT> relative_positions; - - check_coefficients(const_input_sample, BSplinesRegularGridTest_const, - "check BSplines implementation for nearest interpolation"); - - check_at_sample_points(const_input_sample, BSplinesRegularGridTest_const, - "check BSplines implementation for nearest interpolation"); - cerr << "At half points: " ; - for (int i=0, imax=static_cast(const_input_sample.size()); i(linear_input_sample.size()); i BSplinesRegularGridTest_const(const_input_sample, near_n); + BSplinesRegularGrid<1, elemT, elemT> BSplinesRegularGridTest_linear(linear_input_sample, near_n); + BSplinesRegularGrid<1, elemT, elemT> BSplinesRegularGridTest_random(random_input_sample, near_n); + BasicCoordinate<1, elemT> relative_positions; + + check_coefficients(const_input_sample, BSplinesRegularGridTest_const, + "check BSplines implementation for nearest interpolation"); + + check_at_sample_points(const_input_sample, BSplinesRegularGridTest_const, + "check BSplines implementation for nearest interpolation"); + cerr << "At half points: "; + for (int i = 0, imax = static_cast(const_input_sample.size()); i < imax; ++i) { + relative_positions[1] = i - 0.5; + cerr << BSplinesRegularGridTest_const(relative_positions) << " "; + } + cerr << "\n\n"; + check_at_sample_points(linear_input_sample, BSplinesRegularGridTest_linear, + "check BSplines implementation for nearest interpolation"); + cerr << "At half points: "; + for (int i = 0, imax = static_cast(linear_input_sample.size()); i < imax; ++i) { + relative_positions[1] = i - 0.5; + cerr << BSplinesRegularGridTest_linear(relative_positions) << " "; } - } + cerr << "\n\n"; + check_at_sample_points(random_input_sample, BSplinesRegularGridTest_random, + "check BSplines implementation for nearest interpolation"); + } + } + { + cerr << "\nTesting BSplinesRegularGrid: Linear interpolation values and constructor using an 1D array as input..." << endl; { - cerr << "\nTesting BSplinesRegularGrid: Linear interpolation values and constructor using an 1D array as input..." << endl; - { - BSplinesRegularGrid<1, elemT, elemT> BSplinesRegularGridTest_const( - const_input_sample, linear); - BSplinesRegularGrid<1, elemT, elemT> BSplinesRegularGridTest_linear( - linear_input_sample, linear); - BSplinesRegularGrid<1, elemT, elemT> BSplinesRegularGridTest_random( - random_input_sample, linear); - BasicCoordinate<1,elemT> relative_positions; - - check_coefficients(const_input_sample, BSplinesRegularGridTest_const, - "check BSplines implementation for linear interpolation"); - - check_at_sample_points(const_input_sample, BSplinesRegularGridTest_const, - "check BSplines implementation for linear interpolation"); - cerr << "At half points: " ; - for (int i=0, imax=static_cast(const_input_sample.size()); i(linear_input_sample.size()); i BSplinesRegularGridTest_const(const_input_sample, linear); + BSplinesRegularGrid<1, elemT, elemT> BSplinesRegularGridTest_linear(linear_input_sample, linear); + BSplinesRegularGrid<1, elemT, elemT> BSplinesRegularGridTest_random(random_input_sample, linear); + BasicCoordinate<1, elemT> relative_positions; + + check_coefficients(const_input_sample, BSplinesRegularGridTest_const, + "check BSplines implementation for linear interpolation"); + + check_at_sample_points(const_input_sample, BSplinesRegularGridTest_const, + "check BSplines implementation for linear interpolation"); + cerr << "At half points: "; + for (int i = 0, imax = static_cast(const_input_sample.size()); i < imax; ++i) { + relative_positions[1] = i - 0.5; + cerr << BSplinesRegularGridTest_const(relative_positions) << " "; } + cerr << "\n\n"; + check_at_sample_points(linear_input_sample, BSplinesRegularGridTest_linear, + "check BSplines implementation for linear interpolation"); + cerr << "At half points: "; + for (int i = 0, imax = static_cast(linear_input_sample.size()); i < imax; ++i) { + relative_positions[1] = i - 0.5; + cerr << BSplinesRegularGridTest_linear(relative_positions) << " "; + } + cerr << "\n\n"; + check_at_sample_points(random_input_sample, BSplinesRegularGridTest_random, + "check BSplines implementation for linear interpolation"); } + } + { + cerr << "\nTesting BSplinesRegularGrid: Quadratic interpolation values and constructor using an 1D array as input..." << endl; { - cerr << "\nTesting BSplinesRegularGrid: Quadratic interpolation values and constructor using an 1D array as input..." << endl; - { - BSplinesRegularGrid<1, elemT, elemT> BSplinesRegularGridTest_const( - const_input_sample, quadratic); - BSplinesRegularGrid<1, elemT, elemT> BSplinesRegularGridTest_linear( - linear_input_sample, quadratic); - BSplinesRegularGrid<1, elemT, elemT> BSplinesRegularGridTest_random( - random_input_sample, quadratic); - BasicCoordinate<1,elemT> relative_positions; - - check_coefficients(const_input_sample, BSplinesRegularGridTest_const, - "check BSplines implementation for quadratic interpolation"); - - check_at_sample_points(const_input_sample, BSplinesRegularGridTest_const, - "check BSplines implementation for quadratic interpolation"); - cerr << "At half points: " ; - for (std::size_t i=0, imax=const_input_sample.size(); i BSplinesRegularGridTest_const(const_input_sample, quadratic); + BSplinesRegularGrid<1, elemT, elemT> BSplinesRegularGridTest_linear(linear_input_sample, quadratic); + BSplinesRegularGrid<1, elemT, elemT> BSplinesRegularGridTest_random(random_input_sample, quadratic); + BasicCoordinate<1, elemT> relative_positions; + + check_coefficients(const_input_sample, BSplinesRegularGridTest_const, + "check BSplines implementation for quadratic interpolation"); + + check_at_sample_points(const_input_sample, BSplinesRegularGridTest_const, + "check BSplines implementation for quadratic interpolation"); + cerr << "At half points: "; + for (std::size_t i = 0, imax = const_input_sample.size(); i < imax; ++i) { + relative_positions[1] = i - 0.5; + cerr << BSplinesRegularGridTest_const(relative_positions) << " "; } + cerr << "\n\n"; + check_at_sample_points(linear_input_sample, BSplinesRegularGridTest_linear, + "check BSplines implementation for quadratic interpolation"); + cerr << "At half points: "; + for (std::size_t i = 0, imax = linear_input_sample.size(); i < imax; ++i) { + relative_positions[1] = i - 0.5; + cerr << BSplinesRegularGridTest_linear(relative_positions) << " "; + } + cerr << "\n\n"; + check_at_sample_points(random_input_sample, BSplinesRegularGridTest_random, + "check BSplines implementation for quadratic interpolation"); } + } + { + cerr << "\nTesting BSplinesRegularGrid: Cubic interpolation values and constructor using an 1D array as input..." << endl; { - cerr << "\nTesting BSplinesRegularGrid: Cubic interpolation values and constructor using an 1D array as input..." << endl; - { - BSplinesRegularGrid<1, elemT, elemT> BSplinesRegularGridTest_const( - const_input_sample, cubic); - BSplinesRegularGrid<1, elemT, elemT> BSplinesRegularGridTest_linear( - linear_input_sample, cubic); - BSplinesRegularGrid<1, elemT, elemT> BSplinesRegularGridTest_random( - random_input_sample, cubic); - BasicCoordinate<1,elemT> relative_positions; - - check_coefficients(const_input_sample, BSplinesRegularGridTest_const, - "check BSplines implementation for cubic interpolation"); - - check_at_sample_points(const_input_sample, BSplinesRegularGridTest_const, - "check BSplines implementation for cubic interpolation"); - cerr << "At half points: " ; - for (std::size_t i=0, imax=const_input_sample.size(); i BSplinesRegularGridTest_const(const_input_sample, cubic); + BSplinesRegularGrid<1, elemT, elemT> BSplinesRegularGridTest_linear(linear_input_sample, cubic); + BSplinesRegularGrid<1, elemT, elemT> BSplinesRegularGridTest_random(random_input_sample, cubic); + BasicCoordinate<1, elemT> relative_positions; + + check_coefficients(const_input_sample, BSplinesRegularGridTest_const, + "check BSplines implementation for cubic interpolation"); + + check_at_sample_points(const_input_sample, BSplinesRegularGridTest_const, + "check BSplines implementation for cubic interpolation"); + cerr << "At half points: "; + for (std::size_t i = 0, imax = const_input_sample.size(); i < imax; ++i) { + relative_positions[1] = i - 0.5; + cerr << BSplinesRegularGridTest_const(relative_positions) << " "; } + cerr << "\n\n"; + check_at_sample_points(linear_input_sample, BSplinesRegularGridTest_linear, + "check BSplines implementation for cubic interpolation"); + cerr << "At half points: "; + for (std::size_t i = 0, imax = linear_input_sample.size(); i < imax; ++i) { + relative_positions[1] = i - 0.5; + cerr << BSplinesRegularGridTest_linear(relative_positions) << " "; + } + cerr << "\n\n"; + check_at_sample_points(random_input_sample, BSplinesRegularGridTest_random, + "check BSplines implementation for cubic interpolation"); } + } + { + cerr << "\nTesting BSplinesRegularGrid: oMoms interpolation values and constructor using an 1D array as input..." << endl; { - cerr << "\nTesting BSplinesRegularGrid: oMoms interpolation values and constructor using an 1D array as input..." << endl; - { - BSplinesRegularGrid<1, elemT, elemT> BSplinesRegularGridTest_const( - const_input_sample, oMoms); - BSplinesRegularGrid<1, elemT, elemT> BSplinesRegularGridTest_linear( - linear_input_sample, oMoms); - BSplinesRegularGrid<1, elemT, elemT> BSplinesRegularGridTest_random( - random_input_sample, oMoms); - BasicCoordinate<1,elemT> relative_positions; - - check_coefficients(const_input_sample, BSplinesRegularGridTest_const, - "check BSplines implementation for oMoms interpolation"); - - check_at_sample_points(const_input_sample, BSplinesRegularGridTest_const, - "check BSplines implementation for oMoms interpolation"); - cerr << "At half points: " ; - for (std::size_t i=0, imax=const_input_sample.size(); i BSplinesRegularGridTest_const(const_input_sample, oMoms); + BSplinesRegularGrid<1, elemT, elemT> BSplinesRegularGridTest_linear(linear_input_sample, oMoms); + BSplinesRegularGrid<1, elemT, elemT> BSplinesRegularGridTest_random(random_input_sample, oMoms); + BasicCoordinate<1, elemT> relative_positions; + + check_coefficients(const_input_sample, BSplinesRegularGridTest_const, + "check BSplines implementation for oMoms interpolation"); + + check_at_sample_points(const_input_sample, BSplinesRegularGridTest_const, + "check BSplines implementation for oMoms interpolation"); + cerr << "At half points: "; + for (std::size_t i = 0, imax = const_input_sample.size(); i < imax; ++i) { + relative_positions[1] = i - 0.5; + cerr << BSplinesRegularGridTest_const(relative_positions) << " "; } + cerr << "\n\n"; + check_at_sample_points(linear_input_sample, BSplinesRegularGridTest_linear, + "check BSplines implementation for oMoms interpolation"); + cerr << "At half points: "; + for (std::size_t i = 0, imax = linear_input_sample.size(); i < imax; ++i) { + relative_positions[1] = i - 0.5; + cerr << BSplinesRegularGridTest_linear(relative_positions) << " "; + } + cerr << "\n\n"; + check_at_sample_points(random_input_sample, BSplinesRegularGridTest_random, + "check BSplines implementation for oMoms interpolation"); } } +} } // end namespace BSpline END_NAMESPACE_STIR USING_NAMESPACE_STIR -int main(int argc, char **argv) -{ - if (argc != 1) - { - cerr << "Usage : " << argv[0] << " \n"; - return EXIT_FAILURE; - } +int +main(int argc, char** argv) { + if (argc != 1) { + cerr << "Usage : " << argv[0] << " \n"; + return EXIT_FAILURE; + } BSpline::BSplinesRegularGrid1D_Tests tests; tests.run_tests(); return tests.main_return_value(); diff --git a/src/test/numerics/test_Fourier.cxx b/src/test/numerics/test_Fourier.cxx index 9e86853200..2fc286e229 100644 --- a/src/test/numerics/test_Fourier.cxx +++ b/src/test/numerics/test_Fourier.cxx @@ -2,7 +2,7 @@ // /*! - \file + \file \ingroup tests \ingroup DFT \brief Tests for function in the DFT group @@ -28,7 +28,6 @@ #include #include - using std::cin; using std::cout; using std::endl; @@ -37,115 +36,103 @@ START_NAMESPACE_STIR #define DOARRAY #ifdef DOARRAY - typedef Array<1,std::complex > ArrayC1; - typedef Array<1,float> ArrayF1; - typedef Array<2,float> ArrayF2; - typedef Array<2,std::complex > ArrayC2; - typedef Array<3,float> ArrayF3; - typedef Array<3,std::complex > ArrayC3; +typedef Array<1, std::complex> ArrayC1; +typedef Array<1, float> ArrayF1; +typedef Array<2, float> ArrayF2; +typedef Array<2, std::complex> ArrayC2; +typedef Array<3, float> ArrayF3; +typedef Array<3, std::complex> ArrayC3; #else - typedef VectorWithOffset > ArrayC1; - typedef VectorWithOffset ArrayF1; - typedef VectorWithOffset ArrayC2; +typedef VectorWithOffset> ArrayC1; +typedef VectorWithOffset ArrayF1; +typedef VectorWithOffset ArrayC2; #endif - - - -inline float rand1() -{ - return 2*(rand()-RAND_MAX/2.F)/RAND_MAX; +inline float +rand1() { + return 2 * (rand() - RAND_MAX / 2.F) / RAND_MAX; } /*! \ingroup numerics_test \brief A simple class to test the erf and erfc functions. */ -class FourierTests : public RunTests -{ +class FourierTests : public RunTests { public: - FourierTests() - {} + FourierTests() {} void run_tests(); + private: template void test_single_dimension(const IndexRange& index_range); }; template -void FourierTests::test_single_dimension(const IndexRange& index_range) -{ - typedef Array > complex_type; +void +FourierTests::test_single_dimension(const IndexRange& index_range) { + typedef Array> complex_type; typedef Array real_type; complex_type complex_array(index_range); real_type real_array(index_range); // fill { - for (typename real_type::full_iterator iter= real_array.begin_all(); - iter!=real_array.end_all(); - ++iter) - *iter= rand1(); + for (typename real_type::full_iterator iter = real_array.begin_all(); iter != real_array.end_all(); ++iter) + *iter = rand1(); std::copy(real_array.begin_all(), real_array.end_all(), complex_array.begin_all()); } - const int sign=1; - - complex_type pos_frequencies = - fourier_for_real_data(real_array, sign); - const complex_type all_frequencies = - pos_frequencies_to_all(pos_frequencies); - - fourier(complex_array,sign); - //cout << pos_frequencies; - //cout << all_frequencies << complex_array; - //cout << '\n' << complex_array-all_frequencies; + const int sign = 1; + + complex_type pos_frequencies = fourier_for_real_data(real_array, sign); + const complex_type all_frequencies = pos_frequencies_to_all(pos_frequencies); + + fourier(complex_array, sign); + // cout << pos_frequencies; + // cout << all_frequencies << complex_array; + // cout << '\n' << complex_array-all_frequencies; complex_array -= all_frequencies; - cout << "\nReal FT Residual norm " << - norm(complex_array.begin_all(), complex_array.end_all())/norm(real_array.begin_all(), real_array.end_all()); + cout << "\nReal FT Residual norm " + << norm(complex_array.begin_all(), complex_array.end_all()) / norm(real_array.begin_all(), real_array.end_all()); - real_type test_inverse_real = - inverse_fourier_for_real_data(pos_frequencies,sign); - //cout <<"\nv,test "<< v << test_inverse_real << test_inverse_real/v; + real_type test_inverse_real = inverse_fourier_for_real_data(pos_frequencies, sign); + // cout <<"\nv,test "<< v << test_inverse_real << test_inverse_real/v; test_inverse_real -= real_array; - cout << "\ninverse Real FT Residual norm " << - norm(test_inverse_real.begin_all(), test_inverse_real.end_all())/norm(real_array.begin_all(), real_array.end_all()); + cout << "\ninverse Real FT Residual norm " + << norm(test_inverse_real.begin_all(), test_inverse_real.end_all()) / norm(real_array.begin_all(), real_array.end_all()); // fill { - for (typename complex_type::full_iterator iter= complex_array.begin_all(); - iter!=complex_array.end_all(); - ++iter) + for (typename complex_type::full_iterator iter = complex_array.begin_all(); iter != complex_array.end_all(); ++iter) *iter = std::complex(rand1(), rand1()); } const complex_type array_copy(complex_array); - fourier(complex_array,sign); - inverse_fourier(complex_array,sign); + fourier(complex_array, sign); + inverse_fourier(complex_array, sign); complex_array -= array_copy; - cout << "\ninverse FT Residual norm " << - norm(complex_array.begin_all(), complex_array.end_all())/norm(array_copy.begin_all(), array_copy.end_all()); + cout << "\ninverse FT Residual norm " + << norm(complex_array.begin_all(), complex_array.end_all()) / norm(array_copy.begin_all(), array_copy.end_all()); } -void FourierTests::run_tests() -{ +void +FourierTests::run_tests() { std::cerr << "Testing Fourier Functions..." << std::endl; std::cerr << "... Testing 1D\n"; test_single_dimension(IndexRange<1>(128)); std::cerr << "... Testing 2D\n"; - test_single_dimension(IndexRange2D(128,256)); + test_single_dimension(IndexRange2D(128, 256)); std::cerr << "... Testing 3D\n"; - test_single_dimension(IndexRange3D(128,256,16)); + test_single_dimension(IndexRange3D(128, 256, 16)); } END_NAMESPACE_STIR -int main(int argc, char **argv) -{ - if (argc != 1) - { +int +main(int argc, char** argv) { + if (argc != 1) { std::cerr << "Usage : " << argv[0] << " \n"; return EXIT_FAILURE; } diff --git a/src/test/numerics/test_IR_filters.cxx b/src/test/numerics/test_IR_filters.cxx index 97f345fd89..cbe6f1543a 100644 --- a/src/test/numerics/test_IR_filters.cxx +++ b/src/test/numerics/test_IR_filters.cxx @@ -1,7 +1,7 @@ // // /*! - \file + \file \ingroup numerics_test \brief tests the implementation of the IR_filters @@ -25,13 +25,11 @@ See STIR/LICENSE.txt for details */ - #include "stir/RunTests.h" #include "stir/numerics/IR_filters.h" #include #include - #ifndef STIR_NO_NAMESPACES using std::cerr; using std::endl; @@ -43,118 +41,98 @@ START_NAMESPACE_STIR \ingroup numerics_test \brief A simple class to test the IIR_filter and FIR_filter function. */ -class IR_filterTests : public RunTests -{ +class IR_filterTests : public RunTests { public: - IR_filterTests() - {} + IR_filterTests() {} void run_tests(); + private: - //istream& in; + // istream& in; }; - -void IR_filterTests::run_tests() -{ +void +IR_filterTests::run_tests() { cerr << "Testing IR_filter function..." << endl; set_tolerance(0.001); - - std::vector input_test_signal, input_factors, poles, - output_FIR_test_value, output_IIR_test_value, - stir_FIR_output, stir_IIR_output; - - for (int i=1, imax=11; i input_test_signal, input_factors, poles, output_FIR_test_value, output_IIR_test_value, stir_FIR_output, + stir_IIR_output; + + for (int i = 1, imax = 11; i < imax; ++i) { + input_test_signal.push_back(i); + + stir_FIR_output.push_back(0); + stir_IIR_output.push_back(0); + + input_factors.push_back(imax - i); + if (i == 10) + break; + + if (i <= 5) poles.push_back(1); - input_factors.push_back(9); - } - if (i==3) - input_factors.push_back(8); - */ + else + poles.push_back(-1); + + /* For Checking different sizes of the poles and input_factors + + if (i==1) + { + poles.push_back(1); + input_factors.push_back(10); + } + if (i==2) + { + poles.push_back(1); + input_factors.push_back(9); } + if (i==3) + input_factors.push_back(8); + */ + } + + output_FIR_test_value.push_back(10); // 1 + output_FIR_test_value.push_back(29); // 2 + output_FIR_test_value.push_back(56); // 3 + output_FIR_test_value.push_back(90); // 4 + output_FIR_test_value.push_back(130); // 5 + output_FIR_test_value.push_back(175); // 6 + output_FIR_test_value.push_back(224); // 7 + output_FIR_test_value.push_back(276); // 8 + output_FIR_test_value.push_back(330); // 9 + output_FIR_test_value.push_back(385); // 10 + + output_IIR_test_value.push_back(10); // 1 + output_IIR_test_value.push_back(19); // 2 + output_IIR_test_value.push_back(27); // 3 + output_IIR_test_value.push_back(34); // 4 + output_IIR_test_value.push_back(40); // 5 + output_IIR_test_value.push_back(45); // 6 + output_IIR_test_value.push_back(69); // 7 + output_IIR_test_value.push_back(90); // 8 + output_IIR_test_value.push_back(108); // 9 + output_IIR_test_value.push_back(123); // 10 + + { + cerr << "Testing FIR_filter function..." << endl; + + FIR_filter(stir_FIR_output.begin(), stir_FIR_output.end(), input_test_signal.begin(), input_test_signal.end(), + input_factors.begin(), input_factors.end(), 0); + + std::vector::iterator cur_iter_stir_out = stir_FIR_output.begin(), cur_iter_FIR_out = output_FIR_test_value.begin(); - output_FIR_test_value.push_back(10); //1 - output_FIR_test_value.push_back(29); //2 - output_FIR_test_value.push_back(56); //3 - output_FIR_test_value.push_back(90); //4 - output_FIR_test_value.push_back(130); //5 - output_FIR_test_value.push_back(175); //6 - output_FIR_test_value.push_back(224); //7 - output_FIR_test_value.push_back(276); //8 - output_FIR_test_value.push_back(330); //9 - output_FIR_test_value.push_back(385); //10 - - output_IIR_test_value.push_back(10); //1 - output_IIR_test_value.push_back(19); //2 - output_IIR_test_value.push_back(27); //3 - output_IIR_test_value.push_back(34); //4 - output_IIR_test_value.push_back(40); //5 - output_IIR_test_value.push_back(45); //6 - output_IIR_test_value.push_back(69); //7 - output_IIR_test_value.push_back(90); //8 - output_IIR_test_value.push_back(108); //9 - output_IIR_test_value.push_back(123); //10 - - - { - cerr << "Testing FIR_filter function..." << endl; - - FIR_filter(stir_FIR_output.begin(), stir_FIR_output.end(), - input_test_signal.begin(), input_test_signal.end(), - input_factors.begin(), input_factors.end(),0); - - std::vector:: iterator cur_iter_stir_out= stir_FIR_output.begin(), - cur_iter_FIR_out= output_FIR_test_value.begin(); - - for (; - cur_iter_stir_out!=stir_FIR_output.end() - && - cur_iter_FIR_out!=output_FIR_test_value.end(); - ++cur_iter_stir_out, ++cur_iter_FIR_out) - check_if_equal(*cur_iter_stir_out, *cur_iter_FIR_out, - "check FIR filter implementation"); - } - { - cerr << "Testing IIR_filter function..." << endl; - IIR_filter(stir_IIR_output.begin(), stir_IIR_output.end(), - input_test_signal.begin(), input_test_signal.end(), - input_factors.begin(), input_factors.end(), - poles.begin(), poles.end(),0); - std::vector:: iterator cur_iter_stir_IIR_out= - stir_IIR_output.begin(), - cur_iter_IIR_out= output_IIR_test_value.begin(); - for (; - cur_iter_stir_IIR_out!=stir_IIR_output.end() - && - cur_iter_IIR_out!=output_IIR_test_value.end(); - ++cur_iter_stir_IIR_out, ++cur_iter_IIR_out) - check_if_equal(*cur_iter_stir_IIR_out, *cur_iter_IIR_out, - "check IIR filter implementation"); + for (; cur_iter_stir_out != stir_FIR_output.end() && cur_iter_FIR_out != output_FIR_test_value.end(); + ++cur_iter_stir_out, ++cur_iter_FIR_out) + check_if_equal(*cur_iter_stir_out, *cur_iter_FIR_out, "check FIR filter implementation"); + } + { + cerr << "Testing IIR_filter function..." << endl; + IIR_filter(stir_IIR_output.begin(), stir_IIR_output.end(), input_test_signal.begin(), input_test_signal.end(), + input_factors.begin(), input_factors.end(), poles.begin(), poles.end(), 0); + std::vector::iterator cur_iter_stir_IIR_out = stir_IIR_output.begin(), cur_iter_IIR_out = output_IIR_test_value.begin(); + for (; cur_iter_stir_IIR_out != stir_IIR_output.end() && cur_iter_IIR_out != output_IIR_test_value.end(); + ++cur_iter_stir_IIR_out, ++cur_iter_IIR_out) + check_if_equal(*cur_iter_stir_IIR_out, *cur_iter_IIR_out, "check IIR filter implementation"); } } @@ -162,10 +140,9 @@ END_NAMESPACE_STIR USING_NAMESPACE_STIR -int main(int argc, char **argv) -{ - if (argc != 1) - { +int +main(int argc, char** argv) { + if (argc != 1) { cerr << "Usage : " << argv[0] << " \n"; return EXIT_FAILURE; } diff --git a/src/test/numerics/test_erf.cxx b/src/test/numerics/test_erf.cxx index f7ca4f2a78..f461c6f110 100644 --- a/src/test/numerics/test_erf.cxx +++ b/src/test/numerics/test_erf.cxx @@ -17,14 +17,14 @@ See STIR/LICENSE.txt for details */ /*! - \file + \file \ingroup numerics_test \brief tests the error function stir::erf and its complementary \author Charalampos Tsoumpas */ - + #include "stir/RunTests.h" #include "stir/numerics/erf.h" #include @@ -35,31 +35,28 @@ START_NAMESPACE_STIR \ingroup numerics_test \brief A simple class to test the erf and erfc functions. */ -class erfTests : public RunTests -{ +class erfTests : public RunTests { public: - erfTests() - {} + erfTests() {} void run_tests(); + private: - //istream& in; + // istream& in; }; - -void erfTests::run_tests() -{ +void +erfTests::run_tests() { std::cerr << "Testing Error Functions..." << std::endl; set_tolerance(0.000000000000001); - - //std::vector input, mathematica_results, STIR_results; - - { - - std::vector input_vector(10), - output_correct_vector(10), output_correct_vector_c(10), - STIR_vector(10), STIR_vector_c(10); - + + // std::vector input, mathematica_results, STIR_results; + + { + + std::vector input_vector(10), output_correct_vector(10), output_correct_vector_c(10), STIR_vector(10), + STIR_vector_c(10); + output_correct_vector[0] = 0.0000000000000000000000000000000; output_correct_vector[1] = 0.0112834155558496169159095235481; output_correct_vector[2] = 0.0225645746918449442243658616474; @@ -69,7 +66,7 @@ void erfTests::run_tests() output_correct_vector[6] = 0.0676215943933084420794314523912; output_correct_vector[7] = 0.0788577197708907433569970386680; output_correct_vector[8] = 0.0900781258410181607233921876161; - output_correct_vector[9] = 0.101280593914626883352498163244; + output_correct_vector[9] = 0.101280593914626883352498163244; output_correct_vector_c[0] = 1.00000000000000000000000000000; output_correct_vector_c[1] = 0.988716584444150383084090476452; @@ -80,47 +77,35 @@ void erfTests::run_tests() output_correct_vector_c[6] = 0.932378405606691557920568547609; output_correct_vector_c[7] = 0.921142280229109256643002961332; output_correct_vector_c[8] = 0.909921874158981839276607812384; - output_correct_vector_c[9] = 0.898719406085373116647501836756; - - - for (int i=0 ; i<10 ; ++i) - { - input_vector[i] = 0.01*(static_cast(i)); - STIR_vector[i] = erf(input_vector[i]); - STIR_vector_c[i] = erfc(input_vector[i]); - } - std::vector:: iterator cur_iter_cor = output_correct_vector.begin(), - cur_iter_cor_c = output_correct_vector_c.begin(), - cur_iter_STIR = STIR_vector.begin(), - cur_iter_STIR_c = STIR_vector_c.begin(); - - for (cur_iter_STIR = STIR_vector.begin(); - cur_iter_STIR!=STIR_vector.end() && cur_iter_cor!=output_correct_vector.end(); - ++cur_iter_STIR, ++cur_iter_cor) - check_if_equal(*cur_iter_cor, *cur_iter_STIR, - "check erf() implementation"); - for (; - cur_iter_STIR_c!=STIR_vector_c.end() && cur_iter_cor_c!=output_correct_vector_c.end(); - ++cur_iter_STIR_c, ++cur_iter_cor_c) - check_if_equal(*cur_iter_cor_c, *cur_iter_STIR_c, - "check erfc() implementation"); + output_correct_vector_c[9] = 0.898719406085373116647501836756; + + for (int i = 0; i < 10; ++i) { + input_vector[i] = 0.01 * (static_cast(i)); + STIR_vector[i] = erf(input_vector[i]); + STIR_vector_c[i] = erfc(input_vector[i]); + } + std::vector::iterator cur_iter_cor = output_correct_vector.begin(), cur_iter_cor_c = output_correct_vector_c.begin(), + cur_iter_STIR = STIR_vector.begin(), cur_iter_STIR_c = STIR_vector_c.begin(); + + for (cur_iter_STIR = STIR_vector.begin(); cur_iter_STIR != STIR_vector.end() && cur_iter_cor != output_correct_vector.end(); + ++cur_iter_STIR, ++cur_iter_cor) + check_if_equal(*cur_iter_cor, *cur_iter_STIR, "check erf() implementation"); + for (; cur_iter_STIR_c != STIR_vector_c.end() && cur_iter_cor_c != output_correct_vector_c.end(); + ++cur_iter_STIR_c, ++cur_iter_cor_c) + check_if_equal(*cur_iter_cor_c, *cur_iter_STIR_c, "check erfc() implementation"); for (cur_iter_STIR_c = STIR_vector_c.begin(), cur_iter_STIR = STIR_vector.begin(); - cur_iter_STIR!=STIR_vector.end() && - cur_iter_STIR_c!=STIR_vector_c.end(); - ++cur_iter_STIR, ++cur_iter_STIR_c) - check_if_equal(1.0, (*cur_iter_STIR_c) + (*cur_iter_STIR), - "check erfc() and erf() results"); - } + cur_iter_STIR != STIR_vector.end() && cur_iter_STIR_c != STIR_vector_c.end(); ++cur_iter_STIR, ++cur_iter_STIR_c) + check_if_equal(1.0, (*cur_iter_STIR_c) + (*cur_iter_STIR), "check erfc() and erf() results"); + } } END_NAMESPACE_STIR USING_NAMESPACE_STIR -int main(int argc, char **argv) -{ - if (argc != 1) - { +int +main(int argc, char** argv) { + if (argc != 1) { std::cerr << "Usage : " << argv[0] << " \n"; return EXIT_FAILURE; } diff --git a/src/test/numerics/test_integrate_discrete_function.cxx b/src/test/numerics/test_integrate_discrete_function.cxx index 1ddb5d9e44..0fd915c785 100644 --- a/src/test/numerics/test_integrate_discrete_function.cxx +++ b/src/test/numerics/test_integrate_discrete_function.cxx @@ -23,9 +23,9 @@ See STIR/LICENSE.txt for details */ /*! - \file + \file \ingroup numerics_test - \brief tests the integrate_discrete_function function + \brief tests the integrate_discrete_function function */ #include "stir/RunTests.h" @@ -38,44 +38,40 @@ START_NAMESPACE_STIR \ingroup numerics_test \brief A simple class to test the integrate_discrete_function function. */ -class integrate_discrete_functionTests : public RunTests -{ +class integrate_discrete_functionTests : public RunTests { public: - integrate_discrete_functionTests() - {} + integrate_discrete_functionTests() {} void run_tests(); }; - -void integrate_discrete_functionTests::run_tests() -{ +void +integrate_discrete_functionTests::run_tests() { std::cerr << "Testing integrate_discrete_function..." << std::endl; - { + { set_tolerance(0.000001); - + // Give a simple linear function. The analytical estimation of the integral is E=(tmax-tmin)*(fmax+fmin)/2 - const int Nmin=2; - const int Nmax=333; - - const float tmin=12; - const float tmax=123; - const float fmin=113; - const float fmax=1113; - - const float cor_integral_value=(tmax-tmin)*(fmax+fmin)*0.5; - std::vector input_vector_f(Nmax-Nmin+1), input_vector_t(Nmax-Nmin+1); - - for (int i=0;i<=Nmax-Nmin;++i) - { - input_vector_t[i]=tmin+i*(tmax-tmin)/(Nmax-Nmin); - input_vector_f[i]=fmin+i*(fmax-fmin)/(Nmax-Nmin); - } - - check_if_equal(integrate_discrete_function(input_vector_t,input_vector_f,0),cor_integral_value, - "check integrate_discrete_function implementation for linear function using rectangular approximation"); - check_if_equal(integrate_discrete_function(input_vector_t,input_vector_f), cor_integral_value, - "check integrate_discrete_function implementation for linear function using trapezoidal approximation"); + const int Nmin = 2; + const int Nmax = 333; + + const float tmin = 12; + const float tmax = 123; + const float fmin = 113; + const float fmax = 1113; + + const float cor_integral_value = (tmax - tmin) * (fmax + fmin) * 0.5; + std::vector input_vector_f(Nmax - Nmin + 1), input_vector_t(Nmax - Nmin + 1); + + for (int i = 0; i <= Nmax - Nmin; ++i) { + input_vector_t[i] = tmin + i * (tmax - tmin) / (Nmax - Nmin); + input_vector_f[i] = fmin + i * (fmax - fmin) / (Nmax - Nmin); + } + + check_if_equal(integrate_discrete_function(input_vector_t, input_vector_f, 0), cor_integral_value, + "check integrate_discrete_function implementation for linear function using rectangular approximation"); + check_if_equal(integrate_discrete_function(input_vector_t, input_vector_f), cor_integral_value, + "check integrate_discrete_function implementation for linear function using trapezoidal approximation"); } // quadratic @@ -84,37 +80,38 @@ void integrate_discrete_functionTests::run_tests() std::vector values(100); // make non-uniform sampling - int i=0; - for (i=0; i<=50; ++i) - coords[i]=2*(i-50); - for (; i<=70; ++i) - coords[i]=coords[i-1]+1; - for (; i<100; ++i) - coords[i]=coords[i-1]+1.5; + int i = 0; + for (i = 0; i <= 50; ++i) + coords[i] = 2 * (i - 50); + for (; i <= 70; ++i) + coords[i] = coords[i - 1] + 1; + for (; i < 100; ++i) + coords[i] = coords[i - 1] + 1.5; // fill values - for (i=0; i<100; ++i) - values[i]=2*coords[i]*coords[i]- 5*coords[i]+7; + for (i = 0; i < 100; ++i) + values[i] = 2 * coords[i] * coords[i] - 5 * coords[i] + 7; const double analytic_result = - (2*coords[99]*coords[99]*coords[99]/3 - 5*coords[0]*coords[99]/2+7*coords[99]) - - (2*coords[0]*coords[0]*coords[0]/3 - 5*coords[0]*coords[0]/2+7*coords[0]); - double stir_result=integrate_discrete_function(coords, values,1); + (2 * coords[99] * coords[99] * coords[99] / 3 - 5 * coords[0] * coords[99] / 2 + 7 * coords[99]) - + (2 * coords[0] * coords[0] * coords[0] / 3 - 5 * coords[0] * coords[0] / 2 + 7 * coords[0]); + double stir_result = integrate_discrete_function(coords, values, 1); set_tolerance(.03); // expected accuracy is 3% - check_if_equal(stir_result, analytic_result, "check integrate_discrete_function implementation for quadratic function using trapezoidal approximation"); - stir_result=integrate_discrete_function(coords, values, 0); + check_if_equal(stir_result, analytic_result, + "check integrate_discrete_function implementation for quadratic function using trapezoidal approximation"); + stir_result = integrate_discrete_function(coords, values, 0); set_tolerance(.03); - check_if_equal(stir_result, analytic_result, "check integrate_discrete_function implementation for linear function using rectangular approximation"); + check_if_equal(stir_result, analytic_result, + "check integrate_discrete_function implementation for linear function using rectangular approximation"); } } END_NAMESPACE_STIR USING_NAMESPACE_STIR -int main(int argc, char **argv) -{ - if (argc != 1) - { +int +main(int argc, char** argv) { + if (argc != 1) { std::cerr << "Usage : " << argv[0] << " \n"; return EXIT_FAILURE; } diff --git a/src/test/numerics/test_matrices.cxx b/src/test/numerics/test_matrices.cxx index a5fd1396e7..cc295c6ac2 100644 --- a/src/test/numerics/test_matrices.cxx +++ b/src/test/numerics/test_matrices.cxx @@ -16,9 +16,9 @@ See STIR/LICENSE.txt for details */ /*! - \file + \file \ingroup numerics_test - + \brief tests for functions in MatrixFunction.h etc. \author Kris Thielemans @@ -35,22 +35,25 @@ #include "stir/stream.h" #include #include -# ifdef BOOST_NO_STDC_NAMESPACE - namespace std { using ::sqrt; using ::sin; using ::cos} -# endif +#ifdef BOOST_NO_STDC_NAMESPACE +namespace std { +using ::sqrt; +using ::sin; +using ::cos +} // namespace std +#endif START_NAMESPACE_STIR - /*! \brief Tests MatrixFunction.h functionality \ingroup test_numerics */ -class MatrixTests : public RunTests -{ +class MatrixTests : public RunTests { public: void run_tests(); + private: void run_tests_1D(); void run_tests_2D(); @@ -60,30 +63,19 @@ class MatrixTests : public RunTests // local function, copied from the Shape library // takes Euler angles and make an orthogonal matrix // note that because of STIR conventions, this matrix has determinant -1. -static -Array<2,float> -make_orthogonal_matrix(const float alpha, const float beta, const float gamma) -{ - return - make_array(make_1d_array( - std::sin(beta)*std::sin(gamma), - std::cos(gamma)*std::sin(alpha) + std::cos(alpha)*std::cos(beta)*std::sin(gamma), - std::cos(alpha)*std::cos(gamma) - std::cos(beta)*std::sin(alpha)*std::sin(gamma)), - make_1d_array( - std::cos(gamma)*std::sin(beta), - std::cos(alpha)*std::cos(beta)*std::cos(gamma) - std::sin(alpha)*std::sin(gamma), - -(std::cos(beta)*std::cos(gamma)*std::sin(alpha)) - std::cos(alpha)*std::sin(gamma)), - make_1d_array( - std::cos(beta), - -(std::cos(alpha)*std::sin(beta)), - std::sin(alpha)*std::sin(beta)) - ); +static Array<2, float> +make_orthogonal_matrix(const float alpha, const float beta, const float gamma) { + return make_array(make_1d_array(std::sin(beta) * std::sin(gamma), + std::cos(gamma) * std::sin(alpha) + std::cos(alpha) * std::cos(beta) * std::sin(gamma), + std::cos(alpha) * std::cos(gamma) - std::cos(beta) * std::sin(alpha) * std::sin(gamma)), + make_1d_array(std::cos(gamma) * std::sin(beta), + std::cos(alpha) * std::cos(beta) * std::cos(gamma) - std::sin(alpha) * std::sin(gamma), + -(std::cos(beta) * std::cos(gamma) * std::sin(alpha)) - std::cos(alpha) * std::sin(gamma)), + make_1d_array(std::cos(beta), -(std::cos(alpha) * std::sin(beta)), std::sin(alpha) * std::sin(beta))); } void -MatrixTests:: -run_tests() -{ +MatrixTests::run_tests() { std::cerr << "Testing numerics/MatrixFunction.h functions\n"; run_tests_1D(); run_tests_2D(); @@ -91,283 +83,176 @@ run_tests() } void -MatrixTests:: -run_tests_1D() -{ +MatrixTests::run_tests_1D() { std::cerr << "Testing 1D stuff" << std::endl; { - const Array<1,float> v = make_1d_array(1.F,2.F,3.F,-5.F); - check_if_equal(norm(v),static_cast(std::sqrt(square(1.F)+square(2)+square(3)+square(5))), - "norm of float array"); - check_if_equal(inner_product(v,v),square(1.F)+square(2)+square(3)+square(5), - "inner_product of float array with itself"); - const Array<1,float> v2 = make_1d_array(7.F,8.F,3.3F,-5.F); - check_if_equal(inner_product(v,v2),1.F*7+2*8+3*3.3F+25, - "inner_product of float arrays"); + const Array<1, float> v = make_1d_array(1.F, 2.F, 3.F, -5.F); + check_if_equal(norm(v), static_cast(std::sqrt(square(1.F) + square(2) + square(3) + square(5))), + "norm of float array"); + check_if_equal(inner_product(v, v), square(1.F) + square(2) + square(3) + square(5), + "inner_product of float array with itself"); + const Array<1, float> v2 = make_1d_array(7.F, 8.F, 3.3F, -5.F); + check_if_equal(inner_product(v, v2), 1.F * 7 + 2 * 8 + 3 * 3.3F + 25, "inner_product of float arrays"); } { - typedef std::complex complex_t; - const Array<1,complex_t > v = make_1d_array(complex_t(1.F,0.F), - complex_t(2.F,-3.F)); - check_if_equal(norm(v),static_cast(std::sqrt(square(1.F)+square(2)+square(3))), - "norm of complex array"); - check_if_equal(inner_product(v,v), - complex_t(square(1.F)+square(2)+square(3),0.F), - "inner_product of complex array with itself"); - const Array<1,complex_t > v2 = make_1d_array(complex_t(1.F,1.F), - complex_t(4.F,5.F)); - check_if_equal(inner_product(v,v2), - std::conj(v[0])*v2[0]+std::conj(v[1])*v2[1], - "inner_product of complex arrays"); + typedef std::complex complex_t; + const Array<1, complex_t> v = make_1d_array(complex_t(1.F, 0.F), complex_t(2.F, -3.F)); + check_if_equal(norm(v), static_cast(std::sqrt(square(1.F) + square(2) + square(3))), "norm of complex array"); + check_if_equal(inner_product(v, v), complex_t(square(1.F) + square(2) + square(3), 0.F), + "inner_product of complex array with itself"); + const Array<1, complex_t> v2 = make_1d_array(complex_t(1.F, 1.F), complex_t(4.F, 5.F)); + check_if_equal(inner_product(v, v2), std::conj(v[0]) * v2[0] + std::conj(v[1]) * v2[1], "inner_product of complex arrays"); } } void -MatrixTests:: -run_tests_2D() -{ +MatrixTests::run_tests_2D() { std::cerr << "Testing 2D stuff" << std::endl; - const Array<2,float> m1 = - make_array(make_1d_array(3.F,4.F), - make_1d_array(5.F,6.F), - make_1d_array(1.5F,-4.6F)); + const Array<2, float> m1 = make_array(make_1d_array(3.F, 4.F), make_1d_array(5.F, 6.F), make_1d_array(1.5F, -4.6F)); // matrix*vector { - const Array<1,float> v = make_1d_array(1.F,2.F); - check_if_equal(matrix_multiply(m1,v), - make_1d_array(m1[0][0]*v[0]+m1[0][1]*v[1], - m1[1][0]*v[0]+m1[1][1]*v[1], - m1[2][0]*v[0]+m1[2][1]*v[1]), - "matrix times vector"); + const Array<1, float> v = make_1d_array(1.F, 2.F); + check_if_equal( + matrix_multiply(m1, v), + make_1d_array(m1[0][0] * v[0] + m1[0][1] * v[1], m1[1][0] * v[0] + m1[1][1] * v[1], m1[2][0] * v[0] + m1[2][1] * v[1]), + "matrix times vector"); } // matrix*matrix { - const Array<2,float> m2 = - make_array(make_1d_array(1.F,4.3F,6.F,8.F), - make_1d_array(-5.F,6.5F,2.F,5.F)); - check_if_equal(matrix_multiply(m1,m2), - make_array(make_1d_array(m1[0][0]*m2[0][0]+m1[0][1]*m2[1][0], - m1[0][0]*m2[0][1]+m1[0][1]*m2[1][1], - m1[0][0]*m2[0][2]+m1[0][1]*m2[1][2], - m1[0][0]*m2[0][3]+m1[0][1]*m2[1][3]), - make_1d_array(m1[1][0]*m2[0][0]+m1[1][1]*m2[1][0], - m1[1][0]*m2[0][1]+m1[1][1]*m2[1][1], - m1[1][0]*m2[0][2]+m1[1][1]*m2[1][2], - m1[1][0]*m2[0][3]+m1[1][1]*m2[1][3]), - make_1d_array(m1[2][0]*m2[0][0]+m1[2][1]*m2[1][0], - m1[2][0]*m2[0][1]+m1[2][1]*m2[1][1], - m1[2][0]*m2[0][2]+m1[2][1]*m2[1][2], - m1[2][0]*m2[0][3]+m1[2][1]*m2[1][3])), - "matrix times matrix"); + const Array<2, float> m2 = make_array(make_1d_array(1.F, 4.3F, 6.F, 8.F), make_1d_array(-5.F, 6.5F, 2.F, 5.F)); + check_if_equal( + matrix_multiply(m1, m2), + make_array(make_1d_array(m1[0][0] * m2[0][0] + m1[0][1] * m2[1][0], m1[0][0] * m2[0][1] + m1[0][1] * m2[1][1], + m1[0][0] * m2[0][2] + m1[0][1] * m2[1][2], m1[0][0] * m2[0][3] + m1[0][1] * m2[1][3]), + make_1d_array(m1[1][0] * m2[0][0] + m1[1][1] * m2[1][0], m1[1][0] * m2[0][1] + m1[1][1] * m2[1][1], + m1[1][0] * m2[0][2] + m1[1][1] * m2[1][2], m1[1][0] * m2[0][3] + m1[1][1] * m2[1][3]), + make_1d_array(m1[2][0] * m2[0][0] + m1[2][1] * m2[1][0], m1[2][0] * m2[0][1] + m1[2][1] * m2[1][1], + m1[2][0] * m2[0][2] + m1[2][1] * m2[1][2], m1[2][0] * m2[0][3] + m1[2][1] * m2[1][3])), + "matrix times matrix"); } // transpose { - const Array<2,float> m1_trans = - make_array(make_1d_array(m1[0][0],m1[1][0],m1[2][0]), - make_1d_array(m1[0][1],m1[1][1],m1[2][1])); - check_if_equal(matrix_transpose(m1), m1_trans, - "matrix transposition"); + const Array<2, float> m1_trans = + make_array(make_1d_array(m1[0][0], m1[1][0], m1[2][0]), make_1d_array(m1[0][1], m1[1][1], m1[2][1])); + check_if_equal(matrix_transpose(m1), m1_trans, "matrix transposition"); } // diagonal_matrix { - const Array<2,float> d = - diagonal_matrix(2, 3.F); - check_if_equal(d, - make_array(make_1d_array(3.F,0.F), - make_1d_array(0.F,3.F)), - "diagonal_matrix with all diag-elems equal"); - const Array<2,float> d2 = - diagonal_matrix(Coordinate2D(3.F,4.F)); - check_if_equal(d2, - make_array(make_1d_array(3.F,0.F), - make_1d_array(0.F,4.F)), - "diagonal_matrix with differing diag-elems"); + const Array<2, float> d = diagonal_matrix(2, 3.F); + check_if_equal(d, make_array(make_1d_array(3.F, 0.F), make_1d_array(0.F, 3.F)), "diagonal_matrix with all diag-elems equal"); + const Array<2, float> d2 = diagonal_matrix(Coordinate2D(3.F, 4.F)); + check_if_equal(d2, make_array(make_1d_array(3.F, 0.F), make_1d_array(0.F, 4.F)), "diagonal_matrix with differing diag-elems"); } - } - void -MatrixTests:: -run_tests_max_eigenvector() -{ +MatrixTests::run_tests_max_eigenvector() { std::cerr << "Testing max_eigenvector stuff" << std::endl; set_tolerance(.01); float max_eigenvalue; - Array<1,float> max_eigenvector; - { - { - const Array<2,float> d = - diagonal_matrix(Coordinate3D(3.F,4.F,-2.F)); - Succeeded success = - absolute_max_eigenvector_using_power_method(max_eigenvalue, - max_eigenvector, - d, - make_1d_array(1.F,2.F,3.F), - /*tolerance=*/ .001, - 1000UL); - check(success == Succeeded::yes, - "abs_max_using_power: succeeded (float diagonal matrix)"); - - check_if_equal(max_eigenvalue, 4.F, - "abs_max_using_power: eigenvalue (float diagonal matrix)"); - check_if_equal(max_eigenvector, make_1d_array(0.F,1.F,0.F), - "abs_max_using_power: eigenvector (float diagonal matrix)"); - success = - absolute_max_eigenvector_using_shifted_power_method(max_eigenvalue, - max_eigenvector, - d, - make_1d_array(1.F,2.F,3.F), - .5F, // note: shift should be small enough that it doesn't make the most negative eigenvalue 'larger' - /*tolerance=*/ .001, - 1000UL); - check(success == Succeeded::yes, - "abs_max_using_shifted_power: succeeded (float diagonal matrix)"); - - check_if_equal(max_eigenvalue, 4.F, - "abs_max_using_shifted_power: eigenvalue (float diagonal matrix)"); - check_if_equal(max_eigenvector, make_1d_array(0.F,1.F,0.F), - "abs_max_using_shifted_power: eigenvector (float diagonal matrix)"); - - success = - max_eigenvector_using_power_method(max_eigenvalue, - max_eigenvector, - d, - make_1d_array(1.F,-2.F,-3.F), - /*tolerance=*/ .001, - 1000UL); - check(success == Succeeded::yes, - "max_using_power: succeeded (float diagonal matrix)"); - - check_if_equal(max_eigenvalue, 4.F, - "max_using_power: eigenvalue (float diagonal matrix)"); - check_if_equal(max_eigenvector, make_1d_array(0.F,1.F,0.F), - "max_using_power: eigenvector (float diagonal matrix)"); - - success = - max_eigenvector_using_power_method(max_eigenvalue, - max_eigenvector, - Array<2,float>(d*(-1.F)), - make_1d_array(1.F,2.F,3.F), - /*tolerance=*/ .001, - 1000UL); - check(success == Succeeded::yes, - "max_using_power: succeeded (float diagonal matrix with large negative value)"); - - check_if_equal(max_eigenvalue, 2.F, - "max_using_power: eigenvalue (float diagonal matrix with large negative value)"); - check_if_equal(max_eigenvector, make_1d_array(0.F,0.F,1.F), - "max_using_power: eigenvector (float diagonal matrix with large negative value)"); - } - } + Array<1, float> max_eigenvector; + {{const Array<2, float> d = diagonal_matrix(Coordinate3D(3.F, 4.F, -2.F)); + Succeeded success = + absolute_max_eigenvector_using_power_method(max_eigenvalue, max_eigenvector, d, make_1d_array(1.F, 2.F, 3.F), + /*tolerance=*/.001, 1000UL); + check(success == Succeeded::yes, "abs_max_using_power: succeeded (float diagonal matrix)"); - { - const float pi2=static_cast(_PI/2); - const Array<2,float> rotation = - // make_orthogonal_matrix(.2F,.4F,-1.F); - make_orthogonal_matrix(pi2,pi2,pi2); - std::cerr << rotation; - check_if_equal(matrix_multiply(rotation, matrix_transpose(rotation)), - diagonal_matrix(3,1.F), - "construct orthogonal matrix O.O^t"); - check_if_equal(matrix_multiply(matrix_transpose(rotation), rotation), - diagonal_matrix(3,1.F), - "construct orthogonal matrix O^t.O"); - - const Array<2,float> d = - diagonal_matrix(Coordinate3D(3.F,4.F,-2.F)); - - const Array<2,float> m = - matrix_multiply(rotation, matrix_multiply(d, matrix_transpose(rotation))); - Array<1,float> the_max_eigenvector = - matrix_multiply(rotation,make_1d_array(0.F,1.F,0.F)); - the_max_eigenvector /= (*abs_max_element(the_max_eigenvector.begin(), the_max_eigenvector.end())); - - // now repetition of tests with diagonal matrix - Succeeded success = - absolute_max_eigenvector_using_power_method(max_eigenvalue, - max_eigenvector, - m, - make_1d_array(1.F,2.F,3.F), - /*tolerance=*/ .001, - 1000UL); - check(success == Succeeded::yes, - "abs_max_using_power: succeeded (float non-diagonal matrix)"); - - check_if_equal(max_eigenvalue, 4.F, - "abs_max_using_power: eigenvalue (float non-diagonal matrix)"); - check_if_equal(max_eigenvector, the_max_eigenvector, - "abs_max_using_power: eigenvector (float non-diagonal matrix)"); - success = - absolute_max_eigenvector_using_shifted_power_method(max_eigenvalue, - max_eigenvector, - m, - make_1d_array(1.F,2.F,3.F), - .5F, // note: shift should be small enough that it doesn't make the most negative eigenvalue 'larger' - /*tolerance=*/ .001, - 1000UL); - check(success == Succeeded::yes, - "abs_max_using_shifted_power: succeeded (float non-diagonal matrix)"); - - check_if_equal(max_eigenvalue, 4.F, - "abs_max_using_shifted_power: eigenvalue (float non-diagonal matrix)"); - check_if_equal(max_eigenvector, the_max_eigenvector, - "abs_max_using_shifted_power: eigenvector (float non-diagonal matrix)"); - - success = - max_eigenvector_using_power_method(max_eigenvalue, - max_eigenvector, - m, - make_1d_array(1.F,-2.F,-3.F), - /*tolerance=*/ .001, - 1000UL); - check(success == Succeeded::yes, - "max_using_power: succeeded (float non-diagonal matrix)"); - - check_if_equal(max_eigenvalue, 4.F, - "max_using_power: eigenvalue (float non-diagonal matrix)"); - check_if_equal(max_eigenvector, the_max_eigenvector, - "max_using_power: eigenvector (float non-diagonal matrix)"); - - success = - max_eigenvector_using_power_method(max_eigenvalue, - max_eigenvector, - Array<2,float>(m*(-1.F)), - make_1d_array(1.F,2.F,3.F), - /*tolerance=*/ .001, - 1000UL); - check(success == Succeeded::yes, - "max_using_power: succeeded (float non-diagonal matrix with large negative value)"); - - check_if_equal(max_eigenvalue, 2.F, - "max_using_power: eigenvalue (float non-diagonal matrix with large negative value)"); - check_if_equal(max_eigenvector, matrix_multiply(rotation,make_1d_array(0.F,0.F,1.F)), - "max_using_power: eigenvector (float non-diagonal matrix with large negative value)"); - } + check_if_equal(max_eigenvalue, 4.F, "abs_max_using_power: eigenvalue (float diagonal matrix)"); + check_if_equal(max_eigenvector, make_1d_array(0.F, 1.F, 0.F), "abs_max_using_power: eigenvector (float diagonal matrix)"); + success = absolute_max_eigenvector_using_shifted_power_method( + max_eigenvalue, max_eigenvector, d, make_1d_array(1.F, 2.F, 3.F), + .5F, // note: shift should be small enough that it doesn't make the most negative eigenvalue 'larger' + /*tolerance=*/.001, 1000UL); + check(success == Succeeded::yes, "abs_max_using_shifted_power: succeeded (float diagonal matrix)"); - { - // now test for a case where the power-method fails - const Array<2,float> d = - diagonal_matrix(Coordinate2D(3.F,-3.F)); - Succeeded success = - absolute_max_eigenvector_using_power_method(max_eigenvalue, - max_eigenvector, - d, - make_1d_array(1.F,2.F), - /*tolerance=*/ .001, - 100UL); - check(success == Succeeded::no, - "abs_max_using_power should have failed (float diagonal matrix with opposite max eigenvalues)"); - } + check_if_equal(max_eigenvalue, 4.F, "abs_max_using_shifted_power: eigenvalue (float diagonal matrix)"); + check_if_equal(max_eigenvector, make_1d_array(0.F, 1.F, 0.F), + "abs_max_using_shifted_power: eigenvector (float diagonal matrix)"); + + success = max_eigenvector_using_power_method(max_eigenvalue, max_eigenvector, d, make_1d_array(1.F, -2.F, -3.F), + /*tolerance=*/.001, 1000UL); + check(success == Succeeded::yes, "max_using_power: succeeded (float diagonal matrix)"); + + check_if_equal(max_eigenvalue, 4.F, "max_using_power: eigenvalue (float diagonal matrix)"); + check_if_equal(max_eigenvector, make_1d_array(0.F, 1.F, 0.F), "max_using_power: eigenvector (float diagonal matrix)"); + + success = max_eigenvector_using_power_method(max_eigenvalue, max_eigenvector, Array<2, float>(d * (-1.F)), + make_1d_array(1.F, 2.F, 3.F), + /*tolerance=*/.001, 1000UL); + check(success == Succeeded::yes, "max_using_power: succeeded (float diagonal matrix with large negative value)"); + + check_if_equal(max_eigenvalue, 2.F, "max_using_power: eigenvalue (float diagonal matrix with large negative value)"); + check_if_equal(max_eigenvector, make_1d_array(0.F, 0.F, 1.F), + "max_using_power: eigenvector (float diagonal matrix with large negative value)"); +} } -END_NAMESPACE_STIR +{ + const float pi2 = static_cast(_PI / 2); + const Array<2, float> rotation = + // make_orthogonal_matrix(.2F,.4F,-1.F); + make_orthogonal_matrix(pi2, pi2, pi2); + std::cerr << rotation; + check_if_equal(matrix_multiply(rotation, matrix_transpose(rotation)), diagonal_matrix(3, 1.F), + "construct orthogonal matrix O.O^t"); + check_if_equal(matrix_multiply(matrix_transpose(rotation), rotation), diagonal_matrix(3, 1.F), + "construct orthogonal matrix O^t.O"); + + const Array<2, float> d = diagonal_matrix(Coordinate3D(3.F, 4.F, -2.F)); + + const Array<2, float> m = matrix_multiply(rotation, matrix_multiply(d, matrix_transpose(rotation))); + Array<1, float> the_max_eigenvector = matrix_multiply(rotation, make_1d_array(0.F, 1.F, 0.F)); + the_max_eigenvector /= (*abs_max_element(the_max_eigenvector.begin(), the_max_eigenvector.end())); + + // now repetition of tests with diagonal matrix + Succeeded success = + absolute_max_eigenvector_using_power_method(max_eigenvalue, max_eigenvector, m, make_1d_array(1.F, 2.F, 3.F), + /*tolerance=*/.001, 1000UL); + check(success == Succeeded::yes, "abs_max_using_power: succeeded (float non-diagonal matrix)"); + + check_if_equal(max_eigenvalue, 4.F, "abs_max_using_power: eigenvalue (float non-diagonal matrix)"); + check_if_equal(max_eigenvector, the_max_eigenvector, "abs_max_using_power: eigenvector (float non-diagonal matrix)"); + success = absolute_max_eigenvector_using_shifted_power_method( + max_eigenvalue, max_eigenvector, m, make_1d_array(1.F, 2.F, 3.F), + .5F, // note: shift should be small enough that it doesn't make the most negative eigenvalue 'larger' + /*tolerance=*/.001, 1000UL); + check(success == Succeeded::yes, "abs_max_using_shifted_power: succeeded (float non-diagonal matrix)"); + check_if_equal(max_eigenvalue, 4.F, "abs_max_using_shifted_power: eigenvalue (float non-diagonal matrix)"); + check_if_equal(max_eigenvector, the_max_eigenvector, "abs_max_using_shifted_power: eigenvector (float non-diagonal matrix)"); + + success = max_eigenvector_using_power_method(max_eigenvalue, max_eigenvector, m, make_1d_array(1.F, -2.F, -3.F), + /*tolerance=*/.001, 1000UL); + check(success == Succeeded::yes, "max_using_power: succeeded (float non-diagonal matrix)"); + + check_if_equal(max_eigenvalue, 4.F, "max_using_power: eigenvalue (float non-diagonal matrix)"); + check_if_equal(max_eigenvector, the_max_eigenvector, "max_using_power: eigenvector (float non-diagonal matrix)"); + + success = max_eigenvector_using_power_method(max_eigenvalue, max_eigenvector, Array<2, float>(m * (-1.F)), + make_1d_array(1.F, 2.F, 3.F), + /*tolerance=*/.001, 1000UL); + check(success == Succeeded::yes, "max_using_power: succeeded (float non-diagonal matrix with large negative value)"); + + check_if_equal(max_eigenvalue, 2.F, "max_using_power: eigenvalue (float non-diagonal matrix with large negative value)"); + check_if_equal(max_eigenvector, matrix_multiply(rotation, make_1d_array(0.F, 0.F, 1.F)), + "max_using_power: eigenvector (float non-diagonal matrix with large negative value)"); +} -int main() { + // now test for a case where the power-method fails + const Array<2, float> d = diagonal_matrix(Coordinate2D(3.F, -3.F)); + Succeeded success = absolute_max_eigenvector_using_power_method(max_eigenvalue, max_eigenvector, d, make_1d_array(1.F, 2.F), + /*tolerance=*/.001, 100UL); + check(success == Succeeded::no, "abs_max_using_power should have failed (float diagonal matrix with opposite max eigenvalues)"); +} +} + +END_NAMESPACE_STIR + +int +main() { stir::MatrixTests tests; tests.run_tests(); return tests.main_return_value(); diff --git a/src/test/numerics/test_overlap_interpolate.cxx b/src/test/numerics/test_overlap_interpolate.cxx index e9438de93d..a9122fb614 100644 --- a/src/test/numerics/test_overlap_interpolate.cxx +++ b/src/test/numerics/test_overlap_interpolate.cxx @@ -35,116 +35,85 @@ START_NAMESPACE_STIR - /*! \brief Test class for stir::overlap_interpolate \ingroup numerics_test */ -class overlap_interpolateTests : public RunTests -{ +class overlap_interpolateTests : public RunTests { public: void run_tests(); + private: #ifndef STIR_OVERLAP_NORMALISATION template - static - void - divide_by_out_size( - ValueT& outvalues, - const BoundaryT& outboundaries) - { + static void divide_by_out_size(ValueT& outvalues, const BoundaryT& outboundaries) { typename ValueT::iterator iter = outvalues.begin(); typename BoundaryT::const_iterator bound_iter = outboundaries.begin(); - for (; iter != outvalues.end(); ++iter, ++bound_iter) - { - *iter /= (*(bound_iter+1)) - (*bound_iter); - } + for (; iter != outvalues.end(); ++iter, ++bound_iter) { + *iter /= (*(bound_iter + 1)) - (*bound_iter); + } } #endif template - Succeeded - test_case(const ValueT& invalues, - const BoundaryT& inboundaries, - const ValueT& outvalues, - const BoundaryT& outboundaries, - const char * const description) - { + Succeeded test_case(const ValueT& invalues, const BoundaryT& inboundaries, const ValueT& outvalues, + const BoundaryT& outboundaries, const char* const description) { ValueT my_outvalues = outvalues; - overlap_interpolate(my_outvalues.begin(), my_outvalues.end(), - outboundaries.begin(), outboundaries.end(), - invalues.begin(), invalues.end(), - inboundaries.begin(), inboundaries.end()); + overlap_interpolate(my_outvalues.begin(), my_outvalues.end(), outboundaries.begin(), outboundaries.end(), invalues.begin(), + invalues.end(), inboundaries.begin(), inboundaries.end()); #ifndef STIR_OVERLAP_NORMALISATION divide_by_out_size(my_outvalues, outboundaries); #endif - const bool ret = check_if_equal(my_outvalues, outvalues,description); + const bool ret = check_if_equal(my_outvalues, outvalues, description); if (!ret) std::cerr << "\nres: " << my_outvalues << "should be " << outvalues; - return - ret ? Succeeded::yes : Succeeded::no; + return ret ? Succeeded::yes : Succeeded::no; } template - Succeeded - uniform_test_case(const ValueT& invalues, - const float zoom, const float offset, - const ValueT& outvalues, - const char * const description) - { + Succeeded uniform_test_case(const ValueT& invalues, const float zoom, const float offset, const ValueT& outvalues, + const char* const description) { using namespace boost::lambda; ValueT my_outvalues = outvalues; - overlap_interpolate(my_outvalues, - invalues, zoom, offset); + overlap_interpolate(my_outvalues, invalues, zoom, offset); #ifndef STIR_OVERLAP_NORMALISATION std::for_each(my_outvalues.begin(), my_outvalues.end(), _1 *= zoom); #endif - const bool ret = check_if_equal(my_outvalues, outvalues,description); + const bool ret = check_if_equal(my_outvalues, outvalues, description); if (!ret) std::cerr << "\nres: " << my_outvalues << "should be " << outvalues << '\n'; - return - ret ? Succeeded::yes : Succeeded::no; + return ret ? Succeeded::yes : Succeeded::no; } - Succeeded - test_case_1d(const char * const input, - const char * const description) - { + Succeeded test_case_1d(const char* const input, const char* const description) { std::istringstream s(input); VectorWithOffset invalues, inboundaries, outvalues, outboundaries; s >> invalues >> inboundaries >> outvalues >> outboundaries; return test_case(invalues, inboundaries, outvalues, outboundaries, description); } - - Succeeded - uniform_test_case_1d(const char * const input, - const char * const description) - { + Succeeded uniform_test_case_1d(const char* const input, const char* const description) { std::istringstream s(input); VectorWithOffset invalues, outvalues; int instartindex, outstartindex, outendindex; float zoom, offset; char out[10000]; - s >> invalues - >> instartindex >> outstartindex >> outendindex - >> zoom >> offset - >> outvalues; + s >> invalues >> instartindex >> outstartindex >> outendindex >> zoom >> offset >> outvalues; invalues.set_min_index(instartindex); - VectorWithOffset inboundaries(invalues.get_min_index(), invalues.get_max_index()+1); - for (int i=inboundaries.get_min_index(); i<=inboundaries.get_max_index(); ++i) - inboundaries[i]=i-.5F; + VectorWithOffset inboundaries(invalues.get_min_index(), invalues.get_max_index() + 1); + for (int i = inboundaries.get_min_index(); i <= inboundaries.get_max_index(); ++i) + inboundaries[i] = i - .5F; outvalues.set_min_index(outstartindex); sprintf(out, "%s: inconsistent index sizes. Check test program", description); - if (!check_if_equal(outvalues.get_max_index(), outendindex-1, out)) - return Succeeded::no; + if (!check_if_equal(outvalues.get_max_index(), outendindex - 1, out)) + return Succeeded::no; VectorWithOffset outboundaries(outstartindex, outendindex); - for (int i=outboundaries.get_min_index(); i<=outboundaries.get_max_index(); ++i) - outboundaries[i]=(i-.5F)/zoom+offset; + for (int i = outboundaries.get_min_index(); i <= outboundaries.get_max_index(); ++i) + outboundaries[i] = (i - .5F) / zoom + offset; sprintf(out, "%s: test general overlap_interpolate", description); if (test_case(invalues, inboundaries, outvalues, outboundaries, out) == Succeeded::no) @@ -155,149 +124,146 @@ class overlap_interpolateTests : public RunTests return Succeeded::no; return Succeeded::yes; } - }; void -overlap_interpolateTests::run_tests() -{ +overlap_interpolateTests::run_tests() { std::cerr << "Tests for overlap_interpolate\n" - << "Everythings is fine if the program runs without any output." << std::endl; - + << "Everythings is fine if the program runs without any output." << std::endl; + set_tolerance(.0005); test_case_1d("{1,2.1,-3,4}" - "{2,3,4,5,6}" - "{1,2.1,-3,4}" - "{2,3,4,5,6}", - "equal boundaries"); + "{2,3,4,5,6}" + "{1,2.1,-3,4}" + "{2,3,4,5,6}", + "equal boundaries"); test_case_1d("{1.1,2,3,4.5}" - "{2,3,4,5,7}" - "{0,1.1,2,3,4.5}" - "{-1,2,3,4,5,7}", - "equal boundaries, but longer at start"); + "{2,3,4,5,7}" + "{0,1.1,2,3,4.5}" + "{-1,2,3,4,5,7}", + "equal boundaries, but longer at start"); test_case_1d("{1.1,2,3,4.5}" - "{2,3,4,5,7}" - "{1.1,2,3,4.5,0}" - "{2,3,4,5,7,10}", - "equal boundaries, but longer at end"); + "{2,3,4,5,7}" + "{1.1,2,3,4.5,0}" + "{2,3,4,5,7,10}", + "equal boundaries, but longer at end"); test_case_1d("{1.1,2,3,4.5}" - "{2,3,4,5,7}" - "{0,1.1,1.1,2,2,3,3,4.5,4.5,4.5,4.5}" - "{-1,2,2.5,3,3.3,4,4.8,5,5.1,5.2,5.6,7}", - "multiple out boxes in each in box"); + "{2,3,4,5,7}" + "{0,1.1,1.1,2,2,3,3,4.5,4.5,4.5,4.5}" + "{-1,2,2.5,3,3.3,4,4.8,5,5.1,5.2,5.6,7}", + "multiple out boxes in each in box"); test_case_1d("{1,5,2,16,7,4,2,4}" - "{-2,5,6,6.5,7,86,101,110,130}" - "{0.,0.2,1.,1.,1.,5.8,7.,7.,7.,6.53448,1.28421,0.,0.}" - "{-30,-4,-1.5,1,1.5,4,9,13,18,37,95,190,210,250}", - "arbitrary sizes case 1"); + "{-2,5,6,6.5,7,86,101,110,130}" + "{0.,0.2,1.,1.,1.,5.8,7.,7.,7.,6.53448,1.28421,0.,0.}" + "{-30,-4,-1.5,1,1.5,4,9,13,18,37,95,190,210,250}", + "arbitrary sizes case 1"); test_case_1d("{-1,2.5,2,1.6,7,-4,4}" - "{-2.1,5.2,6.3,6.5,7.6,7.8,11,11.7}" - "{-0.516667,-0.0416667,1.6,1.55556}" - "{-5,1,7,7.1,8}", - "arbitrary sizes case 2"); + "{-2.1,5.2,6.3,6.5,7.6,7.8,11,11.7}" + "{-0.516667,-0.0416667,1.6,1.55556}" + "{-5,1,7,7.1,8}", + "arbitrary sizes case 2"); test_case_1d("{-1,2.56,2,11.6,7.3,-4,4}" - "{-2.1,5.2,6.3,6.5,7.6,7.8,11,11.7}" - "{-0.0333333,-1.,-1.,-1.,-0.4304,4.10182,9.58333,-4.,-4.,-2.,0.,0.}" - "{-5,-2,0,1,3.1,5.6,6.7,7.9,8.3,8.9,11.7,13,15}", - "arbitrary sizes case 3"); + "{-2.1,5.2,6.3,6.5,7.6,7.8,11,11.7}" + "{-0.0333333,-1.,-1.,-1.,-0.4304,4.10182,9.58333,-4.,-4.,-2.,0.,0.}" + "{-5,-2,0,1,3.1,5.6,6.7,7.9,8.3,8.9,11.7,13,15}", + "arbitrary sizes case 3"); test_case_1d("{0.594221,0.932554,0.930552,0.479434,0.44984,0.426074,0.574378,\ 0.893291}" - "{0,0.0253037,0.889616,1.52744,2.47386,3.11161,3.56033,4.02598,4.94703}" - "{0.923744,0.930552,0.509265,0.457499,0.44984}" - "{0,0.995915,1.46923,2.34955,2.82987,2.96136}", - "random case 1"); + "{0,0.0253037,0.889616,1.52744,2.47386,3.11161,3.56033,4.02598,4.94703}" + "{0.923744,0.930552,0.509265,0.457499,0.44984}" + "{0,0.995915,1.46923,2.34955,2.82987,2.96136}", + "random case 1"); test_case_1d("{0.43649,0.236381,0.978282,0.537264,0.503936,0.305829,0.498848,\ 0.0874235}" - "{0,0.0778616,0.809312,1.41487,1.47699,1.69054,1.78416,2.4433,2.86767}" - "{0.256752,0.925738,0.493403,0.387417,0.0874235,0.00196432,0.,0.,0.}" - "{0,0.764837,1.3928,2.13089,2.55935,2.85087,3.59851,3.85628,4.15325,5.00828}",\ + "{0,0.0778616,0.809312,1.41487,1.47699,1.69054,1.78416,2.4433,2.86767}" + "{0.256752,0.925738,0.493403,0.387417,0.0874235,0.00196432,0.,0.,0.}" + "{0,0.764837,1.3928,2.13089,2.55935,2.85087,3.59851,3.85628,4.15325,5.00828}", - "random case 2"); + "random case 2"); test_case_1d("{0.511261,0.27949,0.759702,0.351098,0.205432,0.780642,0.672278,\ 0.273237}" - "{0,0.473981,0.649066,1.25922,1.31891,1.69927,2.21521,2.40101,2.69586}" - "{0.491445,0.577761,0.641911,0.672278,0.672278,0.29108,0.}" - "{0,0.75239,1.53024,2.28759,2.29091,2.29566,2.81574,3.27612}", - "random case 3"); + "{0,0.473981,0.649066,1.25922,1.31891,1.69927,2.21521,2.40101,2.69586}" + "{0.491445,0.577761,0.641911,0.672278,0.672278,0.29108,0.}" + "{0,0.75239,1.53024,2.28759,2.29091,2.29566,2.81574,3.27612}", + "random case 3"); test_case_1d("{0.148291,0.493487,0.240592,0.700675,0.797193,0.288055,0.459951,\ 0.0283963,0.523956}" - "{0,0.814074,1.09894,1.51718,1.98145,2.41516,3.18408,3.41653,3.58595,4.26728}\ + "{0,0.814074,1.09894,1.51718,1.98145,2.41516,3.18408,3.41653,3.58595,4.26728}\ " - "{0.209939,0.298013,0.559381,0.74738,0.40299,0.288055,0.288055}" - "{0,0.991066,1.46617,1.63226,2.30884,2.77983,2.79455,2.81236}", - "random case 4"); + "{0.209939,0.298013,0.559381,0.74738,0.40299,0.288055,0.288055}" + "{0,0.991066,1.46617,1.63226,2.30884,2.77983,2.79455,2.81236}", + "random case 4"); test_case_1d("{0.183092,0.230392,0.314051,0.220611,0.895037}" - "{0,0.770442,1.0561,1.75275,1.83371,2.31929}" - "{0.18838,0.246126,0.314051,0.633862,0.}" - "{0,0.867418,1.0998,1.74705,2.4637,3.09868}", - "random case 5"); + "{0,0.770442,1.0561,1.75275,1.83371,2.31929}" + "{0.18838,0.246126,0.314051,0.633862,0.}" + "{0,0.867418,1.0998,1.74705,2.4637,3.09868}", + "random case 5"); test_case_1d("{0.0629649,0.965917,0.72559,0.15987,0.89687,0.289338,0.254605,0.\ 145144}" - "{0,0.879067,0.985312,1.00953,1.84062,2.49907,2.71028,2.96405,3.50949}" - "{0.140638,0.410539,0.178698,0.861756,0.217232,0.0726656,0.,0.}" - "{0,0.961802,1.09205,1.86024,2.53826,3.26768,3.75068,3.80222,3.84527}", - "random case 6"); + "{0,0.879067,0.985312,1.00953,1.84062,2.49907,2.71028,2.96405,3.50949}" + "{0.140638,0.410539,0.178698,0.861756,0.217232,0.0726656,0.,0.}" + "{0,0.961802,1.09205,1.86024,2.53826,3.26768,3.75068,3.80222,3.84527}", + "random case 6"); test_case_1d("{0.666452,0.517083,0.32595,0.883177,0.769582,0.227745,0.0713446,\ 0.738033}" - "{0,0.890515,1.01201,1.05915,1.96609,2.19815,3.10844,3.9018,4.2633}" - "{0.666452,0.626473,0.687369}" - "{0,0.270258,1.0503,1.07547}", - "random case 7"); + "{0,0.890515,1.01201,1.05915,1.96609,2.19815,3.10844,3.9018,4.2633}" + "{0.666452,0.626473,0.687369}" + "{0,0.270258,1.0503,1.07547}", + "random case 7"); test_case_1d("{0.683484,0.540841,0.297046,0.973624,0.640437,0.874389,0.779963,\ 0.647674}" - "{0,0.75726,0.862067,1.41428,1.99061,2.00984,2.22413,2.65485,3.18405}" - "{0.683484,0.57681,0.557915,0.902686,0.701251,0.075033,0.,0.,0.}" - "{0,0.112286,1.09452,1.61495,2.35078,3.10157,3.81354,4.55392,5.2646,5.3319}", - "random case 8"); + "{0,0.75726,0.862067,1.41428,1.99061,2.00984,2.22413,2.65485,3.18405}" + "{0.683484,0.57681,0.557915,0.902686,0.701251,0.075033,0.,0.,0.}" + "{0,0.112286,1.09452,1.61495,2.35078,3.10157,3.81354,4.55392,5.2646,5.3319}", + "random case 8"); uniform_test_case_1d("{1,5,2,16,7,4,2,4}" - "-2 -2 6 1 0" - "{1.,5.,2.,16.,7.,4.,2.,4.}", - "uniform equal in and out"); + "-2 -2 6 1 0" + "{1.,5.,2.,16.,7.,4.,2.,4.}", + "uniform equal in and out"); uniform_test_case_1d("{1,5,2,16,7,4,2,4}" - "-2 -2 3 1 0" - "{1.,5.,2.,16.,7.}", - "uniform equal in and out, but short on right"); + "-2 -2 3 1 0" + "{1.,5.,2.,16.,7.}", + "uniform equal in and out, but short on right"); uniform_test_case_1d("{1,5,2,16,7,4,2,4}" - "-2 0 6 1 0" - "{2.,16.,7.,4.,2.,4.}", - "uniform equal in and out, but short on left"); + "-2 0 6 1 0" + "{2.,16.,7.,4.,2.,4.}", + "uniform equal in and out, but short on left"); uniform_test_case_1d("{1,5,2,16,7,4,2,4}" - "-2 0 10 1 0" - "{2.,16.,7.,4.,2.,4.,0.,0.,0.,0.}", - "uniform equal in and out, but short on left and long on right"); + "-2 0 10 1 0" + "{2.,16.,7.,4.,2.,4.,0.,0.,0.,0.}", + "uniform equal in and out, but short on left and long on right"); uniform_test_case_1d("{1,5,2,16,7,4,2,4}" - "-1 -3 5 1 0" - "{0.,0.,1.,5.,2.,16.,7.,4.}", - "uniform equal in and out, but long on left and short on right"); + "-1 -3 5 1 0" + "{0.,0.,1.,5.,2.,16.,7.,4.}", + "uniform equal in and out, but long on left and short on right"); uniform_test_case_1d("{1,5,2,16,7,4,2,4}" - "-2 0 10 1 -2" - "{1.,5.,2.,16.,7.,4.,2.,4.,0.,0.}", - "uniform equal in and out, offset only"); + "-2 0 10 1 -2" + "{1.,5.,2.,16.,7.,4.,2.,4.,0.,0.}", + "uniform equal in and out, offset only"); uniform_test_case_1d("{1,5,2,16,7,4,2,4}" - "-2 -1 10 0.3 -1" - "{0.,3.2,7.6,1.5,0.,0.,0.,0.,0.,0.,0.}", - "uniform test case 1"); + "-2 -1 10 0.3 -1" + "{0.,3.2,7.6,1.5,0.,0.,0.,0.,0.,0.,0.}", + "uniform test case 1"); uniform_test_case_1d("{1,5,2,16,7,4,2,4}" - "2 1 10 0.3 1.3" - "{7.88,3.42,0.,0.,0.,0.,0.,0.,0.}", - "uniform test case 2"); + "2 1 10 0.3 1.3" + "{7.88,3.42,0.,0.,0.,0.,0.,0.,0.}", + "uniform test case 2"); uniform_test_case_1d("{1,5,2,16,7,4,2,4}" - "-2 -3 10 2.5 -2.2" - "{0.,0.,0.25,1.,1.,4.,5.,4.25,2.,2.,12.5,16.,13.75}", - "uniform test case 3"); + "-2 -3 10 2.5 -2.2" + "{0.,0.,0.25,1.,1.,4.,5.,4.25,2.,2.,12.5,16.,13.75}", + "uniform test case 3"); } - END_NAMESPACE_STIR USING_NAMESPACE_STIR -int main() -{ +int +main() { overlap_interpolateTests tests; tests.run_tests(); return tests.main_return_value(); diff --git a/src/test/test_ArcCorrection.cxx b/src/test/test_ArcCorrection.cxx index 57d9e1ac57..d747c06559 100644 --- a/src/test/test_ArcCorrection.cxx +++ b/src/test/test_ArcCorrection.cxx @@ -49,123 +49,100 @@ START_NAMESPACE_STIR - a geometry test to see that a point in a sinogram is arc-corrected to the correct location. - - a uniformity test that checks that uniform data are arc-corrected to + - a uniformity test that checks that uniform data are arc-corrected to uniform data with the same value. This is currently only done on sinograms and viewgrams. - The tests are performed both at the default arc-corrected bin-size, + The tests are performed both at the default arc-corrected bin-size, but also at twice as large bin-size. */ -class ArcCorrectionTests: public RunTests -{ +class ArcCorrectionTests : public RunTests { public: void run_tests(); void run_tests_tof(); + protected: void run_tests_for_specific_proj_data_info(const ArcCorrection&); }; void -ArcCorrectionTests:: -run_tests_for_specific_proj_data_info(const ArcCorrection& arc_correction) -{ - const ProjDataInfoCylindricalArcCorr& proj_data_info_arc_corr = - arc_correction.get_arc_corrected_proj_data_info(); - const ProjDataInfoCylindricalNoArcCorr& proj_data_info_noarc_corr = - arc_correction.get_not_arc_corrected_proj_data_info(); +ArcCorrectionTests::run_tests_for_specific_proj_data_info(const ArcCorrection& arc_correction) { + const ProjDataInfoCylindricalArcCorr& proj_data_info_arc_corr = arc_correction.get_arc_corrected_proj_data_info(); + const ProjDataInfoCylindricalNoArcCorr& proj_data_info_noarc_corr = arc_correction.get_not_arc_corrected_proj_data_info(); const float sampling_in_s = proj_data_info_arc_corr.get_tangential_sampling(); for (int timing_pos_num = proj_data_info_noarc_corr.get_min_tof_pos_num(); - timing_pos_num <= proj_data_info_noarc_corr.get_max_tof_pos_num(); - ++timing_pos_num) - for (int segment_num=proj_data_info_noarc_corr.get_min_segment_num(); - segment_num<=proj_data_info_noarc_corr.get_max_segment_num(); - ++segment_num) - { - const int axial_pos_num = 0; - Sinogram noarccorr_sinogram = - proj_data_info_noarc_corr.get_empty_sinogram(axial_pos_num, segment_num, false, timing_pos_num); - Sinogram arccorr_sinogram = - proj_data_info_arc_corr.get_empty_sinogram(axial_pos_num, segment_num, false, timing_pos_num); - - for (int view_num=proj_data_info_noarc_corr.get_min_view_num(); - view_num<=proj_data_info_noarc_corr.get_max_view_num(); - view_num+=3) - { - Viewgram noarccorr_viewgram = - proj_data_info_noarc_corr.get_empty_viewgram(view_num, segment_num, false, timing_pos_num); - Viewgram arccorr_viewgram = - proj_data_info_arc_corr.get_empty_viewgram(view_num, segment_num, false, timing_pos_num); - // test geometry by checking if single non-zero value gets put in the right bin - { - for (int tangential_pos_num=proj_data_info_noarc_corr.get_min_tangential_pos_num(); - tangential_pos_num<=proj_data_info_noarc_corr.get_max_tangential_pos_num(); - tangential_pos_num+=4) - { - noarccorr_sinogram.fill(0); - noarccorr_viewgram.fill(0); - noarccorr_viewgram[axial_pos_num][tangential_pos_num]=1; - noarccorr_sinogram[view_num][tangential_pos_num]=1; - arc_correction.do_arc_correction(arccorr_sinogram, noarccorr_sinogram); - arc_correction.do_arc_correction(arccorr_viewgram, noarccorr_viewgram); - check_if_equal(noarccorr_sinogram[view_num], noarccorr_viewgram[axial_pos_num], - "1 line in sinogram and viewgram (geometric test)"); - const int arccorr_tangential_pos_num_at_max= - index_at_maximum(arccorr_viewgram[axial_pos_num]); - - const float noarccorr_s = - proj_data_info_noarc_corr. - get_s(Bin(segment_num,view_num,axial_pos_num,tangential_pos_num,timing_pos_num)); - const float arccorr_s = - proj_data_info_arc_corr. - get_s(Bin(segment_num,view_num,axial_pos_num,arccorr_tangential_pos_num_at_max,timing_pos_num)); - check((arccorr_s - noarccorr_s)/sampling_in_s < 1.1, - "correspondence in location of maximum after arc-correction"); - } - } - // test if uniformity and counts are preserved - { - /* We set a viewgram to 1, and check if the transformed viewgram is also 1 - (except at the boundary). - */ - noarccorr_sinogram.fill(1); - noarccorr_viewgram.fill(1); - arc_correction.do_arc_correction(arccorr_sinogram, noarccorr_sinogram); - arc_correction.do_arc_correction(arccorr_viewgram, noarccorr_viewgram); - check_if_equal(noarccorr_sinogram[view_num], noarccorr_viewgram[axial_pos_num], - "1 line in sinogram and viewgram (uniformity test)"); - - const float max_s = - proj_data_info_noarc_corr. - get_s(Bin(segment_num,view_num,axial_pos_num, - proj_data_info_noarc_corr.get_max_tangential_pos_num())); - const float min_s = - proj_data_info_noarc_corr. - get_s(Bin(segment_num,view_num,axial_pos_num, - proj_data_info_noarc_corr.get_min_tangential_pos_num())); - for (int tangential_pos_num= round(min_s/sampling_in_s)+2; - tangential_pos_num<=round(max_s/sampling_in_s)-2; - ++tangential_pos_num) - check_if_equal(arccorr_viewgram[axial_pos_num][tangential_pos_num], 1.F, - "uniformity"); - } - } + timing_pos_num <= proj_data_info_noarc_corr.get_max_tof_pos_num(); ++timing_pos_num) + for (int segment_num = proj_data_info_noarc_corr.get_min_segment_num(); + segment_num <= proj_data_info_noarc_corr.get_max_segment_num(); ++segment_num) { + const int axial_pos_num = 0; + Sinogram noarccorr_sinogram = + proj_data_info_noarc_corr.get_empty_sinogram(axial_pos_num, segment_num, false, timing_pos_num); + Sinogram arccorr_sinogram = + proj_data_info_arc_corr.get_empty_sinogram(axial_pos_num, segment_num, false, timing_pos_num); + + for (int view_num = proj_data_info_noarc_corr.get_min_view_num(); view_num <= proj_data_info_noarc_corr.get_max_view_num(); + view_num += 3) { + Viewgram noarccorr_viewgram = + proj_data_info_noarc_corr.get_empty_viewgram(view_num, segment_num, false, timing_pos_num); + Viewgram arccorr_viewgram = + proj_data_info_arc_corr.get_empty_viewgram(view_num, segment_num, false, timing_pos_num); + // test geometry by checking if single non-zero value gets put in the right bin + { + for (int tangential_pos_num = proj_data_info_noarc_corr.get_min_tangential_pos_num(); + tangential_pos_num <= proj_data_info_noarc_corr.get_max_tangential_pos_num(); tangential_pos_num += 4) { + noarccorr_sinogram.fill(0); + noarccorr_viewgram.fill(0); + noarccorr_viewgram[axial_pos_num][tangential_pos_num] = 1; + noarccorr_sinogram[view_num][tangential_pos_num] = 1; + arc_correction.do_arc_correction(arccorr_sinogram, noarccorr_sinogram); + arc_correction.do_arc_correction(arccorr_viewgram, noarccorr_viewgram); + check_if_equal(noarccorr_sinogram[view_num], noarccorr_viewgram[axial_pos_num], + "1 line in sinogram and viewgram (geometric test)"); + const int arccorr_tangential_pos_num_at_max = index_at_maximum(arccorr_viewgram[axial_pos_num]); + + const float noarccorr_s = + proj_data_info_noarc_corr.get_s(Bin(segment_num, view_num, axial_pos_num, tangential_pos_num, timing_pos_num)); + const float arccorr_s = proj_data_info_arc_corr.get_s( + Bin(segment_num, view_num, axial_pos_num, arccorr_tangential_pos_num_at_max, timing_pos_num)); + check((arccorr_s - noarccorr_s) / sampling_in_s < 1.1, "correspondence in location of maximum after arc-correction"); + } + } + // test if uniformity and counts are preserved + { + /* We set a viewgram to 1, and check if the transformed viewgram is also 1 + (except at the boundary). + */ + noarccorr_sinogram.fill(1); + noarccorr_viewgram.fill(1); + arc_correction.do_arc_correction(arccorr_sinogram, noarccorr_sinogram); + arc_correction.do_arc_correction(arccorr_viewgram, noarccorr_viewgram); + check_if_equal(noarccorr_sinogram[view_num], noarccorr_viewgram[axial_pos_num], + "1 line in sinogram and viewgram (uniformity test)"); + + const float max_s = proj_data_info_noarc_corr.get_s( + Bin(segment_num, view_num, axial_pos_num, proj_data_info_noarc_corr.get_max_tangential_pos_num())); + const float min_s = proj_data_info_noarc_corr.get_s( + Bin(segment_num, view_num, axial_pos_num, proj_data_info_noarc_corr.get_min_tangential_pos_num())); + for (int tangential_pos_num = round(min_s / sampling_in_s) + 2; tangential_pos_num <= round(max_s / sampling_in_s) - 2; + ++tangential_pos_num) + check_if_equal(arccorr_viewgram[axial_pos_num][tangential_pos_num], 1.F, "uniformity"); + } } + } } - void -ArcCorrectionTests::run_tests() -{ +ArcCorrectionTests::run_tests() { cerr << "-------- Testing ArcCorrection --------\n"; ArcCorrection arc_correction; shared_ptr scanner_ptr(new Scanner(Scanner::E962)); - - shared_ptr proj_data_info_ptr( - ProjDataInfo::ProjDataInfoCTI(scanner_ptr, - /*span*/7, 10,/*views*/ 96, /*tang_pos*/128, /*arc_corrected*/ false)); + + shared_ptr proj_data_info_ptr(ProjDataInfo::ProjDataInfoCTI(scanner_ptr, + /*span*/ 7, 10, /*views*/ 96, /*tang_pos*/ 128, + /*arc_corrected*/ false)); cerr << "Using default range and bin-size\n"; { arc_correction.set_up(proj_data_info_ptr); @@ -173,45 +150,39 @@ ArcCorrectionTests::run_tests() } cerr << "Using non-default range and bin-size\n"; { - arc_correction.set_up(proj_data_info_ptr, - 128, - scanner_ptr->get_default_bin_size()*2); + arc_correction.set_up(proj_data_info_ptr, 128, scanner_ptr->get_default_bin_size() * 2); run_tests_for_specific_proj_data_info(arc_correction); } } void -ArcCorrectionTests::run_tests_tof() -{ - cerr << "-------- Testing ArcCorrection for TOF scanner --------\n"; - ArcCorrection arc_correction; - shared_ptr scanner_ptr(new Scanner(Scanner::PETMR_Signa)); - - shared_ptr proj_data_info_ptr( - ProjDataInfo::ProjDataInfoGE(scanner_ptr, - /*max_delta*/ 5,/*views*/ 112, /*tang_pos*/ 357, /*arc_corrected*/ false, /*tof_mashing_factor*/ 116)); - - cerr << "Using default range and bin-size\n"; - { - arc_correction.set_up(proj_data_info_ptr); - run_tests_for_specific_proj_data_info(arc_correction); - } - cerr << "Using non-default range and bin-size\n"; - { - arc_correction.set_up(proj_data_info_ptr, - 357, - scanner_ptr->get_default_bin_size() * 2); - run_tests_for_specific_proj_data_info(arc_correction); - } +ArcCorrectionTests::run_tests_tof() { + cerr << "-------- Testing ArcCorrection for TOF scanner --------\n"; + ArcCorrection arc_correction; + shared_ptr scanner_ptr(new Scanner(Scanner::PETMR_Signa)); + + shared_ptr proj_data_info_ptr(ProjDataInfo::ProjDataInfoGE(scanner_ptr, + /*max_delta*/ 5, /*views*/ 112, /*tang_pos*/ 357, + /*arc_corrected*/ false, /*tof_mashing_factor*/ 116)); + + cerr << "Using default range and bin-size\n"; + { + arc_correction.set_up(proj_data_info_ptr); + run_tests_for_specific_proj_data_info(arc_correction); + } + cerr << "Using non-default range and bin-size\n"; + { + arc_correction.set_up(proj_data_info_ptr, 357, scanner_ptr->get_default_bin_size() * 2); + run_tests_for_specific_proj_data_info(arc_correction); + } } END_NAMESPACE_STIR - USING_NAMESPACE_STIR -int main() -{ +int +main() { ArcCorrectionTests tests; tests.run_tests(); tests.run_tests_tof(); diff --git a/src/test/test_Array.cxx b/src/test/test_Array.cxx index b738173bd1..80c6ed1905 100644 --- a/src/test/test_Array.cxx +++ b/src/test/test_Array.cxx @@ -19,10 +19,10 @@ See STIR/LICENSE.txt for details */ /*! - \file + \file \ingroup test \ingroup Array - + \brief tests for the stir::Array class \author Kris Thielemans @@ -31,10 +31,10 @@ #ifndef NDEBUG // set to high level of debugging -#ifdef _DEBUG -#undef _DEBUG -#endif -#define _DEBUG 2 +# ifdef _DEBUG +# undef _DEBUG +# endif +# define _DEBUG 2 #endif #include "stir/Array.h" @@ -73,211 +73,195 @@ START_NAMESPACE_STIR namespace detail { - static Array<2,float> test_make_array() - { - return - make_array(make_1d_array(1.F,0.F,0.F), - make_1d_array(0.F,1.F,1.F), - make_1d_array(0.F,-2.F,2.F)); - } +static Array<2, float> +test_make_array() { + return make_array(make_1d_array(1.F, 0.F, 0.F), make_1d_array(0.F, 1.F, 1.F), make_1d_array(0.F, -2.F, 2.F)); } +} // namespace detail /*! \brief Tests Array functionality \ingroup test - \warning Running this will create and delete 2 files with names + \warning Running this will create and delete 2 files with names output.flt and output.other. Existing files with these names will be overwritten. */ -class ArrayTests : public RunTests -{ +class ArrayTests : public RunTests { private: // this function tests the next() function and compare it to using full_iterators // sadly needs to be declared in the class for VC 6.0 template - void - run_tests_on_next(const Array& test) - { + void run_tests_on_next(const Array& test) { // exit if empty array (as do..while() loop would fail) if (test.size() == 0) return; - BasicCoordinate index = get_min_indices(test); + BasicCoordinate index = get_min_indices(test); typename Array::const_full_iterator iter = test.begin_all(); - do - { - check(*iter == test[index], "test on next(): element out of sequence?"); - ++iter; - } - while (next(index, test) && (iter != test.end_all())); - check (iter == test.end_all(), "test on next() : did we cover all elements?"); + do { + check(*iter == test[index], "test on next(): element out of sequence?"); + ++iter; + } while (next(index, test) && (iter != test.end_all())); + check(iter == test.end_all(), "test on next() : did we cover all elements?"); } // functions that runs IO tests for an array of arbitrary dimension // sadly needs to be declared in the class for VC 6.0 template - void run_IO_tests(const Array&t1) - { + void run_IO_tests(const Array& t1) { std::fstream os; std::fstream is; - run_IO_tests_with_file_args(os, is, t1); + run_IO_tests_with_file_args(os, is, t1); FILE* ofptr; FILE* ifptr; - run_IO_tests_with_file_args(ofptr, is, t1); - run_IO_tests_with_file_args(ofptr, ifptr, t1); + run_IO_tests_with_file_args(ofptr, is, t1); + run_IO_tests_with_file_args(ofptr, ifptr, t1); } - template - void run_IO_tests_with_file_args(OFSTREAM& os, IFSTREAM& is, const Array&t1) - { + template + void run_IO_tests_with_file_args(OFSTREAM& os, IFSTREAM& is, const Array& t1) { { open_write_binary(os, "output.flt"); - check(write_data(os,t1)==Succeeded::yes, "write_data could not write array"); + check(write_data(os, t1) == Succeeded::yes, "write_data could not write array"); close_file(os); } - Array t2(t1.get_index_range()); + Array t2(t1.get_index_range()); { open_read_binary(is, "output.flt"); - check(read_data(is,t2)==Succeeded::yes, "read_data could not read from output.flt"); + check(read_data(is, t2) == Succeeded::yes, "read_data could not read from output.flt"); close_file(is); } - check_if_equal(t1 ,t2, "test out/in" ); + check_if_equal(t1, t2, "test out/in"); remove("output.flt"); { open_write_binary(os, "output.flt"); - const Array copy=t1; - check(write_data(os,t1,ByteOrder::swapped)==Succeeded::yes, "write_data could not write array with swapped byte order"); - check_if_equal(t1 ,copy, "test out with byte-swapping didn't change the array" ); + const Array copy = t1; + check(write_data(os, t1, ByteOrder::swapped) == Succeeded::yes, "write_data could not write array with swapped byte order"); + check_if_equal(t1, copy, "test out with byte-swapping didn't change the array"); close_file(os); } { open_read_binary(is, "output.flt"); - check(read_data(is,t2,ByteOrder::swapped)==Succeeded::yes, "read_data could not read from output.flt"); + check(read_data(is, t2, ByteOrder::swapped) == Succeeded::yes, "read_data could not read from output.flt"); close_file(is); } - check_if_equal(t1 ,t2, "test out/in (swapped byte order)" ); + check_if_equal(t1, t2, "test out/in (swapped byte order)"); remove("output.flt"); - cerr <<"\tTests writing as shorts\n"; + cerr << "\tTests writing as shorts\n"; run_IO_tests_mixed(os, is, t1, NumericInfo()); - cerr <<"\tTests writing as floats\n"; + cerr << "\tTests writing as floats\n"; run_IO_tests_mixed(os, is, t1, NumericInfo()); - cerr <<"\tTests writing as signed chars\n"; + cerr << "\tTests writing as signed chars\n"; run_IO_tests_mixed(os, is, t1, NumericInfo()); - /* check on failed IO. Note: needs to be after the others, as we would have to call os.clear() for ostream to be able to write again, but that's not defined for FILE*. */ { - const Array copy=t1; + const Array copy = t1; cerr << "\n\tYou should now see a warning that writing failed. That's by intention.\n"; - check(write_data(os,t1,ByteOrder::swapped)!=Succeeded::yes, "write_data with swapped byte order should have failed"); - check_if_equal(t1 ,copy, "test out with byte-swapping didn't change the array even with failed IO" ); + check(write_data(os, t1, ByteOrder::swapped) != Succeeded::yes, "write_data with swapped byte order should have failed"); + check_if_equal(t1, copy, "test out with byte-swapping didn't change the array even with failed IO"); } - } //! function that runs IO tests with mixed types for array of arbitrary dimension // sadly needs to be implemented in the class for VC 6.0 template - void run_IO_tests_mixed(OFSTREAM& os, IFSTREAM& is, const Array&orig, NumericInfo output_type_info) + void run_IO_tests_mixed(OFSTREAM& os, IFSTREAM& is, const Array& orig, + NumericInfo output_type_info) { { - { - open_write_binary(os, "output.orig"); - elemT scale(1); - check(write_data(os, orig, NumericInfo(), scale)==Succeeded::yes, "write_data could not write array in original data type"); - close_file(os); - check_if_equal(scale ,static_cast(1), "test out/in: data written in original data type: scale factor should be 1" ); - } + open_write_binary(os, "output.orig"); elemT scale(1); - bool write_data_ok; + check(write_data(os, orig, NumericInfo(), scale) == Succeeded::yes, + "write_data could not write array in original data type"); + close_file(os); + check_if_equal(scale, static_cast(1), "test out/in: data written in original data type: scale factor should be 1"); + } + elemT scale(1); + bool write_data_ok; + { + ofstream os; + open_write_binary(os, "output.other"); + write_data_ok = check(write_data(os, orig, output_type_info, scale) == Succeeded::yes, + "write_data could not write array as other_type"); + close_file(os); + } + + if (write_data_ok) { + // only do reading test if data was written + Array data_read_back(orig.get_index_range()); { - ofstream os; - open_write_binary(os, "output.other"); - write_data_ok=check(write_data(os,orig, output_type_info, scale)==Succeeded::yes, "write_data could not write array as other_type"); - close_file(os); + open_read_binary(is, "output.other"); + check(read_data(is, data_read_back) == Succeeded::yes, "read_data could not read from output.other"); + close_file(is); + remove("output.other"); } - if (write_data_ok) - { - // only do reading test if data was written - Array data_read_back(orig.get_index_range()); - { - open_read_binary(is, "output.other"); - check(read_data(is, data_read_back)==Succeeded::yes, "read_data could not read from output.other"); - close_file(is); - remove("output.other"); - } - - // compare with convert() - { - float newscale = static_cast(scale); - Array origconverted = - convert_array(newscale, orig, NumericInfo()); - check_if_equal(newscale ,scale, "test read_data <-> convert : scale factor "); - check_if_equal(origconverted ,data_read_back, "test read_data <-> convert : data"); - } + // compare with convert() + { + float newscale = static_cast(scale); + Array origconverted = convert_array(newscale, orig, NumericInfo()); + check_if_equal(newscale, scale, "test read_data <-> convert : scale factor "); + check_if_equal(origconverted, data_read_back, "test read_data <-> convert : data"); + } - // compare orig/scale with data_read_back - { - const Array orig_scaled(orig/scale); - this->check_array_equality_with_rounding(orig_scaled, data_read_back, - "test out/in: data written as other_type, read as other_type"); - } + // compare orig/scale with data_read_back + { + const Array orig_scaled(orig / scale); + this->check_array_equality_with_rounding(orig_scaled, data_read_back, + "test out/in: data written as other_type, read as other_type"); + } - // compare data written as original, but read as other_type - { - Array data_read_back2(orig.get_index_range()); - - ifstream is; - open_read_binary(is, "output.orig"); - - elemT in_scale = 0; - check(read_data(is, data_read_back2, NumericInfo(), in_scale)==Succeeded::yes, "read_data could not read from output.orig"); - // compare orig/in_scale with data_read_back2 - const Array orig_scaled(orig/in_scale); - this->check_array_equality_with_rounding(orig_scaled, data_read_back2, - "test out/in: data written as original_type, read as other_type"); - } - } // end of if(write_data_ok) - remove("output.orig"); - } + // compare data written as original, but read as other_type + { + Array data_read_back2(orig.get_index_range()); + + ifstream is; + open_read_binary(is, "output.orig"); + + elemT in_scale = 0; + check(read_data(is, data_read_back2, NumericInfo(), in_scale) == Succeeded::yes, + "read_data could not read from output.orig"); + // compare orig/in_scale with data_read_back2 + const Array orig_scaled(orig / in_scale); + this->check_array_equality_with_rounding(orig_scaled, data_read_back2, + "test out/in: data written as original_type, read as other_type"); + } + } // end of if(write_data_ok) + remove("output.orig"); + } //! a special version of check_if_equal just for this class /*! we check up to .5 if output_type is integer, and up to tolerance otherwise */ template - bool check_array_equality_with_rounding(const Array& orig, const Array& data_read_back, const char*const message) - { + bool check_array_equality_with_rounding(const Array& orig, + const Array& data_read_back, const char* const message) { NumericInfo output_type_info; - bool test_failed=false; - typename Array::const_full_iterator diff_iter = orig.begin_all(); - typename Array::const_full_iterator data_read_back_iter = data_read_back.begin_all_const(); - while(diff_iter!=orig.end_all()) - { - if (output_type_info.integer_type()) - { - std::stringstream full_message; - // construct useful error message even though we use a boolean check - full_message << boost::format("unequal values are %2% and %3%. %1%: difference larger than .5") - % message % static_cast(*data_read_back_iter) % *diff_iter; - // difference should be maximum .5 (but we test with slightly larger tolerance to accomodate numerical precision) - test_failed = check(fabs(*diff_iter - *data_read_back_iter)<=.502, - full_message.str().c_str()); - } - else - { - std::string full_message = message; - full_message += ": difference larger than tolerance"; - test_failed = check_if_equal(static_cast(*data_read_back_iter), *diff_iter, - full_message.c_str()); - } - if (test_failed) - break; - diff_iter++; data_read_back_iter++; + bool test_failed = false; + typename Array::const_full_iterator diff_iter = orig.begin_all(); + typename Array::const_full_iterator data_read_back_iter = data_read_back.begin_all_const(); + while (diff_iter != orig.end_all()) { + if (output_type_info.integer_type()) { + std::stringstream full_message; + // construct useful error message even though we use a boolean check + full_message << boost::format("unequal values are %2% and %3%. %1%: difference larger than .5") % message % + static_cast(*data_read_back_iter) % *diff_iter; + // difference should be maximum .5 (but we test with slightly larger tolerance to accomodate numerical precision) + test_failed = check(fabs(*diff_iter - *data_read_back_iter) <= .502, full_message.str().c_str()); + } else { + std::string full_message = message; + full_message += ": difference larger than tolerance"; + test_failed = check_if_equal(static_cast(*data_read_back_iter), *diff_iter, full_message.c_str()); } + if (test_failed) + break; + diff_iter++; + data_read_back_iter++; + } return test_failed; } @@ -285,105 +269,107 @@ class ArrayTests : public RunTests void run_tests(); }; - void -ArrayTests::run_tests() -{ +ArrayTests::run_tests() { cerr << "Testing Array classes\n"; { cerr << "Testing 1D stuff" << endl; { - - Array<1,int> testint(IndexRange<1>(5)); + + Array<1, int> testint(IndexRange<1>(5)); testint[0] = 2; check_if_equal(testint.size(), size_t(5), "test size()"); check_if_equal(testint.size_all(), size_t(5), "test size_all()"); - Array<1,float> test(IndexRange<1>(10)); + Array<1, float> test(IndexRange<1>(10)); check_if_zero(test, "Array1D not initialised to 0"); test[1] = (float)10.5; test.set_offset(-1); check_if_equal(test.size(), size_t(10), "test size() with non-zero offset"); check_if_equal(test.size_all(), size_t(10), "test size_all() with non-zero offset"); - check_if_equal( test[0], 10.5F, "test indexing of Array1D"); + check_if_equal(test[0], 10.5F, "test indexing of Array1D"); test += 1; - check_if_equal( test[0] , 11.5F, "test operator+=(float)"); - check_if_equal( test.sum(), 20.5F, "test operator+=(float) and sum()"); - check_if_zero( test - test, "test operator-(Array1D)"); + check_if_equal(test[0], 11.5F, "test operator+=(float)"); + check_if_equal(test.sum(), 20.5F, "test operator+=(float) and sum()"); + check_if_zero(test - test, "test operator-(Array1D)"); - BasicCoordinate<1,int> c; - c[1]=0; - check_if_equal(test[c] , 11.5F , "test operator[](BasicCoordinate)"); + BasicCoordinate<1, int> c; + c[1] = 0; + check_if_equal(test[c], 11.5F, "test operator[](BasicCoordinate)"); test[c] = 12.5; - check_if_equal(test[c] , 12.5F , "test operator[](BasicCoordinate)"); + check_if_equal(test[c], 12.5F, "test operator[](BasicCoordinate)"); { - Array<1,float> ref(-1,2); - ref[-1]=1.F;ref[0]=3.F;ref[1]=3.14F; - Array<1,float> test = ref; + Array<1, float> ref(-1, 2); + ref[-1] = 1.F; + ref[0] = 3.F; + ref[1] = 3.14F; + Array<1, float> test = ref; test += 1; - for (int i=ref.get_min_index(); i<= ref.get_max_index(); ++i) - check_if_equal( test[i] , ref[i]+1, "test operator+=(float)"); - test = ref; test -= 4; - for (int i=ref.get_min_index(); i<= ref.get_max_index(); ++i) - check_if_equal( test[i] , ref[i]-4, "test operator-=(float)"); - test = ref; test *= 3; - for (int i=ref.get_min_index(); i<= ref.get_max_index(); ++i) - check_if_equal( test[i] , ref[i]*3, "test operator*=(float)"); - test = ref; test /= 3; - for (int i=ref.get_min_index(); i<= ref.get_max_index(); ++i) - check_if_equal( test[i] , ref[i]/3, "test operator/=(float)"); + for (int i = ref.get_min_index(); i <= ref.get_max_index(); ++i) + check_if_equal(test[i], ref[i] + 1, "test operator+=(float)"); + test = ref; + test -= 4; + for (int i = ref.get_min_index(); i <= ref.get_max_index(); ++i) + check_if_equal(test[i], ref[i] - 4, "test operator-=(float)"); + test = ref; + test *= 3; + for (int i = ref.get_min_index(); i <= ref.get_max_index(); ++i) + check_if_equal(test[i], ref[i] * 3, "test operator*=(float)"); + test = ref; + test /= 3; + for (int i = ref.get_min_index(); i <= ref.get_max_index(); ++i) + check_if_equal(test[i], ref[i] / 3, "test operator/=(float)"); } { - Array<1,float> test2; + Array<1, float> test2; test2 = test * 2; - check_if_equal( 2*test[0] , test2[0], "test operator*(float)"); + check_if_equal(2 * test[0], test2[0], "test operator*(float)"); } { - Array<1,float> test2 = test; - test.grow(-2,test.get_max_index()); - Array<1,float> test3 = test2 + test; + Array<1, float> test2 = test; + test.grow(-2, test.get_max_index()); + Array<1, float> test3 = test2 + test; check_if_zero(test3[-2], "test growing during operator+"); } - } -#if 1 +#if 1 { // tests on log/exp - Array<1,float> test(-3,10); + Array<1, float> test(-3, 10); test.fill(1.F); in_place_log(test); { - Array<1,float> testeq(-3,10); - check_if_equal(test , testeq, "test in_place_log of Array1D"); + Array<1, float> testeq(-3, 10); + check_if_equal(test, testeq, "test in_place_log of Array1D"); } { - for (int i=test.get_min_index(); i<= test.get_max_index(); i++) - test[i] = 3.5F*i + 100; + for (int i = test.get_min_index(); i <= test.get_max_index(); i++) + test[i] = 3.5F * i + 100; } - Array<1,float> test_copy = test; + Array<1, float> test_copy = test; in_place_log(test); in_place_exp(test); - check_if_equal(test , test_copy, "test log/exp of Array1D"); + check_if_equal(test, test_copy, "test log/exp of Array1D"); } #endif } - + { cerr << "Testing 2D stuff" << endl; { - const IndexRange<2> range(Coordinate2D(0,0),Coordinate2D(9,9)); - Array<2,float> test2(range); + const IndexRange<2> range(Coordinate2D(0, 0), Coordinate2D(9, 9)); + Array<2, float> test2(range); check_if_equal(test2.size(), size_t(10), "test size()"); check_if_equal(test2.size_all(), size_t(100), "test size_all()"); // KT 17/03/98 added check on initialisation - check_if_zero(test2, "test Array<2,float> not initialised to 0" ); + check_if_zero(test2, "test Array<2,float> not initialised to 0"); #if 0 // KT 06/04/98 removed operator() @@ -391,15 +377,14 @@ ArrayTests::run_tests() #else test2[3][4] = (float)23.3; #endif - //test2.set_offsets(-1,-4); - //check_if_equal( test2[2][0] , 23.3, "test indexing of Array2D"); + // test2.set_offsets(-1,-4); + // check_if_equal( test2[2][0] , 23.3, "test indexing of Array2D"); } - { - IndexRange<2> range(Coordinate2D(0,0),Coordinate2D(3,3)); - Array<2,float> testfp(range); - Array<2,float> t2fp(range); + IndexRange<2> range(Coordinate2D(0, 0), Coordinate2D(3, 3)); + Array<2, float> testfp(range); + Array<2, float> t2fp(range); #if 0 // KT 06/04/98 removed operator() testfp(3,2) = 3.3F; @@ -409,123 +394,117 @@ ArrayTests::run_tests() t2fp[3][2] = 2.2F; #endif - Array<2,float> t2 = t2fp + testfp; - check_if_equal( t2[3][2] , 5.5F, "test operator +(Array2D)"); + Array<2, float> t2 = t2fp + testfp; + check_if_equal(t2[3][2], 5.5F, "test operator +(Array2D)"); t2fp += testfp; - check_if_equal( t2fp[3][2] , 5.5F, "test operator +=(Array2D)"); - check_if_equal(t2 , t2fp, "test comparing Array2D+= and +" ); + check_if_equal(t2fp[3][2], 5.5F, "test operator +=(Array2D)"); + check_if_equal(t2, t2fp, "test comparing Array2D+= and +"); - { - BasicCoordinate<2,int> c; - c[1]=3; c[2]=2; - check_if_equal(t2[c], 5.5F, "test on operator[](BasicCoordinate)"); + { + BasicCoordinate<2, int> c; + c[1] = 3; + c[2] = 2; + check_if_equal(t2[c], 5.5F, "test on operator[](BasicCoordinate)"); t2[c] = 6.; - check_if_equal(t2[c], 6.F, "test on operator[](BasicCoordinate)"); + check_if_equal(t2[c], 6.F, "test on operator[](BasicCoordinate)"); } // assert should break on next line (in Debug build) if uncommented - //t2[-4][3]=1.F; + // t2[-4][3]=1.F; // at() should throw error { - bool exception_thrown=false; - try - { - t2.at(-4).at(3); - } - catch (...) - { - exception_thrown=true; - } + bool exception_thrown = false; + try { + t2.at(-4).at(3); + } catch (...) { + exception_thrown = true; + } check(exception_thrown, "out-of-range index should throw an exception"); } - - //t2.grow_height(-5,5); - IndexRange<2> larger_range(Coordinate2D(-5,0),Coordinate2D(5,3)); + + // t2.grow_height(-5,5); + IndexRange<2> larger_range(Coordinate2D(-5, 0), Coordinate2D(5, 3)); t2.grow(larger_range); - t2[-4][3]=1.F; - check_if_equal( t2[3][2] , 6.F, "test on grow"); - + t2[-4][3] = 1.F; + check_if_equal(t2[3][2], 6.F, "test on grow"); + // test assignment t2fp = t2; - check_if_equal(t2 , t2fp, "test operator=(Array2D)" ); + check_if_equal(t2, t2fp, "test operator=(Array2D)"); { - Array<2,float> tmp; + Array<2, float> tmp; tmp = t2 / 2; - check_if_equal( t2.sum()/2 , tmp.sum(), "test operator/(float)"); + check_if_equal(t2.sum() / 2, tmp.sum(), "test operator/(float)"); } { // copy constructor; - Array<2,float> t21(t2); - check_if_equal(t21 , t2, "test Array2D copy constructor" ); + Array<2, float> t21(t2); + check_if_equal(t21, t2, "test Array2D copy constructor"); // 'assignment constructor' (this simply calls copy constructor) - Array<2,float> t22 = t2; - check_if_equal(t22 , t2, "test Array2D copy constructor" ); + Array<2, float> t22 = t2; + check_if_equal(t22, t2, "test Array2D copy constructor"); } } // size_all with irregular range { - const IndexRange<2> range(Coordinate2D(-1,1),Coordinate2D(1,2)); - Array<2,float> test2(range); + const IndexRange<2> range(Coordinate2D(-1, 1), Coordinate2D(1, 2)); + Array<2, float> test2(range); check(test2.is_regular(), "test is_regular() with regular"); check_if_equal(test2.size(), size_t(3), "test size() with non-zero offset"); - check_if_equal(test2.size_all(), size_t(6), "test size_all() with non-zero offset"); - test2[0].resize(-1,2); + check_if_equal(test2.size_all(), size_t(6), "test size_all() with non-zero offset"); + test2[0].resize(-1, 2); check(!test2.is_regular(), "test is_regular() with irregular"); check_if_equal(test2.size(), size_t(3), "test size() with irregular range"); - check_if_equal(test2.size_all(), size_t(6+2), "test size_all() with irregular range"); + check_if_equal(test2.size_all(), size_t(6 + 2), "test size_all() with irregular range"); } // full iterator { - IndexRange<2> range(Coordinate2D(0,0),Coordinate2D(2,2)); - Array<2,float> test2(range); + IndexRange<2> range(Coordinate2D(0, 0), Coordinate2D(2, 2)); + Array<2, float> test2(range); { float value = 1.2F; - for (Array<2,float>::full_iterator iter = test2.begin_all(); - iter != test2.end_all(); - ) + for (Array<2, float>::full_iterator iter = test2.begin_all(); iter != test2.end_all();) *iter++ = value++; } { float value = 1.2F; - Array<2,float>::const_full_iterator iter = test2.begin_all_const(); - for (int i=test2.get_min_index(); i<= test2.get_max_index(); ++i) - for (int j=test2[i].get_min_index(); j<= test2[i].get_max_index(); ++j) - { - check(iter != test2.end_all_const(), "test on 2D full iterator"); - check_if_equal(*iter++, test2[i][j], "test on 2D full iterator vs. index"); - check_if_equal(test2[i][j], value++, "test on 2D full iterator value"); - } + Array<2, float>::const_full_iterator iter = test2.begin_all_const(); + for (int i = test2.get_min_index(); i <= test2.get_max_index(); ++i) + for (int j = test2[i].get_min_index(); j <= test2[i].get_max_index(); ++j) { + check(iter != test2.end_all_const(), "test on 2D full iterator"); + check_if_equal(*iter++, test2[i][j], "test on 2D full iterator vs. index"); + check_if_equal(test2[i][j], value++, "test on 2D full iterator value"); + } } - const Array<2,float> empty; + const Array<2, float> empty; check(empty.begin_all() == empty.end_all(), "test on 2D full iterator for empty range"); } // tests for next() { - const IndexRange<2> range(Coordinate2D(-1,1),Coordinate2D(1,2)); - Array<2,int> test(range); + const IndexRange<2> range(Coordinate2D(-1, 1), Coordinate2D(1, 2)); + Array<2, int> test(range); // fill array with numbers in sequence { - Array<2,int>::full_iterator iter = test.begin_all(); - for (int i=0; iter!= test.end_all(); ++iter, ++i) - { - *iter = i; - } + Array<2, int>::full_iterator iter = test.begin_all(); + for (int i = 0; iter != test.end_all(); ++iter, ++i) { + *iter = i; + } } std::cerr << "\tTest on next() with regular array\n"; this->run_tests_on_next(test); // now do test with irregular array - test[0].resize(0,2); + test[0].resize(0, 2); test[0][2] = 10; std::cerr << "\tTest on next() with irregular array, case 1\n"; this->run_tests_on_next(test); - test[1].resize(-2,2); + test[1].resize(-2, 2); test[1][-2] = 20; std::cerr << "\tTest on next() with irregular array, case 2\n"; this->run_tests_on_next(test); - test[-1].resize(-2,0); + test[-1].resize(-2, 0); test[-1][-2] = 30; std::cerr << "\tTest on next() with irregular array, case 3\n"; this->run_tests_on_next(test); @@ -535,8 +514,8 @@ ArrayTests::run_tests() { cerr << "Testing 3D stuff" << endl; - IndexRange<3> range(Coordinate3D(0,-1,1),Coordinate3D(3,3,3)); - Array<3,float> test3(range); + IndexRange<3> range(Coordinate3D(0, -1, 1), Coordinate3D(3, 3, 3)); + Array<3, float> test3(range); check_if_equal(test3.size(), size_t(4), "test size()"); check_if_equal(test3.size_all(), size_t(60), "test size_all() with non-zero offset"); // KT 06/04/98 removed operator() @@ -548,76 +527,73 @@ ArrayTests::run_tests() test3[1][0][2] = (float)7.3; test3[1][0][1] = -1; - - check_if_equal( test3.sum() , 12.9F, "test on sum"); - check_if_equal( test3.find_max() , 7.3F, "test on find_max"); - check_if_equal( test3.find_min() , -1.F, "test on find_min"); + check_if_equal(test3.sum(), 12.9F, "test on sum"); + check_if_equal(test3.find_max(), 7.3F, "test on find_max"); + check_if_equal(test3.find_min(), -1.F, "test on find_min"); { - Array<3,float> test3copy(test3); - BasicCoordinate<3,int> c; - c[1]=1; c[2]=0; c[3]=2; - check_if_equal(test3[c], 7.3F, "test on operator[](BasicCoordinate)"); - test3copy[c]=8.; - check_if_equal(test3copy[1][0][2], 8.F, "test on operator[](BasicCoordinate)"); + Array<3, float> test3copy(test3); + BasicCoordinate<3, int> c; + c[1] = 1; + c[2] = 0; + c[3] = 2; + check_if_equal(test3[c], 7.3F, "test on operator[](BasicCoordinate)"); + test3copy[c] = 8.; + check_if_equal(test3copy[1][0][2], 8.F, "test on operator[](BasicCoordinate)"); } - Array<3,float> test3bis(range); + Array<3, float> test3bis(range); test3bis[1][2][1] = (float)6.6; test3bis[1][0][1] = (float)1.3; - Array<3,float> test3ter = test3bis; + Array<3, float> test3ter = test3bis; test3ter += test3; - check_if_equal(test3ter[1][0][1] , .3F, "test on operator+=(Array3D)"); + check_if_equal(test3ter[1][0][1], .3F, "test on operator+=(Array3D)"); - Array<3,float> test3quat = test3 + test3bis; - check_if_equal(test3quat , test3ter, "test summing Array3D"); + Array<3, float> test3quat = test3 + test3bis; + check_if_equal(test3quat, test3ter, "test summing Array3D"); { - Array<3,float> tmp= test3 - 2; - Array<3,float> tmp2 = test3; + Array<3, float> tmp = test3 - 2; + Array<3, float> tmp2 = test3; tmp2.fill(1.F); - - check_if_zero( test3.sum() - 2*tmp2.sum() - tmp.sum(), "test operator-(float)"); + + check_if_zero(test3.sum() - 2 * tmp2.sum() - tmp.sum(), "test operator-(float)"); } -#if !defined(_MSC_VER) || _MSC_VER>1300 +#if !defined(_MSC_VER) || _MSC_VER > 1300 // VC 6.0 cannot compile this in_place_apply_function(test3ter, bind2nd(plus(), 4.F)); test3quat += 4.F; - check_if_equal(test3quat , test3ter, - "test in_place_apply_function and operator+=(NUMBER)"); + check_if_equal(test3quat, test3ter, "test in_place_apply_function and operator+=(NUMBER)"); #endif // size_all with irregular range { - const IndexRange<3> range(Coordinate3D(-1,1,4),Coordinate3D(1,2,6)); - Array<3,float> test(range); + const IndexRange<3> range(Coordinate3D(-1, 1, 4), Coordinate3D(1, 2, 6)); + Array<3, float> test(range); check(test.is_regular(), "test is_regular() with regular"); check_if_equal(test.size(), size_t(3), "test size() with non-zero offset"); - check_if_equal(test.size_all(), size_t(3*2*3), "test size_all() with non-zero offset"); - test[0][1].resize(-1,2); + check_if_equal(test.size_all(), size_t(3 * 2 * 3), "test size_all() with non-zero offset"); + test[0][1].resize(-1, 2); check(!test.is_regular(), "test is_regular() with irregular"); check_if_equal(test.size(), size_t(3), "test size() with irregular range"); - check_if_equal(test.size_all(), size_t(3*2*3+4-3), "test size_all() with irregular range"); + check_if_equal(test.size_all(), size_t(3 * 2 * 3 + 4 - 3), "test size_all() with irregular range"); } // full iterator { - IndexRange<3> range(Coordinate3D(0,0,1),Coordinate3D(2,2,3)); - Array<3,float> test(range); + IndexRange<3> range(Coordinate3D(0, 0, 1), Coordinate3D(2, 2, 3)); + Array<3, float> test(range); { float value = 1.2F; - for (Array<3,float>::full_iterator iter = test.begin_all(); - iter != test.end_all(); - ) + for (Array<3, float>::full_iterator iter = test.begin_all(); iter != test.end_all();) *iter++ = value++; } { float value = 1.2F; - Array<3,float>::const_full_iterator iter = test.begin_all_const(); - for (int i=test.get_min_index(); i<= test.get_max_index(); ++i) - for (int j=test[i].get_min_index(); j<= test[i].get_max_index(); ++j) - for (int k=test[i][j].get_min_index(); k<= test[i][j].get_max_index(); ++k) - { + Array<3, float>::const_full_iterator iter = test.begin_all_const(); + for (int i = test.get_min_index(); i <= test.get_max_index(); ++i) + for (int j = test[i].get_min_index(); j <= test[i].get_max_index(); ++j) + for (int k = test[i][j].get_min_index(); k <= test[i][j].get_max_index(); ++k) { check(iter != test.end_all_const(), "test on 3D full iterator"); check_if_equal(*iter++, test[i][j][k], "test on 3D full iterator vs. index"); check_if_equal(test[i][j][k], value++, "test on 3D full iterator value"); @@ -625,22 +601,21 @@ ArrayTests::run_tests() } // test empty container { - const Array<3,float> empty; + const Array<3, float> empty; check(empty.begin_all() == empty.end_all(), "test on 3D full iterator for empty range"); } // test conversion from full_iterator to const_full_iterator { - Array<3,float>::full_iterator titer= test.begin_all(); - Array<3,float>::const_full_iterator ctiter= titer; // this should compile + Array<3, float>::full_iterator titer = test.begin_all(); + Array<3, float>::const_full_iterator ctiter = titer; // this should compile } } } - { cerr << "Testing 4D stuff" << endl; - const IndexRange<4> range(Coordinate4D(-3,0,-1,1),Coordinate4D(-2,3,3,3)); - Array<4,float> test4(range); + const IndexRange<4> range(Coordinate4D(-3, 0, -1, 1), Coordinate4D(-2, 3, 3, 3)); + Array<4, float> test4(range); test4.fill(1.); test4[-3][1][2][1] = (float)6.6; #if 0 @@ -649,67 +624,70 @@ ArrayTests::run_tests() test4[-2][1][0][2] = (float)7.3; #endif { - float sum = test4.sum(); - check_if_equal( sum , 131.9F, "test on sum()"); + float sum = test4.sum(); + check_if_equal(sum, 131.9F, "test on sum()"); } - const IndexRange<4> larger_range(Coordinate4D(-3,0,-1,1),Coordinate4D(-1,3,3,5)); + const IndexRange<4> larger_range(Coordinate4D(-3, 0, -1, 1), Coordinate4D(-1, 3, 3, 5)); test4.grow(larger_range); check_if_equal(test4.get_index_range(), larger_range, "test Array4D grow index range"); - check_if_equal(test4.sum(), 131.9F , "test Array4D grow sum"); + check_if_equal(test4.sum(), 131.9F, "test Array4D grow sum"); { - const Array<4,float> test41 = test4; - check_if_equal(test4 , test41, "test Array4D copy constructor" ); - check_if_equal( test41[-3][1][2][1] , 6.6F, "test on indexing after grow"); + const Array<4, float> test41 = test4; + check_if_equal(test4, test41, "test Array4D copy constructor"); + check_if_equal(test41[-3][1][2][1], 6.6F, "test on indexing after grow"); } { - Array<4,float> test41 = test4; - const IndexRange<4> mixed_range(Coordinate4D(-4,1,0,1),Coordinate4D(-2,3,3,6)); + Array<4, float> test41 = test4; + const IndexRange<4> mixed_range(Coordinate4D(-4, 1, 0, 1), Coordinate4D(-2, 3, 3, 6)); test41.resize(mixed_range); check_if_equal(test41.get_index_range(), mixed_range, "test Array4D resize index range"); - check_if_equal( test41[-3][1][2][1] , 6.6F, "test on indexing after resize"); + check_if_equal(test41[-3][1][2][1], 6.6F, "test on indexing after resize"); } - { - BasicCoordinate<4,int> c; - c[1]=-2;c[2]=1;c[3]=0;c[4]=2; - check_if_equal(test4[c] , 7.3F , "test on operator[](BasicCoordinate)"); - test4[c]=1.; - check_if_equal(test4[c] , 1.F , "test on operator[](BasicCoordinate)"); + { + BasicCoordinate<4, int> c; + c[1] = -2; + c[2] = 1; + c[3] = 0; + c[4] = 2; + check_if_equal(test4[c], 7.3F, "test on operator[](BasicCoordinate)"); + test4[c] = 1.; + check_if_equal(test4[c], 1.F, "test on operator[](BasicCoordinate)"); } { - Array<4,float> test4bis(range); + Array<4, float> test4bis(range); test4bis[-2][1][2][1] = (float)6.6; test4bis[-3][1][0][1] = (float)1.3; - Array<4,float> test4ter = test4bis; + Array<4, float> test4ter = test4bis; test4ter += test4; - check_if_equal(test4ter[-3][1][0][1] ,2.3F, "test on operator+=(Array4D)"); + check_if_equal(test4ter[-3][1][0][1], 2.3F, "test on operator+=(Array4D)"); check(test4ter.get_index_range() == larger_range, "test range for operator+=(Array4D) with grow"); - + // Note that test4 is bigger in size than test4bis. - Array<4,float> test4quat = test4bis + test4; - check_if_equal(test4quat ,test4ter, "test summing Array4D with grow"); + Array<4, float> test4quat = test4bis + test4; + check_if_equal(test4quat, test4ter, "test summing Array4D with grow"); check(test4quat.get_index_range() == larger_range, "test range for operator+=(Array4D)"); } // test on scalar multiplication, division { - Array<4,float> test4bis = test4; + Array<4, float> test4bis = test4; test4bis *= 6.F; - check_if_equal(test4bis.sum() ,test4.sum()*6, "test operator *=(float)"); + check_if_equal(test4bis.sum(), test4.sum() * 6, "test operator *=(float)"); test4bis /= 5.F; - check_if_equal(test4bis.sum() ,test4.sum()*6.F/5, "test operator /=(float)"); - } + check_if_equal(test4bis.sum(), test4.sum() * 6.F / 5, "test operator /=(float)"); + } // test on element-wise multiplication, division { - Array<4,float> test4bis(range); + Array<4, float> test4bis(range); { - for (int i=test4bis.get_min_index(); i<= test4bis.get_max_index(); i++) - test4bis[i].fill(i+10.F); + for (int i = test4bis.get_min_index(); i <= test4bis.get_max_index(); i++) + test4bis[i].fill(i + 10.F); } // save for comparison later on - Array<4,float> test4ter = test4bis; - + Array<4, float> test4ter = test4bis; + // Note that test4 is bigger than test4bis, so it will grow with the *= // new elements in test4bis will remain 0 because we're using multiplication test4[-1].fill(666); @@ -717,64 +695,63 @@ ArrayTests::run_tests() check_if_zero(test4bis[-1], "test operator *=(Array4D) grows ok"); check(test4.get_index_range() == test4bis.get_index_range(), "test operator *=(Array4D) grows ok: range"); - // compute the new sum. + // compute the new sum. { - float sum_check = 0; - for (int i=test4.get_min_index(); i<= -2; i++) - sum_check += test4[i].sum()*(i+10.F); - check_if_equal(test4bis.sum() ,sum_check, "test operator *=(Array4D)"); + float sum_check = 0; + for (int i = test4.get_min_index(); i <= -2; i++) + sum_check += test4[i].sum() * (i + 10.F); + check_if_equal(test4bis.sum(), sum_check, "test operator *=(Array4D)"); } // divide test4, but add a tiny number to avoid division by zero - const Array<4,float> test4quat = test4bis / (test4+.00001F); + const Array<4, float> test4quat = test4bis / (test4 + .00001F); test4ter.grow(test4.get_index_range()); - check_if_equal(test4ter ,test4quat, "test operator /(Array4D)"); - } - + check_if_equal(test4ter, test4quat, "test operator /(Array4D)"); + } + // test operator+(float) { // KT 31/01/2000 new - Array<4,float> tmp= test4 + 2; - Array<4,float> tmp2 = test4; + Array<4, float> tmp = test4 + 2; + Array<4, float> tmp2 = test4; tmp2.fill(1.F); - + // KT 20/12/2001 made check_if_zero compare relative to 1 by dividing - check_if_zero( (test4.sum() + 2*tmp2.sum() - tmp.sum())/test4.sum(), - "test operator+(float)"); + check_if_zero((test4.sum() + 2 * tmp2.sum() - tmp.sum()) / test4.sum(), "test operator+(float)"); } // test axpby { - Array<4,float> tmp(test4.get_index_range()); - Array<4,float> tmp2(test4+2); + Array<4, float> tmp(test4.get_index_range()); + Array<4, float> tmp2(test4 + 2); tmp.axpby(2.F, test4, 3.3F, tmp2); - const Array<4,float> by_hand = test4*2.F + (test4+2)*3.3F; + const Array<4, float> by_hand = test4 * 2.F + (test4 + 2) * 3.3F; check_if_equal(tmp, by_hand, "test axpby (Array4D)"); } - + // test xapyb, a and b scalar { - Array<4,float> tmp(test4.get_index_range()); - tmp.xapyb(test4, 2.F, test4+2, 3.3F); + Array<4, float> tmp(test4.get_index_range()); + tmp.xapyb(test4, 2.F, test4 + 2, 3.3F); - const Array<4,float> by_hand = test4*2.F + (test4+2)*3.3F; + const Array<4, float> by_hand = test4 * 2.F + (test4 + 2) * 3.3F; check_if_equal(tmp, by_hand, "test xapyb scalar (Array4D)"); tmp = test4; - tmp.sapyb(2.F, test4+2, 3.3F); - check_if_equal(tmp, by_hand, "test sapyb scalar (Array4D)"); + tmp.sapyb(2.F, test4 + 2, 3.3F); + check_if_equal(tmp, by_hand, "test sapyb scalar (Array4D)"); } - // test xapyb, a and b vector + // test xapyb, a and b vector { - Array<4,float> tmp(test4.get_index_range()); - tmp.xapyb(test4, test4+4, test4+2, test4+6); + Array<4, float> tmp(test4.get_index_range()); + tmp.xapyb(test4, test4 + 4, test4 + 2, test4 + 6); const Array<4, float> by_hand = test4 * (test4 + 4) + (test4 + 2) * (test4 + 6); check_if_equal(tmp, by_hand, "test xapyb vector (Array4D)"); tmp = test4; - tmp.sapyb(test4+4, test4+2, test4+6); - check_if_equal(tmp, by_hand, "test sapyb vector (Array4D)"); + tmp.sapyb(test4 + 4, test4 + 2, test4 + 6); + check_if_equal(tmp, by_hand, "test sapyb vector (Array4D)"); } { @@ -792,11 +769,10 @@ ArrayTests::run_tests() NVecArrIter iter_by_hand = by_hand.begin(); int i = 0; - while (iter_tmp != tmp.end()) - { - *iter_x = test4+i; - *iter_y = (test4 +i+ 2); - *iter_by_hand = ((test4 +i)* 2.0F + (test4+i + 2) * 3.3F); + while (iter_tmp != tmp.end()) { + *iter_x = test4 + i; + *iter_y = (test4 + i + 2); + *iter_by_hand = ((test4 + i) * 2.0F + (test4 + i + 2) * 3.3F); iter_tmp++; iter_x++; @@ -808,7 +784,7 @@ ArrayTests::run_tests() check_if_equal(tmp, by_hand, "test xapyb scalar (NumericVectorWithOffset)"); x.sapyb(2.0F, y, 3.3F); - check_if_equal(x, by_hand, "test sapyb scalar (NumericVectorWithOffset)"); + check_if_equal(x, by_hand, "test sapyb scalar (NumericVectorWithOffset)"); } { typedef NumericVectorWithOffset, float> NVecArr; @@ -829,13 +805,12 @@ ArrayTests::run_tests() NVecArrIter iter_by_hand = by_hand.begin(); int i = 0; - while (iter_tmp != tmp.end()) - { - *iter_x = test4+i; - *iter_y = (test4+i + 2); - *iter_a = (test4+i + 4); - *iter_b = (test4+i + 6); - *iter_by_hand = ((test4+i) * (test4+i + 4) + (test4+i + 2) * (test4+i + 6)); + while (iter_tmp != tmp.end()) { + *iter_x = test4 + i; + *iter_y = (test4 + i + 2); + *iter_a = (test4 + i + 4); + *iter_b = (test4 + i + 6); + *iter_by_hand = ((test4 + i) * (test4 + i + 4) + (test4 + i + 2) * (test4 + i + 6)); iter_tmp++; iter_x++; @@ -849,25 +824,25 @@ ArrayTests::run_tests() check_if_equal(tmp, by_hand, "test xapyb vector (NumericVectorWithOffset)"); x.sapyb(a, y, b); - check_if_equal(x, by_hand, "test sapyb vector (NumericVectorWithOffset)"); + check_if_equal(x, by_hand, "test sapyb vector (NumericVectorWithOffset)"); } } #if 1 { cerr << "Testing 1D float IO" << endl; - Array<1,float> t1(IndexRange<1>(-1,10)); - for (int i=-1; i<=10; i++) - t1[i] = static_cast(sin(i* _PI/ 15.)); - run_IO_tests(t1); + Array<1, float> t1(IndexRange<1>(-1, 10)); + for (int i = -1; i <= 10; i++) + t1[i] = static_cast(sin(i * _PI / 15.)); + run_IO_tests(t1); } { cerr << "Testing 2D double IO" << endl; - IndexRange<2> range(Coordinate2D(-1,11),Coordinate2D(10,20)); - Array<2,double> t1(range); - for (int i=-1; i<=10; i++) - for (int j=11; j<=20; j++) - t1[i][j] = static_cast(sin(i*j* _PI/ 15.)); + IndexRange<2> range(Coordinate2D(-1, 11), Coordinate2D(10, 20)); + Array<2, double> t1(range); + for (int i = -1; i <= 10; i++) + for (int j = 11; j <= 20; j++) + t1[i][j] = static_cast(sin(i * j * _PI / 15.)); run_IO_tests(t1); } { @@ -875,44 +850,40 @@ ArrayTests::run_tests() // construct test array which has rows of very different magnitudes, // numbers in last rows do not fit into short integers - IndexRange<3> range(Coordinate3D(-1,11,21),Coordinate3D(10,20,30)); - Array<3,float> t1(range); - for (int i=-1; i<=10; i++) - for (int j=11; j<=20; j++) - for (int k=21; k<=30; k++) - t1[i][j][k] = static_cast(20000.*k*sin(i*j*k* _PI/ 3000.)); + IndexRange<3> range(Coordinate3D(-1, 11, 21), Coordinate3D(10, 20, 30)); + Array<3, float> t1(range); + for (int i = -1; i <= 10; i++) + for (int j = 11; j <= 20; j++) + for (int k = 21; k <= 30; k++) + t1[i][j][k] = static_cast(20000. * k * sin(i * j * k * _PI / 3000.)); run_IO_tests(t1); } #endif { - cerr << "Testing make_array" << endl; + cerr << "Testing make_array" << endl; - const Array<2,float> arr1 = - make_array(make_1d_array(1.F,0.F,0.F), - make_1d_array(0.F,1.F,1.F), - make_1d_array(0.F,-2.F,2.F)); + const Array<2, float> arr1 = + make_array(make_1d_array(1.F, 0.F, 0.F), make_1d_array(0.F, 1.F, 1.F), make_1d_array(0.F, -2.F, 2.F)); - const Array<2,float> arr2( - make_array(make_1d_array(1.F,0.F,0.F), - make_1d_array(0.F,1.F,1.F), - make_1d_array(0.F,-2.F,2.F))); + const Array<2, float> arr2( + make_array(make_1d_array(1.F, 0.F, 0.F), make_1d_array(0.F, 1.F, 1.F), make_1d_array(0.F, -2.F, 2.F))); - const Array<2,float> arr3 = detail::test_make_array(); - const Array<2,float> arr4(detail::test_make_array()); + const Array<2, float> arr3 = detail::test_make_array(); + const Array<2, float> arr4(detail::test_make_array()); check_if_equal(arr1[2][1], -2.F, "make_array element comparison"); check_if_equal(arr1, arr2, "make_array inline assignment vs constructor"); check_if_equal(arr1, arr3, "make_array inline vs function with assignment"); check_if_equal(arr1, arr4, "make_array inline constructor from function"); - } + } } END_NAMESPACE_STIR USING_NAMESPACE_STIR -int main() -{ +int +main() { ArrayTests tests; tests.run_tests(); return tests.main_return_value(); diff --git a/src/test/test_ArrayFilter.cxx b/src/test/test_ArrayFilter.cxx index 2801bbb849..0a831b733c 100644 --- a/src/test/test_ArrayFilter.cxx +++ b/src/test/test_ArrayFilter.cxx @@ -1,8 +1,8 @@ /*! - \file + \file \ingroup test - + \brief tests for the stir::ArrayFilter classes \author Kris Thielemans @@ -38,343 +38,320 @@ #include "stir/modulo.h" #include "stir/RunTests.h" -#include "stir/stream.h"//XXX +#include "stir/stream.h" //XXX #include #include #include #ifdef DO_TIMINGS -#include "stir/CPUTimer.h" +# include "stir/CPUTimer.h" #endif START_NAMESPACE_STIR - /*! \brief Tests Array functionality \ingroup test */ -class ArrayFilterTests : public RunTests -{ +class ArrayFilterTests : public RunTests { public: void run_tests(); -private: - +private: + template + void compare_results_1arg(const ArrayFunctionObject& filter1, + const ArrayFunctionObject& filter2, const Array& test) { + { + Array out1(test); + Array out2(out1); + filter1(out1); + filter2(out2); -template -void -compare_results_1arg(const ArrayFunctionObject& filter1, - const ArrayFunctionObject& filter2, - const Array& test) -{ - { - Array out1(test); - Array out2(out1); - filter1(out1); - filter2(out2); - - check_if_equal( out1, out2, "test comparing output of filters, equal length"); - //std::cerr << out1 << out2; + check_if_equal(out1, out2, "test comparing output of filters, equal length"); + // std::cerr << out1 << out2; + } + { + Array out1(test); + BasicCoordinate min_indices, max_indices; + check(test.get_regular_range(min_indices, max_indices), "test only works for Arrays of regular range"); + const IndexRange larger_range(min_indices - 2, max_indices + 1); + out1.resize(larger_range); + + Array out2(out1); + filter1(out1); + filter2(out2); + + if (!check_if_equal(out1, out2, "test comparing output of filters, larger length")) { + } // std::cerr << out1 << out2; + } } - { - Array out1(test); + + template + void compare_results_2arg(const ArrayFunctionObject& filter1, + const ArrayFunctionObject& filter2, const Array& test) { BasicCoordinate min_indices, max_indices; check(test.get_regular_range(min_indices, max_indices), "test only works for Arrays of regular range"); - const IndexRange larger_range(min_indices-2, max_indices+1); - out1.resize(larger_range); - - Array out2(out1); - filter1(out1); - filter2(out2); - - if (!check_if_equal( out1, out2, "test comparing output of filters, larger length")) - {}//std::cerr << out1 << out2; - } -} + { + Array out1(test.get_index_range()); + Array out2(out1.get_index_range()); + filter1(out1, test); + filter2(out2, test); -template -void -compare_results_2arg(const ArrayFunctionObject& filter1, - const ArrayFunctionObject& filter2, - const Array& test) -{ - BasicCoordinate min_indices, max_indices; - check(test.get_regular_range(min_indices, max_indices), "test only works for Arrays of regular range"); - { - Array out1(test.get_index_range()); - Array out2(out1.get_index_range()); - filter1(out1, test); - filter2(out2, test); - - check_if_equal( out1, out2, "test comparing output of filter2, equal length"); - //std::cerr << out1 << out2; - } - { - const IndexRange larger_range(min_indices-2, max_indices+1); - Array out1(larger_range); - Array out2(larger_range); - filter1(out1, test); - filter2(out2, test); - - check_if_equal( out1, out2, "test comparing output of filter2, larger length"); - //std::cerr << out1 << out2; - } - { - const IndexRange smaller_range(min_indices+2, max_indices-1); - Array out1(smaller_range); - Array out2(smaller_range); - filter1(out1, test); - filter2(out2, test); - - check_if_equal( out1, out2, "test comparing output of filters, smaller length"); - } - if (num_dimensions==1) - { - IndexRange influenced_range; - if (filter2.get_influenced_indices(influenced_range, test.get_index_range())==Succeeded::yes) - { - BasicCoordinate min_indices, max_indices; - check(influenced_range.get_regular_range(min_indices, max_indices), "test only works for Arrays of regular range"); - const IndexRange larger_range(min_indices-3, max_indices+4);// WARNING ALIASING +7 - //Array out1(IndexRange(influenced_range.get_min_index()-3, influenced_range.get_max_index()+4)); - Array out1(larger_range); - Array out2(out1.get_index_range()); - filter1(out1, test); - filter2(out2, test); - - check_if_equal( out1, out2, "test comparing output of filters, out range is in range+ kernel + extra"); - check_if_zero( out2[out2.get_min_index()], "test conv 0 beyond kernel length"); - check_if_zero( out2[out2.get_min_index()+1], "test conv 0 beyond kernel length"); - check_if_zero( out2[out2.get_min_index()+2], "test conv 0 beyond kernel length"); - check_if_zero( out2[out2.get_max_index()], "test conv 0 beyond kernel length"); - check_if_zero( out2[out2.get_max_index()-1], "test conv 0 beyond kernel length"); - check_if_zero( out2[out2.get_max_index()-2], "test conv 0 beyond kernel length"); - check_if_zero( out2[out2.get_max_index()-3], "test conv 0 beyond kernel length"); - - // really not necessary if above tests were ok, - // but in case they failed, this gives some extra info - check_if_zero( out1[out1.get_min_index()], "test DFT 0 beyond kernel length"); - check_if_zero( out1[out1.get_min_index()+1], "test DFT 0 beyond kernel length"); - check_if_zero( out1[out1.get_min_index()+2], "test DFT 0 beyond kernel length"); - check_if_zero( out1[out1.get_max_index()], "test DFT 0 beyond kernel length"); - check_if_zero( out1[out1.get_max_index()-1], "test DFT 0 beyond kernel length"); - check_if_zero( out1[out1.get_max_index()-2], "test DFT 0 beyond kernel length"); - check_if_zero( out1[out1.get_max_index()-3], "test DFT 0 beyond kernel length"); - //std::cerr << out1 << out2; + check_if_equal(out1, out2, "test comparing output of filter2, equal length"); + // std::cerr << out1 << out2; + } + { + const IndexRange larger_range(min_indices - 2, max_indices + 1); + Array out1(larger_range); + Array out2(larger_range); + filter1(out1, test); + filter2(out2, test); + + check_if_equal(out1, out2, "test comparing output of filter2, larger length"); + // std::cerr << out1 << out2; + } + { + const IndexRange smaller_range(min_indices + 2, max_indices - 1); + Array out1(smaller_range); + Array out2(smaller_range); + filter1(out1, test); + filter2(out2, test); + + check_if_equal(out1, out2, "test comparing output of filters, smaller length"); + } + if (num_dimensions == 1) { + IndexRange influenced_range; + if (filter2.get_influenced_indices(influenced_range, test.get_index_range()) == Succeeded::yes) { + BasicCoordinate min_indices, max_indices; + check(influenced_range.get_regular_range(min_indices, max_indices), "test only works for Arrays of regular range"); + const IndexRange larger_range(min_indices - 3, max_indices + 4); // WARNING ALIASING +7 + // Array out1(IndexRange(influenced_range.get_min_index()-3, + // influenced_range.get_max_index()+4)); + Array out1(larger_range); + Array out2(out1.get_index_range()); + filter1(out1, test); + filter2(out2, test); + + check_if_equal(out1, out2, "test comparing output of filters, out range is in range+ kernel + extra"); + check_if_zero(out2[out2.get_min_index()], "test conv 0 beyond kernel length"); + check_if_zero(out2[out2.get_min_index() + 1], "test conv 0 beyond kernel length"); + check_if_zero(out2[out2.get_min_index() + 2], "test conv 0 beyond kernel length"); + check_if_zero(out2[out2.get_max_index()], "test conv 0 beyond kernel length"); + check_if_zero(out2[out2.get_max_index() - 1], "test conv 0 beyond kernel length"); + check_if_zero(out2[out2.get_max_index() - 2], "test conv 0 beyond kernel length"); + check_if_zero(out2[out2.get_max_index() - 3], "test conv 0 beyond kernel length"); + + // really not necessary if above tests were ok, + // but in case they failed, this gives some extra info + check_if_zero(out1[out1.get_min_index()], "test DFT 0 beyond kernel length"); + check_if_zero(out1[out1.get_min_index() + 1], "test DFT 0 beyond kernel length"); + check_if_zero(out1[out1.get_min_index() + 2], "test DFT 0 beyond kernel length"); + check_if_zero(out1[out1.get_max_index()], "test DFT 0 beyond kernel length"); + check_if_zero(out1[out1.get_max_index() - 1], "test DFT 0 beyond kernel length"); + check_if_zero(out1[out1.get_max_index() - 2], "test DFT 0 beyond kernel length"); + check_if_zero(out1[out1.get_max_index() - 3], "test DFT 0 beyond kernel length"); + // std::cerr << out1 << out2; } + } } -} - }; void -ArrayFilterTests::run_tests() -{ +ArrayFilterTests::run_tests() { std::cerr << "\nTesting 1D\n"; { const int size1 = 100; - Array<1,float> test(IndexRange<1>(100));// warning: not using 'size1' here. gcc 3.3 fails to compile it otherwise - Array<1,float> test_neg_offset(IndexRange<1>(-10,size1-11)); - Array<1,float> test_pos_offset(IndexRange<1>(10,size1+9)); + Array<1, float> test(IndexRange<1>(100)); // warning: not using 'size1' here. gcc 3.3 fails to compile it otherwise + Array<1, float> test_neg_offset(IndexRange<1>(-10, size1 - 11)); + Array<1, float> test_pos_offset(IndexRange<1>(10, size1 + 9)); // initialise to some arbitrary values - for (int i=test.get_min_index(); i<=test.get_max_index(); ++i) - test[i]=i*i*2-i-100.F; + for (int i = test.get_min_index(); i <= test.get_max_index(); ++i) + test[i] = i * i * 2 - i - 100.F; std::copy(test.begin(), test.end(), test_neg_offset.begin()); std::copy(test.begin(), test.end(), test_pos_offset.begin()); { - const int kernel_half_length=30; - const int DFT_kernel_size=256; + const int kernel_half_length = 30; + const int DFT_kernel_size = 256; // necessary to avoid aliasing in DFT - BOOST_STATIC_ASSERT(DFT_kernel_size>=(kernel_half_length*2+1)*2); - BOOST_STATIC_ASSERT(DFT_kernel_size>=2*size1+3);// note +3 as test grows the array - Array<1,float> kernel_for_DFT(IndexRange<1>(0,DFT_kernel_size-1)); - Array<1,float> kernel_for_conv(IndexRange<1>(-kernel_half_length,kernel_half_length)); - for (int i=-kernel_half_length; i DFT_filter; - check(DFT_filter.set_kernel(kernel_for_DFT)==Succeeded::yes, "initialisation DFT filter"); + BOOST_STATIC_ASSERT(DFT_kernel_size >= (kernel_half_length * 2 + 1) * 2); + BOOST_STATIC_ASSERT(DFT_kernel_size >= 2 * size1 + 3); // note +3 as test grows the array + Array<1, float> kernel_for_DFT(IndexRange<1>(0, DFT_kernel_size - 1)); + Array<1, float> kernel_for_conv(IndexRange<1>(-kernel_half_length, kernel_half_length)); + for (int i = -kernel_half_length; i < kernel_half_length; ++i) { + kernel_for_conv[i] = i * i - 3 * i + 1.F; + kernel_for_DFT[modulo(i, DFT_kernel_size)] = kernel_for_conv[i]; + } + + ArrayFilterUsingRealDFTWithPadding<1, float> DFT_filter; + check(DFT_filter.set_kernel(kernel_for_DFT) == Succeeded::yes, "initialisation DFT filter"); ArrayFilter1DUsingConvolution conv_filter(kernel_for_conv); check(!DFT_filter.is_trivial(), "DFT is_trivial"); check(!conv_filter.is_trivial(), "conv is_trivial"); - set_tolerance(test.find_max()*kernel_for_conv.sum()*1.E-6); - //std::cerr << get_tolerance(); + set_tolerance(test.find_max() * kernel_for_conv.sum() * 1.E-6); + // std::cerr << get_tolerance(); - std::cerr <<"Comparing DFT and Convolution with input offset 0\n"; + std::cerr << "Comparing DFT and Convolution with input offset 0\n"; compare_results_2arg(DFT_filter, conv_filter, test); compare_results_1arg(DFT_filter, conv_filter, test); - std::cerr <<"Comparing DFT and Convolution with input negative offset\n"; + std::cerr << "Comparing DFT and Convolution with input negative offset\n"; compare_results_2arg(DFT_filter, conv_filter, test_neg_offset); compare_results_1arg(DFT_filter, conv_filter, test_neg_offset); - std::cerr <<"Comparing DFT and Convolution with input positive offset\n"; + std::cerr << "Comparing DFT and Convolution with input positive offset\n"; compare_results_2arg(DFT_filter, conv_filter, test_pos_offset); compare_results_1arg(DFT_filter, conv_filter, test_pos_offset); } { - const int kernel_half_length=30; - Array<1,float> kernel_for_symconv(IndexRange<1>(0,kernel_half_length)); - Array<1,float> kernel_for_conv(IndexRange<1>(-kernel_half_length,kernel_half_length)); - for (int i=0; i kernel_for_symconv(IndexRange<1>(0, kernel_half_length)); + Array<1, float> kernel_for_conv(IndexRange<1>(-kernel_half_length, kernel_half_length)); + for (int i = 0; i < kernel_half_length; ++i) { + kernel_for_symconv[i] = kernel_for_conv[i] = kernel_for_conv[-i] = i * i - 3 * i + 1.F; + } // symmetric convolution currently requires equal in and out range - Array<1,float> test(IndexRange<1>(100)); + Array<1, float> test(IndexRange<1>(100)); // initialise to some arbitrary values - for (int i=test.get_min_index(); i<=test.get_max_index(); ++i) - test[i]=i*i*2-i-100.F; - - - + for (int i = test.get_min_index(); i <= test.get_max_index(); ++i) + test[i] = i * i * 2 - i - 100.F; + ArrayFilter1DUsingConvolution conv_filter(kernel_for_conv); ArrayFilter1DUsingConvolutionSymmetricKernel symconv_filter(kernel_for_symconv); check(!symconv_filter.is_trivial(), "symconv is_trivial"); check(!conv_filter.is_trivial(), "conv is_trivial"); - set_tolerance(test.find_max()*kernel_for_conv.sum()*1.E-6); - std::cerr <<"Comparing SymmetricConvolution and Convolution\n"; + set_tolerance(test.find_max() * kernel_for_conv.sum() * 1.E-6); + std::cerr << "Comparing SymmetricConvolution and Convolution\n"; // note: SymmetricConvolution cannot handle different input and output ranges compare_results_1arg(symconv_filter, conv_filter, test); } std::cerr << "Testing boundary conditions\n"; { - Array<1,float> kernel(IndexRange<1>(-1,2)); + Array<1, float> kernel(IndexRange<1>(-1, 2)); kernel[-1] = 1; kernel[0] = 2; kernel[1] = 3; kernel[2] = .5; const ArrayFilter1DUsingConvolution conv_filter_zero_BC(kernel); const ArrayFilter1DUsingConvolution conv_filter_cst_BC(kernel, BoundaryConditions::constant); - { - Array<1,float> test(IndexRange<1>(101)); - // initialise to some arbitrary values - for (int i=test.get_min_index(); i<=test.get_max_index(); ++i) - test[i]=i*i*2-i-100.F; - set_tolerance(test.find_max()*kernel.sum()*1.E-6); - - Array<1,float> out_zero_BCs = test; - Array<1,float> out_cst_BCs = test; - conv_filter_zero_BC(out_zero_BCs); - conv_filter_cst_BC(out_cst_BCs); - // test if internal array elements are the same - { - Array<1,float> out_zero_BCs_small=out_zero_BCs; - out_zero_BCs_small.resize(out_zero_BCs.get_min_index() + kernel.get_max_index(), - out_zero_BCs.get_max_index() + kernel.get_min_index()); - Array<1,float> out_cst_BCs_small=out_cst_BCs; - out_cst_BCs_small.resize(out_cst_BCs.get_min_index() + kernel.get_max_index(), - out_cst_BCs.get_max_index() + kernel.get_min_index()); - check_if_equal(out_cst_BCs_small, out_zero_BCs_small, "comparing 1D with different boundary conditions: internal values"); - } - // edge - float left_boundary=test[0]*kernel[2] + test[0]*kernel[1] + test[0]*kernel[0] + test[1]*kernel[-1]; - check_if_equal(out_cst_BCs[0], left_boundary, "1D with cst BC: left edge"); - left_boundary=test[0]*kernel[0] + test[1]*kernel[-1]; - check_if_equal(out_zero_BCs[0], left_boundary, "1D with zero BC: left edge"); - float right_boundary=test[98]*kernel[2] + test[99]*kernel[1] + test[100]*kernel[0] + test[100]*kernel[-1]; - check_if_equal(out_cst_BCs[100], right_boundary, "1D with cst BC: right edge"); - right_boundary=test[98]*kernel[2] + test[99]*kernel[1] + test[100]*kernel[0]; - check_if_equal(out_zero_BCs[100], right_boundary, "1D with zero BC: right edge"); + { + Array<1, float> test(IndexRange<1>(101)); + // initialise to some arbitrary values + for (int i = test.get_min_index(); i <= test.get_max_index(); ++i) + test[i] = i * i * 2 - i - 100.F; + set_tolerance(test.find_max() * kernel.sum() * 1.E-6); + + Array<1, float> out_zero_BCs = test; + Array<1, float> out_cst_BCs = test; + conv_filter_zero_BC(out_zero_BCs); + conv_filter_cst_BC(out_cst_BCs); + // test if internal array elements are the same + { + Array<1, float> out_zero_BCs_small = out_zero_BCs; + out_zero_BCs_small.resize(out_zero_BCs.get_min_index() + kernel.get_max_index(), + out_zero_BCs.get_max_index() + kernel.get_min_index()); + Array<1, float> out_cst_BCs_small = out_cst_BCs; + out_cst_BCs_small.resize(out_cst_BCs.get_min_index() + kernel.get_max_index(), + out_cst_BCs.get_max_index() + kernel.get_min_index()); + check_if_equal(out_cst_BCs_small, out_zero_BCs_small, + "comparing 1D with different boundary conditions: internal values"); + } + // edge + float left_boundary = test[0] * kernel[2] + test[0] * kernel[1] + test[0] * kernel[0] + test[1] * kernel[-1]; + check_if_equal(out_cst_BCs[0], left_boundary, "1D with cst BC: left edge"); + left_boundary = test[0] * kernel[0] + test[1] * kernel[-1]; + check_if_equal(out_zero_BCs[0], left_boundary, "1D with zero BC: left edge"); + float right_boundary = test[98] * kernel[2] + test[99] * kernel[1] + test[100] * kernel[0] + test[100] * kernel[-1]; + check_if_equal(out_cst_BCs[100], right_boundary, "1D with cst BC: right edge"); + right_boundary = test[98] * kernel[2] + test[99] * kernel[1] + test[100] * kernel[0]; + check_if_equal(out_zero_BCs[100], right_boundary, "1D with zero BC: right edge"); } { - Array<1,float> test(-2,5), test_out(-5,8); - test.fill(1.F); - set_tolerance(test.find_max()*kernel.sum()*1.E-6); - conv_filter_zero_BC(test_out, test); - check_if_equal(test_out[-5],0.F,"1D with zero BC: element -5"); - check_if_equal(test_out[-4],0.F,"1D with zero BC: element -4"); - check_if_equal(test_out[-3],kernel[-1],"1D with zero BC: element -3"); - check_if_equal(test_out[-2],kernel[-1]+kernel[0],"1D with zero BC: element -2"); - check_if_equal(test_out[-1],kernel[-1]+kernel[0]+kernel[1],"1D with zero BC: element -1"); - check_if_equal(test_out[0],kernel[2]+kernel[1]+kernel[0]+kernel[-1],"1D with zero BC: element 0"); - check_if_equal(test_out[4],kernel[2]+kernel[1]+kernel[0]+kernel[-1],"1D with zero BC: element 4"); - check_if_equal(test_out[5],kernel[2]+kernel[1]+kernel[0],"1D with zero BC: element 5"); - check_if_equal(test_out[6],kernel[2]+kernel[1],"1D with zero BC: element 6"); - check_if_equal(test_out[7],kernel[2],"1D with zero BC: element 7"); - check_if_equal(test_out[8],0.F,"1D with zero BC: element 8"); - conv_filter_cst_BC(test_out, test); - const float sum=kernel.sum(); - check_if_equal(test_out[-5],sum,"1D with cst BC: element -5"); - check_if_equal(test_out[-4],sum,"1D with cst BC: element -4"); - check_if_equal(test_out[-3],sum,"1D with cst BC: element -3"); - check_if_equal(test_out[-2],sum,"1D with cst BC: element -2"); - check_if_equal(test_out[-1],sum,"1D with cst BC: element -1"); - check_if_equal(test_out[0],sum,"1D with cst BC: element 0"); - check_if_equal(test_out[4],sum,"1D with cst BC: element 4"); - check_if_equal(test_out[5],sum,"1D with cst BC: element 5"); - check_if_equal(test_out[6],sum,"1D with cst BC: element 6"); - check_if_equal(test_out[7],sum,"1D with cst BC: element 7"); - check_if_equal(test_out[8],sum,"1D with cst BC: element 8"); + Array<1, float> test(-2, 5), test_out(-5, 8); + test.fill(1.F); + set_tolerance(test.find_max() * kernel.sum() * 1.E-6); + conv_filter_zero_BC(test_out, test); + check_if_equal(test_out[-5], 0.F, "1D with zero BC: element -5"); + check_if_equal(test_out[-4], 0.F, "1D with zero BC: element -4"); + check_if_equal(test_out[-3], kernel[-1], "1D with zero BC: element -3"); + check_if_equal(test_out[-2], kernel[-1] + kernel[0], "1D with zero BC: element -2"); + check_if_equal(test_out[-1], kernel[-1] + kernel[0] + kernel[1], "1D with zero BC: element -1"); + check_if_equal(test_out[0], kernel[2] + kernel[1] + kernel[0] + kernel[-1], "1D with zero BC: element 0"); + check_if_equal(test_out[4], kernel[2] + kernel[1] + kernel[0] + kernel[-1], "1D with zero BC: element 4"); + check_if_equal(test_out[5], kernel[2] + kernel[1] + kernel[0], "1D with zero BC: element 5"); + check_if_equal(test_out[6], kernel[2] + kernel[1], "1D with zero BC: element 6"); + check_if_equal(test_out[7], kernel[2], "1D with zero BC: element 7"); + check_if_equal(test_out[8], 0.F, "1D with zero BC: element 8"); + conv_filter_cst_BC(test_out, test); + const float sum = kernel.sum(); + check_if_equal(test_out[-5], sum, "1D with cst BC: element -5"); + check_if_equal(test_out[-4], sum, "1D with cst BC: element -4"); + check_if_equal(test_out[-3], sum, "1D with cst BC: element -3"); + check_if_equal(test_out[-2], sum, "1D with cst BC: element -2"); + check_if_equal(test_out[-1], sum, "1D with cst BC: element -1"); + check_if_equal(test_out[0], sum, "1D with cst BC: element 0"); + check_if_equal(test_out[4], sum, "1D with cst BC: element 4"); + check_if_equal(test_out[5], sum, "1D with cst BC: element 5"); + check_if_equal(test_out[6], sum, "1D with cst BC: element 6"); + check_if_equal(test_out[7], sum, "1D with cst BC: element 7"); + check_if_equal(test_out[8], sum, "1D with cst BC: element 8"); } } // boundary conditions - } // 1D std::cerr << "\nTesting 2D\n"; { set_tolerance(.001F); - const int size1=6;const int size2=20; - Array<2,float> test(IndexRange2D(size1,size2)); - Array<2,float> test_neg_offset(IndexRange2D(-5,size1-6,-10,size2-11)); - Array<2,float> test_pos_offset(IndexRange2D(1,size1,2,size2+1)); + const int size1 = 6; + const int size2 = 20; + Array<2, float> test(IndexRange2D(size1, size2)); + Array<2, float> test_neg_offset(IndexRange2D(-5, size1 - 6, -10, size2 - 11)); + Array<2, float> test_pos_offset(IndexRange2D(1, size1, 2, size2 + 1)); // initialise to some arbitrary values { - Array<2,float>::full_iterator iter = test.begin_all(); + Array<2, float>::full_iterator iter = test.begin_all(); /*for (int i=-100; iter != test.end_all(); ++i, ++iter) *iter = 1;//i*i*2.F-i-100.F;*/ - test[0][0]=1; + test[0][0] = 1; std::copy(test.begin_all(), test.end_all(), test_neg_offset.begin_all()); std::copy(test.begin_all(), test.end_all(), test_pos_offset.begin_all()); } { - const int kernel_half_length=14; - const int DFT_kernel_size=64; + const int kernel_half_length = 14; + const int DFT_kernel_size = 64; // necessary to avoid aliasing in DFT - BOOST_STATIC_ASSERT(DFT_kernel_size>=(kernel_half_length*2+1)*2); - BOOST_STATIC_ASSERT(DFT_kernel_size>=2*size2+3);// note +3 as test grows the array - BOOST_STATIC_ASSERT(DFT_kernel_size>=2*size1+3);// note +3 as test grows the array - const Coordinate2D sizes(DFT_kernel_size/2,DFT_kernel_size); - Array<2,float> kernel_for_DFT(IndexRange2D(DFT_kernel_size/2,DFT_kernel_size)); - Array<2,float> kernel_for_conv(IndexRange2D(-(kernel_half_length/2),kernel_half_length/2, - -kernel_half_length,kernel_half_length)); - for (int i=-(kernel_half_length/2); i index(i,j); - kernel_for_conv[index] = i*i-3*i+1.F+j*i/20.F; - kernel_for_DFT[modulo(index,sizes)] = - kernel_for_conv[index]; - } - - - ArrayFilterUsingRealDFTWithPadding<2,float> DFT_filter; - check(DFT_filter.set_kernel(kernel_for_DFT)==Succeeded::yes, "initialisation DFT filter"); + BOOST_STATIC_ASSERT(DFT_kernel_size >= (kernel_half_length * 2 + 1) * 2); + BOOST_STATIC_ASSERT(DFT_kernel_size >= 2 * size2 + 3); // note +3 as test grows the array + BOOST_STATIC_ASSERT(DFT_kernel_size >= 2 * size1 + 3); // note +3 as test grows the array + const Coordinate2D sizes(DFT_kernel_size / 2, DFT_kernel_size); + Array<2, float> kernel_for_DFT(IndexRange2D(DFT_kernel_size / 2, DFT_kernel_size)); + Array<2, float> kernel_for_conv( + IndexRange2D(-(kernel_half_length / 2), kernel_half_length / 2, -kernel_half_length, kernel_half_length)); + for (int i = -(kernel_half_length / 2); i < kernel_half_length / 2; ++i) + for (int j = -kernel_half_length; j < kernel_half_length; ++j) { + const Coordinate2D index(i, j); + kernel_for_conv[index] = i * i - 3 * i + 1.F + j * i / 20.F; + kernel_for_DFT[modulo(index, sizes)] = kernel_for_conv[index]; + } + + ArrayFilterUsingRealDFTWithPadding<2, float> DFT_filter; + check(DFT_filter.set_kernel(kernel_for_DFT) == Succeeded::yes, "initialisation DFT filter"); ArrayFilter2DUsingConvolution conv_filter(kernel_for_conv); check(!DFT_filter.is_trivial(), "DFT is_trivial"); check(!conv_filter.is_trivial(), "conv is_trivial"); - set_tolerance(test.find_max()*kernel_for_conv.sum()*1.E-6); - //std::cerr << get_tolerance(); + set_tolerance(test.find_max() * kernel_for_conv.sum() * 1.E-6); + // std::cerr << get_tolerance(); - std::cerr <<"Comparing DFT and Convolution with input offset 0\n"; + std::cerr << "Comparing DFT and Convolution with input offset 0\n"; compare_results_2arg(DFT_filter, conv_filter, test); compare_results_1arg(DFT_filter, conv_filter, test); - std::cerr <<"Comparing DFT and Convolution with input negative offset\n"; + std::cerr << "Comparing DFT and Convolution with input negative offset\n"; compare_results_2arg(DFT_filter, conv_filter, test_neg_offset); compare_results_1arg(DFT_filter, conv_filter, test_neg_offset); - std::cerr <<"Comparing DFT and Convolution with input positive offset\n"; + std::cerr << "Comparing DFT and Convolution with input positive offset\n"; compare_results_2arg(DFT_filter, conv_filter, test_pos_offset); compare_results_1arg(DFT_filter, conv_filter, test_pos_offset); } @@ -382,71 +359,68 @@ ArrayFilterTests::run_tests() std::cerr << "\nTesting 3D\n"; { set_tolerance(.001F); - const int size1=5;const int size2=7; const int size3=6; - Array<3,float> test(IndexRange3D(size1,size2,size3)); - Array<3,float> test_neg_offset(IndexRange3D(-5,size1-6,-10,size2-11,-4,size3-5)); - Array<3,float> test_pos_offset(IndexRange3D(1,size1,2,size2+1,3,size3+4)); + const int size1 = 5; + const int size2 = 7; + const int size3 = 6; + Array<3, float> test(IndexRange3D(size1, size2, size3)); + Array<3, float> test_neg_offset(IndexRange3D(-5, size1 - 6, -10, size2 - 11, -4, size3 - 5)); + Array<3, float> test_pos_offset(IndexRange3D(1, size1, 2, size2 + 1, 3, size3 + 4)); // initialise to some arbitrary values { - Array<3,float>::full_iterator iter = test.begin_all(); - for (int i=-100; iter != test.end_all(); ++i, ++iter) - *iter = 1;//i*i*2.F-i-100.F; + Array<3, float>::full_iterator iter = test.begin_all(); + for (int i = -100; iter != test.end_all(); ++i, ++iter) + *iter = 1; // i*i*2.F-i-100.F; std::copy(test.begin_all(), test.end_all(), test_neg_offset.begin_all()); std::copy(test.begin_all(), test.end_all(), test_pos_offset.begin_all()); } { - const int kernel_half_length=7; - const int DFT_kernel_size=32; + const int kernel_half_length = 7; + const int DFT_kernel_size = 32; // necessary to avoid aliasing in DFT - BOOST_STATIC_ASSERT(DFT_kernel_size>=(kernel_half_length*2+1)*2); - BOOST_STATIC_ASSERT(DFT_kernel_size/2>=2*size1+3);// note +3 as test grows the array - BOOST_STATIC_ASSERT(DFT_kernel_size>=2*size2+3);// note +3 as test grows the array - BOOST_STATIC_ASSERT(DFT_kernel_size>=2*size3+3);// note +3 as test grows the array - const Coordinate3D sizes(DFT_kernel_size/2,DFT_kernel_size,DFT_kernel_size); - Array<3,float> kernel_for_DFT(IndexRange3D(DFT_kernel_size/2,DFT_kernel_size,DFT_kernel_size)); - Array<3,float> kernel_for_conv(IndexRange3D(-(kernel_half_length/2),kernel_half_length/2, - -kernel_half_length,kernel_half_length, - -kernel_half_length,kernel_half_length)); - for (int i=-(kernel_half_length/2); i index(i,j,k); - kernel_for_conv[index] = i*i-3*i+1.F+j*i/20.F+k; - kernel_for_DFT[modulo(index,sizes)] = - kernel_for_conv[index]; - } - - - ArrayFilterUsingRealDFTWithPadding<3,float> DFT_filter; - check(DFT_filter.set_kernel(kernel_for_DFT)==Succeeded::yes, "initialisation DFT filter"); + BOOST_STATIC_ASSERT(DFT_kernel_size >= (kernel_half_length * 2 + 1) * 2); + BOOST_STATIC_ASSERT(DFT_kernel_size / 2 >= 2 * size1 + 3); // note +3 as test grows the array + BOOST_STATIC_ASSERT(DFT_kernel_size >= 2 * size2 + 3); // note +3 as test grows the array + BOOST_STATIC_ASSERT(DFT_kernel_size >= 2 * size3 + 3); // note +3 as test grows the array + const Coordinate3D sizes(DFT_kernel_size / 2, DFT_kernel_size, DFT_kernel_size); + Array<3, float> kernel_for_DFT(IndexRange3D(DFT_kernel_size / 2, DFT_kernel_size, DFT_kernel_size)); + Array<3, float> kernel_for_conv(IndexRange3D(-(kernel_half_length / 2), kernel_half_length / 2, -kernel_half_length, + kernel_half_length, -kernel_half_length, kernel_half_length)); + for (int i = -(kernel_half_length / 2); i < kernel_half_length / 2; ++i) + for (int j = -kernel_half_length; j < kernel_half_length; ++j) + for (int k = -kernel_half_length; k < kernel_half_length; ++k) { + Coordinate3D index(i, j, k); + kernel_for_conv[index] = i * i - 3 * i + 1.F + j * i / 20.F + k; + kernel_for_DFT[modulo(index, sizes)] = kernel_for_conv[index]; + } + + ArrayFilterUsingRealDFTWithPadding<3, float> DFT_filter; + check(DFT_filter.set_kernel(kernel_for_DFT) == Succeeded::yes, "initialisation DFT filter"); ArrayFilter3DUsingConvolution conv_filter(kernel_for_conv); check(!DFT_filter.is_trivial(), "DFT is_trivial"); check(!conv_filter.is_trivial(), "conv is_trivial"); - set_tolerance(test.find_max()*kernel_for_conv.sum()*1.E-6); - //std::cerr << get_tolerance(); + set_tolerance(test.find_max() * kernel_for_conv.sum() * 1.E-6); + // std::cerr << get_tolerance(); - std::cerr <<"Comparing DFT and Convolution with input offset 0\n"; + std::cerr << "Comparing DFT and Convolution with input offset 0\n"; compare_results_2arg(DFT_filter, conv_filter, test); compare_results_1arg(DFT_filter, conv_filter, test); - std::cerr <<"Comparing DFT and Convolution with input negative offset\n"; + std::cerr << "Comparing DFT and Convolution with input negative offset\n"; compare_results_2arg(DFT_filter, conv_filter, test_neg_offset); compare_results_1arg(DFT_filter, conv_filter, test_neg_offset); - std::cerr <<"Comparing DFT and Convolution with input positive offset\n"; + std::cerr << "Comparing DFT and Convolution with input positive offset\n"; compare_results_2arg(DFT_filter, conv_filter, test_pos_offset); compare_results_1arg(DFT_filter, conv_filter, test_pos_offset); } } - } END_NAMESPACE_STIR USING_NAMESPACE_STIR -int main() -{ +int +main() { ArrayFilterTests tests; tests.run_tests(); return tests.main_return_value(); diff --git a/src/test/test_ByteOrder.cxx b/src/test/test_ByteOrder.cxx index a8a82cbc36..9c81d589a2 100644 --- a/src/test/test_ByteOrder.cxx +++ b/src/test/test_ByteOrder.cxx @@ -40,50 +40,45 @@ using std::endl; START_NAMESPACE_STIR - /*! \brief Test class for ByteOrder and the preprocessor defines from ByteOrderDefine.h \ingroup test */ -class ByteOrderTests : public RunTests -{ +class ByteOrderTests : public RunTests { public: void run_tests(); }; void -ByteOrderTests::run_tests() -{ +ByteOrderTests::run_tests() { cerr << "Tests for ByteOrder\n" << "Everythings is fine if the program runs without any output." << endl; // if any of these is wrong, check ByteOrderDefine.h #if STIRIsNativeByteOrderBigEndian check(ByteOrder::get_native_order() == ByteOrder::big_endian, - "STIRIsNativeByteOrderBigEndian preprocessor define is determined incorrectly."); + "STIRIsNativeByteOrderBigEndian preprocessor define is determined incorrectly."); #else check(ByteOrder::get_native_order() == ByteOrder::little_endian, - "STIRIsNativeByteOrderBigEndian preprocessor define is determined incorrectly."); + "STIRIsNativeByteOrderBigEndian preprocessor define is determined incorrectly."); #endif #if STIRIsNativeByteOrderLittleEndian check(ByteOrder::get_native_order() == ByteOrder::little_endian, - "STIRIsNativeByteOrderBigEndian preprocessor define is determined incorrectly."); + "STIRIsNativeByteOrderBigEndian preprocessor define is determined incorrectly."); #else check(ByteOrder::get_native_order() == ByteOrder::big_endian, - "STIRIsNativeByteOrderBigEndian preprocessor define is determined incorrectly."); + "STIRIsNativeByteOrderBigEndian preprocessor define is determined incorrectly."); #endif - } - END_NAMESPACE_STIR USING_NAMESPACE_STIR -int main() -{ +int +main() { ByteOrderTests tests; tests.run_tests(); return tests.main_return_value(); diff --git a/src/test/test_DateTime.cxx b/src/test/test_DateTime.cxx index 986a07c59c..b86586f699 100644 --- a/src/test/test_DateTime.cxx +++ b/src/test/test_DateTime.cxx @@ -18,7 +18,7 @@ */ /*! - \file + \file \ingroup test \ingroup date_time \brief A simple program to test the date-time conversions @@ -30,7 +30,6 @@ #include - START_NAMESPACE_STIR /*! @@ -38,81 +37,80 @@ START_NAMESPACE_STIR \ingroup test \ingroup date_time */ -class DateTimeTest : public RunTests -{ +class DateTimeTest : public RunTests { void check_round_trip(const double secs, const double tz_offset, const std::string& str); + public: void run_tests(); }; - void -DateTimeTest::run_tests() -{ +DateTimeTest::run_tests() { // just do a consistency check first: mktime and local time should be "inverse" of eachother { time_t current_time = time(0); - struct tm * local_time = localtime(¤t_time); + struct tm* local_time = localtime(¤t_time); if (difftime(current_time, mktime(local_time)) != 0) error("CTIME internal error"); } std::cerr << "Testing DICOM DateTime to epoch functionality\n"; { - check_if_zero(DICOM_datetime_to_secs_since_Unix_epoch("19700101000000.00+0000") - - 0., "test 1 Jan 1970 is 0"); + check_if_zero(DICOM_datetime_to_secs_since_Unix_epoch("19700101000000.00+0000") - 0., "test 1 Jan 1970 is 0"); check_if_zero(DICOM_datetime_to_secs_since_Unix_epoch("19710202000000+0000") - - (((((365 + 31 + 1)*24) + 0)*60. + 0)*60 + 0), "test 2 Feb 1971 0:0:"); + (((((365 + 31 + 1) * 24) + 0) * 60. + 0) * 60 + 0), + "test 2 Feb 1971 0:0:"); check_if_zero(DICOM_datetime_to_secs_since_Unix_epoch("19710202230001.80+0000") - - (((((365 + 31 + 1)*24) + 23)*60. + 0)*60 + 1.80), "test 2 Feb 1971 23:0:1.8"); + (((((365 + 31 + 1) * 24) + 23) * 60. + 0) * 60 + 1.80), + "test 2 Feb 1971 23:0:1.8"); check_if_zero(DICOM_datetime_to_secs_since_Unix_epoch("19710202230301+0230") - - (((((365 + 31 + 1)*24) + 23 - 2.5)*60. + 3)*60 + 1), "test 2 Feb 1971 23:03:01 +02:30"); + (((((365 + 31 + 1) * 24) + 23 - 2.5) * 60. + 3) * 60 + 1), + "test 2 Feb 1971 23:03:01 +02:30"); check_if_zero(DICOM_datetime_to_secs_since_Unix_epoch("19710202230301-0500") - - (((((365 + 31 + 1)*24) + 23 + 5)*60. + 3)*60 + 1), "test 2 Feb 1971 23:03:01 -05:00"); + (((((365 + 31 + 1) * 24) + 23 + 5) * 60. + 3) * 60 + 1), + "test 2 Feb 1971 23:03:01 -05:00"); check_if_zero(DICOM_datetime_to_secs_since_Unix_epoch(DICOM_date_time_to_DT("19710202", "230301", "-0500")) - - (((((365 + 31 + 1)*24) + 23 + 5)*60. + 3)*60 + 1), "test 2 Feb 1971 23:03:01 -05:00 (split)"); + (((((365 + 31 + 1) * 24) + 23 + 5) * 60. + 3) * 60 + 1), + "test 2 Feb 1971 23:03:01 -05:00 (split)"); std::cerr << "\nThe next test should throw an error\n"; - try - { - DICOM_datetime_to_secs_since_Unix_epoch("19710202230301+020"); - check(false, "test ill-formed TZ"); - } - catch (...) - { - std::cerr << "Test was ok\n"; - } + try { + DICOM_datetime_to_secs_since_Unix_epoch("19710202230301+020"); + check(false, "test ill-formed TZ"); + } catch (...) { + std::cerr << "Test was ok\n"; + } // test difference, disabling warnings check_if_zero(DICOM_datetime_to_secs_since_Unix_epoch("20700104000000.4", true) - - DICOM_datetime_to_secs_since_Unix_epoch("20700101000000", true) - - (3*24*60.*60 + 0.4), "test difference without TZ"); + DICOM_datetime_to_secs_since_Unix_epoch("20700101000000", true) - (3 * 24 * 60. * 60 + 0.4), + "test difference without TZ"); } std::cerr << "\nTesting Interfile DateTime to epoch functionality\n"; { - check_if_zero(Interfile_datetime_to_secs_since_Unix_epoch(DateTimeStrings("1970:01:01", "00:00:00.00+0000")) - - 0., "test 1 Jan 1970 is 0"); + check_if_zero(Interfile_datetime_to_secs_since_Unix_epoch(DateTimeStrings("1970:01:01", "00:00:00.00+0000")) - 0., + "test 1 Jan 1970 is 0"); check_if_zero(Interfile_datetime_to_secs_since_Unix_epoch(DateTimeStrings("1971:02:02", "00:00:00+0000")) - - (((((365 + 31 + 1)*24) + 0)*60. + 0)*60 + 0), "test 2 Feb 1971 0:0:"); + (((((365 + 31 + 1) * 24) + 0) * 60. + 0) * 60 + 0), + "test 2 Feb 1971 0:0:"); check_if_zero(Interfile_datetime_to_secs_since_Unix_epoch(DateTimeStrings("1971:02:02", "23:00:01.80+0000")) - - (((((365 + 31 + 1)*24) + 23)*60. + 0)*60 + 1.80), "test 2 Feb 1971 23:0:1.8"); + (((((365 + 31 + 1) * 24) + 23) * 60. + 0) * 60 + 1.80), + "test 2 Feb 1971 23:0:1.8"); check_if_zero(Interfile_datetime_to_secs_since_Unix_epoch(DateTimeStrings("1971:02:02", "23:03:01+0230")) - - (((((365 + 31 + 1)*24) + 23 - 2.5)*60. + 3)*60 + 1), "test 2 Feb 1971 23:03:01 +02:30"); + (((((365 + 31 + 1) * 24) + 23 - 2.5) * 60. + 3) * 60 + 1), + "test 2 Feb 1971 23:03:01 +02:30"); check_if_zero(Interfile_datetime_to_secs_since_Unix_epoch(DateTimeStrings("1971:02:02", "23:03:01-0500")) - - (((((365 + 31 + 1)*24) + 23 + 5)*60. + 3)*60 + 1), "test 2 Feb 1971 23:03:01 -05:00"); + (((((365 + 31 + 1) * 24) + 23 + 5) * 60. + 3) * 60 + 1), + "test 2 Feb 1971 23:03:01 -05:00"); std::cerr << "\nThe next test should throw an error\n"; - try - { - Interfile_datetime_to_secs_since_Unix_epoch(DateTimeStrings("1971:02:2", "23:03:01")); - check(false, "test ill-formed date"); - } - catch (...) - { - std::cerr << "Test was ok\n"; - } - + try { + Interfile_datetime_to_secs_since_Unix_epoch(DateTimeStrings("1971:02:2", "23:03:01")); + check(false, "test ill-formed date"); + } catch (...) { + std::cerr << "Test was ok\n"; + } } std::cerr << "\nTesting round-trip\n"; @@ -122,59 +120,47 @@ DateTimeTest::run_tests() secs = DICOM_datetime_to_secs_since_Unix_epoch("20201120223001.5+0000"); check_round_trip(secs, 0., "round-trip 1 tz+0"); - check_round_trip(secs, 5.5*3600., "round-trip 1 tz+5.5"); - check_round_trip(secs, 12*3600., "round-trip 1 tz+12"); - check_round_trip(secs, -12*3600., "round-trip 1 tz-12"); + check_round_trip(secs, 5.5 * 3600., "round-trip 1 tz+5.5"); + check_round_trip(secs, 12 * 3600., "round-trip 1 tz+12"); + check_round_trip(secs, -12 * 3600., "round-trip 1 tz-12"); // a time in July (opposite DST situation) secs = DICOM_datetime_to_secs_since_Unix_epoch("20200720235901.5+0000"); check_round_trip(secs, 0., "round-trip 2 tz+0"); - check_round_trip(secs, 5.5*3600., "round-trip 2 tz+5.5"); - check_round_trip(secs, 12*3600., "round-trip 2 tz+12"); - check_round_trip(secs, -12*3600., "round-trip 2 tz-12"); + check_round_trip(secs, 5.5 * 3600., "round-trip 2 tz+5.5"); + check_round_trip(secs, 12 * 3600., "round-trip 2 tz+12"); + check_round_trip(secs, -12 * 3600., "round-trip 2 tz-12"); } std::cerr << "\nCurrent timezone offset in hours (I cannot check this though):\n" - << " without DST: " << time_zone_offset_in_secs()/3600. - << " with DST: " << current_time_zone_and_DST_offset_in_secs()/3600. - << "\n"; - + << " without DST: " << time_zone_offset_in_secs() / 3600. + << " with DST: " << current_time_zone_and_DST_offset_in_secs() / 3600. << "\n"; } void -DateTimeTest:: -check_round_trip(const double secs, const double tz_offset, const std::string& str) -{ - try +DateTimeTest::check_round_trip(const double secs, const double tz_offset, const std::string& str) { + try { { - { - const std::string time = secs_since_Unix_epoch_to_DICOM_datetime(secs, tz_offset); - const double new_secs = DICOM_datetime_to_secs_since_Unix_epoch(time); - check_if_zero(new_secs - secs, str + " : " +time); - } - { - const DateTimeStrings dt = secs_since_Unix_epoch_to_Interfile_datetime(secs, tz_offset); - const double new_secs = Interfile_datetime_to_secs_since_Unix_epoch(dt); - check_if_zero(new_secs - secs, str + " : " + dt.date + ", " + dt.time); - } + const std::string time = secs_since_Unix_epoch_to_DICOM_datetime(secs, tz_offset); + const double new_secs = DICOM_datetime_to_secs_since_Unix_epoch(time); + check_if_zero(new_secs - secs, str + " : " + time); } - catch (...) { - check(false, str); + const DateTimeStrings dt = secs_since_Unix_epoch_to_Interfile_datetime(secs, tz_offset); + const double new_secs = Interfile_datetime_to_secs_since_Unix_epoch(dt); + check_if_zero(new_secs - secs, str + " : " + dt.date + ", " + dt.time); } + } catch (...) { + check(false, str); + } } - END_NAMESPACE_STIR - - USING_NAMESPACE_STIR - - -int main() -{ +int +main() { DateTimeTest tests; tests.run_tests(); return tests.main_return_value(); diff --git a/src/test/test_DynamicDiscretisedDensity.cxx b/src/test/test_DynamicDiscretisedDensity.cxx index 2519637963..09d8036da0 100644 --- a/src/test/test_DynamicDiscretisedDensity.cxx +++ b/src/test/test_DynamicDiscretisedDensity.cxx @@ -50,255 +50,263 @@ using std::string; #endif START_NAMESPACE_STIR - - class DynamicDiscretisedDensityTests : public RunTests - { - public: - DynamicDiscretisedDensityTests() - {} - void run_tests(); - //private: - }; - - void DynamicDiscretisedDensityTests::run_tests() -{ + +class DynamicDiscretisedDensityTests : public RunTests { +public: + DynamicDiscretisedDensityTests() {} + void run_tests(); + // private: +}; + +void +DynamicDiscretisedDensityTests::run_tests() { { - // Simple Test of one voxel - cerr << "Testing DynamicDiscretisedDensity class for one voxel..." << endl; - - set_tolerance(0.000000000000001); - const CartesianCoordinate3D< float > origin (0.F,0.F,0.F); - BasicCoordinate<3, float > grid_spacing ; - grid_spacing[1] = 1.F; - grid_spacing[2] = 1.F; - grid_spacing[3] = 1.F; - BasicCoordinate<3,int> sizes ; - sizes[1]=1; - sizes[2]=1; - sizes[3]=1; - IndexRange<3> range(sizes); - - const shared_ptr > - frame1_sptr(new VoxelsOnCartesianGrid (range, origin, grid_spacing)); - (*frame1_sptr)[0][0][0] = 1.F; - - std::vector< std::pair< double, double > > time_frame_definitions_vector(1) ; - std::pair< double, double > time_frame_pair(1.,2.5); - time_frame_definitions_vector[0]=time_frame_pair; - const TimeFrameDefinitions time_frame_definitions(time_frame_definitions_vector); - const double scan_start_time_in_secs_since_1970 = double(1277478034); // somewhere in June 2010... - - Scanner::Type test_scanner=Scanner::E966; - shared_ptr scanner_sptr(new Scanner(test_scanner)); - DynamicDiscretisedDensity dynamic_image(time_frame_definitions, scan_start_time_in_secs_since_1970, scanner_sptr); - ExamInfo exam_info = frame1_sptr->get_exam_info(); - exam_info.set_time_frame_definitions(time_frame_definitions); - exam_info.start_time_in_secs_since_1970 = scan_start_time_in_secs_since_1970; - frame1_sptr->set_exam_info(exam_info); - dynamic_image.set_density(*frame1_sptr, 1); - check_if_equal(dynamic_image[1][0][0][0],1.F,"check DynamicDiscretisedDensity class implementation"); + // Simple Test of one voxel + cerr << "Testing DynamicDiscretisedDensity class for one voxel..." << endl; + + set_tolerance(0.000000000000001); + const CartesianCoordinate3D origin(0.F, 0.F, 0.F); + BasicCoordinate<3, float> grid_spacing; + grid_spacing[1] = 1.F; + grid_spacing[2] = 1.F; + grid_spacing[3] = 1.F; + BasicCoordinate<3, int> sizes; + sizes[1] = 1; + sizes[2] = 1; + sizes[3] = 1; + IndexRange<3> range(sizes); + + const shared_ptr> frame1_sptr(new VoxelsOnCartesianGrid(range, origin, grid_spacing)); + (*frame1_sptr)[0][0][0] = 1.F; + + std::vector> time_frame_definitions_vector(1); + std::pair time_frame_pair(1., 2.5); + time_frame_definitions_vector[0] = time_frame_pair; + const TimeFrameDefinitions time_frame_definitions(time_frame_definitions_vector); + const double scan_start_time_in_secs_since_1970 = double(1277478034); // somewhere in June 2010... + + Scanner::Type test_scanner = Scanner::E966; + shared_ptr scanner_sptr(new Scanner(test_scanner)); + DynamicDiscretisedDensity dynamic_image(time_frame_definitions, scan_start_time_in_secs_since_1970, scanner_sptr); + ExamInfo exam_info = frame1_sptr->get_exam_info(); + exam_info.set_time_frame_definitions(time_frame_definitions); + exam_info.start_time_in_secs_since_1970 = scan_start_time_in_secs_since_1970; + frame1_sptr->set_exam_info(exam_info); + dynamic_image.set_density(*frame1_sptr, 1); + check_if_equal(dynamic_image[1][0][0][0], 1.F, "check DynamicDiscretisedDensity class implementation"); } { - // Test of two frame images, read voxel - cerr << "Writing DynamicDiscretisedDensity class for two frames 63x128x128..." << endl; - - set_tolerance(0.001); - const CartesianCoordinate3D< float > origin (0.F,0.F,0.F); - BasicCoordinate<3, float > grid_spacing ; - grid_spacing[1] = 2.425F; - grid_spacing[2] = 2.0594F; - grid_spacing[3] = 2.0594F; - BasicCoordinate<3,int> min_size, max_size; - min_size[1]=0; min_size[2]=-64; min_size[3]=-64; - max_size[1]=63; max_size[2]=63; max_size[3]=63; - - IndexRange<3> range(min_size,max_size); - const shared_ptr > - frame1_2_sptr(new VoxelsOnCartesianGrid (range, origin, grid_spacing)); - const shared_ptr > - frame2_2_sptr(new VoxelsOnCartesianGrid (range, origin, grid_spacing)); - - for(int k=min_size[3];k > time_frame_definitions_vector(2) ; - std::pair< double, double > first_time_frame_pair(1.,3.) ; - std::pair< double, double > second_time_frame_pair(3.,6.) ; - - time_frame_definitions_vector[0]=first_time_frame_pair; - time_frame_definitions_vector[1]=second_time_frame_pair; - - const TimeFrameDefinitions time_frame_definitions(time_frame_definitions_vector); - const double scan_start_time_in_secs_since_1970 = double(1277478034); // somewhere in June 2010... - Scanner::Type test_scanner=Scanner::E966; - shared_ptr scanner_sptr(new Scanner(test_scanner)); - - DynamicDiscretisedDensity empty_dynamic_image(time_frame_definitions, - scan_start_time_in_secs_since_1970, - scanner_sptr,frame1_2_sptr); - - DynamicDiscretisedDensity dynamic_image(time_frame_definitions, - scan_start_time_in_secs_since_1970, - scanner_sptr); - ExamInfo exam_info = frame1_2_sptr->get_exam_info(); - exam_info.set_time_frame_definitions(TimeFrameDefinitions(time_frame_definitions,1)); - exam_info.start_time_in_secs_since_1970 = scan_start_time_in_secs_since_1970; - frame1_2_sptr->set_exam_info(exam_info); - exam_info.set_time_frame_definitions(TimeFrameDefinitions(time_frame_definitions,2)); - frame2_2_sptr->set_exam_info(exam_info); - dynamic_image.set_density(*frame1_2_sptr, 1); - dynamic_image.set_density(*frame2_2_sptr, 2); - - string string_test("STIRtmp_dyn2f.img"); - string string_empty_test("STIRtmp_dyn2f_empty.img"); + // Test of two frame images, read voxel + cerr << "Writing DynamicDiscretisedDensity class for two frames 63x128x128..." << endl; + + set_tolerance(0.001); + const CartesianCoordinate3D origin(0.F, 0.F, 0.F); + BasicCoordinate<3, float> grid_spacing; + grid_spacing[1] = 2.425F; + grid_spacing[2] = 2.0594F; + grid_spacing[3] = 2.0594F; + BasicCoordinate<3, int> min_size, max_size; + min_size[1] = 0; + min_size[2] = -64; + min_size[3] = -64; + max_size[1] = 63; + max_size[2] = 63; + max_size[3] = 63; + + IndexRange<3> range(min_size, max_size); + const shared_ptr> frame1_2_sptr(new VoxelsOnCartesianGrid(range, origin, grid_spacing)); + const shared_ptr> frame2_2_sptr(new VoxelsOnCartesianGrid(range, origin, grid_spacing)); + + for (int k = min_size[3]; k < min_size[3]; ++k) + for (int j = max_size[2]; j < max_size[2]; ++j) + for (int i = min_size[1]; i < min_size[1]; ++i) { + (*frame1_2_sptr)[k][j][i] = 1 * (i + j * 5.F - k * 10.F); + (*frame2_2_sptr)[k][j][i] = 2 * (i + j * 5.F - k * 10.F); + } + + std::vector> time_frame_definitions_vector(2); + std::pair first_time_frame_pair(1., 3.); + std::pair second_time_frame_pair(3., 6.); + + time_frame_definitions_vector[0] = first_time_frame_pair; + time_frame_definitions_vector[1] = second_time_frame_pair; + + const TimeFrameDefinitions time_frame_definitions(time_frame_definitions_vector); + const double scan_start_time_in_secs_since_1970 = double(1277478034); // somewhere in June 2010... + Scanner::Type test_scanner = Scanner::E966; + shared_ptr scanner_sptr(new Scanner(test_scanner)); + + DynamicDiscretisedDensity empty_dynamic_image(time_frame_definitions, scan_start_time_in_secs_since_1970, scanner_sptr, + frame1_2_sptr); + + DynamicDiscretisedDensity dynamic_image(time_frame_definitions, scan_start_time_in_secs_since_1970, scanner_sptr); + ExamInfo exam_info = frame1_2_sptr->get_exam_info(); + exam_info.set_time_frame_definitions(TimeFrameDefinitions(time_frame_definitions, 1)); + exam_info.start_time_in_secs_since_1970 = scan_start_time_in_secs_since_1970; + frame1_2_sptr->set_exam_info(exam_info); + exam_info.set_time_frame_definitions(TimeFrameDefinitions(time_frame_definitions, 2)); + frame2_2_sptr->set_exam_info(exam_info); + dynamic_image.set_density(*frame1_2_sptr, 1); + dynamic_image.set_density(*frame2_2_sptr, 2); + + string string_test("STIRtmp_dyn2f.img"); + string string_empty_test("STIRtmp_dyn2f_empty.img"); #ifdef HAVE_LLN_MATRIX - check(dynamic_image.write_to_ecat7(string_test)==Succeeded::yes,"check DynamicDiscretisedDensity::write_to_ecat7 implementation"); + check(dynamic_image.write_to_ecat7(string_test) == Succeeded::yes, + "check DynamicDiscretisedDensity::write_to_ecat7 implementation"); #endif - check_if_zero((empty_dynamic_image.get_density(1)).find_min(),"check DynamicDiscretisedDensity constructor implementation"); - check_if_zero((empty_dynamic_image.get_density(1)).find_max(),"check DynamicDiscretisedDensity constructor implementation"); - check_if_zero((empty_dynamic_image.get_density(2)).find_min(),"check DynamicDiscretisedDensity constructor implementation"); - check_if_zero((empty_dynamic_image.get_density(2)).find_max(),"check DynamicDiscretisedDensity constructor implementation"); -} - { - // Test of three frame images, read voxel - cerr << "Testing DynamicDiscretisedDensity class for three frames..." << endl; - - set_tolerance(0.001); - const CartesianCoordinate3D< float > origin (0.F,0.F,0.F); - BasicCoordinate<3, float > grid_spacing ; - grid_spacing[1] = 2.425F; - grid_spacing[2] = 2.0594F; - grid_spacing[3] = 2.0594F; - BasicCoordinate<3,int> min_size, max_size; - min_size[1]=0; min_size[2]=-64; min_size[3]=-64; - max_size[1]=63; max_size[2]=63; max_size[3]=63; - IndexRange<3> range(min_size,max_size); - const shared_ptr > - frame1_3_sptr(new VoxelsOnCartesianGrid (range, origin, grid_spacing)); - const shared_ptr > - frame2_3_sptr(new VoxelsOnCartesianGrid (range, origin, grid_spacing)); - const shared_ptr > - frame3_3_sptr(new VoxelsOnCartesianGrid (range, origin, grid_spacing)); - - for(int k=min_size[3];k > time_frame_definitions_vector(3) ; - std::pair< double, double > first_time_frame_pair(1.,3.) ; - std::pair< double, double > second_time_frame_pair(3.,6.) ; - std::pair< double, double > third_time_frame_pair(6.5,7.) ; - - time_frame_definitions_vector[0]=first_time_frame_pair; - time_frame_definitions_vector[1]=second_time_frame_pair; - time_frame_definitions_vector[2]=third_time_frame_pair; - - const TimeFrameDefinitions time_frame_definitions(time_frame_definitions_vector); - const double scan_start_time_in_secs_since_1970 = double(1277478034); // somewhere in June 2010... - Scanner::Type test_scanner=Scanner::E966; - shared_ptr scanner_sptr(new Scanner(test_scanner)); - DynamicDiscretisedDensity dynamic_image(time_frame_definitions, - scan_start_time_in_secs_since_1970, - scanner_sptr); - ExamInfo exam_info = dynamic_image.get_exam_info(); - exam_info.set_time_frame_definitions(TimeFrameDefinitions(time_frame_definitions,1)); - exam_info.start_time_in_secs_since_1970 = scan_start_time_in_secs_since_1970; - frame1_3_sptr->set_exam_info(exam_info); - - exam_info.set_time_frame_definitions(TimeFrameDefinitions(time_frame_definitions,2)); - exam_info.start_time_in_secs_since_1970 = scan_start_time_in_secs_since_1970; - frame2_3_sptr->set_exam_info(exam_info); - - exam_info.set_time_frame_definitions(TimeFrameDefinitions(time_frame_definitions,3)); - exam_info.start_time_in_secs_since_1970 = scan_start_time_in_secs_since_1970; - frame3_3_sptr->set_exam_info(exam_info); - - dynamic_image.set_density(*frame1_3_sptr, 1); - dynamic_image.set_density(*frame2_3_sptr, 2); - dynamic_image.set_density(*frame3_3_sptr, 3); - - // testing full iterators + check_if_zero((empty_dynamic_image.get_density(1)).find_min(), "check DynamicDiscretisedDensity constructor implementation"); + check_if_zero((empty_dynamic_image.get_density(1)).find_max(), "check DynamicDiscretisedDensity constructor implementation"); + check_if_zero((empty_dynamic_image.get_density(2)).find_min(), "check DynamicDiscretisedDensity constructor implementation"); + check_if_zero((empty_dynamic_image.get_density(2)).find_max(), "check DynamicDiscretisedDensity constructor implementation"); + } { - DiscretisedDensity<3,float>::const_full_iterator dens_iter; - DynamicDiscretisedDensity::const_full_iterator dyn_dens_iter = dynamic_image.begin_all_const(); - bool ok_upto_now = true; - // check frame 1 - dens_iter = frame1_3_sptr->begin_all_const(); - while (ok_upto_now && dens_iter != frame1_3_sptr->end_all_const()) - { + // Test of three frame images, read voxel + cerr << "Testing DynamicDiscretisedDensity class for three frames..." << endl; + + set_tolerance(0.001); + const CartesianCoordinate3D origin(0.F, 0.F, 0.F); + BasicCoordinate<3, float> grid_spacing; + grid_spacing[1] = 2.425F; + grid_spacing[2] = 2.0594F; + grid_spacing[3] = 2.0594F; + BasicCoordinate<3, int> min_size, max_size; + min_size[1] = 0; + min_size[2] = -64; + min_size[3] = -64; + max_size[1] = 63; + max_size[2] = 63; + max_size[3] = 63; + IndexRange<3> range(min_size, max_size); + const shared_ptr> frame1_3_sptr(new VoxelsOnCartesianGrid(range, origin, grid_spacing)); + const shared_ptr> frame2_3_sptr(new VoxelsOnCartesianGrid(range, origin, grid_spacing)); + const shared_ptr> frame3_3_sptr(new VoxelsOnCartesianGrid(range, origin, grid_spacing)); + + for (int k = min_size[3]; k < min_size[3]; ++k) + for (int j = max_size[2]; j < max_size[2]; ++j) + for (int i = min_size[1]; i < min_size[1]; ++i) { + (*frame1_3_sptr)[k][j][i] = 1 * (i + j * 5.F - k * 10.F); + (*frame2_3_sptr)[k][j][i] = 2 * (i + j * 5.F - k * 10.F); + (*frame3_3_sptr)[k][j][i] = 3 * (i + j * 5.F - k * 10.F); + } + + std::vector> time_frame_definitions_vector(3); + std::pair first_time_frame_pair(1., 3.); + std::pair second_time_frame_pair(3., 6.); + std::pair third_time_frame_pair(6.5, 7.); + + time_frame_definitions_vector[0] = first_time_frame_pair; + time_frame_definitions_vector[1] = second_time_frame_pair; + time_frame_definitions_vector[2] = third_time_frame_pair; + + const TimeFrameDefinitions time_frame_definitions(time_frame_definitions_vector); + const double scan_start_time_in_secs_since_1970 = double(1277478034); // somewhere in June 2010... + Scanner::Type test_scanner = Scanner::E966; + shared_ptr scanner_sptr(new Scanner(test_scanner)); + DynamicDiscretisedDensity dynamic_image(time_frame_definitions, scan_start_time_in_secs_since_1970, scanner_sptr); + ExamInfo exam_info = dynamic_image.get_exam_info(); + exam_info.set_time_frame_definitions(TimeFrameDefinitions(time_frame_definitions, 1)); + exam_info.start_time_in_secs_since_1970 = scan_start_time_in_secs_since_1970; + frame1_3_sptr->set_exam_info(exam_info); + + exam_info.set_time_frame_definitions(TimeFrameDefinitions(time_frame_definitions, 2)); + exam_info.start_time_in_secs_since_1970 = scan_start_time_in_secs_since_1970; + frame2_3_sptr->set_exam_info(exam_info); + + exam_info.set_time_frame_definitions(TimeFrameDefinitions(time_frame_definitions, 3)); + exam_info.start_time_in_secs_since_1970 = scan_start_time_in_secs_since_1970; + frame3_3_sptr->set_exam_info(exam_info); + + dynamic_image.set_density(*frame1_3_sptr, 1); + dynamic_image.set_density(*frame2_3_sptr, 2); + dynamic_image.set_density(*frame3_3_sptr, 3); + + // testing full iterators + { + DiscretisedDensity<3, float>::const_full_iterator dens_iter; + DynamicDiscretisedDensity::const_full_iterator dyn_dens_iter = dynamic_image.begin_all_const(); + bool ok_upto_now = true; + // check frame 1 + dens_iter = frame1_3_sptr->begin_all_const(); + while (ok_upto_now && dens_iter != frame1_3_sptr->end_all_const()) { ok_upto_now = check_if_equal(*dyn_dens_iter++, *dens_iter++, "check full-iterator: frame 1"); } - // check frame 2 - dens_iter = frame2_3_sptr->begin_all_const(); - while (ok_upto_now && dens_iter != frame2_3_sptr->end_all_const()) - { + // check frame 2 + dens_iter = frame2_3_sptr->begin_all_const(); + while (ok_upto_now && dens_iter != frame2_3_sptr->end_all_const()) { ok_upto_now = check_if_equal(*dyn_dens_iter++, *dens_iter++, "check full-iterator: frame 1"); } - // check frame 3 - dens_iter = frame3_3_sptr->begin_all_const(); - while (ok_upto_now && dens_iter != frame3_3_sptr->end_all_const()) - { + // check frame 3 + dens_iter = frame3_3_sptr->begin_all_const(); + while (ok_upto_now && dens_iter != frame3_3_sptr->end_all_const()) { ok_upto_now = check_if_equal(*dyn_dens_iter++, *dens_iter++, "check full-iterator: frame 1"); - } - } + } + } #ifndef HAVE_LLN_MATRIX - warning("write_to_ecat7 not tested as LLN library not present"); + warning("write_to_ecat7 not tested as LLN library not present"); #else - string string_test("STIRtmp_dyn3f.img");//TODO: Use the path info!!! - // string string_test2("./local/samples/dyn_image_write_to_ecat7_test2.img"); - // dynamic_image.write_to_ecat7(string_test); - check(dynamic_image.write_to_ecat7(string_test)==Succeeded::yes,"check DynamicDiscretisedDensity::write_to_ecat7 implementation"); - shared_ptr< DynamicDiscretisedDensity > - dyn_image_read_test_sptr(read_from_file(string_test)); - const DynamicDiscretisedDensity & dyn_image_read_test = *dyn_image_read_test_sptr; - // dyn_image_read_test.write_to_ecat7(string_test2); - - for(int k=min_size[3];k dyn_image_read_test_sptr(read_from_file(string_test)); + const DynamicDiscretisedDensity& dyn_image_read_test = *dyn_image_read_test_sptr; + // dyn_image_read_test.write_to_ecat7(string_test2); + + for (int k = min_size[3]; k < min_size[3]; ++k) + for (int j = max_size[2]; j < max_size[2]; ++j) + for (int i = min_size[1]; i < min_size[1]; ++i) { + check_if_equal(dynamic_image[1][k][j][i], (*frame1_3_sptr)[k][j][i], + "check DynamicDiscretisedDensity class implementation"); + check_if_equal(dynamic_image[2][k][j][i], (*frame2_3_sptr)[k][j][i], + "check DynamicDiscretisedDensity class implementation"); + check_if_equal(dynamic_image[3][k][j][i], (*frame3_3_sptr)[k][j][i], + "check DynamicDiscretisedDensity class implementation"); + check_if_equal(dyn_image_read_test[1][k][j - 64][i - 64], (*frame1_3_sptr)[k][j][i], + "check DynamicDiscretisedDensity::read_from_file implementation"); // The written image is read in + // respect to its center as origin!!! + check_if_equal(dyn_image_read_test[2][k][j - 64][i - 64], (*frame2_3_sptr)[k][j][i], + "check DynamicDiscretisedDensity::read_from_file implementation"); + check_if_equal(dyn_image_read_test[3][k][j - 64][i - 64], (*frame3_3_sptr)[k][j][i], + "check DynamicDiscretisedDensity::read_from_file implementation"); } - check_if_equal((dynamic_image.get_time_frame_definitions()).get_end_time(1),3.,"check DynamicDiscretisedDensity class implementation"); - check_if_equal((dynamic_image.get_time_frame_definitions()).get_start_time(1),1.,"check DynamicDiscretisedDensity class implementation"); - check_if_equal((dynamic_image.get_time_frame_definitions()).get_end_time(2),6.,"check DynamicDiscretisedDensity class implementation"); - check_if_equal((dynamic_image.get_time_frame_definitions()).get_start_time(2),3.,"check DynamicDiscretisedDensity class implementation"); - check_if_equal((dynamic_image.get_time_frame_definitions()).get_end_time(3),7.,"check DynamicDiscretisedDensity class implementation"); - check_if_equal((dynamic_image.get_time_frame_definitions()).get_start_time(3),6.5,"check DynamicDiscretisedDensity class implementation"); + check_if_equal((dynamic_image.get_time_frame_definitions()).get_end_time(1), 3., + "check DynamicDiscretisedDensity class implementation"); + check_if_equal((dynamic_image.get_time_frame_definitions()).get_start_time(1), 1., + "check DynamicDiscretisedDensity class implementation"); + check_if_equal((dynamic_image.get_time_frame_definitions()).get_end_time(2), 6., + "check DynamicDiscretisedDensity class implementation"); + check_if_equal((dynamic_image.get_time_frame_definitions()).get_start_time(2), 3., + "check DynamicDiscretisedDensity class implementation"); + check_if_equal((dynamic_image.get_time_frame_definitions()).get_end_time(3), 7., + "check DynamicDiscretisedDensity class implementation"); + check_if_equal((dynamic_image.get_time_frame_definitions()).get_start_time(3), 6.5, + "check DynamicDiscretisedDensity class implementation"); // test if info read is ok - check_if_equal((dyn_image_read_test.get_time_frame_definitions()).get_end_time(1),3.,"check DynamicDiscretisedDensity class implementation"); - check_if_equal((dyn_image_read_test.get_time_frame_definitions()).get_start_time(1),1.,"check DynamicDiscretisedDensity class implementation"); - check_if_equal((dyn_image_read_test.get_time_frame_definitions()).get_end_time(2),6.,"check DynamicDiscretisedDensity class implementation"); - check_if_equal((dyn_image_read_test.get_time_frame_definitions()).get_start_time(2),3.,"check DynamicDiscretisedDensity class implementation"); - check_if_equal((dyn_image_read_test.get_time_frame_definitions()).get_end_time(3),7.,"check DynamicDiscretisedDensity class implementation"); - check_if_equal((dyn_image_read_test.get_time_frame_definitions()).get_start_time(3),6.5,"check DynamicDiscretisedDensity class implementation"); + check_if_equal((dyn_image_read_test.get_time_frame_definitions()).get_end_time(1), 3., + "check DynamicDiscretisedDensity class implementation"); + check_if_equal((dyn_image_read_test.get_time_frame_definitions()).get_start_time(1), 1., + "check DynamicDiscretisedDensity class implementation"); + check_if_equal((dyn_image_read_test.get_time_frame_definitions()).get_end_time(2), 6., + "check DynamicDiscretisedDensity class implementation"); + check_if_equal((dyn_image_read_test.get_time_frame_definitions()).get_start_time(2), 3., + "check DynamicDiscretisedDensity class implementation"); + check_if_equal((dyn_image_read_test.get_time_frame_definitions()).get_end_time(3), 7., + "check DynamicDiscretisedDensity class implementation"); + check_if_equal((dyn_image_read_test.get_time_frame_definitions()).get_start_time(3), 6.5, + "check DynamicDiscretisedDensity class implementation"); #endif - } + } } END_NAMESPACE_STIR USING_NAMESPACE_STIR -int main(int argc, char **argv) -{ - if (argc != 1) - { +int +main(int argc, char** argv) { + if (argc != 1) { cerr << "Usage : " << argv[0] << " \n"; return EXIT_FAILURE; } diff --git a/src/test/test_GeneralisedPoissonNoiseGenerator.cxx b/src/test/test_GeneralisedPoissonNoiseGenerator.cxx index 6fd38c5281..acaed1be4d 100644 --- a/src/test/test_GeneralisedPoissonNoiseGenerator.cxx +++ b/src/test/test_GeneralisedPoissonNoiseGenerator.cxx @@ -16,10 +16,10 @@ See STIR/LICENSE.txt for details */ /*! - \file + \file \ingroup test \ingroup buildblock - + \brief tests for the stir::GeneralisedPoissonNoiseGenerator class \author Kris Thielemans @@ -36,56 +36,51 @@ START_NAMESPACE_STIR - /*! \brief Tests GeneralisedPoissonNoiseGenerator functionality \ingroup test Currently contains only simple tests to check mean and variance. */ -class GeneralisedPoissonNoiseGeneratorTests : public RunTests -{ +class GeneralisedPoissonNoiseGeneratorTests : public RunTests { private: - void - run_one_test(const int size, const float mu, const float scaling_factor, const bool preserve_mean); - + void run_one_test(const int size, const float mu, const float scaling_factor, const bool preserve_mean); + public: void run_tests(); }; void -GeneralisedPoissonNoiseGeneratorTests:: -run_one_test(const int size, const float mu, const float scaling_factor, const bool preserve_mean) -{ - Array<1,float> input(size); - Array<1,float> output(size); +GeneralisedPoissonNoiseGeneratorTests::run_one_test(const int size, const float mu, const float scaling_factor, + const bool preserve_mean) { + Array<1, float> input(size); + Array<1, float> output(size); input.fill(mu); GeneralisedPoissonNoiseGenerator generator(scaling_factor, preserve_mean); generator.generate_random(output, input); - + using namespace boost::accumulators; - - // The accumulator set which will calculate the properties for us: - accumulator_set< float, features< tag::variance, tag::mean > > acc; + + // The accumulator set which will calculate the properties for us: + accumulator_set> acc; // Use std::for_each to accumulate the statistical properties: - acc = std::for_each( output.begin(), output.end(), acc ); + acc = std::for_each(output.begin(), output.end(), acc); set_tolerance(.1); - const float actual_mean = preserve_mean? mu : mu*scaling_factor; - const float actual_variance = preserve_mean? mu/scaling_factor : actual_mean; + const float actual_mean = preserve_mean ? mu : mu * scaling_factor; + const float actual_variance = preserve_mean ? mu / scaling_factor : actual_mean; boost::format formatter("size %1%, mu %2%, scaling_factor %3%, preserve_mean %4%"); formatter % size % mu % scaling_factor % preserve_mean; - + check_if_equal(mean(acc), actual_mean, "test mean with " + formatter.str()); check_if_equal(variance(acc), actual_variance, "test variance with " + formatter.str()); } void -GeneralisedPoissonNoiseGeneratorTests::run_tests() -{ +GeneralisedPoissonNoiseGeneratorTests::run_tests() { std::cerr << "Testing GeneralisedPoissonNoiseGenerator\n"; @@ -102,8 +97,8 @@ END_NAMESPACE_STIR USING_NAMESPACE_STIR -int main() -{ +int +main() { GeneralisedPoissonNoiseGeneratorTests tests; tests.run_tests(); return tests.main_return_value(); diff --git a/src/test/test_IndexRange.cxx b/src/test/test_IndexRange.cxx index 1a3945ed7e..bfd9c7cbdc 100644 --- a/src/test/test_IndexRange.cxx +++ b/src/test/test_IndexRange.cxx @@ -2,8 +2,8 @@ // /*! - \file - + \file + \brief A simple program to test the stir::IndexRange class \author Kris Thielemans @@ -44,109 +44,87 @@ START_NAMESPACE_STIR /*! \brief Class with tests for IndexRange, IndexRange3D. */ -class IndexRange_Tests : public RunTests -{ +class IndexRange_Tests : public RunTests { public: void run_tests(); }; - void -IndexRange_Tests::run_tests() -{ +IndexRange_Tests::run_tests() { cerr << "Testing IndexRange classes" << endl - <<" (There should be only informative messages here starting with 'Testing')" << endl; + << " (There should be only informative messages here starting with 'Testing')" << endl; // make an irregular range { - IndexRange<1> range1(1,3); - IndexRange<1> range2(2,4); - VectorWithOffset< IndexRange<1> > range2d(3,4); - range2d[3]=range1; - range2d[4]=range2; + IndexRange<1> range1(1, 3); + IndexRange<1> range2(2, 4); + VectorWithOffset> range2d(3, 4); + range2d[3] = range1; + range2d[4] = range2; IndexRange<2> idx_range2d = range2d; - - check(idx_range2d[3].get_min_index() == range1.get_min_index(), - "testing constructor from base_type"); - check(idx_range2d[3].get_max_index() == range1.get_max_index(), - "testing constructor from base_type"); - check(idx_range2d.is_regular()==false, - "testing is_regular on irregular range"); + + check(idx_range2d[3].get_min_index() == range1.get_min_index(), "testing constructor from base_type"); + check(idx_range2d[3].get_max_index() == range1.get_max_index(), "testing constructor from base_type"); + check(idx_range2d.is_regular() == false, "testing is_regular on irregular range"); } // make a regular range { - IndexRange<1> range1(1,3); - VectorWithOffset< IndexRange<1> > range2d(3,4); - range2d[3]=range1; - range2d[4]=range1; + IndexRange<1> range1(1, 3); + VectorWithOffset> range2d(3, 4); + range2d[3] = range1; + range2d[4] = range1; IndexRange<2> idx_range2d = range2d; - - check(idx_range2d[3].get_min_index() == range1.get_min_index(), - "testing constructor from base_type"); - check(idx_range2d[3].get_max_index() == range1.get_max_index(), - "testing constructor from base_type"); - check(idx_range2d.is_regular()==true, - "testing is_regular on irregular range"); - - IndexRange2D another_idx_range2d(3,4, 1,3); + + check(idx_range2d[3].get_min_index() == range1.get_min_index(), "testing constructor from base_type"); + check(idx_range2d[3].get_max_index() == range1.get_max_index(), "testing constructor from base_type"); + check(idx_range2d.is_regular() == true, "testing is_regular on irregular range"); + + IndexRange2D another_idx_range2d(3, 4, 1, 3); check(another_idx_range2d == idx_range2d, "test IndexRange2D"); } { - Coordinate3D low(1,2,3); - Coordinate3D high(3,4,5); + Coordinate3D low(1, 2, 3); + Coordinate3D high(3, 4, 5); IndexRange<3> idx_range3d(low, high); - - check(idx_range3d[3].get_max_index() == 4, - "testing constructor from 2 Coordinate objects"); - check(idx_range3d.is_regular()==true, - "testing is_regular on regular range"); + + check(idx_range3d[3].get_max_index() == 4, "testing constructor from 2 Coordinate objects"); + check(idx_range3d.is_regular() == true, "testing is_regular on regular range"); Coordinate3D low_test, high_test; - if (idx_range3d.get_regular_range(low_test, high_test)) - { + if (idx_range3d.get_regular_range(low_test, high_test)) { check_if_equal(low, low_test, "testing is_regular on regular range: lower indices"); check_if_equal(high, high_test, "testing is_regular on regular range: higher indices"); } - IndexRange3D another_idx_range3d(low[1],high[1], low[2], high[2], low[3], high[3]); + IndexRange3D another_idx_range3d(low[1], high[1], low[2], high[2], low[3], high[3]); check(another_idx_range3d == idx_range3d, "test IndexRange3D"); } { - const Coordinate3D sizes(3,4,5); + const Coordinate3D sizes(3, 4, 5); const IndexRange<3> idx_range3d(sizes); - - check(idx_range3d.get_max_index() == 2, - "testing constructor from 1 Coordinate object"); - check(idx_range3d[0].get_max_index() == 3, - "testing constructor from 1 Coordinate object"); - check(idx_range3d[0][0].get_max_index() == 4, - "testing constructor from 1 Coordinate object"); - check(idx_range3d.is_regular()==true, - "testing is_regular on regular range"); + + check(idx_range3d.get_max_index() == 2, "testing constructor from 1 Coordinate object"); + check(idx_range3d[0].get_max_index() == 3, "testing constructor from 1 Coordinate object"); + check(idx_range3d[0][0].get_max_index() == 4, "testing constructor from 1 Coordinate object"); + check(idx_range3d.is_regular() == true, "testing is_regular on regular range"); Coordinate3D low_test, high_test; - if (idx_range3d.get_regular_range(low_test, high_test)) - { - check_if_equal(Coordinate3D(0,0,0), low_test, "testing is_regular on regular range: lower indices"); - check_if_equal(sizes-1, high_test, "testing is_regular on regular range: higher indices"); + if (idx_range3d.get_regular_range(low_test, high_test)) { + check_if_equal(Coordinate3D(0, 0, 0), low_test, "testing is_regular on regular range: lower indices"); + check_if_equal(sizes - 1, high_test, "testing is_regular on regular range: higher indices"); } const IndexRange3D another_idx_range3d(sizes[1], sizes[2], sizes[3]); check(another_idx_range3d == idx_range3d, "test IndexRange3D"); } - } END_NAMESPACE_STIR - - USING_NAMESPACE_STIR - -int main() -{ +int +main() { IndexRange_Tests tests; tests.run_tests(); return tests.main_return_value(); - } diff --git a/src/test/test_KeyParser.cxx b/src/test/test_KeyParser.cxx index 6116934d6c..5ed8e45075 100644 --- a/src/test/test_KeyParser.cxx +++ b/src/test/test_KeyParser.cxx @@ -3,8 +3,8 @@ /* Copyright (C) 2020, University College London This file is part of STIR. - - This file is free software; you can redistribute it and/or modify + + This file is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. @@ -34,14 +34,9 @@ START_NAMESPACE_STIR template -class TestKP : public KeyParser -{ +class TestKP : public KeyParser { public: - TestKP() - : - scalar_v(0), - vector_v(2,0) - { + TestKP() : scalar_v(0), vector_v(2, 0) { add_start_key("start"); add_stop_key("stop"); add_key("scalar", &scalar_v); @@ -56,16 +51,15 @@ class TestKP : public KeyParser \brief Test class for KeyParser */ -class KeyParserTests : public RunTests -{ +class KeyParserTests : public RunTests { public: - template void run_tests_one_type(); + template + void run_tests_one_type(); void run_tests(); }; void -KeyParserTests::run_tests() -{ +KeyParserTests::run_tests() { std::cerr << "Tests for KeyParser\n"; std::cerr << "... int parsing\n"; run_tests_one_type(); @@ -79,10 +73,9 @@ KeyParserTests::run_tests() template void -KeyParserTests::run_tests_one_type() -{ +KeyParserTests::run_tests_one_type() { - TestKP parser; + TestKP parser; // basic test if parsing ok { std::stringstream str; @@ -101,16 +94,13 @@ KeyParserTests::run_tests_one_type() << "scalar[1]:=2\n" << "vector[1] := 3\n" << "stop :=\n"; - try - { - std::cerr << "Next test should write an error (but not crash!)" << std::endl; - parser.parse(str); - check(false, "parsing non-vectorised key with vector should have failed"); - } - catch (...) - { - // ok - } + try { + std::cerr << "Next test should write an error (but not crash!)" << std::endl; + parser.parse(str); + check(false, "parsing non-vectorised key with vector should have failed"); + } catch (...) { + // ok + } } // test 2 if parsing catches errors { @@ -119,28 +109,22 @@ KeyParserTests::run_tests_one_type() << "scalar:=2\n" << "vector := 3\n" << "stop :=\n"; - try - { - std::cerr << "Next test should write an error (but not crash!)" << std::endl; - parser.parse(str); - check(false, "parsing vectorised key with non-vector should have failed"); - } - catch (...) - { - // ok - } + try { + std::cerr << "Next test should write an error (but not crash!)" << std::endl; + parser.parse(str); + check(false, "parsing vectorised key with non-vector should have failed"); + } catch (...) { + // ok + } } - } END_NAMESPACE_STIR - USING_NAMESPACE_STIR - -int main() -{ +int +main() { KeyParserTests tests; tests.run_tests(); return tests.main_return_value(); diff --git a/src/test/test_NestedIterator.cxx b/src/test/test_NestedIterator.cxx index 39bfcbde78..1a36c74d2f 100644 --- a/src/test/test_NestedIterator.cxx +++ b/src/test/test_NestedIterator.cxx @@ -16,9 +16,9 @@ */ /*! - \file + \file \ingroup test - + \brief tests for the stir::NestedIterator class \author Kris Thielemans @@ -44,334 +44,288 @@ START_NAMESPACE_STIR \todo Code is ugly. Copy-paste with tiny modifications. */ -class NestedIteratorTests : public RunTests -{ +class NestedIteratorTests : public RunTests { private: - public: void run_tests(); }; - void -NestedIteratorTests::run_tests() -{ +NestedIteratorTests::run_tests() { std::cerr << "Testing NestedIterator\n"; { std::cerr << "Compare with full iterator\n"; { - IndexRange<2> range(make_coordinate(0,0),make_coordinate(2,2)); - Array<2,float> test2(range); + IndexRange<2> range(make_coordinate(0, 0), make_coordinate(2, 2)); + Array<2, float> test2(range); { float value = 1.2F; - for (Array<2,float>::full_iterator iter = test2.begin_all(); - iter != test2.end_all(); - ) + for (Array<2, float>::full_iterator iter = test2.begin_all(); iter != test2.end_all();) *iter++ = value++; } - check(test2.begin()->begin() == BeginEndFunction::iterator>().begin(test2.begin()), "begin"); - check((test2.begin()+1)->begin() == BeginEndFunction::iterator>().begin(test2.begin()+1), "begin"); - typedef NestedIterator::iterator> FullIter; + check(test2.begin()->begin() == BeginEndFunction::iterator>().begin(test2.begin()), "begin"); + check((test2.begin() + 1)->begin() == BeginEndFunction::iterator>().begin(test2.begin() + 1), "begin"); + typedef NestedIterator::iterator> FullIter; FullIter fiter1; - FullIter fiter(test2.begin(),test2.end()); - //fiter1=fiter; - const FullIter fiter_end(test2.end(),test2.end()); - for (Array<2,float>::full_iterator iter = test2.begin_all(); - iter != test2.end_all(); - ) - { - check(fiter!=fiter_end, "fiter"); - check(*fiter++ == *iter++,"fiter=="); - } - - check(fiter==fiter_end,"fiter end"); + FullIter fiter(test2.begin(), test2.end()); + // fiter1=fiter; + const FullIter fiter_end(test2.end(), test2.end()); + for (Array<2, float>::full_iterator iter = test2.begin_all(); iter != test2.end_all();) { + check(fiter != fiter_end, "fiter"); + check(*fiter++ == *iter++, "fiter=="); + } + + check(fiter == fiter_end, "fiter end"); // check(test2.begin()->begin() == make_begin_function(test2.begin())(), "begin"); - const Array<2,float> empty; + const Array<2, float> empty; check(empty.begin_all() == empty.end_all(), "test on 2D full iterator for empty range"); } } - std::cerr<< " full iterator coord\n"; + std::cerr << " full iterator coord\n"; { - IndexRange<2> range(make_coordinate(0,0),make_coordinate(2,2)); - typedef BasicCoordinate<2,float> elemT; - Array<2,elemT > test2(range); + IndexRange<2> range(make_coordinate(0, 0), make_coordinate(2, 2)); + typedef BasicCoordinate<2, float> elemT; + Array<2, elemT> test2(range); { float value = 1.2F; - for (Array<2,elemT>::full_iterator iter = test2.begin_all(); - iter != test2.end_all(); - ) - { - *iter++ = make_coordinate(value,value+.3F); - ++value; - } + for (Array<2, elemT>::full_iterator iter = test2.begin_all(); iter != test2.end_all();) { + *iter++ = make_coordinate(value, value + .3F); + ++value; + } } - check(test2.begin()->begin() == BeginEndFunction::iterator>().begin(test2.begin()), "begin"); - check((test2.begin()+1)->begin() == BeginEndFunction::iterator>().begin(test2.begin()+1), "begin"); - typedef NestedIterator::iterator> - FullIter; + check(test2.begin()->begin() == BeginEndFunction::iterator>().begin(test2.begin()), "begin"); + check((test2.begin() + 1)->begin() == BeginEndFunction::iterator>().begin(test2.begin() + 1), "begin"); + typedef NestedIterator::iterator> FullIter; FullIter fiter1; - FullIter fiter(test2.begin(),test2.end()); - //fiter1=fiter; - const FullIter fiter_end(test2.end(),test2.end()); + FullIter fiter(test2.begin(), test2.end()); + // fiter1=fiter; + const FullIter fiter_end(test2.end(), test2.end()); { - for (Array<2,elemT>::full_iterator iter = test2.begin_all(); - iter != test2.end_all(); - ) - { - check(fiter!=fiter_end, "fiter"); - check(*fiter++ == *iter++,"fiter=="); - } + for (Array<2, elemT>::full_iterator iter = test2.begin_all(); iter != test2.end_all();) { + check(fiter != fiter_end, "fiter"); + check(*fiter++ == *iter++, "fiter=="); + } } - check(fiter==fiter_end,"fiter end"); + check(fiter == fiter_end, "fiter end"); } - std::cerr<< " full iterator coord\n"; + std::cerr << " full iterator coord\n"; { - IndexRange<2> range(make_coordinate(0,0),make_coordinate(2,2)); - typedef BasicCoordinate<2,float> elemT; - Array<2,elemT > test(range); + IndexRange<2> range(make_coordinate(0, 0), make_coordinate(2, 2)); + typedef BasicCoordinate<2, float> elemT; + Array<2, elemT> test(range); { float value = 1.2F; - for (Array<2,elemT>::full_iterator iter = test.begin_all(); - iter != test.end_all(); - ) - { - *iter++ = make_coordinate(value,value+.3F); - ++value; - } + for (Array<2, elemT>::full_iterator iter = test.begin_all(); iter != test.end_all();) { + *iter++ = make_coordinate(value, value + .3F); + ++value; + } } - const Array<2,elemT> test2 = test; -# if defined __GNUC__ && __GNUC__ < 3 + const Array<2, elemT> test2 = test; +#if defined __GNUC__ && __GNUC__ < 3 // at some point, it seemed we needed a work-around for gcc 3 or earlier, but that is no longer the case - typedef BeginEndFunction::const_iterator> constbeginendfunction_type; + typedef BeginEndFunction::const_iterator> constbeginendfunction_type; #else - typedef ConstBeginEndFunction::const_iterator> constbeginendfunction_type; + typedef ConstBeginEndFunction::const_iterator> constbeginendfunction_type; #endif check(test2.begin()->begin() == constbeginendfunction_type().begin(test2.begin()), "begin"); - check((test2.begin()+1)->begin() == constbeginendfunction_type().begin(test2.begin()+1), "begin"); - typedef NestedIterator::const_iterator, constbeginendfunction_type> - FullIter; + check((test2.begin() + 1)->begin() == constbeginendfunction_type().begin(test2.begin() + 1), "begin"); + typedef NestedIterator::const_iterator, constbeginendfunction_type> FullIter; FullIter fiter1; - FullIter fiter(test2.begin(),test2.end()); - //fiter1=fiter; - FullIter fiter_end(test2.end(),test2.end()); - + FullIter fiter(test2.begin(), test2.end()); + // fiter1=fiter; + FullIter fiter_end(test2.end(), test2.end()); + { - for (Array<2,elemT>::const_full_iterator iter = test2.begin_all_const(); - iter != test2.end_all_const(); - ) - { - check(fiter!=fiter_end, "fiter"); - check(*fiter++ == *iter++,"fiter=="); - } + for (Array<2, elemT>::const_full_iterator iter = test2.begin_all_const(); iter != test2.end_all_const();) { + check(fiter != fiter_end, "fiter"); + check(*fiter++ == *iter++, "fiter=="); + } } - check(fiter==fiter_end,"fiter end"); + check(fiter == fiter_end, "fiter end"); } - std::cerr<< " full iterator coord full\n"; + std::cerr << " full iterator coord full\n"; { - IndexRange<2> range(make_coordinate(0,0),make_coordinate(2,2)); - typedef BasicCoordinate<2,float> elemT; - Array<2,elemT > test2(range); + IndexRange<2> range(make_coordinate(0, 0), make_coordinate(2, 2)); + typedef BasicCoordinate<2, float> elemT; + Array<2, elemT> test2(range); { float value = 1.2F; - for (Array<2,elemT>::full_iterator iter = test2.begin_all(); - iter != test2.end_all(); - ) - { - *iter++ = make_coordinate(value,value+.3F); - ++value; - } + for (Array<2, elemT>::full_iterator iter = test2.begin_all(); iter != test2.end_all();) { + *iter++ = make_coordinate(value, value + .3F); + ++value; + } } - check(test2.begin()->begin() == BeginEndFunction::iterator>().begin(test2.begin()), "begin"); - check((test2.begin()+1)->begin() == BeginEndFunction::iterator>().begin(test2.begin()+1), "begin"); - typedef - NestedIterator::full_iterator, - BeginEndFunction::full_iterator> > - FullIter; + check(test2.begin()->begin() == BeginEndFunction::iterator>().begin(test2.begin()), "begin"); + check((test2.begin() + 1)->begin() == BeginEndFunction::iterator>().begin(test2.begin() + 1), "begin"); + typedef NestedIterator::full_iterator, BeginEndFunction::full_iterator>> FullIter; FullIter fiter1; - FullIter fiter(test2.begin_all(),test2.end_all()); - //fiter1=fiter; - FullIter fiter_end(test2.end_all(),test2.end_all()); - + FullIter fiter(test2.begin_all(), test2.end_all()); + // fiter1=fiter; + FullIter fiter_end(test2.end_all(), test2.end_all()); + { - for (Array<2,elemT>::full_iterator iter = test2.begin_all(); - iter != test2.end_all(); - ) - { - check(fiter!=fiter_end, "fiter"); - check_if_equal(*fiter++, *iter->begin(),"fiter== 0"); - check_if_equal(*fiter++,*(iter->begin()+1),"fiter== 1"); - ++iter; - } + for (Array<2, elemT>::full_iterator iter = test2.begin_all(); iter != test2.end_all();) { + check(fiter != fiter_end, "fiter"); + check_if_equal(*fiter++, *iter->begin(), "fiter== 0"); + check_if_equal(*fiter++, *(iter->begin() + 1), "fiter== 1"); + ++iter; + } } - check(fiter==fiter_end,"fiter end"); + check(fiter == fiter_end, "fiter end"); } - std::cerr<< " full iterator coord full const\n"; + std::cerr << " full iterator coord full const\n"; { - IndexRange<2> range(make_coordinate(0,0),make_coordinate(2,2)); - typedef BasicCoordinate<2,float> elemT; - Array<2,elemT > test(range); + IndexRange<2> range(make_coordinate(0, 0), make_coordinate(2, 2)); + typedef BasicCoordinate<2, float> elemT; + Array<2, elemT> test(range); { float value = 1.2F; - for (Array<2,elemT>::full_iterator iter = test.begin_all(); - iter != test.end_all(); - ) - { - *iter++ = make_coordinate(value,value+.3F); - ++value; - } - + for (Array<2, elemT>::full_iterator iter = test.begin_all(); iter != test.end_all();) { + *iter++ = make_coordinate(value, value + .3F); + ++value; + } } - const Array<2,elemT> test2 = test; + const Array<2, elemT> test2 = test; - check(test2.begin_all_const()->begin() == ConstBeginEndFunction::const_full_iterator>().begin(test2.begin_all_const()), "begin"); - typedef - NestedIterator::const_full_iterator, - ConstBeginEndFunction::const_full_iterator> > - FullIter; + check(test2.begin_all_const()->begin() == + ConstBeginEndFunction::const_full_iterator>().begin(test2.begin_all_const()), + "begin"); + typedef NestedIterator::const_full_iterator, ConstBeginEndFunction::const_full_iterator>> + FullIter; FullIter fiter1; - FullIter fiter(test2.begin_all_const(),test2.end_all_const()); - //fiter1=fiter; - FullIter fiter_end(test2.end_all_const(),test2.end_all_const()); - + FullIter fiter(test2.begin_all_const(), test2.end_all_const()); + // fiter1=fiter; + FullIter fiter_end(test2.end_all_const(), test2.end_all_const()); + { - for (Array<2,elemT>::const_full_iterator iter = test2.begin_all_const(); - iter != test2.end_all_const(); - ) - { - check(fiter!=fiter_end, "fiter"); - check_if_equal(*fiter++, *iter->begin(),"fiter== 0"); - check_if_equal(*fiter++,*(iter->begin()+1),"fiter== 1"); - ++iter; - } + for (Array<2, elemT>::const_full_iterator iter = test2.begin_all_const(); iter != test2.end_all_const();) { + check(fiter != fiter_end, "fiter"); + check_if_equal(*fiter++, *iter->begin(), "fiter== 0"); + check_if_equal(*fiter++, *(iter->begin() + 1), "fiter== 1"); + ++iter; + } } - check(fiter==fiter_end,"fiter end"); + check(fiter == fiter_end, "fiter end"); } - std::cerr<< " full iterator coord full\n"; + std::cerr << " full iterator coord full\n"; { - IndexRange<2> range(make_coordinate(0,0),make_coordinate(2,2)); - typedef BasicCoordinate<2,float> elemT; - Array<2,elemT > test2(range); + IndexRange<2> range(make_coordinate(0, 0), make_coordinate(2, 2)); + typedef BasicCoordinate<2, float> elemT; + Array<2, elemT> test2(range); { float value = 1.2F; - for (Array<2,elemT>::full_iterator iter = test2.begin_all(); - iter != test2.end_all(); - ) - { - *iter++ = make_coordinate(value,value+.3F); - ++value; - } + for (Array<2, elemT>::full_iterator iter = test2.begin_all(); iter != test2.end_all();) { + *iter++ = make_coordinate(value, value + .3F); + ++value; + } } - check(test2.begin()->begin() == BeginEndFunction::iterator>().begin(test2.begin()), "begin"); - check((test2.begin()+1)->begin() == BeginEndFunction::iterator>().begin(test2.begin()+1), "begin"); - typedef - NestedIterator::full_iterator, - BeginEndFunction::full_iterator> > - FullIter; + check(test2.begin()->begin() == BeginEndFunction::iterator>().begin(test2.begin()), "begin"); + check((test2.begin() + 1)->begin() == BeginEndFunction::iterator>().begin(test2.begin() + 1), "begin"); + typedef NestedIterator::full_iterator, BeginEndFunction::full_iterator>> FullIter; FullIter fiter1; - FullIter fiter(test2.begin_all(),test2.end_all()); - //fiter1=fiter; - FullIter fiter_end(test2.end_all(),test2.end_all()); - + FullIter fiter(test2.begin_all(), test2.end_all()); + // fiter1=fiter; + FullIter fiter_end(test2.end_all(), test2.end_all()); + { - for (Array<2,elemT>::full_iterator iter = test2.begin_all(); - iter != test2.end_all(); - ) - { - check(fiter!=fiter_end, "fiter"); - check_if_equal(*fiter++, *iter->begin(),"fiter== 0"); - check_if_equal(*fiter++,*(iter->begin()+1),"fiter== 1"); - ++iter; - } + for (Array<2, elemT>::full_iterator iter = test2.begin_all(); iter != test2.end_all();) { + check(fiter != fiter_end, "fiter"); + check_if_equal(*fiter++, *iter->begin(), "fiter== 0"); + check_if_equal(*fiter++, *(iter->begin() + 1), "fiter== 1"); + ++iter; + } } - check(fiter==fiter_end,"fiter end"); + check(fiter == fiter_end, "fiter end"); } - std::cerr<< " NestedIterator vector\n"; + std::cerr << " NestedIterator vector\n"; { typedef std::list C2; typedef std::vector C1; C1 c(2); - c[0].push_back(1); c[0].push_back(2); - c[1].push_back(3); c[1].push_back(4); c[1].push_back(5); + c[0].push_back(1); + c[0].push_back(2); + c[1].push_back(3); + c[1].push_back(4); + c[1].push_back(5); // normal iterator { typedef NestedIterator FullIter; - FullIter fiter(c.begin(),c.end()); - const FullIter fiter_end(c.end(),c.end()); - int count=1; - while (fiter != fiter_end) - { + FullIter fiter(c.begin(), c.end()); + const FullIter fiter_end(c.end(), c.end()); + int count = 1; + while (fiter != fiter_end) { check_if_equal(*fiter++, count++, "nestiterator of vector"); } check_if_equal(count, 6, "nestiterator of vector: num elements"); } // const iterator { - typedef NestedIterator > FullIter; - FullIter fiter(c.begin(),c.end()); - const FullIter fiter_end(c.end(),c.end()); - int count=1; - while (fiter != fiter_end) - { + typedef NestedIterator> FullIter; + FullIter fiter(c.begin(), c.end()); + const FullIter fiter_end(c.end(), c.end()); + int count = 1; + while (fiter != fiter_end) { check_if_equal(*fiter++, count++, "nestiterator of vector"); } check_if_equal(count, 6, "nestiterator of vector: num elements"); } // test conversion from full_iterator to const_full_iterator { - typedef NestedIterator > CFullIter; + typedef NestedIterator> CFullIter; typedef NestedIterator FullIter; - FullIter fiter(c.begin(),c.end()); - CFullIter cfiter= fiter; // this should compile + FullIter fiter(c.begin(), c.end()); + CFullIter cfiter = fiter; // this should compile } } - std::cerr<< " NestedIterator vector\n"; + std::cerr << " NestedIterator vector\n"; { typedef std::list C2; - typedef std::vector C1; + typedef std::vector C1; C1 c(2); c[0] = new C2; c[1] = new C2; - c[0]->push_back(1); c[0]->push_back(2); - c[1]->push_back(3); c[1]->push_back(4); c[1]->push_back(5); + c[0]->push_back(1); + c[0]->push_back(2); + c[1]->push_back(3); + c[1]->push_back(4); + c[1]->push_back(5); // normal iterator { - typedef NestedIterator > FullIter; - FullIter fiter(c.begin(),c.end()); - const FullIter fiter_end(c.end(),c.end()); - int count=1; - while (fiter != fiter_end) - { + typedef NestedIterator> FullIter; + FullIter fiter(c.begin(), c.end()); + const FullIter fiter_end(c.end(), c.end()); + int count = 1; + while (fiter != fiter_end) { check_if_equal(*fiter++, count++, "nestiterator of vector< list *>"); } check_if_equal(count, 6, "nestiterator of vector: num elements"); } // const iterator { - typedef NestedIterator > FullIter; - FullIter fiter(c.begin(),c.end()); - const FullIter fiter_end(c.end(),c.end()); - int count=1; - while (fiter != fiter_end) - { + typedef NestedIterator> FullIter; + FullIter fiter(c.begin(), c.end()); + const FullIter fiter_end(c.end(), c.end()); + int count = 1; + while (fiter != fiter_end) { check_if_equal(*fiter++, count++, "nestiterator of vector< list *>"); } check_if_equal(count, 6, "nestiterator of vector: num elements"); @@ -380,60 +334,55 @@ NestedIteratorTests::run_tests() delete c[1]; } - std::cerr<< " NestedIterator vector< shared_ptr >\n"; + std::cerr << " NestedIterator vector< shared_ptr >\n"; { - IndexRange<2> range1(make_coordinate(0,0),make_coordinate(1,2)); - IndexRange<2> range2(make_coordinate(0,0),make_coordinate(1,3)); + IndexRange<2> range1(make_coordinate(0, 0), make_coordinate(1, 2)); + IndexRange<2> range2(make_coordinate(0, 0), make_coordinate(1, 3)); typedef int elemT; - typedef Array<2,int> C2; - typedef std::vector > C1; + typedef Array<2, int> C2; + typedef std::vector> C1; C1 c(2); c[0].reset(new C2(range1)); c[1].reset(new C2(range2)); int count = 1; - for (C2::full_iterator fullarrayiter = c[0]->begin_all(); fullarrayiter != c[0]->end_all(); - ++fullarrayiter, ++count) + for (C2::full_iterator fullarrayiter = c[0]->begin_all(); fullarrayiter != c[0]->end_all(); ++fullarrayiter, ++count) *fullarrayiter = count; - for (C2::full_iterator fullarrayiter = c[1]->begin_all(); fullarrayiter != c[1]->end_all(); - ++fullarrayiter, ++count) + for (C2::full_iterator fullarrayiter = c[1]->begin_all(); fullarrayiter != c[1]->end_all(); ++fullarrayiter, ++count) *fullarrayiter = count; // normal iterator { - typedef NestedIterator > FullIter; - FullIter fiter(c.begin(),c.end()); - const FullIter fiter_end(c.end(),c.end()); - count=1; - while (fiter != fiter_end) - { + typedef NestedIterator> FullIter; + FullIter fiter(c.begin(), c.end()); + const FullIter fiter_end(c.end(), c.end()); + count = 1; + while (fiter != fiter_end) { check_if_equal(*fiter++, count++, "nestiterator of vector>>"); } - check_if_equal(count, static_cast(c[0]->size_all() + c[1]->size_all() + 1), - "nestiterator of vector>>: num elements"); + check_if_equal(count, static_cast(c[0]->size_all() + c[1]->size_all() + 1), + "nestiterator of vector>>: num elements"); } // const iterator { - typedef NestedIterator > FullIter; - FullIter fiter(c.begin(),c.end()); - const FullIter fiter_end(c.end(),c.end()); - count=1; - while (fiter != fiter_end) - { + typedef NestedIterator> FullIter; + FullIter fiter(c.begin(), c.end()); + const FullIter fiter_end(c.end(), c.end()); + count = 1; + while (fiter != fiter_end) { check_if_equal(*fiter++, count++, "nestiterator of vector>>"); } - check_if_equal(count, static_cast(c[0]->size_all() + c[1]->size_all() + 1), - "nestiterator of vector>>: num elements"); + check_if_equal(count, static_cast(c[0]->size_all() + c[1]->size_all() + 1), + "nestiterator of vector>>: num elements"); } } - } END_NAMESPACE_STIR USING_NAMESPACE_STIR -int main() -{ +int +main() { NestedIteratorTests tests; tests.run_tests(); return tests.main_return_value(); diff --git a/src/test/test_OutputFileFormat.cxx b/src/test/test_OutputFileFormat.cxx index 17eca35f4d..b5b92e493f 100644 --- a/src/test/test_OutputFileFormat.cxx +++ b/src/test/test_OutputFileFormat.cxx @@ -10,7 +10,7 @@ \author Kris Thielemans - + To run the test, you should use a command line argument with the name of a file. This should contain a test par file. See stir::OutputFileFormatTests class documentation for file contents. @@ -38,11 +38,11 @@ See STIR/LICENSE.txt for details */ - + #include "stir/IO/OutputFileFormat.h" #include "stir/IO/read_from_file.h" #ifdef HAVE_LLN_MATRIX -#include "stir/IO/ECAT6OutputFileFormat.h" // need this for test on pixel_size +# include "stir/IO/ECAT6OutputFileFormat.h" // need this for test on pixel_size #endif #include "stir/RunTests.h" #include "stir/KeyParser.h" @@ -76,47 +76,43 @@ START_NAMESPACE_STIR \verbatim Test OutputFileFormat Parameters:= - output file format type := + output file format type := ; here are parameters specific for the file format End:= \endverbatim \warning Overwrites files STIRtmp.* in the current directory - \todo Delete STIRtmp.* files, but that's a bit difficult as we don't know which ones + \todo Delete STIRtmp.* files, but that's a bit difficult as we don't know which ones are written. */ -class OutputFileFormatTests : public RunTests -{ +class OutputFileFormatTests : public RunTests { public: - OutputFileFormatTests(istream& in) ; + OutputFileFormatTests(istream& in); void run_tests(); + private: istream& in; - shared_ptr > > output_file_format_ptr; + shared_ptr>> output_file_format_ptr; KeyParser parser; }; -OutputFileFormatTests:: -OutputFileFormatTests(istream& in) : - in(in) -{ +OutputFileFormatTests::OutputFileFormatTests(istream& in) : in(in) { output_file_format_ptr.reset(); parser.add_start_key("Test OutputFileFormat Parameters"); parser.add_parsing_key("output file format type", &output_file_format_ptr); parser.add_stop_key("END"); } -void OutputFileFormatTests::run_tests() -{ +void +OutputFileFormatTests::run_tests() { cerr << "Testing OutputFileFormat parsing function..." << endl; cerr << "WARNING: will overwite files called STIRtmp*\n"; if (!check(parser.parse(in), "parsing failed")) return; - if (!check(!is_null_ptr(output_file_format_ptr), - "parsing failed to set output_file_format_ptr")) + if (!check(!is_null_ptr(output_file_format_ptr), "parsing failed to set output_file_format_ptr")) return; #if 0 cerr << "Output parameters after reading from input file:\n" @@ -125,7 +121,7 @@ void OutputFileFormatTests::run_tests() cerr << "-------------------------------------------\n\n"; #endif - cerr << "Now writing to file and reading it back." << endl; + cerr << "Now writing to file and reading it back." << endl; // construct density and write to file { #ifdef HAVE_LLN_MATRIX @@ -134,112 +130,90 @@ void OutputFileFormatTests::run_tests() // TODO get next info from OutputFileFormat class instead of hard-wiring // this in here const bool supports_different_xy_pixel_sizes = - dynamic_cast(output_file_format_ptr.get()) == 0 - ? true : false; + dynamic_cast(output_file_format_ptr.get()) == 0 ? true : false; const bool supports_origin_z_shift = - dynamic_cast(output_file_format_ptr.get()) == 0 - ? true : false; - const bool supports_origin_xy_shift = - true; + dynamic_cast(output_file_format_ptr.get()) == 0 ? true : false; + const bool supports_origin_xy_shift = true; #else const bool supports_different_xy_pixel_sizes = true; const bool supports_origin_z_shift = true; - const bool supports_origin_xy_shift = - true; + const bool supports_origin_xy_shift = true; #endif - CartesianCoordinate3D origin (0.F,0.F,0.F); - if (supports_origin_xy_shift) - { origin.x()=2.4F; origin.y() = -3.5F; } - if (supports_origin_z_shift) - { origin.z()=6.4F; } - - CartesianCoordinate3D grid_spacing (3.F,4.F,supports_different_xy_pixel_sizes?5.F:4.F); - - IndexRange<3> - range(CartesianCoordinate3D(0,-15,-14), - CartesianCoordinate3D(4,14,14)); - - VoxelsOnCartesianGrid image(range,origin, grid_spacing); + CartesianCoordinate3D origin(0.F, 0.F, 0.F); + if (supports_origin_xy_shift) { + origin.x() = 2.4F; + origin.y() = -3.5F; + } + if (supports_origin_z_shift) { + origin.z() = 6.4F; + } + + CartesianCoordinate3D grid_spacing(3.F, 4.F, supports_different_xy_pixel_sizes ? 5.F : 4.F); + + IndexRange<3> range(CartesianCoordinate3D(0, -15, -14), CartesianCoordinate3D(4, 14, 14)); + + VoxelsOnCartesianGrid image(range, origin, grid_spacing); { // fill with some data - for (int z=image.get_min_z(); z<=image.get_max_z(); ++z) - for (int y=image.get_min_y(); y<=image.get_max_y(); ++y) - for (int x=image.get_min_x(); x<=image.get_max_x(); ++x) - image[z][y][x]= - 300*sin(static_cast(x*_PI)/image.get_max_x()) - *sin(static_cast(y+10*_PI)/image.get_max_y()) - *cos(static_cast(z*_PI/3)/image.get_max_z()); + for (int z = image.get_min_z(); z <= image.get_max_z(); ++z) + for (int y = image.get_min_y(); y <= image.get_max_y(); ++y) + for (int x = image.get_min_x(); x <= image.get_max_x(); ++x) + image[z][y][x] = 300 * sin(static_cast(x * _PI) / image.get_max_x()) * + sin(static_cast(y + 10 * _PI) / image.get_max_y()) * + cos(static_cast(z * _PI / 3) / image.get_max_z()); } // write to file std::string filename = "STIRtmp"; - const Succeeded success = - output_file_format_ptr->write_to_file(filename,image); - - if (check( success==Succeeded::yes, "test writing to file")) - { - - // now read it back - - unique_ptr > - density_ptr = read_from_file >(filename); - - const VoxelsOnCartesianGrid * image_as_read_ptr = - dynamic_cast< VoxelsOnCartesianGrid const *> - (density_ptr.get()); - - set_tolerance(.00001); - if (check(!is_null_ptr(image_as_read_ptr), "test on image type read back from file")) - { - check_if_equal(image_as_read_ptr->get_grid_spacing(), grid_spacing, "test on grid spacing read back from file"); - - - if (output_file_format_ptr->get_type_of_numbers().integer_type()) - { - set_tolerance(image.find_max()/ - pow(2.,static_cast(output_file_format_ptr->get_type_of_numbers().size_in_bits()))); - } - - check_if_equal(image, *density_ptr, "test on data read back from file"); - set_tolerance(.00001); - check_if_equal(density_ptr->get_origin(), origin, "test on origin read back from file"); - } - } - if (is_everything_ok()) - { - - } - else - cerr << "You can check what was written in STIRtmp.*\n"; + const Succeeded success = output_file_format_ptr->write_to_file(filename, image); - } - + if (check(success == Succeeded::yes, "test writing to file")) { + // now read it back -} + unique_ptr> density_ptr = read_from_file>(filename); + const VoxelsOnCartesianGrid* image_as_read_ptr = + dynamic_cast const*>(density_ptr.get()); + + set_tolerance(.00001); + if (check(!is_null_ptr(image_as_read_ptr), "test on image type read back from file")) { + check_if_equal(image_as_read_ptr->get_grid_spacing(), grid_spacing, "test on grid spacing read back from file"); + + if (output_file_format_ptr->get_type_of_numbers().integer_type()) { + set_tolerance(image.find_max() / + pow(2., static_cast(output_file_format_ptr->get_type_of_numbers().size_in_bits()))); + } + + check_if_equal(image, *density_ptr, "test on data read back from file"); + set_tolerance(.00001); + check_if_equal(density_ptr->get_origin(), origin, "test on origin read back from file"); + } + } + if (is_everything_ok()) { + + } else + cerr << "You can check what was written in STIRtmp.*\n"; + } +} END_NAMESPACE_STIR USING_NAMESPACE_STIR -int main(int argc, char **argv) -{ - if (argc != 2) - { +int +main(int argc, char** argv) { + if (argc != 2) { cerr << "Usage : " << argv[0] << " filename\n" << "See source file for the format of this file.\n\n"; return EXIT_FAILURE; } - ifstream in(argv[1]); - if (!in) - { - cerr << argv[0] - << ": Error opening input file " << argv[1] << "\nExiting.\n"; + if (!in) { + cerr << argv[0] << ": Error opening input file " << argv[1] << "\nExiting.\n"; return EXIT_FAILURE; } diff --git a/src/test/test_ROIs.cxx b/src/test/test_ROIs.cxx index 1e8081a45d..ab3ef489a4 100644 --- a/src/test/test_ROIs.cxx +++ b/src/test/test_ROIs.cxx @@ -20,15 +20,15 @@ \file \ingroup test - + \brief Test program for ROI functionality (and a bit of stir::Shape3D hierarchy) - + \author Kris Thielemans \author C. Ross Schmidtlein (added stir::Box3D test) - + */ -/*! +/*! \def test_ROIs_DISPLAY Enable visual display of the ROIs */ @@ -52,7 +52,7 @@ #include "stir/make_array.h" #include "stir/numerics/determinant.h" #ifdef test_ROIs_DISPLAY -#include "stir/display.h" +# include "stir/display.h" #endif #include @@ -65,246 +65,218 @@ START_NAMESPACE_STIR Visual tests can be enabled by setting the compiler define test_ROIs_DISPLAY. \todo Tests are currently somewhat simplistic - + */ -class ROITests : public RunTests -{ +class ROITests : public RunTests { public: void run_tests(); + private: //! Run a series of tests for a shape /*! - This function tests ROI values and Shape3D::geometric_volume before and after + This function tests ROI values and Shape3D::geometric_volume before and after translating and scaling the shape. \warning fills /changes image and shape \warning - If you want to add new tests, the "scale" and "set_direction_vectors" test-code + If you want to add new tests, the "scale" and "set_direction_vectors" test-code does not work for all shapes as it assumes that the transformed shape is inside the original one. You can disable the "set_direction_vectors" test by setting the do_rotated_ROI_test to false. */ - void run_tests_one_shape(Shape3D& shape, - VoxelsOnCartesianGrid& image, - const bool do_rotated_ROI_test=true); + void run_tests_one_shape(Shape3D& shape, VoxelsOnCartesianGrid& image, const bool do_rotated_ROI_test = true); }; void -ROITests::run_tests_one_shape(Shape3D& shape, - VoxelsOnCartesianGrid& image, - const bool do_rotated_ROI_test) -{ - shape.construct_volume(image, Coordinate3D(1,1,1)); +ROITests::run_tests_one_shape(Shape3D& shape, VoxelsOnCartesianGrid& image, const bool do_rotated_ROI_test) { + shape.construct_volume(image, Coordinate3D(1, 1, 1)); - if (dynamic_cast(&shape) != 0) - { + if (dynamic_cast(&shape) != 0) { #ifdef test_ROIs_DISPLAY - shared_ptr > copy_sptr = - static_cast(shape).get_discretised_density().clone(); + shared_ptr> copy_sptr = + static_cast(shape).get_discretised_density().clone(); #endif - MinimalImageFilter3D erosion_filter(make_coordinate(2,2,2)); - erosion_filter.apply(static_cast(shape).get_discretised_density()); + MinimalImageFilter3D erosion_filter(make_coordinate(2, 2, 2)); + erosion_filter.apply(static_cast(shape).get_discretised_density()); #ifdef test_ROIs_DISPLAY - const float max = copy_sptr->find_max(); - *copy_sptr -= static_cast(shape).get_discretised_density(); - *copy_sptr += max/2; - display(*copy_sptr, - max, - "Original - erosion"); -#endif - } - const CartesianCoordinate3D voxel_size = image.get_voxel_size(); - const float voxel_volume = voxel_size.x() * voxel_size.y() * voxel_size.z(); + const float max = copy_sptr->find_max(); + *copy_sptr -= static_cast(shape).get_discretised_density(); + *copy_sptr += max / 2; + display(*copy_sptr, max, "Original - erosion"); +#endif + } + const CartesianCoordinate3D voxel_size = image.get_voxel_size(); + const float voxel_volume = voxel_size.x() * voxel_size.y() * voxel_size.z(); - image *= 2; + image *= 2; #ifdef test_ROIs_DISPLAY - display(image, image.find_max(), "image corresponding to shape (before erosion in the discretised case)"); + display(image, image.find_max(), "image corresponding to shape (before erosion in the discretised case)"); #endif + { + const ROIValues ROI_values = compute_total_ROI_values(image, shape, Coordinate3D(1, 1, 1)); + + check_if_equal(ROI_values.get_mean(), 2.F, "ROI mean"); + check_if_equal(ROI_values.get_stddev(), 0.F, "ROI stddev"); + check_if_equal(ROI_values.get_max(), 2.F, "ROI max"); + check_if_equal(ROI_values.get_min(), 2.F, "ROI min"); + // check volume + // test supposes that shape is entirely within the volume + const float volume = shape.get_geometric_volume(); + if (volume >= 0) // only test it if it's implemented { - const ROIValues ROI_values = - compute_total_ROI_values(image, shape, Coordinate3D(1,1,1)); - - check_if_equal(ROI_values.get_mean(), 2.F, "ROI mean"); - check_if_equal(ROI_values.get_stddev(), 0.F, "ROI stddev"); - check_if_equal(ROI_values.get_max(), 2.F, "ROI max"); - check_if_equal(ROI_values.get_min(), 2.F, "ROI min"); - // check volume - // test supposes that shape is entirely within the volume - const float volume = shape.get_geometric_volume(); - if (volume>=0) // only test it if it's implemented - { - const double old_tolerance = get_tolerance(); - set_tolerance(pow(volume/voxel_volume,1.F/3)*.1); - check_if_equal(ROI_values.get_roi_volume(), volume, "ROI volume"); - set_tolerance(old_tolerance); - } + const double old_tolerance = get_tolerance(); + set_tolerance(pow(volume / voxel_volume, 1.F / 3) * .1); + check_if_equal(ROI_values.get_roi_volume(), volume, "ROI volume"); + set_tolerance(old_tolerance); } - // test on translation (image and shape translate same way) + } + // test on translation (image and shape translate same way) + { + const CartesianCoordinate3D translation(3, 1, 20); + image.set_origin(image.get_origin() + translation); + shape.translate(translation); + const ROIValues ROI_values = compute_total_ROI_values(image, shape, Coordinate3D(1, 1, 1)); + + check_if_equal(ROI_values.get_mean(), 2.F, "ROI mean after translation"); + check_if_equal(ROI_values.get_stddev(), 0.F, "ROI stddev after translation"); + check_if_equal(ROI_values.get_max(), 2.F, "ROI max after translation"); + check_if_equal(ROI_values.get_min(), 2.F, "ROI min after translation"); + // check volume + // test supposes that shape is entirely within the volume + const float volume = shape.get_geometric_volume(); + if (volume >= 0) // only test it if it's implemented { - const CartesianCoordinate3D translation (3,1,20); - image.set_origin(image.get_origin()+translation); - shape.translate(translation); - const ROIValues ROI_values = - compute_total_ROI_values(image, shape, Coordinate3D(1,1,1)); - - check_if_equal(ROI_values.get_mean(), 2.F, "ROI mean after translation"); - check_if_equal(ROI_values.get_stddev(), 0.F, "ROI stddev after translation"); - check_if_equal(ROI_values.get_max(), 2.F, "ROI max after translation"); - check_if_equal(ROI_values.get_min(), 2.F, "ROI min after translation"); - // check volume - // test supposes that shape is entirely within the volume - const float volume = shape.get_geometric_volume(); - if (volume>=0) // only test it if it's implemented - { - const double old_tolerance = get_tolerance(); - set_tolerance(pow(volume/voxel_volume,1.F/3)*.1); - check_if_equal(ROI_values.get_roi_volume(), volume, "ROI volume after translation"); - set_tolerance(old_tolerance); - } - image.set_origin(image.get_origin()-translation); - shape.translate(translation*-1); + const double old_tolerance = get_tolerance(); + set_tolerance(pow(volume / voxel_volume, 1.F / 3) * .1); + check_if_equal(ROI_values.get_roi_volume(), volume, "ROI volume after translation"); + set_tolerance(old_tolerance); } - // test on translation (image and shape translate separately) + image.set_origin(image.get_origin() - translation); + shape.translate(translation * -1); + } + // test on translation (image and shape translate separately) + { + const CartesianCoordinate3D translation(3, 1, 10); + image.set_origin(image.get_origin() + translation); + const ROIValues ROI_values = compute_total_ROI_values(image, shape, Coordinate3D(1, 1, 1)); + image.set_origin(image.get_origin() - translation); + shape.translate(translation * -1); + const ROIValues ROI_values2 = compute_total_ROI_values(image, shape, Coordinate3D(1, 1, 1)); + shape.translate(translation); + + check_if_equal(ROI_values.get_mean(), ROI_values2.get_mean(), "ROI mean after translation shape vs. image"); + check_if_equal(ROI_values.get_stddev(), ROI_values2.get_stddev(), "ROI stddev after translation shape vs. image"); + check_if_equal(ROI_values.get_max(), ROI_values2.get_max(), "ROI max after translation shape vs. image"); + check_if_equal(ROI_values.get_min(), ROI_values2.get_min(), "ROI min after translation shape vs. image"); + } + // test on scaling (test only works if all scale factors < 1) + if (dynamic_cast(&shape) == 0) { + const float volume_before_scale = shape.get_geometric_volume(); + const CartesianCoordinate3D scale(.5F, .9F, .8F); + const float total_scale = scale[1] * scale[2] * scale[3]; + shared_ptr new_shape_sptr(shape.clone()); + new_shape_sptr->scale_around_origin(scale); + + const ROIValues ROI_values = compute_total_ROI_values(image, *new_shape_sptr, Coordinate3D(1, 1, 1)); + + check_if_equal(ROI_values.get_mean(), 2.F, "ROI mean after scale"); + check_if_equal(ROI_values.get_stddev(), 0.F, "ROI stddev after scale"); + check_if_equal(ROI_values.get_max(), 2.F, "ROI max after scale"); + check_if_equal(ROI_values.get_min(), 2.F, "ROI min after scale"); + // check volume + // test supposes that shape is entirely within the volume + const float volume = new_shape_sptr->get_geometric_volume(); + if (volume >= 0) // only test it if it's implemented { - const CartesianCoordinate3D translation (3,1,10); - image.set_origin(image.get_origin()+translation); - const ROIValues ROI_values = - compute_total_ROI_values(image, shape, Coordinate3D(1,1,1)); - image.set_origin(image.get_origin()-translation); - shape.translate(translation*-1); - const ROIValues ROI_values2 = - compute_total_ROI_values(image, shape, Coordinate3D(1,1,1)); - shape.translate(translation); - - check_if_equal(ROI_values.get_mean(), ROI_values2.get_mean(), "ROI mean after translation shape vs. image"); - check_if_equal(ROI_values.get_stddev(), ROI_values2.get_stddev(), "ROI stddev after translation shape vs. image"); - check_if_equal(ROI_values.get_max(), ROI_values2.get_max(), "ROI max after translation shape vs. image"); - check_if_equal(ROI_values.get_min(), ROI_values2.get_min(), "ROI min after translation shape vs. image"); + check_if_equal(volume_before_scale * total_scale, volume, "shape volume after scale"); + const double old_tolerance = get_tolerance(); + set_tolerance(pow(volume / voxel_volume, 1.F / 3) * .1); + check_if_equal(ROI_values.get_roi_volume(), volume, "ROI volume after scale"); + set_tolerance(old_tolerance); } - // test on scaling (test only works if all scale factors < 1) - if (dynamic_cast(&shape) == 0) - { - const float volume_before_scale = shape.get_geometric_volume(); - const CartesianCoordinate3D scale(.5F,.9F,.8F); - const float total_scale = scale[1]*scale[2]*scale[3]; - shared_ptr new_shape_sptr(shape.clone()); - new_shape_sptr->scale_around_origin(scale); - - const ROIValues ROI_values = - compute_total_ROI_values(image, *new_shape_sptr, Coordinate3D(1,1,1)); - - check_if_equal(ROI_values.get_mean(), 2.F, "ROI mean after scale"); - check_if_equal(ROI_values.get_stddev(), 0.F, "ROI stddev after scale"); - check_if_equal(ROI_values.get_max(), 2.F, "ROI max after scale"); - check_if_equal(ROI_values.get_min(), 2.F, "ROI min after scale"); - // check volume - // test supposes that shape is entirely within the volume - const float volume = new_shape_sptr->get_geometric_volume(); - if (volume>=0) // only test it if it's implemented - { - check_if_equal(volume_before_scale*total_scale, volume, "shape volume after scale"); - const double old_tolerance = get_tolerance(); - set_tolerance(pow(volume/voxel_volume,1.F/3)*.1); - check_if_equal(ROI_values.get_roi_volume(), volume, "ROI volume after scale"); - set_tolerance(old_tolerance); - } #ifdef test_ROIs_DISPLAY - VoxelsOnCartesianGrid image2 = image; - new_shape_sptr->construct_volume(image2, Coordinate3D(1,1,1)); - image2 *= 4; - image2 -= image; - image2 += 2; - display(image2, image2.find_max(), "(image corresponding to scaled (.5,.9,.8) shape)*2 - (original shape) + 1"); + VoxelsOnCartesianGrid image2 = image; + new_shape_sptr->construct_volume(image2, Coordinate3D(1, 1, 1)); + image2 *= 4; + image2 -= image; + image2 += 2; + display(image2, image2.find_max(), "(image corresponding to scaled (.5,.9,.8) shape)*2 - (original shape) + 1"); #endif - } + } - // test on setting direction vectors (test only works if new shape is smaller than original) - if (dynamic_cast(&shape) != 0) + // test on setting direction vectors (test only works if new shape is smaller than original) + if (dynamic_cast(&shape) != 0) { + const float volume_before_scale = shape.get_geometric_volume(); + // rotate over 45 degrees around 1 and scale + const Array<2, float> direction_vectors = + make_array(make_1d_array(1.F, 0.F, 0.F), make_1d_array(0.F, 1.F, 1.F), make_1d_array(0.F, -2.F, 2.F)); + const float total_scale = 1 / determinant(direction_vectors); + shared_ptr new_shape_sptr(dynamic_cast(shape.clone())); + check(new_shape_sptr->set_direction_vectors(direction_vectors) == Succeeded::yes, "set_direction_vectors"); + // std::cerr << new_shape_sptr->parameter_info(); + + const ROIValues ROI_values = compute_total_ROI_values(image, *new_shape_sptr, Coordinate3D(1, 1, 1)); + + if (do_rotated_ROI_test) { + check_if_equal(ROI_values.get_mean(), 2.F, "ROI mean after changing direction vectors"); + check_if_equal(ROI_values.get_stddev(), 0.F, "ROI stddev after changing direction vectors"); + check_if_equal(ROI_values.get_min(), 2.F, "ROI min after changing direction vectors"); + } + check_if_equal(ROI_values.get_max(), 2.F, "ROI max after changing direction vectors"); + // check volume + // test supposes that shape is entirely within the volume + const float volume = new_shape_sptr->get_geometric_volume(); + if (volume >= 0) // only test it if it's implemented { - const float volume_before_scale = shape.get_geometric_volume(); - // rotate over 45 degrees around 1 and scale - const Array<2,float> direction_vectors= - make_array(make_1d_array(1.F,0.F,0.F), - make_1d_array(0.F,1.F,1.F), - make_1d_array(0.F,-2.F,2.F)); - const float total_scale = 1/determinant(direction_vectors); - shared_ptr - new_shape_sptr(dynamic_cast(shape.clone())); - check(new_shape_sptr->set_direction_vectors(direction_vectors) == Succeeded::yes, "set_direction_vectors"); - //std::cerr << new_shape_sptr->parameter_info(); - - const ROIValues ROI_values = - compute_total_ROI_values(image, *new_shape_sptr, Coordinate3D(1,1,1)); - - if (do_rotated_ROI_test) - { - check_if_equal(ROI_values.get_mean(), 2.F, "ROI mean after changing direction vectors"); - check_if_equal(ROI_values.get_stddev(), 0.F, "ROI stddev after changing direction vectors"); - check_if_equal(ROI_values.get_min(), 2.F, "ROI min after changing direction vectors"); - } - check_if_equal(ROI_values.get_max(), 2.F, "ROI max after changing direction vectors"); - // check volume - // test supposes that shape is entirely within the volume - const float volume = new_shape_sptr->get_geometric_volume(); - if (volume>=0) // only test it if it's implemented - { - check_if_equal(volume_before_scale*total_scale, volume, "shape volume after changing direction vectors"); - const double old_tolerance = get_tolerance(); - set_tolerance(pow(volume/voxel_volume,1.F/3)*.1); - check_if_equal(ROI_values.get_roi_volume(), volume, "ROI volume after changing direction vectors"); - set_tolerance(old_tolerance); - } + check_if_equal(volume_before_scale * total_scale, volume, "shape volume after changing direction vectors"); + const double old_tolerance = get_tolerance(); + set_tolerance(pow(volume / voxel_volume, 1.F / 3) * .1); + check_if_equal(ROI_values.get_roi_volume(), volume, "ROI volume after changing direction vectors"); + set_tolerance(old_tolerance); + } #ifdef test_ROIs_DISPLAY - VoxelsOnCartesianGrid image2 = image; - new_shape_sptr->construct_volume(image2, Coordinate3D(1,1,1)); - image2 *= 4; - image2 -= image; - image2 += 2; - display(image2, image2.find_max(), "(image corresponding to rotated and scaled (x,y) shape)*2 - (original shape) + 1"); + VoxelsOnCartesianGrid image2 = image; + new_shape_sptr->construct_volume(image2, Coordinate3D(1, 1, 1)); + image2 *= 4; + image2 -= image; + image2 += 2; + display(image2, image2.find_max(), "(image corresponding to rotated and scaled (x,y) shape)*2 - (original shape) + 1"); #endif - } - // test on parsing - if (dynamic_cast(&shape) == 0) - { - shared_ptr shape_sptr(shape.clone()); - KeyParser parser; - parser.add_start_key("start"); - parser.add_stop_key("stop"); - parser.add_parsing_key("shape type", &shape_sptr); - // construct stream with all info - std::stringstream str; - str << parser.parameter_info(); - // now read it back in and check - if (check(parser.parse(str) && !is_null_ptr(shape_sptr), - "parsing parameters failed")) - { - // check if it's what we expect - if(!check(*shape_sptr == shape, - "parsed shape not equal to original")) - { - std::cerr << "Original: \n" << shape.parameter_info() - << "\nParsed: \n" << shape_sptr->parameter_info(); - } + } + // test on parsing + if (dynamic_cast(&shape) == 0) { + shared_ptr shape_sptr(shape.clone()); + KeyParser parser; + parser.add_start_key("start"); + parser.add_stop_key("stop"); + parser.add_parsing_key("shape type", &shape_sptr); + // construct stream with all info + std::stringstream str; + str << parser.parameter_info(); + // now read it back in and check + if (check(parser.parse(str) && !is_null_ptr(shape_sptr), "parsing parameters failed")) { + // check if it's what we expect + if (!check(*shape_sptr == shape, "parsed shape not equal to original")) { + std::cerr << "Original: \n" << shape.parameter_info() << "\nParsed: \n" << shape_sptr->parameter_info(); } + } } } void ROITests::run_tests() -{ +{ std::cerr << "Tests for compute_ROI_values and Shape3D hierarchy\n"; - - CartesianCoordinate3D origin (0,0,0); - CartesianCoordinate3D grid_spacing (3,4,5); - - const IndexRange<3> - range(Coordinate3D(0,-45,-44), - Coordinate3D(24,44,45)); - VoxelsOnCartesianGrid image(range,origin, grid_spacing); + + CartesianCoordinate3D origin(0, 0, 0); + CartesianCoordinate3D grid_spacing(3, 4, 5); + + const IndexRange<3> range(Coordinate3D(0, -45, -44), Coordinate3D(24, 44, 45)); + VoxelsOnCartesianGrid image(range, origin, grid_spacing); /* WARNING: - If you want to add new tests, the "scale" and "set_direction_vectors" test-code + If you want to add new tests, the "scale" and "set_direction_vectors" test-code in run_tests_one_shape() does not work for all shapes as it assumes that the transformed shape is inside the original one. You can disable the "set_direction_vectors" test by setting the do_rotated_ROI_test to false. @@ -312,91 +284,85 @@ ROITests::run_tests() { std::cerr << "\tTests with ellipsoidal cylinder.\n"; // object at centre of image - EllipsoidalCylinder - cylinder(/*length*/image.size()*grid_spacing.z()/3, - /*radius_x*/image[0][0].size()*grid_spacing.x()/4, - /*radius_y*/image[0].size()*grid_spacing.y()/4, - /*centre*/CartesianCoordinate3D((image.get_min_index()+image.get_max_index())/2*grid_spacing.z(), 0,0)); + EllipsoidalCylinder cylinder( + /*length*/ image.size() * grid_spacing.z() / 3, + /*radius_x*/ image[0][0].size() * grid_spacing.x() / 4, + /*radius_y*/ image[0].size() * grid_spacing.y() / 4, + /*centre*/ CartesianCoordinate3D((image.get_min_index() + image.get_max_index()) / 2 * grid_spacing.z(), 0, 0)); this->run_tests_one_shape(cylinder, image); } image.set_origin(origin); { std::cerr << "\tTests with ellipsoidal cylinder and wedge.\n"; // object at centre of image - EllipsoidalCylinder - cylinder(/*length*/image.size()*grid_spacing.z()/3, - /*radius_x*/image[0][0].size()*grid_spacing.x()/4, - /*radius_y*/image[0].size()*grid_spacing.y()/4, - /* theta_1 */ 10.F, - /* theta_2 */ 280.F, - /*centre*/CartesianCoordinate3D((image.get_min_index()+image.get_max_index())/2*grid_spacing.z(), 0,0)); - this->run_tests_one_shape(cylinder, image, /*do_rotated_ROI_test=*/ false); + EllipsoidalCylinder cylinder( + /*length*/ image.size() * grid_spacing.z() / 3, + /*radius_x*/ image[0][0].size() * grid_spacing.x() / 4, + /*radius_y*/ image[0].size() * grid_spacing.y() / 4, + /* theta_1 */ 10.F, + /* theta_2 */ 280.F, + /*centre*/ CartesianCoordinate3D((image.get_min_index() + image.get_max_index()) / 2 * grid_spacing.z(), 0, 0)); + this->run_tests_one_shape(cylinder, image, /*do_rotated_ROI_test=*/false); } image.set_origin(origin); { std::cerr << "\tTests with ellipsoid.\n"; // object at centre of image - Ellipsoid - ellipsoid(CartesianCoordinate3D(/*radius_z*/image.size()*grid_spacing.z()/3, - /*radius_y*/image[0].size()*grid_spacing.y()/5, - /*radius_x*/image[0][0].size()*grid_spacing.x()/4), - /*centre*/CartesianCoordinate3D((image.get_min_index()+image.get_max_index())/2*grid_spacing.z(), 0,0)); + Ellipsoid ellipsoid( + CartesianCoordinate3D(/*radius_z*/ image.size() * grid_spacing.z() / 3, + /*radius_y*/ image[0].size() * grid_spacing.y() / 5, + /*radius_x*/ image[0][0].size() * grid_spacing.x() / 4), + /*centre*/ CartesianCoordinate3D((image.get_min_index() + image.get_max_index()) / 2 * grid_spacing.z(), 0, 0)); this->run_tests_one_shape(ellipsoid, image); } { std::cerr << "\tTests with Box3D.\n"; // object at centre of image - Box3D - box(/*length_x*/image[0][0].size()*grid_spacing.x()/4, - /*length_y*/image[0].size()*grid_spacing.y()/5, - /*length_z*/image.size()*grid_spacing.z()/3, - /*centre*/CartesianCoordinate3D((image.get_min_index()+image.get_max_index())/2*grid_spacing.z(), 0,0)); + Box3D box( + /*length_x*/ image[0][0].size() * grid_spacing.x() / 4, + /*length_y*/ image[0].size() * grid_spacing.y() / 5, + /*length_z*/ image.size() * grid_spacing.z() / 3, + /*centre*/ CartesianCoordinate3D((image.get_min_index() + image.get_max_index()) / 2 * grid_spacing.z(), 0, 0)); this->run_tests_one_shape(box, image); - } + } { std::cerr << "\tTests with DiscretisedShape3D.\n"; // object at centre of image - Ellipsoid - ellipsoid(CartesianCoordinate3D(/*radius_z*/image.size()*grid_spacing.z()/3, - /*radius_y*/image[0].size()*grid_spacing.y()/5, - /*radius_x*/image[0][0].size()*grid_spacing.x()/4), - /*centre*/CartesianCoordinate3D((image.get_min_index()+image.get_max_index())/2*grid_spacing.z(), 0,0)); + Ellipsoid ellipsoid( + CartesianCoordinate3D(/*radius_z*/ image.size() * grid_spacing.z() / 3, + /*radius_y*/ image[0].size() * grid_spacing.y() / 5, + /*radius_x*/ image[0][0].size() * grid_spacing.x() / 4), + /*centre*/ CartesianCoordinate3D((image.get_min_index() + image.get_max_index()) / 2 * grid_spacing.z(), 0, 0)); // note: it is important to use num_samples=(1,1,1) here, otherwise tests will fail - // this is because the shape would have smooth edges, and the tests do not take + // this is because the shape would have smooth edges, and the tests do not take // that into account - ellipsoid.construct_volume(image, make_coordinate(1,1,1)); + ellipsoid.construct_volume(image, make_coordinate(1, 1, 1)); { DiscretisedShape3D discretised_shape(image); std::cerr << "\t\tidentical image\n"; this->run_tests_one_shape(discretised_shape, image); } // need to fill in image again, as the tests change it - ellipsoid.construct_volume(image, make_coordinate(1,1,1)); + ellipsoid.construct_volume(image, make_coordinate(1, 1, 1)); { std::cerr << "\t\tNot-identical image\n"; DiscretisedShape3D discretised_shape(image); - CartesianCoordinate3D other_origin (2,4,9); - CartesianCoordinate3D other_grid_spacing (3.3,4.4,5.5); - - const IndexRange<3> - other_range(Coordinate3D(-1,-40,-43), - Coordinate3D(25,45,47)); - VoxelsOnCartesianGrid other_image(other_range,other_origin, other_grid_spacing); + CartesianCoordinate3D other_origin(2, 4, 9); + CartesianCoordinate3D other_grid_spacing(3.3, 4.4, 5.5); + + const IndexRange<3> other_range(Coordinate3D(-1, -40, -43), Coordinate3D(25, 45, 47)); + VoxelsOnCartesianGrid other_image(other_range, other_origin, other_grid_spacing); this->run_tests_one_shape(discretised_shape, other_image); } - } - } END_NAMESPACE_STIR - USING_NAMESPACE_STIR - -int main() -{ +int +main() { ROITests tests; tests.run_tests(); return tests.main_return_value(); diff --git a/src/test/test_Scanner.cxx b/src/test/test_Scanner.cxx index e01b427c9b..42bf736e65 100644 --- a/src/test/test_Scanner.cxx +++ b/src/test/test_Scanner.cxx @@ -31,12 +31,12 @@ #include "stir/Succeeded.h" #include "stir/shared_ptr.h" #ifdef HAVE_LLN_MATRIX -#include "ecat_model.h" +# include "ecat_model.h" extern "C" { - EcatModel *ecat_model(int); +EcatModel* ecat_model(int); } -#include "stir/IO/stir_ecat_common.h" +# include "stir/IO/stir_ecat_common.h" #endif #include #include @@ -51,23 +51,19 @@ START_NAMESPACE_STIR \ingroup test \brief Test class for Scanner */ -class ScannerTests: public RunTests -{ -public: +class ScannerTests : public RunTests { +public: void run_tests(); + private: void test_scanner(const Scanner&); }; - void -ScannerTests:: -run_tests() -{ - Scanner::Type type= Scanner::E931; - while (type != Scanner::Unknown_scanner) - { - if (type!=Scanner::User_defined_scanner) +ScannerTests::run_tests() { + Scanner::Type type = Scanner::E931; + while (type != Scanner::Unknown_scanner) { + if (type != Scanner::User_defined_scanner) test_scanner(Scanner(type)); // tricky business to find next type int int_type = type; @@ -77,20 +73,18 @@ run_tests() } void -ScannerTests:: -test_scanner(const Scanner& scanner) -{ +ScannerTests::test_scanner(const Scanner& scanner) { set_tolerance(.00001); - cerr << "Tests for scanner model " << scanner.get_name()<<'\n'; + cerr << "Tests for scanner model " << scanner.get_name() << '\n'; check(scanner.check_consistency() == Succeeded::yes, "check_consistency"); /* check if number of non-arccorrected tangential positions is smaller than the maximum - allowed for a full-ring tomograph + allowed for a full-ring tomograph */ { check(scanner.get_max_num_non_arccorrected_bins() <= scanner.get_num_detectors_per_ring(), - "too large max_num_non_arccorrected_bins compared to num_detectors_per_ring"); + "too large max_num_non_arccorrected_bins compared to num_detectors_per_ring"); } /* check if default_bin_size is close to the central bin size. This is especially true for CTI scanners. @@ -104,22 +98,20 @@ test_scanner(const Scanner& scanner) scanner.get_type() != Scanner::DiscoveryRX/* && scanner.get_type() != Scanner::HZLR*/) { - const float natural_bin_size = - scanner.get_inner_ring_radius()*float(_PI)/scanner.get_num_detectors_per_ring(); - if (fabs(natural_bin_size - scanner.get_default_bin_size())> .03) + const float natural_bin_size = scanner.get_inner_ring_radius() * float(_PI) / scanner.get_num_detectors_per_ring(); + if (fabs(natural_bin_size - scanner.get_default_bin_size()) > .03) warning("central bin size (derived from inner ring radius and num detectors) %g\n" - "differs from given default bin size %g\n" - "(unequal values do not necessarily mean there's an error as " - "it's a convention used by the scanner manufacturer)\n", - natural_bin_size, scanner.get_default_bin_size()); + "differs from given default bin size %g\n" + "(unequal values do not necessarily mean there's an error as " + "it's a convention used by the scanner manufacturer)\n", + natural_bin_size, scanner.get_default_bin_size()); } // (weak) test on get_scanner_from_name { string name = scanner.get_name(); name += " "; shared_ptr scanner_from_name_sptr(Scanner::get_scanner_from_name(name)); - check_if_equal(scanner.get_type(), scanner_from_name_sptr->get_type(), - "get_scanner_from_name"); + check_if_equal(scanner.get_type(), scanner_from_name_sptr->get_type(), "get_scanner_from_name"); } #ifdef HAVE_LLN_MATRIX if (scanner.get_type() <= Scanner::E966) // TODO relies on ordering of enum @@ -128,45 +120,37 @@ test_scanner(const Scanner& scanner) cerr << "Comparing STIR scanner info with LLN matrix\n"; short ecat_type = ecat::find_ECAT_system_type(scanner); - if (ecat_type==0) + if (ecat_type == 0) return; - EcatModel * ecat_scanner_info = ecat_model(static_cast(ecat_type)); - if (ecat_scanner_info==0) + EcatModel* ecat_scanner_info = ecat_model(static_cast(ecat_type)); + if (ecat_scanner_info == 0) return; - check_if_equal(scanner.get_num_axial_buckets(), ecat_scanner_info->rings, - "number of rings of buckets"); + check_if_equal(scanner.get_num_axial_buckets(), ecat_scanner_info->rings, "number of rings of buckets"); if (scanner.get_type() != Scanner::E925) // ART is a partial ring tomograph - check_if_equal(scanner.get_num_axial_buckets()*scanner.get_num_transaxial_buckets(), - ecat_scanner_info->nbuckets, - "total number of buckets"); + check_if_equal(scanner.get_num_axial_buckets() * scanner.get_num_transaxial_buckets(), ecat_scanner_info->nbuckets, + "total number of buckets"); check_if_equal(scanner.get_num_transaxial_blocks_per_bucket(), ecat_scanner_info->transBlocksPerBucket, - "transaxial blocks per bucket"); - check_if_equal(scanner.get_num_axial_blocks_per_bucket(), ecat_scanner_info->axialBlocksPerBucket, - "axial blocks per bucket"); - check_if_equal(scanner.get_num_transaxial_blocks_per_bucket() * scanner.get_num_axial_blocks_per_bucket(), - ecat_scanner_info->blocks, - "total number of blocks"); + "transaxial blocks per bucket"); + check_if_equal(scanner.get_num_axial_blocks_per_bucket(), ecat_scanner_info->axialBlocksPerBucket, "axial blocks per bucket"); + check_if_equal(scanner.get_num_transaxial_blocks_per_bucket() * scanner.get_num_axial_blocks_per_bucket(), + ecat_scanner_info->blocks, "total number of blocks"); check_if_equal(scanner.get_num_axial_crystals_per_block(), ecat_scanner_info->axialCrystalsPerBlock, - "number of crystals in the axial direction"); + "number of crystals in the axial direction"); check_if_equal(scanner.get_num_transaxial_crystals_per_block(), ecat_scanner_info->angularCrystalsPerBlock, - "number of transaxial crystals"); - check_if_equal(scanner.get_inner_ring_radius(), ecat_scanner_info->crystalRad*10, - "detector radius"); - check_if_equal(scanner.get_ring_spacing()/2, ecat_scanner_info->planesep*10, - "plane separation"); - check_if_equal(scanner.get_default_bin_size(), ecat_scanner_info->binsize*10, - "bin size (spacing of transaxial elements)"); + "number of transaxial crystals"); + check_if_equal(scanner.get_inner_ring_radius(), ecat_scanner_info->crystalRad * 10, "detector radius"); + check_if_equal(scanner.get_ring_spacing() / 2, ecat_scanner_info->planesep * 10, "plane separation"); + check_if_equal(scanner.get_default_bin_size(), ecat_scanner_info->binsize * 10, "bin size (spacing of transaxial elements)"); } #endif -} +} END_NAMESPACE_STIR - -int main() -{ -USING_NAMESPACE_STIR +int +main() { + USING_NAMESPACE_STIR ScannerTests tests; tests.run_tests(); diff --git a/src/test/test_ScatterSimulation.cxx b/src/test/test_ScatterSimulation.cxx index 5110178d64..5048737f53 100644 --- a/src/test/test_ScatterSimulation.cxx +++ b/src/test/test_ScatterSimulation.cxx @@ -37,11 +37,11 @@ #include "stir/zoom.h" #include "stir/round.h" #if 0 -#include "stir/recon_buildblock/ForwardProjectorByBin.h" -#include "stir/recon_buildblock/ForwardProjectorByBinUsingRayTracing.h" -#include "stir/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.h" -#include "stir/recon_buildblock/ProjMatrixByBinUsingRayTracing.h" -#include "stir/recon_buildblock/BinNormalisationFromAttenuationImage.h" +# include "stir/recon_buildblock/ForwardProjectorByBin.h" +# include "stir/recon_buildblock/ForwardProjectorByBinUsingRayTracing.h" +# include "stir/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.h" +# include "stir/recon_buildblock/ProjMatrixByBinUsingRayTracing.h" +# include "stir/recon_buildblock/BinNormalisationFromAttenuationImage.h" #endif #include "stir/Shape/EllipsoidalCylinder.h" #include "stir/Shape/Box3D.h" @@ -62,195 +62,180 @@ START_NAMESPACE_STIR \ingroup scatter \brief Test class for ScatterSimulation */ -class ScatterSimulationTests: public RunTests -{ -public: - bool write_output; - void run_tests(); -private: +class ScatterSimulationTests : public RunTests { +public: + bool write_output; + void run_tests(); - //! Load a ProjDataInfo downsample and perform some consistency checks. - void test_downsampling_ProjDataInfo(); - //! Load an attenuation image for scatter points, downsample and check if - //! the mean value is approximately the same. - void test_downsampling_DiscretisedDensity(); +private: + //! Load a ProjDataInfo downsample and perform some consistency checks. + void test_downsampling_ProjDataInfo(); + //! Load an attenuation image for scatter points, downsample and check if + //! the mean value is approximately the same. + void test_downsampling_DiscretisedDensity(); - //! Do simulation of object in the centre, check if symmetric - void test_scatter_simulation(); + //! Do simulation of object in the centre, check if symmetric + void test_scatter_simulation(); - void test_symmetric(ScatterSimulation& sss, const std::string& name); - void test_output_is_symmetric(const ProjData& proj_data, const std::string& name); + void test_symmetric(ScatterSimulation& sss, const std::string& name); + void test_output_is_symmetric(const ProjData& proj_data, const std::string& name); }; +void +ScatterSimulationTests::test_downsampling_ProjDataInfo() { -void ScatterSimulationTests:: -test_downsampling_ProjDataInfo() -{ - - Scanner::Type type= Scanner::E931; - shared_ptr test_scanner(new Scanner(type)); + Scanner::Type type = Scanner::E931; + shared_ptr test_scanner(new Scanner(type)); - // Create the original projdata - shared_ptr original_projdata( dynamic_cast( - ProjDataInfo::ProjDataInfoCTI(test_scanner, - 1, test_scanner->get_num_rings()-1, - test_scanner->get_num_detectors_per_ring()/2, - test_scanner->get_max_num_non_arccorrected_bins(), - false))); + // Create the original projdata + shared_ptr original_projdata(dynamic_cast( + ProjDataInfo::ProjDataInfoCTI(test_scanner, 1, test_scanner->get_num_rings() - 1, + test_scanner->get_num_detectors_per_ring() / 2, + test_scanner->get_max_num_non_arccorrected_bins(), false))); + unique_ptr sss(new SingleScatterSimulation()); + sss->set_template_proj_data_info(*original_projdata); - unique_ptr sss(new SingleScatterSimulation()); - sss->set_template_proj_data_info(*original_projdata); + { + auto sss_projdata(sss->get_template_proj_data_info_sptr()); + check(*original_projdata == *sss_projdata, "Check the ProjDataInfo has been set correctly."); + } - { - auto sss_projdata(sss->get_template_proj_data_info_sptr()); - check(*original_projdata == *sss_projdata, "Check the ProjDataInfo has been set correctly."); - } + // Downsample the scanner 50% + { + int down_rings = static_cast(test_scanner->get_num_rings() * 0.5 + 0.5); + int down_dets = static_cast(test_scanner->get_num_detectors_per_ring() * 0.5); - // Downsample the scanner 50% - { - int down_rings = static_cast(test_scanner->get_num_rings()*0.5 + 0.5); - int down_dets = static_cast(test_scanner->get_num_detectors_per_ring() * 0.5); - - sss->downsample_scanner(down_rings, down_dets); - auto sss_projdata(sss->get_template_proj_data_info_sptr()); - check_if_equal(original_projdata->get_scanner_ptr()->get_num_rings(), 2*sss_projdata->get_scanner_ptr()->get_num_rings(), "Check the number of rings is correct"); - check_if_equal(original_projdata->get_scanner_ptr()->get_num_detectors_per_ring(), - 2*sss_projdata->get_scanner_ptr()->get_num_detectors_per_ring(), "Check number of detectors per ring."); - - set_tolerance(0.01); - check_if_equal(2.f*original_projdata->get_ring_spacing(), sss_projdata->get_ring_spacing(), "Check the ring spacing."); - check_if_equal(2.f*original_projdata->get_axial_sampling(0), sss_projdata->get_axial_sampling(0), "Check axial samping. Seg 0"); - - check_if_equal(2.f*original_projdata->get_axial_sampling(original_projdata->get_min_segment_num()), - sss_projdata->get_axial_sampling(sss_projdata->get_min_segment_num()), "Check axial samping. Min. Seg"); - check_if_equal(2.f*original_projdata->get_axial_sampling(original_projdata->get_max_segment_num()), - sss_projdata->get_axial_sampling(sss_projdata->get_max_segment_num()), "Check axial samping. Max Seg."); - - Bin b1(original_projdata->get_min_segment_num(),0, - original_projdata->get_max_axial_pos_num(original_projdata->get_min_segment_num())/2,0); - Bin b2(sss_projdata->get_min_segment_num(),0, - sss_projdata->get_max_axial_pos_num(sss_projdata->get_min_segment_num())/2,0); - check_if_equal(original_projdata->get_m(b1), sss_projdata->get_m(b2), "Check center of Bin (min_seg, 0, mid_axial, 0, 0)"); - } + sss->downsample_scanner(down_rings, down_dets); + auto sss_projdata(sss->get_template_proj_data_info_sptr()); + check_if_equal(original_projdata->get_scanner_ptr()->get_num_rings(), 2 * sss_projdata->get_scanner_ptr()->get_num_rings(), + "Check the number of rings is correct"); + check_if_equal(original_projdata->get_scanner_ptr()->get_num_detectors_per_ring(), + 2 * sss_projdata->get_scanner_ptr()->get_num_detectors_per_ring(), "Check number of detectors per ring."); + set_tolerance(0.01); + check_if_equal(2.f * original_projdata->get_ring_spacing(), sss_projdata->get_ring_spacing(), "Check the ring spacing."); + check_if_equal(2.f * original_projdata->get_axial_sampling(0), sss_projdata->get_axial_sampling(0), + "Check axial samping. Seg 0"); + + check_if_equal(2.f * original_projdata->get_axial_sampling(original_projdata->get_min_segment_num()), + sss_projdata->get_axial_sampling(sss_projdata->get_min_segment_num()), "Check axial samping. Min. Seg"); + check_if_equal(2.f * original_projdata->get_axial_sampling(original_projdata->get_max_segment_num()), + sss_projdata->get_axial_sampling(sss_projdata->get_max_segment_num()), "Check axial samping. Max Seg."); + + Bin b1(original_projdata->get_min_segment_num(), 0, + original_projdata->get_max_axial_pos_num(original_projdata->get_min_segment_num()) / 2, 0); + Bin b2(sss_projdata->get_min_segment_num(), 0, sss_projdata->get_max_axial_pos_num(sss_projdata->get_min_segment_num()) / 2, + 0); + check_if_equal(original_projdata->get_m(b1), sss_projdata->get_m(b2), "Check center of Bin (min_seg, 0, mid_axial, 0, 0)"); + } } -void ScatterSimulationTests:: -test_downsampling_DiscretisedDensity() -{ - Scanner::Type type= Scanner::E931; - shared_ptr test_scanner(new Scanner(type)); - - // Create the original projdata - shared_ptr original_projdata( dynamic_cast( - ProjDataInfo::ProjDataInfoCTI(test_scanner, - 1, test_scanner->get_num_rings()-1, - test_scanner->get_num_detectors_per_ring()/2, - test_scanner->get_max_num_non_arccorrected_bins(), - false))); - - // Create an appropriate image for the projdata. - shared_ptr > tmpl_density( new VoxelsOnCartesianGrid(*original_projdata)); - +void +ScatterSimulationTests::test_downsampling_DiscretisedDensity() { + Scanner::Type type = Scanner::E931; + shared_ptr test_scanner(new Scanner(type)); - Box3D phantom(tmpl_density->get_x_size()*tmpl_density->get_voxel_size().x()*0.25, - tmpl_density->get_y_size()*tmpl_density->get_voxel_size().y()*0.25, - tmpl_density->get_z_size()*tmpl_density->get_voxel_size().z()*2, - tmpl_density->get_origin()); + // Create the original projdata + shared_ptr original_projdata(dynamic_cast( + ProjDataInfo::ProjDataInfoCTI(test_scanner, 1, test_scanner->get_num_rings() - 1, + test_scanner->get_num_detectors_per_ring() / 2, + test_scanner->get_max_num_non_arccorrected_bins(), false))); + // Create an appropriate image for the projdata. + shared_ptr> tmpl_density(new VoxelsOnCartesianGrid(*original_projdata)); - CartesianCoordinate3D num_samples(3,3,3); - shared_ptr > water_density(tmpl_density->clone()); + Box3D phantom(tmpl_density->get_x_size() * tmpl_density->get_voxel_size().x() * 0.25, + tmpl_density->get_y_size() * tmpl_density->get_voxel_size().y() * 0.25, + tmpl_density->get_z_size() * tmpl_density->get_voxel_size().z() * 2, tmpl_density->get_origin()); - phantom.construct_volume(*water_density, num_samples); - // Water attenuation coefficient. - *water_density *= 9.687E-02; + CartesianCoordinate3D num_samples(3, 3, 3); + shared_ptr> water_density(tmpl_density->clone()); -// EllipsoidalCylinder cyl2(tmpl_density->get_z_size()*tmpl_density->get_voxel_size().z()*2, -// tmpl_density->get_y_size()*tmpl_density->get_voxel_size().y()*0.125, -// tmpl_density->get_x_size()*tmpl_density->get_voxel_size().x()*0.125, -// tmpl_density->get_origin()); + phantom.construct_volume(*water_density, num_samples); + // Water attenuation coefficient. + *water_density *= 9.687E-02; -// shared_ptr > bone_density(tmpl_density->clone()); + // EllipsoidalCylinder cyl2(tmpl_density->get_z_size()*tmpl_density->get_voxel_size().z()*2, + // tmpl_density->get_y_size()*tmpl_density->get_voxel_size().y()*0.125, + // tmpl_density->get_x_size()*tmpl_density->get_voxel_size().x()*0.125, + // tmpl_density->get_origin()); -// cyl2.construct_volume(*bone_density, num_samples); + // shared_ptr > bone_density(tmpl_density->clone()); -// // Watter attenuation coefficient. -// *bone_density *= 9.687E-02 - 9.696E-02; + // cyl2.construct_volume(*bone_density, num_samples); - shared_ptr > atten_density(new VoxelsOnCartesianGrid(*tmpl_density)); + // // Watter attenuation coefficient. + // *bone_density *= 9.687E-02 - 9.696E-02; -// *atten_density = *bone_density; - *atten_density += *water_density; + shared_ptr> atten_density(new VoxelsOnCartesianGrid(*tmpl_density)); - unique_ptr sss(new SingleScatterSimulation()); + // *atten_density = *bone_density; + *atten_density += *water_density; - sss->set_template_proj_data_info(*original_projdata); - sss->set_density_image_sptr(atten_density); + unique_ptr sss(new SingleScatterSimulation()); -// int total_scatter_points_orig = sss.get_num_scatter_points(); + sss->set_template_proj_data_info(*original_projdata); + sss->set_density_image_sptr(atten_density); - sss->downsample_density_image_for_scatter_points(0.5f, 0.5f, 1); - - auto downed_image = sss->get_density_image_for_scatter_points_sptr(); + // int total_scatter_points_orig = sss.get_num_scatter_points(); + sss->downsample_density_image_for_scatter_points(0.5f, 0.5f, 1); - float mean_value_atten = 0.0f; - int atten_counter = 0; + auto downed_image = sss->get_density_image_for_scatter_points_sptr(); - BasicCoordinate<3,int> min_index, max_index ; - CartesianCoordinate3D coord; + float mean_value_atten = 0.0f; + int atten_counter = 0; - atten_density->get_regular_range(min_index, max_index); + BasicCoordinate<3, int> min_index, max_index; + CartesianCoordinate3D coord; - for(coord[1]=min_index[1];coord[1]<=max_index[1];++coord[1]) - for(coord[2]=min_index[2];coord[2]<=max_index[2];++coord[2]) - for(coord[3]=min_index[3];coord[3]<=max_index[3];++coord[3]) - if((*atten_density)[coord] > 0.02f) - { - atten_counter++; - mean_value_atten += (*atten_density)[coord]; - } + atten_density->get_regular_range(min_index, max_index); - mean_value_atten /= atten_counter; + for (coord[1] = min_index[1]; coord[1] <= max_index[1]; ++coord[1]) + for (coord[2] = min_index[2]; coord[2] <= max_index[2]; ++coord[2]) + for (coord[3] = min_index[3]; coord[3] <= max_index[3]; ++coord[3]) + if ((*atten_density)[coord] > 0.02f) { + atten_counter++; + mean_value_atten += (*atten_density)[coord]; + } - float mean_value_downed = 0.0f; - int downed_counter = 0; + mean_value_atten /= atten_counter; - downed_image->get_regular_range(min_index, max_index); + float mean_value_downed = 0.0f; + int downed_counter = 0; - for(coord[1]=min_index[1]+1;coord[1]<=max_index[1]-1;++coord[1]) - for(coord[2]=min_index[2];coord[2]<=max_index[2];++coord[2]) - for(coord[3]=min_index[3];coord[3]<=max_index[3];++coord[3]) - if((*downed_image)[coord] > 0.02f) - { - downed_counter++; - mean_value_downed += (*downed_image)[coord]; - } + downed_image->get_regular_range(min_index, max_index); - mean_value_downed /= downed_counter; + for (coord[1] = min_index[1] + 1; coord[1] <= max_index[1] - 1; ++coord[1]) + for (coord[2] = min_index[2]; coord[2] <= max_index[2]; ++coord[2]) + for (coord[3] = min_index[3]; coord[3] <= max_index[3]; ++coord[3]) + if ((*downed_image)[coord] > 0.02f) { + downed_counter++; + mean_value_downed += (*downed_image)[coord]; + } - set_tolerance(0.1); - check_if_equal(mean_value_atten, mean_value_downed, "Check the mean value of downsampled image."); - set_tolerance(0.01); + mean_value_downed /= downed_counter; - CartesianCoordinate3D cog_atten = find_centre_of_gravity_in_mm(*atten_density); - CartesianCoordinate3D cog_downed = find_centre_of_gravity_in_mm(*dynamic_cast*>(downed_image.get())); + set_tolerance(0.1); + check_if_equal(mean_value_atten, mean_value_downed, "Check the mean value of downsampled image."); + set_tolerance(0.01); + CartesianCoordinate3D cog_atten = find_centre_of_gravity_in_mm(*atten_density); + CartesianCoordinate3D cog_downed = + find_centre_of_gravity_in_mm(*dynamic_cast*>(downed_image.get())); - check_if_equal(cog_atten, cog_downed, "Check centre of gravity of the original image is the same as the downsampled."); -// int total_scatter_points_down = sss.get_num_scatter_points(); + check_if_equal(cog_atten, cog_downed, "Check centre of gravity of the original image is the same as the downsampled."); + // int total_scatter_points_down = sss.get_num_scatter_points(); -// std::string density_image_for_scatter_points_output_filename("./output_image"); -// write_to_file(density_image_for_scatter_points_output_filename, -// *downed_image); -//int debug_stop = 0; + // std::string density_image_for_scatter_points_output_filename("./output_image"); + // write_to_file(density_image_for_scatter_points_output_filename, + // *downed_image); + // int debug_stop = 0; } void -ScatterSimulationTests::test_symmetric(ScatterSimulation& ss, const std::string& name) -{ +ScatterSimulationTests::test_symmetric(ScatterSimulation& ss, const std::string& name) { auto output_projdata_info(ss.get_template_proj_data_info_sptr()); shared_ptr sss_output(new ProjDataInMemory(ss.get_exam_info_sptr(), output_projdata_info)); ss.set_output_proj_data_sptr(sss_output); @@ -258,12 +243,11 @@ ScatterSimulationTests::test_symmetric(ScatterSimulation& ss, const std::string& std::cerr << "\nSetting up for test " << name << "\n"; check(ss.set_up() == Succeeded::yes ? true : false, "Check Scatter Simulation set_up. test " + name); - if (this->write_output) - { - write_to_file("my_sss_activity__"+name+".hv", ss.get_activity_image()); - write_to_file("my_sss_attenuation_"+name+".hv", ss.get_attenuation_image()); - write_to_file("my_sss_scatter-points_"+name+".hv", ss.get_attenuation_image_for_scatter_points()); - } + if (this->write_output) { + write_to_file("my_sss_activity__" + name + ".hv", ss.get_activity_image()); + write_to_file("my_sss_attenuation_" + name + ".hv", ss.get_attenuation_image()); + write_to_file("my_sss_scatter-points_" + name + ".hv", ss.get_attenuation_image_for_scatter_points()); + } std::cerr << "Starting processing\n"; check(ss.process_data() == Succeeded::yes ? true : false, "Check Scatter Simulation process. test " + name); @@ -272,12 +256,12 @@ ScatterSimulationTests::test_symmetric(ScatterSimulation& ss, const std::string& // check if max within 5% const SegmentByView seg = sss_output->get_segment_by_view(0); - //check(seg.find_max()>0.F, "Check Scatter Simulation output not zero. test " + name); + // check(seg.find_max()>0.F, "Check Scatter Simulation output not zero. test " + name); { const float approx_max = 0.195F; const double old_tolerance = get_tolerance(); set_tolerance(.5); - check_if_equal(seg.find_max()*1000, approx_max*1000, + check_if_equal(seg.find_max() * 1000, approx_max * 1000, "Check Scatter Simulation output maximum value is approximately ok. test " + name); set_tolerance(old_tolerance); } @@ -289,40 +273,33 @@ ScatterSimulationTests::test_symmetric(ScatterSimulation& ss, const std::string& } void -ScatterSimulationTests::test_output_is_symmetric(const ProjData& proj_data, const std::string& name) -{ +ScatterSimulationTests::test_output_is_symmetric(const ProjData& proj_data, const std::string& name) { SegmentBySinogram seg = proj_data.get_segment_by_sinogram(0); - seg *=1000; // work-around problem in RunTests::check_if_equal for floats that it can fail for small numbers + seg *= 1000; // work-around problem in RunTests::check_if_equal for floats that it can fail for small numbers // values have to be symmetric around the middle of the scanner - for (int first=seg.get_min_axial_pos_num(), last=seg.get_max_axial_pos_num(); - first=seg.get_min_tangential_pos_num() && last<=seg.get_max_tangential_pos_num(); + for (int first = -1, last = +1; first >= seg.get_min_tangential_pos_num() && last <= seg.get_max_tangential_pos_num(); --first, ++last) check_if_equal(seg[3][0][first], seg[3][0][last], "check if symmetric along the tangential_pos direction. test " + name); - // test views. Need to reduce tolerance due to discretisation artefacts { const double old_tolerance = get_tolerance(); set_tolerance(.1); - const int mid_axial_pos_num = (seg.get_min_axial_pos_num()+seg.get_max_axial_pos_num())/2; + const int mid_axial_pos_num = (seg.get_min_axial_pos_num() + seg.get_max_axial_pos_num()) / 2; const int first_view = seg.get_min_view_num(); // we will compare the middle 11 entries // (the profile goes down a lot, and we want to avoid errors for very small numbers) - Array<1,float> row_first = seg[mid_axial_pos_num][first_view]; - row_first.resize(-5,5); - for (int view=seg.get_min_view_num() + 1; view<=seg.get_max_view_num(); ++view) - { - Array<1,float> row = seg[mid_axial_pos_num][view]; - row.resize(-5,5); - check_if_equal(row_first, row, - "check if symmetric along views. test " + name); - } + Array<1, float> row_first = seg[mid_axial_pos_num][first_view]; + row_first.resize(-5, 5); + for (int view = seg.get_min_view_num() + 1; view <= seg.get_max_view_num(); ++view) { + Array<1, float> row = seg[mid_axial_pos_num][view]; + row.resize(-5, 5); + check_if_equal(row_first, row, "check if symmetric along views. test " + name); + } set_tolerance(old_tolerance); } #if 0 @@ -333,229 +310,212 @@ ScatterSimulationTests::test_output_is_symmetric(const ProjData& proj_data, cons } void -ScatterSimulationTests::test_scatter_simulation() -{ - unique_ptr sss(new SingleScatterSimulation()); - - Scanner::Type type= Scanner::E931; - shared_ptr test_scanner(new Scanner(type)); - const float scanner_length = test_scanner->get_num_rings() * test_scanner->get_ring_spacing(); - - std::cerr << "Testing scatter simulation for the following scanner:\n" - << test_scanner->parameter_info() - << "\nAxial length = " << scanner_length << " mm" << std::endl; - - if(!test_scanner->has_energy_information()) - { - test_scanner->set_reference_energy(511); - test_scanner->set_energy_resolution(0.34f); - } +ScatterSimulationTests::test_scatter_simulation() { + unique_ptr sss(new SingleScatterSimulation()); - check(test_scanner->has_energy_information() == true, "Check the scanner has energy information."); + Scanner::Type type = Scanner::E931; + shared_ptr test_scanner(new Scanner(type)); + const float scanner_length = test_scanner->get_num_rings() * test_scanner->get_ring_spacing(); - shared_ptr exam(new ExamInfo); - exam->set_low_energy_thres(450); - exam->set_high_energy_thres(650); - exam->imaging_modality = ImagingModality::PT; + std::cerr << "Testing scatter simulation for the following scanner:\n" + << test_scanner->parameter_info() << "\nAxial length = " << scanner_length << " mm" << std::endl; - check(exam->has_energy_information() == true, "Check the ExamInfo has energy information."); + if (!test_scanner->has_energy_information()) { + test_scanner->set_reference_energy(511); + test_scanner->set_energy_resolution(0.34f); + } - sss->set_exam_info(*exam); + check(test_scanner->has_energy_information() == true, "Check the scanner has energy information."); - // Create the original projdata - shared_ptr original_projdata_info( dynamic_cast( - ProjDataInfo::ProjDataInfoCTI(test_scanner, - 1, 0, - test_scanner->get_num_detectors_per_ring()/2, - test_scanner->get_max_num_non_arccorrected_bins(), - false))); + shared_ptr exam(new ExamInfo); + exam->set_low_energy_thres(450); + exam->set_high_energy_thres(650); + exam->imaging_modality = ImagingModality::PT; - check(original_projdata_info->has_energy_information() == true, "Check the ProjDataInfo has energy information."); + check(exam->has_energy_information() == true, "Check the ExamInfo has energy information."); - shared_ptr > tmpl_density( new VoxelsOnCartesianGrid(exam, *original_projdata_info)); + sss->set_exam_info(*exam); - //// Create an object in the middle of the image (which will be in the middle of the scanner - CartesianCoordinate3D min_ind, max_ind; - tmpl_density->get_regular_range(min_ind, max_ind); - CartesianCoordinate3D centre((tmpl_density->get_physical_coordinates_for_indices(min_ind) + - tmpl_density->get_physical_coordinates_for_indices(max_ind))/2.F); + // Create the original projdata + shared_ptr original_projdata_info(dynamic_cast( + ProjDataInfo::ProjDataInfoCTI(test_scanner, 1, 0, test_scanner->get_num_detectors_per_ring() / 2, + test_scanner->get_max_num_non_arccorrected_bins(), false))); - EllipsoidalCylinder phantom(50.F, 50.F, 50.F, centre); - CartesianCoordinate3D num_samples(2,2,2); + check(original_projdata_info->has_energy_information() == true, "Check the ProjDataInfo has energy information."); - //// attenuation image - shared_ptr > water_density(tmpl_density->clone()); - phantom.construct_volume(*water_density, num_samples); - // Water attenuation coefficient. - *water_density *= 9.687E-02; - sss->set_density_image_sptr(water_density); + shared_ptr> tmpl_density(new VoxelsOnCartesianGrid(exam, *original_projdata_info)); - ////activity image (same object) - shared_ptr > act_density(tmpl_density->clone()); - phantom.construct_volume(*act_density, num_samples); - sss->set_activity_image_sptr(act_density); + //// Create an object in the middle of the image (which will be in the middle of the scanner + CartesianCoordinate3D min_ind, max_ind; + tmpl_density->get_regular_range(min_ind, max_ind); + CartesianCoordinate3D centre((tmpl_density->get_physical_coordinates_for_indices(min_ind) + + tmpl_density->get_physical_coordinates_for_indices(max_ind)) / + 2.F); + + EllipsoidalCylinder phantom(50.F, 50.F, 50.F, centre); + CartesianCoordinate3D num_samples(2, 2, 2); + + //// attenuation image + shared_ptr> water_density(tmpl_density->clone()); + phantom.construct_volume(*water_density, num_samples); + // Water attenuation coefficient. + *water_density *= 9.687E-02; + sss->set_density_image_sptr(water_density); - //// sss settings - sss->set_randomly_place_scatter_points(false); + ////activity image (same object) + shared_ptr> act_density(tmpl_density->clone()); + phantom.construct_volume(*act_density, num_samples); + sss->set_activity_image_sptr(act_density); - sss->set_template_proj_data_info(*original_projdata_info); - sss->downsample_scanner(original_projdata_info->get_scanner_sptr()->get_num_rings(), -1); + //// sss settings + sss->set_randomly_place_scatter_points(false); + + sss->set_template_proj_data_info(*original_projdata_info); + sss->downsample_scanner(original_projdata_info->get_scanner_sptr()->get_num_rings(), -1); #if 1 - set_tolerance(.02); - { - const int new_size_z = 14; // original_projdata_info->get_scanner_sptr()->get_num_rings() - const float zoom_z = static_cast(new_size_z-1)/(water_density->size()-1); - sss->downsample_density_image_for_scatter_points(.2F, zoom_z, -1, new_size_z); - test_symmetric(*sss, "rings_size14"); - } - { - const int new_size_z = 14; // original_projdata_info->get_scanner_sptr()->get_num_rings() - sss->downsample_density_image_for_scatter_points(.2F, -1.F, -1, new_size_z); - test_symmetric(*sss, "rings_size14_def_zoom"); - } - { - sss->downsample_density_image_for_scatter_points(.3F, .4F, -1, -1); - test_symmetric(*sss, "rings_zoomxy.3_zoomz.4"); - } - // reduce for smaller number of rings - set_tolerance(.03); - { - const int new_size_z = 5; - sss->downsample_density_image_for_scatter_points(.2F, -1.F, -1, new_size_z); - test_symmetric(*sss, "rings_size5"); - } - { - sss->downsample_density_image_for_scatter_points(.2F, .3F, -1, -1); - test_symmetric(*sss, "rings_zoomz.3"); - } + set_tolerance(.02); + { + const int new_size_z = 14; // original_projdata_info->get_scanner_sptr()->get_num_rings() + const float zoom_z = static_cast(new_size_z - 1) / (water_density->size() - 1); + sss->downsample_density_image_for_scatter_points(.2F, zoom_z, -1, new_size_z); + test_symmetric(*sss, "rings_size14"); + } + { + const int new_size_z = 14; // original_projdata_info->get_scanner_sptr()->get_num_rings() + sss->downsample_density_image_for_scatter_points(.2F, -1.F, -1, new_size_z); + test_symmetric(*sss, "rings_size14_def_zoom"); + } + { + sss->downsample_density_image_for_scatter_points(.3F, .4F, -1, -1); + test_symmetric(*sss, "rings_zoomxy.3_zoomz.4"); + } + // reduce for smaller number of rings + set_tolerance(.03); + { + const int new_size_z = 5; + sss->downsample_density_image_for_scatter_points(.2F, -1.F, -1, new_size_z); + test_symmetric(*sss, "rings_size5"); + } + { + sss->downsample_density_image_for_scatter_points(.2F, .3F, -1, -1); + test_symmetric(*sss, "rings_zoomz.3"); + } - // testing with zooming (but currently fails) - { - const CartesianCoordinate3D zooms(.5F, .3F, .3F); - shared_ptr > - zoomed_act_sptr(new VoxelsOnCartesianGrid - (zoom_image(*act_density, - zooms, - CartesianCoordinate3D(0.F,0.F,0.F) /* offset */, - round(BasicCoordinate<3,float>(act_density->get_lengths())*zooms)+1 /* sizes */, - ZoomOptions::preserve_projections) - ) - ); - //std::cerr << "new origin : " << zoomed_act_sptr->get_origin(); - sss->set_activity_image_sptr(zoomed_act_sptr); - std::cerr << "\nThis test should currently throw an error. You'll see some error messages therefore.\n"; - try - { - test_symmetric(*sss, "act_zoom_rings_zoomxy.3_zoomz.4"); - check(false, "Test on zooming of activity image should have thrown."); - } - catch(...) - { - // ok - } - // restore to original activity - sss->set_activity_image_sptr(act_density); + // testing with zooming (but currently fails) + { + const CartesianCoordinate3D zooms(.5F, .3F, .3F); + shared_ptr> zoomed_act_sptr(new VoxelsOnCartesianGrid( + zoom_image(*act_density, zooms, CartesianCoordinate3D(0.F, 0.F, 0.F) /* offset */, + round(BasicCoordinate<3, float>(act_density->get_lengths()) * zooms) + 1 /* sizes */, + ZoomOptions::preserve_projections))); + // std::cerr << "new origin : " << zoomed_act_sptr->get_origin(); + sss->set_activity_image_sptr(zoomed_act_sptr); + std::cerr << "\nThis test should currently throw an error. You'll see some error messages therefore.\n"; + try { + test_symmetric(*sss, "act_zoom_rings_zoomxy.3_zoomz.4"); + check(false, "Test on zooming of activity image should have thrown."); + } catch (...) { + // ok } + // restore to original activity + sss->set_activity_image_sptr(act_density); + } - // a few tests with more downsampled scanner - sss->set_template_proj_data_info(*original_projdata_info); - sss->downsample_scanner(original_projdata_info->get_scanner_sptr()->get_num_rings()/2, -1); - { - const int new_size_z = 14; - const float zoom_z = static_cast(new_size_z-1)/(water_density->size()-1); - sss->downsample_density_image_for_scatter_points(.2F, zoom_z, -1, new_size_z); - test_symmetric(*sss, "halfrings_size14"); - } - // reduce for smaller number of rings - set_tolerance(.03); - { - const int new_size_z = 5; - sss->downsample_density_image_for_scatter_points(.2F, -1.F, -1, new_size_z); - test_symmetric(*sss, "halfrings_size5"); - } - { - sss->downsample_density_image_for_scatter_points(.2F, .3F, -1, -1); - test_symmetric(*sss, "halfrings_zoomz.3"); - } + // a few tests with more downsampled scanner + sss->set_template_proj_data_info(*original_projdata_info); + sss->downsample_scanner(original_projdata_info->get_scanner_sptr()->get_num_rings() / 2, -1); + { + const int new_size_z = 14; + const float zoom_z = static_cast(new_size_z - 1) / (water_density->size() - 1); + sss->downsample_density_image_for_scatter_points(.2F, zoom_z, -1, new_size_z); + test_symmetric(*sss, "halfrings_size14"); + } + // reduce for smaller number of rings + set_tolerance(.03); + { + const int new_size_z = 5; + sss->downsample_density_image_for_scatter_points(.2F, -1.F, -1, new_size_z); + test_symmetric(*sss, "halfrings_size5"); + } + { + sss->downsample_density_image_for_scatter_points(.2F, .3F, -1, -1); + test_symmetric(*sss, "halfrings_zoomz.3"); + } #endif - // shared_ptr atten_sino(new ProjDataInMemory(exam, output_projdata_info)); - // atten_sino->fill(1.F); - // shared_ptr act_sino(new ProjDataInMemory(exam, output_projdata_info)); - // { - // info("ScatterSimulationTests: Calculate the Attenuation coefficients."); - // shared_ptr forw_projector_ptr; - // shared_ptr PM(new ProjMatrixByBinUsingRayTracing()); - // forw_projector_ptr.reset(new ForwardProjectorByBinUsingProjMatrixByBin(PM)); - - // shared_ptr normalisation_ptr - // (new BinNormalisationFromAttenuationImage(water_density, - // forw_projector_ptr)); - - // { - // normalisation_ptr->set_up(output_projdata_info->create_shared_clone()); - // const double start_frame = 0; - // const double end_frame = 0; - // shared_ptr symmetries_sptr(forw_projector_ptr->get_symmetries_used()->clone()); - // normalisation_ptr->undo(*atten_sino,start_frame,end_frame, symmetries_sptr); - // } - - //// atten_sino->write_to_file("_sino"); - //// std::string density_image_for_scatter_points_output_filename("./image"); - //// write_to_file(density_image_for_scatter_points_output_filename, - //// *act_density); - - // { - // forw_projector_ptr->set_up(output_projdata_info->create_shared_clone(), act_density); - // forw_projector_ptr->forward_project(*act_sino, *act_density); - // } - - // for (int i = output_projdata_info->get_min_view_num(); - // i < output_projdata_info->get_max_view_num(); ++i) - // { - // Viewgram view_att = atten_sino->get_viewgram(i, 0); - // Viewgram view_act = act_sino->get_viewgram(i,0); - // Viewgram view_sct = sss_output->get_viewgram(i,0); - - // view_act *= view_att; - // view_act += view_sct; - - // act_sino->set_viewgram(view_act); - // } - // } + // shared_ptr atten_sino(new ProjDataInMemory(exam, output_projdata_info)); + // atten_sino->fill(1.F); + // shared_ptr act_sino(new ProjDataInMemory(exam, output_projdata_info)); + // { + // info("ScatterSimulationTests: Calculate the Attenuation coefficients."); + // shared_ptr forw_projector_ptr; + // shared_ptr PM(new ProjMatrixByBinUsingRayTracing()); + // forw_projector_ptr.reset(new ForwardProjectorByBinUsingProjMatrixByBin(PM)); + + // shared_ptr normalisation_ptr + // (new BinNormalisationFromAttenuationImage(water_density, + // forw_projector_ptr)); + + // { + // normalisation_ptr->set_up(output_projdata_info->create_shared_clone()); + // const double start_frame = 0; + // const double end_frame = 0; + // shared_ptr + // symmetries_sptr(forw_projector_ptr->get_symmetries_used()->clone()); + // normalisation_ptr->undo(*atten_sino,start_frame,end_frame, symmetries_sptr); + // } + + //// atten_sino->write_to_file("_sino"); + //// std::string density_image_for_scatter_points_output_filename("./image"); + //// write_to_file(density_image_for_scatter_points_output_filename, + //// *act_density); + + // { + // forw_projector_ptr->set_up(output_projdata_info->create_shared_clone(), act_density); + // forw_projector_ptr->forward_project(*act_sino, *act_density); + // } + + // for (int i = output_projdata_info->get_min_view_num(); + // i < output_projdata_info->get_max_view_num(); ++i) + // { + // Viewgram view_att = atten_sino->get_viewgram(i, 0); + // Viewgram view_act = act_sino->get_viewgram(i,0); + // Viewgram view_sct = sss_output->get_viewgram(i,0); + + // view_act *= view_att; + // view_act += view_sct; + + // act_sino->set_viewgram(view_act); + // } + // } } - -//void -//ScatterSimulationTests::simulate_scatter_for_one_point(shared_ptr) +// void +// ScatterSimulationTests::simulate_scatter_for_one_point(shared_ptr) //{ //} void -ScatterSimulationTests:: -run_tests() -{ +ScatterSimulationTests::run_tests() { - // test the downsampling functions. - test_downsampling_ProjDataInfo(); - test_downsampling_DiscretisedDensity(); + // test the downsampling functions. + test_downsampling_ProjDataInfo(); + test_downsampling_DiscretisedDensity(); - test_scatter_simulation(); + test_scatter_simulation(); } - END_NAMESPACE_STIR +int +main() { + USING_NAMESPACE_STIR + // decrease verbosity a bit to avoid too much output + Verbosity::set(0); -int main() -{ - USING_NAMESPACE_STIR - // decrease verbosity a bit to avoid too much output - Verbosity::set(0); - - ScatterSimulationTests tests; - tests.write_output = true; // TODO get this from the command line args - tests.run_tests(); - return tests.main_return_value(); + ScatterSimulationTests tests; + tests.write_output = true; // TODO get this from the command line args + tests.run_tests(); + return tests.main_return_value(); } diff --git a/src/test/test_SeparableGaussianArrayFilter.cxx b/src/test/test_SeparableGaussianArrayFilter.cxx index 13823eb1cf..83a3f1af1d 100644 --- a/src/test/test_SeparableGaussianArrayFilter.cxx +++ b/src/test/test_SeparableGaussianArrayFilter.cxx @@ -1,8 +1,8 @@ /*! - \file + \file \ingroup test - + \brief tests for the stir::SeparableGaussianArrayFilter class \author Ludovica Brusaferri @@ -41,7 +41,6 @@ START_NAMESPACE_STIR - /*! \brief Tests SeparableGaussianArrayFilter functionality \ingroup test @@ -52,31 +51,27 @@ START_NAMESPACE_STIR #define num_dimensions 3 -class SeparableGaussianArrayFilterTests : public RunTests -{ +class SeparableGaussianArrayFilterTests : public RunTests { public: void run_tests(); + private: //! test one case (overwrites contents of \c test) - void test_one(Array&, - const BasicCoordinate< num_dimensions,float>& fwhms, - const BasicCoordinate< num_dimensions,int>& max_kernel_sizes); - + void test_one(Array&, const BasicCoordinate& fwhms, + const BasicCoordinate& max_kernel_sizes); }; void -SeparableGaussianArrayFilterTests:: -test_one(Array& test, - const BasicCoordinate< num_dimensions,float>& fwhms, - const BasicCoordinate< num_dimensions,int>& max_kernel_sizes) -{ +SeparableGaussianArrayFilterTests::test_one(Array& test, + const BasicCoordinate& fwhms, + const BasicCoordinate& max_kernel_sizes) { test.fill(0.F); - BasicCoordinate<3,int> min_ind, max_ind; + BasicCoordinate<3, int> min_ind, max_ind; test.get_regular_range(min_ind, max_ind); - BasicCoordinate<3,int> centre = (max_ind+min_ind)/2; + BasicCoordinate<3, int> centre = (max_ind + min_ind) / 2; test[centre] = 1.F; - SeparableGaussianArrayFilter<3,float> filter(fwhms,max_kernel_sizes,true); + SeparableGaussianArrayFilter<3, float> filter(fwhms, max_kernel_sizes, true); filter(test); double old_tol = get_tolerance(); set_tolerance(.01); @@ -85,50 +80,54 @@ test_one(Array& test, check(test.find_min() >= 0, "test if Gaussian kernel is non-negative"); set_tolerance(.1); - for (int d=1; d<=3; ++d) - { - std::cerr << "testing FWHM along dim " << d << '\n'; - const Array<1,float> line = extract_line(test, centre, d); - const float fwhm = find_level_width(line.begin(), line.end(), .5*test[centre]); - check_if_equal(fwhm, fwhms[d], "FWHM of kernel"); - } + for (int d = 1; d <= 3; ++d) { + std::cerr << "testing FWHM along dim " << d << '\n'; + const Array<1, float> line = extract_line(test, centre, d); + const float fwhm = find_level_width(line.begin(), line.end(), .5 * test[centre]); + check_if_equal(fwhm, fwhms[d], "FWHM of kernel"); + } set_tolerance(old_tol); } void -SeparableGaussianArrayFilterTests::run_tests() -{ +SeparableGaussianArrayFilterTests::run_tests() { std::cerr << "\nTesting 3D\n"; { set_tolerance(.001F); - const int size1=69; - const int size2=200; - const int size3=130; - Array<3,float> test(IndexRange3D(size1,size2,size3)); + const int size1 = 69; + const int size2 = 200; + const int size3 = 130; + Array<3, float> test(IndexRange3D(size1, size2, size3)); { - BasicCoordinate< num_dimensions,float> fwhms; - fwhms[1]=9.F; fwhms[2]=7.4F; fwhms[3]=5.4F; - BasicCoordinate< num_dimensions,int> max_kernel_sizes; + BasicCoordinate fwhms; + fwhms[1] = 9.F; + fwhms[2] = 7.4F; + fwhms[3] = 5.4F; + BasicCoordinate max_kernel_sizes; std::cerr << "Fixed kernel size\n"; { - max_kernel_sizes[1]=19; max_kernel_sizes[2]=19; max_kernel_sizes[3]=29; - test_one(test, fwhms, max_kernel_sizes); + max_kernel_sizes[1] = 19; + max_kernel_sizes[2] = 19; + max_kernel_sizes[3] = 29; + test_one(test, fwhms, max_kernel_sizes); } std::cerr << "Automatic kernel size\n"; { - max_kernel_sizes[1]=-1; max_kernel_sizes[2]=-1; max_kernel_sizes[3]=-1; - test_one(test, fwhms, max_kernel_sizes); + max_kernel_sizes[1] = -1; + max_kernel_sizes[2] = -1; + max_kernel_sizes[3] = -1; + test_one(test, fwhms, max_kernel_sizes); } } - } + } } END_NAMESPACE_STIR USING_NAMESPACE_STIR -int main() -{ +int +main() { SeparableGaussianArrayFilterTests tests; tests.run_tests(); return tests.main_return_value(); diff --git a/src/test/test_SeparableMetzArrayFilter.cxx b/src/test/test_SeparableMetzArrayFilter.cxx index 4deaf46fa7..2fc1d0c3b1 100644 --- a/src/test/test_SeparableMetzArrayFilter.cxx +++ b/src/test/test_SeparableMetzArrayFilter.cxx @@ -1,8 +1,8 @@ /*! - \file + \file \ingroup test - + \brief tests for the stir::SeparableMetzArrayFilter class \author Kris Thielemans @@ -39,7 +39,6 @@ START_NAMESPACE_STIR - /*! \brief Tests SeparableMetzArrayFilter functionality \ingroup test @@ -47,57 +46,61 @@ START_NAMESPACE_STIR Currently only very basic tests on normalisation */ -class SeparableMetzArrayFilterTests : public RunTests -{ +class SeparableMetzArrayFilterTests : public RunTests { public: void run_tests(); -private: - - +private: }; void -SeparableMetzArrayFilterTests::run_tests() -{ +SeparableMetzArrayFilterTests::run_tests() { std::cerr << "\nTesting 3D\n"; { set_tolerance(.001F); - const int size1=69; - const int size2=200; - const int size3=130; - Array<3,float> test(IndexRange3D(size1,size2,size3)); + const int size1 = 69; + const int size2 = 200; + const int size3 = 130; + Array<3, float> test(IndexRange3D(size1, size2, size3)); #if 1 test.fill(0.F); - test[size1/2][size2/2][size3/2] = 1.F; -#else + test[size1 / 2][size2 / 2][size3 / 2] = 1.F; +#else // initialise to some arbitrary values { - Array<3,float>::full_iterator iter = test.begin_all(); + Array<3, float>::full_iterator iter = test.begin_all(); /*for (int i=-100; iter != test.end_all(); ++i, ++iter) *iter = 1;//i*i*2.F-i-100.F;*/ - test[0][0[0]]=1; + test[0][0 [0]] = 1; } #endif { - VectorWithOffset fwhms(1,3); - fwhms[1]=9.F; fwhms[2]=7.4F; fwhms[3]=5.4F; - VectorWithOffset metz_powers(1,3); - BasicCoordinate<3,float> sampling_distances = make_coordinate(3.1F, 2.F, 2.5F); - VectorWithOffset max_kernel_sizes(1,3); - max_kernel_sizes[1]=19; max_kernel_sizes[2]=19; max_kernel_sizes[3]=29; - + VectorWithOffset fwhms(1, 3); + fwhms[1] = 9.F; + fwhms[2] = 7.4F; + fwhms[3] = 5.4F; + VectorWithOffset metz_powers(1, 3); + BasicCoordinate<3, float> sampling_distances = make_coordinate(3.1F, 2.F, 2.5F); + VectorWithOffset max_kernel_sizes(1, 3); + max_kernel_sizes[1] = 19; + max_kernel_sizes[2] = 19; + max_kernel_sizes[3] = 29; + { - metz_powers[1]=0.F; metz_powers[2]=0.F; metz_powers[3]=0.F; - SeparableMetzArrayFilter<3,float> filter(fwhms, metz_powers, sampling_distances, max_kernel_sizes); + metz_powers[1] = 0.F; + metz_powers[2] = 0.F; + metz_powers[3] = 0.F; + SeparableMetzArrayFilter<3, float> filter(fwhms, metz_powers, sampling_distances, max_kernel_sizes); filter(test); check_if_equal(1.F, test.sum(), "test if Gaussian kernel is normalised to 1"); check(test.find_min() >= -0.005F, "test if Gaussian kernel is almost non-negative"); } { - metz_powers[1]=1.F; metz_powers[2]=2.F; metz_powers[3]=1.F; - SeparableMetzArrayFilter<3,float> filter(fwhms, metz_powers, sampling_distances, max_kernel_sizes); + metz_powers[1] = 1.F; + metz_powers[2] = 2.F; + metz_powers[3] = 1.F; + SeparableMetzArrayFilter<3, float> filter(fwhms, metz_powers, sampling_distances, max_kernel_sizes); filter(test); double old_tol = get_tolerance(); @@ -106,15 +109,15 @@ SeparableMetzArrayFilterTests::run_tests() set_tolerance(old_tol); } } - } + } } END_NAMESPACE_STIR USING_NAMESPACE_STIR -int main() -{ +int +main() { SeparableMetzArrayFilterTests tests; tests.run_tests(); return tests.main_return_value(); diff --git a/src/test/test_VectorWithOffset.cxx b/src/test/test_VectorWithOffset.cxx index 39fd55ea49..22dab9e5ea 100644 --- a/src/test/test_VectorWithOffset.cxx +++ b/src/test/test_VectorWithOffset.cxx @@ -29,10 +29,10 @@ */ #ifndef NDEBUG // set to high level of debugging -#ifdef _DEBUG -#undef _DEBUG -#endif -#define _DEBUG 2 +# ifdef _DEBUG +# undef _DEBUG +# endif +# define _DEBUG 2 #endif #include "stir/VectorWithOffset.h" @@ -53,74 +53,68 @@ using std::size_t; START_NAMESPACE_STIR - /*! \brief Test class for VectorWithOffset \ingroup test */ -class VectorWithOffsetTests : public RunTests -{ +class VectorWithOffsetTests : public RunTests { public: void run_tests(); }; void -VectorWithOffsetTests::run_tests() -{ +VectorWithOffsetTests::run_tests() { cerr << "Tests for VectorWithOffset\n" << "Everythings is fine if the program runs without any output." << endl; - + VectorWithOffset v(-3, 40); check_if_equal(v.get_min_index(), -3, "test basic constructor and get_min_index"); check_if_equal(v.get_max_index(), 40, "test basic constructor and get_max_index"); - check(v.size()== 40+3+1, "test basic constructor and size"); - check(v.capacity()== 40+3+1, "test basic constructor and capacity"); + check(v.size() == 40 + 3 + 1, "test basic constructor and size"); + check(v.capacity() == 40 + 3 + 1, "test basic constructor and capacity"); - for (int i=v.get_min_index(); i<=v.get_max_index(); i++) - v[i] = 2*i; + for (int i = v.get_min_index(); i <= v.get_max_index(); i++) + v[i] = 2 * i; check_if_equal(v[4], 8, "test operator[]"); - int *ptr = v.get_data_ptr(); - ptr[4+3] = 5; + int* ptr = v.get_data_ptr(); + ptr[4 + 3] = 5; v.release_data_ptr(); check_if_equal(v[4], 5, "test get_data_tr/release_data_ptr"); // iterator tests { - { + { int value = -3; - for (VectorWithOffset::iterator iter = v.begin(); - iter != v.end(); - iter++, value++) - *iter = value; + for (VectorWithOffset::iterator iter = v.begin(); iter != v.end(); iter++, value++) + *iter = value; check_if_equal(v[4], 4, "test iterators operator++ and *"); } { VectorWithOffset::const_iterator iter = v.begin(); - check_if_equal(*(iter+4-v.get_min_index()), 4, "test iterators operator+ and *"); - iter += 4-v.get_min_index(); + check_if_equal(*(iter + 4 - v.get_min_index()), 4, "test iterators operator+ and *"); + iter += 4 - v.get_min_index(); check_if_equal(*iter, 4, "test iterators operator+= and *"); ++iter; - check_if_equal(*(iter-1), 4, "test iterators operator+= and *"); + check_if_equal(*(iter - 1), 4, "test iterators operator+= and *"); --iter; check_if_equal(*iter, 4, "test iterators operator+= and *"); } { const VectorWithOffset& const_v = v; VectorWithOffset::const_iterator iter = const_v.begin(); - check_if_equal(*(iter+4-v.get_min_index()), 4, "test iterators operator+ and *"); - iter += 4-v.get_min_index(); + check_if_equal(*(iter + 4 - v.get_min_index()), 4, "test iterators operator+ and *"); + iter += 4 - v.get_min_index(); check_if_equal(*iter, 4, "test iterators operator+= and *"); ++iter; - check_if_equal(*(iter-1), 4, "test iterators operator+= and *"); + check_if_equal(*(iter - 1), 4, "test iterators operator+= and *"); --iter; check_if_equal(*iter, 4, "test iterators operator+= and *"); } - { - VectorWithOffset::iterator p=find(v.begin(), v.end(), 6); + VectorWithOffset::iterator p = find(v.begin(), v.end(), 6); check_if_zero(p - v.begin() - 9, "test iterators: find"); check_if_equal(*p, 6, "test iterators: find"); } @@ -132,7 +126,7 @@ VectorWithOffsetTests::run_tests() { VectorWithOffset::reverse_iterator pr = v.rbegin(); check_if_equal(*pr++, -3, "test reverse iterator operator++ and *"); - check_if_equal(*(pr+2), 0, "test reverse iterator operator++ and *"); + check_if_equal(*(pr + 2), 0, "test reverse iterator operator++ and *"); } sort(v.rbegin(), v.rend(), greater()); @@ -141,515 +135,420 @@ VectorWithOffsetTests::run_tests() } // end test iterators - { VectorWithOffset test = v; test.set_min_index(-1); - check( test.size()==v.size(), "test set_min_index (size)"); - check( test.capacity()==v.capacity(), "test set_min_index (capacity)"); - check_if_equal( test[-1], v[v.get_min_index()], "test set_min_index (operator[])"); + check(test.size() == v.size(), "test set_min_index (size)"); + check(test.capacity() == v.capacity(), "test set_min_index (capacity)"); + check_if_equal(test[-1], v[v.get_min_index()], "test set_min_index (operator[])"); } /**********************************************************************/ // tests on reserve() /**********************************************************************/ - { - // tests on reserve() with 0 length - { - // check reserve 0,max - { - VectorWithOffset test; - check_if_equal(test.capacity(), size_t(0), - "check capacity after default constructor"); - test.reserve(2); - check_if_equal(test.size(), size_t(0), - "check reserve of empty vector (0,max) (size)"); - check_if_equal(test.capacity(), size_t(2), - "check reserve of empty vector (0,max) (capacity)"); - check_if_equal(test.get_capacity_min_index(), 0, - "check reserve of empty vector (0,max) (capacity_min_index)"); - check_if_equal(test.get_capacity_max_index(), 1, - "check reserve of empty vector (0,max) (capacity_max_index)"); - } - // check reserve -1,2 - { - VectorWithOffset test; - test.reserve(-1,2); - check_if_equal(test.size(), size_t(0), - "check reserve of empty vector (-1,2) (size)"); - check_if_equal(test.capacity(), size_t(4), - "check reserve of empty vector (-1,2) (capacity)"); - // note: for length 0 vectors, get_capacity_min_index() is always 0 - check_if_equal(test.get_capacity_min_index(), 0, - "check reserve of empty vector (-1,2) (capacity_min_index)"); - check_if_equal(test.get_capacity_max_index(), 3, - "check reserve of empty vector (-1,2) (capacity_max_index)"); - } - // check reserve -1,2 and then 1,6 - { - VectorWithOffset test; - test.reserve(-1,2); - test.reserve(1,6); - check_if_equal(test.size(), size_t(0), - "check reserve of empty vector (-1,2 and then 1,6) (size)"); - check_if_equal(test.capacity(), size_t(6), - "check reserve of empty vector (-1,2 and then 1,6) (capacity)"); - // note: for length 0 vectors, get_capacity_min_index() is always 0 - check_if_equal(test.get_capacity_min_index(), 0, - "check reserve of empty vector (-1,2 and then 1,6) (capacity_min_index)"); - check_if_equal(test.get_capacity_max_index(), 5, - "check reserve of empty vector (-1,2 and then 1,6) (capacity_max_index)"); - } - } // end of tests length 0 - - // tests of reserve() with non-zero length - { - const VectorWithOffset ref = v; - VectorWithOffset test = ref; - // check reserve within range (should have no effect) - test.reserve(0,1); - check_if_equal(test.size(), ref.size(), - "check reserve within range (size)"); - check_if_equal(test.get_min_index(), ref.get_min_index(), - "check reserve within range (get_min_index)"); - check_if_equal(test, ref, - "check reserve within range (values)"); - check_if_equal(test.capacity(), ref.size(), - "check reserve within range (capacity)"); - check_if_equal(test.get_capacity_min_index(), ref.get_min_index(), - "check reserve within range (capacity_min_index)"); - check_if_equal(test.get_capacity_max_index(), ref.get_max_index(), - "check reserve within range (capacity_max_index)"); - // check reserve within range on low index (should reserve space at higher indices only) - test.reserve(0,test.get_max_index()+5); - check_if_equal(test.size(), ref.size(), - "check reserve within range on low index (size)"); - check_if_equal(test.get_min_index(), ref.get_min_index(), - "check reserve within range on low index (get_min_index)"); - check_if_equal(test, ref, - "check reserve within range on low index (values)"); - check_if_equal(test.capacity(), ref.size()+5, - "check reserve within range on low index (capacity)"); - check_if_equal(test.get_capacity_min_index(), ref.get_min_index(), - "check reserve within range on low index (capacity_min_index)"); - check_if_equal(test.get_capacity_max_index(), ref.get_max_index()+5, - "check reserve within range on low index (capacity_max_index)"); - // check reserve within range on high index (should reserve space at low indices only) - test.recycle(); - check_if_equal(test.capacity(), size_t(0), "test recycle"); - test = ref; - test.reserve(test.get_min_index()-5,0); - check_if_equal(test.size(), ref.size(), - "check reserve within range on high index (size)"); - check_if_equal(test.get_min_index(), ref.get_min_index(), - "check reserve within range on high index (get_min_index)"); - check_if_equal(test, ref, - "check reserve within range on high index (values)"); - check_if_equal(test.capacity(), ref.size()+5, - "check reserve within range on high index (capacity)"); - check_if_equal(test.get_capacity_min_index(), ref.get_min_index()-5, - "check reserve within range on high index (capacity_min_index)"); - check_if_equal(test.get_capacity_max_index(), ref.get_max_index(), - "check reserve within range on high index (capacity_max_index)"); - // check reserve for both ranges - test.recycle(); - check_if_equal(test.capacity(), size_t(0), "test recycle"); - test = ref; - test.reserve(test.get_min_index()-5,test.get_max_index()+4); - check_if_equal(test.size(), ref.size(), - "check reserve (size)"); - check_if_equal(test.get_min_index(), ref.get_min_index(), - "check reserve (get_min_index)"); - check_if_equal(test, ref, - "check reserve (values)"); - check_if_equal(test.capacity(), ref.size()+9, - "check reserve (capacity)"); - check_if_equal(test.get_capacity_min_index(), ref.get_min_index()-5, - "check reserve (capacity_min_index)"); - check_if_equal(test.get_capacity_max_index(), ref.get_max_index()+4, - "check reserve (capacity_max_index)"); - } - } - /**********************************************************************/ - // tests on resize() - /**********************************************************************/ - { - // tests on resize() with 0 length - { - // check resize 0,max - { - VectorWithOffset test; - check_if_equal(test.capacity(), size_t(0), - "check capacity after default constructor"); - test.resize(2); - check_if_equal(test.size(), size_t(2), - "check resize of empty vector (0,max) (size)"); - check_if_equal(test.capacity(), size_t(2), - "check resize of empty vector (0,max) (capacity)"); - check_if_equal(test.get_min_index(), 0, - "check resize of empty vector (0,max) (min_index)"); - check_if_equal(test.get_max_index(), 1, - "check resize of empty vector (0,max) (max_index)"); - } - // check resize -1,2 - { - VectorWithOffset test; - test.resize(-1,2); - check_if_equal(test.size(), size_t(4), - "check resize of empty vector (-1,2) (size)"); - check_if_equal(test.capacity(), size_t(4), - "check resize of empty vector (-1,2) (capacity)"); - check_if_equal(test.get_min_index(), -1, - "check resize of empty vector (-1,2) (min_index)"); - check_if_equal(test.get_max_index(), 2, - "check resize of empty vector (-1,2) (max_index)"); - } - // check resize -1,2 and then 1,6 - { - VectorWithOffset test; - test.resize(-1,2); - test.resize(1,6); - check_if_equal(test.size(), size_t(6), - "check resize of empty vector (-1,2 and then 1,6) (size)"); - check_if_equal(test.capacity(), size_t(8), - "check resize of empty vector (-1,2 and then 1,6) (capacity)"); - // note: for length 0 vectors, get_min_index() is always 0 - check_if_equal(test.get_min_index(), 1, - "check resize of empty vector (-1,2 and then 1,6) (min_index)"); - check_if_equal(test.get_max_index(), 6, - "check resize of empty vector (-1,2 and then 1,6) (max_index)"); - } - } // end of tests length 0 - - // tests of resize() with non-zero length - { - const VectorWithOffset ref = v; - VectorWithOffset test = ref; - // check resize with identical range (should have no effect) - test.resize(ref.get_min_index(), ref.get_max_index()); - check_if_equal(test.size(), ref.size(), - "check resize with identical range (size)"); - check_if_equal(test.get_min_index(), ref.get_min_index(), - "check resize with identical range (get_min_index)"); - check_if_equal(test, ref, - "check resize with identical range (values)"); - check_if_equal(test.capacity(), ref.size(), - "check resize with identical range (capacity)"); - check_if_equal(test.get_capacity_min_index(), ref.get_min_index(), - "check resize with identical range (capacity_min_index)"); - check_if_equal(test.get_capacity_max_index(), ref.get_max_index(), - "check resize with identical range (capacity_max_index)"); - // check resize with grow on high index (should resize space at higher indices only) - test.resize(ref.get_min_index(),test.get_max_index()+5); - check_if_equal(test.size(), ref.size()+5, - "check resize with grow on high index (size)"); - check_if_equal(test.get_min_index(), ref.get_min_index(), - "check resize with grow on high index (get_min_index)"); - for (int i=ref.get_min_index(); i<=ref.get_max_index(); ++i) - check_if_equal(test[i], ref[i], - "check resize with grow on high index (values)"); - check_if_equal(test.capacity(), ref.size()+5, - "check resize with grow on high index (capacity)"); - check_if_equal(test.get_capacity_min_index(), ref.get_min_index(), - "check resize with grow on high index (capacity_min_index)"); - check_if_equal(test.get_capacity_max_index(), ref.get_max_index()+5, - "check resize with grow on high index (capacity_max_index)"); - // check resize with grow on low index (should resize space at low indices only) - test.recycle(); - check_if_equal(test.capacity(), size_t(0), "test recycle"); - test = ref; - test.resize(test.get_min_index()-5,ref.get_max_index()); - check_if_equal(test.size(), ref.size()+5, - "check resize with grow on low index (size)"); - check_if_equal(test.get_min_index(), ref.get_min_index()-5, - "check resize with grow on low index (get_min_index)"); - for (int i=ref.get_min_index(); i<=ref.get_max_index(); ++i) - check_if_equal(test[i], ref[i], - "check resize with grow on low index (values)"); - check_if_equal(test.capacity(), ref.size()+5, - "check resize with grow on low index (capacity)"); - check_if_equal(test.get_capacity_min_index(), ref.get_min_index()-5, - "check resize with grow on low index (capacity_min_index)"); - check_if_equal(test.get_capacity_max_index(), ref.get_max_index(), - "check resize with grow on low index (capacity_max_index)"); - // check grow for both ranges - test.recycle(); - check_if_equal(test.capacity(), size_t(0), "test recycle"); - test = ref; - test.resize(test.get_min_index()-5,test.get_max_index()+4); - check_if_equal(test.size(), ref.size()+9, - "check resize with grow at both ends (size)"); - check_if_equal(test.get_min_index(), ref.get_min_index()-5, - "check resize with grow at both ends (get_min_index)"); - for (int i=ref.get_min_index(); i<=ref.get_max_index(); ++i) - check_if_equal(test[i], ref[i], - "check resize with grow at both ends (values)"); - check_if_equal(test.capacity(), ref.size()+9, - "check resize with grow at both ends (capacity)"); - check_if_equal(test.get_capacity_min_index(), ref.get_min_index()-5, - "check resize with grow at both ends (capacity_min_index)"); - check_if_equal(test.get_capacity_max_index(), ref.get_max_index()+4, - "check resize with grow at both ends (capacity_max_index)"); - // check resize with shrink for both ranges - test.recycle(); - check_if_equal(test.capacity(), size_t(0), "test recycle"); - test = ref; - test.resize(test.get_min_index()+5,test.get_max_index()-4); - check_if_equal(test.size(), ref.size()-9, - "check resize with shrink at both ends(size)"); - check_if_equal(test.get_min_index(), ref.get_min_index()+5, - "check resize with shrink at both ends(get_min_index)"); - for (int i=test.get_min_index(); i<=test.get_max_index(); ++i) - check_if_equal(test[i], ref[i], - "check resize with shrink at both ends (values)"); - check_if_equal(test.capacity(), ref.size(), - "check resize with shrink at both ends(capacity)"); - check_if_equal(test.get_capacity_min_index(), ref.get_min_index(), - "check resize with shrink at both ends(capacity_min_index)"); - check_if_equal(test.get_capacity_max_index(), ref.get_max_index(), - "check resize with shrink at both ends(capacity_max_index)"); - // check resize with shrink at left and grow at right - test.recycle(); - check_if_equal(test.capacity(), size_t(0), "test recycle"); - test = ref; - test.resize(test.get_min_index()+5,test.get_max_index()+4); - check_if_equal(test.size(), ref.size()-1, - "check resize with shrink at left and grow at right (size)"); - check_if_equal(test.get_min_index(), ref.get_min_index()+5, - "check resize with shrink at left and grow at right (get_min_index)"); - for (int i=test.get_min_index(); i<=ref.get_max_index(); ++i) - check_if_equal(test[i], ref[i], - "check resize with shrink at left and grow at right (values)"); - check_if_equal(test.capacity(), ref.size()+4, - "check resize with shrink at left and grow at right (capacity)"); - check_if_equal(test.get_capacity_min_index(), ref.get_min_index(), - "check resize with shrink at left and grow at right (capacity_min_index)"); - check_if_equal(test.get_capacity_max_index(), ref.get_max_index()+4, - "check resize with shrink at left and grow at right (capacity_max_index)"); - // check resize with resize non-overlapping to right - test.recycle(); - check_if_equal(test.capacity(), size_t(0), "test recycle"); - test = ref; - // note: test new size is smaller than original size, so no reallocation should occur - test.resize(test.get_max_index()+20, static_cast(test.get_max_index()+20+ref.size()-3)); - check_if_equal(test.size(), size_t(ref.size()-2), - "check resize with resize non-overlapping to right (size)"); - check_if_equal(test.get_min_index(), ref.get_max_index()+20, - "check resize with resize non-overlapping to right (get_min_index)"); - check_if_equal(test.capacity(), ref.size(), - "check resize with resize non-overlapping to right (capacity)"); - check_if_equal(test.get_capacity_min_index(), test.get_min_index(), - "check resize with resize non-overlapping to right (capacity_min_index)"); - check_if_equal(test.get_capacity_max_index(), test.get_min_index()+static_cast(test.capacity())-1, - "check resize with resize non-overlapping to right (capacity_max_index)"); - // check resize with resize non-overlapping to left - test.recycle(); - check_if_equal(test.capacity(), size_t(0), "test recycle"); - test = ref; - // not: test new size is larger than original size, so reallocation should occur - test.resize(test.get_min_index()-300, static_cast(test.get_min_index()-300+ref.size()+4)); - check_if_equal(test.size(), size_t(ref.size()+5), - "check resize with resize non-overlapping to left (size)"); - check_if_equal(test.get_min_index(), ref.get_min_index()-300, - "check resize with resize non-overlapping to left (get_min_index)"); - check_if_equal(test.capacity(), test.size(), - "check resize with resize non-overlapping to left (capacity)"); - check_if_equal(test.get_capacity_min_index(), test.get_min_index(), - "check resize with resize non-overlapping to left (capacity_min_index)"); - check_if_equal(test.get_capacity_max_index(), test.get_max_index(), - "check resize with resize non-overlapping to left (capacity_max_index)"); - } - } + { // tests on reserve() with 0 length + {// check reserve 0,max + {VectorWithOffset test; + check_if_equal(test.capacity(), size_t(0), "check capacity after default constructor"); + test.reserve(2); + check_if_equal(test.size(), size_t(0), "check reserve of empty vector (0,max) (size)"); + check_if_equal(test.capacity(), size_t(2), "check reserve of empty vector (0,max) (capacity)"); + check_if_equal(test.get_capacity_min_index(), 0, "check reserve of empty vector (0,max) (capacity_min_index)"); + check_if_equal(test.get_capacity_max_index(), 1, "check reserve of empty vector (0,max) (capacity_max_index)"); +} +// check reserve -1,2 +{ + VectorWithOffset test; + test.reserve(-1, 2); + check_if_equal(test.size(), size_t(0), "check reserve of empty vector (-1,2) (size)"); + check_if_equal(test.capacity(), size_t(4), "check reserve of empty vector (-1,2) (capacity)"); + // note: for length 0 vectors, get_capacity_min_index() is always 0 + check_if_equal(test.get_capacity_min_index(), 0, "check reserve of empty vector (-1,2) (capacity_min_index)"); + check_if_equal(test.get_capacity_max_index(), 3, "check reserve of empty vector (-1,2) (capacity_max_index)"); +} +// check reserve -1,2 and then 1,6 +{ + VectorWithOffset test; + test.reserve(-1, 2); + test.reserve(1, 6); + check_if_equal(test.size(), size_t(0), "check reserve of empty vector (-1,2 and then 1,6) (size)"); + check_if_equal(test.capacity(), size_t(6), "check reserve of empty vector (-1,2 and then 1,6) (capacity)"); + // note: for length 0 vectors, get_capacity_min_index() is always 0 + check_if_equal(test.get_capacity_min_index(), 0, "check reserve of empty vector (-1,2 and then 1,6) (capacity_min_index)"); + check_if_equal(test.get_capacity_max_index(), 5, "check reserve of empty vector (-1,2 and then 1,6) (capacity_max_index)"); +} +} // end of tests length 0 - /**********************************************************************/ - // tests on operator += etc - /**********************************************************************/ - { - const VectorWithOffset ref = v; - VectorWithOffset test = ref; - - test = ref; test += ref; - for (int i=ref.get_min_index(); i<= ref.get_max_index(); ++i) - check_if_equal( test[i] , ref[i]*2, "test operator+=(VectorWithOffset)"); - test = ref; test -= ref; - for (int i=ref.get_min_index(); i<= ref.get_max_index(); ++i) - check_if_equal( test[i] , 0, "test operator-=(VectorWithOffset)"); - test = ref; test *= ref; - for (int i=ref.get_min_index(); i<= ref.get_max_index(); ++i) - check_if_equal( test[i] , ref[i]*ref[i], "test operator*=(VectorWithOffset)"); - - const int minimum = *std::min_element(ref.begin(), ref.end()); - const int ensure_non_zero = - minimum<= 0 ? -minimum+1 : 0; - VectorWithOffset denominator = ref; - test = ref; - for (int i=ref.get_min_index(); i<= ref.get_max_index(); ++i) - { - denominator[i] += ensure_non_zero; - test[i]+=10000; - } - test /= denominator; - for (int i=ref.get_min_index(); i<= ref.get_max_index(); ++i) - check_if_equal( test[i] , (ref[i]+10000)/(ref[i]+ensure_non_zero), "test operator/=(VectorWithOffset)"); +// tests of reserve() with non-zero length +{ + const VectorWithOffset ref = v; + VectorWithOffset test = ref; + // check reserve within range (should have no effect) + test.reserve(0, 1); + check_if_equal(test.size(), ref.size(), "check reserve within range (size)"); + check_if_equal(test.get_min_index(), ref.get_min_index(), "check reserve within range (get_min_index)"); + check_if_equal(test, ref, "check reserve within range (values)"); + check_if_equal(test.capacity(), ref.size(), "check reserve within range (capacity)"); + check_if_equal(test.get_capacity_min_index(), ref.get_min_index(), "check reserve within range (capacity_min_index)"); + check_if_equal(test.get_capacity_max_index(), ref.get_max_index(), "check reserve within range (capacity_max_index)"); + // check reserve within range on low index (should reserve space at higher indices only) + test.reserve(0, test.get_max_index() + 5); + check_if_equal(test.size(), ref.size(), "check reserve within range on low index (size)"); + check_if_equal(test.get_min_index(), ref.get_min_index(), "check reserve within range on low index (get_min_index)"); + check_if_equal(test, ref, "check reserve within range on low index (values)"); + check_if_equal(test.capacity(), ref.size() + 5, "check reserve within range on low index (capacity)"); + check_if_equal(test.get_capacity_min_index(), ref.get_min_index(), + "check reserve within range on low index (capacity_min_index)"); + check_if_equal(test.get_capacity_max_index(), ref.get_max_index() + 5, + "check reserve within range on low index (capacity_max_index)"); + // check reserve within range on high index (should reserve space at low indices only) + test.recycle(); + check_if_equal(test.capacity(), size_t(0), "test recycle"); + test = ref; + test.reserve(test.get_min_index() - 5, 0); + check_if_equal(test.size(), ref.size(), "check reserve within range on high index (size)"); + check_if_equal(test.get_min_index(), ref.get_min_index(), "check reserve within range on high index (get_min_index)"); + check_if_equal(test, ref, "check reserve within range on high index (values)"); + check_if_equal(test.capacity(), ref.size() + 5, "check reserve within range on high index (capacity)"); + check_if_equal(test.get_capacity_min_index(), ref.get_min_index() - 5, + "check reserve within range on high index (capacity_min_index)"); + check_if_equal(test.get_capacity_max_index(), ref.get_max_index(), + "check reserve within range on high index (capacity_max_index)"); + // check reserve for both ranges + test.recycle(); + check_if_equal(test.capacity(), size_t(0), "test recycle"); + test = ref; + test.reserve(test.get_min_index() - 5, test.get_max_index() + 4); + check_if_equal(test.size(), ref.size(), "check reserve (size)"); + check_if_equal(test.get_min_index(), ref.get_min_index(), "check reserve (get_min_index)"); + check_if_equal(test, ref, "check reserve (values)"); + check_if_equal(test.capacity(), ref.size() + 9, "check reserve (capacity)"); + check_if_equal(test.get_capacity_min_index(), ref.get_min_index() - 5, "check reserve (capacity_min_index)"); + check_if_equal(test.get_capacity_max_index(), ref.get_max_index() + 4, "check reserve (capacity_max_index)"); +} +} +/**********************************************************************/ +// tests on resize() +/**********************************************************************/ +{ // tests on resize() with 0 length + {// check resize 0,max + {VectorWithOffset test; +check_if_equal(test.capacity(), size_t(0), "check capacity after default constructor"); +test.resize(2); +check_if_equal(test.size(), size_t(2), "check resize of empty vector (0,max) (size)"); +check_if_equal(test.capacity(), size_t(2), "check resize of empty vector (0,max) (capacity)"); +check_if_equal(test.get_min_index(), 0, "check resize of empty vector (0,max) (min_index)"); +check_if_equal(test.get_max_index(), 1, "check resize of empty vector (0,max) (max_index)"); +} +// check resize -1,2 +{ + VectorWithOffset test; + test.resize(-1, 2); + check_if_equal(test.size(), size_t(4), "check resize of empty vector (-1,2) (size)"); + check_if_equal(test.capacity(), size_t(4), "check resize of empty vector (-1,2) (capacity)"); + check_if_equal(test.get_min_index(), -1, "check resize of empty vector (-1,2) (min_index)"); + check_if_equal(test.get_max_index(), 2, "check resize of empty vector (-1,2) (max_index)"); +} +// check resize -1,2 and then 1,6 +{ + VectorWithOffset test; + test.resize(-1, 2); + test.resize(1, 6); + check_if_equal(test.size(), size_t(6), "check resize of empty vector (-1,2 and then 1,6) (size)"); + check_if_equal(test.capacity(), size_t(8), "check resize of empty vector (-1,2 and then 1,6) (capacity)"); + // note: for length 0 vectors, get_min_index() is always 0 + check_if_equal(test.get_min_index(), 1, "check resize of empty vector (-1,2 and then 1,6) (min_index)"); + check_if_equal(test.get_max_index(), 6, "check resize of empty vector (-1,2 and then 1,6) (max_index)"); +} +} // end of tests length 0 - } +// tests of resize() with non-zero length +{ + const VectorWithOffset ref = v; + VectorWithOffset test = ref; + // check resize with identical range (should have no effect) + test.resize(ref.get_min_index(), ref.get_max_index()); + check_if_equal(test.size(), ref.size(), "check resize with identical range (size)"); + check_if_equal(test.get_min_index(), ref.get_min_index(), "check resize with identical range (get_min_index)"); + check_if_equal(test, ref, "check resize with identical range (values)"); + check_if_equal(test.capacity(), ref.size(), "check resize with identical range (capacity)"); + check_if_equal(test.get_capacity_min_index(), ref.get_min_index(), "check resize with identical range (capacity_min_index)"); + check_if_equal(test.get_capacity_max_index(), ref.get_max_index(), "check resize with identical range (capacity_max_index)"); + // check resize with grow on high index (should resize space at higher indices only) + test.resize(ref.get_min_index(), test.get_max_index() + 5); + check_if_equal(test.size(), ref.size() + 5, "check resize with grow on high index (size)"); + check_if_equal(test.get_min_index(), ref.get_min_index(), "check resize with grow on high index (get_min_index)"); + for (int i = ref.get_min_index(); i <= ref.get_max_index(); ++i) + check_if_equal(test[i], ref[i], "check resize with grow on high index (values)"); + check_if_equal(test.capacity(), ref.size() + 5, "check resize with grow on high index (capacity)"); + check_if_equal(test.get_capacity_min_index(), ref.get_min_index(), "check resize with grow on high index (capacity_min_index)"); + check_if_equal(test.get_capacity_max_index(), ref.get_max_index() + 5, + "check resize with grow on high index (capacity_max_index)"); + // check resize with grow on low index (should resize space at low indices only) + test.recycle(); + check_if_equal(test.capacity(), size_t(0), "test recycle"); + test = ref; + test.resize(test.get_min_index() - 5, ref.get_max_index()); + check_if_equal(test.size(), ref.size() + 5, "check resize with grow on low index (size)"); + check_if_equal(test.get_min_index(), ref.get_min_index() - 5, "check resize with grow on low index (get_min_index)"); + for (int i = ref.get_min_index(); i <= ref.get_max_index(); ++i) + check_if_equal(test[i], ref[i], "check resize with grow on low index (values)"); + check_if_equal(test.capacity(), ref.size() + 5, "check resize with grow on low index (capacity)"); + check_if_equal(test.get_capacity_min_index(), ref.get_min_index() - 5, + "check resize with grow on low index (capacity_min_index)"); + check_if_equal(test.get_capacity_max_index(), ref.get_max_index(), "check resize with grow on low index (capacity_max_index)"); + // check grow for both ranges + test.recycle(); + check_if_equal(test.capacity(), size_t(0), "test recycle"); + test = ref; + test.resize(test.get_min_index() - 5, test.get_max_index() + 4); + check_if_equal(test.size(), ref.size() + 9, "check resize with grow at both ends (size)"); + check_if_equal(test.get_min_index(), ref.get_min_index() - 5, "check resize with grow at both ends (get_min_index)"); + for (int i = ref.get_min_index(); i <= ref.get_max_index(); ++i) + check_if_equal(test[i], ref[i], "check resize with grow at both ends (values)"); + check_if_equal(test.capacity(), ref.size() + 9, "check resize with grow at both ends (capacity)"); + check_if_equal(test.get_capacity_min_index(), ref.get_min_index() - 5, + "check resize with grow at both ends (capacity_min_index)"); + check_if_equal(test.get_capacity_max_index(), ref.get_max_index() + 4, + "check resize with grow at both ends (capacity_max_index)"); + // check resize with shrink for both ranges + test.recycle(); + check_if_equal(test.capacity(), size_t(0), "test recycle"); + test = ref; + test.resize(test.get_min_index() + 5, test.get_max_index() - 4); + check_if_equal(test.size(), ref.size() - 9, "check resize with shrink at both ends(size)"); + check_if_equal(test.get_min_index(), ref.get_min_index() + 5, "check resize with shrink at both ends(get_min_index)"); + for (int i = test.get_min_index(); i <= test.get_max_index(); ++i) + check_if_equal(test[i], ref[i], "check resize with shrink at both ends (values)"); + check_if_equal(test.capacity(), ref.size(), "check resize with shrink at both ends(capacity)"); + check_if_equal(test.get_capacity_min_index(), ref.get_min_index(), "check resize with shrink at both ends(capacity_min_index)"); + check_if_equal(test.get_capacity_max_index(), ref.get_max_index(), "check resize with shrink at both ends(capacity_max_index)"); + // check resize with shrink at left and grow at right + test.recycle(); + check_if_equal(test.capacity(), size_t(0), "test recycle"); + test = ref; + test.resize(test.get_min_index() + 5, test.get_max_index() + 4); + check_if_equal(test.size(), ref.size() - 1, "check resize with shrink at left and grow at right (size)"); + check_if_equal(test.get_min_index(), ref.get_min_index() + 5, + "check resize with shrink at left and grow at right (get_min_index)"); + for (int i = test.get_min_index(); i <= ref.get_max_index(); ++i) + check_if_equal(test[i], ref[i], "check resize with shrink at left and grow at right (values)"); + check_if_equal(test.capacity(), ref.size() + 4, "check resize with shrink at left and grow at right (capacity)"); + check_if_equal(test.get_capacity_min_index(), ref.get_min_index(), + "check resize with shrink at left and grow at right (capacity_min_index)"); + check_if_equal(test.get_capacity_max_index(), ref.get_max_index() + 4, + "check resize with shrink at left and grow at right (capacity_max_index)"); + // check resize with resize non-overlapping to right + test.recycle(); + check_if_equal(test.capacity(), size_t(0), "test recycle"); + test = ref; + // note: test new size is smaller than original size, so no reallocation should occur + test.resize(test.get_max_index() + 20, static_cast(test.get_max_index() + 20 + ref.size() - 3)); + check_if_equal(test.size(), size_t(ref.size() - 2), "check resize with resize non-overlapping to right (size)"); + check_if_equal(test.get_min_index(), ref.get_max_index() + 20, + "check resize with resize non-overlapping to right (get_min_index)"); + check_if_equal(test.capacity(), ref.size(), "check resize with resize non-overlapping to right (capacity)"); + check_if_equal(test.get_capacity_min_index(), test.get_min_index(), + "check resize with resize non-overlapping to right (capacity_min_index)"); + check_if_equal(test.get_capacity_max_index(), test.get_min_index() + static_cast(test.capacity()) - 1, + "check resize with resize non-overlapping to right (capacity_max_index)"); + // check resize with resize non-overlapping to left + test.recycle(); + check_if_equal(test.capacity(), size_t(0), "test recycle"); + test = ref; + // not: test new size is larger than original size, so reallocation should occur + test.resize(test.get_min_index() - 300, static_cast(test.get_min_index() - 300 + ref.size() + 4)); + check_if_equal(test.size(), size_t(ref.size() + 5), "check resize with resize non-overlapping to left (size)"); + check_if_equal(test.get_min_index(), ref.get_min_index() - 300, + "check resize with resize non-overlapping to left (get_min_index)"); + check_if_equal(test.capacity(), test.size(), "check resize with resize non-overlapping to left (capacity)"); + check_if_equal(test.get_capacity_min_index(), test.get_min_index(), + "check resize with resize non-overlapping to left (capacity_min_index)"); + check_if_equal(test.get_capacity_max_index(), test.get_max_index(), + "check resize with resize non-overlapping to left (capacity_max_index)"); +} +} - // some checks with min_index==0 - { - VectorWithOffset v0(40); - check_if_equal(v0.get_min_index(), 0, "test 1-arg constructor and get_min_index"); - check_if_equal(v0.get_max_index(), 40-1, "test 1-arg constructor and get_max_index"); - check_if_equal(v0.size(), size_t(40), "test 1-arg constructor and size"); - check_if_equal(v0.capacity(), size_t(40), "test 1-arg constructor and capacity"); +/**********************************************************************/ +// tests on operator += etc +/**********************************************************************/ +{ + const VectorWithOffset ref = v; + VectorWithOffset test = ref; + + test = ref; + test += ref; + for (int i = ref.get_min_index(); i <= ref.get_max_index(); ++i) + check_if_equal(test[i], ref[i] * 2, "test operator+=(VectorWithOffset)"); + test = ref; + test -= ref; + for (int i = ref.get_min_index(); i <= ref.get_max_index(); ++i) + check_if_equal(test[i], 0, "test operator-=(VectorWithOffset)"); + test = ref; + test *= ref; + for (int i = ref.get_min_index(); i <= ref.get_max_index(); ++i) + check_if_equal(test[i], ref[i] * ref[i], "test operator*=(VectorWithOffset)"); + + const int minimum = *std::min_element(ref.begin(), ref.end()); + const int ensure_non_zero = minimum <= 0 ? -minimum + 1 : 0; + VectorWithOffset denominator = ref; + test = ref; + for (int i = ref.get_min_index(); i <= ref.get_max_index(); ++i) { + denominator[i] += ensure_non_zero; + test[i] += 10000; } + test /= denominator; + for (int i = ref.get_min_index(); i <= ref.get_max_index(); ++i) + check_if_equal(test[i], (ref[i] + 10000) / (ref[i] + ensure_non_zero), "test operator/=(VectorWithOffset)"); +} - // tests on empty - { - { - VectorWithOffset test; - check(test.empty(), "test default constructor gives empty vector"); - } - { - VectorWithOffset test(1,-1); - check(test.empty(), "test reverse range gives empty vector"); - } - { - VectorWithOffset test(3,6); - check(!test.empty(), "test vector says !empty()"); - test.resize(0); - check(test.empty(), "test vector resized to size 0 is empty()"); - } - } +// some checks with min_index==0 +{ + VectorWithOffset v0(40); + check_if_equal(v0.get_min_index(), 0, "test 1-arg constructor and get_min_index"); + check_if_equal(v0.get_max_index(), 40 - 1, "test 1-arg constructor and get_max_index"); + check_if_equal(v0.size(), size_t(40), "test 1-arg constructor and size"); + check_if_equal(v0.capacity(), size_t(40), "test 1-arg constructor and capacity"); +} - // tests on at() with out-of-range - { - { - VectorWithOffset test; - try - { - int a=test.at(5); - // if we get here, there's a problem, so we report that by failing the next test. - check(false, "test out-of-range on empty vector"); - } - catch (std::out_of_range& ) - { - } - } - { - VectorWithOffset test(1,54); - try - { - test[4]=1; - check_if_equal(test.at(4),1, "test using at() to read content"); - test.at(3)=2; - check_if_equal(test[3],2, "test using at() to set content"); - - int a=test.at(55); - // if we get here, there's a problem, so we report that by failing the next test. - check(false, "test out-of-range on vector"); - } - catch (std::out_of_range& ) - { - } - } - } +// tests on empty +{{VectorWithOffset test; +check(test.empty(), "test default constructor gives empty vector"); +} +{ + VectorWithOffset test(1, -1); + check(test.empty(), "test reverse range gives empty vector"); +} +{ + VectorWithOffset test(3, 6); + check(!test.empty(), "test vector says !empty()"); + test.resize(0); + check(test.empty(), "test vector resized to size 0 is empty()"); +} +} - // checks on using existing data_ptr with constructor indices starting at 0 - { - const int size=100; - int data[size]; - std::fill(data, data+size, 12345); - check_if_equal(data[0], 12345, "test filling data block at 0"); - check_if_equal(data[size-1], 12345, "test filling data block at end"); - // set data_ptr to somewhere in the block to check overrun - int * data_ptr = data+10; - - const int vsize = size-20; - VectorWithOffset v(vsize, data_ptr, data + size); - check(!v.owns_memory_for_data(), "test vector using data_ptr: should not allocate new memory"); - check(data_ptr == v.get_data_ptr(), "test vector using data_ptr: get_data_ptr()"); - v.release_data_ptr(); - check_if_equal(v[1], 12345, "test vector using data_ptr: vector at 1 after construction"); - v[1]=1; - check_if_equal(data_ptr[1], 1, "test filling vector using data_ptr: data at 1 after setting"); - check_if_equal(v[1], 1, "test filling vector using data_ptr: vector at 1 after setting"); - v.fill(2); - check_if_equal(std::accumulate(v.begin(), v.end(), 0), 2*vsize , "test filling vector using data_ptr"); - check_if_equal(data_ptr[0], 2, "test filling vector using data_ptr: data at 0"); - check_if_equal(data_ptr[-1], 12345, "test filling vector using data_ptr: data block before vector"); - check_if_equal(data_ptr[vsize], 12345, "test filling vector using data_ptr: data block after vector"); - - // test resize using existing memory - v[1]=5; - v.resize(1,vsize-5); - check(!v.owns_memory_for_data(), "test vector using data_ptr: resize should not allocate new memory"); - check(data_ptr+1 == v.get_data_ptr(), "test vector using data_ptr: get_data_ptr() after resize"); - v.release_data_ptr(); - check_if_equal(v[1], 5 , "test resizing vector using data_ptr: data at 1"); - v[1]=6; - check_if_equal(data_ptr[1], 6, "test resizing vector using data_ptr: data should still be refered to"); - check_if_equal(std::accumulate(v.begin(), v.end(), 0), 6+v[2]*(v.get_length()-1) , "test resizing vector using data_ptr"); - - // test resize that should allocate new memory - v.resize(-1,vsize-2); - check(v.owns_memory_for_data(), "test vector using data_ptr: resize should allocate new memory"); - v.fill(7); - check_if_equal(data[9], 12345, "test vector using data_ptr: after resize data block at 9"); - check_if_equal(data[size-9], 12345, "test vector using data_ptr: after resize data block at end-9"); - check_if_equal(data_ptr[1], 6, "test vector using data_ptr: after resize data 1"); +// tests on at() with out-of-range +{{VectorWithOffset test; +try { + int a = test.at(5); + // if we get here, there's a problem, so we report that by failing the next test. + check(false, "test out-of-range on empty vector"); +} catch (std::out_of_range&) { +} +} +{ + VectorWithOffset test(1, 54); + try { + test[4] = 1; + check_if_equal(test.at(4), 1, "test using at() to read content"); + test.at(3) = 2; + check_if_equal(test[3], 2, "test using at() to set content"); + + int a = test.at(55); + // if we get here, there's a problem, so we report that by failing the next test. + check(false, "test out-of-range on vector"); + } catch (std::out_of_range&) { } +} +} - // checks on using existing data_ptr with constructor indices starting at -3 - { - const int size=100; - int data[size]; - std::fill(data, data+size, 12345); - check_if_equal(data[0], 12345, "test filling data block at 0"); - check_if_equal(data[size-1], 12345, "test filling data block at end"); - // set data_ptr to somewhere in the block to check overrun - int * data_ptr = data+10; - - const int vsize = size-20; - VectorWithOffset v(-3, vsize-4, data_ptr, data + size); - check_if_equal(v.get_length(), vsize, "test vector using data_ptr (negative min_index):size"); - // first essentially same tests as above - check(!v.owns_memory_for_data(), "test vector using data_ptr (negative min_index): should not allocate new memory"); - check(data_ptr == v.get_data_ptr(), "test vector using data_ptr (negative min_index): get_data_ptr()"); - v.release_data_ptr(); - check_if_equal(v[1], 12345, "test vector using data_ptr (negative min_index): vector at 1 after construction"); - check_if_equal(v[-3], 12345, "test vector using data_ptr (negative min_index): vector at -3 after construction"); - v[-3]=1; - check_if_equal(data_ptr[0], 1, "test filling vector using data_ptr (negative min_index) data at -3 after setting"); - check_if_equal(v[-3], 1, "test filling vector using data_ptr (negative min_index) vector at -3 after setting"); - v.fill(2); - check_if_equal(std::accumulate(v.begin(), v.end(), 0), 2*vsize , "test filling vector using data_ptr (negative min_index)"); - check_if_equal(data_ptr[0], 2, "test filling vector using data_ptr (negative min_index) data at 0"); - check_if_equal(data_ptr[-1], 12345, "test filling vector using data_ptr (negative min_index) data block before vector"); - check_if_equal(data_ptr[vsize], 12345, "test filling vector using data_ptr (negative min_index) data block after vector"); - - // assignment that doesn't reallocate - v = VectorWithOffset(2,6); - check(!v.owns_memory_for_data(), "test vector using data_ptr (negative min_index): assignment should not allocate new memory"); - v[4]=4; - check_if_equal(v[4], 4, "test vector using data_ptr (negative min_index): vector at 4 after assignment and setting"); - // vector will again start at data_ptr - check_if_equal(data_ptr[4-v.get_min_index()], 4, "test vector using data_ptr (negative min_index): data at 4-min_index after assignment and setting"); - // another assignment that does not reallocate - v = VectorWithOffset(static_cast(size-(data_ptr-data) )); - check(!v.owns_memory_for_data(), "test vector using data_ptr (negative min_index): 2nd assignment should not allocate new memory"); - // assignment that does reallocate - v = VectorWithOffset(static_cast(size-(data_ptr-data)+1 )); - check(v.owns_memory_for_data(), "test vector using data_ptr (negative min_index): 3rd assignment should allocate new memory"); - } +// checks on using existing data_ptr with constructor indices starting at 0 +{ + const int size = 100; + int data[size]; + std::fill(data, data + size, 12345); + check_if_equal(data[0], 12345, "test filling data block at 0"); + check_if_equal(data[size - 1], 12345, "test filling data block at end"); + // set data_ptr to somewhere in the block to check overrun + int* data_ptr = data + 10; + + const int vsize = size - 20; + VectorWithOffset v(vsize, data_ptr, data + size); + check(!v.owns_memory_for_data(), "test vector using data_ptr: should not allocate new memory"); + check(data_ptr == v.get_data_ptr(), "test vector using data_ptr: get_data_ptr()"); + v.release_data_ptr(); + check_if_equal(v[1], 12345, "test vector using data_ptr: vector at 1 after construction"); + v[1] = 1; + check_if_equal(data_ptr[1], 1, "test filling vector using data_ptr: data at 1 after setting"); + check_if_equal(v[1], 1, "test filling vector using data_ptr: vector at 1 after setting"); + v.fill(2); + check_if_equal(std::accumulate(v.begin(), v.end(), 0), 2 * vsize, "test filling vector using data_ptr"); + check_if_equal(data_ptr[0], 2, "test filling vector using data_ptr: data at 0"); + check_if_equal(data_ptr[-1], 12345, "test filling vector using data_ptr: data block before vector"); + check_if_equal(data_ptr[vsize], 12345, "test filling vector using data_ptr: data block after vector"); + + // test resize using existing memory + v[1] = 5; + v.resize(1, vsize - 5); + check(!v.owns_memory_for_data(), "test vector using data_ptr: resize should not allocate new memory"); + check(data_ptr + 1 == v.get_data_ptr(), "test vector using data_ptr: get_data_ptr() after resize"); + v.release_data_ptr(); + check_if_equal(v[1], 5, "test resizing vector using data_ptr: data at 1"); + v[1] = 6; + check_if_equal(data_ptr[1], 6, "test resizing vector using data_ptr: data should still be refered to"); + check_if_equal(std::accumulate(v.begin(), v.end(), 0), 6 + v[2] * (v.get_length() - 1), "test resizing vector using data_ptr"); + + // test resize that should allocate new memory + v.resize(-1, vsize - 2); + check(v.owns_memory_for_data(), "test vector using data_ptr: resize should allocate new memory"); + v.fill(7); + check_if_equal(data[9], 12345, "test vector using data_ptr: after resize data block at 9"); + check_if_equal(data[size - 9], 12345, "test vector using data_ptr: after resize data block at end-9"); + check_if_equal(data_ptr[1], 6, "test vector using data_ptr: after resize data 1"); } +// checks on using existing data_ptr with constructor indices starting at -3 +{ + const int size = 100; + int data[size]; + std::fill(data, data + size, 12345); + check_if_equal(data[0], 12345, "test filling data block at 0"); + check_if_equal(data[size - 1], 12345, "test filling data block at end"); + // set data_ptr to somewhere in the block to check overrun + int* data_ptr = data + 10; + + const int vsize = size - 20; + VectorWithOffset v(-3, vsize - 4, data_ptr, data + size); + check_if_equal(v.get_length(), vsize, "test vector using data_ptr (negative min_index):size"); + // first essentially same tests as above + check(!v.owns_memory_for_data(), "test vector using data_ptr (negative min_index): should not allocate new memory"); + check(data_ptr == v.get_data_ptr(), "test vector using data_ptr (negative min_index): get_data_ptr()"); + v.release_data_ptr(); + check_if_equal(v[1], 12345, "test vector using data_ptr (negative min_index): vector at 1 after construction"); + check_if_equal(v[-3], 12345, "test vector using data_ptr (negative min_index): vector at -3 after construction"); + v[-3] = 1; + check_if_equal(data_ptr[0], 1, "test filling vector using data_ptr (negative min_index) data at -3 after setting"); + check_if_equal(v[-3], 1, "test filling vector using data_ptr (negative min_index) vector at -3 after setting"); + v.fill(2); + check_if_equal(std::accumulate(v.begin(), v.end(), 0), 2 * vsize, "test filling vector using data_ptr (negative min_index)"); + check_if_equal(data_ptr[0], 2, "test filling vector using data_ptr (negative min_index) data at 0"); + check_if_equal(data_ptr[-1], 12345, "test filling vector using data_ptr (negative min_index) data block before vector"); + check_if_equal(data_ptr[vsize], 12345, "test filling vector using data_ptr (negative min_index) data block after vector"); + + // assignment that doesn't reallocate + v = VectorWithOffset(2, 6); + check(!v.owns_memory_for_data(), "test vector using data_ptr (negative min_index): assignment should not allocate new memory"); + v[4] = 4; + check_if_equal(v[4], 4, "test vector using data_ptr (negative min_index): vector at 4 after assignment and setting"); + // vector will again start at data_ptr + check_if_equal(data_ptr[4 - v.get_min_index()], 4, + "test vector using data_ptr (negative min_index): data at 4-min_index after assignment and setting"); + // another assignment that does not reallocate + v = VectorWithOffset(static_cast(size - (data_ptr - data))); + check(!v.owns_memory_for_data(), + "test vector using data_ptr (negative min_index): 2nd assignment should not allocate new memory"); + // assignment that does reallocate + v = VectorWithOffset(static_cast(size - (data_ptr - data) + 1)); + check(v.owns_memory_for_data(), "test vector using data_ptr (negative min_index): 3rd assignment should allocate new memory"); +} +} END_NAMESPACE_STIR USING_NAMESPACE_STIR -int main() -{ +int +main() { VectorWithOffsetTests tests; tests.run_tests(); return tests.main_return_value(); diff --git a/src/test/test_VoxelsOnCartesianGrid.cxx b/src/test/test_VoxelsOnCartesianGrid.cxx index 8f4025ddc6..6a7d550790 100644 --- a/src/test/test_VoxelsOnCartesianGrid.cxx +++ b/src/test/test_VoxelsOnCartesianGrid.cxx @@ -2,35 +2,35 @@ // /* Copyright (C) 2000 PARAPET partners - Copyright (C) 2000- 2011, Hammersmith Imanet Ltd + Copyright (C) 2000- 2011, Hammersmith Imanet Ltd Copyright (C) 2018, Commonwealth Scientific and Industrial Research Organisation Australian eHealth Research Centre Copyright (C) 2019, University College London - This file is part of STIR. - - This file is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - This file is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - + This file is part of STIR. + + This file is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + See STIR/LICENSE.txt for details */ /*! \file \ingroup test - + \brief Test program for stir::VoxelsOnCartesianGrid and image hierarchy - + \author Ashley Gillman \author Sanida Mustafovic \author Kris Thielemans \author PARAPET project - + */ #include "stir/VoxelsOnCartesianGrid.h" @@ -55,221 +55,197 @@ START_NAMESPACE_STIR \brief Test class for VoxelsOnCartesianGrid and image hierarchy */ -class VoxelsOnCartesianGridTests : public RunTests -{ +class VoxelsOnCartesianGridTests : public RunTests { public: void run_tests(); }; - void VoxelsOnCartesianGridTests::run_tests() -{ +{ cerr << "Tests for VoxelsOnCartesianGrid and the image hierarchy\n"; - - CartesianCoordinate3D origin (0,1,2); - CartesianCoordinate3D grid_spacing (3,4,5); - - IndexRange<3> - range(CartesianCoordinate3D(0,-15,-14), - CartesianCoordinate3D(4,14,15)); - - Array<3,float> test1(range); - + + CartesianCoordinate3D origin(0, 1, 2); + CartesianCoordinate3D grid_spacing(3, 4, 5); + + IndexRange<3> range(CartesianCoordinate3D(0, -15, -14), CartesianCoordinate3D(4, 14, 15)); + + Array<3, float> test1(range); + { cerr << "Tests with default constructor\n"; - - VoxelsOnCartesianGrid ob1; - + + VoxelsOnCartesianGrid ob1; + // Check set.* & constructor - + ob1.set_origin(origin); - ob1.set_grid_spacing (grid_spacing); - - check_if_equal( ob1.get_grid_spacing(), grid_spacing,"test on grid_spacing"); - check_if_equal( ob1.get_origin(), origin, "test on origin"); + ob1.set_grid_spacing(grid_spacing); + + check_if_equal(ob1.get_grid_spacing(), grid_spacing, "test on grid_spacing"); + check_if_equal(ob1.get_origin(), origin, "test on origin"); } - + { cerr << "Tests with 2nd constructor (array, origin, grid_spacing)\n"; - - VoxelsOnCartesianGrid ob2(test1,origin, grid_spacing); + + VoxelsOnCartesianGrid ob2(test1, origin, grid_spacing); test1[1][12][5] = float(5.5); test1[4][5][-5] = float(4.5); - - check_if_equal( ob2.get_grid_spacing(),grid_spacing, "test on grid_spacing"); - check_if_equal( ob2.get_origin(), origin, "test on origin"); - check_if_equal( test1.sum(), 10.F, "test on arrays"); + + check_if_equal(ob2.get_grid_spacing(), grid_spacing, "test on grid_spacing"); + check_if_equal(ob2.get_origin(), origin, "test on origin"); + check_if_equal(test1.sum(), 10.F, "test on arrays"); } { - + cerr << "Tests with 3rd constructor(index_range, origin, grid_spacing)\n"; - VoxelsOnCartesianGrid ob3(range,origin, grid_spacing); - - check( ob3.get_index_range() == range, "test on range"); - check_if_equal( ob3.get_grid_spacing(),grid_spacing, "test on grid_spacing"); - check_if_equal( ob3.get_origin(), origin, "test on origin"); - - const BasicCoordinate<3,int> indices = make_coordinate(1,2,3); - const CartesianCoordinate3D coord = - ob3.get_physical_coordinates_for_indices(indices); - const CartesianCoordinate3D rel_coord = - ob3.get_relative_coordinates_for_indices(indices); - - check_if_equal(coord, rel_coord + origin, - "test on get_physical_coordinates_for_indices"); - check_if_equal(rel_coord, grid_spacing*BasicCoordinate<3,float>(indices), - "test on get_relative_coordinates_for_indices"); - check_if_equal(indices, ob3.get_indices_closest_to_relative_coordinates(rel_coord), - "test on get_indices_closest_to_relative_coordinates"); - check_if_equal(indices, ob3.get_indices_closest_to_physical_coordinates(coord), - "test on get_indices_closest_to_relative_coordinates"); - check_if_equal(indices,ob3.get_indices_closest_to_relative_coordinates(rel_coord + grid_spacing/3), - "test on get_indices_closest_to_relative_coordinates (not on grid point)"); + VoxelsOnCartesianGrid ob3(range, origin, grid_spacing); + + check(ob3.get_index_range() == range, "test on range"); + check_if_equal(ob3.get_grid_spacing(), grid_spacing, "test on grid_spacing"); + check_if_equal(ob3.get_origin(), origin, "test on origin"); + const BasicCoordinate<3, int> indices = make_coordinate(1, 2, 3); + const CartesianCoordinate3D coord = ob3.get_physical_coordinates_for_indices(indices); + const CartesianCoordinate3D rel_coord = ob3.get_relative_coordinates_for_indices(indices); + check_if_equal(coord, rel_coord + origin, "test on get_physical_coordinates_for_indices"); + check_if_equal(rel_coord, grid_spacing * BasicCoordinate<3, float>(indices), "test on get_relative_coordinates_for_indices"); + check_if_equal(indices, ob3.get_indices_closest_to_relative_coordinates(rel_coord), + "test on get_indices_closest_to_relative_coordinates"); + check_if_equal(indices, ob3.get_indices_closest_to_physical_coordinates(coord), + "test on get_indices_closest_to_relative_coordinates"); + check_if_equal(indices, ob3.get_indices_closest_to_relative_coordinates(rel_coord + grid_spacing / 3), + "test on get_indices_closest_to_relative_coordinates (not on grid point)"); } - + shared_ptr scanner_ptr(new Scanner(Scanner::E953)); - shared_ptr proj_data_info_ptr( - ProjDataInfo::ProjDataInfoCTI(scanner_ptr, - /*span=*/1, - /*max_delta=*/5, - /*num_views=*/8, - /*num_tang_poss=*/16)); - + shared_ptr proj_data_info_ptr(ProjDataInfo::ProjDataInfoCTI(scanner_ptr, + /*span=*/1, + /*max_delta=*/5, + /*num_views=*/8, + /*num_tang_poss=*/16)); + { cerr << "Tests with constructor with ProjDataInfo with default sizes\n"; - - const float zoom=2.3F; - //KT 10/12/2001 removed make_xy_size_odd things - - VoxelsOnCartesianGrid - ob4(*proj_data_info_ptr,zoom,origin); - + + const float zoom = 2.3F; + // KT 10/12/2001 removed make_xy_size_odd things + + VoxelsOnCartesianGrid ob4(*proj_data_info_ptr, zoom, origin); + IndexRange<3> obtained_range = ob4.get_index_range(); CartesianCoordinate3D low_bound, high_bound; check(obtained_range.get_regular_range(low_bound, high_bound), "test regular range"); - + // KT 11/09/2001 adapted as this constructor now takes zoom into account - const bool is_arccorrected = - dynamic_cast(proj_data_info_ptr.get()) != 0; + const bool is_arccorrected = dynamic_cast(proj_data_info_ptr.get()) != 0; check(is_arccorrected, "ProjDataInfoCTI should have returned arc-corrected data"); - if (is_arccorrected) - { - const int FOVradius_in_bins = - max(proj_data_info_ptr->get_max_tangential_pos_num(), - -proj_data_info_ptr->get_min_tangential_pos_num()); - const int diameter_int = - 2*static_cast(ceil(FOVradius_in_bins * zoom)) + 1; - - check_if_equal(low_bound, CartesianCoordinate3D(0,-(diameter_int/2),-(diameter_int/2)), - "test on index range: lower bounds"); - check_if_equal(high_bound, CartesianCoordinate3D(30,+(diameter_int/2),+(diameter_int/2)), - "test on index range: higher bounds"); + if (is_arccorrected) { + const int FOVradius_in_bins = + max(proj_data_info_ptr->get_max_tangential_pos_num(), -proj_data_info_ptr->get_min_tangential_pos_num()); + const int diameter_int = 2 * static_cast(ceil(FOVradius_in_bins * zoom)) + 1; + + check_if_equal(low_bound, CartesianCoordinate3D(0, -(diameter_int / 2), -(diameter_int / 2)), + "test on index range: lower bounds"); + check_if_equal(high_bound, CartesianCoordinate3D(30, +(diameter_int / 2), +(diameter_int / 2)), + "test on index range: higher bounds"); } - check_if_equal(ob4.get_grid_spacing(), - CartesianCoordinate3D(scanner_ptr->get_ring_spacing()/2, - scanner_ptr->get_default_bin_size()/zoom, - scanner_ptr->get_default_bin_size()/zoom), + check_if_equal(ob4.get_grid_spacing(), + CartesianCoordinate3D(scanner_ptr->get_ring_spacing() / 2, scanner_ptr->get_default_bin_size() / zoom, + scanner_ptr->get_default_bin_size() / zoom), "test on grid spacing"); check_if_equal(ob4.get_origin(), origin); } { - + cerr << "Tests with constructor with ProjDataInfo with non-default sizes\n"; // KT 10/12/2001 changed to allow for new format of constructor, and add z_size const int xy_size = 100; - const float zoom=3.1F; - const int min_xy = -(xy_size/2); - const int max_xy = -(xy_size/2)+xy_size-1; + const float zoom = 3.1F; + const int min_xy = -(xy_size / 2); + const int max_xy = -(xy_size / 2) + xy_size - 1; const int z_size = 9; - VoxelsOnCartesianGrid - ob5(*proj_data_info_ptr,zoom,origin,CartesianCoordinate3D(z_size,xy_size,xy_size)); + VoxelsOnCartesianGrid ob5(*proj_data_info_ptr, zoom, origin, CartesianCoordinate3D(z_size, xy_size, xy_size)); // put in some data for further testing ob5.fill(1.F); - ob5[1][1][1]=5.F; + ob5[1][1][1] = 5.F; IndexRange<3> obtained_range = ob5.get_index_range(); CartesianCoordinate3D low_bound, high_bound; check(obtained_range.get_regular_range(low_bound, high_bound), "test regular range"); - - check_if_equal(low_bound, CartesianCoordinate3D(0,min_xy,min_xy),"test on index range: lower bounds"); - check_if_equal(high_bound, CartesianCoordinate3D(z_size-1,max_xy,max_xy),"test on index range: higher bounds"); - check_if_equal(ob5.get_grid_spacing(), - CartesianCoordinate3D(scanner_ptr->get_ring_spacing()/2, - scanner_ptr->get_default_bin_size()/zoom, - scanner_ptr->get_default_bin_size()/zoom), + + check_if_equal(low_bound, CartesianCoordinate3D(0, min_xy, min_xy), "test on index range: lower bounds"); + check_if_equal(high_bound, CartesianCoordinate3D(z_size - 1, max_xy, max_xy), "test on index range: higher bounds"); + check_if_equal(ob5.get_grid_spacing(), + CartesianCoordinate3D(scanner_ptr->get_ring_spacing() / 2, scanner_ptr->get_default_bin_size() / zoom, + scanner_ptr->get_default_bin_size() / zoom), "test on grid spacing"); check_if_equal(ob5.get_origin(), origin); { // with different zooms shared_ptr exam_info_sptr(new ExamInfo()); - CartesianCoordinate3D zooms(1.1F,1.2F,1.3F); - VoxelsOnCartesianGrid - ob6(exam_info_sptr, *proj_data_info_ptr,zooms,origin,CartesianCoordinate3D(z_size,xy_size,xy_size)); + CartesianCoordinate3D zooms(1.1F, 1.2F, 1.3F); + VoxelsOnCartesianGrid ob6(exam_info_sptr, *proj_data_info_ptr, zooms, origin, + CartesianCoordinate3D(z_size, xy_size, xy_size)); check_if_equal(ob6.get_grid_spacing(), - CartesianCoordinate3D(scanner_ptr->get_ring_spacing()/2/zooms[1], - scanner_ptr->get_default_bin_size()/zooms[2], - scanner_ptr->get_default_bin_size()/zooms[3]), + CartesianCoordinate3D(scanner_ptr->get_ring_spacing() / 2 / zooms[1], + scanner_ptr->get_default_bin_size() / zooms[2], + scanner_ptr->get_default_bin_size() / zooms[3]), "test on grid spacing (3 different zooms)"); check_if_equal(ob6.get_origin(), origin); } { cerr << "Tests get_empty_voxels_on_cartesian_grid\n"; - - shared_ptr< VoxelsOnCartesianGrid > emp(ob5.get_empty_voxels_on_cartesian_grid()); - + + shared_ptr> emp(ob5.get_empty_voxels_on_cartesian_grid()); + IndexRange<3> obtained_range2 = emp->get_index_range(); - check_if_equal( emp->get_origin(), ob5.get_origin(), "test on origin"); - check_if_equal( emp->get_grid_spacing(), ob5.get_grid_spacing(),"test on grid_spacing"); - check(emp->get_index_range() == ob5.get_index_range(),"test on index range"); - + check_if_equal(emp->get_origin(), ob5.get_origin(), "test on origin"); + check_if_equal(emp->get_grid_spacing(), ob5.get_grid_spacing(), "test on grid_spacing"); + check(emp->get_index_range() == ob5.get_index_range(), "test on index range"); } - + { cerr << "Tests get_empty_copy()\n"; - - shared_ptr > emp(ob5.get_empty_copy()); - - VoxelsOnCartesianGrid* emp1 = - dynamic_cast* >(emp.get()); + + shared_ptr> emp(ob5.get_empty_copy()); + + VoxelsOnCartesianGrid* emp1 = dynamic_cast*>(emp.get()); check(emp1 != 0, "test on pointer conversion from get_empty_copy"); - + IndexRange<3> obtained_range3 = emp1->get_index_range(); - check_if_equal( emp->get_origin(), ob5.get_origin(), "test on origin"); - check_if_equal( emp1->get_grid_spacing(), ob5.get_grid_spacing(),"test on grid_spacing"); - check(emp->get_index_range() == ob5.get_index_range(),"test on index range"); + check_if_equal(emp->get_origin(), ob5.get_origin(), "test on origin"); + check_if_equal(emp1->get_grid_spacing(), ob5.get_grid_spacing(), "test on grid_spacing"); + check(emp->get_index_range() == ob5.get_index_range(), "test on index range"); } { cerr << "Tests has_same_characteristics()\n"; - shared_ptr > emp (ob5.get_empty_copy()); + shared_ptr> emp(ob5.get_empty_copy()); check(ob5.has_same_characteristics(*emp), "test on has_same_characteristics after get_empty_copy"); check(ob5 != *emp, "test on operator!= after get_empty_copy"); *emp += ob5; check(ob5 == *emp, "test on operator== after get_empty_copy and operator+="); - emp->set_origin(ob5.get_origin()+1.F); + emp->set_origin(ob5.get_origin() + 1.F); check(ob5 != *emp, "test on operator!= after shifting origin"); emp->set_origin(ob5.get_origin()); check(ob5 == *emp, "test on operator== after shifting origin back to original"); - dynamic_cast& >(*emp). - set_grid_spacing(ob5.get_grid_spacing()+.3F); + dynamic_cast&>(*emp).set_grid_spacing(ob5.get_grid_spacing() + .3F); check(ob5 != *emp, "test on operator!= after changing voxel size"); - dynamic_cast& >(*emp). - set_grid_spacing(ob5.get_grid_spacing()); + dynamic_cast&>(*emp).set_grid_spacing(ob5.get_grid_spacing()); check(ob5 == *emp, "test on operator== after changing voxel size back to original"); { - IndexRange<3> range = emp->get_index_range(); - range.resize(0,0); - emp->resize(range); - check(!ob5.has_same_characteristics(*emp), "test on has_same_characteristics after resize"); + IndexRange<3> range = emp->get_index_range(); + range.resize(0, 0); + emp->resize(range); + check(!ob5.has_same_characteristics(*emp), "test on has_same_characteristics after resize"); } } - } { @@ -278,98 +254,72 @@ VoxelsOnCartesianGridTests::run_tests() shared_ptr hfs_exam_info_sptr(new ExamInfo()); hfs_exam_info_sptr->patient_position.set_orientation(PatientPosition::head_in); hfs_exam_info_sptr->patient_position.set_rotation(PatientPosition::supine); - VoxelsOnCartesianGrid hfs_image(hfs_exam_info_sptr, - range, origin, grid_spacing); + VoxelsOnCartesianGrid hfs_image(hfs_exam_info_sptr, range, origin, grid_spacing); shared_ptr ffs_exam_info_sptr(new ExamInfo()); ffs_exam_info_sptr->patient_position.set_orientation(PatientPosition::feet_in); ffs_exam_info_sptr->patient_position.set_rotation(PatientPosition::supine); - VoxelsOnCartesianGrid ffs_image(ffs_exam_info_sptr, - range, origin, grid_spacing); + VoxelsOnCartesianGrid ffs_image(ffs_exam_info_sptr, range, origin, grid_spacing); shared_ptr hfp_exam_info_sptr(new ExamInfo()); hfp_exam_info_sptr->patient_position.set_orientation(PatientPosition::head_in); hfp_exam_info_sptr->patient_position.set_rotation(PatientPosition::prone); - VoxelsOnCartesianGrid hfp_image(hfp_exam_info_sptr, - range, origin, grid_spacing); + VoxelsOnCartesianGrid hfp_image(hfp_exam_info_sptr, range, origin, grid_spacing); shared_ptr ffp_exam_info_sptr(new ExamInfo()); ffp_exam_info_sptr->patient_position.set_orientation(PatientPosition::feet_in); ffp_exam_info_sptr->patient_position.set_rotation(PatientPosition::prone); - VoxelsOnCartesianGrid ffp_image(ffp_exam_info_sptr, - range, origin, grid_spacing); + VoxelsOnCartesianGrid ffp_image(ffp_exam_info_sptr, range, origin, grid_spacing); - const BasicCoordinate<3,int> indices = make_coordinate(1,2,3); + const BasicCoordinate<3, int> indices = make_coordinate(1, 2, 3); // Check some known relations check_if_equal(hfs_image.get_LPS_coordinates_for_indices(indices).x(), - -hfp_image.get_LPS_coordinates_for_indices(indices).x(), - "head in (suppine/prone) have opposite x"); + -hfp_image.get_LPS_coordinates_for_indices(indices).x(), "head in (suppine/prone) have opposite x"); check_if_equal(hfs_image.get_LPS_coordinates_for_indices(indices).y(), - -hfp_image.get_LPS_coordinates_for_indices(indices).y(), - "head in (suppine/prone) have opposite y"); - check_if_equal(hfs_image.get_LPS_coordinates_for_indices(indices).z(), - hfp_image.get_LPS_coordinates_for_indices(indices).z(), + -hfp_image.get_LPS_coordinates_for_indices(indices).y(), "head in (suppine/prone) have opposite y"); + check_if_equal(hfs_image.get_LPS_coordinates_for_indices(indices).z(), hfp_image.get_LPS_coordinates_for_indices(indices).z(), "head in (suppine/prone) have same z"); check_if_equal(ffs_image.get_LPS_coordinates_for_indices(indices).x(), - -ffp_image.get_LPS_coordinates_for_indices(indices).x(), - "feet in (suppine/prone) have opposite x"); + -ffp_image.get_LPS_coordinates_for_indices(indices).x(), "feet in (suppine/prone) have opposite x"); check_if_equal(ffs_image.get_LPS_coordinates_for_indices(indices).y(), - -ffp_image.get_LPS_coordinates_for_indices(indices).y(), - "feet in (suppine/prone) have opposite y"); - check_if_equal(ffs_image.get_LPS_coordinates_for_indices(indices).z(), - ffp_image.get_LPS_coordinates_for_indices(indices).z(), + -ffp_image.get_LPS_coordinates_for_indices(indices).y(), "feet in (suppine/prone) have opposite y"); + check_if_equal(ffs_image.get_LPS_coordinates_for_indices(indices).z(), ffp_image.get_LPS_coordinates_for_indices(indices).z(), "feet in (suppine/prone) have same z"); check_if_equal(hfs_image.get_LPS_coordinates_for_indices(indices).x(), - -ffs_image.get_LPS_coordinates_for_indices(indices).x(), - "(head/feet) in suppine have opposite x"); - check_if_equal(hfs_image.get_LPS_coordinates_for_indices(indices).y(), - ffs_image.get_LPS_coordinates_for_indices(indices).y(), + -ffs_image.get_LPS_coordinates_for_indices(indices).x(), "(head/feet) in suppine have opposite x"); + check_if_equal(hfs_image.get_LPS_coordinates_for_indices(indices).y(), ffs_image.get_LPS_coordinates_for_indices(indices).y(), "(head/feet) in suppine have same y"); check_if_equal(hfs_image.get_LPS_coordinates_for_indices(indices).z(), - -ffs_image.get_LPS_coordinates_for_indices(indices).z(), - "(head/feet) in suppine have opposite z"); + -ffs_image.get_LPS_coordinates_for_indices(indices).z(), "(head/feet) in suppine have opposite z"); check_if_equal(hfp_image.get_LPS_coordinates_for_indices(indices).x(), - -ffp_image.get_LPS_coordinates_for_indices(indices).x(), - "(head/feet) in prone have opposite x"); - check_if_equal(hfp_image.get_LPS_coordinates_for_indices(indices).y(), - ffp_image.get_LPS_coordinates_for_indices(indices).y(), + -ffp_image.get_LPS_coordinates_for_indices(indices).x(), "(head/feet) in prone have opposite x"); + check_if_equal(hfp_image.get_LPS_coordinates_for_indices(indices).y(), ffp_image.get_LPS_coordinates_for_indices(indices).y(), "(head/feet) in prone have same y"); check_if_equal(hfp_image.get_LPS_coordinates_for_indices(indices).z(), - -ffp_image.get_LPS_coordinates_for_indices(indices).z(), - "(head/feet) in prone have opposite z"); + -ffp_image.get_LPS_coordinates_for_indices(indices).z(), "(head/feet) in prone have opposite z"); // Check inverse consistency - check_if_equal(indices, - hfs_image.get_indices_closest_to_LPS_coordinates( - hfs_image.get_LPS_coordinates_for_indices(indices)), + check_if_equal(indices, hfs_image.get_indices_closest_to_LPS_coordinates(hfs_image.get_LPS_coordinates_for_indices(indices)), "HFS inverse consistency"); - check_if_equal(indices, - ffs_image.get_indices_closest_to_LPS_coordinates( - ffs_image.get_LPS_coordinates_for_indices(indices)), + check_if_equal(indices, ffs_image.get_indices_closest_to_LPS_coordinates(ffs_image.get_LPS_coordinates_for_indices(indices)), "FFS inverse consistency"); - check_if_equal(indices, - hfp_image.get_indices_closest_to_LPS_coordinates( - hfp_image.get_LPS_coordinates_for_indices(indices)), + check_if_equal(indices, hfp_image.get_indices_closest_to_LPS_coordinates(hfp_image.get_LPS_coordinates_for_indices(indices)), "HFP inverse consistency"); - check_if_equal(indices, - ffp_image.get_indices_closest_to_LPS_coordinates( - ffp_image.get_LPS_coordinates_for_indices(indices)), + check_if_equal(indices, ffp_image.get_indices_closest_to_LPS_coordinates(ffp_image.get_LPS_coordinates_for_indices(indices)), "FFP inverse consistency"); } } END_NAMESPACE_STIR - USING_NAMESPACE_STIR - -int main() -{ +int +main() { VoxelsOnCartesianGridTests tests; tests.run_tests(); return tests.main_return_value(); diff --git a/src/test/test_convert_array.cxx b/src/test/test_convert_array.cxx index 5d6274206a..97360a4d06 100644 --- a/src/test/test_convert_array.cxx +++ b/src/test/test_convert_array.cxx @@ -18,8 +18,8 @@ See STIR/LICENSE.txt for details */ /*! - \file - \ingroup test + \file + \ingroup test \brief tests for the stir::convert_array functions \author Kris Thielemans @@ -41,108 +41,100 @@ using std::endl; START_NAMESPACE_STIR //! tests convert_array functionality -class convert_array_Tests : public RunTests -{ +class convert_array_Tests : public RunTests { public: void run_tests(); }; - void -convert_array_Tests::run_tests() -{ +convert_array_Tests::run_tests() { + + cerr << "Test program for 'convert_array'." << endl << "Everything is fine when there is no output below." << endl; - cerr << "Test program for 'convert_array'." << endl - << "Everything is fine when there is no output below." << endl; - // 1D { - Array<1,float> tf1(1,20); + Array<1, float> tf1(1, 20); tf1.fill(100.F); - - Array<1,short> ti1(1,20); + + Array<1, short> ti1(1, 20); ti1.fill(100); - + { // float -> short with a preferred scale factor float scale_factor = float(1); - Array<1,short> ti2 = convert_array(scale_factor, tf1, NumericInfo()); - - check(scale_factor == float(1),"test convert_array float->short 1D"); + Array<1, short> ti2 = convert_array(scale_factor, tf1, NumericInfo()); + + check(scale_factor == float(1), "test convert_array float->short 1D"); check_if_equal(ti1, ti2, "test convert_array float->short 1D"); } - - + { // float -> short with automatic scale factor float scale_factor = 0; - Array<1,short> ti2 = convert_array(scale_factor, tf1, NumericInfo()); - - check(fabs(NumericInfo().max_value()/1.01 / ti2[1] -1) < 1E-4); - for (int i=1; i<= 20; i++) - ti2[i] = short( double(ti2[i]) *scale_factor); + Array<1, short> ti2 = convert_array(scale_factor, tf1, NumericInfo()); + + check(fabs(NumericInfo().max_value() / 1.01 / ti2[1] - 1) < 1E-4); + for (int i = 1; i <= 20; i++) + ti2[i] = short(double(ti2[i]) * scale_factor); check(ti1 == ti2); } - + tf1 *= 1E20F; { // float -> short with a preferred scale factor that needs to be adjusted float scale_factor = 1; - Array<1,short> ti2 = convert_array(scale_factor, tf1, NumericInfo()); - - check(fabs(NumericInfo().max_value()/1.01 / ti2[1] -1) < 1E-4); - for (int i=1; i<= 20; i++) - check(fabs(double(ti2[i]) *scale_factor / tf1[i] - 1) < 1E-4) ; - + Array<1, short> ti2 = convert_array(scale_factor, tf1, NumericInfo()); + + check(fabs(NumericInfo().max_value() / 1.01 / ti2[1] - 1) < 1E-4); + for (int i = 1; i <= 20; i++) + check(fabs(double(ti2[i]) * scale_factor / tf1[i] - 1) < 1E-4); } - + { // short -> float with a scale factor = 1 float scale_factor = 1; - Array<1,float> tf2 = convert_array(scale_factor, ti1, NumericInfo()); - Array<1,short> ti2(1,20); - - + Array<1, float> tf2 = convert_array(scale_factor, ti1, NumericInfo()); + Array<1, short> ti2(1, 20); + check(scale_factor == float(1)); check(tf2[1] == 100.F); - for (int i=1; i<= 20; i++) - ti2[i] = short(double(tf2[i]) *scale_factor) ; + for (int i = 1; i <= 20; i++) + ti2[i] = short(double(tf2[i]) * scale_factor); check(ti1 == ti2); } - + { // short -> float with a preferred scale factor = .01 float scale_factor = .01F; - Array<1,float> tf2 = convert_array(scale_factor, ti1, NumericInfo()); - Array<1,short> ti2(1,20); - + Array<1, float> tf2 = convert_array(scale_factor, ti1, NumericInfo()); + Array<1, short> ti2(1, 20); + check(scale_factor == float(.01)); - //TODO double->short - for (int i=1; i<= 20; i++) - ti2[i] = short(double(tf2[i]) *scale_factor + 0.5) ; + // TODO double->short + for (int i = 1; i <= 20; i++) + ti2[i] = short(double(tf2[i]) * scale_factor + 0.5); check(ti1 == ti2); } - + tf1.fill(-3.2F); ti1.fill(-3); { // positive float -> unsigned short with a preferred scale factor float scale_factor = 1; - Array<1,short> ti2 = convert_array(scale_factor, tf1, NumericInfo()); - + Array<1, short> ti2 = convert_array(scale_factor, tf1, NumericInfo()); + check(scale_factor == float(1)); check(ti1 == ti2); } - + { - Array<1,unsigned short> ti3(1,20); + Array<1, unsigned short> ti3(1, 20); ti3.fill(0); - + // negative float -> unsigned short with a preferred scale factor float scale_factor = 1; - Array<1,unsigned short> ti2 = - convert_array(scale_factor, tf1, NumericInfo()); - + Array<1, unsigned short> ti2 = convert_array(scale_factor, tf1, NumericInfo()); + check(scale_factor == float(1)); check(ti3 == ti2); } @@ -150,99 +142,85 @@ convert_array_Tests::run_tests() // 3D { - Array<3,float> tf1(IndexRange3D(1,30,1,182,-2,182)); + Array<3, float> tf1(IndexRange3D(1, 30, 1, 182, -2, 182)); tf1.fill(100.F); - - Array<3,short> ti1(tf1.get_index_range()); + + Array<3, short> ti1(tf1.get_index_range()); ti1.fill(100); - + { // float -> short with a preferred scale factor float scale_factor = float(1); - Array<3,short> ti2 = convert_array(scale_factor, tf1, NumericInfo()); - + Array<3, short> ti2 = convert_array(scale_factor, tf1, NumericInfo()); + check(scale_factor == float(1)); check(ti1 == ti2); } - + { // float -> short with automatic scale factor float scale_factor = 0; - Array<3,short> ti2 = convert_array(scale_factor, tf1, NumericInfo()); -#ifndef DO_TIMING_ONLY - check(fabs(NumericInfo().max_value()/1.01 / (*ti2.begin_all()) -1) < 1E-4); - const Array<3,short>::full_iterator iter_end= ti2.end_all(); - for (Array<3,short>::full_iterator iter= ti2.begin_all(); - iter != iter_end; - ++iter) - *iter = short( double((*iter)) *scale_factor); + Array<3, short> ti2 = convert_array(scale_factor, tf1, NumericInfo()); +#ifndef DO_TIMING_ONLY + check(fabs(NumericInfo().max_value() / 1.01 / (*ti2.begin_all()) - 1) < 1E-4); + const Array<3, short>::full_iterator iter_end = ti2.end_all(); + for (Array<3, short>::full_iterator iter = ti2.begin_all(); iter != iter_end; ++iter) + *iter = short(double((*iter)) * scale_factor); check(ti1 == ti2); #endif } - + tf1 *= 1E20F; { // float -> short with a preferred scale factor that needs to be adjusted float scale_factor = 1; - Array<3,short> ti2 = convert_array(scale_factor, tf1, NumericInfo()); + Array<3, short> ti2 = convert_array(scale_factor, tf1, NumericInfo()); #ifndef DO_TIMING_ONLY - check(fabs(NumericInfo().max_value()/1.01 / (*ti2.begin_all()) -1) < 1E-4); - Array<3,short>::full_iterator iter_ti2= ti2.begin_all(); - const Array<3,short>::full_iterator iter_ti2_end= ti2.end_all(); - Array<3,float>::full_iterator iter_tf1= tf1.begin_all(); - for (; - iter_ti2 != iter_ti2_end; - ++iter_ti2, ++iter_tf1) - check(fabs(double(*iter_ti2) *scale_factor / *iter_tf1 - 1) < 1E-4) ; -#endif + check(fabs(NumericInfo().max_value() / 1.01 / (*ti2.begin_all()) - 1) < 1E-4); + Array<3, short>::full_iterator iter_ti2 = ti2.begin_all(); + const Array<3, short>::full_iterator iter_ti2_end = ti2.end_all(); + Array<3, float>::full_iterator iter_tf1 = tf1.begin_all(); + for (; iter_ti2 != iter_ti2_end; ++iter_ti2, ++iter_tf1) + check(fabs(double(*iter_ti2) * scale_factor / *iter_tf1 - 1) < 1E-4); +#endif } } // tests on convert_range { - std::vector vin(10,2); + std::vector vin(10, 2); std::vector vout(10); - float scale_factor=0; + float scale_factor = 0; convert_range(vout.begin(), scale_factor, vin.begin(), vin.end()); { - std::vector::const_iterator iter_out= vout.begin(); - std::vector::const_iterator iter_in= vin.begin(); - for (; - iter_out != vout.end(); - ++iter_in, ++iter_out) - check(fabs(double(*iter_out) *scale_factor / *iter_in - 1) < 1E-4, - "convert_range signed char->int") ; + std::vector::const_iterator iter_out = vout.begin(); + std::vector::const_iterator iter_in = vin.begin(); + for (; iter_out != vout.end(); ++iter_in, ++iter_out) + check(fabs(double(*iter_out) * scale_factor / *iter_in - 1) < 1E-4, "convert_range signed char->int"); } } // equal type { - std::vector vin(10,2); + std::vector vin(10, 2); std::vector vout(10); - float scale_factor=3; + float scale_factor = 3; convert_range(vout.begin(), scale_factor, vin.begin(), vin.end()); { check_if_equal(scale_factor, 1.F, "scale_factor should be 1 when using equal types"); - std::vector::const_iterator iter_out= vout.begin(); - std::vector::const_iterator iter_in= vin.begin(); - for (; - iter_out != vout.end(); - ++iter_in, ++iter_out) - check(fabs(double(*iter_out) *scale_factor / *iter_in - 1) < 1E-4, - "convert_range equal types") ; + std::vector::const_iterator iter_out = vout.begin(); + std::vector::const_iterator iter_in = vin.begin(); + for (; iter_out != vout.end(); ++iter_in, ++iter_out) + check(fabs(double(*iter_out) * scale_factor / *iter_in - 1) < 1E-4, "convert_range equal types"); } } } END_NAMESPACE_STIR - - USING_NAMESPACE_STIR - - -int main() -{ +int +main() { convert_array_Tests tests; tests.run_tests(); return tests.main_return_value(); diff --git a/src/test/test_coordinates.cxx b/src/test/test_coordinates.cxx index 341279989a..672e1cf4bb 100644 --- a/src/test/test_coordinates.cxx +++ b/src/test/test_coordinates.cxx @@ -19,9 +19,9 @@ */ /*! - \file + \file \ingroup test - + \brief A simple program to test the Coordinate classes \author Kris Thielemans @@ -44,68 +44,71 @@ using std::cerr; using std::endl; #endif - START_NAMESPACE_STIR /*! \brief Class with tests for BasicCoordinate, Coordinate3D et al. \ingroup test */ -class coordinateTests : public RunTests -{ +class coordinateTests : public RunTests { public: void run_tests(); }; - void -coordinateTests::run_tests() -{ +coordinateTests::run_tests() { cerr << "Testing Coordinate classes" << endl - <<" (There should be only informative messages here starting with 'Testing')" << endl; + << " (There should be only informative messages here starting with 'Testing')" << endl; { cerr << "Testing BasicCoordinate<3,float>" << endl; BasicCoordinate<3, float> a; - a[1]=1;a[2]=2;a[3]=3; + a[1] = 1; + a[2] = 2; + a[3] = 3; BasicCoordinate<3, float> copy_of_a; - copy_of_a[1]=1;copy_of_a[2]=2;copy_of_a[3]=3; + copy_of_a[1] = 1; + copy_of_a[2] = 2; + copy_of_a[3] = 3; BasicCoordinate<3, float> b; - b[1]=-1;b[2]=-3;b[3]=5; + b[1] = -1; + b[2] = -3; + b[3] = 5; BasicCoordinate<3, float> a_plus_b; - a_plus_b[1]=0;a_plus_b[2]=-1;a_plus_b[3]=8; + a_plus_b[1] = 0; + a_plus_b[2] = -1; + a_plus_b[3] = 8; - - check(a[3]==3, "testing operator[]"); - check_if_equal(inner_product(a,b), 8.F, "testing inner_product"); + check(a[3] == 3, "testing operator[]"); + check_if_equal(inner_product(a, b), 8.F, "testing inner_product"); check_if_equal(norm(a), 3.74166, "testing norm"); a += b; - check_if_zero(a- a_plus_b, "testing operator+=(BasicCoordinate)"); + check_if_zero(a - a_plus_b, "testing operator+=(BasicCoordinate)"); a -= b; check_if_equal(a, copy_of_a, "testing operator-=(BasicCoordinate)"); { BasicCoordinate<3, float> b2(3.F); - check_if_zero(norm(b2-3.F), "testing constructor with single element, and operator-"); + check_if_zero(norm(b2 - 3.F), "testing constructor with single element, and operator-"); b2.fill(4.F); - check_if_zero(norm(b2-4.F), "testing fill, and operator-"); - } + check_if_zero(norm(b2 - 4.F), "testing fill, and operator-"); + } { BasicCoordinate<3, float> b1 = b; - check_if_zero(norm(b1-b), "testing copy constructor, and operator-"); - + check_if_zero(norm(b1 - b), "testing copy constructor, and operator-"); + b1 = a; - check_if_zero(norm(a-b1), "testing assignment"); + check_if_zero(norm(a - b1), "testing assignment"); } a *= 4; - check(a[1] == copy_of_a[1]*4, "testing operator*=(float)"); - check_if_equal(norm(a), norm(copy_of_a)*4, "testing operator*=(float)"); + check(a[1] == copy_of_a[1] * 4, "testing operator*=(float)"); + check_if_equal(norm(a), norm(copy_of_a) * 4, "testing operator*=(float)"); a /= 4; - check_if_zero(norm(a-copy_of_a), "testing operator/=(float)"); + check_if_zero(norm(a - copy_of_a), "testing operator/=(float)"); { BasicCoordinate<3, float> a1; @@ -119,54 +122,60 @@ coordinateTests::run_tests() a1 *= 3; a1 += a; a1 -= 4; - BasicCoordinate<3, float> a2 = (b*3+a)-4; - check_if_zero(norm(a1-a2), "testing various numerical operators"); + BasicCoordinate<3, float> a2 = (b * 3 + a) - 4; + check_if_zero(norm(a1 - a2), "testing various numerical operators"); } // basic iterator tests - { + { #ifndef STIR_NO_NAMESPACES - float *p=std::find(b.begin(), b.end(), -3); + float* p = std::find(b.begin(), b.end(), -3); #else - float *p=find(b.begin(), b.end(), -3); + float* p = find(b.begin(), b.end(), -3); #endif check_if_zero(p - b.begin() - 1, "iterator test"); BasicCoordinate<3, float> b_sorted; - b_sorted[1]=-3;b_sorted[2]=-1;b_sorted[3]=5; + b_sorted[1] = -3; + b_sorted[2] = -1; + b_sorted[3] = 5; #ifndef STIR_NO_NAMESPACES - std::sort(b.begin(), b.end()); + std::sort(b.begin(), b.end()); #else - sort(b.begin(), b.end()); + sort(b.begin(), b.end()); #endif - check_if_zero(norm(b-b_sorted), "testing iterators via STL sort"); + check_if_zero(norm(b - b_sorted), "testing iterators via STL sort"); } } { cerr << "Testing join/cut_first_dimension/comparisons on BasicCoordinate" << endl; - + // join { BasicCoordinate<3, int> a; - a[1]=1;a[2]=2;a[3]=3; + a[1] = 1; + a[2] = 2; + a[3] = 3; { - BasicCoordinate<4, int> a4 = join(0, a); - check_if_equal(a4[1], 0, "testing join of float with BasicCoordinate"); - check_if_equal(a4[2], 1, "testing join of float with BasicCoordinate"); - check_if_equal(a4[3], 2, "testing join of float with BasicCoordinate"); - check_if_equal(a4[4], 3, "testing join of float with BasicCoordinate"); + BasicCoordinate<4, int> a4 = join(0, a); + check_if_equal(a4[1], 0, "testing join of float with BasicCoordinate"); + check_if_equal(a4[2], 1, "testing join of float with BasicCoordinate"); + check_if_equal(a4[3], 2, "testing join of float with BasicCoordinate"); + check_if_equal(a4[4], 3, "testing join of float with BasicCoordinate"); } { - BasicCoordinate<4, int> a4 = join(a, 0); - check_if_equal(a4[1], 1, "testing join of BasicCoordinate with float"); - check_if_equal(a4[2], 2, "testing join of BasicCoordinate with float"); - check_if_equal(a4[3], 3, "testing join of BasicCoordinate with float"); - check_if_equal(a4[4], 0, "testing join of BasicCoordinate with float"); + BasicCoordinate<4, int> a4 = join(a, 0); + check_if_equal(a4[1], 1, "testing join of BasicCoordinate with float"); + check_if_equal(a4[2], 2, "testing join of BasicCoordinate with float"); + check_if_equal(a4[3], 3, "testing join of BasicCoordinate with float"); + check_if_equal(a4[4], 0, "testing join of BasicCoordinate with float"); } } // cut*dimension { BasicCoordinate<3, int> a; - a[1]=1;a[2]=2;a[3]=3; + a[1] = 1; + a[2] = 2; + a[3] = 3; const BasicCoordinate<2, int> start = cut_last_dimension(a); check_if_equal(start[1], 1, "testing cut_last_dimension"); check_if_equal(start[2], 2, "testing cut_last_dimension"); @@ -177,47 +186,59 @@ coordinateTests::run_tests() // comparison 2D { BasicCoordinate<2, int> a; - a[1]=1;a[2]=2; + a[1] = 1; + a[2] = 2; BasicCoordinate<2, int> b; - b[1]=1;b[2]=1; - check(a==a, "2D operator=="); - check(a<=a, "2D operator<= (when equal)"); - check(a>=a, "2D operator>= (when equal)"); - check(a>b, "2D operator>"); - check(b=b, "2D operator>= (when not equal)"); - check(b<=a, "2D operator<= (when not equal)"); - check(a!=b, "2D operator!="); + b[1] = 1; + b[2] = 1; + check(a == a, "2D operator=="); + check(a <= a, "2D operator<= (when equal)"); + check(a >= a, "2D operator>= (when equal)"); + check(a > b, "2D operator>"); + check(b < a, "2D operator<"); + check(a >= b, "2D operator>= (when not equal)"); + check(b <= a, "2D operator<= (when not equal)"); + check(a != b, "2D operator!="); } // comparison 3D { BasicCoordinate<3, int> a; - a[1]=1;a[2]=2;a[3]=3; + a[1] = 1; + a[2] = 2; + a[3] = 3; BasicCoordinate<3, int> b; - b[1]=1;b[2]=1;b[3]=3; - check(a==a, "3D operator=="); - check(a<=a, "3D operator<= (when equal)"); - check(a>=a, "3D operator>= (when equal)"); - check(a>b, "3D operator>"); - check(b=b, "3D operator>= (when not equal)"); - check(b<=a, "3D operator<= (when not equal)"); - check(a!=b, "3D operator!="); + b[1] = 1; + b[2] = 1; + b[3] = 3; + check(a == a, "3D operator=="); + check(a <= a, "3D operator<= (when equal)"); + check(a >= a, "3D operator>= (when equal)"); + check(a > b, "3D operator>"); + check(b < a, "3D operator<"); + check(a >= b, "3D operator>= (when not equal)"); + check(b <= a, "3D operator<= (when not equal)"); + check(a != b, "3D operator!="); } // comparison 4D { BasicCoordinate<4, int> a; - a[1]=1;a[2]=2;a[3]=3; a[4]=1; + a[1] = 1; + a[2] = 2; + a[3] = 3; + a[4] = 1; BasicCoordinate<4, int> b; - b[1]=1;b[2]=1;b[3]=3; b[4]=2; - check(a==a, "4D operator=="); - check(a<=a, "4D operator<= (when equal)"); - check(a>=a, "4D operator>= (when equal)"); - check(a>b, "4D operator>"); - check(b=b, "4D operator>= (when not equal)"); - check(b<=a, "4D operator<= (when not equal)"); - check(a!=b, "4D operator!="); + b[1] = 1; + b[2] = 1; + b[3] = 3; + b[4] = 2; + check(a == a, "4D operator=="); + check(a <= a, "4D operator<= (when equal)"); + check(a >= a, "4D operator>= (when equal)"); + check(a > b, "4D operator>"); + check(b < a, "4D operator<"); + check(a >= b, "4D operator>= (when not equal)"); + check(b <= a, "4D operator<= (when not equal)"); + check(a != b, "4D operator!="); } } @@ -227,36 +248,42 @@ coordinateTests::run_tests() cerr << "Testing Coordinate3D" << endl; Coordinate3D a; - a[1]=1;a[2]=2;a[3]=3; + a[1] = 1; + a[2] = 2; + a[3] = 3; // use new constructor - Coordinate3D copy_of_a(1,2,3); + Coordinate3D copy_of_a(1, 2, 3); Coordinate3D b; - b[1]=-1;b[2]=-3;b[3]=5; + b[1] = -1; + b[2] = -3; + b[3] = 5; Coordinate3D a_plus_b; - a_plus_b[1]=0;a_plus_b[2]=-1;a_plus_b[3]=8; - - check(a[3]==3, "testing operator[]"); - check_if_equal(inner_product(a,b), 8.F, "testing inner_product"); + a_plus_b[1] = 0; + a_plus_b[2] = -1; + a_plus_b[3] = 8; + + check(a[3] == 3, "testing operator[]"); + check_if_equal(inner_product(a, b), 8.F, "testing inner_product"); check_if_equal(norm(a), 3.74166, "testing norm"); a += b; check_if_zero(norm(a - a_plus_b), "testing operator+=(BasicCoordinate)"); a -= b; check_if_zero(norm(a - copy_of_a), "testing operator-=(BasicCoordinate)"); - + { Coordinate3D b1 = b; - check_if_zero(norm(b1-b), "testing copy constructor, and operator-"); - + check_if_zero(norm(b1 - b), "testing copy constructor, and operator-"); + b1 = a; - check_if_zero(norm(a-b1), "testing assignment"); + check_if_zero(norm(a - b1), "testing assignment"); } a *= 4; - check_if_zero(norm(a)- norm(copy_of_a)*4, "testing operator*=(float)"); - check_if_zero(a[1]- copy_of_a[1]*4, "testing operator*=(float)"); + check_if_zero(norm(a) - norm(copy_of_a) * 4, "testing operator*=(float)"); + check_if_zero(a[1] - copy_of_a[1] * 4, "testing operator*=(float)"); a /= 4; - check_if_zero(norm(a-copy_of_a), "testing operator/=(float)"); + check_if_zero(norm(a - copy_of_a), "testing operator/=(float)"); { Coordinate3D a1; @@ -264,17 +291,16 @@ coordinateTests::run_tests() a1 *= 3; a1 += a; a1 -= 4; - Coordinate3D a2 = (b*3+a)-4; - check_if_zero(norm(a1-a2), "testing various numerical operators"); + Coordinate3D a2 = (b * 3 + a) - 4; + check_if_zero(norm(a1 - a2), "testing various numerical operators"); } { - BasicCoordinate<3,float> gen_a(a); + BasicCoordinate<3, float> gen_a(a); a = gen_a; - check_if_zero(norm(a-copy_of_a), "testing conversions"); - check_if_zero(norm(gen_a-copy_of_a), "testing conversions"); + check_if_zero(norm(a - copy_of_a), "testing conversions"); + check_if_zero(norm(gen_a - copy_of_a), "testing conversions"); } - } // essentially the same as above, but now with CartesianCoordinate3D @@ -282,36 +308,42 @@ coordinateTests::run_tests() cerr << "Testing CartesianCoordinate3D" << endl; CartesianCoordinate3D a; - a[1]=1;a[2]=2;a[3]=3; + a[1] = 1; + a[2] = 2; + a[3] = 3; // use new constructor - CartesianCoordinate3D copy_of_a(1,2,3); + CartesianCoordinate3D copy_of_a(1, 2, 3); CartesianCoordinate3D b; - b[1]=-1;b[2]=-3;b[3]=5; + b[1] = -1; + b[2] = -3; + b[3] = 5; CartesianCoordinate3D a_plus_b; - a_plus_b[1]=0;a_plus_b[2]=-1;a_plus_b[3]=8; - - check(a[3]==3, "testing operator[]"); - check_if_equal(inner_product(a,b), 8.F, "testing inner_product"); + a_plus_b[1] = 0; + a_plus_b[2] = -1; + a_plus_b[3] = 8; + + check(a[3] == 3, "testing operator[]"); + check_if_equal(inner_product(a, b), 8.F, "testing inner_product"); check_if_equal(norm(a), 3.74166, "testing norm"); a += b; check_if_zero(norm(a - a_plus_b), "testing operator+=(BasicCoordinate)"); a -= b; check_if_zero(norm(a - copy_of_a), "testing operator-=(BasicCoordinate)"); - + { CartesianCoordinate3D b1 = b; - check_if_zero(norm(b1-b), "testing copy constructor, and operator-"); - + check_if_zero(norm(b1 - b), "testing copy constructor, and operator-"); + b1 = a; - check_if_zero(norm(a-b1), "testing assignment"); + check_if_zero(norm(a - b1), "testing assignment"); } a *= 4; - check_if_zero(norm(a)- norm(copy_of_a)*4, "testing operator*=(float)"); - check_if_zero(a[1]- copy_of_a[1]*4, "testing operator*=(float)"); + check_if_zero(norm(a) - norm(copy_of_a) * 4, "testing operator*=(float)"); + check_if_zero(a[1] - copy_of_a[1] * 4, "testing operator*=(float)"); a /= 4; - check_if_zero(norm(a-copy_of_a), "testing operator/=(float)"); + check_if_zero(norm(a - copy_of_a), "testing operator/=(float)"); { CartesianCoordinate3D a1; @@ -319,24 +351,23 @@ coordinateTests::run_tests() a1 *= 3; a1 += a; a1 -= 4; - CartesianCoordinate3D a2 = (b*3+a)-4; - check_if_zero(norm(a1-a2), "testing various numerical operators"); + CartesianCoordinate3D a2 = (b * 3 + a) - 4; + check_if_zero(norm(a1 - a2), "testing various numerical operators"); } { - BasicCoordinate<3,float> gen_a(a); + BasicCoordinate<3, float> gen_a(a); a = gen_a; - check_if_zero(norm(a-copy_of_a), "testing conversions"); - check_if_zero(norm(gen_a-copy_of_a), "testing conversions"); + check_if_zero(norm(a - copy_of_a), "testing conversions"); + check_if_zero(norm(gen_a - copy_of_a), "testing conversions"); } - } { cerr << "Testing round with coordinates" << endl; - const Coordinate3D af(1.1F,-1.1F,3.6F); + const Coordinate3D af(1.1F, -1.1F, 3.6F); const Coordinate3D aint = round(af); - check_if_equal(aint, Coordinate3D (1,-1,4)); + check_if_equal(aint, Coordinate3D(1, -1, 4)); } { cerr << "Testing constructor with different types of coordinates" << endl; @@ -344,40 +375,31 @@ coordinateTests::run_tests() would not work if 'af' is defined as const Coordinate3D af(1.1F,-1.1F,3.6F); */ - const BasicCoordinate<3,float> af = Coordinate3D (1.1F,-1.1F,3.6F); - const BasicCoordinate<3,int> aint(af); - check_if_equal(aint, Coordinate3D (1,-1,3)); - const BasicCoordinate<3,float> af2(aint); - check_if_equal(af2, Coordinate3D (1.F,-1.F,3.F)); + const BasicCoordinate<3, float> af = Coordinate3D(1.1F, -1.1F, 3.6F); + const BasicCoordinate<3, int> aint(af); + check_if_equal(aint, Coordinate3D(1, -1, 3)); + const BasicCoordinate<3, float> af2(aint); + check_if_equal(af2, Coordinate3D(1.F, -1.F, 3.F)); } { cerr << "Testing make_coordinate" << endl; - check_if_equal(make_coordinate(1.F)[1],1.F, "test make_coordinate with 1 arg"); - check_if_equal(make_coordinate(1.F,3.4F),Coordinate2D(1.F,3.4F), - "test make_coordinate with 2 args"); - check_if_equal(make_coordinate(1.,3.4,-4.8),Coordinate3D(1.,3.4,-4.8), - "test make_coordinate with 3 args"); - check_if_equal(make_coordinate(1,2,3,4),Coordinate4D(1,2,3,4), - "test make_coordinate with 4 args"); - check_if_equal(make_coordinate(1,2,3,4,5),join(Coordinate4D(1,2,3,4),5), - "test make_coordinate with 5 args"); - check_if_equal(make_coordinate(1,2,3,4,5,6),join(join(Coordinate4D(1,2,3,4),5),6), - "test make_coordinate with 6 args"); + check_if_equal(make_coordinate(1.F)[1], 1.F, "test make_coordinate with 1 arg"); + check_if_equal(make_coordinate(1.F, 3.4F), Coordinate2D(1.F, 3.4F), "test make_coordinate with 2 args"); + check_if_equal(make_coordinate(1., 3.4, -4.8), Coordinate3D(1., 3.4, -4.8), "test make_coordinate with 3 args"); + check_if_equal(make_coordinate(1, 2, 3, 4), Coordinate4D(1, 2, 3, 4), "test make_coordinate with 4 args"); + check_if_equal(make_coordinate(1, 2, 3, 4, 5), join(Coordinate4D(1, 2, 3, 4), 5), "test make_coordinate with 5 args"); + check_if_equal(make_coordinate(1, 2, 3, 4, 5, 6), join(join(Coordinate4D(1, 2, 3, 4), 5), 6), + "test make_coordinate with 6 args"); } } - END_NAMESPACE_STIR - - USING_NAMESPACE_STIR - - -int main() -{ +int +main() { coordinateTests tests; tests.run_tests(); return tests.main_return_value(); diff --git a/src/test/test_display.cxx b/src/test/test_display.cxx index 9de7a3968c..71887a85a6 100644 --- a/src/test/test_display.cxx +++ b/src/test/test_display.cxx @@ -35,75 +35,68 @@ USING_NAMESPACE_STIR int -main() -{ +main() { std::cerr << "Tests display with a few very simple bitmaps.\n" - << "You should see 10 bitmaps (4th and 5th brighter) twice, and then a single bitmap\n"; + << "You should see 10 bitmaps (4th and 5th brighter) twice, and then a single bitmap\n"; typedef float test_type; - // provide a test example. This could easily be changed in reading + // provide a test example. This could easily be changed in reading // something from file - // note: sizes are prime numbers to avoid having accidental matches - // with 'word-boundaries' etc. This is especailly an issue + // note: sizes are prime numbers to avoid having accidental matches + // with 'word-boundaries' etc. This is especailly an issue // when using X windows. - Array<3,test_type> t(IndexRange3D(10,87,123)); + Array<3, test_type> t(IndexRange3D(10, 87, 123)); VectorWithOffset scale_factors(10); scale_factors.fill(1.F); // make images 3 and 4 stand out scale_factors[3] = 1.3F; scale_factors[4] = 1.5F; - for (int i=0; i text(t.get_min_index(), t.get_max_index()); - for (int i=t.get_min_index(); i<= t.get_max_index(); i++) - { - text[i] = new char [15]; - sprintf(text[i], "image %d", i); - } + VectorWithOffset text(t.get_min_index(), t.get_max_index()); + for (int i = t.get_min_index(); i <= t.get_max_index(); i++) { + text[i] = new char[15]; + sprintf(text[i], "image %d", i); + } display(t, scale_factors, text, maxi, "Test display 3D all args", scale); - display(t,t.find_max()/2,"Test display 3D 3 args, half the colour scale" ); + display(t, t.find_max() / 2, "Test display 3D 3 args, half the colour scale"); - for (int i=t.get_min_index(); i<= t.get_max_index(); i++) + for (int i = t.get_min_index(); i <= t.get_max_index(); i++) delete[] text[i]; - display(*t.begin(), "Test display 2D, 2 args"); return EXIT_SUCCESS; diff --git a/src/test/test_export_array.cxx b/src/test/test_export_array.cxx index 6732a8fa3e..dd74d6be94 100644 --- a/src/test/test_export_array.cxx +++ b/src/test/test_export_array.cxx @@ -45,63 +45,45 @@ START_NAMESPACE_STIR -class ExportArrayTests : public RunTests -{ +class ExportArrayTests : public RunTests { public: - void run_tests(); + void run_tests(); protected: - void test_static_data(); - void test_dynamic_data(); - void run_static_test(ProjData& test_proj_data, - ProjData& check_proj_data, - const std::string& test_name); - void check_if_equal_projdata(const ProjData& test_proj_data, - const ProjData& check_proj_data, - const std::string& test_name); + void test_static_data(); + void test_dynamic_data(); + void run_static_test(ProjData& test_proj_data, ProjData& check_proj_data, const std::string& test_name); + void check_if_equal_projdata(const ProjData& test_proj_data, const ProjData& check_proj_data, const std::string& test_name); }; -void ExportArrayTests :: run_tests() -{ - test_static_data(); - test_dynamic_data(); +void +ExportArrayTests ::run_tests() { + test_static_data(); + test_dynamic_data(); } -void ExportArrayTests :: check_if_equal_projdata(const ProjData& test_proj_data, - const ProjData& check_proj_data, - const std::string& test_name) -{ - for (int segment_num = test_proj_data.get_min_segment_num(); - segment_num <= test_proj_data.get_max_segment_num(); - ++segment_num) - { - const SegmentByView test_segment_by_view_data = - test_proj_data.get_segment_by_view(segment_num); - - const SegmentByView check_segment_by_view_data = - check_proj_data.get_segment_by_view(segment_num); - - for (int view_num = test_segment_by_view_data.get_min_view_num(); - view_num<=test_segment_by_view_data.get_max_view_num(); - ++view_num) - { - Viewgram test_view = test_segment_by_view_data.get_viewgram(view_num); - Viewgram check_view = check_segment_by_view_data.get_viewgram(view_num); - - for (int axial = test_view.get_min_axial_pos_num(); - axial <= test_view.get_max_axial_pos_num(); - ++axial) - { - for (int s = test_view.get_min_tangential_pos_num(); - s <= test_view.get_max_tangential_pos_num(); - ++s) - { - check_if_equal(test_view[axial][s], check_view[axial][s], test_name + ": test ProjData different from check ProjData."); - check_if_equal(check_view[axial][s], (float)segment_num, test_name + ": check ProjData different from segment number."); - } - } +void +ExportArrayTests ::check_if_equal_projdata(const ProjData& test_proj_data, const ProjData& check_proj_data, + const std::string& test_name) { + for (int segment_num = test_proj_data.get_min_segment_num(); segment_num <= test_proj_data.get_max_segment_num(); + ++segment_num) { + const SegmentByView test_segment_by_view_data = test_proj_data.get_segment_by_view(segment_num); + + const SegmentByView check_segment_by_view_data = check_proj_data.get_segment_by_view(segment_num); + + for (int view_num = test_segment_by_view_data.get_min_view_num(); view_num <= test_segment_by_view_data.get_max_view_num(); + ++view_num) { + Viewgram test_view = test_segment_by_view_data.get_viewgram(view_num); + Viewgram check_view = check_segment_by_view_data.get_viewgram(view_num); + + for (int axial = test_view.get_min_axial_pos_num(); axial <= test_view.get_max_axial_pos_num(); ++axial) { + for (int s = test_view.get_min_tangential_pos_num(); s <= test_view.get_max_tangential_pos_num(); ++s) { + check_if_equal(test_view[axial][s], check_view[axial][s], test_name + ": test ProjData different from check ProjData."); + check_if_equal(check_view[axial][s], (float)segment_num, test_name + ": check ProjData different from segment number."); } + } } + } } //! @@ -111,201 +93,167 @@ void ExportArrayTests :: check_if_equal_projdata(const ProjData& test_proj_data, //! written and retrieved from the disk, resorted in DynamicProjData and //! compared with the original. The test will fails if the array retrieved //! from the disk is of different size from the original. -void ExportArrayTests::test_dynamic_data() -{ - info("Initialising..."); - //. Create ProjData - - //- ProjDataInfo - //-- Scanner - shared_ptr test_scanner_sptr( new Scanner(Scanner::Siemens_mMR)); - - //-- ExamInfo - shared_ptr test_exam_info_sptr(new ExamInfo()); - // TODO, Currently all stir::Scanner types are PET. - test_exam_info_sptr->imaging_modality = ImagingModality::PT; - - info("Creating test DynamicProjData..."); - shared_ptr test_dynamic_projData_sptr (new DynamicProjData(test_exam_info_sptr)); - - shared_ptr tmp_proj_data_info_sptr( - ProjDataInfo::ProjDataInfoCTI(test_scanner_sptr, - 1, - 1, /* Reduce the number of segments */ - test_scanner_sptr->get_max_num_views(), - test_scanner_sptr->get_max_num_non_arccorrected_bins(), - false)); - - const int num_of_gates = 3; - info(boost::format("Resizing the DynamicProjData for %1% gates... ") % num_of_gates); - test_dynamic_projData_sptr->resize( num_of_gates); - - for (int i_gate = 1; i_gate <= num_of_gates; i_gate++) - { - info(boost::format("Allocating and filling the %1% gate... ") % i_gate); +void +ExportArrayTests::test_dynamic_data() { + info("Initialising..."); + //. Create ProjData - shared_ptr test_proj_data_gate_ptr( - new ProjDataInMemory(test_exam_info_sptr, - tmp_proj_data_info_sptr)); + //- ProjDataInfo + //-- Scanner + shared_ptr test_scanner_sptr(new Scanner(Scanner::Siemens_mMR)); - for (int segment_num = test_proj_data_gate_ptr->get_min_segment_num(); - segment_num <= test_proj_data_gate_ptr->get_max_segment_num(); - ++segment_num) - { - SegmentByView segment_by_view_data = - test_proj_data_gate_ptr->get_segment_by_view(segment_num); + //-- ExamInfo + shared_ptr test_exam_info_sptr(new ExamInfo()); + // TODO, Currently all stir::Scanner types are PET. + test_exam_info_sptr->imaging_modality = ImagingModality::PT; - // 1000 is an arbitary number to distiguish data in different gates. - segment_by_view_data.fill(static_cast(segment_num + (i_gate * 1000))); + info("Creating test DynamicProjData..."); + shared_ptr test_dynamic_projData_sptr(new DynamicProjData(test_exam_info_sptr)); - if (!(test_proj_data_gate_ptr->set_segment(segment_by_view_data) == Succeeded::yes)) - warning("Error set_segment %d\n", segment_num); - } + shared_ptr tmp_proj_data_info_sptr(ProjDataInfo::ProjDataInfoCTI( + test_scanner_sptr, 1, 1, /* Reduce the number of segments */ + test_scanner_sptr->get_max_num_views(), test_scanner_sptr->get_max_num_non_arccorrected_bins(), false)); - info("Populating the Dynamic ProjData... "); - test_dynamic_projData_sptr->set_proj_data_sptr(test_proj_data_gate_ptr, i_gate); - } + const int num_of_gates = 3; + info(boost::format("Resizing the DynamicProjData for %1% gates... ") % num_of_gates); + test_dynamic_projData_sptr->resize(num_of_gates); - const std::size_t total_size = test_dynamic_projData_sptr->size_all(); - const int total_gates = static_cast(test_dynamic_projData_sptr->get_num_proj_data()); - const int projdata_size = static_cast(test_dynamic_projData_sptr->get_proj_data_size()); + for (int i_gate = 1; i_gate <= num_of_gates; i_gate++) { + info(boost::format("Allocating and filling the %1% gate... ") % i_gate); - info(boost::format("Total size: %1%, number of gates: %2%, size of projdata %3%") % total_size % total_gates % - projdata_size); - // Allocate 2D array to store the data. - info("Allocating test array..."); - Array<2, float> test_array (IndexRange2D(0, total_gates, 0, projdata_size)); - test_array.fill(-1); - Array<2, float>::full_iterator test_array_iter = test_array.begin_all(); + shared_ptr test_proj_data_gate_ptr(new ProjDataInMemory(test_exam_info_sptr, tmp_proj_data_info_sptr)); - // Copy data to array. - info("Copying test dynamic projdata to array ..."); - copy_to(*test_dynamic_projData_sptr, test_array_iter); + for (int segment_num = test_proj_data_gate_ptr->get_min_segment_num(); + segment_num <= test_proj_data_gate_ptr->get_max_segment_num(); ++segment_num) { + SegmentByView segment_by_view_data = test_proj_data_gate_ptr->get_segment_by_view(segment_num); - // Convert it to ProjData - info("Copying data from array to check dynamic projdata ..."); + // 1000 is an arbitary number to distiguish data in different gates. + segment_by_view_data.fill(static_cast(segment_num + (i_gate * 1000))); - shared_ptr check_dynamic_projData_sptr (new DynamicProjData(test_exam_info_sptr, - num_of_gates)); + if (!(test_proj_data_gate_ptr->set_segment(segment_by_view_data) == Succeeded::yes)) + warning("Error set_segment %d\n", segment_num); + } - for (int i_gate = 0; i_gate < num_of_gates; i_gate++) - { - info(boost::format("Allocating and filling the %1% gate... ") % (i_gate+1)); + info("Populating the Dynamic ProjData... "); + test_dynamic_projData_sptr->set_proj_data_sptr(test_proj_data_gate_ptr, i_gate); + } - shared_ptr test_proj_data_gate_ptr( - new ProjDataInMemory(test_exam_info_sptr, - tmp_proj_data_info_sptr)); + const std::size_t total_size = test_dynamic_projData_sptr->size_all(); + const int total_gates = static_cast(test_dynamic_projData_sptr->get_num_proj_data()); + const int projdata_size = static_cast(test_dynamic_projData_sptr->get_proj_data_size()); - info("Populating the Dynamic ProjData... "); - check_dynamic_projData_sptr->set_proj_data_sptr(test_proj_data_gate_ptr, (i_gate+1)); - } + info(boost::format("Total size: %1%, number of gates: %2%, size of projdata %3%") % total_size % total_gates % projdata_size); + // Allocate 2D array to store the data. + info("Allocating test array..."); + Array<2, float> test_array(IndexRange2D(0, total_gates, 0, projdata_size)); + test_array.fill(-1); + Array<2, float>::full_iterator test_array_iter = test_array.begin_all(); - fill_from(*check_dynamic_projData_sptr, test_array.begin_all_const(), test_array.end_all_const()); + // Copy data to array. + info("Copying test dynamic projdata to array ..."); + copy_to(*test_dynamic_projData_sptr, test_array_iter); - info ("Checking if data are the same..."); - for(int i_gate = 1; i_gate <= num_of_gates; i_gate++) - { - shared_ptr _test_projdata_sptr (test_dynamic_projData_sptr->get_proj_data_sptr(i_gate)); - shared_ptr _check_projdata_sptr(check_dynamic_projData_sptr->get_proj_data_sptr(i_gate)); - - for (int segment_num = _test_projdata_sptr->get_min_segment_num(); - segment_num <= _test_projdata_sptr->get_max_segment_num(); - ++segment_num) - { - SegmentByView _test_segment_by_view_data = - _test_projdata_sptr->get_segment_by_view(segment_num); - - SegmentByView _check_segment_by_view_data = - _check_projdata_sptr->get_segment_by_view(segment_num); - - for (int view_num = _test_segment_by_view_data.get_min_view_num(); - view_num <= _test_segment_by_view_data.get_max_view_num(); - ++view_num) - { - Viewgram _test_view = _test_segment_by_view_data.get_viewgram(view_num); - Viewgram _check_view = _check_segment_by_view_data.get_viewgram(view_num); - - for (int axial = _test_view.get_min_axial_pos_num(); - axial <= _test_view.get_max_axial_pos_num(); - ++axial) - { - for (int s = _test_view.get_min_tangential_pos_num(); - s <= _test_view.get_max_tangential_pos_num(); - ++s) - { - check_if_equal(_test_view[axial][s], _check_view[axial][s], "Test ProjData different from check ProjData."); - check_if_equal(_check_view[axial][s], (float) (segment_num + (i_gate * 1000)), "Check ProjData different from segment number."); - } - } - } - } - } + // Convert it to ProjData + info("Copying data from array to check dynamic projdata ..."); -} + shared_ptr check_dynamic_projData_sptr(new DynamicProjData(test_exam_info_sptr, num_of_gates)); -void ExportArrayTests :: run_static_test(ProjData& test_proj_data, - ProjData& check_proj_data, - const std::string& test_name) -{ - std::cerr << "Running " << test_name << '\n'; + for (int i_gate = 0; i_gate < num_of_gates; i_gate++) { + info(boost::format("Allocating and filling the %1% gate... ") % (i_gate + 1)); - //. Fill ProjData with the number of the segment - info("Filling test ProjData with the segment number ... "); + shared_ptr test_proj_data_gate_ptr(new ProjDataInMemory(test_exam_info_sptr, tmp_proj_data_info_sptr)); - for (int segment_num = test_proj_data.get_min_segment_num(); - segment_num <= test_proj_data.get_max_segment_num(); - ++segment_num) - { - info(boost::format("Segment: %1% ") % segment_num); - SegmentByView segment_by_view_data = - test_proj_data.get_empty_segment_by_view(segment_num); + info("Populating the Dynamic ProjData... "); + check_dynamic_projData_sptr->set_proj_data_sptr(test_proj_data_gate_ptr, (i_gate + 1)); + } - segment_by_view_data.fill(static_cast(segment_num)); + fill_from(*check_dynamic_projData_sptr, test_array.begin_all_const(), test_array.end_all_const()); - if (!(test_proj_data.set_segment(segment_by_view_data) == Succeeded::yes)) - error("Error set_segment %d\n", segment_num); - } + info("Checking if data are the same..."); + for (int i_gate = 1; i_gate <= num_of_gates; i_gate++) { + shared_ptr _test_projdata_sptr(test_dynamic_projData_sptr->get_proj_data_sptr(i_gate)); + shared_ptr _check_projdata_sptr(check_dynamic_projData_sptr->get_proj_data_sptr(i_gate)); - //- Get the total size of the ProjData + for (int segment_num = _test_projdata_sptr->get_min_segment_num(); segment_num <= _test_projdata_sptr->get_max_segment_num(); + ++segment_num) { + SegmentByView _test_segment_by_view_data = _test_projdata_sptr->get_segment_by_view(segment_num); - const unsigned int total_size = test_proj_data.size_all(); + SegmentByView _check_segment_by_view_data = _check_projdata_sptr->get_segment_by_view(segment_num); - //- Allocate 1D array and get iterator + for (int view_num = _test_segment_by_view_data.get_min_view_num(); + view_num <= _test_segment_by_view_data.get_max_view_num(); ++view_num) { + Viewgram _test_view = _test_segment_by_view_data.get_viewgram(view_num); + Viewgram _check_view = _check_segment_by_view_data.get_viewgram(view_num); - info("Allocating array ..."); - Array<3,float> test_array(IndexRange3D(test_proj_data.get_num_sinograms(), test_proj_data.get_num_views(), test_proj_data.get_num_tangential_poss())); - check (test_array.size_all() == total_size, "check on size of array"); - Array<3,float>::full_iterator test_array_iter = test_array.begin_all(); - - //- - info("Copying from ProjData to array ..."); - copy_to(test_proj_data, test_array_iter); + for (int axial = _test_view.get_min_axial_pos_num(); axial <= _test_view.get_max_axial_pos_num(); ++axial) { + for (int s = _test_view.get_min_tangential_pos_num(); s <= _test_view.get_max_tangential_pos_num(); ++s) { + check_if_equal(_test_view[axial][s], _check_view[axial][s], "Test ProjData different from check ProjData."); + check_if_equal(_check_view[axial][s], (float)(segment_num + (i_gate * 1000)), + "Check ProjData different from segment number."); + } + } + } + } + } +} - //- Check if segment order is as expected - { - const std::vector segment_sequence = ProjData::standard_segment_sequence(*test_proj_data.get_proj_data_info_sptr()); - test_array_iter = test_array.begin_all(); - for (std::vector::const_iterator iter = segment_sequence.begin(); iter != segment_sequence.end(); ++iter) - { - const int seg = *iter; - const SegmentBySinogram segment = test_proj_data.get_segment_by_sinogram(seg); - for (SegmentBySinogram::const_full_iterator seg_iter = segment.begin_all(); seg_iter != segment.end_all(); ++seg_iter, ++test_array_iter) - { - if (!check_if_equal(*seg_iter, *test_array_iter, "check if array in correct order")) - { - // one failed, so many others will as well. we just stop checking. - // make sure we get out of the outer loop as well - iter = segment_sequence.end() - 1; - break; - } - } +void +ExportArrayTests ::run_static_test(ProjData& test_proj_data, ProjData& check_proj_data, const std::string& test_name) { + std::cerr << "Running " << test_name << '\n'; + + //. Fill ProjData with the number of the segment + info("Filling test ProjData with the segment number ... "); + + for (int segment_num = test_proj_data.get_min_segment_num(); segment_num <= test_proj_data.get_max_segment_num(); + ++segment_num) { + info(boost::format("Segment: %1% ") % segment_num); + SegmentByView segment_by_view_data = test_proj_data.get_empty_segment_by_view(segment_num); + + segment_by_view_data.fill(static_cast(segment_num)); + + if (!(test_proj_data.set_segment(segment_by_view_data) == Succeeded::yes)) + error("Error set_segment %d\n", segment_num); + } + + //- Get the total size of the ProjData + + const unsigned int total_size = test_proj_data.size_all(); + + //- Allocate 1D array and get iterator + + info("Allocating array ..."); + Array<3, float> test_array( + IndexRange3D(test_proj_data.get_num_sinograms(), test_proj_data.get_num_views(), test_proj_data.get_num_tangential_poss())); + check(test_array.size_all() == total_size, "check on size of array"); + Array<3, float>::full_iterator test_array_iter = test_array.begin_all(); + + //- + info("Copying from ProjData to array ..."); + copy_to(test_proj_data, test_array_iter); + + //- Check if segment order is as expected + { + const std::vector segment_sequence = ProjData::standard_segment_sequence(*test_proj_data.get_proj_data_info_sptr()); + test_array_iter = test_array.begin_all(); + for (std::vector::const_iterator iter = segment_sequence.begin(); iter != segment_sequence.end(); ++iter) { + const int seg = *iter; + const SegmentBySinogram segment = test_proj_data.get_segment_by_sinogram(seg); + for (SegmentBySinogram::const_full_iterator seg_iter = segment.begin_all(); seg_iter != segment.end_all(); + ++seg_iter, ++test_array_iter) { + if (!check_if_equal(*seg_iter, *test_array_iter, "check if array in correct order")) { + // one failed, so many others will as well. we just stop checking. + // make sure we get out of the outer loop as well + iter = segment_sequence.end() - 1; + break; } + } } + } - // Convert it back to ProjData - info("Copying from array to a new ProjData ..."); - fill_from(check_proj_data, test_array.begin_all_const(), test_array.end_all_const()); - this->check_if_equal_projdata(test_proj_data, check_proj_data, test_name); + // Convert it back to ProjData + info("Copying from array to a new ProjData ..."); + fill_from(check_proj_data, test_array.begin_all_const(), test_array.end_all_const()); + this->check_if_equal_projdata(test_proj_data, check_proj_data, test_name); } //! @@ -313,64 +261,58 @@ void ExportArrayTests :: run_static_test(ProjData& test_proj_data, //! \details This test will chech if projection data copied to arrays are the //! same when copied back to projdata. //! -void ExportArrayTests :: test_static_data() -{ - info("Initialising..."); - //. Create ProjData - - //- ProjDataInfo - //-- Scanner - shared_ptr test_scanner_sptr( new Scanner(Scanner::Siemens_mMR)); - - //-- ExamInfo - shared_ptr test_exam_info_sptr(new ExamInfo()); - // TODO, Currently all stir::Scanner types are PET. - test_exam_info_sptr->imaging_modality = ImagingModality::PT; - - //- - shared_ptr tmp_proj_data_info_sptr( - ProjDataInfo::ProjDataInfoCTI(test_scanner_sptr, - 1, - 1, - test_scanner_sptr->get_max_num_views(), - test_scanner_sptr->get_max_num_non_arccorrected_bins(), - false)); - +void +ExportArrayTests ::test_static_data() { + info("Initialising..."); + //. Create ProjData + + //- ProjDataInfo + //-- Scanner + shared_ptr test_scanner_sptr(new Scanner(Scanner::Siemens_mMR)); + + //-- ExamInfo + shared_ptr test_exam_info_sptr(new ExamInfo()); + // TODO, Currently all stir::Scanner types are PET. + test_exam_info_sptr->imaging_modality = ImagingModality::PT; + + //- + shared_ptr tmp_proj_data_info_sptr( + ProjDataInfo::ProjDataInfoCTI(test_scanner_sptr, 1, 1, test_scanner_sptr->get_max_num_views(), + test_scanner_sptr->get_max_num_non_arccorrected_bins(), false)); + + { + ProjDataInMemory test_proj_data(test_exam_info_sptr, tmp_proj_data_info_sptr); { - ProjDataInMemory test_proj_data(test_exam_info_sptr, tmp_proj_data_info_sptr); - { - ProjDataInMemory check_proj_data(test_exam_info_sptr, tmp_proj_data_info_sptr); - run_static_test(test_proj_data, check_proj_data, "static test in-memory"); - } - { - ProjDataInterfile check_proj_data(test_exam_info_sptr, tmp_proj_data_info_sptr, - "test_proj_data_export_check.hs", std::ios::out | std::ios::trunc | std::ios::in); - run_static_test(test_proj_data, check_proj_data, "static test in-memory/interfile"); - } + ProjDataInMemory check_proj_data(test_exam_info_sptr, tmp_proj_data_info_sptr); + run_static_test(test_proj_data, check_proj_data, "static test in-memory"); } { - ProjDataInterfile test_proj_data(test_exam_info_sptr, tmp_proj_data_info_sptr, - "test_proj_data_export.hs", std::ios::out | std::ios::trunc | std::ios::in); - { - ProjDataInMemory check_proj_data(test_exam_info_sptr, tmp_proj_data_info_sptr); - run_static_test(test_proj_data, check_proj_data, "static test in-memory"); - } - { - ProjDataInterfile check_proj_data(test_exam_info_sptr, tmp_proj_data_info_sptr, - "test_proj_data_export_check.hs", std::ios::out | std::ios::trunc | std::ios::in); - run_static_test(test_proj_data, check_proj_data, "static test in-memory/interfile"); - } + ProjDataInterfile check_proj_data(test_exam_info_sptr, tmp_proj_data_info_sptr, "test_proj_data_export_check.hs", + std::ios::out | std::ios::trunc | std::ios::in); + run_static_test(test_proj_data, check_proj_data, "static test in-memory/interfile"); } - - + } + { + ProjDataInterfile test_proj_data(test_exam_info_sptr, tmp_proj_data_info_sptr, "test_proj_data_export.hs", + std::ios::out | std::ios::trunc | std::ios::in); + { + ProjDataInMemory check_proj_data(test_exam_info_sptr, tmp_proj_data_info_sptr); + run_static_test(test_proj_data, check_proj_data, "static test in-memory"); + } + { + ProjDataInterfile check_proj_data(test_exam_info_sptr, tmp_proj_data_info_sptr, "test_proj_data_export_check.hs", + std::ios::out | std::ios::trunc | std::ios::in); + run_static_test(test_proj_data, check_proj_data, "static test in-memory/interfile"); + } + } } END_NAMESPACE_STIR USING_NAMESPACE_STIR -int main(int argc, char **argv) -{ - ExportArrayTests tests; - tests.run_tests(); - return tests.main_return_value(); +int +main(int argc, char** argv) { + ExportArrayTests tests; + tests.run_tests(); + return tests.main_return_value(); } diff --git a/src/test/test_filename_functions.cxx b/src/test/test_filename_functions.cxx index e258231723..638ceee283 100644 --- a/src/test/test_filename_functions.cxx +++ b/src/test/test_filename_functions.cxx @@ -43,23 +43,22 @@ START_NAMESPACE_STIR \brief Test class for filename functions defined in utility.h \ingroup test */ -class FilenameTests : public RunTests -{ +class FilenameTests : public RunTests { public: void run_tests(); }; -void FilenameTests::run_tests() -{ +void +FilenameTests::run_tests() { char filename_with_directory[max_filename_length]; cerr << "Testing various filename utilities "; #if defined(__OS_VAX__) cerr << "(using VAX-VMS filesystem conventions)" << endl; - + // relative names either contain no '[', or have '[.' -#if 0 +# if 0 // tests disabled as add_extension is disabled strcpy(filename_with_directory, "[dir.name]filename"); add_extension(filename_with_directory, ".img"); @@ -67,13 +66,13 @@ void FilenameTests::run_tests() strcpy(filename_with_directory, "[dir.name]filename.v"); add_extension(filename_with_directory, ".img"); check(strcmp(filename_with_directory, "[dir.name]filename.v") == 0); -#endif +# endif strcpy(filename_with_directory, "[dir.name]filename.v"); check(strcmp(find_filename(filename_with_directory), "filename.v") == 0); strcpy(filename_with_directory, "filename.v"); check(strcmp(find_filename(filename_with_directory), "filename.v") == 0); - + { // same checks but with string versions string filename_with_directory = "[dir.name]filename"; @@ -82,39 +81,30 @@ void FilenameTests::run_tests() filename_with_directory = "[dir.name]filename.v"; add_extension(filename_with_directory, ".img"); check(filename_with_directory == "[dir.name]filename.v"); - + filename_with_directory = "[dir.name]filename.v"; - check(filename_with_directory.substr( - find_pos_of_filename(filename_with_directory), - string::npos) - == "filename.v"); + check(filename_with_directory.substr(find_pos_of_filename(filename_with_directory), string::npos) == "filename.v"); filename_with_directory = "filename.v"; - check(filename_with_directory.substr( - find_pos_of_filename(filename_with_directory), - string::npos) - == "filename.v"); + check(filename_with_directory.substr(find_pos_of_filename(filename_with_directory), string::npos) == "filename.v"); } check(is_absolute_pathname("da0:[bladi.b]bla.v") == true); check(is_absolute_pathname("[.bladi]bla.v") == false); check(is_absolute_pathname("bla.v") == false); strcpy(filename_with_directory, "[.b]c.v"); - check(strcmp(prepend_directory_name(filename_with_directory, "da0:[a]"), - "da0:[a.b]c.v") == 0); + check(strcmp(prepend_directory_name(filename_with_directory, "da0:[a]"), "da0:[a.b]c.v") == 0); strcpy(filename_with_directory, "c.v"); - check(strcmp(prepend_directory_name(filename_with_directory, "da0:[a.b]"), - "da0:[a.b]c.v") == 0); + check(strcmp(prepend_directory_name(filename_with_directory, "da0:[a.b]"), "da0:[a.b]c.v") == 0); strcpy(filename_with_directory, "da0:[b]c.v"); - check(strcmp(prepend_directory_name(filename_with_directory, "a"), - "da0:[b]c.v") == 0); + check(strcmp(prepend_directory_name(filename_with_directory, "a"), "da0:[b]c.v") == 0); #elif defined(__OS_WIN__) cerr << "(using Windows filesystem conventions)" << endl; - + // relative names do not start with '\' or '?:\' // but we allow forward slashes as well -#if 0 +# if 0 // tests disabled as add_extension is disabled strcpy(filename_with_directory, "dir.name\\filename"); add_extension(filename_with_directory, ".img"); @@ -128,7 +118,7 @@ void FilenameTests::run_tests() strcpy(filename_with_directory, "dir.name/filename.v"); add_extension(filename_with_directory, ".img"); check(strcmp(filename_with_directory, "dir.name/filename.v") == 0); -#endif +# endif strcpy(filename_with_directory, "dir.name\\filename.v"); check(strcmp(find_filename(filename_with_directory), "filename.v") == 0); strcpy(filename_with_directory, "dir.name/filename.v"); @@ -137,193 +127,170 @@ void FilenameTests::run_tests() check(strcmp(find_filename(filename_with_directory), "filename.v") == 0); strcpy(filename_with_directory, "filename.v"); check(strcmp(find_filename(filename_with_directory), "filename.v") == 0); - { // The same with the new FilePath class. - FilePath filename_with_directory("dir.name\\filename.v", false); + { // The same with the new FilePath class. + FilePath filename_with_directory("dir.name\\filename.v", false); - check(filename_with_directory.get_filename() == "filename.v"); + check(filename_with_directory.get_filename() == "filename.v"); - filename_with_directory = "dir.name/filename.v"; - check(filename_with_directory.get_filename() == "filename.v"); + filename_with_directory = "dir.name/filename.v"; + check(filename_with_directory.get_filename() == "filename.v"); - filename_with_directory = "a:filename.v"; - check(filename_with_directory.get_filename() == "filename.v"); + filename_with_directory = "a:filename.v"; + check(filename_with_directory.get_filename() == "filename.v"); - filename_with_directory = "filename.v"; - check(filename_with_directory.get_filename() == "filename.v"); + filename_with_directory = "filename.v"; + check(filename_with_directory.get_filename() == "filename.v"); } { // same checks with string versions - string filename_with_directory = "dir.name\\filename"; + string filename_with_directory = "dir.name\\filename"; check(get_directory_name(filename_with_directory) == "dir.name\\"); add_extension(filename_with_directory, ".img"); - check(filename_with_directory == "dir.name\\filename.img"); - filename_with_directory = "dir.name\\filename.v"; + check(filename_with_directory == "dir.name\\filename.img"); + filename_with_directory = "dir.name\\filename.v"; add_extension(filename_with_directory, ".img"); - check(filename_with_directory == "dir.name\\filename.v"); - filename_with_directory = "dir.name/filename"; + check(filename_with_directory == "dir.name\\filename.v"); + filename_with_directory = "dir.name/filename"; add_extension(filename_with_directory, ".img"); - check(filename_with_directory == "dir.name/filename.img"); - filename_with_directory = "dir.name/filename.v"; + check(filename_with_directory == "dir.name/filename.img"); + filename_with_directory = "dir.name/filename.v"; add_extension(filename_with_directory, ".img"); - check(filename_with_directory == "dir.name/filename.v"); + check(filename_with_directory == "dir.name/filename.v"); - filename_with_directory = "dir.name\\filename.v"; - check(filename_with_directory.substr( - find_pos_of_filename(filename_with_directory), - string::npos) - == "filename.v"); - filename_with_directory = "dir.name/filename.v"; + filename_with_directory = "dir.name\\filename.v"; + check(filename_with_directory.substr(find_pos_of_filename(filename_with_directory), string::npos) == "filename.v"); + filename_with_directory = "dir.name/filename.v"; check(get_directory_name(filename_with_directory) == "dir.name/"); - check(filename_with_directory.substr( - find_pos_of_filename(filename_with_directory), - string::npos) - == "filename.v"); - filename_with_directory = "a:filename.v"; - check(filename_with_directory.substr( - find_pos_of_filename(filename_with_directory), - string::npos) - == "filename.v"); - filename_with_directory = "filename.v"; - check(filename_with_directory.substr( - find_pos_of_filename(filename_with_directory), - string::npos) - == "filename.v"); + check(filename_with_directory.substr(find_pos_of_filename(filename_with_directory), string::npos) == "filename.v"); + filename_with_directory = "a:filename.v"; + check(filename_with_directory.substr(find_pos_of_filename(filename_with_directory), string::npos) == "filename.v"); + filename_with_directory = "filename.v"; + check(filename_with_directory.substr(find_pos_of_filename(filename_with_directory), string::npos) == "filename.v"); } - { - check(is_absolute_pathname("\\bladi\\bla.v") == true); - check(is_absolute_pathname("a:\\bladi\\bla.v") == true); - check(is_absolute_pathname("bladi\\bla.v") == false); - check(is_absolute_pathname("/bladi/bla.v") == true); - check(is_absolute_pathname("a:/bladi/bla.v") == true); - check(is_absolute_pathname("bladi/bla.v") == false); - check(is_absolute_pathname("bla.v") == false); - - strcpy(filename_with_directory, "b\\c.v"); - check(strcmp(prepend_directory_name(filename_with_directory, "a"), - "a\\b\\c.v") == 0); - strcpy(filename_with_directory, "b\\c.v"); - check(strcmp(prepend_directory_name(filename_with_directory, "a\\"), - "a\\b\\c.v") == 0); - strcpy(filename_with_directory, "b\\c.v"); - check(strcmp(prepend_directory_name(filename_with_directory, "a:"), - "a:b\\c.v") == 0); - strcpy(filename_with_directory, "c.v"); - check(strcmp(prepend_directory_name(filename_with_directory, "a\\b"), - "a\\b\\c.v") == 0); - strcpy(filename_with_directory, "\\b\\c.v"); - check(strcmp(prepend_directory_name(filename_with_directory, "a"), - "\\b\\c.v") == 0); - strcpy(filename_with_directory, "b/c.v"); - check(strcmp(prepend_directory_name(filename_with_directory, "a"), - "a\\b/c.v") == 0); - strcpy(filename_with_directory, "b/c.v"); - check(strcmp(prepend_directory_name(filename_with_directory, "a/"), - "a/b/c.v") == 0); - strcpy(filename_with_directory, "b/c.v"); - check(strcmp(prepend_directory_name(filename_with_directory, "a:"), - "a:b/c.v") == 0); - strcpy(filename_with_directory, "c.v"); - check(strcmp(prepend_directory_name(filename_with_directory, "a/b"), - "a/b\\c.v") == 0); - strcpy(filename_with_directory, "/b/c.v"); - check(strcmp(prepend_directory_name(filename_with_directory, "a"), - "/b/c.v") == 0); + { + check(is_absolute_pathname("\\bladi\\bla.v") == true); + check(is_absolute_pathname("a:\\bladi\\bla.v") == true); + check(is_absolute_pathname("bladi\\bla.v") == false); + check(is_absolute_pathname("/bladi/bla.v") == true); + check(is_absolute_pathname("a:/bladi/bla.v") == true); + check(is_absolute_pathname("bladi/bla.v") == false); + check(is_absolute_pathname("bla.v") == false); + + strcpy(filename_with_directory, "b\\c.v"); + check(strcmp(prepend_directory_name(filename_with_directory, "a"), "a\\b\\c.v") == 0); + strcpy(filename_with_directory, "b\\c.v"); + check(strcmp(prepend_directory_name(filename_with_directory, "a\\"), "a\\b\\c.v") == 0); + strcpy(filename_with_directory, "b\\c.v"); + check(strcmp(prepend_directory_name(filename_with_directory, "a:"), "a:b\\c.v") == 0); + strcpy(filename_with_directory, "c.v"); + check(strcmp(prepend_directory_name(filename_with_directory, "a\\b"), "a\\b\\c.v") == 0); + strcpy(filename_with_directory, "\\b\\c.v"); + check(strcmp(prepend_directory_name(filename_with_directory, "a"), "\\b\\c.v") == 0); + strcpy(filename_with_directory, "b/c.v"); + check(strcmp(prepend_directory_name(filename_with_directory, "a"), "a\\b/c.v") == 0); + strcpy(filename_with_directory, "b/c.v"); + check(strcmp(prepend_directory_name(filename_with_directory, "a/"), "a/b/c.v") == 0); + strcpy(filename_with_directory, "b/c.v"); + check(strcmp(prepend_directory_name(filename_with_directory, "a:"), "a:b/c.v") == 0); + strcpy(filename_with_directory, "c.v"); + check(strcmp(prepend_directory_name(filename_with_directory, "a/b"), "a/b\\c.v") == 0); + strcpy(filename_with_directory, "/b/c.v"); + check(strcmp(prepend_directory_name(filename_with_directory, "a"), "/b/c.v") == 0); } - // Directory tests new tests + // Directory tests new tests { - check(FilePath::is_absolute("\\bladi\\bla.v") == true); - check(FilePath::is_absolute("bladi\\bla.v") == false); - check(FilePath::is_absolute("/bladi/bla.v") == true); - check(FilePath::is_absolute("a:/bladi/bla.v") == true); - check(FilePath::is_absolute("bladi/bla.v") == false); - check(FilePath::is_absolute("bla.v") == false); + check(FilePath::is_absolute("\\bladi\\bla.v") == true); + check(FilePath::is_absolute("bladi\\bla.v") == false); + check(FilePath::is_absolute("/bladi/bla.v") == true); + check(FilePath::is_absolute("a:/bladi/bla.v") == true); + check(FilePath::is_absolute("bladi/bla.v") == false); + check(FilePath::is_absolute("bla.v") == false); - FilePath filename_with_directory("b\\c.v", false); + FilePath filename_with_directory("b\\c.v", false); - filename_with_directory.prepend_directory_name("a"); - check( filename_with_directory == "a\\b\\c.v"); + filename_with_directory.prepend_directory_name("a"); + check(filename_with_directory == "a\\b\\c.v"); - filename_with_directory = "b\\c.v"; - filename_with_directory.prepend_directory_name("a\\"); - check( filename_with_directory == "a\\b\\c.v"); + filename_with_directory = "b\\c.v"; + filename_with_directory.prepend_directory_name("a\\"); + check(filename_with_directory == "a\\b\\c.v"); - filename_with_directory = "b\\c.v"; - filename_with_directory.prepend_directory_name("a:"); - check( filename_with_directory == "a:b\\c.v"); + filename_with_directory = "b\\c.v"; + filename_with_directory.prepend_directory_name("a:"); + check(filename_with_directory == "a:b\\c.v"); - filename_with_directory = "c.v"; - filename_with_directory.prepend_directory_name("a\\b"); - check( filename_with_directory == "a\\b\\c.v"); + filename_with_directory = "c.v"; + filename_with_directory.prepend_directory_name("a\\b"); + check(filename_with_directory == "a\\b\\c.v"); - filename_with_directory = "\\b\\c.v"; - filename_with_directory.prepend_directory_name("a"); - check( filename_with_directory == "\\b\\c.v"); + filename_with_directory = "\\b\\c.v"; + filename_with_directory.prepend_directory_name("a"); + check(filename_with_directory == "\\b\\c.v"); - filename_with_directory = "b/c.v"; - filename_with_directory.prepend_directory_name("a/"); - check( filename_with_directory == "a/b/c.v"); + filename_with_directory = "b/c.v"; + filename_with_directory.prepend_directory_name("a/"); + check(filename_with_directory == "a/b/c.v"); - filename_with_directory = "b\\c.v"; - filename_with_directory.prepend_directory_name("a:"); - check( filename_with_directory == "a:b\\c.v"); + filename_with_directory = "b\\c.v"; + filename_with_directory.prepend_directory_name("a:"); + check(filename_with_directory == "a:b\\c.v"); - filename_with_directory = "c.v"; - filename_with_directory.prepend_directory_name("a/b"); - check( filename_with_directory == "a/b\\c.v"); - - filename_with_directory = "/b/c.v"; - filename_with_directory.prepend_directory_name("a"); - check( filename_with_directory == "/b/c.v"); + filename_with_directory = "c.v"; + filename_with_directory.prepend_directory_name("a/b"); + check(filename_with_directory == "a/b\\c.v"); + filename_with_directory = "/b/c.v"; + filename_with_directory.prepend_directory_name("a"); + check(filename_with_directory == "/b/c.v"); } - //N.E: New directory tests. + // N.E: New directory tests. { - // No checks again because it will throw error. - FilePath fake_directory("dir.name\\filename", false); - check(FilePath::exists(fake_directory.get_path()) == false); + // No checks again because it will throw error. + FilePath fake_directory("dir.name\\filename", false); + check(FilePath::exists(fake_directory.get_path()) == false); - FilePath current_directory(FilePath::get_current_working_directory()); - check(FilePath::exists(current_directory.get_path()) == true); - check(current_directory.is_directory() == true); - check(current_directory.is_writable() == true); + FilePath current_directory(FilePath::get_current_working_directory()); + check(FilePath::exists(current_directory.get_path()) == true); + check(current_directory.is_directory() == true); + check(current_directory.is_writable() == true); - { - // Test create Path from Path. - // This is a bit of paradox so we have to set the first the - // checks to false. - // False, because not yet created. - FilePath path_to_append("my_test_folder_a", false); + { + // Test create Path from Path. + // This is a bit of paradox so we have to set the first the + // checks to false. + // False, because not yet created. + FilePath path_to_append("my_test_folder_a", false); - FilePath newly_created_path = current_directory.append(path_to_append); + FilePath newly_created_path = current_directory.append(path_to_append); - check(newly_created_path.is_directory() == true); - check(newly_created_path.is_writable() == true); + check(newly_created_path.is_directory() == true); + check(newly_created_path.is_writable() == true); - check(FilePath::exists(path_to_append.get_path()) == true); - } + check(FilePath::exists(path_to_append.get_path()) == true); + } - { - // Test create Path from String. - string path_to_append("my_test_folder_b"); + { + // Test create Path from String. + string path_to_append("my_test_folder_b"); - FilePath newly_created_path = current_directory.append(path_to_append); + FilePath newly_created_path = current_directory.append(path_to_append); - check(newly_created_path.is_directory() == true); - check(newly_created_path.is_writable() == true); + check(newly_created_path.is_directory() == true); + check(newly_created_path.is_writable() == true); - // test for recrussive creation - string paths_to_append("my_test_folder_c\\my_test_folder_d"); - FilePath newly_created_subfolder = newly_created_path.append(paths_to_append); + // test for recrussive creation + string paths_to_append("my_test_folder_c\\my_test_folder_d"); + FilePath newly_created_subfolder = newly_created_path.append(paths_to_append); - check(newly_created_subfolder.is_directory() == true); - check(newly_created_subfolder.is_writable() == true); - } + check(newly_created_subfolder.is_directory() == true); + check(newly_created_subfolder.is_writable() == true); + } } #elif defined(__OS_MAC__) @@ -331,7 +298,7 @@ void FilenameTests::run_tests() cerr << "(using MacOS filesystem conventions)" << endl; // relative names either have no ':' or do not start with ':' -#if 0 +# if 0 // tests disabled as add_extension is disabled strcpy(filename_with_directory, "dir.name:filename"); add_extension(filename_with_directory, ".img"); @@ -344,134 +311,122 @@ void FilenameTests::run_tests() check(strcmp(find_filename(filename_with_directory), "filename.v") == 0); strcpy(filename_with_directory, "filename.v"); check(strcmp(find_filename(filename_with_directory), "filename.v") == 0); -#endif +# endif { // same checks with string versions - string filename_with_directory = "dir.name:filename"; + string filename_with_directory = "dir.name:filename"; check(get_directory_name(filename_with_directory) == "dir.name:"); - + add_extension(filename_with_directory, ".img"); - check(filename_with_directory == "dir.name:filename.img"); - filename_with_directory = "dir.name:filename.v"; + check(filename_with_directory == "dir.name:filename.img"); + filename_with_directory = "dir.name:filename.v"; add_extension(filename_with_directory, ".img"); - check(filename_with_directory == "dir.name:filename.v"); - - filename_with_directory = "dir.name:filename.v"; - check(filename_with_directory.substr( - find_pos_of_filename(filename_with_directory), - string::npos) - == "filename.v"); - filename_with_directory = "filename.v"; - check(filename_with_directory.substr( - find_pos_of_filename(filename_with_directory), - string::npos) - == "filename.v"); - } - // N.E: same checks with Path class -{ + check(filename_with_directory == "dir.name:filename.v"); - FilePath filename_with_directory("dir.name:filename", false); + filename_with_directory = "dir.name:filename.v"; + check(filename_with_directory.substr(find_pos_of_filename(filename_with_directory), string::npos) == "filename.v"); + filename_with_directory = "filename.v"; + check(filename_with_directory.substr(find_pos_of_filename(filename_with_directory), string::npos) == "filename.v"); + } + // N.E: same checks with Path class + { - check( filename_with_directory.get_path() == "dir.name:"); + FilePath filename_with_directory("dir.name:filename", false); - filename_with_directory.add_extension(".img"); - check(filename_with_directory == "dir.name:filename.img"); + check(filename_with_directory.get_path() == "dir.name:"); - filename_with_directory = "dir.name:filename.v"; - check(filename_with_directory == "dir.name:filename.v"); + filename_with_directory.add_extension(".img"); + check(filename_with_directory == "dir.name:filename.img"); - filename_with_directory.add_extension(".img"); + filename_with_directory = "dir.name:filename.v"; + check(filename_with_directory == "dir.name:filename.v"); - // check no change made - check(filename_with_directory == "dir.name:filename.v"); + filename_with_directory.add_extension(".img"); - // Replace is the proper action - filename_with_directory.replace_extension(".img"); - check(filename_with_directory == "dir.name:filename.img"); + // check no change made + check(filename_with_directory == "dir.name:filename.v"); - // N.E: Not sure about this. Set again in case of failure of the - // previous test? - filename_with_directory = "dir.name:filename.v"; - check(filename_with_directory.get_filename() == "filename.v"); + // Replace is the proper action + filename_with_directory.replace_extension(".img"); + check(filename_with_directory == "dir.name:filename.img"); - filename_with_directory = "filename.v"; - check(filename_with_directory.get_filename() == "filename.v"); + // N.E: Not sure about this. Set again in case of failure of the + // previous test? + filename_with_directory = "dir.name:filename.v"; + check(filename_with_directory.get_filename() == "filename.v"); -} + filename_with_directory = "filename.v"; + check(filename_with_directory.get_filename() == "filename.v"); + } { - check(is_absolute_pathname("bladi:bla.v") == true); - check(is_absolute_pathname(":bladi:bla.v") == false); - check(is_absolute_pathname("bla.v") == false); - - strcpy(filename_with_directory, ":b:c.v"); - check(strcmp(prepend_directory_name(filename_with_directory, "a"), - "a:b:c.v") == 0); - strcpy(filename_with_directory, ":b:c.v"); - check(strcmp(prepend_directory_name(filename_with_directory, "a:"), - "a:b:c.v") == 0); - strcpy(filename_with_directory, "c.v"); - check(strcmp(prepend_directory_name(filename_with_directory, "a:b:"), - "a:b:c.v") == 0); - strcpy(filename_with_directory, "c.v"); - check(strcmp(prepend_directory_name(filename_with_directory, "a:b"), - "a:b:c.v") == 0); - strcpy(filename_with_directory, "b:c.v"); - check(strcmp(prepend_directory_name(filename_with_directory, "a"), - "b:c.v") == 0); -} + check(is_absolute_pathname("bladi:bla.v") == true); + check(is_absolute_pathname(":bladi:bla.v") == false); + check(is_absolute_pathname("bla.v") == false); + + strcpy(filename_with_directory, ":b:c.v"); + check(strcmp(prepend_directory_name(filename_with_directory, "a"), "a:b:c.v") == 0); + strcpy(filename_with_directory, ":b:c.v"); + check(strcmp(prepend_directory_name(filename_with_directory, "a:"), "a:b:c.v") == 0); + strcpy(filename_with_directory, "c.v"); + check(strcmp(prepend_directory_name(filename_with_directory, "a:b:"), "a:b:c.v") == 0); + strcpy(filename_with_directory, "c.v"); + check(strcmp(prepend_directory_name(filename_with_directory, "a:b"), "a:b:c.v") == 0); + strcpy(filename_with_directory, "b:c.v"); + check(strcmp(prepend_directory_name(filename_with_directory, "a"), "b:c.v") == 0); + } // Directory tests new tests { - check(FilePath::is_absolute(":bladi:bla.v") == true); - check(FilePath::is_absolute("bladi:bla.v") == false); - check(FilePath::is_absolute("bla.v") == false); + check(FilePath::is_absolute(":bladi:bla.v") == true); + check(FilePath::is_absolute("bladi:bla.v") == false); + check(FilePath::is_absolute("bla.v") == false); - FilePath filename_with_directory("b:c.v", false); + FilePath filename_with_directory("b:c.v", false); - filename_with_directory.prepend_directory_name("a"); - check( filename_with_directory == "a:b:c.v"); + filename_with_directory.prepend_directory_name("a"); + check(filename_with_directory == "a:b:c.v"); - filename_with_directory = "b:c.v"; - filename_with_directory.prepend_directory_name("a:"); - check( filename_with_directory == "a:b:c.v"); + filename_with_directory = "b:c.v"; + filename_with_directory.prepend_directory_name("a:"); + check(filename_with_directory == "a:b:c.v"); - filename_with_directory = "c.v"; - filename_with_directory.prepend_directory_name("a:b"); - check( filename_with_directory == "a:b:c.v"); + filename_with_directory = "c.v"; + filename_with_directory.prepend_directory_name("a:b"); + check(filename_with_directory == "a:b:c.v"); - filename_with_directory = ":b:c.v"; - filename_with_directory.prepend_directory_name("a"); - check( filename_with_directory == ":b:c.v"); + filename_with_directory = ":b:c.v"; + filename_with_directory.prepend_directory_name("a"); + check(filename_with_directory == ":b:c.v"); } - //N.E: New directory tests. + // N.E: New directory tests. { - // No checks again because it will throw error. - FilePath fake_directory("dir.name:filename", false); + // No checks again because it will throw error. + FilePath fake_directory("dir.name:filename", false); - check (FilePath::exists(fake_directory.get_path()) == false); + check(FilePath::exists(fake_directory.get_path()) == false); - FilePath current_directory(FilePath::get_current_working_directory()); - check (FilePath::exists(current_directory.get_path()) == true); - check(current_directory.is_directory() == true); - check(current_directory.is_writable() == true); + FilePath current_directory(FilePath::get_current_working_directory()); + check(FilePath::exists(current_directory.get_path()) == true); + check(current_directory.is_directory() == true); + check(current_directory.is_writable() == true); - { - // Test create Path from Path. - // This is a bit of paradox so we have to set the first the - // checks to false. - // False, because not yet created. - FilePath path_to_append("my_test_folder_a", false); + { + // Test create Path from Path. + // This is a bit of paradox so we have to set the first the + // checks to false. + // False, because not yet created. + FilePath path_to_append("my_test_folder_a", false); - FilePath newly_created_path = current_directory.append(path_to_append); + FilePath newly_created_path = current_directory.append(path_to_append); - check(newly_created_path.is_directory() == true); - check(newly_created_path.is_writable() == true); + check(newly_created_path.is_directory() == true); + check(newly_created_path.is_writable() == true); - check (FilePath::exists(path_to_append.get_path()) == true); - } + check(FilePath::exists(path_to_append.get_path()) == true); + } - { + { // Test create Path from String. string path_to_append("my_test_folder_b"); @@ -486,13 +441,13 @@ void FilenameTests::run_tests() check(newly_created_subfolder.is_directory() == true); check(newly_created_subfolder.is_writable() == true); - } + } } #else // defined(__OS_UNIX__) cerr << "(using Unix filesystem conventions)" << endl; -#if 0 +# if 0 // tests disabled as add_extension is disabled strcpy(filename_with_directory, "dir.name/filename"); add_extension(filename_with_directory, ".img"); @@ -505,136 +460,123 @@ void FilenameTests::run_tests() check(strcmp(find_filename(filename_with_directory), "filename.v") == 0); strcpy(filename_with_directory, "filename.v"); check(strcmp(find_filename(filename_with_directory), "filename.v") == 0); -#endif +# endif { // same checks with string versions - string filename_with_directory = "dir.name/filename"; + string filename_with_directory = "dir.name/filename"; check(get_directory_name(filename_with_directory) == "dir.name/"); add_extension(filename_with_directory, ".img"); - check(filename_with_directory == "dir.name/filename.img"); - filename_with_directory = "dir.name/filename.v"; + check(filename_with_directory == "dir.name/filename.img"); + filename_with_directory = "dir.name/filename.v"; add_extension(filename_with_directory, ".img"); - check(filename_with_directory == "dir.name/filename.v"); - - filename_with_directory = "dir.name/filename.v"; - check(filename_with_directory.substr( - find_pos_of_filename(filename_with_directory), - string::npos) - == "filename.v"); - filename_with_directory = "filename.v"; - check(filename_with_directory.substr( - find_pos_of_filename(filename_with_directory), - string::npos) - == "filename.v"); + check(filename_with_directory == "dir.name/filename.v"); + filename_with_directory = "dir.name/filename.v"; + check(filename_with_directory.substr(find_pos_of_filename(filename_with_directory), string::npos) == "filename.v"); + filename_with_directory = "filename.v"; + check(filename_with_directory.substr(find_pos_of_filename(filename_with_directory), string::npos) == "filename.v"); } - // N.E: same checks with Path class + // N.E: same checks with Path class { FilePath filename_with_directory("dir.name/filename", false); - check( filename_with_directory.get_path() == "dir.name/"); + check(filename_with_directory.get_path() == "dir.name/"); filename_with_directory.add_extension(".img"); - check(filename_with_directory == "dir.name/filename.img"); + check(filename_with_directory == "dir.name/filename.img"); - filename_with_directory = "dir.name/filename.v"; + filename_with_directory = "dir.name/filename.v"; check(filename_with_directory == "dir.name/filename.v"); filename_with_directory.add_extension(".img"); // check no change made - check(filename_with_directory == "dir.name/filename.v"); + check(filename_with_directory == "dir.name/filename.v"); // Replace is the proper action filename_with_directory.replace_extension(".img"); - check(filename_with_directory == "dir.name/filename.img"); + check(filename_with_directory == "dir.name/filename.img"); // N.E: Not sure about this. Set again in case of failure of the // previous test? - filename_with_directory = "dir.name/filename.v"; + filename_with_directory = "dir.name/filename.v"; check(filename_with_directory.get_filename() == "filename.v"); - filename_with_directory = "filename.v"; + filename_with_directory = "filename.v"; check(filename_with_directory.get_filename() == "filename.v"); - } - // N.E: Finished the old tests with the new FilePath; + // N.E: Finished the old tests with the new FilePath; // Directory tests old tests { - check(is_absolute_pathname("/bladi/bla.v") == true); - check(is_absolute_pathname("bladi/bla.v") == false); - check(is_absolute_pathname("bla.v") == false); - - strcpy(filename_with_directory, "b/c.v"); - check(strcmp(prepend_directory_name(filename_with_directory, "a"), - "a/b/c.v") == 0); - strcpy(filename_with_directory, "b/c.v"); - check(strcmp(prepend_directory_name(filename_with_directory, "a/"), - "a/b/c.v") == 0); - strcpy(filename_with_directory, "c.v"); - check(strcmp(prepend_directory_name(filename_with_directory, "a/b"), - "a/b/c.v") == 0); - strcpy(filename_with_directory, "/b/c.v"); - check(strcmp(prepend_directory_name(filename_with_directory, "a"), - "/b/c.v") == 0); + check(is_absolute_pathname("/bladi/bla.v") == true); + check(is_absolute_pathname("bladi/bla.v") == false); + check(is_absolute_pathname("bla.v") == false); + + strcpy(filename_with_directory, "b/c.v"); + check(strcmp(prepend_directory_name(filename_with_directory, "a"), "a/b/c.v") == 0); + strcpy(filename_with_directory, "b/c.v"); + check(strcmp(prepend_directory_name(filename_with_directory, "a/"), "a/b/c.v") == 0); + strcpy(filename_with_directory, "c.v"); + check(strcmp(prepend_directory_name(filename_with_directory, "a/b"), "a/b/c.v") == 0); + strcpy(filename_with_directory, "/b/c.v"); + check(strcmp(prepend_directory_name(filename_with_directory, "a"), "/b/c.v") == 0); } // Directory tests new tests { - check(FilePath::is_absolute("/bladi/bla.v") == true); - check(FilePath::is_absolute("bladi/bla.v") == false); - check(FilePath::is_absolute("bla.v") == false); + check(FilePath::is_absolute("/bladi/bla.v") == true); + check(FilePath::is_absolute("bladi/bla.v") == false); + check(FilePath::is_absolute("bla.v") == false); - FilePath filename_with_directory("b/c.v", false); + FilePath filename_with_directory("b/c.v", false); - filename_with_directory.prepend_directory_name("a"); - check( filename_with_directory == "a/b/c.v"); + filename_with_directory.prepend_directory_name("a"); + check(filename_with_directory == "a/b/c.v"); - filename_with_directory = "b/c.v"; - filename_with_directory.prepend_directory_name("a/"); - check( filename_with_directory == "a/b/c.v"); + filename_with_directory = "b/c.v"; + filename_with_directory.prepend_directory_name("a/"); + check(filename_with_directory == "a/b/c.v"); - filename_with_directory = "c.v"; - filename_with_directory.prepend_directory_name("a/b"); - check( filename_with_directory == "a/b/c.v"); + filename_with_directory = "c.v"; + filename_with_directory.prepend_directory_name("a/b"); + check(filename_with_directory == "a/b/c.v"); - filename_with_directory = "/b/c.v"; - filename_with_directory.prepend_directory_name("a"); - check( filename_with_directory == "/b/c.v"); + filename_with_directory = "/b/c.v"; + filename_with_directory.prepend_directory_name("a"); + check(filename_with_directory == "/b/c.v"); } - - //N.E: New directory tests. + // N.E: New directory tests. { - // No checks again because it will throw error. - FilePath fake_directory("dir.name/filename", false); - check (FilePath::exists(fake_directory.get_path()) == false); + // No checks again because it will throw error. + FilePath fake_directory("dir.name/filename", false); + check(FilePath::exists(fake_directory.get_path()) == false); - FilePath current_directory(FilePath::get_current_working_directory()); - check (FilePath::exists(current_directory.get_path()) == true); - check(current_directory.is_directory() == true); - check(current_directory.is_writable() == true); + FilePath current_directory(FilePath::get_current_working_directory()); + check(FilePath::exists(current_directory.get_path()) == true); + check(current_directory.is_directory() == true); + check(current_directory.is_writable() == true); - { - // Test create Path from Path. - // This is a bit of paradox so we have to set the first the - // checks to false. - // False, because not yet created. - FilePath path_to_append("my_test_folder_a", false); + { + // Test create Path from Path. + // This is a bit of paradox so we have to set the first the + // checks to false. + // False, because not yet created. + FilePath path_to_append("my_test_folder_a", false); - FilePath newly_created_path = current_directory.append(path_to_append); + FilePath newly_created_path = current_directory.append(path_to_append); - check(newly_created_path.is_directory() == true); - check(newly_created_path.is_writable() == true); + check(newly_created_path.is_directory() == true); + check(newly_created_path.is_writable() == true); - check (FilePath::exists(path_to_append.get_path()) == true); - } + check(FilePath::exists(path_to_append.get_path()) == true); + } - { + { // Test create Path from String. string path_to_append("my_test_folder_b"); @@ -649,17 +591,17 @@ void FilenameTests::run_tests() check(newly_created_subfolder.is_directory() == true); check(newly_created_subfolder.is_writable() == true); - } + } } -#endif /* Unix */ +#endif /* Unix */ } END_NAMESPACE_STIR USING_NAMESPACE_STIR -int main() -{ +int +main() { FilenameTests tests; tests.run_tests(); return tests.main_return_value(); diff --git a/src/test/test_find_fwhm_in_image.cxx b/src/test/test_find_fwhm_in_image.cxx index 742d73d018..8086f946bc 100644 --- a/src/test/test_find_fwhm_in_image.cxx +++ b/src/test/test_find_fwhm_in_image.cxx @@ -6,12 +6,12 @@ it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. - + This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - + See STIR/LICENSE.txt for details */ /*! @@ -23,17 +23,17 @@ \author Pablo Aguiar \author Kris Thielemans - - To run the test, simply run the executable. + + To run the test, simply run the executable. */ - + #include "stir/RunTests.h" #include "stir/VoxelsOnCartesianGrid.h" #include "stir/SeparableCartesianMetzImageFilter.h" #define DO_DISPLAY 0 #if DO_DISPLAY -#include "stir/display.h" +# include "stir/display.h" #endif #include "stir/find_fwhm_in_image.h" #include "stir/Coordinate3D.h" @@ -55,259 +55,220 @@ START_NAMESPACE_STIR \ingroup test \brief A simple class to test the find_fwhm_in_image function. */ -class find_fwhm_in_imageTests : public RunTests -{ +class find_fwhm_in_imageTests : public RunTests { public: - find_fwhm_in_imageTests() - {} + find_fwhm_in_imageTests() {} void run_tests(); + private: - //istream& in; + // istream& in; }; -void set_Gaussian_filter_fwhm(SeparableCartesianMetzImageFilter& filter, - const float fwhm_z, - const float fwhm_y, - const float fwhm_x) -{ +void +set_Gaussian_filter_fwhm(SeparableCartesianMetzImageFilter& filter, const float fwhm_z, const float fwhm_y, + const float fwhm_x) { std::string buffer; std::stringstream parameterstream(buffer); parameterstream << "Separable Cartesian Metz Filter Parameters :=\n" - << "x-dir filter FWHM (in mm):= " << fwhm_x << "\n" - << "y-dir filter FWHM (in mm):= " << fwhm_y << "\n" - << "z-dir filter FWHM (in mm):= " << fwhm_z << "\n" - << "x-dir filter Metz power:= .0\n" - << "y-dir filter Metz power:= .0\n" - << "z-dir filter Metz power:=.0\n" - << "END Separable Cartesian Metz Filter Parameters :=\n"; - filter.parse(parameterstream); + << "x-dir filter FWHM (in mm):= " << fwhm_x << "\n" + << "y-dir filter FWHM (in mm):= " << fwhm_y << "\n" + << "z-dir filter FWHM (in mm):= " << fwhm_z << "\n" + << "x-dir filter Metz power:= .0\n" + << "y-dir filter Metz power:= .0\n" + << "z-dir filter Metz power:=.0\n" + << "END Separable Cartesian Metz Filter Parameters :=\n"; + filter.parse(parameterstream); } - -void find_fwhm_in_imageTests::run_tests() -{ +void +find_fwhm_in_imageTests::run_tests() { cerr << "Testing find_fwhm_in_image function..." << endl; set_tolerance(1.0); - - CartesianCoordinate3D origin (0,1,2); - CartesianCoordinate3D grid_spacing (2,1.4F,2.5F); - - IndexRange<3> - range(CartesianCoordinate3D(0,-65,-64), - CartesianCoordinate3D(24,64,65)); - + CartesianCoordinate3D origin(0, 1, 2); + CartesianCoordinate3D grid_spacing(2, 1.4F, 2.5F); + + IndexRange<3> range(CartesianCoordinate3D(0, -65, -64), CartesianCoordinate3D(24, 64, 65)); - VoxelsOnCartesianGrid image(range,origin, grid_spacing); + VoxelsOnCartesianGrid image(range, origin, grid_spacing); SeparableCartesianMetzImageFilter filter; - set_Gaussian_filter_fwhm(filter, 12,14,12); + set_Gaussian_filter_fwhm(filter, 12, 14, 12); { // point source image.fill(0); - Coordinate3D location_of_maximum(12,0,0); + Coordinate3D location_of_maximum(12, 0, 0); image[location_of_maximum] = 1; - check_if_equal(image[location_of_maximum], 1.F, - "for parameter constant, should be equal"); + check_if_equal(image[location_of_maximum], 1.F, "for parameter constant, should be equal"); filter.apply(image); #if DO_DISPLAY - std::cerr << "min, max: " << image.find_min() <<", " << image.find_max() << '\n'; - display(image,image.find_max()); + std::cerr << "min, max: " << image.find_min() << ", " << image.find_max() << '\n'; + display(image, image.find_max()); #endif - const std::list > result = - find_fwhm_in_image(image, 1, 2, 0, true); + const std::list> result = find_fwhm_in_image(image, 1, 2, 0, true); check(result.size() == 1, "check only 1 maximum for single point source case"); - std::list >::const_iterator current_result_iter = - result.begin(); - - check_if_equal(current_result_iter->voxel_location, location_of_maximum, - "check location of maximum for single point source case"); - check_if_equal(current_result_iter->resolution, - Coordinate3D(12,14,12), - "check resolution for single point source case"); + std::list>::const_iterator current_result_iter = result.begin(); - - } + check_if_equal(current_result_iter->voxel_location, location_of_maximum, + "check location of maximum for single point source case"); + check_if_equal(current_result_iter->resolution, Coordinate3D(12, 14, 12), + "check resolution for single point source case"); + } { // two spheres in a slice image.fill(0); - Coordinate3D location_of_maximum(12,0,0); - image[location_of_maximum]=2; - image[12][0][18]=1; - set_Gaussian_filter_fwhm(filter, 12,14,12); + Coordinate3D location_of_maximum(12, 0, 0); + image[location_of_maximum] = 2; + image[12][0][18] = 1; + set_Gaussian_filter_fwhm(filter, 12, 14, 12); filter.apply(image); #if DO_DISPLAY - display(image, image.find_max()); + display(image, image.find_max()); #endif - const std::list > result = - find_fwhm_in_image(image, 1, 2, 0, true); + const std::list> result = find_fwhm_in_image(image, 1, 2, 0, true); check(result.size() == 1, "check only 1 maximum for 2 point sources in 1 slice"); - std::list >::const_iterator current_result_iter = - result.begin(); - + std::list>::const_iterator current_result_iter = result.begin(); + check_if_equal(current_result_iter->voxel_location, location_of_maximum, - "check location of maximum for 2 point sources in 1 slice"); - check_if_equal(current_result_iter->resolution, - Coordinate3D(12,14,12), - "check resolution for 2 point sources in 1 slice"); + "check location of maximum for 2 point sources in 1 slice"); + check_if_equal(current_result_iter->resolution, Coordinate3D(12, 14, 12), + "check resolution for 2 point sources in 1 slice"); } { SeparableCartesianMetzImageFilter filter2; - set_Gaussian_filter_fwhm(filter2, 13,14,11); + set_Gaussian_filter_fwhm(filter2, 13, 14, 11); // two spheres in different slices image.fill(0); - Coordinate3D location_of_maximum(14,0,0); - image[location_of_maximum]=2; - image[3][0][0]=1; + Coordinate3D location_of_maximum(14, 0, 0); + image[location_of_maximum] = 2; + image[3][0][0] = 1; filter2.apply(image); #if DO_DISPLAY - display(image, image.find_max()); + display(image, image.find_max()); #endif - const std::list > result = - find_fwhm_in_image(image, 1, 2, 0, true); + const std::list> result = find_fwhm_in_image(image, 1, 2, 0, true); check(result.size() == 1, "check only 1 maximum for 2 point sources in different slices"); - std::list >::const_iterator current_result_iter = - result.begin(); - + std::list>::const_iterator current_result_iter = result.begin(); + check_if_equal(current_result_iter->voxel_location, location_of_maximum, - "check location of maximum for 2 point sources in different slices"); - check_if_equal(current_result_iter->resolution, - Coordinate3D(13,14,11), - "check resolution for 2 point sources in different slices"); + "check location of maximum for 2 point sources in different slices"); + check_if_equal(current_result_iter->resolution, Coordinate3D(13, 14, 11), + "check resolution for 2 point sources in different slices"); } { // 3 spheres source image.fill(0.0005F); - Coordinate3D location_of_maximum1(12,0,0); + Coordinate3D location_of_maximum1(12, 0, 0); image[location_of_maximum1] = 5; - - Coordinate3D location_of_maximum2(19,32,36); - Coordinate3D location_of_maximum3(3,-32,-32); - image[location_of_maximum2]=3.F; - image[location_of_maximum3]=1.2F; + + Coordinate3D location_of_maximum2(19, 32, 36); + Coordinate3D location_of_maximum3(3, -32, -32); + image[location_of_maximum2] = 3.F; + image[location_of_maximum3] = 1.2F; // other_image[24][10][0]=1; - //other_image[0][0][10]=1; - //other_image[14][64][0]=1; - //other_image[8][0][65]=1; + // other_image[0][0][10]=1; + // other_image[14][64][0]=1; + // other_image[8][0][65]=1; - set_Gaussian_filter_fwhm(filter, 12,14,12); + set_Gaussian_filter_fwhm(filter, 12, 14, 12); filter.apply(image); - + #if DO_DISPLAY - display(image, image.find_max()); + display(image, image.find_max()); #endif - const std::list > result = - find_fwhm_in_image(image, 2, 2, 0, true); + const std::list> result = find_fwhm_in_image(image, 2, 2, 0, true); check(result.size() == 2, "check only 2 maxima from 3 point source case"); - std::list >::const_iterator current_result_iter = - result.begin(); - + std::list>::const_iterator current_result_iter = result.begin(); + check_if_equal(current_result_iter->voxel_location, location_of_maximum1, - "check location of 1st maximum for 3 point source case"); - check_if_equal(current_result_iter->resolution, - Coordinate3D(12,14,12), - "check resolution for 1st maximum 3 point source case"); + "check location of 1st maximum for 3 point source case"); + check_if_equal(current_result_iter->resolution, Coordinate3D(12, 14, 12), + "check resolution for 1st maximum 3 point source case"); ++current_result_iter; check_if_equal(current_result_iter->voxel_location, location_of_maximum2, - "check location of 2nd maximum for 3 point source case"); - check_if_equal(current_result_iter->resolution, - Coordinate3D(12,14,12), - "check resolution for 2nd maximum 3 point source case"); + "check location of 2nd maximum for 3 point source case"); + check_if_equal(current_result_iter->resolution, Coordinate3D(12, 14, 12), + "check resolution for 2nd maximum 3 point source case"); //++current_result_iter; - //check_if_equal(current_result_iter->voxel_location, location_of_maximum3, + // check_if_equal(current_result_iter->voxel_location, location_of_maximum3, // "check location of 3rd maximum for 3 point source case"); - //check_if_equal(current_result_iter->resolution, + // check_if_equal(current_result_iter->resolution, // Coordinate3D(4,4,4), // "check resolution for 3rd maximum 3 point source case"); - } - + { // test line source in y-direction image.fill(0); const int z_location = 12; const int x_location = 0; - for (int y=image[z_location].get_min_index(); - y<=image[z_location].get_max_index();++y) - image[z_location][y][x_location] = 1; - set_Gaussian_filter_fwhm(filter, 12,0,10); - filter.apply(image); - const std::list > result = - find_fwhm_in_image(image, image[z_location].get_length(), 2, 2, true); - #if DO_DISPLAY - display(image, image.find_max()); + for (int y = image[z_location].get_min_index(); y <= image[z_location].get_max_index(); ++y) + image[z_location][y][x_location] = 1; + set_Gaussian_filter_fwhm(filter, 12, 0, 10); + filter.apply(image); + const std::list> result = find_fwhm_in_image(image, image[z_location].get_length(), 2, 2, true); +#if DO_DISPLAY + display(image, image.find_max()); #endif - check(result.size() == static_cast(image[z_location].get_length()) , - "check number of maxima in line source along y axis"); - int y=image[z_location].get_min_index(); - for(std::list >::const_iterator current_result_iter = - result.begin(); - current_result_iter != result.end(); - ++current_result_iter) - { - Coordinate3D location_of_maximum(z_location,y,x_location); - check_if_equal(current_result_iter->voxel_location, location_of_maximum, - "check location of maximum in line source along y axis"); - check_if_equal(current_result_iter->resolution, - Coordinate3D(12,0,10), - "check resolution in line source along y axisXXX"); - ++y; - } - + check(result.size() == static_cast(image[z_location].get_length()), + "check number of maxima in line source along y axis"); + int y = image[z_location].get_min_index(); + for (std::list>::const_iterator current_result_iter = result.begin(); + current_result_iter != result.end(); ++current_result_iter) { + Coordinate3D location_of_maximum(z_location, y, x_location); + check_if_equal(current_result_iter->voxel_location, location_of_maximum, + "check location of maximum in line source along y axis"); + check_if_equal(current_result_iter->resolution, Coordinate3D(12, 0, 10), + "check resolution in line source along y axisXXX"); + ++y; + } } { // test line source in x-direction - image.fill(0); - const int z_location = 9; - const int y_location = 0; - for (int x=image[z_location][y_location].get_min_index(); - x<=image[z_location][y_location].get_max_index();++x) - { - image[z_location][y_location][x] = 1; - } - set_Gaussian_filter_fwhm(filter, 12,10,0); - filter.apply(image); - const std::list > result = - find_fwhm_in_image(image, image[z_location].get_length(), 2, 3, true); - #if DO_DISPLAY - display(image, image.find_max()); + image.fill(0); + const int z_location = 9; + const int y_location = 0; + for (int x = image[z_location][y_location].get_min_index(); x <= image[z_location][y_location].get_max_index(); ++x) { + image[z_location][y_location][x] = 1; + } + set_Gaussian_filter_fwhm(filter, 12, 10, 0); + filter.apply(image); + const std::list> result = find_fwhm_in_image(image, image[z_location].get_length(), 2, 3, true); +#if DO_DISPLAY + display(image, image.find_max()); #endif - check(result.size() == static_cast(image[z_location][y_location].get_length()) , - "check number of maxima in line source along x axis"); - int x=image[z_location][y_location].get_min_index(); - for(std::list >::const_iterator current_result_iter = - result.begin(); - current_result_iter != result.end(); - ++current_result_iter) - { - Coordinate3D location_of_maximum(z_location,y_location,x); - check_if_equal(current_result_iter->voxel_location, location_of_maximum, - "check location of maximum in line source along x axis"); - check_if_equal(current_result_iter->resolution, - Coordinate3D(12,10,0), - "check resolution in line source along x axis"); - ++x; - } + check(result.size() == static_cast(image[z_location][y_location].get_length()), + "check number of maxima in line source along x axis"); + int x = image[z_location][y_location].get_min_index(); + for (std::list>::const_iterator current_result_iter = result.begin(); + current_result_iter != result.end(); ++current_result_iter) { + Coordinate3D location_of_maximum(z_location, y_location, x); + check_if_equal(current_result_iter->voxel_location, location_of_maximum, + "check location of maximum in line source along x axis"); + check_if_equal(current_result_iter->resolution, Coordinate3D(12, 10, 0), + "check resolution in line source along x axis"); + ++x; + } } { @@ -316,53 +277,43 @@ void find_fwhm_in_imageTests::run_tests() const int y_location = 0; const int x_location = 0; - for (int z=image.get_min_index(); z<=image.get_max_index();++z) - { - image[z][y_location][x_location] = 1; - } - set_Gaussian_filter_fwhm(filter, 12,11,10); - filter.apply(image); - const std::list > result = - find_fwhm_in_image(image, image.get_length(), 2, 1, true); + for (int z = image.get_min_index(); z <= image.get_max_index(); ++z) { + image[z][y_location][x_location] = 1; + } + set_Gaussian_filter_fwhm(filter, 12, 11, 10); + filter.apply(image); + const std::list> result = find_fwhm_in_image(image, image.get_length(), 2, 1, true); #if DO_DISPLAY - display(image, image.find_max()); + display(image, image.find_max()); #endif - check(result.size() == static_cast(image.get_length()) , - "check number of maxima in line source along z axis"); - int z=image.get_min_index(); - for(std::list >::const_iterator current_result_iter = - result.begin(); - current_result_iter != result.end(); - ++current_result_iter) - { - Coordinate3D location_of_maximum(z, y_location,x_location); - check_if_equal(current_result_iter->voxel_location, location_of_maximum, - "check location of maximum in line source along z axis"); - check_if_equal(current_result_iter->resolution, - Coordinate3D(0,11,10), - "check resolution in line source along z axis"); - ++z; - } + check(result.size() == static_cast(image.get_length()), "check number of maxima in line source along z axis"); + int z = image.get_min_index(); + for (std::list>::const_iterator current_result_iter = result.begin(); + current_result_iter != result.end(); ++current_result_iter) { + Coordinate3D location_of_maximum(z, y_location, x_location); + check_if_equal(current_result_iter->voxel_location, location_of_maximum, + "check location of maximum in line source along z axis"); + check_if_equal(current_result_iter->resolution, Coordinate3D(0, 11, 10), + "check resolution in line source along z axis"); + ++z; + } } - /* - const double old_tolerance = get_tolerance(); - set_tolerance(error_on_chi_square); - check_if_equal(expected_chi_square, chi_square, - "for parameter chi_square, should be equal"); - */ + /* + const double old_tolerance = get_tolerance(); + set_tolerance(error_on_chi_square); + check_if_equal(expected_chi_square, chi_square, + "for parameter chi_square, should be equal"); + */ } - END_NAMESPACE_STIR - USING_NAMESPACE_STIR -int main(int argc, char **argv) -{ - if (argc != 1) - { +int +main(int argc, char** argv) { + if (argc != 1) { cerr << "Usage : " << argv[0] << " \n"; return EXIT_FAILURE; } diff --git a/src/test/test_interpolate.cxx b/src/test/test_interpolate.cxx index 1800b28d65..ded2cf954a 100644 --- a/src/test/test_interpolate.cxx +++ b/src/test/test_interpolate.cxx @@ -42,38 +42,29 @@ using std::endl; USING_NAMESPACE_STIR -int -main(int argc, char *argv[]) -{ - if (ask ("1D", false)) - { - typedef Array<1,float> array; - - array in(-9,9); +int +main(int argc, char* argv[]) { + if (ask("1D", false)) { + typedef Array<1, float> array; + + array in(-9, 9); // initialise with some data - for (int i=-9; i<=9; i++) - in[i] = .5*square(i-6)+3.*i-30; - - - do - { - const float zoom = - ask_num("Zoom ", 0., 100., 1.); - const float offset = - ask_num("Offset ", -100., 100., 0.); - const float min2 = (in.get_min_index()-.5 - offset)*zoom; - const float max2 = (in.get_max_index()+.5 - offset)*zoom; - cout << "Range of non-zero 'out' coordinates : (" - << min2 << ", " << max2 << ")" << endl; - const int out_min = - ask_num("Start index ", (int)(min2-20), (int)(max2+20), (int)min2); - const int out_max = - ask_num("End index ", out_min, (int)(max2+20), (int)max2); - + for (int i = -9; i <= 9; i++) + in[i] = .5 * square(i - 6) + 3. * i - 30; + + do { + const float zoom = ask_num("Zoom ", 0., 100., 1.); + const float offset = ask_num("Offset ", -100., 100., 0.); + const float min2 = (in.get_min_index() - .5 - offset) * zoom; + const float max2 = (in.get_max_index() + .5 - offset) * zoom; + cout << "Range of non-zero 'out' coordinates : (" << min2 << ", " << max2 << ")" << endl; + const int out_min = ask_num("Start index ", (int)(min2 - 20), (int)(max2 + 20), (int)min2); + const int out_max = ask_num("End index ", out_min, (int)(max2 + 20), (int)max2); + array out(out_min, out_max); // fill with some junk to see if it sets it to 0 out.fill(111111111.F); - + overlap_interpolate(out, in, zoom, offset, true); cout << "in:" << in; cout << "out:" << out; @@ -81,83 +72,67 @@ main(int argc, char *argv[]) cout << "old sum : " << in.sum() << ", new sum : " << out.sum() << endl; { - Array<1,float> in_coords(in.get_min_index(), in.get_max_index()+1); - for (int i=in_coords.get_min_index(); i<=in_coords.get_max_index(); ++i) - in_coords[i]=i-.5F; - Array<1,float> out_coords(out.get_min_index(), out.get_max_index()+1); - for (int i=out_coords.get_min_index(); i<=out_coords.get_max_index(); ++i) - out_coords[i]=(i-.5F)/zoom+offset; - array new_out(out.get_index_range()); - overlap_interpolate(new_out.begin(), new_out.end(), - out_coords.begin(), out_coords.end(), - in.begin(), in.end(), - in_coords.begin(), in_coords.end() - ); - cout << new_out; - new_out -= out; - cout << "diff:\n" << new_out; + Array<1, float> in_coords(in.get_min_index(), in.get_max_index() + 1); + for (int i = in_coords.get_min_index(); i <= in_coords.get_max_index(); ++i) + in_coords[i] = i - .5F; + Array<1, float> out_coords(out.get_min_index(), out.get_max_index() + 1); + for (int i = out_coords.get_min_index(); i <= out_coords.get_max_index(); ++i) + out_coords[i] = (i - .5F) / zoom + offset; + array new_out(out.get_index_range()); + overlap_interpolate(new_out.begin(), new_out.end(), out_coords.begin(), out_coords.end(), in.begin(), in.end(), + in_coords.begin(), in_coords.end()); + cout << new_out; + new_out -= out; + cout << "diff:\n" << new_out; } } while (ask("More ?", true)); } - - - if (ask ("2D", true)) - { - - typedef Array<2,float> array; - - array in(IndexRange2D(-4,4,3,6)); + + if (ask("2D", true)) { + + typedef Array<2, float> array; + + array in(IndexRange2D(-4, 4, 3, 6)); // initialise with arbitrary data - for (int i=-4; i<=4; i++) - for (int j=3; j<=6; j++) - in[i][j] = .5*square(i-6)+3.*i-30+j; - - do - { - const float zoom = - ask_num("Zoom ", 0., 100., 1.); - const float offset = - ask_num("Offset ", -100., 100., 0.); - const float min2 = (in.get_min_index()-.5 - offset)*zoom; - const float max2 = (in.get_max_index()+.5 - offset)*zoom; - cout << "Range of non-zero 'out' coordinates : (" - << min2 << ", " << max2 << ")" << endl; - const int out_min = - ask_num("Start index ", (int)(min2-20), (int)(max2+20), (int)min2); - const int out_max = - ask_num("End index ", out_min, (int)(max2+20), (int)max2); - - array out(IndexRange2D(out_min, out_max, 3,6)); - - - // fill with some junk to see if it sets it to 0 - out.fill(111111111.F); - - overlap_interpolate(out, in, zoom, offset, true); - cout << out; - - cout << "old sum : " << in.sum() << ", new sum : " << out.sum() << endl; + for (int i = -4; i <= 4; i++) + for (int j = 3; j <= 6; j++) + in[i][j] = .5 * square(i - 6) + 3. * i - 30 + j; + + do { + const float zoom = ask_num("Zoom ", 0., 100., 1.); + const float offset = ask_num("Offset ", -100., 100., 0.); + const float min2 = (in.get_min_index() - .5 - offset) * zoom; + const float max2 = (in.get_max_index() + .5 - offset) * zoom; + cout << "Range of non-zero 'out' coordinates : (" << min2 << ", " << max2 << ")" << endl; + const int out_min = ask_num("Start index ", (int)(min2 - 20), (int)(max2 + 20), (int)min2); + const int out_max = ask_num("End index ", out_min, (int)(max2 + 20), (int)max2); + + array out(IndexRange2D(out_min, out_max, 3, 6)); + + // fill with some junk to see if it sets it to 0 + out.fill(111111111.F); + + overlap_interpolate(out, in, zoom, offset, true); + cout << out; + + cout << "old sum : " << in.sum() << ", new sum : " << out.sum() << endl; { - Array<1,float> in_coords(in.get_min_index(), in.get_max_index()+1); - for (int i=in_coords.get_min_index(); i<=in_coords.get_max_index(); ++i) - in_coords[i]=i-.5F; - Array<1,float> out_coords(out.get_min_index(), out.get_max_index()+1); - for (int i=out_coords.get_min_index(); i<=out_coords.get_max_index(); ++i) - out_coords[i]=(i-.5F)/zoom+offset; - array new_out(out.get_index_range()); - overlap_interpolate(new_out.begin(), new_out.end(), - out_coords.begin(), out_coords.end(), - in.begin(), in.end(), - in_coords.begin(), in_coords.end() - ); - cout << new_out; - new_out -= out; - cout << "diff:\n" << new_out; + Array<1, float> in_coords(in.get_min_index(), in.get_max_index() + 1); + for (int i = in_coords.get_min_index(); i <= in_coords.get_max_index(); ++i) + in_coords[i] = i - .5F; + Array<1, float> out_coords(out.get_min_index(), out.get_max_index() + 1); + for (int i = out_coords.get_min_index(); i <= out_coords.get_max_index(); ++i) + out_coords[i] = (i - .5F) / zoom + offset; + array new_out(out.get_index_range()); + overlap_interpolate(new_out.begin(), new_out.end(), out_coords.begin(), out_coords.end(), in.begin(), in.end(), + in_coords.begin(), in_coords.end()); + cout << new_out; + new_out -= out; + cout << "diff:\n" << new_out; } - - } while (ask("More ?", true)); + + } while (ask("More ?", true)); } - - + return EXIT_SUCCESS; } diff --git a/src/test/test_linear_regression.cxx b/src/test/test_linear_regression.cxx index ba1415e257..e79f14097b 100644 --- a/src/test/test_linear_regression.cxx +++ b/src/test/test_linear_regression.cxx @@ -12,7 +12,7 @@ - + To run the test, you should use a command line argument with the name of a file. This should contain a number of test cases for the fit. See linear_regressionTests for file contents. @@ -34,7 +34,7 @@ See STIR/LICENSE.txt for details */ - + #include "stir/linear_regression.h" #include "stir/RunTests.h" #include "stir/ArrayFunction.h" @@ -64,8 +64,8 @@ START_NAMESPACE_STIR list_of_numbers
    ... - - where list_of_numbers is the following list of numbers + + where list_of_numbers is the following list of numbers (white space is ignored) number_of_points
    @@ -73,170 +73,145 @@ START_NAMESPACE_STIR data
    weights
    expected_constant expected_scale
    - expected_chi_square
    + expected_chi_square
    expected_variance_of_constant expected_variance_of_scale
    expected_covariance_of_constant_with_scale
    */ -class linear_regressionTests : public RunTests -{ +class linear_regressionTests : public RunTests { public: - linear_regressionTests(istream& in) - : in(in) - {} + linear_regressionTests(istream& in) : in(in) {} void run_tests(); + private: istream& in; }; - -void linear_regressionTests::run_tests() -{ +void +linear_regressionTests::run_tests() { cerr << "Testing linear_regression function..." << endl; char text[200]; - in.get(text,200); + in.get(text, 200); cerr << text << endl; int test_case = 0; - - while (in) - { + + while (in) { // first get rid of EOL - in.getline(text,200); + in.getline(text, 200); // now get real text - in.get(text,200); - test_case ++; - + in.get(text, 200); + test_case++; + int size = 0; in >> size; - if (size<=0) + if (size <= 0) break; cerr << text << endl; - Array<1,float> coordinates(size); - Array<1,float> measured_data(size); - Array<1,float> weights(size); - for (int i=0; i coordinates(size); + Array<1, float> measured_data(size); + Array<1, float> weights(size); + for (int i = 0; i < size; i++) in >> coordinates[i]; - for (int i=0; i> measured_data[i]; - for (int i=0; i> weights[i]; - + double expected_scale; double expected_constant; double expected_variance_of_scale; double expected_variance_of_constant; double expected_covariance_of_constant_with_scale; double expected_chi_square; - - in >> expected_constant >> expected_scale - >> expected_chi_square - >> expected_variance_of_constant >> expected_variance_of_scale - >> expected_covariance_of_constant_with_scale; - - - double scale=0; - double constant=0; - double variance_of_scale=0; - double variance_of_constant=0; - double covariance_of_constant_with_scale=0; + + in >> expected_constant >> expected_scale >> expected_chi_square >> expected_variance_of_constant >> + expected_variance_of_scale >> expected_covariance_of_constant_with_scale; + + double scale = 0; + double constant = 0; + double variance_of_scale = 0; + double variance_of_constant = 0; + double covariance_of_constant_with_scale = 0; double chi_square = 0; - - linear_regression( - constant, scale, - chi_square, - variance_of_constant, - variance_of_scale, - covariance_of_constant_with_scale, - measured_data, - coordinates, - weights); - - check_if_equal(expected_constant, constant, - "for parameter constant, should be equal"); - check_if_equal(expected_scale, scale, - "for parameter scale, should be equal"); + + linear_regression(constant, scale, chi_square, variance_of_constant, variance_of_scale, covariance_of_constant_with_scale, + measured_data, coordinates, weights); + + check_if_equal(expected_constant, constant, "for parameter constant, should be equal"); + check_if_equal(expected_scale, scale, "for parameter scale, should be equal"); // KT 24/01/2001 changed tolerance for this comparisons - /* chi_square is computed as a + /* chi_square is computed as a chi^2 = weights.(data - (constant + scale coordinates))^2 - + There's a subtraction of nearly matching floating - point numbers. This means there's a lot of potential for + point numbers. This means there's a lot of potential for numerical error. Float computations have a precision of about 10^-5. Which means you can really trust numbers upto 10^-5 times some value related to the size of the numbers you're adding/subtracing. So, we're really computing - chi^2 +- 10^-5*(weights.data) + chi^2 +- 10^-5*(weights.data) */ double error_on_chi_square = 0; { - for (int i=0; i scanner_sptr(new Scanner(Scanner::E953)); - shared_ptr proj_data_info_sptr(ProjDataInfo::ProjDataInfoCTI(scanner_sptr, 1, 10, 96, 128, true)); - shared_ptr exam_info_sptr(new ExamInfo); - - // Create and write proj data 1 - shared_ptr proj_data_1_sptr(new ProjDataInMemory(exam_info_sptr, proj_data_info_sptr)); - float fill_value = 5.F; - proj_data_1_sptr->fill(fill_value); - proj_data_1_sptr->write_to_file("test_proj_data1"); - - // Create and write proj data 2 - shared_ptr proj_data_2_sptr(new ProjDataInMemory(exam_info_sptr, proj_data_info_sptr)); - proj_data_2_sptr->fill(fill_value*2.F); - proj_data_2_sptr->write_to_file("test_proj_data2"); - - // Create a multi header file - std::ofstream myfile("test_multi_file.txt"); - if (myfile.is_open()) { - myfile << "Multi :=\n"; - myfile << "\ttotal number of data sets := 2\n"; - myfile << "\tdata set[1] := test_proj_data1.hs\n"; - myfile << "\tdata set[2] := test_proj_data2.hs\n"; - myfile << "end :=\n"; - myfile.close(); - } - else { - everything_ok = false; - return; - } - - // Read back in - shared_ptr read_in_multi_proj_data; - read_in_multi_proj_data = MultipleProjData::read_from_file("test_multi_file.txt"); - - // Compare results - check_if_equal(read_in_multi_proj_data->get_proj_data(1).get_viewgram(0,0).find_max(), - proj_data_1_sptr->get_viewgram(0,0).find_max(), - "test between maxes of first sinogram"); - - check_if_equal(read_in_multi_proj_data->get_proj_data(2).get_viewgram(0,0).find_min(), - proj_data_2_sptr->get_viewgram(1,1).find_min(), - "test between mins of second sinogram"); +MultipleProjDataTests::run_tests() { + std::cout << "-------- Testing MultipleProjData --------\n"; + + // Create single proj data + shared_ptr scanner_sptr(new Scanner(Scanner::E953)); + shared_ptr proj_data_info_sptr(ProjDataInfo::ProjDataInfoCTI(scanner_sptr, 1, 10, 96, 128, true)); + shared_ptr exam_info_sptr(new ExamInfo); + + // Create and write proj data 1 + shared_ptr proj_data_1_sptr(new ProjDataInMemory(exam_info_sptr, proj_data_info_sptr)); + float fill_value = 5.F; + proj_data_1_sptr->fill(fill_value); + proj_data_1_sptr->write_to_file("test_proj_data1"); + + // Create and write proj data 2 + shared_ptr proj_data_2_sptr(new ProjDataInMemory(exam_info_sptr, proj_data_info_sptr)); + proj_data_2_sptr->fill(fill_value * 2.F); + proj_data_2_sptr->write_to_file("test_proj_data2"); + + // Create a multi header file + std::ofstream myfile("test_multi_file.txt"); + if (myfile.is_open()) { + myfile << "Multi :=\n"; + myfile << "\ttotal number of data sets := 2\n"; + myfile << "\tdata set[1] := test_proj_data1.hs\n"; + myfile << "\tdata set[2] := test_proj_data2.hs\n"; + myfile << "end :=\n"; + myfile.close(); + } else { + everything_ok = false; + return; + } + + // Read back in + shared_ptr read_in_multi_proj_data; + read_in_multi_proj_data = MultipleProjData::read_from_file("test_multi_file.txt"); + + // Compare results + check_if_equal(read_in_multi_proj_data->get_proj_data(1).get_viewgram(0, 0).find_max(), + proj_data_1_sptr->get_viewgram(0, 0).find_max(), "test between maxes of first sinogram"); + + check_if_equal(read_in_multi_proj_data->get_proj_data(2).get_viewgram(0, 0).find_min(), + proj_data_2_sptr->get_viewgram(1, 1).find_min(), "test between mins of second sinogram"); } END_NAMESPACE_STIR - USING_NAMESPACE_STIR -int main() -{ +int +main() { set_default_num_threads(); diff --git a/src/test/test_proj_data.cxx b/src/test/test_proj_data.cxx index 21bb8d82d0..bb6881a9fc 100644 --- a/src/test/test_proj_data.cxx +++ b/src/test/test_proj_data.cxx @@ -42,211 +42,192 @@ START_NAMESPACE_STIR - /*! \ingroup test \brief Test class for ProjData and ProjDataInMemory */ -class ProjDataTests: public RunTests -{ +class ProjDataTests : public RunTests { public: void run_tests(); + private: void run_tests_on_proj_data(ProjData&); void run_tests_in_memory_only(ProjDataInMemory&); }; void -ProjDataTests:: -run_tests_on_proj_data(ProjData& proj_data) -{ +ProjDataTests::run_tests_on_proj_data(ProjData& proj_data) { CPUTimer timer; const float value = 1.2F; std::cerr << "test fill(float)\n"; - timer.reset(); timer.start(); + timer.reset(); + timer.start(); { proj_data.fill(value); - Viewgram viewgram = proj_data.get_viewgram(0,0); - check_if_equal(viewgram.find_min(), - value, - "test fill(float) and get_viewgram"); + Viewgram viewgram = proj_data.get_viewgram(0, 0); + check_if_equal(viewgram.find_min(), value, "test fill(float) and get_viewgram"); } - - timer.stop(); std::cerr<< "-- CPU Time " << timer.value() << '\n'; + + timer.stop(); + std::cerr << "-- CPU Time " << timer.value() << '\n'; std::cerr << "\ntest set_viewgram\n"; - timer.reset(); timer.start(); + timer.reset(); + timer.start(); { - Viewgram viewgram = proj_data.get_empty_viewgram(1,1); - viewgram.fill(value*2); - check(proj_data.set_viewgram(viewgram) == Succeeded::yes, - "test set_viewgram succeeded"); - - Viewgram viewgram2 = proj_data.get_viewgram(1,1); - check_if_equal(viewgram2.find_min(), - viewgram.find_min(), - "test set/get_viewgram"); + Viewgram viewgram = proj_data.get_empty_viewgram(1, 1); + viewgram.fill(value * 2); + check(proj_data.set_viewgram(viewgram) == Succeeded::yes, "test set_viewgram succeeded"); + + Viewgram viewgram2 = proj_data.get_viewgram(1, 1); + check_if_equal(viewgram2.find_min(), viewgram.find_min(), "test set/get_viewgram"); } - timer.stop(); std::cerr<< "-- CPU Time " << timer.value() << '\n'; + timer.stop(); + std::cerr << "-- CPU Time " << timer.value() << '\n'; std::cerr << "\ntest making a copy\n"; - timer.reset(); timer.start(); + timer.reset(); + timer.start(); { ProjDataInMemory proj_data2(proj_data); - check_if_equal(proj_data2.get_viewgram(0,0).find_max(), - proj_data.get_viewgram(0,0).find_max(), + check_if_equal(proj_data2.get_viewgram(0, 0).find_max(), proj_data.get_viewgram(0, 0).find_max(), "test 1 for copy-constructor and get_viewgram"); - check_if_equal(proj_data2.get_viewgram(1,1).find_max(), - proj_data.get_viewgram(1,1).find_max(), + check_if_equal(proj_data2.get_viewgram(1, 1).find_max(), proj_data.get_viewgram(1, 1).find_max(), "test 1 for copy-constructor and get_viewgram"); // check this was a deep-copy by filling and check if the original is not affected proj_data2.fill(1e4f); - check(std::abs(proj_data2.get_viewgram(0,0).find_max() - - proj_data.get_viewgram(0,0).find_max()) > 1.f, + check(std::abs(proj_data2.get_viewgram(0, 0).find_max() - proj_data.get_viewgram(0, 0).find_max()) > 1.f, "test 1 for deep copy and get_viewgram"); } - timer.stop(); std::cerr<< "-- CPU Time " << timer.value() << '\n'; + timer.stop(); + std::cerr << "-- CPU Time " << timer.value() << '\n'; std::cerr << "\ntest making a copy using stir::copy_to\n"; - timer.reset(); timer.start(); + timer.reset(); + timer.start(); { ProjDataInMemory proj_data2(proj_data.get_exam_info_sptr(), proj_data.get_proj_data_info_sptr()); - ProjData const& p=proj_data; + ProjData const& p = proj_data; copy_to(p, proj_data2.begin_all()); - check_if_equal(proj_data2.get_viewgram(0,0).find_max(), - proj_data.get_viewgram(0,0).find_max(), + check_if_equal(proj_data2.get_viewgram(0, 0).find_max(), proj_data.get_viewgram(0, 0).find_max(), "test 1 for templated-copy and get_viewgram(0,0)"); - check_if_equal(proj_data2.get_viewgram(1,1).find_max(), - proj_data.get_viewgram(1,1).find_max(), + check_if_equal(proj_data2.get_viewgram(1, 1).find_max(), proj_data.get_viewgram(1, 1).find_max(), "test 1 for templated-copy and get_viewgram(1,1)"); } - timer.stop(); std::cerr<< "-- CPU Time " << timer.value() << '\n'; + timer.stop(); + std::cerr << "-- CPU Time " << timer.value() << '\n'; std::cerr << "\ntest making a copy using stir::copy_to with reference to ProjData\n"; - timer.reset(); timer.start(); + timer.reset(); + timer.start(); { ProjDataInMemory proj_data2(proj_data.get_exam_info_sptr(), proj_data.get_proj_data_info_sptr()); ProjData& p_ref(proj_data); copy_to(p_ref, proj_data2.begin_all()); - check_if_equal(proj_data2.get_viewgram(0,0).find_max(), - proj_data.get_viewgram(0,0).find_max(), + check_if_equal(proj_data2.get_viewgram(0, 0).find_max(), proj_data.get_viewgram(0, 0).find_max(), "test 1 for templated-copy ProjData& and get_viewgram(0,0)"); - check_if_equal(proj_data2.get_viewgram(1,1).find_max(), - proj_data.get_viewgram(1,1).find_max(), + check_if_equal(proj_data2.get_viewgram(1, 1).find_max(), proj_data.get_viewgram(1, 1).find_max(), "test 1 for templated-copy ProjData& and get_viewgram(1,1)"); } - timer.stop(); std::cerr<< "-- CPU Time " << timer.value() << '\n'; + timer.stop(); + std::cerr << "-- CPU Time " << timer.value() << '\n'; std::cerr << "\ntest fill with larger input\n"; - timer.reset(); timer.start(); - { - shared_ptr proj_data_info_sptr2 - (ProjDataInfo::ProjDataInfoCTI(proj_data.get_proj_data_info_sptr()->get_scanner_sptr(), - /*span*/1, 8, - proj_data.get_num_views(), proj_data.get_num_tangential_poss(), - /*arc_corrected*/ true) - ); - - + timer.reset(); + timer.start(); + { + shared_ptr proj_data_info_sptr2( + ProjDataInfo::ProjDataInfoCTI(proj_data.get_proj_data_info_sptr()->get_scanner_sptr(), + /*span*/ 1, 8, proj_data.get_num_views(), proj_data.get_num_tangential_poss(), + /*arc_corrected*/ true)); + // construct without filling ProjDataInMemory proj_data2(proj_data.get_exam_info_sptr(), proj_data_info_sptr2, false); proj_data2.fill(proj_data); - check_if_equal(proj_data2.get_viewgram(0,0).find_max(), - proj_data.get_viewgram(0,0).find_max(), + check_if_equal(proj_data2.get_viewgram(0, 0).find_max(), proj_data.get_viewgram(0, 0).find_max(), "test 1 for copy-constructor and get_viewgram"); - check_if_equal(proj_data2.get_viewgram(1,1).find_max(), - proj_data.get_viewgram(1,1).find_max(), + check_if_equal(proj_data2.get_viewgram(1, 1).find_max(), proj_data.get_viewgram(1, 1).find_max(), "test 1 for copy-constructor and get_viewgram"); } - timer.stop(); std::cerr<< "-- CPU Time " << timer.value() << '\n'; + timer.stop(); + std::cerr << "-- CPU Time " << timer.value() << '\n'; std::cerr << "\ntest fill with smaller input\n"; - timer.reset(); timer.start(); - { - shared_ptr proj_data_info_sptr2 - (ProjDataInfo::ProjDataInfoCTI(proj_data.get_proj_data_info_sptr()->get_scanner_sptr(), - /*span*/1, 12, - proj_data.get_num_views(), proj_data.get_num_tangential_poss(), - /*arc_corrected*/ true) - ); - - + timer.reset(); + timer.start(); + { + shared_ptr proj_data_info_sptr2( + ProjDataInfo::ProjDataInfoCTI(proj_data.get_proj_data_info_sptr()->get_scanner_sptr(), + /*span*/ 1, 12, proj_data.get_num_views(), proj_data.get_num_tangential_poss(), + /*arc_corrected*/ true)); + // construct without filling ProjDataInMemory proj_data2(proj_data.get_exam_info_sptr(), proj_data_info_sptr2, false); // this should call error, so we'll catch it - try - { - std::cout << "\nthis test should intentionally throw an error\n"; - proj_data2.fill(proj_data); - check(false, "test fill wtih too small proj_data should have thrown"); - } - catch (...) - { - // ok - } + try { + std::cout << "\nthis test should intentionally throw an error\n"; + proj_data2.fill(proj_data); + check(false, "test fill wtih too small proj_data should have thrown"); + } catch (...) { + // ok + } } - timer.stop(); std::cerr<< "-- CPU Time " << timer.value() << '\n'; + timer.stop(); + std::cerr << "-- CPU Time " << timer.value() << '\n'; } void -ProjDataTests::run_tests_in_memory_only(ProjDataInMemory& proj_data) -{ +ProjDataTests::run_tests_in_memory_only(ProjDataInMemory& proj_data) { std::cerr << "\ntest set_bin_value() and get_bin_value\n"; { - std::vector test; - test.resize(proj_data.size_all()); - - for(unsigned int i=0;i viewgram=proj_data.get_viewgram(bin.view_num(), bin.segment_num()); - check_if_equal(bin.get_bin_value(),viewgram[bin.axial_pos_num()][bin.tangential_pos_num()], - "ProjDataInMemory::set_bin_value/get_viewgram not consistent"); + std::vector test; + test.resize(proj_data.size_all()); + + for (unsigned int i = 0; i < test.size(); i++) + test[i] = i; + + fill_from(proj_data, test.begin(), test.end()); + + Bin bin(0, proj_data.get_max_view_num() / 2, proj_data.get_max_axial_pos_num(0) / 2, 0); + + bin.set_bin_value(42); + proj_data.set_bin_value(bin); + check_if_equal(bin.get_bin_value(), proj_data.get_bin_value(bin), + "ProjDataInMemory::set_bin_value/get_bin_value not consistent"); + // also check via get_viewgram + const Viewgram viewgram = proj_data.get_viewgram(bin.view_num(), bin.segment_num()); + check_if_equal(bin.get_bin_value(), viewgram[bin.axial_pos_num()][bin.tangential_pos_num()], + "ProjDataInMemory::set_bin_value/get_viewgram not consistent"); } std::cerr << "test if copy_to is consistent with iterators\n"; { - Array<3,float> test_array(IndexRange3D(proj_data.get_num_sinograms(), proj_data.get_num_views(), proj_data.get_num_tangential_poss())); + Array<3, float> test_array( + IndexRange3D(proj_data.get_num_sinograms(), proj_data.get_num_views(), proj_data.get_num_tangential_poss())); // copy to the array copy_to(proj_data, test_array.begin_all()); { - Array<3,float>::const_full_iterator test_array_iter = test_array.begin_all_const(); + Array<3, float>::const_full_iterator test_array_iter = test_array.begin_all_const(); ProjDataInMemory::full_iterator proj_data_iter = proj_data.begin_all(); - while (test_array_iter != test_array.end_all_const()) - { - if (!check_if_equal(*proj_data_iter, *test_array_iter, "check if array iterator in correct order")) - { - // get out as there will be lots of other failures - break; - } - ++test_array_iter; - ++proj_data_iter; + while (test_array_iter != test_array.end_all_const()) { + if (!check_if_equal(*proj_data_iter, *test_array_iter, "check if array iterator in correct order")) { + // get out as there will be lots of other failures + break; } + ++test_array_iter; + ++proj_data_iter; + } } } } void -ProjDataTests:: -run_tests() -{ +ProjDataTests::run_tests() { std::cerr << "-------- Testing ProjData --------\n"; shared_ptr scanner_sptr(new Scanner(Scanner::E953)); - shared_ptr proj_data_info_sptr - (ProjDataInfo::ProjDataInfoCTI(scanner_sptr, - /*span*/1, 10,/*views*/ 2096, /*tang_pos*/132, /*arc_corrected*/ true) - ); + shared_ptr proj_data_info_sptr(ProjDataInfo::ProjDataInfoCTI(scanner_sptr, + /*span*/ 1, 10, /*views*/ 2096, /*tang_pos*/ 132, + /*arc_corrected*/ true)); shared_ptr exam_info_sptr(new ExamInfo); exam_info_sptr->imaging_modality = ImagingModality::PT; @@ -254,29 +235,24 @@ run_tests() // construct with filling to 0 ProjDataInMemory proj_data_in_memory(exam_info_sptr, proj_data_info_sptr); { - Sinogram sinogram = proj_data_in_memory.get_sinogram(0,0); - check_if_equal(sinogram.find_min(), - 0.F, - "test constructor and get_sinogram"); + Sinogram sinogram = proj_data_in_memory.get_sinogram(0, 0); + check_if_equal(sinogram.find_min(), 0.F, "test constructor and get_sinogram"); } run_tests_on_proj_data(proj_data_in_memory); run_tests_in_memory_only(proj_data_in_memory); - std::cerr<< "\n-----------------Repeating tests but now with interfile input\n"; + std::cerr << "\n-----------------Repeating tests but now with interfile input\n"; - ProjDataInterfile(exam_info_sptr, proj_data_info_sptr, - "test_proj_data.hs", std::ios::in|std::ios::out|std::ios::trunc); + ProjDataInterfile(exam_info_sptr, proj_data_info_sptr, "test_proj_data.hs", std::ios::in | std::ios::out | std::ios::trunc); run_tests_on_proj_data(proj_data_in_memory); - } END_NAMESPACE_STIR - USING_NAMESPACE_STIR -int main() -{ +int +main() { ProjDataTests tests; tests.run_tests(); return tests.main_return_value(); diff --git a/src/test/test_proj_data_in_memory.cxx b/src/test/test_proj_data_in_memory.cxx index ade318c1e5..cb058947d4 100644 --- a/src/test/test_proj_data_in_memory.cxx +++ b/src/test/test_proj_data_in_memory.cxx @@ -38,269 +38,203 @@ START_NAMESPACE_STIR - /*! \ingroup test \brief Test class for ProjDataInMemory */ -class ProjDataInMemoryTests: public RunTests -{ +class ProjDataInMemoryTests : public RunTests { public: void run_tests(); void run_tests_no_tof(); void run_tests_tof(); }; - void -ProjDataInMemoryTests:: -run_tests() -{ - this->run_tests_no_tof(); - this->run_tests_tof(); +ProjDataInMemoryTests::run_tests() { + this->run_tests_no_tof(); + this->run_tests_tof(); } void -ProjDataInMemoryTests:: -run_tests_no_tof() -{ +ProjDataInMemoryTests::run_tests_no_tof() { std::cerr << "-------- Testing ProjDataInMemory without TOF --------\n"; shared_ptr scanner_sptr(new Scanner(Scanner::E953)); - - shared_ptr proj_data_info_sptr - (ProjDataInfo::ProjDataInfoCTI(scanner_sptr, - /*span*/1, 10,/*views*/ 96, /*tang_pos*/128, /*arc_corrected*/ true) - ); + + shared_ptr proj_data_info_sptr(ProjDataInfo::ProjDataInfoCTI(scanner_sptr, + /*span*/ 1, 10, /*views*/ 96, /*tang_pos*/ 128, + /*arc_corrected*/ true)); shared_ptr exam_info_sptr(new ExamInfo); - // construct with filling to 0 ProjDataInMemory proj_data(exam_info_sptr, proj_data_info_sptr); { - Sinogram sinogram = proj_data.get_sinogram(0,0); - check_if_equal(sinogram.find_min(), - 0.F, - "test constructor and get_sinogram"); + Sinogram sinogram = proj_data.get_sinogram(0, 0); + check_if_equal(sinogram.find_min(), 0.F, "test constructor and get_sinogram"); } const float value = 1.2F; // test fill(float) { proj_data.fill(value); - Viewgram viewgram = proj_data.get_viewgram(0,0); - check_if_equal(viewgram.find_min(), - value, - "test fill(float) and get_viewgram"); + Viewgram viewgram = proj_data.get_viewgram(0, 0); + check_if_equal(viewgram.find_min(), value, "test fill(float) and get_viewgram"); } - + // test set_viewgram { - Viewgram viewgram = proj_data.get_empty_viewgram(1,1); - viewgram.fill(value*2); - check(proj_data.set_viewgram(viewgram) == Succeeded::yes, - "test set_viewgram succeeded"); - - Viewgram viewgram2 = proj_data.get_viewgram(1,1); - check_if_equal(viewgram2.find_min(), - viewgram.find_min(), - "test set/get_viewgram"); + Viewgram viewgram = proj_data.get_empty_viewgram(1, 1); + viewgram.fill(value * 2); + check(proj_data.set_viewgram(viewgram) == Succeeded::yes, "test set_viewgram succeeded"); + + Viewgram viewgram2 = proj_data.get_viewgram(1, 1); + check_if_equal(viewgram2.find_min(), viewgram.find_min(), "test set/get_viewgram"); } - // test making a copy + // test making a copy { ProjDataInMemory proj_data2(proj_data); - check_if_equal(proj_data2.get_viewgram(0,0).find_max(), - proj_data.get_viewgram(0,0).find_max(), + check_if_equal(proj_data2.get_viewgram(0, 0).find_max(), proj_data.get_viewgram(0, 0).find_max(), "test 1 for copy-constructor and get_viewgram"); - check_if_equal(proj_data2.get_viewgram(1,1).find_max(), - proj_data.get_viewgram(1,1).find_max(), + check_if_equal(proj_data2.get_viewgram(1, 1).find_max(), proj_data.get_viewgram(1, 1).find_max(), "test 1 for copy-constructor and get_viewgram"); proj_data2.fill(1e4f); - check(std::abs(proj_data2.get_viewgram(0,0).find_max() - - proj_data.get_viewgram(0,0).find_max()) > 1.f, + check(std::abs(proj_data2.get_viewgram(0, 0).find_max() - proj_data.get_viewgram(0, 0).find_max()) > 1.f, "test 1 for deep copy and get_viewgram"); } // test fill with larger input - { - shared_ptr proj_data_info_sptr2 - (ProjDataInfo::ProjDataInfoCTI(scanner_sptr, - /*span*/1, 8,/*views*/ 96, /*tang_pos*/128, /*arc_corrected*/ true) - ); - - + { + shared_ptr proj_data_info_sptr2(ProjDataInfo::ProjDataInfoCTI(scanner_sptr, + /*span*/ 1, 8, /*views*/ 96, /*tang_pos*/ 128, + /*arc_corrected*/ true)); + // construct without filling ProjDataInMemory proj_data2(exam_info_sptr, proj_data_info_sptr2, false); proj_data2.fill(proj_data); - check_if_equal(proj_data2.get_viewgram(0,0).find_max(), - proj_data.get_viewgram(0,0).find_max(), + check_if_equal(proj_data2.get_viewgram(0, 0).find_max(), proj_data.get_viewgram(0, 0).find_max(), "test 1 for constructor, fill and get_viewgram(0,0)"); - check_if_equal(proj_data2.get_viewgram(1,1).find_max(), - proj_data.get_viewgram(1,1).find_max(), + check_if_equal(proj_data2.get_viewgram(1, 1).find_max(), proj_data.get_viewgram(1, 1).find_max(), "test 1 for constructor, fill and get_viewgram(1,1)"); } // test fill with smaller input - { - shared_ptr proj_data_info_sptr2 - (ProjDataInfo::ProjDataInfoCTI(scanner_sptr, - /*span*/1, 12,/*views*/ 96, /*tang_pos*/128, /*arc_corrected*/ true) - ); - - + { + shared_ptr proj_data_info_sptr2(ProjDataInfo::ProjDataInfoCTI(scanner_sptr, + /*span*/ 1, 12, /*views*/ 96, /*tang_pos*/ 128, + /*arc_corrected*/ true)); + // construct without filling ProjDataInMemory proj_data2(exam_info_sptr, proj_data_info_sptr2, false); // this should call error, so we'll catch it - try - { - std::cout << "\nthis test should throw an error (which we will catch)\n"; - proj_data2.fill(proj_data); - check(false, "test fill with too small proj_data should have thrown"); - } - catch (...) - { - // ok - } + try { + std::cout << "\nthis test should throw an error (which we will catch)\n"; + proj_data2.fill(proj_data); + check(false, "test fill with too small proj_data should have thrown"); + } catch (...) { + // ok + } } } void -ProjDataInMemoryTests:: -run_tests_tof() -{ +ProjDataInMemoryTests::run_tests_tof() { std::cerr << "-------- Testing ProjDataInMemory with TOF --------\n"; shared_ptr scanner_sptr(new Scanner(Scanner::PETMR_Signa)); - shared_ptr proj_data_info_sptr - (ProjDataInfo::ProjDataInfoCTI(scanner_sptr, - /*span*/1, 10,/*views*/ 96, /*tang_pos*/64, /*arc_corrected*/ true, 70) - ); + shared_ptr proj_data_info_sptr(ProjDataInfo::ProjDataInfoCTI(scanner_sptr, + /*span*/ 1, 10, /*views*/ 96, /*tang_pos*/ 64, + /*arc_corrected*/ true, 70)); shared_ptr exam_info_sptr(new ExamInfo); - // construct with filling to 0 ProjDataInMemory proj_data(exam_info_sptr, proj_data_info_sptr); { - check_if_equal( proj_data.get_sinogram(0,0,false,-2).get_timing_pos_num(), - -2, - "test get_sinogram timing position index"); - Sinogram sinogram = proj_data.get_sinogram(0,0,false,-2); - check_if_equal(sinogram.get_timing_pos_num(), - -2, - "test constructor and get_sinogram timing position index"); - check_if_equal(sinogram.find_min(), - 0.F, - "test constructor and get_sinogram"); + check_if_equal(proj_data.get_sinogram(0, 0, false, -2).get_timing_pos_num(), -2, "test get_sinogram timing position index"); + Sinogram sinogram = proj_data.get_sinogram(0, 0, false, -2); + check_if_equal(sinogram.get_timing_pos_num(), -2, "test constructor and get_sinogram timing position index"); + check_if_equal(sinogram.find_min(), 0.F, "test constructor and get_sinogram"); } const float value = 1.2F; // test fill(float) { proj_data.fill(value); - Viewgram viewgram = proj_data.get_viewgram(0,0,false,-2); - check_if_equal(viewgram.get_timing_pos_num(), - -2, - "test constructor and get_viewgram timing position index"); - check_if_equal(viewgram.find_min(), - value, - "test fill(float) and get_viewgram"); + Viewgram viewgram = proj_data.get_viewgram(0, 0, false, -2); + check_if_equal(viewgram.get_timing_pos_num(), -2, "test constructor and get_viewgram timing position index"); + check_if_equal(viewgram.find_min(), value, "test fill(float) and get_viewgram"); } // test set_viewgram { - Viewgram viewgram = proj_data.get_empty_viewgram(1,1,false,-2); - viewgram.fill(value*2); - check(proj_data.set_viewgram(viewgram) == Succeeded::yes, - "test set_viewgram succeeded"); - - Viewgram viewgram2 = proj_data.get_viewgram(1,1,false,-2); - check_if_equal(viewgram2.get_timing_pos_num(), - -2, - "test set/get_viewgram timing position index"); - check_if_equal(viewgram2.find_min(), - viewgram.find_min(), - "test set/get_viewgram"); + Viewgram viewgram = proj_data.get_empty_viewgram(1, 1, false, -2); + viewgram.fill(value * 2); + check(proj_data.set_viewgram(viewgram) == Succeeded::yes, "test set_viewgram succeeded"); + + Viewgram viewgram2 = proj_data.get_viewgram(1, 1, false, -2); + check_if_equal(viewgram2.get_timing_pos_num(), -2, "test set/get_viewgram timing position index"); + check_if_equal(viewgram2.find_min(), viewgram.find_min(), "test set/get_viewgram"); } // test set_segment_by_view { - SegmentByView segment = proj_data.get_empty_segment_by_view(1,false,-2); - segment.fill(value*2); - check(proj_data.set_segment(segment) == Succeeded::yes, - "test set_segment succeeded"); - - SegmentByView segment2 = proj_data.get_segment_by_view(1,-2); - check_if_equal(segment2.get_timing_pos_num(), - -2, - "test set/get_segment_by_view timing position index"); - check_if_equal(segment2.find_min(), - segment.find_min(), - "test set/get_segment_by_view"); + SegmentByView segment = proj_data.get_empty_segment_by_view(1, false, -2); + segment.fill(value * 2); + check(proj_data.set_segment(segment) == Succeeded::yes, "test set_segment succeeded"); + + SegmentByView segment2 = proj_data.get_segment_by_view(1, -2); + check_if_equal(segment2.get_timing_pos_num(), -2, "test set/get_segment_by_view timing position index"); + check_if_equal(segment2.find_min(), segment.find_min(), "test set/get_segment_by_view"); } // test making a copy { ProjDataInMemory proj_data2(proj_data); - check_if_equal(proj_data2.get_viewgram(0,0,false,-2).find_max(), - proj_data.get_viewgram(0,0,false,-2).find_max(), + check_if_equal(proj_data2.get_viewgram(0, 0, false, -2).find_max(), proj_data.get_viewgram(0, 0, false, -2).find_max(), "test 1 for copy-constructor and get_viewgram"); - check_if_equal(proj_data2.get_viewgram(1,1,false,-2).find_max(), - proj_data.get_viewgram(1,1,false,-2).find_max(), + check_if_equal(proj_data2.get_viewgram(1, 1, false, -2).find_max(), proj_data.get_viewgram(1, 1, false, -2).find_max(), "test 1 for copy-constructor and get_viewgram"); - check_if_equal(proj_data2.get_viewgram(1,1,false,-2).get_timing_pos_num(), - -2, + check_if_equal(proj_data2.get_viewgram(1, 1, false, -2).get_timing_pos_num(), -2, "test 2 for copy-constructor and get_viewgram"); } // test fill with larger input { - shared_ptr proj_data_info_sptr2 - (ProjDataInfo::ProjDataInfoCTI(scanner_sptr, - /*span*/1, 8,/*views*/ 96, /*tang_pos*/64, /*arc_corrected*/ true, proj_data.get_tof_mash_factor()) - ); - + shared_ptr proj_data_info_sptr2(ProjDataInfo::ProjDataInfoCTI( + scanner_sptr, + /*span*/ 1, 8, /*views*/ 96, /*tang_pos*/ 64, /*arc_corrected*/ true, proj_data.get_tof_mash_factor())); // construct without filling ProjDataInMemory proj_data2(exam_info_sptr, proj_data_info_sptr2, false); proj_data2.fill(proj_data); - check_if_equal(proj_data2.get_viewgram(0,0,false,-2).find_max(), - proj_data.get_viewgram(0,0,false,-2).find_max(), + check_if_equal(proj_data2.get_viewgram(0, 0, false, -2).find_max(), proj_data.get_viewgram(0, 0, false, -2).find_max(), "test 1 for constructor, fill and get_viewgram(0,0,-2)"); - check_if_equal(proj_data2.get_viewgram(1,1,false,2).find_max(), - proj_data.get_viewgram(1,1,false,2).find_max(), + check_if_equal(proj_data2.get_viewgram(1, 1, false, 2).find_max(), proj_data.get_viewgram(1, 1, false, 2).find_max(), "test 1 for constructor, fill and get_viewgram(1,1,2)"); - } // test fill with smaller input { - shared_ptr proj_data_info_sptr2 - (ProjDataInfo::ProjDataInfoCTI(scanner_sptr, - /*span*/1, 20,/*views*/ 96, /*tang_pos*/64, /*arc_corrected*/ true, 70) - ); + shared_ptr proj_data_info_sptr2(ProjDataInfo::ProjDataInfoCTI(scanner_sptr, + /*span*/ 1, 20, /*views*/ 96, /*tang_pos*/ 64, + /*arc_corrected*/ true, 70)); // construct without filling ProjDataInMemory proj_data2(exam_info_sptr, proj_data_info_sptr2, false); // this should call error, so we'll catch it - try - { - std::cout << "\nthis test should throw an error (which we will catch)\n"; - proj_data2.fill(proj_data); - check(false, "test fill with too small proj_data should have thrown"); - } - catch (...) - { - // ok - } - + try { + std::cout << "\nthis test should throw an error (which we will catch)\n"; + proj_data2.fill(proj_data); + check(false, "test fill with too small proj_data should have thrown"); + } catch (...) { + // ok + } } } END_NAMESPACE_STIR - USING_NAMESPACE_STIR -int main() -{ +int +main() { ProjDataInMemoryTests tests; tests.run_tests(); return tests.main_return_value(); diff --git a/src/test/test_proj_data_info.cxx b/src/test/test_proj_data_info.cxx index 9c7e12e103..8afe8f52e5 100644 --- a/src/test/test_proj_data_info.cxx +++ b/src/test/test_proj_data_info.cxx @@ -57,35 +57,32 @@ using std::size_t; START_NAMESPACE_STIR -static inline -int intabs(const int x) -{ return x>=0?x:-x; } +static inline int +intabs(const int x) { + return x >= 0 ? x : -x; +} // prints a michelogram to the screen #if 1 // TODO move somewhere else -void michelogram(const ProjDataInfoCylindrical& proj_data_info) -{ +void +michelogram(const ProjDataInfoCylindrical& proj_data_info) { cerr << '{'; - for (int ring1=0; ring1get_num_rings(); ++ring1) - { + for (int ring1 = 0; ring1 < proj_data_info.get_scanner_ptr()->get_num_rings(); ++ring1) { cerr << '{'; - for (int ring2=0; ring2get_num_rings(); ++ring2) - { - int segment_num=0; + for (int ring2 = 0; ring2 < proj_data_info.get_scanner_ptr()->get_num_rings(); ++ring2) { + int segment_num = 0; int ax_pos_num = 0; - if (proj_data_info.get_segment_axial_pos_num_for_ring_pair(segment_num, ax_pos_num, ring1, ring2) - == Succeeded::yes) + if (proj_data_info.get_segment_axial_pos_num_for_ring_pair(segment_num, ax_pos_num, ring1, ring2) == Succeeded::yes) cerr << '{' << setw(3) << segment_num << ',' << setw(2) << ax_pos_num << "}"; else cerr << "{} "; - if (ring2 != proj_data_info.get_scanner_ptr()->get_num_rings()-1) + if (ring2 != proj_data_info.get_scanner_ptr()->get_num_rings() - 1) cerr << ','; - } cerr << '}'; - if (ring1 != proj_data_info.get_scanner_ptr()->get_num_rings()-1) - cerr << ','; + if (ring1 != proj_data_info.get_scanner_ptr()->get_num_rings() - 1) + cerr << ','; cerr << endl; } cerr << '}' << endl; @@ -96,269 +93,216 @@ void michelogram(const ProjDataInfoCylindrical& proj_data_info) \ingroup test \brief Test class for ProjDataInfo */ -class ProjDataInfoTests: public RunTests -{ -protected: +class ProjDataInfoTests : public RunTests { +protected: void test_generic_proj_data_info(ProjDataInfo& proj_data_info); }; void -ProjDataInfoTests:: -test_generic_proj_data_info(ProjDataInfo& proj_data_info) -{ +ProjDataInfoTests::test_generic_proj_data_info(ProjDataInfo& proj_data_info) { cerr << "\tTests on get_min/max_num\n"; check_if_equal(proj_data_info.get_max_tangential_pos_num() - proj_data_info.get_min_tangential_pos_num() + 1, - proj_data_info.get_num_tangential_poss(), - "basic check on min/max/num_tangential_pos_num"); - check(abs(proj_data_info.get_max_tangential_pos_num() + proj_data_info.get_min_tangential_pos_num()) <=1, - "check on min/max_tangential_pos_num being (almost) centred"); + proj_data_info.get_num_tangential_poss(), "basic check on min/max/num_tangential_pos_num"); + check(abs(proj_data_info.get_max_tangential_pos_num() + proj_data_info.get_min_tangential_pos_num()) <= 1, + "check on min/max_tangential_pos_num being (almost) centred"); check_if_equal(proj_data_info.get_max_tof_pos_num() - proj_data_info.get_min_tof_pos_num() + 1, - proj_data_info.get_num_tof_poss(), - "basic check on min/max/num_tof_pos_num"); + proj_data_info.get_num_tof_poss(), "basic check on min/max/num_tof_pos_num"); check_if_equal(proj_data_info.get_max_tof_pos_num() + proj_data_info.get_min_tof_pos_num(), 0, - "check on min/max_tof_pos_num being (almost) centred"); - check_if_equal(proj_data_info.get_max_view_num() - proj_data_info.get_min_view_num() + 1, - proj_data_info.get_num_views(), - "basic check on min/max/num_view_num"); + "check on min/max_tof_pos_num being (almost) centred"); + check_if_equal(proj_data_info.get_max_view_num() - proj_data_info.get_min_view_num() + 1, proj_data_info.get_num_views(), + "basic check on min/max/num_view_num"); check_if_equal(proj_data_info.get_max_segment_num() - proj_data_info.get_min_segment_num() + 1, - proj_data_info.get_num_segments(), - "basic check on min/max/num_segment_num"); + proj_data_info.get_num_segments(), "basic check on min/max/num_segment_num"); // not strictly necessary in most of the code, but most likely required in some of it check_if_equal(proj_data_info.get_max_segment_num() + proj_data_info.get_min_segment_num(), 0, - "check on min/max_segment_num being centred"); + "check on min/max_segment_num being centred"); check_if_equal(proj_data_info.get_max_axial_pos_num(0) - proj_data_info.get_min_axial_pos_num(0) + 1, - proj_data_info.get_num_axial_poss(0), - "basic check on min/max/num_axial_pos_num"); + proj_data_info.get_num_axial_poss(0), "basic check on min/max/num_axial_pos_num"); cerr << "\tTests on get_LOR/get_bin\n"; - int max_diff_segment_num=0; - int max_diff_view_num=0; - int max_diff_axial_pos_num=0; - int max_diff_tangential_pos_num=0; + int max_diff_segment_num = 0; + int max_diff_view_num = 0; + int max_diff_axial_pos_num = 0; + int max_diff_tangential_pos_num = 0; int max_diff_timing_pos_num = 0; #ifdef STIR_OPENMP -#pragma omp parallel for schedule(dynamic) +# pragma omp parallel for schedule(dynamic) #endif - for (int segment_num=proj_data_info.get_min_segment_num(); - segment_num<=proj_data_info.get_max_segment_num(); - ++segment_num) - { - for (int view_num=proj_data_info.get_min_view_num(); - view_num<=proj_data_info.get_max_view_num(); - view_num+=3) - { - // loop over axial_positions. Avoid using first and last positions, as - // if there is axial compression, the central LOR of a bin might actually not - // fall within the scanner. In this case, the get_bin(get_LOR(org_bin)) code - // will return an out-of-range bin (i.e. value<0). - int axial_pos_num_margin=0; - const ProjDataInfoCylindrical* const proj_data_info_cyl_ptr = - dynamic_cast(&proj_data_info); - if (proj_data_info_cyl_ptr!=0) - { - axial_pos_num_margin = - std::max( - round(ceil(proj_data_info_cyl_ptr->get_average_ring_difference(segment_num) - - proj_data_info_cyl_ptr->get_min_ring_difference(segment_num))), - round(ceil(proj_data_info_cyl_ptr->get_max_ring_difference(segment_num) - - proj_data_info_cyl_ptr->get_average_ring_difference(segment_num)))); - } - for (int axial_pos_num=proj_data_info.get_min_axial_pos_num(segment_num)+axial_pos_num_margin ; - axial_pos_num<=proj_data_info.get_max_axial_pos_num(segment_num)-axial_pos_num_margin; - axial_pos_num+=3) - { - for (int tangential_pos_num=proj_data_info.get_min_tangential_pos_num()+1; - tangential_pos_num<=proj_data_info.get_max_tangential_pos_num()-1; - tangential_pos_num+=1) - { - for (int timing_pos_num = proj_data_info.get_min_tof_pos_num(); - timing_pos_num <= proj_data_info.get_max_tof_pos_num(); - timing_pos_num += std::max(1,(proj_data_info.get_max_tof_pos_num() - proj_data_info.get_min_tof_pos_num()) / 2))// take 3 or 1 steps, always going through 0 - { - const Bin org_bin(segment_num,view_num,axial_pos_num,tangential_pos_num, timing_pos_num, /* value*/1.f); - const double delta_time = proj_data_info.get_tof_delta_time(org_bin); - LORInAxialAndNoArcCorrSinogramCoordinates lor; - proj_data_info.get_LOR(lor, org_bin); - { - const Bin new_bin = proj_data_info.get_bin(lor, delta_time); -#if STIR_TOF_DEBUG>1 - std::cerr << "T:" << org_bin.timing_pos_num() << ", swapped=" << lor.is_swapped() - << ", z1=" << lor.z1() << ", z2=" << lor.z2() << ", phi=" << lor.phi() << ", beta=" << lor.beta() << std::endl; + for (int segment_num = proj_data_info.get_min_segment_num(); segment_num <= proj_data_info.get_max_segment_num(); + ++segment_num) { + for (int view_num = proj_data_info.get_min_view_num(); view_num <= proj_data_info.get_max_view_num(); view_num += 3) { + // loop over axial_positions. Avoid using first and last positions, as + // if there is axial compression, the central LOR of a bin might actually not + // fall within the scanner. In this case, the get_bin(get_LOR(org_bin)) code + // will return an out-of-range bin (i.e. value<0). + int axial_pos_num_margin = 0; + const ProjDataInfoCylindrical* const proj_data_info_cyl_ptr = + dynamic_cast(&proj_data_info); + if (proj_data_info_cyl_ptr != 0) { + axial_pos_num_margin = std::max(round(ceil(proj_data_info_cyl_ptr->get_average_ring_difference(segment_num) - + proj_data_info_cyl_ptr->get_min_ring_difference(segment_num))), + round(ceil(proj_data_info_cyl_ptr->get_max_ring_difference(segment_num) - + proj_data_info_cyl_ptr->get_average_ring_difference(segment_num)))); + } + for (int axial_pos_num = proj_data_info.get_min_axial_pos_num(segment_num) + axial_pos_num_margin; + axial_pos_num <= proj_data_info.get_max_axial_pos_num(segment_num) - axial_pos_num_margin; axial_pos_num += 3) { + for (int tangential_pos_num = proj_data_info.get_min_tangential_pos_num() + 1; + tangential_pos_num <= proj_data_info.get_max_tangential_pos_num() - 1; tangential_pos_num += 1) { + for (int timing_pos_num = proj_data_info.get_min_tof_pos_num(); timing_pos_num <= proj_data_info.get_max_tof_pos_num(); + timing_pos_num += std::max(1, (proj_data_info.get_max_tof_pos_num() - proj_data_info.get_min_tof_pos_num()) / + 2)) // take 3 or 1 steps, always going through 0 + { + const Bin org_bin(segment_num, view_num, axial_pos_num, tangential_pos_num, timing_pos_num, /* value*/ 1.f); + const double delta_time = proj_data_info.get_tof_delta_time(org_bin); + LORInAxialAndNoArcCorrSinogramCoordinates lor; + proj_data_info.get_LOR(lor, org_bin); + { + const Bin new_bin = proj_data_info.get_bin(lor, delta_time); +#if STIR_TOF_DEBUG > 1 + std::cerr << "T:" << org_bin.timing_pos_num() << ", swapped=" << lor.is_swapped() << ", z1=" << lor.z1() + << ", z2=" << lor.z2() << ", phi=" << lor.phi() << ", beta=" << lor.beta() << std::endl; #endif #if 1 - const int diff_segment_num = - intabs(org_bin.segment_num() - new_bin.segment_num()); - const int diff_view_num = - intabs(org_bin.view_num() - new_bin.view_num()); - const int diff_axial_pos_num = - intabs(org_bin.axial_pos_num() - new_bin.axial_pos_num()); - const int diff_tangential_pos_num = - intabs(org_bin.tangential_pos_num() - new_bin.tangential_pos_num()); - const int diff_timing_pos_num = - intabs(org_bin.timing_pos_num() - new_bin.timing_pos_num()); - if (new_bin.get_bin_value()>0) - { - if (diff_segment_num>max_diff_segment_num) - max_diff_segment_num=diff_segment_num; - if (diff_view_num>max_diff_view_num) - max_diff_view_num=diff_view_num; - if (diff_axial_pos_num>max_diff_axial_pos_num) - max_diff_axial_pos_num=diff_axial_pos_num; - if (diff_tangential_pos_num>max_diff_tangential_pos_num) - max_diff_tangential_pos_num=diff_tangential_pos_num; - if (diff_timing_pos_num>max_diff_timing_pos_num) - max_diff_timing_pos_num = diff_timing_pos_num; - } - if (!check(org_bin.get_bin_value() == new_bin.get_bin_value(), "round-trip get_LOR then get_bin: value") || - !check(diff_segment_num<=0, "round-trip get_LOR then get_bin: segment") || - !check(diff_view_num<=1, "round-trip get_LOR then get_bin: view") || - !check(diff_axial_pos_num<=1, "round-trip get_LOR then get_bin: axial_pos") || - !check(diff_tangential_pos_num<=1, "round-trip get_LOR then get_bin: tangential_pos") || - !check(diff_timing_pos_num == 0, "round-trip get_LOR then get_bin: timing_pos")) + const int diff_segment_num = intabs(org_bin.segment_num() - new_bin.segment_num()); + const int diff_view_num = intabs(org_bin.view_num() - new_bin.view_num()); + const int diff_axial_pos_num = intabs(org_bin.axial_pos_num() - new_bin.axial_pos_num()); + const int diff_tangential_pos_num = intabs(org_bin.tangential_pos_num() - new_bin.tangential_pos_num()); + const int diff_timing_pos_num = intabs(org_bin.timing_pos_num() - new_bin.timing_pos_num()); + if (new_bin.get_bin_value() > 0) { + if (diff_segment_num > max_diff_segment_num) + max_diff_segment_num = diff_segment_num; + if (diff_view_num > max_diff_view_num) + max_diff_view_num = diff_view_num; + if (diff_axial_pos_num > max_diff_axial_pos_num) + max_diff_axial_pos_num = diff_axial_pos_num; + if (diff_tangential_pos_num > max_diff_tangential_pos_num) + max_diff_tangential_pos_num = diff_tangential_pos_num; + if (diff_timing_pos_num > max_diff_timing_pos_num) + max_diff_timing_pos_num = diff_timing_pos_num; + } + if (!check(org_bin.get_bin_value() == new_bin.get_bin_value(), "round-trip get_LOR then get_bin: value") || + !check(diff_segment_num <= 0, "round-trip get_LOR then get_bin: segment") || + !check(diff_view_num <= 1, "round-trip get_LOR then get_bin: view") || + !check(diff_axial_pos_num <= 1, "round-trip get_LOR then get_bin: axial_pos") || + !check(diff_tangential_pos_num <= 1, "round-trip get_LOR then get_bin: tangential_pos") || + !check(diff_timing_pos_num == 0, "round-trip get_LOR then get_bin: timing_pos")) #else - if (!check(org_bin == new_bin, "round-trip get_LOR then get_bin")) + if (!check(org_bin == new_bin, "round-trip get_LOR then get_bin")) #endif - { - cerr << "\tProblem at segment = " << org_bin.segment_num() - << ", axial pos " << org_bin.axial_pos_num() - << ", view = " << org_bin.view_num() - << ", tangential_pos = " << org_bin.tangential_pos_num() - << ", timing_pos = " << org_bin.timing_pos_num() << "\n"; - if (new_bin.get_bin_value()>0) - cerr << "\tround-trip to segment = " << new_bin.segment_num() - << ", axial pos " << new_bin.axial_pos_num() - << ", view = " << new_bin.view_num() - << ", tangential_pos = " << new_bin.tangential_pos_num() - << ", timing_pos = " << new_bin.timing_pos_num() - <<'\n'; - } - } - // repeat test but with different type of LOR - { - LORAs2Points lor_as_points; - lor.get_intersections_with_cylinder(lor_as_points, lor.radius()); -#if STIR_TOF_DEBUG>1 - std::cerr - << " z1=" << lor_as_points.p1().z() << ", y1=" << lor_as_points.p1().y() << ", x1=" << lor_as_points.p1().x() - << "\n z2=" << lor_as_points.p2().z() << ", y2=" << lor_as_points.p2().y() << ", x2=" << lor_as_points.p2().x() - << std::endl; + { + cerr << "\tProblem at segment = " << org_bin.segment_num() << ", axial pos " << org_bin.axial_pos_num() + << ", view = " << org_bin.view_num() << ", tangential_pos = " << org_bin.tangential_pos_num() + << ", timing_pos = " << org_bin.timing_pos_num() << "\n"; + if (new_bin.get_bin_value() > 0) + cerr << "\tround-trip to segment = " << new_bin.segment_num() << ", axial pos " << new_bin.axial_pos_num() + << ", view = " << new_bin.view_num() << ", tangential_pos = " << new_bin.tangential_pos_num() + << ", timing_pos = " << new_bin.timing_pos_num() << '\n'; + } + } + // repeat test but with different type of LOR + { + LORAs2Points lor_as_points; + lor.get_intersections_with_cylinder(lor_as_points, lor.radius()); +#if STIR_TOF_DEBUG > 1 + std::cerr << " z1=" << lor_as_points.p1().z() << ", y1=" << lor_as_points.p1().y() + << ", x1=" << lor_as_points.p1().x() << "\n z2=" << lor_as_points.p2().z() + << ", y2=" << lor_as_points.p2().y() << ", x2=" << lor_as_points.p2().x() << std::endl; #endif - const Bin new_bin = proj_data_info.get_bin(lor_as_points, proj_data_info.get_tof_delta_time(org_bin)); + const Bin new_bin = proj_data_info.get_bin(lor_as_points, proj_data_info.get_tof_delta_time(org_bin)); #if 1 - const int diff_segment_num = - intabs(org_bin.segment_num() - new_bin.segment_num()); - const int diff_view_num = - intabs(org_bin.view_num() - new_bin.view_num()); - const int diff_axial_pos_num = - intabs(org_bin.axial_pos_num() - new_bin.axial_pos_num()); - const int diff_tangential_pos_num = - intabs(org_bin.tangential_pos_num() - new_bin.tangential_pos_num()); - const int diff_timing_pos_num = - intabs(org_bin.timing_pos_num() - new_bin.timing_pos_num()); - if (new_bin.get_bin_value()>0) - { - if (diff_segment_num>max_diff_segment_num) - max_diff_segment_num=diff_segment_num; - if (diff_view_num>max_diff_view_num) - max_diff_view_num=diff_view_num; - if (diff_axial_pos_num>max_diff_axial_pos_num) - max_diff_axial_pos_num=diff_axial_pos_num; - if (diff_tangential_pos_num>max_diff_tangential_pos_num) - max_diff_tangential_pos_num=diff_tangential_pos_num; - if (diff_timing_pos_num>max_diff_timing_pos_num) - max_diff_timing_pos_num = diff_timing_pos_num; - } - if (!check(org_bin.get_bin_value() == new_bin.get_bin_value(), "round-trip get_LOR then get_bin (LORAs2Points): value") || - !check(diff_segment_num<=0, "round-trip get_LOR then get_bin (LORAs2Points): segment") || - !check(diff_view_num<=1, "round-trip get_LOR then get_bin (LORAs2Points): view") || - !check(diff_axial_pos_num<=1, "round-trip get_LOR then get_bin (LORAs2Points): axial_pos") || - !check(diff_tangential_pos_num<=1, "round-trip get_LOR then get_bin (LORAs2Points): tangential_pos") || - !check(diff_timing_pos_num == 0, "round-trip get_LOR then get_bin (LORAs2Points): timing_pos")) - + const int diff_segment_num = intabs(org_bin.segment_num() - new_bin.segment_num()); + const int diff_view_num = intabs(org_bin.view_num() - new_bin.view_num()); + const int diff_axial_pos_num = intabs(org_bin.axial_pos_num() - new_bin.axial_pos_num()); + const int diff_tangential_pos_num = intabs(org_bin.tangential_pos_num() - new_bin.tangential_pos_num()); + const int diff_timing_pos_num = intabs(org_bin.timing_pos_num() - new_bin.timing_pos_num()); + if (new_bin.get_bin_value() > 0) { + if (diff_segment_num > max_diff_segment_num) + max_diff_segment_num = diff_segment_num; + if (diff_view_num > max_diff_view_num) + max_diff_view_num = diff_view_num; + if (diff_axial_pos_num > max_diff_axial_pos_num) + max_diff_axial_pos_num = diff_axial_pos_num; + if (diff_tangential_pos_num > max_diff_tangential_pos_num) + max_diff_tangential_pos_num = diff_tangential_pos_num; + if (diff_timing_pos_num > max_diff_timing_pos_num) + max_diff_timing_pos_num = diff_timing_pos_num; + } + if (!check(org_bin.get_bin_value() == new_bin.get_bin_value(), + "round-trip get_LOR then get_bin (LORAs2Points): value") || + !check(diff_segment_num <= 0, "round-trip get_LOR then get_bin (LORAs2Points): segment") || + !check(diff_view_num <= 1, "round-trip get_LOR then get_bin (LORAs2Points): view") || + !check(diff_axial_pos_num <= 1, "round-trip get_LOR then get_bin (LORAs2Points): axial_pos") || + !check(diff_tangential_pos_num <= 1, "round-trip get_LOR then get_bin (LORAs2Points): tangential_pos") || + !check(diff_timing_pos_num == 0, "round-trip get_LOR then get_bin (LORAs2Points): timing_pos")) + #else - if (!check(org_bin == new_bin, "round-trip get_LOR then get_bin")) + if (!check(org_bin == new_bin, "round-trip get_LOR then get_bin")) #endif - { - cerr << "\tProblem at segment = " << org_bin.segment_num() - << ", axial pos " << org_bin.axial_pos_num() - << ", view = " << org_bin.view_num() - << ", tangential_pos_num = " << org_bin.tangential_pos_num() - << ", timing_pos = " << org_bin.timing_pos_num() << "\n"; - if (new_bin.get_bin_value()>0) - cerr << "\tround-trip to segment = " << new_bin.segment_num() - << ", axial pos " << new_bin.axial_pos_num() - << ", view = " << new_bin.view_num() - << ", tangential_pos_num = " << new_bin.tangential_pos_num() - << ", timing_pos = " << new_bin.timing_pos_num() - <<'\n'; - } - } - } - } - } - } + { + cerr << "\tProblem at segment = " << org_bin.segment_num() << ", axial pos " << org_bin.axial_pos_num() + << ", view = " << org_bin.view_num() << ", tangential_pos_num = " << org_bin.tangential_pos_num() + << ", timing_pos = " << org_bin.timing_pos_num() << "\n"; + if (new_bin.get_bin_value() > 0) + cerr << "\tround-trip to segment = " << new_bin.segment_num() << ", axial pos " << new_bin.axial_pos_num() + << ", view = " << new_bin.view_num() << ", tangential_pos_num = " << new_bin.tangential_pos_num() + << ", timing_pos = " << new_bin.timing_pos_num() << '\n'; + } + } + } + } } - cerr << "Max Deviation: segment = " << max_diff_segment_num - << ", axial pos " << max_diff_axial_pos_num - << ", view = " << max_diff_view_num - << ", tangential_pos_num = " << max_diff_tangential_pos_num - << ", timing_pos_num = " << max_diff_timing_pos_num << "\n"; - - // test on reduce_segment_range and operator>= - { - shared_ptr smaller(proj_data_info.clone()); - check(proj_data_info >= *smaller, "check on operator>= and equal objects"); - smaller->set_min_tangential_pos_num(0); - check(proj_data_info >= *smaller, "check on tangential_pos and operator>="); - smaller->set_min_axial_pos_num(4, 0); - check(proj_data_info >= *smaller, "check on axial_pos and operator>="); - // if (proj_data_info.is_tof_data()) - // { - // smaller->set_min_timing_pos_num(0); - // check(proj_data_info >= *smaller, "check on timing_pos and operator>="); - // } - smaller->reduce_segment_range(0,0); - check(proj_data_info >= *smaller, "check on reduce_segment_range and operator>="); - // make one range larger, so should now fail - smaller->set_min_tangential_pos_num(proj_data_info.get_min_tangential_pos_num() - 4); - check(!(proj_data_info >= *smaller), "check on mixed case with tangential_pos_num and operator>="); - // reset and do the same for axial pos - smaller->set_min_tangential_pos_num(proj_data_info.get_min_tangential_pos_num() + 4); - check(proj_data_info >= *smaller, "check on reduced segments and tangential_pos and operator>="); - smaller->set_max_axial_pos_num(proj_data_info.get_max_axial_pos_num(0)+4, 0); - check(!(proj_data_info >= *smaller), "check on mixed case with axial_pos_num and operator>="); } -} + } + cerr << "Max Deviation: segment = " << max_diff_segment_num << ", axial pos " << max_diff_axial_pos_num + << ", view = " << max_diff_view_num << ", tangential_pos_num = " << max_diff_tangential_pos_num + << ", timing_pos_num = " << max_diff_timing_pos_num << "\n"; + // test on reduce_segment_range and operator>= + { + shared_ptr smaller(proj_data_info.clone()); + check(proj_data_info >= *smaller, "check on operator>= and equal objects"); + smaller->set_min_tangential_pos_num(0); + check(proj_data_info >= *smaller, "check on tangential_pos and operator>="); + smaller->set_min_axial_pos_num(4, 0); + check(proj_data_info >= *smaller, "check on axial_pos and operator>="); + // if (proj_data_info.is_tof_data()) + // { + // smaller->set_min_timing_pos_num(0); + // check(proj_data_info >= *smaller, "check on timing_pos and operator>="); + // } + smaller->reduce_segment_range(0, 0); + check(proj_data_info >= *smaller, "check on reduce_segment_range and operator>="); + // make one range larger, so should now fail + smaller->set_min_tangential_pos_num(proj_data_info.get_min_tangential_pos_num() - 4); + check(!(proj_data_info >= *smaller), "check on mixed case with tangential_pos_num and operator>="); + // reset and do the same for axial pos + smaller->set_min_tangential_pos_num(proj_data_info.get_min_tangential_pos_num() + 4); + check(proj_data_info >= *smaller, "check on reduced segments and tangential_pos and operator>="); + smaller->set_max_axial_pos_num(proj_data_info.get_max_axial_pos_num(0) + 4, 0); + check(!(proj_data_info >= *smaller), "check on mixed case with axial_pos_num and operator>="); + } +} /*! \ingroup test \brief Test class for ProjDataInfoCylindrical */ -class ProjDataInfoCylindricalTests: public ProjDataInfoTests -{ -protected: +class ProjDataInfoCylindricalTests : public ProjDataInfoTests { +protected: void test_cylindrical_proj_data_info(ProjDataInfoCylindrical& proj_data_info); }; - void -ProjDataInfoCylindricalTests:: -test_cylindrical_proj_data_info(ProjDataInfoCylindrical& proj_data_info) -{ +ProjDataInfoCylindricalTests::test_cylindrical_proj_data_info(ProjDataInfoCylindrical& proj_data_info) { cerr << "\tTesting consistency between different implementations of geometric info\n"; { - const Bin bin(proj_data_info.get_max_segment_num(), - 1, - proj_data_info.get_max_axial_pos_num(proj_data_info.get_max_segment_num())/2, - 1); - check_if_equal(proj_data_info.get_sampling_in_m(bin), - proj_data_info.ProjDataInfo::get_sampling_in_m(bin), - "test consistency get_sampling_in_m"); - check_if_equal(proj_data_info.get_sampling_in_t(bin), - proj_data_info.ProjDataInfo::get_sampling_in_t(bin), - "test consistency get_sampling_in_t"); + const Bin bin(proj_data_info.get_max_segment_num(), 1, + proj_data_info.get_max_axial_pos_num(proj_data_info.get_max_segment_num()) / 2, 1); + check_if_equal(proj_data_info.get_sampling_in_m(bin), proj_data_info.ProjDataInfo::get_sampling_in_m(bin), + "test consistency get_sampling_in_m"); + check_if_equal(proj_data_info.get_sampling_in_t(bin), proj_data_info.ProjDataInfo::get_sampling_in_t(bin), + "test consistency get_sampling_in_t"); #if 0 // ProjDataInfo has no default implementation for get_tantheta // I just leave the code here to make this explicit @@ -366,125 +310,87 @@ test_cylindrical_proj_data_info(ProjDataInfoCylindrical& proj_data_info) proj_data_info.ProjDataInfo::get_tantheta(bin), "test consistency get_tantheta"); #endif - check_if_equal(proj_data_info.get_costheta(bin), - proj_data_info.ProjDataInfo::get_costheta(bin), - "test consistency get_costheta"); + check_if_equal(proj_data_info.get_costheta(bin), proj_data_info.ProjDataInfo::get_costheta(bin), + "test consistency get_costheta"); - check_if_equal(proj_data_info.get_costheta(bin), - cos(atan(proj_data_info.get_tantheta(bin))), - "cross check get_costheta and get_tantheta"); + check_if_equal(proj_data_info.get_costheta(bin), cos(atan(proj_data_info.get_tantheta(bin))), + "cross check get_costheta and get_tantheta"); // try the same with a non-standard ring spacing const float old_ring_spacing = proj_data_info.get_ring_spacing(); proj_data_info.set_ring_spacing(2.1F); - check_if_equal(proj_data_info.get_sampling_in_m(bin), - proj_data_info.ProjDataInfo::get_sampling_in_m(bin), - "test consistency get_sampling_in_m"); - check_if_equal(proj_data_info.get_sampling_in_t(bin), - proj_data_info.ProjDataInfo::get_sampling_in_t(bin), - "test consistency get_sampling_in_t"); + check_if_equal(proj_data_info.get_sampling_in_m(bin), proj_data_info.ProjDataInfo::get_sampling_in_m(bin), + "test consistency get_sampling_in_m"); + check_if_equal(proj_data_info.get_sampling_in_t(bin), proj_data_info.ProjDataInfo::get_sampling_in_t(bin), + "test consistency get_sampling_in_t"); #if 0 check_if_equal(proj_data_info.get_tantheta(bin), proj_data_info.ProjDataInfo::get_tantheta(bin), "test consistency get_tantheta"); #endif - check_if_equal(proj_data_info.get_costheta(bin), - proj_data_info.ProjDataInfo::get_costheta(bin), - "test consistency get_costheta"); + check_if_equal(proj_data_info.get_costheta(bin), proj_data_info.ProjDataInfo::get_costheta(bin), + "test consistency get_costheta"); - check_if_equal(proj_data_info.get_costheta(bin), - cos(atan(proj_data_info.get_tantheta(bin))), - "cross check get_costheta and get_tantheta"); + check_if_equal(proj_data_info.get_costheta(bin), cos(atan(proj_data_info.get_tantheta(bin))), + "cross check get_costheta and get_tantheta"); // set back to usual value proj_data_info.set_ring_spacing(old_ring_spacing); } - if (proj_data_info.get_max_ring_difference(0) == proj_data_info.get_min_ring_difference(0) - && proj_data_info.get_max_ring_difference(1) == proj_data_info.get_min_ring_difference(1) - && proj_data_info.get_max_ring_difference(2) == proj_data_info.get_min_ring_difference(2) - ) - { - // these tests work only without axial compression - cerr << "\tTest ring pair to segment,ax_pos (span 1)\n"; + if (proj_data_info.get_max_ring_difference(0) == proj_data_info.get_min_ring_difference(0) && + proj_data_info.get_max_ring_difference(1) == proj_data_info.get_min_ring_difference(1) && + proj_data_info.get_max_ring_difference(2) == proj_data_info.get_min_ring_difference(2)) { + // these tests work only without axial compression + cerr << "\tTest ring pair to segment,ax_pos (span 1)\n"; #ifdef STIR_OPENMP -#pragma omp parallel for schedule(dynamic) +# pragma omp parallel for schedule(dynamic) #endif - for (int ring1=0; ring1get_num_rings(); ++ring1) - for (int ring2=0; ring2get_num_rings(); ++ring2) - { - int segment_num = 0, axial_pos_num = 0; - check(proj_data_info. - get_segment_axial_pos_num_for_ring_pair(segment_num, - axial_pos_num, - ring1, - ring2) == - Succeeded::yes, - "test if segment,ax_pos_num found for a ring pair"); - check_if_equal(segment_num, ring2-ring1, - "test if segment_num is equal to ring difference\n"); - check_if_equal(axial_pos_num, min(ring2,ring1), - "test if segment_num is equal to ring difference\n"); - - int check_ring1 = 0, check_ring2 = 0; - proj_data_info. - get_ring_pair_for_segment_axial_pos_num(check_ring1, - check_ring2, - segment_num, - axial_pos_num); - check_if_equal(ring1, check_ring1, - "test ring1 equal after going to segment/ax_pos and returning\n"); - check_if_equal(ring2, check_ring2, - "test ring2 equal after going to segment/ax_pos and returning\n"); - - const ProjDataInfoCylindrical::RingNumPairs& ring_pairs = - proj_data_info. - get_all_ring_pairs_for_segment_axial_pos_num(segment_num, - axial_pos_num); - - check_if_equal(ring_pairs.size(), static_cast(1), - "test total number of ring-pairs for 1 segment/ax_pos should be 1 for span=1\n"); - check_if_equal(ring1, ring_pairs[0].first, - "test ring1 equal after going to segment/ax_pos and returning (version with all ring_pairs)\n"); - check_if_equal(ring2, ring_pairs[0].second, - "test ring2 equal after going to segment/ax_pos and returning (version with all ring_pairs)\n"); - } + for (int ring1 = 0; ring1 < proj_data_info.get_scanner_ptr()->get_num_rings(); ++ring1) + for (int ring2 = 0; ring2 < proj_data_info.get_scanner_ptr()->get_num_rings(); ++ring2) { + int segment_num = 0, axial_pos_num = 0; + check(proj_data_info.get_segment_axial_pos_num_for_ring_pair(segment_num, axial_pos_num, ring1, ring2) == Succeeded::yes, + "test if segment,ax_pos_num found for a ring pair"); + check_if_equal(segment_num, ring2 - ring1, "test if segment_num is equal to ring difference\n"); + check_if_equal(axial_pos_num, min(ring2, ring1), "test if segment_num is equal to ring difference\n"); + + int check_ring1 = 0, check_ring2 = 0; + proj_data_info.get_ring_pair_for_segment_axial_pos_num(check_ring1, check_ring2, segment_num, axial_pos_num); + check_if_equal(ring1, check_ring1, "test ring1 equal after going to segment/ax_pos and returning\n"); + check_if_equal(ring2, check_ring2, "test ring2 equal after going to segment/ax_pos and returning\n"); + + const ProjDataInfoCylindrical::RingNumPairs& ring_pairs = + proj_data_info.get_all_ring_pairs_for_segment_axial_pos_num(segment_num, axial_pos_num); + + check_if_equal(ring_pairs.size(), static_cast(1), + "test total number of ring-pairs for 1 segment/ax_pos should be 1 for span=1\n"); + check_if_equal(ring1, ring_pairs[0].first, + "test ring1 equal after going to segment/ax_pos and returning (version with all ring_pairs)\n"); + check_if_equal(ring2, ring_pairs[0].second, + "test ring2 equal after going to segment/ax_pos and returning (version with all ring_pairs)\n"); + } } cerr << "\tTest ring pair to segment,ax_pos and vice versa (for any axial compression)\n"; { #ifdef STIR_OPENMP -#pragma omp parallel for schedule(dynamic) +# pragma omp parallel for schedule(dynamic) #endif - for (int segment_num=proj_data_info.get_min_segment_num(); - segment_num<=proj_data_info.get_max_segment_num(); - ++segment_num) - for (int axial_pos_num=proj_data_info.get_min_axial_pos_num(segment_num); - axial_pos_num<=proj_data_info.get_max_axial_pos_num(segment_num); - ++axial_pos_num) - { - const ProjDataInfoCylindrical::RingNumPairs& ring_pairs = - proj_data_info. - get_all_ring_pairs_for_segment_axial_pos_num(segment_num, - axial_pos_num); - for (ProjDataInfoCylindrical::RingNumPairs::const_iterator iter = ring_pairs.begin(); - iter != ring_pairs.end(); - ++iter) - { - int check_segment_num = 0, check_axial_pos_num = 0; - check(proj_data_info. - get_segment_axial_pos_num_for_ring_pair(check_segment_num, - check_axial_pos_num, - iter->first, - iter->second) == - Succeeded::yes, - "test if segment,ax_pos_num found for a ring pair"); - check_if_equal(check_segment_num, segment_num, - "test if segment_num is consistent\n"); - check_if_equal(check_axial_pos_num, axial_pos_num, - "test if axial_pos_num is consistent\n"); - } - } + for (int segment_num = proj_data_info.get_min_segment_num(); segment_num <= proj_data_info.get_max_segment_num(); + ++segment_num) + for (int axial_pos_num = proj_data_info.get_min_axial_pos_num(segment_num); + axial_pos_num <= proj_data_info.get_max_axial_pos_num(segment_num); ++axial_pos_num) { + const ProjDataInfoCylindrical::RingNumPairs& ring_pairs = + proj_data_info.get_all_ring_pairs_for_segment_axial_pos_num(segment_num, axial_pos_num); + for (ProjDataInfoCylindrical::RingNumPairs::const_iterator iter = ring_pairs.begin(); iter != ring_pairs.end(); ++iter) { + int check_segment_num = 0, check_axial_pos_num = 0; + check(proj_data_info.get_segment_axial_pos_num_for_ring_pair(check_segment_num, check_axial_pos_num, iter->first, + iter->second) == Succeeded::yes, + "test if segment,ax_pos_num found for a ring pair"); + check_if_equal(check_segment_num, segment_num, "test if segment_num is consistent\n"); + check_if_equal(check_axial_pos_num, axial_pos_num, "test if axial_pos_num is consistent\n"); + } + } } test_generic_proj_data_info(proj_data_info); @@ -495,104 +401,96 @@ test_cylindrical_proj_data_info(ProjDataInfoCylindrical& proj_data_info) \brief Test class for ProjDataInfoCylindricalArcCorr */ -class ProjDataInfoCylindricalArcCorrTests: public ProjDataInfoCylindricalTests -{ -public: +class ProjDataInfoCylindricalArcCorrTests : public ProjDataInfoCylindricalTests { +public: void run_tests(); }; - void ProjDataInfoCylindricalArcCorrTests::run_tests() -{ +{ cerr << "-------- Testing ProjDataInfoCylindricalArcCorr --------\n"; { // Test on the empty constructor - + ProjDataInfoCylindricalArcCorr ob1; - + // Test on set.* & get.* + constructor const float test_tangential_sampling = 1.5; - //const float test_azimuthal_angle_sampling = 10.1; - + // const float test_azimuthal_angle_sampling = 10.1; + ob1.set_tangential_sampling(test_tangential_sampling); // Set_azimuthal_angle_sampling // ob1.set_azimuthal_angle_sampling(test_azimuthal_angle_sampling); - - - check_if_equal( ob1.get_tangential_sampling(), test_tangential_sampling,"test on tangential_sampling"); - //check_if_zero( ob1.get_azimuthal_angle_sampling() - test_azimuthal_angle_sampling, " test on azimuthal_angle_sampling"); - + + check_if_equal(ob1.get_tangential_sampling(), test_tangential_sampling, "test on tangential_sampling"); + // check_if_zero( ob1.get_azimuthal_angle_sampling() - test_azimuthal_angle_sampling, " test on azimuthal_angle_sampling"); } { shared_ptr scanner_ptr(new Scanner(Scanner::E953)); - - VectorWithOffset num_axial_pos_per_segment(-1,1); - VectorWithOffset min_ring_diff(-1,1); - VectorWithOffset max_ring_diff(-1,1); + + VectorWithOffset num_axial_pos_per_segment(-1, 1); + VectorWithOffset min_ring_diff(-1, 1); + VectorWithOffset max_ring_diff(-1, 1); // simulate span=3 for segment 0, span=1 for segment 2 - num_axial_pos_per_segment[-1]=14; - num_axial_pos_per_segment[0]=31; - num_axial_pos_per_segment[1]=14; + num_axial_pos_per_segment[-1] = 14; + num_axial_pos_per_segment[0] = 31; + num_axial_pos_per_segment[1] = 14; // KT 28/11/2001 corrected typo (bug): min_ring_diff[-1] was initialised twice, and max_ring_diff[-1] wasn't min_ring_diff[-1] = max_ring_diff[-1] = -2; - min_ring_diff[ 0] = -1; max_ring_diff[ 0] = 1; + min_ring_diff[0] = -1; + max_ring_diff[0] = 1; min_ring_diff[+1] = max_ring_diff[+1] = +2; const int num_views = 96; const int num_tangential_poss = 128; - + const float bin_size = 1.2F; - - - //Test on the constructor - ProjDataInfoCylindricalArcCorr - ob2(scanner_ptr, bin_size, - num_axial_pos_per_segment, min_ring_diff, max_ring_diff, - num_views,num_tangential_poss); - - check_if_equal( ob2.get_tangential_sampling(), bin_size,"test on tangential_sampling"); - check_if_equal( ob2.get_azimuthal_angle_sampling() , _PI/num_views, " test on azimuthal_angle_sampling"); - check_if_equal( ob2.get_axial_sampling(1), scanner_ptr->get_ring_spacing(), "test on axial_sampling"); - check_if_equal( ob2.get_axial_sampling(0), scanner_ptr->get_ring_spacing()/2, "test on axial_sampling for segment0"); - + + // Test on the constructor + ProjDataInfoCylindricalArcCorr ob2(scanner_ptr, bin_size, num_axial_pos_per_segment, min_ring_diff, max_ring_diff, num_views, + num_tangential_poss); + + check_if_equal(ob2.get_tangential_sampling(), bin_size, "test on tangential_sampling"); + check_if_equal(ob2.get_azimuthal_angle_sampling(), _PI / num_views, " test on azimuthal_angle_sampling"); + check_if_equal(ob2.get_axial_sampling(1), scanner_ptr->get_ring_spacing(), "test on axial_sampling"); + check_if_equal(ob2.get_axial_sampling(0), scanner_ptr->get_ring_spacing() / 2, "test on axial_sampling for segment0"); + { // segment 0 - Bin bin(0,10,10,20); + Bin bin(0, 10, 10, 20); float theta = ob2.get_tantheta(bin); - float phi = ob2.get_phi(bin); + float phi = ob2.get_phi(bin); // Get t float t = ob2.get_t(bin); //! Get s float s = ob2.get_s(bin); - - check_if_equal( theta, 0.F,"test on get_tantheta, seg 0"); - check_if_equal( phi, 10*ob2.get_azimuthal_angle_sampling(), " get_phi , seg 0"); + + check_if_equal(theta, 0.F, "test on get_tantheta, seg 0"); + check_if_equal(phi, 10 * ob2.get_azimuthal_angle_sampling(), " get_phi , seg 0"); // KT 25/10/2000 adjust to new convention - const float ax_pos_origin = - (ob2.get_min_axial_pos_num(0) + ob2.get_max_axial_pos_num(0))/2.F; - check_if_equal( t, (10-ax_pos_origin)*ob2.get_axial_sampling(0) , "get_t, seg 0"); - check_if_equal( s, 20*ob2.get_tangential_sampling() , "get_s, seg 0"); + const float ax_pos_origin = (ob2.get_min_axial_pos_num(0) + ob2.get_max_axial_pos_num(0)) / 2.F; + check_if_equal(t, (10 - ax_pos_origin) * ob2.get_axial_sampling(0), "get_t, seg 0"); + check_if_equal(s, 20 * ob2.get_tangential_sampling(), "get_s, seg 0"); } { // Segment 1 - Bin bin (1,10,10,20); + Bin bin(1, 10, 10, 20); float theta = ob2.get_tantheta(bin); - float phi = ob2.get_phi(bin); + float phi = ob2.get_phi(bin); // Get t float t = ob2.get_t(bin); // Get s float s = ob2.get_s(bin); - - float thetatest = 2*ob2.get_axial_sampling(1)/(2*sqrt(square(scanner_ptr->get_effective_ring_radius())-square(s))); - - check_if_equal( theta, thetatest,"test on get_tantheta, seg 1"); - check_if_equal( phi, 10*ob2.get_azimuthal_angle_sampling(), " get_phi , seg 1"); + + float thetatest = 2 * ob2.get_axial_sampling(1) / (2 * sqrt(square(scanner_ptr->get_effective_ring_radius()) - square(s))); + + check_if_equal(theta, thetatest, "test on get_tantheta, seg 1"); + check_if_equal(phi, 10 * ob2.get_azimuthal_angle_sampling(), " get_phi , seg 1"); // KT 25/10/2000 adjust to new convention - const float ax_pos_origin = - (ob2.get_min_axial_pos_num(1) + ob2.get_max_axial_pos_num(1))/2.F; - check_if_equal( t, (10-ax_pos_origin)/sqrt(1+square(thetatest))*ob2.get_axial_sampling(1) , "get_t, seg 1"); - check_if_equal( s, 20*ob2.get_tangential_sampling() , "get_s, seg 1"); + const float ax_pos_origin = (ob2.get_min_axial_pos_num(1) + ob2.get_max_axial_pos_num(1)) / 2.F; + check_if_equal(t, (10 - ax_pos_origin) / sqrt(1 + square(thetatest)) * ob2.get_axial_sampling(1), "get_t, seg 1"); + check_if_equal(s, 20 * ob2.get_tangential_sampling(), "get_s, seg 1"); } #if 0 @@ -625,435 +523,363 @@ ProjDataInfoCylindricalArcCorrTests::run_tests() } #endif - - shared_ptr scanner_ptr(new Scanner(Scanner::E953)); + shared_ptr scanner_ptr(new Scanner(Scanner::E953)); cerr << "Tests with proj_data_info without mashing and axial compression\n\n"; - // Note: test without axial compression requires that all ring differences + // Note: test without axial compression requires that all ring differences // are in some segment, so use maximum ring difference shared_ptr proj_data_info_ptr( - ProjDataInfo::construct_proj_data_info(scanner_ptr, - /*span*/1, scanner_ptr->get_num_rings()-1, - /*views*/ scanner_ptr->get_num_detectors_per_ring()/2, - /*tang_pos*/64, - /*arc_corrected*/ true)); - test_cylindrical_proj_data_info(dynamic_cast(*proj_data_info_ptr)); + ProjDataInfo::construct_proj_data_info(scanner_ptr, + /*span*/ 1, scanner_ptr->get_num_rings() - 1, + /*views*/ scanner_ptr->get_num_detectors_per_ring() / 2, + /*tang_pos*/ 64, + /*arc_corrected*/ true)); + test_cylindrical_proj_data_info(dynamic_cast(*proj_data_info_ptr)); cerr << "\nTests with proj_data_info with mashing and axial compression (span 5)\n\n"; - proj_data_info_ptr = - ProjDataInfo::construct_proj_data_info(scanner_ptr, - /*span*/5, scanner_ptr->get_num_rings()-1, - /*views*/ scanner_ptr->get_num_detectors_per_ring()/2/8, - /*tang_pos*/64, - /*arc_corrected*/ true); - test_cylindrical_proj_data_info(dynamic_cast(*proj_data_info_ptr)); + proj_data_info_ptr = ProjDataInfo::construct_proj_data_info(scanner_ptr, + /*span*/ 5, scanner_ptr->get_num_rings() - 1, + /*views*/ scanner_ptr->get_num_detectors_per_ring() / 2 / 8, + /*tang_pos*/ 64, + /*arc_corrected*/ true); + test_cylindrical_proj_data_info(dynamic_cast(*proj_data_info_ptr)); cerr << "\nTests with proj_data_info with mashing and axial compression (span 4)\n\n"; - proj_data_info_ptr = - ProjDataInfo::construct_proj_data_info(scanner_ptr, - /*span*/4, scanner_ptr->get_num_rings() - 1, - /*views*/ scanner_ptr->get_num_detectors_per_ring() / 2 / 8, - /*tang_pos*/64, - /*arc_corrected*/ true); + proj_data_info_ptr = ProjDataInfo::construct_proj_data_info(scanner_ptr, + /*span*/ 4, scanner_ptr->get_num_rings() - 1, + /*views*/ scanner_ptr->get_num_detectors_per_ring() / 2 / 8, + /*tang_pos*/ 64, + /*arc_corrected*/ true); #if 0 // disabled to get noninteractive test michelogram(dynamic_cast(*proj_data_info_ptr)); cerr << endl; #endif - test_cylindrical_proj_data_info(dynamic_cast(*proj_data_info_ptr)); + test_cylindrical_proj_data_info(dynamic_cast(*proj_data_info_ptr)); } - /*! \ingroup test \brief Test class for ProjDataInfoCylindricalNoArcCorr */ -class ProjDataInfoCylindricalNoArcCorrTests: public ProjDataInfoCylindricalTests -{ -public: +class ProjDataInfoCylindricalNoArcCorrTests : public ProjDataInfoCylindricalTests { +public: void run_tests(); + private: void test_proj_data_info(ProjDataInfoCylindricalNoArcCorr& proj_data_info); }; - void -ProjDataInfoCylindricalNoArcCorrTests:: -run_tests() -{ +ProjDataInfoCylindricalNoArcCorrTests::run_tests() { cerr << "\n-------- Testing ProjDataInfoCylindricalNoArcCorr --------\n"; shared_ptr scanner_ptr(new Scanner(Scanner::E953)); cerr << "Tests with proj_data_info without mashing and axial compression\n\n"; - // Note: test without axial compression requires that all ring differences + // Note: test without axial compression requires that all ring differences // are in some segment, so use maximum ring difference shared_ptr proj_data_info_ptr( - ProjDataInfo::construct_proj_data_info(scanner_ptr, - /*span*/1, scanner_ptr->get_num_rings()-1, - /*views*/ scanner_ptr->get_num_detectors_per_ring()/2, - /*tang_pos*/64, - /*arc_corrected*/ false)); + ProjDataInfo::construct_proj_data_info(scanner_ptr, + /*span*/ 1, scanner_ptr->get_num_rings() - 1, + /*views*/ scanner_ptr->get_num_detectors_per_ring() / 2, + /*tang_pos*/ 64, + /*arc_corrected*/ false)); #ifndef STIR_TOF_DEBUG // disable these for speed of testing - test_proj_data_info(dynamic_cast(*proj_data_info_ptr)); + test_proj_data_info(dynamic_cast(*proj_data_info_ptr)); cerr << "\nTests with proj_data_info with mashing and axial compression (span 5)\n\n"; - proj_data_info_ptr = - ProjDataInfo::construct_proj_data_info(scanner_ptr, - /*span*/5, scanner_ptr->get_num_rings()-1, - /*views*/ scanner_ptr->get_num_detectors_per_ring()/2/8, - /*tang_pos*/64, - /*arc_corrected*/ false); - test_proj_data_info(dynamic_cast(*proj_data_info_ptr)); + proj_data_info_ptr = ProjDataInfo::construct_proj_data_info(scanner_ptr, + /*span*/ 5, scanner_ptr->get_num_rings() - 1, + /*views*/ scanner_ptr->get_num_detectors_per_ring() / 2 / 8, + /*tang_pos*/ 64, + /*arc_corrected*/ false); + test_proj_data_info(dynamic_cast(*proj_data_info_ptr)); cerr << "\nTests with proj_data_info with mashing and axial compression (span 2)\n\n"; - proj_data_info_ptr = - ProjDataInfo::construct_proj_data_info(scanner_ptr, - /*span*/2, scanner_ptr->get_num_rings() - 7, - /*views*/ scanner_ptr->get_num_detectors_per_ring() / 2 / 8, - /*tang_pos*/64, - /*arc_corrected*/ false); - test_proj_data_info(dynamic_cast(*proj_data_info_ptr)); + proj_data_info_ptr = ProjDataInfo::construct_proj_data_info(scanner_ptr, + /*span*/ 2, scanner_ptr->get_num_rings() - 7, + /*views*/ scanner_ptr->get_num_detectors_per_ring() / 2 / 8, + /*tang_pos*/ 64, + /*arc_corrected*/ false); + test_proj_data_info(dynamic_cast(*proj_data_info_ptr)); #endif // STIR_TOF_DEBUG cerr << "\nTests with proj_data_info with time-of-flight\n\n"; shared_ptr scanner_tof_ptr(new Scanner(Scanner::Discovery690)); - proj_data_info_ptr = - ProjDataInfo::construct_proj_data_info(scanner_tof_ptr, - /*span*/11, scanner_tof_ptr->get_num_rings()-1, - /*views*/ scanner_tof_ptr->get_num_detectors_per_ring()/2, - /*tang_pos*/64, - /*arc_corrected*/ false, - /*tof_mashing*/5); - test_proj_data_info(dynamic_cast(*proj_data_info_ptr)); + proj_data_info_ptr = ProjDataInfo::construct_proj_data_info(scanner_tof_ptr, + /*span*/ 11, scanner_tof_ptr->get_num_rings() - 1, + /*views*/ scanner_tof_ptr->get_num_detectors_per_ring() / 2, + /*tang_pos*/ 64, + /*arc_corrected*/ false, + /*tof_mashing*/ 5); + test_proj_data_info(dynamic_cast(*proj_data_info_ptr)); } void -ProjDataInfoCylindricalNoArcCorrTests:: -test_proj_data_info(ProjDataInfoCylindricalNoArcCorr& proj_data_info) -{ +ProjDataInfoCylindricalNoArcCorrTests::test_proj_data_info(ProjDataInfoCylindricalNoArcCorr& proj_data_info) { test_cylindrical_proj_data_info(proj_data_info); const int num_detectors = proj_data_info.get_scanner_ptr()->get_num_detectors_per_ring(); #ifndef TEST_ONLY_GET_BIN - if (proj_data_info.get_view_mashing_factor()==1) - { - // these tests work only without mashing + if (proj_data_info.get_view_mashing_factor() == 1) { + // these tests work only without mashing + + cerr << "\n\tTest code for sinogram <-> detector conversions."; + +# ifdef STIR_OPENMP +# pragma omp parallel for schedule(dynamic) +# endif + for (int det_num_a = 0; det_num_a < num_detectors; det_num_a++) + for (int det_num_b = 0; det_num_b < num_detectors; det_num_b++) { + int det1, det2; + bool positive_segment; + int tang_pos_num; + int view; + + // skip case of equal detectors (as this is a singular LOR) + if (det_num_a == det_num_b) + continue; + + positive_segment = proj_data_info.get_view_tangential_pos_num_for_det_num_pair(view, tang_pos_num, det_num_a, det_num_b); + proj_data_info.get_det_num_pair_for_view_tangential_pos_num(det1, det2, view, tang_pos_num); + + if (!check((det_num_a == det1 && det_num_b == det2 && positive_segment) || + (det_num_a == det2 && det_num_b == det1 && !positive_segment))) { + cerr << "Problem at det1 = " << det_num_a << ", det2 = " << det_num_b + << "\n dets -> sino -> dets gives new detector numbers " << det1 << ", " << det2 << endl; + continue; + } + if (!check(view < num_detectors / 2)) { + cerr << "Problem at det1 = " << det_num_a << ", det2 = " << det_num_b << ":\n view is too big : " << view << endl; + } + if (!check(tang_pos_num < num_detectors / 2 && tang_pos_num >= -(num_detectors / 2))) { + cerr << "Problem at det1 = " << det_num_a << ", det2 = " << det_num_b + << ":\n tang_pos_num is out of range : " << tang_pos_num << endl; + } + } // end of detectors_to_sinogram, sinogram_to_detector test + +# ifdef STIR_OPENMP +# pragma omp parallel for +# endif + for (int view = 0; view < num_detectors / 2; ++view) + for (int tang_pos_num = -(num_detectors / 2) + 1; tang_pos_num < num_detectors / 2; ++tang_pos_num) { + int new_tang_pos_num, new_view; + bool positive_segment; + int det_num_a; + int det_num_b; + + proj_data_info.get_det_num_pair_for_view_tangential_pos_num(det_num_a, det_num_b, view, tang_pos_num); + positive_segment = + proj_data_info.get_view_tangential_pos_num_for_det_num_pair(new_view, new_tang_pos_num, det_num_a, det_num_b); + + if (tang_pos_num != new_tang_pos_num || view != new_view || !positive_segment) { + cerr << "Problem at view = " << view << ", tang_pos_num = " << tang_pos_num + << "\n sino -> dets -> sino gives new view, tang_pos_num :" << new_view << ", " << new_tang_pos_num + << " with detector swapping " << positive_segment << endl; + } + } // end of sinogram_to_detector, detectors_to_sinogram test + + } // end of tests that work only without mashing + + if (proj_data_info.get_view_mashing_factor() == 1 && + proj_data_info.get_max_ring_difference(0) == proj_data_info.get_min_ring_difference(0) && + proj_data_info.get_max_ring_difference(1) == proj_data_info.get_min_ring_difference(1) && + proj_data_info.get_max_ring_difference(2) == proj_data_info.get_min_ring_difference(2)) { + // these tests work only without mashing and axial compression + + cerr << "\n\tTest code for detector,ring -> bin and back conversions."; + + DetectionPositionPair<> det_pos_pair; + for (det_pos_pair.pos1().axial_coord() = 0; det_pos_pair.pos1().axial_coord() <= 2; det_pos_pair.pos1().axial_coord()++) + for (det_pos_pair.pos2().axial_coord() = 0; det_pos_pair.pos2().axial_coord() <= 2; det_pos_pair.pos2().axial_coord()++) +# ifdef STIR_OPENMP + // insert a parallel for here for testing. + // we do it at this level to avoid too much overhead for the thread creation, while still having enough jobs to do + // note: for-loop writing somewhat awkwardly as openmp needs int variables for the loop +# pragma omp parallel for firstprivate(det_pos_pair) +# endif + for (int tangential_coord1 = 0; tangential_coord1 < num_detectors; tangential_coord1++) + for (det_pos_pair.pos2().tangential_coord() = 0; det_pos_pair.pos2().tangential_coord() < (unsigned)num_detectors; + det_pos_pair.pos2().tangential_coord()++) + for (det_pos_pair.timing_pos() = 0; // currently unsigned so start from 0 + det_pos_pair.timing_pos() <= (unsigned)proj_data_info.get_max_tof_pos_num(); + det_pos_pair.timing_pos() += (unsigned)std::max(1, proj_data_info.get_max_tof_pos_num())) { + + // set from for-loop variable + det_pos_pair.pos1().tangential_coord() = (unsigned)tangential_coord1; + // skip case of equal detector numbers (as this is either a singular LOR) + // or an LOR parallel to the scanner axis + if (det_pos_pair.pos1().tangential_coord() == det_pos_pair.pos2().tangential_coord()) + continue; + Bin bin(0, 0, 0, 0, 0, 0.0f); + DetectionPositionPair<> new_det_pos_pair; + const bool there_is_a_bin = proj_data_info.get_bin_for_det_pos_pair(bin, det_pos_pair) == Succeeded::yes; + if (there_is_a_bin) + proj_data_info.get_det_pos_pair_for_bin(new_det_pos_pair, bin); + if (!check(there_is_a_bin, "checking if there is a bin for this det_pos_pair") || + !check(det_pos_pair == new_det_pos_pair, "checking if we round-trip to the same detection positions")) { + cerr << "Problem at det1 = " << det_pos_pair.pos1().tangential_coord() + << ", det2 = " << det_pos_pair.pos2().tangential_coord() << ", ring1 = " << det_pos_pair.pos1().axial_coord() + << ", ring2 = " << det_pos_pair.pos2().axial_coord() << ", timing_pos = " << det_pos_pair.timing_pos() + << endl; + if (there_is_a_bin) + cerr << " dets,rings -> bin -> dets,rings, gives new numbers:\n\t" + << "det1 = " << new_det_pos_pair.pos1().tangential_coord() - cerr << "\n\tTest code for sinogram <-> detector conversions."; - -#ifdef STIR_OPENMP - #pragma omp parallel for schedule(dynamic) -#endif - for (int det_num_a = 0; det_num_a < num_detectors; det_num_a++) - for (int det_num_b = 0; det_num_b < num_detectors; det_num_b++) - { - int det1, det2; - bool positive_segment; - int tang_pos_num; - int view; - - // skip case of equal detectors (as this is a singular LOR) - if (det_num_a == det_num_b) - continue; - - positive_segment = - proj_data_info.get_view_tangential_pos_num_for_det_num_pair(view, tang_pos_num, det_num_a, det_num_b); - proj_data_info.get_det_num_pair_for_view_tangential_pos_num(det1, det2, view, tang_pos_num); - - if (!check((det_num_a == det1 && det_num_b == det2 && positive_segment) || - (det_num_a == det2 && det_num_b == det1 && !positive_segment))) - { - cerr << "Problem at det1 = " << det_num_a << ", det2 = " << det_num_b - << "\n dets -> sino -> dets gives new detector numbers " - << det1 << ", " << det2 << endl; - continue; - } - if (!check(view < num_detectors/2)) - { - cerr << "Problem at det1 = " << det_num_a << ", det2 = " << det_num_b - << ":\n view is too big : " << view << endl; - } - if (!check(tang_pos_num < num_detectors/2 && tang_pos_num >= -(num_detectors/2))) - { - cerr << "Problem at det1 = " << det_num_a << ", det2 = " << det_num_b - << ":\n tang_pos_num is out of range : " << tang_pos_num << endl; - } - } // end of detectors_to_sinogram, sinogram_to_detector test - - -#ifdef STIR_OPENMP -#pragma omp parallel for -#endif - for (int view = 0; view < num_detectors/2; ++view) - for (int tang_pos_num = -(num_detectors/2)+1; tang_pos_num < num_detectors/2; ++tang_pos_num) - { - int new_tang_pos_num, new_view; - bool positive_segment; - int det_num_a; - int det_num_b; - - proj_data_info.get_det_num_pair_for_view_tangential_pos_num(det_num_a, det_num_b, view, tang_pos_num); - positive_segment = - proj_data_info.get_view_tangential_pos_num_for_det_num_pair(new_view, new_tang_pos_num, det_num_a, det_num_b); - - if (tang_pos_num != new_tang_pos_num || view != new_view || !positive_segment) - { - cerr << "Problem at view = " << view << ", tang_pos_num = " << tang_pos_num - << "\n sino -> dets -> sino gives new view, tang_pos_num :" - << new_view << ", " << new_tang_pos_num - << " with detector swapping " << positive_segment - << endl; - } - } // end of sinogram_to_detector, detectors_to_sinogram test - - } // end of tests that work only without mashing - - if (proj_data_info.get_view_mashing_factor()==1 - && proj_data_info.get_max_ring_difference(0) == proj_data_info.get_min_ring_difference(0) - && proj_data_info.get_max_ring_difference(1) == proj_data_info.get_min_ring_difference(1) - && proj_data_info.get_max_ring_difference(2) == proj_data_info.get_min_ring_difference(2) - ) + << ", det2 = " << new_det_pos_pair.pos2().tangential_coord() + << ", ring1 = " << new_det_pos_pair.pos1().axial_coord() + << ", ring2 = " << new_det_pos_pair.pos2().axial_coord() << ", timing_pos = " << det_pos_pair.timing_pos() + << endl; + } + + } // end of get_bin_for_det_pos_pair and vice versa code + + cerr << "\n\tTest code for bin -> detector,ring and back conversions. (This might take a while...)"; { - // these tests work only without mashing and axial compression - - cerr << "\n\tTest code for detector,ring -> bin and back conversions."; - - DetectionPositionPair<> det_pos_pair; - for (det_pos_pair.pos1().axial_coord() = 0; - det_pos_pair.pos1().axial_coord() <= 2; - det_pos_pair.pos1().axial_coord()++) - for (det_pos_pair.pos2().axial_coord() = 0; - det_pos_pair.pos2().axial_coord() <= 2; - det_pos_pair.pos2().axial_coord()++) -#ifdef STIR_OPENMP - // insert a parallel for here for testing. - // we do it at this level to avoid too much overhead for the thread creation, while still having enough jobs to do - // note: for-loop writing somewhat awkwardly as openmp needs int variables for the loop -#pragma omp parallel for firstprivate(det_pos_pair) -#endif - for (int tangential_coord1 = 0; - tangential_coord1 < num_detectors; - tangential_coord1++) - for (det_pos_pair.pos2().tangential_coord() = 0; - det_pos_pair.pos2().tangential_coord() < (unsigned)num_detectors; - det_pos_pair.pos2().tangential_coord()++) - for (det_pos_pair.timing_pos() = 0; // currently unsigned so start from 0 - det_pos_pair.timing_pos() <= (unsigned)proj_data_info.get_max_tof_pos_num(); - det_pos_pair.timing_pos() += (unsigned)std::max(1,proj_data_info.get_max_tof_pos_num())) - { - - // set from for-loop variable - det_pos_pair.pos1().tangential_coord() = (unsigned)tangential_coord1; - // skip case of equal detector numbers (as this is either a singular LOR) - // or an LOR parallel to the scanner axis - if (det_pos_pair.pos1().tangential_coord() == det_pos_pair.pos2().tangential_coord()) - continue; - Bin bin(0,0,0,0,0,0.0f); - DetectionPositionPair<> new_det_pos_pair; - const bool there_is_a_bin = - proj_data_info.get_bin_for_det_pos_pair(bin, det_pos_pair) == - Succeeded::yes; + Bin bin(0, 0, 0, 0, 0, 0.0f); + // set value for comparison later on + bin.set_bin_value(0.f); + for (bin.timing_pos_num() = proj_data_info.get_min_tof_pos_num(); + bin.timing_pos_num() <= proj_data_info.get_max_tof_pos_num(); + bin.timing_pos_num() += std::max(1, (proj_data_info.get_max_tof_pos_num() - proj_data_info.get_min_tof_pos_num()) / + 2)) // take 3 or 1 steps, always going through 0) + for (bin.segment_num() = max(-5, proj_data_info.get_min_segment_num()); + bin.segment_num() <= min(5, proj_data_info.get_max_segment_num()); ++bin.segment_num()) + for (bin.axial_pos_num() = proj_data_info.get_min_axial_pos_num(bin.segment_num()); + bin.axial_pos_num() <= proj_data_info.get_max_axial_pos_num(bin.segment_num()); + bin.axial_pos_num() += std::min(3, proj_data_info.get_num_axial_poss(bin.segment_num()))) +# ifdef STIR_OPENMP + // insert a parallel for here for testing. + // we do it at this level to avoid too much overhead for the thread creation, while still having enough jobs to do // + // Note that the omp construct needs an int loop variable +# pragma omp parallel for firstprivate(bin) +# endif + for (int tangential_pos_num = -(num_detectors / 2) + 1; tangential_pos_num < num_detectors / 2; ++tangential_pos_num) + for (bin.view_num() = 0; bin.view_num() < num_detectors / 2; ++bin.view_num()) { + // set from for-loop variable + bin.tangential_pos_num() = tangential_pos_num; + Bin new_bin(0, 0, 0, 0, 0, 0.0f); + // set value for comparison with bin + new_bin.set_bin_value(0); + DetectionPositionPair<> det_pos_pair; + proj_data_info.get_det_pos_pair_for_bin(det_pos_pair, bin); + + const bool there_is_a_bin = proj_data_info.get_bin_for_det_pos_pair(new_bin, det_pos_pair) == Succeeded::yes; + if (!check(there_is_a_bin, "checking if there is a bin for this det_pos_pair") || + !check(bin == new_bin, "checking if we round-trip to the same bin")) { + cerr << "Problem at segment = " << bin.segment_num() << ", axial pos " << bin.axial_pos_num() + << ", view = " << bin.view_num() << ", tangential_pos_num = " << bin.tangential_pos_num() + << ", timing pos num = " << bin.timing_pos_num() << "\n"; if (there_is_a_bin) - proj_data_info.get_det_pos_pair_for_bin(new_det_pos_pair, bin); - if (!check(there_is_a_bin, "checking if there is a bin for this det_pos_pair") || - !check(det_pos_pair == new_det_pos_pair, "checking if we round-trip to the same detection positions")) - { - cerr << "Problem at det1 = " << det_pos_pair.pos1().tangential_coord() - << ", det2 = " << det_pos_pair.pos2().tangential_coord() - << ", ring1 = " << det_pos_pair.pos1().axial_coord() - << ", ring2 = " << det_pos_pair.pos2().axial_coord() - << ", timing_pos = " << det_pos_pair.timing_pos() - << endl; - if (there_is_a_bin) - cerr << " dets,rings -> bin -> dets,rings, gives new numbers:\n\t" - << "det1 = " << new_det_pos_pair.pos1().tangential_coord() - - << ", det2 = " << new_det_pos_pair.pos2().tangential_coord() - << ", ring1 = " << new_det_pos_pair.pos1().axial_coord() - << ", ring2 = " << new_det_pos_pair.pos2().axial_coord() - << ", timing_pos = " << det_pos_pair.timing_pos() - << endl; - } - - } // end of get_bin_for_det_pos_pair and vice versa code - - cerr << "\n\tTest code for bin -> detector,ring and back conversions. (This might take a while...)"; - { - Bin bin(0,0,0,0,0,0.0f); - // set value for comparison later on - bin.set_bin_value(0.f); - for (bin.timing_pos_num() = proj_data_info.get_min_tof_pos_num(); - bin.timing_pos_num() <= proj_data_info.get_max_tof_pos_num(); - bin.timing_pos_num() += std::max(1,(proj_data_info.get_max_tof_pos_num() - proj_data_info.get_min_tof_pos_num()) / 2))// take 3 or 1 steps, always going through 0) - for (bin.segment_num() = max(-5,proj_data_info.get_min_segment_num()); - bin.segment_num() <= min(5,proj_data_info.get_max_segment_num()); - ++bin.segment_num()) - for (bin.axial_pos_num() = proj_data_info.get_min_axial_pos_num(bin.segment_num()); - bin.axial_pos_num() <= proj_data_info.get_max_axial_pos_num(bin.segment_num()); - bin.axial_pos_num() += std::min(3, proj_data_info.get_num_axial_poss(bin.segment_num()))) -#ifdef STIR_OPENMP - // insert a parallel for here for testing. - // we do it at this level to avoid too much overhead for the thread creation, while still having enough jobs to do // Note that the omp construct needs an int loop variable -#pragma omp parallel for firstprivate(bin) -#endif - for (int tangential_pos_num = -(num_detectors/2)+1; - tangential_pos_num < num_detectors/2; - ++tangential_pos_num) - for (bin.view_num() = 0; bin.view_num() < num_detectors/2; ++bin.view_num()) - { - // set from for-loop variable - bin.tangential_pos_num() = tangential_pos_num; - Bin new_bin(0,0,0,0,0,0.0f); - // set value for comparison with bin - new_bin.set_bin_value(0); - DetectionPositionPair<> det_pos_pair; - proj_data_info.get_det_pos_pair_for_bin(det_pos_pair, bin); - - const bool there_is_a_bin = - proj_data_info.get_bin_for_det_pos_pair(new_bin, - det_pos_pair) == - Succeeded::yes; - if (!check(there_is_a_bin, "checking if there is a bin for this det_pos_pair") || - !check(bin == new_bin, "checking if we round-trip to the same bin")) - { - cerr << "Problem at segment = " << bin.segment_num() - << ", axial pos " << bin.axial_pos_num() - << ", view = " << bin.view_num() - << ", tangential_pos_num = " << bin.tangential_pos_num() - << ", timing pos num = " << bin.timing_pos_num() << "\n"; - if (there_is_a_bin) - cerr << " bin -> dets -> bin, gives new numbers:\n\t" - << "segment = " << new_bin.segment_num() - << ", axial pos " << new_bin.axial_pos_num() - << ", view = " << new_bin.view_num() - << ", tangential_pos_num = " << new_bin.tangential_pos_num() - << ", timing pos num = " << new_bin.timing_pos_num() - << endl; - } - - } // end of get_det_pos_pair_for_bin and back code - } - } // end of tests which require no mashing nor axial compression + cerr << " bin -> dets -> bin, gives new numbers:\n\t" + << "segment = " << new_bin.segment_num() << ", axial pos " << new_bin.axial_pos_num() + << ", view = " << new_bin.view_num() << ", tangential_pos_num = " << new_bin.tangential_pos_num() + << ", timing pos num = " << new_bin.timing_pos_num() << endl; + } + + } // end of get_det_pos_pair_for_bin and back code + } + } // end of tests which require no mashing nor axial compression { cerr << "\n\tTest code for bins <-> detectors routines that work with any mashing and axial compression"; - Bin bin(0,0,0,0,0,0.0f); + Bin bin(0, 0, 0, 0, 0, 0.0f); // set value for comparison later on bin.set_bin_value(0); - std::vector > det_pos_pairs; -#ifdef STIR_OPENMP + std::vector> det_pos_pairs; +# ifdef STIR_OPENMP //#pragma omp parallel for schedule(dynamic) -#endif - for (bin.segment_num() = proj_data_info.get_min_segment_num(); - bin.segment_num() <= proj_data_info.get_max_segment_num(); - bin.segment_num() += std::max(1, proj_data_info.get_num_segments()/2)) +# endif + for (bin.segment_num() = proj_data_info.get_min_segment_num(); bin.segment_num() <= proj_data_info.get_max_segment_num(); + bin.segment_num() += std::max(1, proj_data_info.get_num_segments() / 2)) for (bin.axial_pos_num() = proj_data_info.get_min_axial_pos_num(bin.segment_num()); - bin.axial_pos_num() <= proj_data_info.get_max_axial_pos_num(bin.segment_num()); - bin.axial_pos_num()+=std::min(3, proj_data_info.get_num_axial_poss(bin.segment_num()))) - for (bin.view_num() = proj_data_info.get_min_view_num(); - bin.view_num() <= proj_data_info.get_max_view_num(); - bin.view_num()+= std::min(5, proj_data_info.get_num_views())) - for (bin.tangential_pos_num() = proj_data_info.get_min_tangential_pos_num(); - bin.tangential_pos_num() <= proj_data_info.get_max_tangential_pos_num(); - bin.tangential_pos_num()+= std::min(7, proj_data_info.get_num_tangential_poss())) + bin.axial_pos_num() <= proj_data_info.get_max_axial_pos_num(bin.segment_num()); + bin.axial_pos_num() += std::min(3, proj_data_info.get_num_axial_poss(bin.segment_num()))) + for (bin.view_num() = proj_data_info.get_min_view_num(); bin.view_num() <= proj_data_info.get_max_view_num(); + bin.view_num() += std::min(5, proj_data_info.get_num_views())) + for (bin.tangential_pos_num() = proj_data_info.get_min_tangential_pos_num(); + bin.tangential_pos_num() <= proj_data_info.get_max_tangential_pos_num(); + bin.tangential_pos_num() += std::min(7, proj_data_info.get_num_tangential_poss())) for (bin.timing_pos_num() = proj_data_info.get_min_tof_pos_num(); bin.timing_pos_num() <= proj_data_info.get_max_tof_pos_num(); - bin.timing_pos_num() += std::max(1,(proj_data_info.get_max_tof_pos_num() - proj_data_info.get_min_tof_pos_num()) / 2))// take 3 or 1 steps, always going through 0) - { - proj_data_info.get_all_det_pos_pairs_for_bin(det_pos_pairs, bin); - Bin new_bin(0,0,0,0,0,0.0f); - // set value for comparison with bin - new_bin.set_bin_value(0); - for (std::vector >::const_iterator det_pos_pair_iter = det_pos_pairs.begin(); - det_pos_pair_iter != det_pos_pairs.end(); - ++det_pos_pair_iter) - { - const bool there_is_a_bin = - proj_data_info.get_bin_for_det_pos_pair(new_bin, - *det_pos_pair_iter) == - Succeeded::yes; - if (!check(there_is_a_bin, "checking if there is a bin for this det_pos_pair") || - !check(bin == new_bin, "checking if we round-trip to the same bin")) - { - cerr << "Problem at segment = " << bin.segment_num() - << ", axial pos " << bin.axial_pos_num() - << ", view = " << bin.view_num() - << ", tangential_pos = " << bin.tangential_pos_num() - << ", timing_pos - " << bin.timing_pos_num() << "\n"; - if (there_is_a_bin) - cerr << " bin -> dets -> bin, gives new numbers:\n\t" - << "segment = " << new_bin.segment_num() - << ", axial pos " << new_bin.axial_pos_num() - << ", view = " << new_bin.view_num() - << ", tangential_pos_num = " << new_bin.tangential_pos_num() - << ", timing_pos - " << new_bin.timing_pos_num() - << endl; - } - } // end of iteration of det_pos_pairs - } // end of loop over all bins - } // end of get_all_det_pairs_for_bin and back code -#endif //TEST_ONLY_GET_BIN + bin.timing_pos_num() += + std::max(1, (proj_data_info.get_max_tof_pos_num() - proj_data_info.get_min_tof_pos_num()) / + 2)) // take 3 or 1 steps, always going through 0) + { + proj_data_info.get_all_det_pos_pairs_for_bin(det_pos_pairs, bin); + Bin new_bin(0, 0, 0, 0, 0, 0.0f); + // set value for comparison with bin + new_bin.set_bin_value(0); + for (std::vector>::const_iterator det_pos_pair_iter = det_pos_pairs.begin(); + det_pos_pair_iter != det_pos_pairs.end(); ++det_pos_pair_iter) { + const bool there_is_a_bin = + proj_data_info.get_bin_for_det_pos_pair(new_bin, *det_pos_pair_iter) == Succeeded::yes; + if (!check(there_is_a_bin, "checking if there is a bin for this det_pos_pair") || + !check(bin == new_bin, "checking if we round-trip to the same bin")) { + cerr << "Problem at segment = " << bin.segment_num() << ", axial pos " << bin.axial_pos_num() + << ", view = " << bin.view_num() << ", tangential_pos = " << bin.tangential_pos_num() << ", timing_pos - " + << bin.timing_pos_num() << "\n"; + if (there_is_a_bin) + cerr << " bin -> dets -> bin, gives new numbers:\n\t" + << "segment = " << new_bin.segment_num() << ", axial pos " << new_bin.axial_pos_num() + << ", view = " << new_bin.view_num() << ", tangential_pos_num = " << new_bin.tangential_pos_num() + << ", timing_pos - " << new_bin.timing_pos_num() << endl; + } + } // end of iteration of det_pos_pairs + } // end of loop over all bins + } // end of get_all_det_pairs_for_bin and back code +#endif // TEST_ONLY_GET_BIN { cerr << endl; cerr << "\tTesting find scanner coordinates given cartesian and vice versa." << endl; - { - const int num_detectors_per_ring = - proj_data_info.get_scanner_ptr()->get_num_detectors_per_ring(); - const int num_rings = - proj_data_info.get_scanner_ptr()->get_num_rings(); + { + const int num_detectors_per_ring = proj_data_info.get_scanner_ptr()->get_num_detectors_per_ring(); + const int num_rings = proj_data_info.get_scanner_ptr()->get_num_rings(); #ifdef STIR_OPENMP -#pragma omp parallel for schedule(dynamic) +# pragma omp parallel for schedule(dynamic) #endif - for ( int Ring_A = 0; Ring_A < num_rings; Ring_A+=num_rings/3) - for ( int Ring_B = 0; Ring_B < num_rings; Ring_B+=num_rings/3) - for ( int det1 =0; det1 < num_detectors_per_ring; ++det1) - for ( int det2 =0; det2 < num_detectors_per_ring; ++det2) - { - if (det1==det2) - continue; - CartesianCoordinate3D coord_1; - CartesianCoordinate3D coord_2; - - proj_data_info.find_cartesian_coordinates_given_scanner_coordinates (coord_1,coord_2, - Ring_A,Ring_B, - det1,det2); - - const CartesianCoordinate3D coord_1_new = coord_1 + (coord_2-coord_1)*5; - const CartesianCoordinate3D coord_2_new = coord_1 + (coord_2-coord_1)*2; - - int det1_f, det2_f,ring1_f, ring2_f; - - check(proj_data_info.find_scanner_coordinates_given_cartesian_coordinates(det1_f, det2_f, ring1_f, ring2_f, - coord_1_new, coord_2_new) == - Succeeded::yes); - if (det1_f == det1 && Ring_A == ring1_f) - { - check_if_equal( det1_f, det1, "test on det1"); - check_if_equal( Ring_A, ring1_f, "test on ring1"); - check_if_equal( det2_f, det2, "test on det2"); - check_if_equal( Ring_B, ring2_f, "test on ring1"); - } - else - { - check_if_equal( det2_f, det1, "test on det1"); - check_if_equal( Ring_B, ring1_f, "test on ring1"); - check_if_equal( det1_f, det2, "test on det2"); - check_if_equal( Ring_A, ring2_f, "test on ring1"); - } - } + for (int Ring_A = 0; Ring_A < num_rings; Ring_A += num_rings / 3) + for (int Ring_B = 0; Ring_B < num_rings; Ring_B += num_rings / 3) + for (int det1 = 0; det1 < num_detectors_per_ring; ++det1) + for (int det2 = 0; det2 < num_detectors_per_ring; ++det2) { + if (det1 == det2) + continue; + CartesianCoordinate3D coord_1; + CartesianCoordinate3D coord_2; + + proj_data_info.find_cartesian_coordinates_given_scanner_coordinates(coord_1, coord_2, Ring_A, Ring_B, det1, det2); + + const CartesianCoordinate3D coord_1_new = coord_1 + (coord_2 - coord_1) * 5; + const CartesianCoordinate3D coord_2_new = coord_1 + (coord_2 - coord_1) * 2; + + int det1_f, det2_f, ring1_f, ring2_f; + + check(proj_data_info.find_scanner_coordinates_given_cartesian_coordinates( + det1_f, det2_f, ring1_f, ring2_f, coord_1_new, coord_2_new) == Succeeded::yes); + if (det1_f == det1 && Ring_A == ring1_f) { + check_if_equal(det1_f, det1, "test on det1"); + check_if_equal(Ring_A, ring1_f, "test on ring1"); + check_if_equal(det2_f, det2, "test on det2"); + check_if_equal(Ring_B, ring2_f, "test on ring1"); + } else { + check_if_equal(det2_f, det1, "test on det1"); + check_if_equal(Ring_B, ring1_f, "test on ring1"); + check_if_equal(det1_f, det2, "test on det2"); + check_if_equal(Ring_A, ring2_f, "test on ring1"); + } + } } } } END_NAMESPACE_STIR - USING_NAMESPACE_STIR -int main() -{ +int +main() { set_default_num_threads(); #ifndef STIR_TOF_DEBUG // disable for speed of testing diff --git a/src/test/test_proj_data_maths.cxx b/src/test/test_proj_data_maths.cxx index b33bfb4ce7..fcfec82c0a 100644 --- a/src/test/test_proj_data_maths.cxx +++ b/src/test/test_proj_data_maths.cxx @@ -38,104 +38,94 @@ #include "stir/copy_fill.h" START_NAMESPACE_STIR - /*! \ingroup test \brief Test class for ProjDataInMemory */ -class ProjDataInMemoryTests: public RunTests -{ +class ProjDataInMemoryTests : public RunTests { public: void run_tests(); }; -static -void check_proj_data_are_equal_and_non_zero(const ProjData& x, const ProjData& y) -{ - const size_t n = x.size_all(); - const size_t ny = y.size_all(); - if (n!=ny) - error("ProjData::xapyb and ProjDataInMemory::xapyb mismatch"); - - // Create arrays - std::vector arr1(n), arr2(n); - copy_to(x, arr1.begin()); - copy_to(y, arr2.begin()); - - // Check for mismatch - for (unsigned i=0; i 1e-4f) - error("ProjData::xapyb and ProjDataInMemory::xapyb mismatch"); - - // Check for non-zero - if (std::abs(*std::max_element(arr1.begin(),arr1.end())) < 1e-4f) - error("ProjData::xapyb and ProjDataInMemory::xapyb mismatch"); +static void +check_proj_data_are_equal_and_non_zero(const ProjData& x, const ProjData& y) { + const size_t n = x.size_all(); + const size_t ny = y.size_all(); + if (n != ny) + error("ProjData::xapyb and ProjDataInMemory::xapyb mismatch"); + + // Create arrays + std::vector arr1(n), arr2(n); + copy_to(x, arr1.begin()); + copy_to(y, arr2.begin()); + + // Check for mismatch + for (unsigned i = 0; i < n; ++i) + if (std::abs(arr1[i] - arr2[i]) > 1e-4f) + error("ProjData::xapyb and ProjDataInMemory::xapyb mismatch"); + + // Check for non-zero + if (std::abs(*std::max_element(arr1.begin(), arr1.end())) < 1e-4f) + error("ProjData::xapyb and ProjDataInMemory::xapyb mismatch"); } void -ProjDataInMemoryTests:: -run_tests() -{ - // Create scanner and proj data info - shared_ptr scanner_sptr(new Scanner(Scanner::E953)); - shared_ptr - proj_data_info_sptr( - ProjDataInfo::construct_proj_data_info - (scanner_sptr, - /*span*/1, 10,/*views*/ 96, /*tang_pos*/128, /*arc_corrected*/ true) - ); - - // Create pd1 and pd2 - shared_ptr exam_info_sptr(new ExamInfo); - ProjDataInMemory pd1(exam_info_sptr, proj_data_info_sptr); - ProjDataInMemory pd2(pd1); - - // Create x1 and x2 - ProjDataInMemory x1(pd1); - x1.fill(100.f); - ProjDataInMemory x2(x1); - - // Create y1 and y2 - ProjDataInMemory y1(pd1); - y1.fill(1000.f); - ProjDataInMemory y2(y1); - - - // Check xapby with general and ProjDataInMemory methods - const float a = 2.f; - const float b = 3.f; - pd1.xapyb(x1,a,y1,b); - pd2.ProjData::xapyb(x2,a,y2,b); - check_proj_data_are_equal_and_non_zero(pd1,pd2); - - // Check sapby with general and ProjDataInMemory methods - ProjDataInMemory out1(x1); - out1.sapyb(a, y1, b); - check_proj_data_are_equal_and_non_zero(pd1,out1); - - ProjDataInMemory out2(x1); - out2.ProjData::sapyb(a, y1, b); - check_proj_data_are_equal_and_non_zero(pd1,out2); - - // Check using iterators - ProjDataInMemory pd3(pd1); - pd3.fill(0.f); - ProjDataInMemory::iterator pd_iter = pd3.begin(); - ProjDataInMemory::const_iterator x_iter = x1.begin(); - ProjDataInMemory::const_iterator y_iter = y1.begin(); - while (pd_iter != pd3.end()) - *pd_iter++ = a*(*x_iter++) + b*(*y_iter++); - - check_proj_data_are_equal_and_non_zero(pd1,pd3); +ProjDataInMemoryTests::run_tests() { + // Create scanner and proj data info + shared_ptr scanner_sptr(new Scanner(Scanner::E953)); + shared_ptr proj_data_info_sptr(ProjDataInfo::construct_proj_data_info(scanner_sptr, + /*span*/ 1, 10, /*views*/ 96, + /*tang_pos*/ 128, /*arc_corrected*/ true)); + + // Create pd1 and pd2 + shared_ptr exam_info_sptr(new ExamInfo); + ProjDataInMemory pd1(exam_info_sptr, proj_data_info_sptr); + ProjDataInMemory pd2(pd1); + + // Create x1 and x2 + ProjDataInMemory x1(pd1); + x1.fill(100.f); + ProjDataInMemory x2(x1); + + // Create y1 and y2 + ProjDataInMemory y1(pd1); + y1.fill(1000.f); + ProjDataInMemory y2(y1); + + // Check xapby with general and ProjDataInMemory methods + const float a = 2.f; + const float b = 3.f; + pd1.xapyb(x1, a, y1, b); + pd2.ProjData::xapyb(x2, a, y2, b); + check_proj_data_are_equal_and_non_zero(pd1, pd2); + + // Check sapby with general and ProjDataInMemory methods + ProjDataInMemory out1(x1); + out1.sapyb(a, y1, b); + check_proj_data_are_equal_and_non_zero(pd1, out1); + + ProjDataInMemory out2(x1); + out2.ProjData::sapyb(a, y1, b); + check_proj_data_are_equal_and_non_zero(pd1, out2); + + // Check using iterators + ProjDataInMemory pd3(pd1); + pd3.fill(0.f); + ProjDataInMemory::iterator pd_iter = pd3.begin(); + ProjDataInMemory::const_iterator x_iter = x1.begin(); + ProjDataInMemory::const_iterator y_iter = y1.begin(); + while (pd_iter != pd3.end()) + *pd_iter++ = a * (*x_iter++) + b * (*y_iter++); + + check_proj_data_are_equal_and_non_zero(pd1, pd3); } END_NAMESPACE_STIR - USING_NAMESPACE_STIR -int main() -{ +int +main() { ProjDataInMemoryTests tests; tests.run_tests(); return tests.main_return_value(); diff --git a/src/test/test_stir_math.cxx b/src/test/test_stir_math.cxx index 2fef311744..351405db2c 100644 --- a/src/test/test_stir_math.cxx +++ b/src/test/test_stir_math.cxx @@ -2,10 +2,10 @@ \file \ingroup test - + \brief Test program for the stir_math utility - - Takes as single command line argument the filename of the stir_math executable + + Takes as single command line argument the filename of the stir_math executable (with a relative or absolute path). If no argument is given, the executable im the path is used. \warning will overwite (and then delete) files called STIR$$*. @@ -61,146 +61,131 @@ START_NAMESPACE_STIR \warning will overwite (and then delete) files called STIR$$*. */ -class stir_mathTests : public RunTests -{ +class stir_mathTests : public RunTests { public: - stir_mathTests(const string& stir_math_executable) - : stir_math_executable(stir_math_executable) - {} - + stir_mathTests(const string& stir_math_executable) : stir_math_executable(stir_math_executable) {} + void run_tests(); + private: string stir_math_executable; //! returns true if it ran it successfully - bool run_stir_math(const char * const arguments); - + bool run_stir_math(const char* const arguments); }; - bool -stir_mathTests:: -run_stir_math(const char * const arguments) -{ +stir_mathTests::run_stir_math(const char* const arguments) { static string cmdline; static string error_string; // note: add extra quotes around executable name to cope with spaces in the filename cmdline = "\"" + stir_math_executable + "\""; - cmdline += ' ' ; + cmdline += ' '; cmdline += arguments; error_string = "error executing command '"; error_string += cmdline; error_string += "'"; cerr << "\nExecuting '" << cmdline << "'\n"; - return check(system(cmdline.c_str())==EXIT_SUCCESS, - "executing previous command returned non-zero status\n."); + return check(system(cmdline.c_str()) == EXIT_SUCCESS, "executing previous command returned non-zero status\n."); } void stir_mathTests::run_tests() -{ +{ cerr << "Tests for the stir_math utility\n"; cerr << "WARNING: will overwite (and then delete) files called STIRtmp*\n"; - + // images { - CartesianCoordinate3D origin (0,0,0); - CartesianCoordinate3D grid_spacing (3,4,5); - IndexRange<3> - range(CartesianCoordinate3D(0,-2,-3), - CartesianCoordinate3D(1,2,3)); - VoxelsOnCartesianGrid data1(range,origin, grid_spacing); - VoxelsOnCartesianGrid data2(range,origin, grid_spacing); - VoxelsOnCartesianGrid data3(range,origin, grid_spacing); + CartesianCoordinate3D origin(0, 0, 0); + CartesianCoordinate3D grid_spacing(3, 4, 5); + IndexRange<3> range(CartesianCoordinate3D(0, -2, -3), CartesianCoordinate3D(1, 2, 3)); + VoxelsOnCartesianGrid data1(range, origin, grid_spacing); + VoxelsOnCartesianGrid data2(range, origin, grid_spacing); + VoxelsOnCartesianGrid data3(range, origin, grid_spacing); VoxelsOnCartesianGrid calc_data; - shared_ptr > out_data_ptr; - + shared_ptr> out_data_ptr; + // fill with some random numbers srand(1); generate(data1.begin_all(), data1.end_all(), rand); generate(data2.begin_all(), data2.end_all(), rand); generate(data3.begin_all(), data3.end_all(), rand); - + InterfileOutputFileFormat output_file_format; - check(output_file_format.write_to_file("STIRtmp1.v", data1)== Succeeded::yes, - "test writing Interfile image STIRtmp1"); - check(output_file_format.write_to_file("STIRtmp2.v", data2)== Succeeded::yes, - "test writing Interfile image STIRtmp2"); - check(output_file_format.write_to_file("STIRtmp3.v", data3)== Succeeded::yes, - "test writing Interfile image STIRtmp3"); - - set_tolerance(data1.find_max()/10000); - + check(output_file_format.write_to_file("STIRtmp1.v", data1) == Succeeded::yes, "test writing Interfile image STIRtmp1"); + check(output_file_format.write_to_file("STIRtmp2.v", data2) == Succeeded::yes, "test writing Interfile image STIRtmp2"); + check(output_file_format.write_to_file("STIRtmp3.v", data3) == Succeeded::yes, "test writing Interfile image STIRtmp3"); + + set_tolerance(data1.find_max() / 10000); + // add - if (run_stir_math("STIRtmpout.v STIRtmp1.hv STIRtmp2.hv STIRtmp3.hv")) - { - out_data_ptr = read_from_file >("STIRtmpout.hv"); + if (run_stir_math("STIRtmpout.v STIRtmp1.hv STIRtmp2.hv STIRtmp3.hv")) { + out_data_ptr = read_from_file>("STIRtmpout.hv"); calc_data = data1; calc_data += data2 + data3; - - check_if_equal( calc_data, *out_data_ptr,"test on adding"); + + check_if_equal(calc_data, *out_data_ptr, "test on adding"); } // mult - if (run_stir_math("--mult STIRtmpout.v STIRtmp1.hv STIRtmp2.hv STIRtmp3.hv")) - { - out_data_ptr = read_from_file >("STIRtmpout.hv"); + if (run_stir_math("--mult STIRtmpout.v STIRtmp1.hv STIRtmp2.hv STIRtmp3.hv")) { + out_data_ptr = read_from_file>("STIRtmpout.hv"); calc_data = data1; calc_data *= data2 * data3; - - check_if_equal( calc_data, *out_data_ptr,"test on multiplying"); + + check_if_equal(calc_data, *out_data_ptr, "test on multiplying"); } // add with power etc // range for rand() is 0 to RAND_MAX - const float min_threshold = RAND_MAX/5.F; - const float max_threshold = RAND_MAX/2.F; + const float min_threshold = RAND_MAX / 5.F; + const float max_threshold = RAND_MAX / 2.F; { char cmd_args[1000]; - sprintf(cmd_args, "--add-scalar 3.1 --power 2 --times-scalar 3.2 --min-threshold %g --max-threshold %g " - "STIRtmpout.v STIRtmp1.hv STIRtmp2.hv STIRtmp3.hv", - min_threshold, max_threshold); - if (run_stir_math(cmd_args)) - { - out_data_ptr = read_from_file >("STIRtmpout.hv"); - calc_data.fill(0); - VoxelsOnCartesianGrid data2_thresholded(data2); - VoxelsOnCartesianGrid data3_thresholded(data3); - threshold_upper_lower( data2_thresholded.begin_all(), data2_thresholded.end_all(), - min_threshold, max_threshold); - threshold_upper_lower( data3_thresholded.begin_all(), data3_thresholded.end_all(), - min_threshold, max_threshold); - calc_data += data1 + (data2_thresholded*data2_thresholded)*3.2F + 3.1F + (data3_thresholded*data3_thresholded)*3.2F + 3.1F; - - check_if_equal( calc_data, *out_data_ptr,"test with thresholding, power and scalar multiplication and addition"); - } + sprintf(cmd_args, + "--add-scalar 3.1 --power 2 --times-scalar 3.2 --min-threshold %g --max-threshold %g " + "STIRtmpout.v STIRtmp1.hv STIRtmp2.hv STIRtmp3.hv", + min_threshold, max_threshold); + if (run_stir_math(cmd_args)) { + out_data_ptr = read_from_file>("STIRtmpout.hv"); + calc_data.fill(0); + VoxelsOnCartesianGrid data2_thresholded(data2); + VoxelsOnCartesianGrid data3_thresholded(data3); + threshold_upper_lower(data2_thresholded.begin_all(), data2_thresholded.end_all(), min_threshold, max_threshold); + threshold_upper_lower(data3_thresholded.begin_all(), data3_thresholded.end_all(), min_threshold, max_threshold); + calc_data += + data1 + (data2_thresholded * data2_thresholded) * 3.2F + 3.1F + (data3_thresholded * data3_thresholded) * 3.2F + 3.1F; + + check_if_equal(calc_data, *out_data_ptr, "test with thresholding, power and scalar multiplication and addition"); + } } // add with power etc and including-first - if (run_stir_math("--power 2 --times-scalar 3.2 --including-first STIRtmpout.v STIRtmp1.hv STIRtmp2.hv STIRtmp3.hv")) - { - out_data_ptr = read_from_file >("STIRtmpout.hv"); + if (run_stir_math("--power 2 --times-scalar 3.2 --including-first STIRtmpout.v STIRtmp1.hv STIRtmp2.hv STIRtmp3.hv")) { + out_data_ptr = read_from_file>("STIRtmpout.hv"); calc_data.fill(0); - calc_data += (data1*data1 + data2*data2 + data3*data3)*3.2F; - check_if_equal( calc_data, *out_data_ptr,"test with power and scalar multiplication with --including-first"); + calc_data += (data1 * data1 + data2 * data2 + data3 * data3) * 3.2F; + check_if_equal(calc_data, *out_data_ptr, "test with power and scalar multiplication with --including-first"); } - // add with power etc and accumulate - if (run_stir_math("--accumulate --power 2 --times-scalar 3.2 --add-scalar 3.1 --divide-scalar 2.1 --mult STIRtmp1.hv STIRtmp3.hv")) - { + // add with power etc and accumulate + if (run_stir_math( + "--accumulate --power 2 --times-scalar 3.2 --add-scalar 3.1 --divide-scalar 2.1 --mult STIRtmp1.hv STIRtmp3.hv")) { calc_data.fill(0); calc_data += data1; - calc_data += (data3*data3)*3.2F/2.1F + 3.1F; - out_data_ptr = read_from_file >("STIRtmp1.hv"); - check_if_equal( calc_data, *out_data_ptr,"test with power and scalar multiplication, division and addition with --accumulate"); + calc_data += (data3 * data3) * 3.2F / 2.1F + 3.1F; + out_data_ptr = read_from_file>("STIRtmp1.hv"); + check_if_equal(calc_data, *out_data_ptr, + "test with power and scalar multiplication, division and addition with --accumulate"); } // add with power etc and accumulate and including-first - if (run_stir_math("--accumulate --power 2 --times-scalar 3.2 --including-first STIRtmp2.hv STIRtmp3.hv")) - { + if (run_stir_math("--accumulate --power 2 --times-scalar 3.2 --including-first STIRtmp2.hv STIRtmp3.hv")) { calc_data.fill(0); calc_data += data2; calc_data *= calc_data * 3.2F; - calc_data += (data3*data3)*3.2F; - out_data_ptr = read_from_file >("STIRtmp2.hv"); - check_if_equal( calc_data, *out_data_ptr,"test with power and scalar multiplication with --including-first and --accumulate"); - } + calc_data += (data3 * data3) * 3.2F; + out_data_ptr = read_from_file>("STIRtmp2.hv"); + check_if_equal(calc_data, *out_data_ptr, + "test with power and scalar multiplication with --including-first and --accumulate"); + } remove("STIRtmp1.ahv"); remove("STIRtmp1.hv"); @@ -218,16 +203,15 @@ stir_mathTests::run_tests() // projdata { - // to keep testing code below as close as possible to the image case, we'll just + // to keep testing code below as close as possible to the image case, we'll just // take a single segment in the data. shared_ptr scanner_ptr(new Scanner(Scanner::E953)); - shared_ptr proj_data_info_ptr( - ProjDataInfo::ProjDataInfoCTI(scanner_ptr, - /*span=*/1, - /*max_delta=*/0, - /*num_views=*/8, - /*num_tang_poss=*/16)); + shared_ptr proj_data_info_ptr(ProjDataInfo::ProjDataInfoCTI(scanner_ptr, + /*span=*/1, + /*max_delta=*/0, + /*num_views=*/8, + /*num_tang_poss=*/16)); SegmentByView data1 = proj_data_info_ptr->get_empty_segment_by_view(0); SegmentByView data2 = proj_data_info_ptr->get_empty_segment_by_view(0); SegmentByView data3 = proj_data_info_ptr->get_empty_segment_by_view(0); @@ -239,98 +223,91 @@ stir_mathTests::run_tests() generate(data1.begin_all(), data1.end_all(), rand); generate(data2.begin_all(), data2.end_all(), rand); generate(data3.begin_all(), data3.end_all(), rand); - + // first create files and close them afterwards // This is necessary because system() requires all streams to be flushed. { shared_ptr exam_info_sptr(new ExamInfo); - + ProjDataInterfile proj_data1(exam_info_sptr, proj_data_info_ptr, "STIRtmp1"); ProjDataInterfile proj_data2(exam_info_sptr, proj_data_info_ptr, "STIRtmp2"); ProjDataInterfile proj_data3(exam_info_sptr, proj_data_info_ptr, "STIRtmp3"); - + proj_data1.set_segment(data1); proj_data2.set_segment(data2); proj_data3.set_segment(data3); - - set_tolerance(data1.find_max()/10000); + + set_tolerance(data1.find_max() / 10000); } - + // add - if (run_stir_math("-s STIRtmpout.hs STIRtmp1.hs STIRtmp2.hs STIRtmp3.hs")) - { + if (run_stir_math("-s STIRtmpout.hs STIRtmp1.hs STIRtmp2.hs STIRtmp3.hs")) { out_data_ptr = ProjData::read_from_file("STIRtmpout.hs"); calc_data = data1; calc_data += data2 + data3; - - check_if_equal( calc_data, out_data_ptr->get_segment_by_view(0), - "test on adding"); + + check_if_equal(calc_data, out_data_ptr->get_segment_by_view(0), "test on adding"); } // mult - if (run_stir_math("-s --mult STIRtmpout.hs STIRtmp1.hs STIRtmp2.hs STIRtmp3.hs")) - { + if (run_stir_math("-s --mult STIRtmpout.hs STIRtmp1.hs STIRtmp2.hs STIRtmp3.hs")) { out_data_ptr = ProjData::read_from_file("STIRtmpout.hs"); calc_data = data1; calc_data *= data2 * data3; - - check_if_equal( calc_data, out_data_ptr->get_segment_by_view(0), - "test on multiplying"); + + check_if_equal(calc_data, out_data_ptr->get_segment_by_view(0), "test on multiplying"); } // add with power etc // range for rand() is 0 to RAND_MAX - const float min_threshold = RAND_MAX/5.F; - const float max_threshold = RAND_MAX/2.F; + const float min_threshold = RAND_MAX / 5.F; + const float max_threshold = RAND_MAX / 2.F; { char cmd_args[1000]; - sprintf(cmd_args, "-s --add-scalar 3.1 --power 2 --times-scalar 3.2 --min-threshold %g --max-threshold %g " - "STIRtmpout.hs STIRtmp1.hs STIRtmp2.hs STIRtmp3.hs", - min_threshold, max_threshold); - if (run_stir_math(cmd_args)) - { - out_data_ptr = ProjData::read_from_file("STIRtmpout.hs"); - calc_data.fill(0); - SegmentByView data2_thresholded(data2); - SegmentByView data3_thresholded(data3); - threshold_upper_lower( data2_thresholded.begin_all(), data2_thresholded.end_all(), - min_threshold, max_threshold); - threshold_upper_lower( data3_thresholded.begin_all(), data3_thresholded.end_all(), - min_threshold, max_threshold); - calc_data += data1 + (data2_thresholded*data2_thresholded)*3.2F + 3.1F + (data3_thresholded*data3_thresholded)*3.2F + 3.1F; - - check_if_equal( calc_data, out_data_ptr->get_segment_by_view(0), - "test with thresholding, power and scalar multiplication and addition"); - } + sprintf(cmd_args, + "-s --add-scalar 3.1 --power 2 --times-scalar 3.2 --min-threshold %g --max-threshold %g " + "STIRtmpout.hs STIRtmp1.hs STIRtmp2.hs STIRtmp3.hs", + min_threshold, max_threshold); + if (run_stir_math(cmd_args)) { + out_data_ptr = ProjData::read_from_file("STIRtmpout.hs"); + calc_data.fill(0); + SegmentByView data2_thresholded(data2); + SegmentByView data3_thresholded(data3); + threshold_upper_lower(data2_thresholded.begin_all(), data2_thresholded.end_all(), min_threshold, max_threshold); + threshold_upper_lower(data3_thresholded.begin_all(), data3_thresholded.end_all(), min_threshold, max_threshold); + calc_data += + data1 + (data2_thresholded * data2_thresholded) * 3.2F + 3.1F + (data3_thresholded * data3_thresholded) * 3.2F + 3.1F; + + check_if_equal(calc_data, out_data_ptr->get_segment_by_view(0), + "test with thresholding, power and scalar multiplication and addition"); + } } // add with power etc and including-first - if (run_stir_math("-s --power 2 --times-scalar 3.2 --including-first STIRtmpout.hs STIRtmp1.hs STIRtmp2.hs STIRtmp3.hs")) - { + if (run_stir_math("-s --power 2 --times-scalar 3.2 --including-first STIRtmpout.hs STIRtmp1.hs STIRtmp2.hs STIRtmp3.hs")) { out_data_ptr = ProjData::read_from_file("STIRtmpout.hs"); calc_data.fill(0); - calc_data += (data1*data1 + data2*data2 + data3*data3)*3.2F; - check_if_equal( calc_data, out_data_ptr->get_segment_by_view(0), - "test with power and scalar multiplication with --including-first"); + calc_data += (data1 * data1 + data2 * data2 + data3 * data3) * 3.2F; + check_if_equal(calc_data, out_data_ptr->get_segment_by_view(0), + "test with power and scalar multiplication with --including-first"); } - // add with power etc and accumulate - if (run_stir_math("-s --accumulate --power 2 --times-scalar 3.2 --divide-scalar 1.9 --add-scalar 3.1 --mult STIRtmp1.hs STIRtmp3.hs")) - { + // add with power etc and accumulate + if (run_stir_math( + "-s --accumulate --power 2 --times-scalar 3.2 --divide-scalar 1.9 --add-scalar 3.1 --mult STIRtmp1.hs STIRtmp3.hs")) { calc_data.fill(0); calc_data += data1; - calc_data += (data3*data3)*3.2F/1.9F+3.1F; + calc_data += (data3 * data3) * 3.2F / 1.9F + 3.1F; out_data_ptr = ProjData::read_from_file("STIRtmp1.hs"); - check_if_equal( calc_data, out_data_ptr->get_segment_by_view(0), - "test with power and scalar multiplication, division and addition with --accumulate"); + check_if_equal(calc_data, out_data_ptr->get_segment_by_view(0), + "test with power and scalar multiplication, division and addition with --accumulate"); } // add with power etc and accumulate and including-first - if (run_stir_math("-s --accumulate --power 2 --times-scalar 3.2 --including-first STIRtmp2.hs STIRtmp3.hs")) - { + if (run_stir_math("-s --accumulate --power 2 --times-scalar 3.2 --including-first STIRtmp2.hs STIRtmp3.hs")) { calc_data.fill(0); calc_data += data2; calc_data *= calc_data * 3.2F; - calc_data += (data3*data3)*3.2F; + calc_data += (data3 * data3) * 3.2F; out_data_ptr = ProjData::read_from_file("STIRtmp2.hs"); - check_if_equal( calc_data, out_data_ptr->get_segment_by_view(0), - "test with power and scalar multiplication with --including-first and --accumulate"); + check_if_equal(calc_data, out_data_ptr->get_segment_by_view(0), + "test with power and scalar multiplication with --including-first and --accumulate"); } remove("STIRtmp1.hs"); @@ -342,18 +319,15 @@ stir_mathTests::run_tests() remove("STIRtmpout.hs"); remove("STIRtmpout.s"); } - } END_NAMESPACE_STIR - USING_NAMESPACE_STIR - -int main(int argc, char** argv) -{ - stir_mathTests tests(argc==2?argv[1] : "stir_math"); +int +main(int argc, char** argv) { + stir_mathTests tests(argc == 2 ? argv[1] : "stir_math"); tests.run_tests(); return tests.main_return_value(); } diff --git a/src/test/test_time_of_flight.cxx b/src/test/test_time_of_flight.cxx index 3ddd24922a..ae56468b82 100644 --- a/src/test/test_time_of_flight.cxx +++ b/src/test/test_time_of_flight.cxx @@ -45,44 +45,33 @@ START_NAMESPACE_STIR //! a key tight. //! \author Nikos Efthimiou //! -class cache_index{ +class cache_index { public: - cache_index(): - key(0){ - view_num = 0; - seg_num = 0; - } + cache_index() : key(0) { + view_num = 0; + seg_num = 0; + } - inline bool operator==(const cache_index& Y) const - { - return view_num == Y.view_num && - seg_num == Y.seg_num && - key == Y.key; - } + inline bool operator==(const cache_index& Y) const { return view_num == Y.view_num && seg_num == Y.seg_num && key == Y.key; } - inline bool operator!=(const cache_index& Y) const - { - return !(*this == Y); - } + inline bool operator!=(const cache_index& Y) const { return !(*this == Y); } - inline bool operator< (const cache_index& Y) const - { - return view_num < Y.view_num && - seg_num < Y.seg_num && - key < Y.key; - } + inline bool operator<(const cache_index& Y) const { return view_num < Y.view_num && seg_num < Y.seg_num && key < Y.key; } - int view_num; - int seg_num; - boost::uint32_t key; + int view_num; + int seg_num; + boost::uint32_t key; }; // Helper class. -class FloatFloat{ +class FloatFloat { public: - FloatFloat() { float1 = 0.f; float2 = 0.f;} - float float1; - float float2; + FloatFloat() { + float1 = 0.f; + float2 = 0.f; + } + float float1; + float float2; }; /*! @@ -100,401 +89,355 @@ class FloatFloat{ \warning If you change the mashing factor the test_tof_proj_data_info() will fail. \warning The execution time strongly depends on the value of the TOF mashing factor */ -class TOF_Tests : public RunTests -{ +class TOF_Tests : public RunTests { public: - void run_tests(); + void run_tests(); private: + void test_tof_proj_data_info(); - void test_tof_proj_data_info(); - - //! This checks peaks a specific bin, finds the LOR and applies all the - //! kernels of all available timing positions. Then check if the sum - //! of the TOF bins is equal to the non-TOF LOR. - void test_tof_kernel_application(bool export_to_file); - - //! Exports the nonTOF LOR to a file indicated by the current_id value - //! in the filename. - void - export_lor(ProjMatrixElemsForOneBin& probabilities, - const CartesianCoordinate3D& point1, - const CartesianCoordinate3D& point2,int current_id); - - //! Exports the TOF LOR. The TOFid is indicated in the fileName. - //! Only the common elements with the nonTOF LOR will be written in the file. - //! Although changing that is straight forward. - void - export_lor(ProjMatrixElemsForOneBin& probabilities, - const CartesianCoordinate3D& point1, - const CartesianCoordinate3D& point2,int current_id, - ProjMatrixElemsForOneBin& template_probabilities); - - shared_ptr test_scanner_sptr; - shared_ptr test_proj_data_info_sptr; - - shared_ptr test_nonTOF_scanner_sptr; - shared_ptr test_nonTOF_proj_data_info_sptr; - - shared_ptr > test_discretised_density_sptr; - shared_ptr test_proj_matrix_sptr; - shared_ptr test_nonTOF_proj_matrix_sptr; - - shared_ptr projector_pair_sptr; - shared_ptr symmetries_used_sptr; -}; + //! This checks peaks a specific bin, finds the LOR and applies all the + //! kernels of all available timing positions. Then check if the sum + //! of the TOF bins is equal to the non-TOF LOR. + void test_tof_kernel_application(bool export_to_file); -void -TOF_Tests::run_tests() -{ - // New Scanner - test_scanner_sptr.reset(new Scanner(Scanner::PETMR_Signa)); - test_nonTOF_scanner_sptr.reset(new Scanner(Scanner::PETMR_Signa_nonTOF)); - - // New Proj_Data_Info - const int test_tof_mashing_factor = 39; // to have 9 TOF bins (381/39=9) - test_proj_data_info_sptr.reset(ProjDataInfo::ProjDataInfoCTI(test_scanner_sptr, - 1,test_scanner_sptr->get_num_rings() -1, - test_scanner_sptr->get_num_detectors_per_ring()/2, - test_scanner_sptr->get_max_num_non_arccorrected_bins(), - /* arc_correction*/false)); - test_proj_data_info_sptr->set_tof_mash_factor(test_tof_mashing_factor); - - test_nonTOF_proj_data_info_sptr.reset(ProjDataInfo::ProjDataInfoCTI(test_nonTOF_scanner_sptr, - 1,test_scanner_sptr->get_num_rings() -1, - test_scanner_sptr->get_num_detectors_per_ring()/2, - test_scanner_sptr->get_max_num_non_arccorrected_bins(), - /* arc_correction*/false)); - - test_tof_proj_data_info(); - // test_tof_geometry_1(); - - // New Discretised Density - test_discretised_density_sptr.reset( new VoxelsOnCartesianGrid (*test_proj_data_info_sptr, 1.f, - CartesianCoordinate3D(0.f, 0.f, 0.f), - CartesianCoordinate3D(-1, -1, -1))); - // New ProjMatrix - test_proj_matrix_sptr.reset(new ProjMatrixByBinUsingRayTracing()); - dynamic_cast(test_proj_matrix_sptr.get())->set_num_tangential_LORs(1); - dynamic_cast(test_proj_matrix_sptr.get())->set_up(test_proj_data_info_sptr, test_discretised_density_sptr); - - test_nonTOF_proj_matrix_sptr.reset(new ProjMatrixByBinUsingRayTracing()); - dynamic_cast(test_nonTOF_proj_matrix_sptr.get())->set_num_tangential_LORs(1); - dynamic_cast(test_nonTOF_proj_matrix_sptr.get())->set_up(test_nonTOF_proj_data_info_sptr, test_discretised_density_sptr); - - // Switch to true in order to export the LORs at files in the current directory - test_tof_kernel_application(false); -} + //! Exports the nonTOF LOR to a file indicated by the current_id value + //! in the filename. + void export_lor(ProjMatrixElemsForOneBin& probabilities, const CartesianCoordinate3D& point1, + const CartesianCoordinate3D& point2, int current_id); -void -TOF_Tests::test_tof_proj_data_info() -{ - const int correct_tof_mashing_factor = 39; - const int num_timing_positions = 9; - float correct_width_of_tof_bin = test_scanner_sptr->get_size_of_timing_pos() * - test_proj_data_info_sptr->get_tof_mash_factor() * 0.299792458f/2; - float correct_timing_locations[num_timing_positions] = {-360.201f/2 + correct_width_of_tof_bin/2, -280.156f/2 + correct_width_of_tof_bin/2, - -200.111f/2 + correct_width_of_tof_bin/2, -120.067f/2 + correct_width_of_tof_bin/2, - 0.0f, 40.022f/2 + correct_width_of_tof_bin/2, - 120.067f/2 + correct_width_of_tof_bin/2, 200.111f/2 + correct_width_of_tof_bin/2, - 280.156f/2+ correct_width_of_tof_bin/2}; - - check_if_equal(correct_tof_mashing_factor, - test_proj_data_info_sptr->get_tof_mash_factor(), "Different TOF mashing factor."); - - check_if_equal(num_timing_positions, - test_proj_data_info_sptr->get_num_tof_poss(), "Different number of timing positions."); - - for (int timing_num = test_proj_data_info_sptr->get_min_tof_pos_num(), counter = 0; - timing_num <= test_proj_data_info_sptr->get_max_tof_pos_num(); ++ timing_num, counter++) - { - Bin bin(0, 0, 0, 0, timing_num, 1.f); + //! Exports the TOF LOR. The TOFid is indicated in the fileName. + //! Only the common elements with the nonTOF LOR will be written in the file. + //! Although changing that is straight forward. + void export_lor(ProjMatrixElemsForOneBin& probabilities, const CartesianCoordinate3D& point1, + const CartesianCoordinate3D& point2, int current_id, ProjMatrixElemsForOneBin& template_probabilities); - check_if_equal(static_cast(correct_width_of_tof_bin), - static_cast(test_proj_data_info_sptr->get_sampling_in_k(bin)), "Error in get_sampling_in_k()"); - check_if_equal(static_cast(correct_timing_locations[counter]), - static_cast(test_proj_data_info_sptr->get_k(bin)), "Error in get_sampling_in_k()"); - } + shared_ptr test_scanner_sptr; + shared_ptr test_proj_data_info_sptr; - float total_width = test_proj_data_info_sptr->get_k(Bin(0,0,0,0,test_proj_data_info_sptr->get_max_tof_pos_num(),1.f)) - - test_proj_data_info_sptr->get_k(Bin(0,0,0,0,test_proj_data_info_sptr->get_min_tof_pos_num(),1.f)) - + test_proj_data_info_sptr->get_sampling_in_k(Bin(0,0,0,0,0,1.f)); + shared_ptr test_nonTOF_scanner_sptr; + shared_ptr test_nonTOF_proj_data_info_sptr; - set_tolerance(static_cast(0.005)); - check_if_equal(static_cast(total_width), static_cast(test_proj_data_info_sptr->get_coincidence_window_width()), - "Coincidence widths don't match."); + shared_ptr> test_discretised_density_sptr; + shared_ptr test_proj_matrix_sptr; + shared_ptr test_nonTOF_proj_matrix_sptr; + shared_ptr projector_pair_sptr; + shared_ptr symmetries_used_sptr; +}; +void +TOF_Tests::run_tests() { + // New Scanner + test_scanner_sptr.reset(new Scanner(Scanner::PETMR_Signa)); + test_nonTOF_scanner_sptr.reset(new Scanner(Scanner::PETMR_Signa_nonTOF)); + + // New Proj_Data_Info + const int test_tof_mashing_factor = 39; // to have 9 TOF bins (381/39=9) + test_proj_data_info_sptr.reset(ProjDataInfo::ProjDataInfoCTI(test_scanner_sptr, 1, test_scanner_sptr->get_num_rings() - 1, + test_scanner_sptr->get_num_detectors_per_ring() / 2, + test_scanner_sptr->get_max_num_non_arccorrected_bins(), + /* arc_correction*/ false)); + test_proj_data_info_sptr->set_tof_mash_factor(test_tof_mashing_factor); + + test_nonTOF_proj_data_info_sptr.reset(ProjDataInfo::ProjDataInfoCTI( + test_nonTOF_scanner_sptr, 1, test_scanner_sptr->get_num_rings() - 1, test_scanner_sptr->get_num_detectors_per_ring() / 2, + test_scanner_sptr->get_max_num_non_arccorrected_bins(), + /* arc_correction*/ false)); + + test_tof_proj_data_info(); + // test_tof_geometry_1(); + + // New Discretised Density + test_discretised_density_sptr.reset(new VoxelsOnCartesianGrid( + *test_proj_data_info_sptr, 1.f, CartesianCoordinate3D(0.f, 0.f, 0.f), CartesianCoordinate3D(-1, -1, -1))); + // New ProjMatrix + test_proj_matrix_sptr.reset(new ProjMatrixByBinUsingRayTracing()); + dynamic_cast(test_proj_matrix_sptr.get())->set_num_tangential_LORs(1); + dynamic_cast(test_proj_matrix_sptr.get()) + ->set_up(test_proj_data_info_sptr, test_discretised_density_sptr); + + test_nonTOF_proj_matrix_sptr.reset(new ProjMatrixByBinUsingRayTracing()); + dynamic_cast(test_nonTOF_proj_matrix_sptr.get())->set_num_tangential_LORs(1); + dynamic_cast(test_nonTOF_proj_matrix_sptr.get()) + ->set_up(test_nonTOF_proj_data_info_sptr, test_discretised_density_sptr); + + // Switch to true in order to export the LORs at files in the current directory + test_tof_kernel_application(false); } void -TOF_Tests::test_tof_kernel_application(bool print_to_file) -{ - int seg_num = 0; - int view_num = 0; - int axial_num = 0; - int tang_num = 0; +TOF_Tests::test_tof_proj_data_info() { + const int correct_tof_mashing_factor = 39; + const int num_timing_positions = 9; + float correct_width_of_tof_bin = + test_scanner_sptr->get_size_of_timing_pos() * test_proj_data_info_sptr->get_tof_mash_factor() * 0.299792458f / 2; + float correct_timing_locations[num_timing_positions] = {-360.201f / 2 + correct_width_of_tof_bin / 2, + -280.156f / 2 + correct_width_of_tof_bin / 2, + -200.111f / 2 + correct_width_of_tof_bin / 2, + -120.067f / 2 + correct_width_of_tof_bin / 2, + 0.0f, + 40.022f / 2 + correct_width_of_tof_bin / 2, + 120.067f / 2 + correct_width_of_tof_bin / 2, + 200.111f / 2 + correct_width_of_tof_bin / 2, + 280.156f / 2 + correct_width_of_tof_bin / 2}; + + check_if_equal(correct_tof_mashing_factor, test_proj_data_info_sptr->get_tof_mash_factor(), "Different TOF mashing factor."); + + check_if_equal(num_timing_positions, test_proj_data_info_sptr->get_num_tof_poss(), "Different number of timing positions."); + + for (int timing_num = test_proj_data_info_sptr->get_min_tof_pos_num(), counter = 0; + timing_num <= test_proj_data_info_sptr->get_max_tof_pos_num(); ++timing_num, counter++) { + Bin bin(0, 0, 0, 0, timing_num, 1.f); + + check_if_equal(static_cast(correct_width_of_tof_bin), + static_cast(test_proj_data_info_sptr->get_sampling_in_k(bin)), "Error in get_sampling_in_k()"); + check_if_equal(static_cast(correct_timing_locations[counter]), + static_cast(test_proj_data_info_sptr->get_k(bin)), "Error in get_sampling_in_k()"); + } + + float total_width = test_proj_data_info_sptr->get_k(Bin(0, 0, 0, 0, test_proj_data_info_sptr->get_max_tof_pos_num(), 1.f)) - + test_proj_data_info_sptr->get_k(Bin(0, 0, 0, 0, test_proj_data_info_sptr->get_min_tof_pos_num(), 1.f)) + + test_proj_data_info_sptr->get_sampling_in_k(Bin(0, 0, 0, 0, 0, 1.f)); + + set_tolerance(static_cast(0.005)); + check_if_equal(static_cast(total_width), static_cast(test_proj_data_info_sptr->get_coincidence_window_width()), + "Coincidence widths don't match."); +} - float nonTOF_val = 0.0; - float TOF_val = 0.0; +void +TOF_Tests::test_tof_kernel_application(bool print_to_file) { + int seg_num = 0; + int view_num = 0; + int axial_num = 0; + int tang_num = 0; - ProjMatrixElemsForOneBin proj_matrix_row; - ProjMatrixElemsForOneBin sum_tof_proj_matrix_row; + float nonTOF_val = 0.0; + float TOF_val = 0.0; - HighResWallClockTimer t; - std::vector times_of_tofing; + ProjMatrixElemsForOneBin proj_matrix_row; + ProjMatrixElemsForOneBin sum_tof_proj_matrix_row; - ProjDataInfoCylindrical* proj_data_ptr = - dynamic_cast (test_proj_data_info_sptr.get()); + HighResWallClockTimer t; + std::vector times_of_tofing; -// ProjDataInfoCylindrical* proj_data_nonTOF_ptr = -// dynamic_cast (test_nonTOF_proj_data_info_sptr.get()); + ProjDataInfoCylindrical* proj_data_ptr = dynamic_cast(test_proj_data_info_sptr.get()); - LORInAxialAndNoArcCorrSinogramCoordinates lor; + // ProjDataInfoCylindrical* proj_data_nonTOF_ptr = + // dynamic_cast (test_nonTOF_proj_data_info_sptr.get()); - Bin this_bin(seg_num, view_num, axial_num, tang_num, 1.f); + LORInAxialAndNoArcCorrSinogramCoordinates lor; - t.reset(); t.start(); - test_nonTOF_proj_matrix_sptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, this_bin); - t.stop(); + Bin this_bin(seg_num, view_num, axial_num, tang_num, 1.f); - std::cerr<<"Execution time for nonTOF: "<get_LOR(lor, this_bin); - LORAs2Points lor2(lor); + t.reset(); + t.start(); + test_nonTOF_proj_matrix_sptr->get_proj_matrix_elems_for_one_bin(proj_matrix_row, this_bin); + t.stop(); - if (print_to_file) - export_lor(proj_matrix_row, - lor2.p1(), lor2.p2(), 500000000); + std::cerr << "Execution time for nonTOF: " << t.value() << std::endl; + proj_data_ptr->get_LOR(lor, this_bin); + LORAs2Points lor2(lor); - for (int timing_num = test_proj_data_info_sptr->get_min_tof_pos_num(); - timing_num <= test_proj_data_info_sptr->get_max_tof_pos_num(); ++ timing_num) - { - ProjMatrixElemsForOneBin new_proj_matrix_row; - Bin bin(seg_num, view_num, axial_num, tang_num, timing_num, 1.f); - - t.reset(); t.start(); - test_proj_matrix_sptr->get_proj_matrix_elems_for_one_bin(new_proj_matrix_row, - bin); - t.stop(); - times_of_tofing.push_back(t.value()); - - if (print_to_file) - export_lor(new_proj_matrix_row, - lor2.p1(), lor2.p2(), timing_num, - proj_matrix_row); - - - if (sum_tof_proj_matrix_row.size() > 0) - { - ProjMatrixElemsForOneBin::iterator element_ptr = new_proj_matrix_row.begin(); - while (element_ptr != new_proj_matrix_row.end()) - { - ProjMatrixElemsForOneBin::iterator sum_element_ptr = sum_tof_proj_matrix_row.begin(); - bool found = false; - while(sum_element_ptr != sum_tof_proj_matrix_row.end()) - { - if(element_ptr->get_coords() == sum_element_ptr->get_coords()) - { - float new_value = element_ptr->get_value() + sum_element_ptr->get_value(); - *sum_element_ptr = ProjMatrixElemsForOneBin::value_type(element_ptr->get_coords(), new_value); - found = true; - break; - } - ++sum_element_ptr; - } - if (!found) - { - sum_tof_proj_matrix_row.push_back(ProjMatrixElemsForOneBin::value_type(element_ptr->get_coords(), - element_ptr->get_value())); - break; - } - ++element_ptr; - } + if (print_to_file) + export_lor(proj_matrix_row, lor2.p1(), lor2.p2(), 500000000); + + for (int timing_num = test_proj_data_info_sptr->get_min_tof_pos_num(); + timing_num <= test_proj_data_info_sptr->get_max_tof_pos_num(); ++timing_num) { + ProjMatrixElemsForOneBin new_proj_matrix_row; + Bin bin(seg_num, view_num, axial_num, tang_num, timing_num, 1.f); + t.reset(); + t.start(); + test_proj_matrix_sptr->get_proj_matrix_elems_for_one_bin(new_proj_matrix_row, bin); + t.stop(); + times_of_tofing.push_back(t.value()); + + if (print_to_file) + export_lor(new_proj_matrix_row, lor2.p1(), lor2.p2(), timing_num, proj_matrix_row); + + if (sum_tof_proj_matrix_row.size() > 0) { + ProjMatrixElemsForOneBin::iterator element_ptr = new_proj_matrix_row.begin(); + while (element_ptr != new_proj_matrix_row.end()) { + ProjMatrixElemsForOneBin::iterator sum_element_ptr = sum_tof_proj_matrix_row.begin(); + bool found = false; + while (sum_element_ptr != sum_tof_proj_matrix_row.end()) { + if (element_ptr->get_coords() == sum_element_ptr->get_coords()) { + float new_value = element_ptr->get_value() + sum_element_ptr->get_value(); + *sum_element_ptr = ProjMatrixElemsForOneBin::value_type(element_ptr->get_coords(), new_value); + found = true; + break; + } + ++sum_element_ptr; } - else - { - ProjMatrixElemsForOneBin::iterator element_ptr = new_proj_matrix_row.begin(); - while (element_ptr != new_proj_matrix_row.end()) - { - sum_tof_proj_matrix_row.push_back(ProjMatrixElemsForOneBin::value_type(element_ptr->get_coords(), - element_ptr->get_value())); - ++element_ptr; - } + if (!found) { + sum_tof_proj_matrix_row.push_back( + ProjMatrixElemsForOneBin::value_type(element_ptr->get_coords(), element_ptr->get_value())); + break; } + ++element_ptr; + } + } else { + ProjMatrixElemsForOneBin::iterator element_ptr = new_proj_matrix_row.begin(); + while (element_ptr != new_proj_matrix_row.end()) { + sum_tof_proj_matrix_row.push_back( + ProjMatrixElemsForOneBin::value_type(element_ptr->get_coords(), element_ptr->get_value())); + ++element_ptr; + } } + } - // Get value of nonTOF LOR, for central voxels only + // Get value of nonTOF LOR, for central voxels only - { - ProjMatrixElemsForOneBin::iterator element_ptr = proj_matrix_row.begin(); - while (element_ptr != proj_matrix_row.end()) - { - if (element_ptr->get_value() > nonTOF_val) - nonTOF_val = element_ptr->get_value(); - ++element_ptr; - } + { + ProjMatrixElemsForOneBin::iterator element_ptr = proj_matrix_row.begin(); + while (element_ptr != proj_matrix_row.end()) { + if (element_ptr->get_value() > nonTOF_val) + nonTOF_val = element_ptr->get_value(); + ++element_ptr; } + } - // Get value of TOF LOR, for central voxels only + // Get value of TOF LOR, for central voxels only - { - ProjMatrixElemsForOneBin::iterator element_ptr = sum_tof_proj_matrix_row.begin(); - while (element_ptr != sum_tof_proj_matrix_row.end()) - { - if (element_ptr->get_value() > TOF_val) - TOF_val = element_ptr->get_value(); - ++element_ptr; - } + { + ProjMatrixElemsForOneBin::iterator element_ptr = sum_tof_proj_matrix_row.begin(); + while (element_ptr != sum_tof_proj_matrix_row.end()) { + if (element_ptr->get_value() > TOF_val) + TOF_val = element_ptr->get_value(); + ++element_ptr; } + } + check_if_equal(static_cast(nonTOF_val), static_cast(TOF_val), + "Sum over nonTOF LOR does not match sum over TOF LOR."); - check_if_equal(static_cast(nonTOF_val), static_cast(TOF_val), - "Sum over nonTOF LOR does not match sum over TOF LOR."); + { + double mean = 0.0; + for (unsigned i = 0; i < times_of_tofing.size(); i++) + mean += times_of_tofing.at(i); - { - double mean = 0.0; - for (unsigned i = 0; i < times_of_tofing.size(); i++) - mean += times_of_tofing.at(i); + mean /= (times_of_tofing.size()); - mean /= (times_of_tofing.size()); + double s = 0.0; + for (unsigned i = 0; i < times_of_tofing.size(); i++) + s += (times_of_tofing.at(i) - mean) * (times_of_tofing.at(i) - mean) / (times_of_tofing.size() - 1); - double s=0.0; - for (unsigned i = 0; i < times_of_tofing.size(); i++) - s += (times_of_tofing.at(i) - mean) * (times_of_tofing.at(i) - mean) / (times_of_tofing.size()-1); + s = std::sqrt(s); + std::cerr << "Execution time for TOF: " << mean << " ±" << s; + } - s = std::sqrt(s); - std::cerr<<"Execution time for TOF: "<& point1, - const CartesianCoordinate3D& point2, - int current_id) -{ - std::ofstream myfile; - std::string file_name = "glor_" + boost::lexical_cast(current_id) + ".txt"; - myfile.open (file_name.c_str()); - - CartesianCoordinate3D voxel_center; - - std::vector lor_to_export; - lor_to_export.reserve(probabilities.size()); - - const CartesianCoordinate3D middle = (point1 + point2)*0.5f; - const CartesianCoordinate3D diff = point2 - middle; - - const float lor_length = 1.f / (std::sqrt(diff.x() * diff.x() + - diff.y() * diff.y() + - diff.z() * diff.z())); - - ProjMatrixElemsForOneBin::iterator element_ptr = probabilities.begin(); - while (element_ptr != probabilities.end()) - { - voxel_center = - test_discretised_density_sptr->get_physical_coordinates_for_indices (element_ptr->get_coords()); +TOF_Tests::export_lor(ProjMatrixElemsForOneBin& probabilities, const CartesianCoordinate3D& point1, + const CartesianCoordinate3D& point2, int current_id) { + std::ofstream myfile; + std::string file_name = "glor_" + boost::lexical_cast(current_id) + ".txt"; + myfile.open(file_name.c_str()); -// if(voxel_center.z() == 0.f) - { - project_point_on_a_line(point1, point2, voxel_center ); + CartesianCoordinate3D voxel_center; - const CartesianCoordinate3D x = voxel_center - middle; + std::vector lor_to_export; + lor_to_export.reserve(probabilities.size()); - const float d2 = -inner_product(x, diff) * lor_length; + const CartesianCoordinate3D middle = (point1 + point2) * 0.5f; + const CartesianCoordinate3D diff = point2 - middle; - FloatFloat tmp; - tmp.float1 = d2; + const float lor_length = 1.f / (std::sqrt(diff.x() * diff.x() + diff.y() * diff.y() + diff.z() * diff.z())); -// std::cerr<< voxel_center.x() << " " << voxel_center.y() << " " << voxel_center.z() << " " << -// d1 << " " << d2 << " " << d12 << " " << element_ptr->get_value() <get_physical_coordinates_for_indices(element_ptr->get_coords()); - tmp.float2 = element_ptr->get_value(); - lor_to_export.push_back(tmp); - } - ++element_ptr; + // if(voxel_center.z() == 0.f) + { + project_point_on_a_line(point1, point2, voxel_center); + + const CartesianCoordinate3D x = voxel_center - middle; + + const float d2 = -inner_product(x, diff) * lor_length; + + FloatFloat tmp; + tmp.float1 = d2; + + // std::cerr<< voxel_center.x() << " " << voxel_center.y() << " " << voxel_center.z() << " " << + // d1 << " " << d2 << " " << d12 << " " << element_ptr->get_value() <get_value(); + lor_to_export.push_back(tmp); } + ++element_ptr; + } - for (unsigned int i = 0; i < lor_to_export.size(); i++) - myfile << lor_to_export.at(i).float1 << " " << lor_to_export.at(i).float2 << std::endl; + for (unsigned int i = 0; i < lor_to_export.size(); i++) + myfile << lor_to_export.at(i).float1 << " " << lor_to_export.at(i).float2 << std::endl; - myfile << std::endl; - myfile.close(); + myfile << std::endl; + myfile.close(); } void -TOF_Tests:: -export_lor(ProjMatrixElemsForOneBin& probabilities, - const CartesianCoordinate3D& point1, - const CartesianCoordinate3D& point2, int current_id, - ProjMatrixElemsForOneBin& template_probabilities) -{ - std::ofstream myfile; - std::string file_name = "glor_" + boost::lexical_cast(current_id) + ".txt"; - myfile.open (file_name.c_str()); - - const CartesianCoordinate3D middle = (point1 + point2)*0.5f; - const CartesianCoordinate3D diff = point2 - middle; - - const float lor_length = 1.f / (std::sqrt(diff.x() * diff.x() + - diff.y() * diff.y() + - diff.z() * diff.z())); - - CartesianCoordinate3D voxel_center; - - std::vector lor_to_export; - lor_to_export.reserve(template_probabilities.size()); - - ProjMatrixElemsForOneBin::iterator tmpl_element_ptr = template_probabilities.begin(); - while (tmpl_element_ptr != template_probabilities.end()) +TOF_Tests::export_lor(ProjMatrixElemsForOneBin& probabilities, const CartesianCoordinate3D& point1, + const CartesianCoordinate3D& point2, int current_id, + ProjMatrixElemsForOneBin& template_probabilities) { + std::ofstream myfile; + std::string file_name = "glor_" + boost::lexical_cast(current_id) + ".txt"; + myfile.open(file_name.c_str()); + + const CartesianCoordinate3D middle = (point1 + point2) * 0.5f; + const CartesianCoordinate3D diff = point2 - middle; + + const float lor_length = 1.f / (std::sqrt(diff.x() * diff.x() + diff.y() * diff.y() + diff.z() * diff.z())); + + CartesianCoordinate3D voxel_center; + + std::vector lor_to_export; + lor_to_export.reserve(template_probabilities.size()); + + ProjMatrixElemsForOneBin::iterator tmpl_element_ptr = template_probabilities.begin(); + while (tmpl_element_ptr != template_probabilities.end()) { + voxel_center = test_discretised_density_sptr->get_physical_coordinates_for_indices(tmpl_element_ptr->get_coords()); + // if(voxel_center.z() == 0.f) { - voxel_center = - test_discretised_density_sptr->get_physical_coordinates_for_indices (tmpl_element_ptr->get_coords()); -// if(voxel_center.z() == 0.f) - { - project_point_on_a_line(point1, point2, voxel_center ); - - const CartesianCoordinate3D x = voxel_center - middle; - - const float d2 = -inner_product(x, diff) * lor_length; - - FloatFloat tmp; - tmp.float1 = d2; - - ProjMatrixElemsForOneBin::iterator element_ptr = probabilities.begin(); - - while (element_ptr != probabilities.end()) - { - if (element_ptr->get_coords() == tmpl_element_ptr->get_coords()) - { - tmp.float2 = element_ptr->get_value(); - lor_to_export.push_back(tmp); - break; - } - ++element_ptr; - } + project_point_on_a_line(point1, point2, voxel_center); + + const CartesianCoordinate3D x = voxel_center - middle; + + const float d2 = -inner_product(x, diff) * lor_length; + + FloatFloat tmp; + tmp.float1 = d2; + + ProjMatrixElemsForOneBin::iterator element_ptr = probabilities.begin(); + + while (element_ptr != probabilities.end()) { + if (element_ptr->get_coords() == tmpl_element_ptr->get_coords()) { + tmp.float2 = element_ptr->get_value(); + lor_to_export.push_back(tmp); + break; } - ++tmpl_element_ptr; + ++element_ptr; + } } + ++tmpl_element_ptr; + } - for (unsigned int i = 0; i < lor_to_export.size(); i++) - myfile << lor_to_export.at(i).float1 << " " << lor_to_export.at(i).float2 << std::endl; + for (unsigned int i = 0; i < lor_to_export.size(); i++) + myfile << lor_to_export.at(i).float1 << " " << lor_to_export.at(i).float2 << std::endl; - myfile << std::endl; - myfile.close(); + myfile << std::endl; + myfile.close(); } END_NAMESPACE_STIR -int main() -{ - USING_NAMESPACE_STIR - TOF_Tests tests; - tests.run_tests(); - return tests.main_return_value(); +int +main() { + USING_NAMESPACE_STIR + TOF_Tests tests; + tests.run_tests(); + return tests.main_return_value(); } diff --git a/src/test/test_warp_image.cxx b/src/test/test_warp_image.cxx index d442e683ac..65a09f7fa6 100644 --- a/src/test/test_warp_image.cxx +++ b/src/test/test_warp_image.cxx @@ -3,12 +3,12 @@ /* Copyright (C) 2013, King's College London This file is part of STIR. - + This file is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.3 of the License, or (at your option) any later version. - + This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -17,10 +17,10 @@ See STIR/LICENSE.txt for details */ /*! - \file + \file \ingroup test \ingroup spatial_transformation - + \brief A simple program to test the warp image functions \author Charalampos Tsoumpas */ @@ -46,135 +46,140 @@ START_NAMESPACE_STIR \brief Class with tests for warp_image functions. \ingroup test */ -class warp_imageTests : public RunTests -{ +class warp_imageTests : public RunTests { public: void run_tests(); }; void -warp_imageTests::run_tests() -{ +warp_imageTests::run_tests() { std::cerr << "Tests for warp_image" << std::endl; - CartesianCoordinate3D origin (0,1,2); - CartesianCoordinate3D grid_spacing (3,4,5); - - IndexRange<3> - range(CartesianCoordinate3D(0,-15,-14), - CartesianCoordinate3D(40,44,45)); - - VoxelsOnCartesianGrid image(range, origin, grid_spacing); + CartesianCoordinate3D origin(0, 1, 2); + CartesianCoordinate3D grid_spacing(3, 4, 5); + + IndexRange<3> range(CartesianCoordinate3D(0, -15, -14), CartesianCoordinate3D(40, 44, 45)); + + VoxelsOnCartesianGrid image(range, origin, grid_spacing); image.fill(0.F); - const BasicCoordinate<3,int> indices = make_coordinate(10,22,23); + const BasicCoordinate<3, int> indices = make_coordinate(10, 22, 23); image[indices] = 1.F; - VoxelsOnCartesianGrid motion_x(range, origin, grid_spacing); - VoxelsOnCartesianGrid motion_y(range, origin, grid_spacing); - VoxelsOnCartesianGrid motion_z(range, origin, grid_spacing); + VoxelsOnCartesianGrid motion_x(range, origin, grid_spacing); + VoxelsOnCartesianGrid motion_y(range, origin, grid_spacing); + VoxelsOnCartesianGrid motion_z(range, origin, grid_spacing); - motion_x.fill(3*grid_spacing[3]); - motion_y.fill(2*grid_spacing[2]); + motion_x.fill(3 * grid_spacing[3]); + motion_y.fill(2 * grid_spacing[2]); motion_z.fill(grid_spacing[1]); - // horrible way - but it works. I need to make it simpler. - const shared_ptr > image_sptr(image.clone()) ; - const shared_ptr > motion_x_sptr(motion_x.clone()) ; - const shared_ptr > motion_y_sptr(motion_y.clone()) ; - const shared_ptr > motion_z_sptr(motion_z.clone()) ; - std::vector > gate_sequence; + // horrible way - but it works. I need to make it simpler. + const shared_ptr> image_sptr(image.clone()); + const shared_ptr> motion_x_sptr(motion_x.clone()); + const shared_ptr> motion_y_sptr(motion_y.clone()); + const shared_ptr> motion_z_sptr(motion_z.clone()); + std::vector> gate_sequence; gate_sequence.resize(2); - for (unsigned int current_gate = 1; - current_gate <= 2; - ++current_gate) - { - gate_sequence[current_gate-1].first = current_gate; - gate_sequence[current_gate-1].second = 1; - } + for (unsigned int current_gate = 1; current_gate <= 2; ++current_gate) { + gate_sequence[current_gate - 1].first = current_gate; + gate_sequence[current_gate - 1].second = 1; + } TimeGateDefinitions gate_defs(gate_sequence); - const VoxelsOnCartesianGrid new_image=warp_image(image_sptr,motion_x_sptr,motion_y_sptr,motion_z_sptr,BSpline::BSplineType(1),0); - const BasicCoordinate<3,int> new_indices = make_coordinate(indices[1]-1,indices[2]-2,indices[3]-3); + const VoxelsOnCartesianGrid new_image = + warp_image(image_sptr, motion_x_sptr, motion_y_sptr, motion_z_sptr, BSpline::BSplineType(1), 0); + const BasicCoordinate<3, int> new_indices = make_coordinate(indices[1] - 1, indices[2] - 2, indices[3] - 3); { - check_if_equal(image[indices],1.F, "testing original image at non-zero point"); - check_if_equal(image[new_indices],0.F, "testing original image at new location"); - check_if_equal(new_image[indices],0.F, "testing warped image at original location"); - check_if_equal(new_image[new_indices],1.F, "testing warped image at new location"); + check_if_equal(image[indices], 1.F, "testing original image at non-zero point"); + check_if_equal(image[new_indices], 0.F, "testing original image at new location"); + check_if_equal(new_image[indices], 0.F, "testing warped image at original location"); + check_if_equal(new_image[new_indices], 1.F, "testing warped image at new location"); } std::cerr << "Tests for class GatedSpatialTransformation::warp_image etc" << std::endl; - const shared_ptr > new_image_sptr(new_image.clone()) ; - GatedDiscretisedDensity gated_image(image_sptr,2); - gated_image.set_density_sptr(image_sptr,1); - gated_image.set_density_sptr(new_image_sptr,2); + const shared_ptr> new_image_sptr(new_image.clone()); + GatedDiscretisedDensity gated_image(image_sptr, 2); + gated_image.set_density_sptr(image_sptr, 1); + gated_image.set_density_sptr(new_image_sptr, 2); gated_image.set_time_gate_definitions(gate_defs); - VoxelsOnCartesianGrid reverse_motion_x(range, origin, grid_spacing); - VoxelsOnCartesianGrid reverse_motion_y(range, origin, grid_spacing); - VoxelsOnCartesianGrid reverse_motion_z(range, origin, grid_spacing); - reverse_motion_x.fill(-3*grid_spacing[3]); - reverse_motion_y.fill(-2*grid_spacing[2]); - reverse_motion_z.fill(-1*grid_spacing[1]); - - // horrible way - but it works. I need to make it simpler. - const shared_ptr > reverse_motion1_x_sptr(motion_x.get_empty_copy()) ; - const shared_ptr > reverse_motion1_y_sptr(motion_y.get_empty_copy()) ; - const shared_ptr > reverse_motion1_z_sptr(motion_z.get_empty_copy()) ; - const shared_ptr > reverse_motion2_x_sptr(reverse_motion_x.clone()) ; - const shared_ptr > reverse_motion2_y_sptr(reverse_motion_y.clone()) ; - const shared_ptr > reverse_motion2_z_sptr(reverse_motion_z.clone()) ; - - GatedDiscretisedDensity reverse_gated_motion_x(image_sptr,2); - reverse_gated_motion_x.set_density_sptr(reverse_motion1_x_sptr,1); - reverse_gated_motion_x.set_density_sptr(reverse_motion2_x_sptr,2); + VoxelsOnCartesianGrid reverse_motion_x(range, origin, grid_spacing); + VoxelsOnCartesianGrid reverse_motion_y(range, origin, grid_spacing); + VoxelsOnCartesianGrid reverse_motion_z(range, origin, grid_spacing); + reverse_motion_x.fill(-3 * grid_spacing[3]); + reverse_motion_y.fill(-2 * grid_spacing[2]); + reverse_motion_z.fill(-1 * grid_spacing[1]); + + // horrible way - but it works. I need to make it simpler. + const shared_ptr> reverse_motion1_x_sptr(motion_x.get_empty_copy()); + const shared_ptr> reverse_motion1_y_sptr(motion_y.get_empty_copy()); + const shared_ptr> reverse_motion1_z_sptr(motion_z.get_empty_copy()); + const shared_ptr> reverse_motion2_x_sptr(reverse_motion_x.clone()); + const shared_ptr> reverse_motion2_y_sptr(reverse_motion_y.clone()); + const shared_ptr> reverse_motion2_z_sptr(reverse_motion_z.clone()); + + GatedDiscretisedDensity reverse_gated_motion_x(image_sptr, 2); + reverse_gated_motion_x.set_density_sptr(reverse_motion1_x_sptr, 1); + reverse_gated_motion_x.set_density_sptr(reverse_motion2_x_sptr, 2); reverse_gated_motion_x.set_time_gate_definitions(gate_defs); - GatedDiscretisedDensity reverse_gated_motion_y(image_sptr,2); - reverse_gated_motion_y.set_density_sptr(reverse_motion1_y_sptr,1); - reverse_gated_motion_y.set_density_sptr(reverse_motion2_y_sptr,2); + GatedDiscretisedDensity reverse_gated_motion_y(image_sptr, 2); + reverse_gated_motion_y.set_density_sptr(reverse_motion1_y_sptr, 1); + reverse_gated_motion_y.set_density_sptr(reverse_motion2_y_sptr, 2); reverse_gated_motion_y.set_time_gate_definitions(gate_defs); - GatedDiscretisedDensity reverse_gated_motion_z(image_sptr,2); - reverse_gated_motion_z.set_density_sptr(reverse_motion1_z_sptr,1); - reverse_gated_motion_z.set_density_sptr(reverse_motion2_z_sptr,2); + GatedDiscretisedDensity reverse_gated_motion_z(image_sptr, 2); + reverse_gated_motion_z.set_density_sptr(reverse_motion1_z_sptr, 1); + reverse_gated_motion_z.set_density_sptr(reverse_motion2_z_sptr, 2); reverse_gated_motion_z.set_time_gate_definitions(gate_defs); - + GatedSpatialTransformation mvtest; mvtest.set_gate_defs(gate_defs); - mvtest.set_spatial_transformations(reverse_gated_motion_z,reverse_gated_motion_y,reverse_gated_motion_x); + mvtest.set_spatial_transformations(reverse_gated_motion_z, reverse_gated_motion_y, reverse_gated_motion_x); VoxelsOnCartesianGrid accumulated_image(range, origin, grid_spacing); - mvtest.warp_image(accumulated_image,gated_image); + mvtest.warp_image(accumulated_image, gated_image); { // simple test for gated_image values - check_if_equal((gated_image.get_density(1))[indices],1.F, "testing 1st gate (i.e. original image) at non-zero point"); - check_if_equal((gated_image.get_density(1))[new_indices],0.F, "testing 1st gate at new location of the non-zero point"); - check_if_equal((gated_image.get_density(2))[indices],0.F, "testing 2nd gate at the original location of non-zero point"); - check_if_equal((gated_image.get_density(2))[new_indices],1.F, "testing 2nd gate at the new location of the non-zero point"); - check_if_equal((int)gated_image.get_time_gate_definitions().get_num_gates(),2, "testing gate_defs of gated_image are set correctly"); + check_if_equal((gated_image.get_density(1))[indices], 1.F, "testing 1st gate (i.e. original image) at non-zero point"); + check_if_equal((gated_image.get_density(1))[new_indices], 0.F, "testing 1st gate at new location of the non-zero point"); + check_if_equal((gated_image.get_density(2))[indices], 0.F, "testing 2nd gate at the original location of non-zero point"); + check_if_equal((gated_image.get_density(2))[new_indices], 1.F, "testing 2nd gate at the new location of the non-zero point"); + check_if_equal((int)gated_image.get_time_gate_definitions().get_num_gates(), 2, + "testing gate_defs of gated_image are set correctly"); // test if motion vectors have been set correctly - check_if_equal((reverse_gated_motion_z.get_density(2))[indices],-1*grid_spacing[1], "testing the input to set the motion in z"); - check_if_equal((mvtest.get_spatial_transformation_z().get_density(2))[indices],-1*grid_spacing[1], "testing GatedSpatialTransformation class get the motion vector z correctly"); - check_if_equal((mvtest.get_spatial_transformation_z().get_density(2))[new_indices],-1*grid_spacing[1], "testing GatedSpatialTransformation class get the motion vector z correctly"); - check_if_equal((mvtest.get_spatial_transformation_y().get_density(2))[indices],-2*grid_spacing[2], "testing GatedSpatialTransformation class get the motion vector y correctly"); - check_if_equal((mvtest.get_spatial_transformation_y().get_density(2))[new_indices],-2*grid_spacing[2], "testing GatedSpatialTransformation class get the motion vector y correctly"); - check_if_equal((mvtest.get_spatial_transformation_x().get_density(2))[indices],-3*grid_spacing[3], "testing GatedSpatialTransformation class get the motion vector x correctly"); - check_if_equal((mvtest.get_spatial_transformation_x().get_density(2))[new_indices],-3*grid_spacing[3], "testing GatedSpatialTransformation class get the motion vector x correctly"); - check_if_equal((int)gate_defs.get_num_gates(),2, "testing gate_defs are set correctly"); - check_if_equal((int)reverse_gated_motion_z.get_time_gate_definitions().get_num_gates(),2, "testing GatedSpatialTransformation class get the motion vector x correctly"); - check_if_equal((int)(mvtest.get_time_gate_definitions()).get_num_gates(),2, "testing GatedSpatialTransformation class time_gate_difinitions"); + check_if_equal((reverse_gated_motion_z.get_density(2))[indices], -1 * grid_spacing[1], + "testing the input to set the motion in z"); + check_if_equal((mvtest.get_spatial_transformation_z().get_density(2))[indices], -1 * grid_spacing[1], + "testing GatedSpatialTransformation class get the motion vector z correctly"); + check_if_equal((mvtest.get_spatial_transformation_z().get_density(2))[new_indices], -1 * grid_spacing[1], + "testing GatedSpatialTransformation class get the motion vector z correctly"); + check_if_equal((mvtest.get_spatial_transformation_y().get_density(2))[indices], -2 * grid_spacing[2], + "testing GatedSpatialTransformation class get the motion vector y correctly"); + check_if_equal((mvtest.get_spatial_transformation_y().get_density(2))[new_indices], -2 * grid_spacing[2], + "testing GatedSpatialTransformation class get the motion vector y correctly"); + check_if_equal((mvtest.get_spatial_transformation_x().get_density(2))[indices], -3 * grid_spacing[3], + "testing GatedSpatialTransformation class get the motion vector x correctly"); + check_if_equal((mvtest.get_spatial_transformation_x().get_density(2))[new_indices], -3 * grid_spacing[3], + "testing GatedSpatialTransformation class get the motion vector x correctly"); + check_if_equal((int)gate_defs.get_num_gates(), 2, "testing gate_defs are set correctly"); + check_if_equal((int)reverse_gated_motion_z.get_time_gate_definitions().get_num_gates(), 2, + "testing GatedSpatialTransformation class get the motion vector x correctly"); + check_if_equal((int)(mvtest.get_time_gate_definitions()).get_num_gates(), 2, + "testing GatedSpatialTransformation class time_gate_difinitions"); // actual test for accumulate_warp_image check_if_equal(accumulated_image[indices], 2.F, "testing the accumulated image at the original location of non-zero point"); - check_if_equal(accumulated_image[new_indices], 0.F, "testing the accumulated image at the location where the non-zero point had moved"); + check_if_equal(accumulated_image[new_indices], 0.F, + "testing the accumulated image at the location where the non-zero point had moved"); } } END_NAMESPACE_STIR USING_NAMESPACE_STIR -int main() -{ +int +main() { warp_imageTests tests; tests.run_tests(); return tests.main_return_value(); diff --git a/src/test/test_zoom_image.cxx b/src/test/test_zoom_image.cxx index 6e8857e778..6c74510c22 100644 --- a/src/test/test_zoom_image.cxx +++ b/src/test/test_zoom_image.cxx @@ -1,29 +1,29 @@ // // /* - Copyright (C) 2006- 2007, Hammersmith Imanet Ltd - This file is part of STIR. - - This file is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - This file is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - + Copyright (C) 2006- 2007, Hammersmith Imanet Ltd + This file is part of STIR. + + This file is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + See STIR/LICENSE.txt for details */ /*! \file \ingroup test - + \brief Test program for stir::zoom_image (and stir::centre_of_gravity) - + \author Kris Thielemans - + */ #include "stir/VoxelsOnCartesianGrid.h" @@ -45,39 +45,33 @@ START_NAMESPACE_STIR after zooming. This is done by checking the centre of gravity of the zoomed image. */ -class zoom_imageTests : public RunTests -{ +class zoom_imageTests : public RunTests { public: void run_tests(); }; - void zoom_imageTests::run_tests() -{ +{ std::cerr << "Tests for zoom_image\n"; - - CartesianCoordinate3D origin (0,1,2); - CartesianCoordinate3D grid_spacing (3,4,5); - - IndexRange<3> - range(CartesianCoordinate3D(0,-15,-14), - CartesianCoordinate3D(4,14,15)); - - VoxelsOnCartesianGrid image(range,origin, grid_spacing); + + CartesianCoordinate3D origin(0, 1, 2); + CartesianCoordinate3D grid_spacing(3, 4, 5); + + IndexRange<3> range(CartesianCoordinate3D(0, -15, -14), CartesianCoordinate3D(4, 14, 15)); + + VoxelsOnCartesianGrid image(range, origin, grid_spacing); image.fill(0.F); - const BasicCoordinate<3,int> indices = make_coordinate(1,2,3); + const BasicCoordinate<3, int> indices = make_coordinate(1, 2, 3); image[indices] = 1.F; - const CartesianCoordinate3D coord = - image.get_physical_coordinates_for_indices(indices); + const CartesianCoordinate3D coord = image.get_physical_coordinates_for_indices(indices); { // check if centre_of_gravity_in_mm returns same point - check_if_equal(coord, - find_centre_of_gravity_in_mm(image), - "test on get_physical_coordinates_for_indices and find_centre_of_gravity_in_mm"); + check_if_equal(coord, find_centre_of_gravity_in_mm(image), + "test on get_physical_coordinates_for_indices and find_centre_of_gravity_in_mm"); } // we cannot have very good accuracy in the centre of gravity @@ -88,94 +82,69 @@ zoom_imageTests::run_tests() // test 2 arg zoom_image { - CartesianCoordinate3D new_origin (4.F,5.F,6.F); - CartesianCoordinate3D new_grid_spacing (2.2F,3.1F,4.3F); - - IndexRange<3> - new_range(CartesianCoordinate3D(-1,-16,-17), - CartesianCoordinate3D(5,15,20)); - - VoxelsOnCartesianGrid new_image(new_range,new_origin, new_grid_spacing); + CartesianCoordinate3D new_origin(4.F, 5.F, 6.F); + CartesianCoordinate3D new_grid_spacing(2.2F, 3.1F, 4.3F); + + IndexRange<3> new_range(CartesianCoordinate3D(-1, -16, -17), CartesianCoordinate3D(5, 15, 20)); + + VoxelsOnCartesianGrid new_image(new_range, new_origin, new_grid_spacing); zoom_image(new_image, image); { // check if centre_of_gravity_in_mm returns same point this->set_tolerance(tolerance_for_distance); - check_if_equal(coord, - find_centre_of_gravity_in_mm(new_image), - "test on 2-argument zoom_image"); + check_if_equal(coord, find_centre_of_gravity_in_mm(new_image), "test on 2-argument zoom_image"); this->set_tolerance(old_tolerance); - check_if_equal(new_range, new_image.get_index_range(), - "test on 2-argument argument zoom_image: index range"); - check_if_equal(new_grid_spacing, new_image.get_voxel_size(), - "test on 2-argument argument zoom_image: voxel size"); - check_if_equal(new_origin, new_image.get_origin(), - "test on 2-argument argument zoom_image: origin"); - + check_if_equal(new_range, new_image.get_index_range(), "test on 2-argument argument zoom_image: index range"); + check_if_equal(new_grid_spacing, new_image.get_voxel_size(), "test on 2-argument argument zoom_image: voxel size"); + check_if_equal(new_origin, new_image.get_origin(), "test on 2-argument argument zoom_image: origin"); } } - // test multiple argument zoom_image { - const CartesianCoordinate3D zooms(1.3F,1.2F,1.5F); - const CartesianCoordinate3D offsets_in_mm(3.F,4.F,5.5F); - const Coordinate3D new_sizes(30,40,50); - const VoxelsOnCartesianGrid new_image = - zoom_image(image, zooms, offsets_in_mm, new_sizes); + const CartesianCoordinate3D zooms(1.3F, 1.2F, 1.5F); + const CartesianCoordinate3D offsets_in_mm(3.F, 4.F, 5.5F); + const Coordinate3D new_sizes(30, 40, 50); + const VoxelsOnCartesianGrid new_image = zoom_image(image, zooms, offsets_in_mm, new_sizes); { // check if centre_of_gravity_in_mm returns same point this->set_tolerance(tolerance_for_distance); - check_if_equal(coord, - find_centre_of_gravity_in_mm(new_image), - "test on multiple argument zoom_image"); + check_if_equal(coord, find_centre_of_gravity_in_mm(new_image), "test on multiple argument zoom_image"); this->set_tolerance(old_tolerance); - check_if_equal(new_sizes, new_image.get_lengths(), - "test on multiple argument zoom_image: index range"); - check_if_equal(new_image.get_voxel_size(), image.get_voxel_size()/zooms, - "test on multiple argument zoom_image: voxel size"); - + check_if_equal(new_sizes, new_image.get_lengths(), "test on multiple argument zoom_image: index range"); + check_if_equal(new_image.get_voxel_size(), image.get_voxel_size() / zooms, + "test on multiple argument zoom_image: voxel size"); } } // test multiple argument zoom_image in 2D { const float zoom = 1.3F; - const CartesianCoordinate3D zooms(1.F,zoom,zoom); - const CartesianCoordinate3D offsets_in_mm(0.F,4.F,5.5F); + const CartesianCoordinate3D zooms(1.F, zoom, zoom); + const CartesianCoordinate3D offsets_in_mm(0.F, 4.F, 5.5F); const int new_size = 30; - const VoxelsOnCartesianGrid new_image = - zoom_image(image, zoom, offsets_in_mm.x(), offsets_in_mm.y(), new_size); + const VoxelsOnCartesianGrid new_image = zoom_image(image, zoom, offsets_in_mm.x(), offsets_in_mm.y(), new_size); { // check if centre_of_gravity_in_mm returns same point this->set_tolerance(tolerance_for_distance); - check_if_equal(coord, - find_centre_of_gravity_in_mm(new_image), - "test on multiple argument (2d) zoom_image"); + check_if_equal(coord, find_centre_of_gravity_in_mm(new_image), "test on multiple argument (2d) zoom_image"); this->set_tolerance(old_tolerance); - check_if_equal(image.get_min_z(), new_image.get_min_z(), - "test on multiple argument (2d) zoom_image: min_z"); - check_if_equal(image.get_max_z(), new_image.get_max_z(), - "test on multiple argument (2d) zoom_image: max_z"); - check_if_equal(new_size, new_image.get_x_size(), - "test on multiple argument (2d) zoom_image: x_size"); - check_if_equal(new_size, new_image.get_y_size(), - "test on multiple argument (2d) zoom_image: y_size"); - check_if_equal(new_image.get_voxel_size(), image.get_voxel_size()/zooms, - "test on multiple argument (2d) zoom_image: voxel size"); - + check_if_equal(image.get_min_z(), new_image.get_min_z(), "test on multiple argument (2d) zoom_image: min_z"); + check_if_equal(image.get_max_z(), new_image.get_max_z(), "test on multiple argument (2d) zoom_image: max_z"); + check_if_equal(new_size, new_image.get_x_size(), "test on multiple argument (2d) zoom_image: x_size"); + check_if_equal(new_size, new_image.get_y_size(), "test on multiple argument (2d) zoom_image: y_size"); + check_if_equal(new_image.get_voxel_size(), image.get_voxel_size() / zooms, + "test on multiple argument (2d) zoom_image: voxel size"); } } - } END_NAMESPACE_STIR - USING_NAMESPACE_STIR - -int main() -{ +int +main() { zoom_imageTests tests; tests.run_tests(); return tests.main_return_value(); diff --git a/src/utilities/SSRB.cxx b/src/utilities/SSRB.cxx index 643f45c467..1d08ffa1a0 100644 --- a/src/utilities/SSRB.cxx +++ b/src/utilities/SSRB.cxx @@ -36,7 +36,7 @@ \endcode \param num_segments_to_combine has to be odd. It is used as the number of segments in the original data to combine. - \param num_views_to_combine has to be at least 1 (which is the default). + \param num_views_to_combine has to be at least 1 (which is the default). It is used as the number of views in the original data to combine. \param num_tangential_poss_to_trim has to be smaller than the available number of tangential positions. @@ -53,14 +53,14 @@ output would correspond to a span=3 file with mashing factor 2, and would be normalised (at least as far as SSRB concerns). \a num_segments_to_combine=3 results in ring differences -1,0,1 to be combined all into segment 0, etc. - \see + \see stir::SSRB(const std::string& output_filename, const stir::ProjData& in_projdata, - const int num_segments_to_combine, - const int num_views_to_combine, - const int num_tang_poss_to_trim, - const bool do_normalisation, - const int max_in_segment_num_to_process + const int num_segments_to_combine, + const int num_views_to_combine, + const int num_tang_poss_to_trim, + const bool do_normalisation, + const int max_in_segment_num_to_process ) for info on parameters and restrictions. \code @@ -82,16 +82,15 @@ using std::cerr; USING_NAMESPACE_STIR - -static void print_usage_and_exit(const std::string& prog_name) -{ +static void +print_usage_and_exit(const std::string& prog_name) { cerr << "Usage:\n\n" << "Two options:\n" << "\t - Classical STIR SSRB method\n" << prog_name << " [-t num_tangential_poss_to_trim] \\\n" << "\toutput_filename input_projdata_name \\\n" << "\t[num_segments_to_combine \\\n" - <<"\t[ num_views_to_combine [do_norm [max_in_segment_num_to_process ]]]]\n" + << "\t[ num_views_to_combine [do_norm [max_in_segment_num_to_process ]]]]\n" << "num_segments_to_combine has to be odd. It is used as the number of segments\n" << " in the original data to combine.\n" << "num_views_to_combine has to be at least 1 (which is the default)\n" @@ -107,53 +106,45 @@ static void print_usage_and_exit(const std::string& prog_name) exit(EXIT_FAILURE); } -void classic_SSRB(int argc, char **argv) -{ +void +classic_SSRB(int argc, char** argv) { // Does the standard method of SSRB based upon numerical inputs int num_tangential_poss_to_trim = 0; - if (argc>1 && strcmp(argv[1], "-t")==0) - { + if (argc > 1 && strcmp(argv[1], "-t") == 0) { num_tangential_poss_to_trim = atoi(argv[2]); - argc -= 2; argv += 2; + argc -= 2; + argv += 2; } const string output_filename = argv[1]; shared_ptr in_projdata_ptr = ProjData::read_from_file(argv[2]); const int num_segments_to_combine = argc <= 3 ? 1 : atoi(argv[3]); - const int num_views_to_combine = argc<=4 ? 1 : atoi(argv[4]); - const bool do_norm = argc<=5 ? true : atoi(argv[5]) != 0; - const int max_segment_num_to_process = argc <=6 ? -1 : atoi(argv[6]); + const int num_views_to_combine = argc <= 4 ? 1 : atoi(argv[4]); + const bool do_norm = argc <= 5 ? true : atoi(argv[5]) != 0; + const int max_segment_num_to_process = argc <= 6 ? -1 : atoi(argv[6]); // do standard SSRB - SSRB(output_filename, - *in_projdata_ptr, - num_segments_to_combine, - num_views_to_combine, - num_tangential_poss_to_trim, - do_norm, - max_segment_num_to_process - ); + SSRB(output_filename, *in_projdata_ptr, num_segments_to_combine, num_views_to_combine, num_tangential_poss_to_trim, do_norm, + max_segment_num_to_process); } -void template_based_SSRB(int argc, char **argv) -{ +void +template_based_SSRB(int argc, char** argv) { // Does the template based SSRB whereby the target template is given as an argument shared_ptr template_projdata_ptr = ProjData::read_from_file(argv[2]); const string output_filename = argv[3]; shared_ptr in_projdata_ptr = ProjData::read_from_file(argv[4]); - ProjDataInterfile out_proj_data(in_projdata_ptr->get_exam_info_sptr(), - template_projdata_ptr->get_proj_data_info_sptr(), output_filename, std::ios::out); - const bool do_norm = argc<=5 ? true : atoi(argv[5]) != 0; + ProjDataInterfile out_proj_data(in_projdata_ptr->get_exam_info_sptr(), template_projdata_ptr->get_proj_data_info_sptr(), + output_filename, std::ios::out); + const bool do_norm = argc <= 5 ? true : atoi(argv[5]) != 0; SSRB(out_proj_data, *in_projdata_ptr, do_norm); } -int main(int argc, char **argv) -{ - if (argc > 7 || argc < 3 ) - { +int +main(int argc, char** argv) { + if (argc > 7 || argc < 3) { print_usage_and_exit(argv[0]); } - if (strcmp(argv[1], "--template")==0) - { + if (strcmp(argv[1], "--template") == 0) { template_based_SSRB(argc, argv); } else { classic_SSRB(argc, argv); diff --git a/src/utilities/abs_image.cxx b/src/utilities/abs_image.cxx index a5ae5259e6..d98bca4f78 100644 --- a/src/utilities/abs_image.cxx +++ b/src/utilities/abs_image.cxx @@ -19,7 +19,7 @@ /*! \file \ingroup utilities - \brief It produces the absolute value image of an image. + \brief It produces the absolute value image of an image. \author Kris Thielemans \author Charalampos Tsoumpas @@ -28,7 +28,7 @@ \par Usage: \code - abs_image [-p || -d] -o output_filename -i input_filename + abs_image [-p || -d] -o output_filename -i input_filename \endcode Use -p switch for parametric images, or the -d switch for dynamic images. */ @@ -41,111 +41,97 @@ #include #include "stir/getopt.h" -int main(int argc, char * argv[]) -{ +int +main(int argc, char* argv[]) { USING_NAMESPACE_STIR; - const char * output_filename = 0; - const char * input_filename = 0; - bool do_parametric=0; - bool do_dynamic=0; + const char* output_filename = 0; + const char* input_filename = 0; + bool do_parametric = 0; + bool do_dynamic = 0; - const char * const usage = "abs_image [ -p | -d ] -o output_filename -i input_filename\n"; + const char* const usage = "abs_image [ -p | -d ] -o output_filename -i input_filename\n"; opterr = 0; { char c; - while ((c = getopt (argc, argv, "i:o:(p||d)")) != -1) - switch (c) - { - case 'i': - input_filename = optarg; - break; - case 'o': - output_filename = optarg; - break; - case 'p': - do_parametric=true; - break; - case 'd': - do_dynamic=true; - break; - case '?': - std::cerr << usage; - return EXIT_FAILURE; - default: - if (isprint (optopt)) - fprintf (stderr, "Unknown option `-%c'.\n", optopt); - else - fprintf (stderr, - "Unknown option character `\\x%x'.\n", - optopt); - std::cerr << usage; - return EXIT_FAILURE; - } + while ((c = getopt(argc, argv, "i:o:(p||d)")) != -1) + switch (c) { + case 'i': + input_filename = optarg; + break; + case 'o': + output_filename = optarg; + break; + case 'p': + do_parametric = true; + break; + case 'd': + do_dynamic = true; + break; + case '?': + std::cerr << usage; + return EXIT_FAILURE; + default: + if (isprint(optopt)) + fprintf(stderr, "Unknown option `-%c'.\n", optopt); + else + fprintf(stderr, "Unknown option character `\\x%x'.\n", optopt); + std::cerr << usage; + return EXIT_FAILURE; + } } - if (output_filename==0 || input_filename==0) - { - std::cerr << usage; - return EXIT_FAILURE; - } + if (output_filename == 0 || input_filename == 0) { + std::cerr << usage; + return EXIT_FAILURE; + } - if(do_parametric)// A better way will be to template it... - { - shared_ptr - input_image_sptr(ParametricVoxelsOnCartesianGrid::read_from_file(input_filename)); - shared_ptr output_image_sptr(input_image_sptr->clone()); - ParametricVoxelsOnCartesianGrid::full_iterator out_iter = output_image_sptr->begin_all(); - ParametricVoxelsOnCartesianGrid::const_full_iterator in_iter = input_image_sptr->begin_all_const(); - while( in_iter != input_image_sptr->end_all_const()) - { - if (*in_iter<0.F) - *out_iter = -(*in_iter); - ++in_iter; ++out_iter; - } - Succeeded success = - OutputFileFormat::default_sptr()-> - write_to_file(output_filename, *output_image_sptr); - return success==Succeeded::yes ? EXIT_SUCCESS : EXIT_FAILURE; - } - else if(do_dynamic)// A better way will be to template it... - { - const shared_ptr - input_image_sptr(read_from_file(input_filename)); - const DynamicDiscretisedDensity input_image = *input_image_sptr; - DynamicDiscretisedDensity output_image = input_image; - for(unsigned int frame_num=1;frame_num<=(input_image.get_time_frame_definitions()).get_num_frames();++frame_num) - { - DiscretisedDensity<3,float>::full_iterator out_iter = output_image[frame_num].begin_all(); - DiscretisedDensity<3,float>::const_full_iterator in_iter = input_image[frame_num].begin_all_const(); - while( in_iter != input_image[frame_num].end_all_const()) - { - if (*in_iter<0.F) - *out_iter = -(*in_iter); - ++in_iter; ++out_iter; - } - } - Succeeded success = - output_image.write_to_ecat7(output_filename); - return success==Succeeded::yes ? EXIT_SUCCESS : EXIT_FAILURE; + if (do_parametric) // A better way will be to template it... + { + shared_ptr input_image_sptr(ParametricVoxelsOnCartesianGrid::read_from_file(input_filename)); + shared_ptr output_image_sptr(input_image_sptr->clone()); + ParametricVoxelsOnCartesianGrid::full_iterator out_iter = output_image_sptr->begin_all(); + ParametricVoxelsOnCartesianGrid::const_full_iterator in_iter = input_image_sptr->begin_all_const(); + while (in_iter != input_image_sptr->end_all_const()) { + if (*in_iter < 0.F) + *out_iter = -(*in_iter); + ++in_iter; + ++out_iter; } - else - { - shared_ptr > - input_image_sptr(read_from_file >(input_filename)); + Succeeded success = + OutputFileFormat::default_sptr()->write_to_file(output_filename, *output_image_sptr); + return success == Succeeded::yes ? EXIT_SUCCESS : EXIT_FAILURE; + } else if (do_dynamic) // A better way will be to template it... + { + const shared_ptr input_image_sptr(read_from_file(input_filename)); + const DynamicDiscretisedDensity input_image = *input_image_sptr; + DynamicDiscretisedDensity output_image = input_image; + for (unsigned int frame_num = 1; frame_num <= (input_image.get_time_frame_definitions()).get_num_frames(); ++frame_num) { + DiscretisedDensity<3, float>::full_iterator out_iter = output_image[frame_num].begin_all(); + DiscretisedDensity<3, float>::const_full_iterator in_iter = input_image[frame_num].begin_all_const(); + while (in_iter != input_image[frame_num].end_all_const()) { + if (*in_iter < 0.F) + *out_iter = -(*in_iter); + ++in_iter; + ++out_iter; + } + } + Succeeded success = output_image.write_to_ecat7(output_filename); + return success == Succeeded::yes ? EXIT_SUCCESS : EXIT_FAILURE; + } else { + shared_ptr> input_image_sptr(read_from_file>(input_filename)); - shared_ptr > output_image_sptr(input_image_sptr->clone()); - DiscretisedDensity<3,float>::full_iterator out_iter = output_image_sptr->begin_all(); - DiscretisedDensity<3,float>::const_full_iterator in_iter = input_image_sptr->begin_all_const(); - while( in_iter != input_image_sptr->end_all_const()) - { - if (*in_iter<0.F) - *out_iter = -(*in_iter); - ++in_iter; ++out_iter; - } - Succeeded success = - OutputFileFormat >::default_sptr()-> - write_to_file(output_filename, *output_image_sptr); - return success==Succeeded::yes ? EXIT_SUCCESS : EXIT_FAILURE; + shared_ptr> output_image_sptr(input_image_sptr->clone()); + DiscretisedDensity<3, float>::full_iterator out_iter = output_image_sptr->begin_all(); + DiscretisedDensity<3, float>::const_full_iterator in_iter = input_image_sptr->begin_all_const(); + while (in_iter != input_image_sptr->end_all_const()) { + if (*in_iter < 0.F) + *out_iter = -(*in_iter); + ++in_iter; + ++out_iter; } + Succeeded success = + OutputFileFormat>::default_sptr()->write_to_file(output_filename, *output_image_sptr); + return success == Succeeded::yes ? EXIT_SUCCESS : EXIT_FAILURE; + } } diff --git a/src/utilities/apply_normfactors.cxx b/src/utilities/apply_normfactors.cxx index e88b6b8e08..edeccb8cbb 100644 --- a/src/utilities/apply_normfactors.cxx +++ b/src/utilities/apply_normfactors.cxx @@ -40,124 +40,103 @@ #include #include - USING_NAMESPACE_STIR -int main(int argc, char **argv) -{ - if (argc<7 || argc>10) - { - std::cerr << "Usage: " << argv[0] - << " out_filename in_norm_filename_prefix measured_data apply_or_undo iter_num eff_iter_num [do_eff [ do_geo [ do_block]]]\n" - << "apply_or_undo is 1 (multiply) or 0 (divide)\n" - << "do_eff, do_geo, do_block are 1 or 0 and all default to 1\n"; - return EXIT_FAILURE; - } - - const bool do_block = argc>=10?atoi(argv[9])!=0: true; - const bool do_geo = argc>=9?atoi(argv[8])!=0: true; - const bool do_eff = argc>=8?atoi(argv[7])!=0: true; +int +main(int argc, char** argv) { + if (argc < 7 || argc > 10) { + std::cerr << "Usage: " << argv[0] + << " out_filename in_norm_filename_prefix measured_data apply_or_undo iter_num eff_iter_num [do_eff [ do_geo [ " + "do_block]]]\n" + << "apply_or_undo is 1 (multiply) or 0 (divide)\n" + << "do_eff, do_geo, do_block are 1 or 0 and all default to 1\n"; + return EXIT_FAILURE; + } + + const bool do_block = argc >= 10 ? atoi(argv[9]) != 0 : true; + const bool do_geo = argc >= 9 ? atoi(argv[8]) != 0 : true; + const bool do_eff = argc >= 8 ? atoi(argv[7]) != 0 : true; const int eff_iter_num = atoi(argv[6]); const int iter_num = atoi(argv[5]); - const bool apply_or_undo = atoi(argv[4])!=0; + const bool apply_or_undo = atoi(argv[4]) != 0; shared_ptr measured_data = ProjData::read_from_file(argv[3]); const std::string in_filename_prefix = argv[2]; const std::string output_file_name = argv[1]; const std::string program_name = argv[0]; - shared_ptr out_proj_data_ptr - (new ProjDataInterfile(measured_data->get_exam_info_sptr(), - measured_data->get_proj_data_info_sptr()->create_shared_clone(), - output_file_name)); + shared_ptr out_proj_data_ptr(new ProjDataInterfile( + measured_data->get_exam_info_sptr(), measured_data->get_proj_data_info_sptr()->create_shared_clone(), output_file_name)); - const int num_detectors = - measured_data->get_proj_data_info_sptr()->get_scanner_ptr()->get_num_detectors_per_ring(); - const int num_crystals_per_block = - measured_data->get_proj_data_info_sptr()->get_scanner_ptr()-> - get_num_transaxial_crystals_per_block(); - const int num_blocks = - measured_data->get_proj_data_info_sptr()->get_scanner_ptr()-> - get_num_transaxial_blocks(); + const int num_detectors = measured_data->get_proj_data_info_sptr()->get_scanner_ptr()->get_num_detectors_per_ring(); + const int num_crystals_per_block = + measured_data->get_proj_data_info_sptr()->get_scanner_ptr()->get_num_transaxial_crystals_per_block(); + const int num_blocks = measured_data->get_proj_data_info_sptr()->get_scanner_ptr()->get_num_transaxial_blocks(); const int segment_num = 0; - Array<1,float> efficiencies(num_detectors); - assert(num_crystals_per_block%2 == 0); - GeoData norm_geo_data(IndexRange2D(num_crystals_per_block/2, num_detectors)); + Array<1, float> efficiencies(num_detectors); + assert(num_crystals_per_block % 2 == 0); + GeoData norm_geo_data(IndexRange2D(num_crystals_per_block / 2, num_detectors)); BlockData norm_block_data(IndexRange2D(num_blocks, num_blocks)); DetPairData det_pair_data; for (int ax_pos_num = measured_data->get_min_axial_pos_num(segment_num); - ax_pos_num <= measured_data->get_max_axial_pos_num(segment_num); - ++ax_pos_num) - { + ax_pos_num <= measured_data->get_max_axial_pos_num(segment_num); ++ax_pos_num) { + + // efficiencies + if (do_eff) { + char* in_filename = new char[in_filename_prefix.size() + 30]; + sprintf(in_filename, "%s_%s_%d_%d_%d.out", in_filename_prefix.c_str(), "eff", ax_pos_num, iter_num, eff_iter_num); + std::ifstream in(in_filename); + in >> efficiencies; + if (!in) { + warning("Error reading %s, using all 1s instead\n", in_filename); + efficiencies = Array<1, float>(num_detectors); + efficiencies.fill(1); + } - // efficiencies + delete[] in_filename; + } + // geo norm + if (do_geo) { + { + char* in_filename = new char[in_filename_prefix.size() + 30]; + sprintf(in_filename, "%s_%s_%d_%d.out", in_filename_prefix.c_str(), "geo", ax_pos_num, iter_num); + std::ifstream in(in_filename); + in >> norm_geo_data; + if (!in) { + warning("Error reading %s, using all 1s instead\n", in_filename); + norm_geo_data = GeoData(IndexRange2D(num_crystals_per_block / 2, num_detectors)); + norm_geo_data.fill(1); + } + delete[] in_filename; + } + } + // block norm + if (do_block) { + { + char* in_filename = new char[in_filename_prefix.size() + 30]; + sprintf(in_filename, "%s_%s_%d_%d.out", in_filename_prefix.c_str(), "block", ax_pos_num, iter_num); + std::ifstream in(in_filename); + in >> norm_block_data; + if (!in) { + warning("Error reading %s, using all 1s instead\n", in_filename); + norm_block_data = BlockData(IndexRange2D(num_blocks, num_blocks)); + norm_block_data.fill(1); + } + delete[] in_filename; + } + } + { + make_det_pair_data(det_pair_data, *measured_data, segment_num, ax_pos_num); if (do_eff) - { - char *in_filename = new char[in_filename_prefix.size() + 30]; - sprintf(in_filename, "%s_%s_%d_%d_%d.out", - in_filename_prefix.c_str(), "eff", ax_pos_num, iter_num, eff_iter_num); - std::ifstream in(in_filename); - in >> efficiencies; - if (!in) - { - warning("Error reading %s, using all 1s instead\n", in_filename); - efficiencies = Array<1,float>(num_detectors); - efficiencies.fill(1); - } - - delete[] in_filename; - } - // geo norm + apply_efficiencies(det_pair_data, efficiencies, apply_or_undo); if (do_geo) - { - { - char *in_filename = new char[in_filename_prefix.size() + 30]; - sprintf(in_filename, "%s_%s_%d_%d.out", - in_filename_prefix.c_str(), "geo", ax_pos_num, iter_num); - std::ifstream in(in_filename); - in >> norm_geo_data; - if (!in) - { - warning("Error reading %s, using all 1s instead\n", in_filename); - norm_geo_data= GeoData(IndexRange2D(num_crystals_per_block/2, num_detectors)); - norm_geo_data.fill(1); - } - delete[] in_filename; - } - } - // block norm + apply_geo_norm(det_pair_data, norm_geo_data, apply_or_undo); if (do_block) - { - { - char *in_filename = new char[in_filename_prefix.size() + 30]; - sprintf(in_filename, "%s_%s_%d_%d.out", - in_filename_prefix.c_str(), "block", ax_pos_num, iter_num); - std::ifstream in(in_filename); - in >> norm_block_data; - if (!in) - { - warning("Error reading %s, using all 1s instead\n", in_filename); - norm_block_data = BlockData(IndexRange2D(num_blocks, num_blocks)); - norm_block_data.fill(1); - } - delete[] in_filename; - } - } - { - make_det_pair_data(det_pair_data, *measured_data, segment_num, ax_pos_num); - if (do_eff) - apply_efficiencies(det_pair_data, efficiencies, apply_or_undo); - if (do_geo) - apply_geo_norm(det_pair_data, norm_geo_data, apply_or_undo); - if (do_block) - apply_block_norm(det_pair_data, norm_block_data, apply_or_undo); - set_det_pair_data(*out_proj_data_ptr, - det_pair_data, - segment_num, - ax_pos_num); - } + apply_block_norm(det_pair_data, norm_block_data, apply_or_undo); + set_det_pair_data(*out_proj_data_ptr, det_pair_data, segment_num, ax_pos_num); } + } return EXIT_SUCCESS; } diff --git a/src/utilities/apply_normfactors3D.cxx b/src/utilities/apply_normfactors3D.cxx index ff858845d0..6f82933f0a 100644 --- a/src/utilities/apply_normfactors3D.cxx +++ b/src/utilities/apply_normfactors3D.cxx @@ -41,139 +41,114 @@ #include #include - USING_NAMESPACE_STIR -int main(int argc, char **argv) -{ - if (argc<7 || argc>12) - { - std::cerr << "Usage: " << argv[0] - << " out_filename in_norm_filename_prefix measured_data apply_or_undo iter_num eff_iter_num [do_eff [ do_geo [ do_block [do_display]]]]\n" - << "apply_or_undo is 1 (multiply) or 0 (divide)\n" - << "do_eff, do_geo, do_block are 1 or 0 and all default to 1\n" - << "do_display is 1 or 0 (defaults to 0)\n"; - return EXIT_FAILURE; - } - - const bool do_display = argc>=11?atoi(argv[10])!=0 : false; - bool do_block = argc>=10?atoi(argv[9])!=0: true; - bool do_geo = argc>=9?atoi(argv[8])!=0: true; - bool do_eff = argc>=8?atoi(argv[7])!=0: true; - - // if (do_geo) - // error("Cannot do geometric factors in 3D yet"); +int +main(int argc, char** argv) { + if (argc < 7 || argc > 12) { + std::cerr << "Usage: " << argv[0] + << " out_filename in_norm_filename_prefix measured_data apply_or_undo iter_num eff_iter_num [do_eff [ do_geo [ " + "do_block [do_display]]]]\n" + << "apply_or_undo is 1 (multiply) or 0 (divide)\n" + << "do_eff, do_geo, do_block are 1 or 0 and all default to 1\n" + << "do_display is 1 or 0 (defaults to 0)\n"; + return EXIT_FAILURE; + } + + const bool do_display = argc >= 11 ? atoi(argv[10]) != 0 : false; + bool do_block = argc >= 10 ? atoi(argv[9]) != 0 : true; + bool do_geo = argc >= 9 ? atoi(argv[8]) != 0 : true; + bool do_eff = argc >= 8 ? atoi(argv[7]) != 0 : true; + + // if (do_geo) + // error("Cannot do geometric factors in 3D yet"); const int eff_iter_num = atoi(argv[6]); const int iter_num = atoi(argv[5]); - const bool apply_or_undo = atoi(argv[4])!=0; + const bool apply_or_undo = atoi(argv[4]) != 0; shared_ptr measured_data = ProjData::read_from_file(argv[3]); const std::string in_filename_prefix = argv[2]; const std::string output_file_name = argv[1]; const std::string program_name = argv[0]; - shared_ptr out_proj_data_ptr - (new ProjDataInterfile(measured_data->get_exam_info_sptr(), - measured_data->get_proj_data_info_sptr()->create_shared_clone(), - output_file_name)); - - const int num_rings = - measured_data->get_proj_data_info_sptr()->get_scanner_sptr()-> - get_num_rings(); - const int num_detectors_per_ring = - measured_data->get_proj_data_info_sptr()->get_scanner_sptr()-> - get_num_detectors_per_ring(); - const int num_transaxial_blocks = - measured_data->get_proj_data_info_sptr()->get_scanner_sptr()-> - get_num_transaxial_blocks(); - const int num_axial_blocks = - measured_data->get_proj_data_info_sptr()->get_scanner_sptr()-> - get_num_axial_blocks(); + shared_ptr out_proj_data_ptr(new ProjDataInterfile( + measured_data->get_exam_info_sptr(), measured_data->get_proj_data_info_sptr()->create_shared_clone(), output_file_name)); + + const int num_rings = measured_data->get_proj_data_info_sptr()->get_scanner_sptr()->get_num_rings(); + const int num_detectors_per_ring = measured_data->get_proj_data_info_sptr()->get_scanner_sptr()->get_num_detectors_per_ring(); + const int num_transaxial_blocks = measured_data->get_proj_data_info_sptr()->get_scanner_sptr()->get_num_transaxial_blocks(); + const int num_axial_blocks = measured_data->get_proj_data_info_sptr()->get_scanner_sptr()->get_num_axial_blocks(); const int num_transaxial_crystals_per_block = - measured_data->get_proj_data_info_sptr()->get_scanner_sptr()-> - get_num_transaxial_crystals_per_block(); + measured_data->get_proj_data_info_sptr()->get_scanner_sptr()->get_num_transaxial_crystals_per_block(); const int num_axial_crystals_per_block = - measured_data->get_proj_data_info_sptr()->get_scanner_sptr()-> - get_num_axial_crystals_per_block(); - - GeoData3D norm_geo_data(num_axial_crystals_per_block, num_transaxial_crystals_per_block/2, num_rings, num_detectors_per_ring); + measured_data->get_proj_data_info_sptr()->get_scanner_sptr()->get_num_axial_crystals_per_block(); - BlockData3D norm_block_data(num_axial_blocks, num_transaxial_blocks, - num_axial_blocks-1, num_transaxial_blocks-1); - DetectorEfficiencies efficiencies(IndexRange2D(num_rings, num_detectors_per_ring)); + GeoData3D norm_geo_data(num_axial_crystals_per_block, num_transaxial_crystals_per_block / 2, num_rings, num_detectors_per_ring); - { + BlockData3D norm_block_data(num_axial_blocks, num_transaxial_blocks, num_axial_blocks - 1, num_transaxial_blocks - 1); + DetectorEfficiencies efficiencies(IndexRange2D(num_rings, num_detectors_per_ring)); - // efficiencies - if (do_eff) - { - char *in_filename = new char[in_filename_prefix.size() + 30]; - sprintf(in_filename, "%s_%s_%d_%d.out", - in_filename_prefix.c_str(), "eff", iter_num, eff_iter_num); - std::ifstream in(in_filename); - in >> efficiencies; - if (!in) - { - warning("Error reading %s, using all 1s instead\n", in_filename); - do_eff = false; - } - delete[] in_filename; - } + { + + // efficiencies + if (do_eff) { + char* in_filename = new char[in_filename_prefix.size() + 30]; + sprintf(in_filename, "%s_%s_%d_%d.out", in_filename_prefix.c_str(), "eff", iter_num, eff_iter_num); + std::ifstream in(in_filename); + in >> efficiencies; + if (!in) { + warning("Error reading %s, using all 1s instead\n", in_filename); + do_eff = false; + } + delete[] in_filename; + } // geo norm - if (do_geo) - { - { - char *in_filename = new char[in_filename_prefix.size() + 30]; - sprintf(in_filename, "%s_%s_%d.out", - in_filename_prefix.c_str(), "geo", iter_num); - std::ifstream in(in_filename); - in >> norm_geo_data; - if (!in) - { - warning("Error reading %s, using all 1s instead\n", in_filename); - do_geo = false; - } - delete[] in_filename; - } + if (do_geo) { + { + char* in_filename = new char[in_filename_prefix.size() + 30]; + sprintf(in_filename, "%s_%s_%d.out", in_filename_prefix.c_str(), "geo", iter_num); + std::ifstream in(in_filename); + in >> norm_geo_data; + if (!in) { + warning("Error reading %s, using all 1s instead\n", in_filename); + do_geo = false; } - - // block norm - if (do_block) - { - { - char *in_filename = new char[in_filename_prefix.size() + 30]; - sprintf(in_filename, "%s_%s_%d.out", - in_filename_prefix.c_str(), "block", iter_num); - std::ifstream in(in_filename); - in >> norm_block_data; - if (!in) - { - warning("Error reading %s, using all 1s instead\n", in_filename); - do_block = false; - } - delete[] in_filename; - } - } + delete[] in_filename; + } + } + // block norm + if (do_block) { { - FanProjData fan_data; - make_fan_data(fan_data, *measured_data); -// make_fan_data_remove_gaps(fan_data, *measured_data); - - if (do_eff) - apply_efficiencies(fan_data, efficiencies, apply_or_undo); - if (do_geo) - apply_geo_norm(fan_data, norm_geo_data, apply_or_undo); - if (do_block) - apply_block_norm(fan_data, norm_block_data, apply_or_undo); - - if (do_display) - display(fan_data, "input*norm"); - set_fan_data(*out_proj_data_ptr, fan_data); -// set_fan_data_add_gaps(*out_proj_data_ptr, fan_data); - - + char* in_filename = new char[in_filename_prefix.size() + 30]; + sprintf(in_filename, "%s_%s_%d.out", in_filename_prefix.c_str(), "block", iter_num); + std::ifstream in(in_filename); + in >> norm_block_data; + if (!in) { + warning("Error reading %s, using all 1s instead\n", in_filename); + do_block = false; + } + delete[] in_filename; } } + { + FanProjData fan_data; + make_fan_data(fan_data, *measured_data); + // make_fan_data_remove_gaps(fan_data, *measured_data); + + if (do_eff) + apply_efficiencies(fan_data, efficiencies, apply_or_undo); + if (do_geo) + apply_geo_norm(fan_data, norm_geo_data, apply_or_undo); + if (do_block) + apply_block_norm(fan_data, norm_block_data, apply_or_undo); + + if (do_display) + display(fan_data, "input*norm"); + set_fan_data(*out_proj_data_ptr, fan_data); + // set_fan_data_add_gaps(*out_proj_data_ptr, fan_data); + } + } + return EXIT_SUCCESS; } diff --git a/src/utilities/attenuation_coefficients_to_projections.cxx b/src/utilities/attenuation_coefficients_to_projections.cxx index 522eb7eb87..220c33a687 100644 --- a/src/utilities/attenuation_coefficients_to_projections.cxx +++ b/src/utilities/attenuation_coefficients_to_projections.cxx @@ -26,7 +26,7 @@ attenuation_coefficients_to_projections \ --AF|--ACF \endverbatim - Use --AF if input are attenuation factors, --ACF for + Use --AF if input are attenuation factors, --ACF for attenuation correction factors (i.e. the inverse of the former). \warning Currently thresholds ACF values to maximum 150 (and AF to minimum 1/150) @@ -36,7 +36,6 @@ \author Kris Thielemans */ - #include "stir/ProjData.h" #include "stir/ProjDataInterfile.h" #include "stir/Viewgram.h" @@ -46,75 +45,63 @@ USING_NAMESPACE_STIR -static void print_usage_and_exit() -{ - std::cerr<<"\nUsage:\nattenuation_coefficients_to_projections\n\t" - << " --AF|--ACF \n"; +static void +print_usage_and_exit() { + std::cerr << "\nUsage:\nattenuation_coefficients_to_projections\n\t" + << " --AF|--ACF \n"; exit(EXIT_FAILURE); } -int -main (int argc, char * argv[]) -{ +int +main(int argc, char* argv[]) { // TODO get this from cmdline - const float acf_threshold=150.F; - - if (argc!=4) + const float acf_threshold = 150.F; + + if (argc != 4) print_usage_and_exit(); - bool doACF=true;// initialise to avoid compiler warning - if (strcmp(argv[1],"--ACF")==0) - doACF=true; - else if (strcmp(argv[1],"--AF")==0) - doACF=false; + bool doACF = true; // initialise to avoid compiler warning + if (strcmp(argv[1], "--ACF") == 0) + doACF = true; + else if (strcmp(argv[1], "--AF") == 0) + doACF = false; else print_usage_and_exit(); - ++argv; --argc; - - shared_ptr attenuation_proj_data_ptr = - ProjData::read_from_file(argv[2]); + ++argv; + --argc; + + shared_ptr attenuation_proj_data_ptr = ProjData::read_from_file(argv[2]); const std::string output_file_name = argv[1]; - shared_ptr - out_proj_data_ptr(new ProjDataInterfile(attenuation_proj_data_ptr->get_exam_info_sptr(), - attenuation_proj_data_ptr->get_proj_data_info_sptr()->create_shared_clone(), - output_file_name)); - - for (int segment_num = attenuation_proj_data_ptr->get_min_segment_num(); - segment_num<= attenuation_proj_data_ptr->get_max_segment_num(); - ++segment_num) - for ( int view_num = attenuation_proj_data_ptr->get_min_view_num(); - view_num<=attenuation_proj_data_ptr->get_max_view_num(); - ++view_num) - { - Viewgram viewgram = attenuation_proj_data_ptr->get_viewgram(view_num,segment_num); - - if (doACF) - { - // threshold minimum to arbitrary value as log will otherwise explode) - threshold_lower(viewgram.begin_all(), viewgram.end_all(), 1/acf_threshold); - in_place_log(viewgram); - } - else - { - // threshold maximum to arbitrary value as log will otherwise explode) - threshold_upper(viewgram.begin_all(), viewgram.end_all(), acf_threshold); - in_place_log(viewgram); - viewgram *= -1.F; - } - - if (out_proj_data_ptr->set_viewgram(viewgram) != Succeeded::yes) - { - warning("Error setting output viewgram at segment %d view %d. Exiting", - segment_num, view_num); - return EXIT_FAILURE; - } + shared_ptr out_proj_data_ptr( + new ProjDataInterfile(attenuation_proj_data_ptr->get_exam_info_sptr(), + attenuation_proj_data_ptr->get_proj_data_info_sptr()->create_shared_clone(), output_file_name)); + + for (int segment_num = attenuation_proj_data_ptr->get_min_segment_num(); + segment_num <= attenuation_proj_data_ptr->get_max_segment_num(); ++segment_num) + for (int view_num = attenuation_proj_data_ptr->get_min_view_num(); view_num <= attenuation_proj_data_ptr->get_max_view_num(); + ++view_num) { + Viewgram viewgram = attenuation_proj_data_ptr->get_viewgram(view_num, segment_num); + + if (doACF) { + // threshold minimum to arbitrary value as log will otherwise explode) + threshold_lower(viewgram.begin_all(), viewgram.end_all(), 1 / acf_threshold); + in_place_log(viewgram); + } else { + // threshold maximum to arbitrary value as log will otherwise explode) + threshold_upper(viewgram.begin_all(), viewgram.end_all(), acf_threshold); + in_place_log(viewgram); + viewgram *= -1.F; + } + + if (out_proj_data_ptr->set_viewgram(viewgram) != Succeeded::yes) { + warning("Error setting output viewgram at segment %d view %d. Exiting", segment_num, view_num); + return EXIT_FAILURE; + } } - - + return EXIT_SUCCESS; } - diff --git a/src/utilities/back_project.cxx b/src/utilities/back_project.cxx index c296e559af..5e06a9c09c 100644 --- a/src/utilities/back_project.cxx +++ b/src/utilities/back_project.cxx @@ -53,60 +53,52 @@ #include #include -static void print_usage_and_exit() -{ - std::cerr<<"\nUsage:\nback_project output-filename proj_data_to_back_project template_image [backprojector-parfile ]\n"; - std::cerr<<"The default projector uses the ray-tracing matrix.\n\n"; - std::cerr<<"Example parameter file:\n\n" - <<"Back Projector parameters:=\n" - <<" type := Matrix\n" - <<" Back projector Using Matrix Parameters :=\n" - <<" Matrix type := Ray Tracing\n" - <<" Ray tracing matrix parameters :=\n" - <<" End Ray tracing matrix parameters :=\n" - <<" End Back Projector Using Matrix Parameters :=\n" - <<"End:=\n"; +static void +print_usage_and_exit() { + std::cerr << "\nUsage:\nback_project output-filename proj_data_to_back_project template_image [backprojector-parfile ]\n"; + std::cerr << "The default projector uses the ray-tracing matrix.\n\n"; + std::cerr << "Example parameter file:\n\n" + << "Back Projector parameters:=\n" + << " type := Matrix\n" + << " Back projector Using Matrix Parameters :=\n" + << " Matrix type := Ray Tracing\n" + << " Ray tracing matrix parameters :=\n" + << " End Ray tracing matrix parameters :=\n" + << " End Back Projector Using Matrix Parameters :=\n" + << "End:=\n"; exit(EXIT_FAILURE); } - -int -main (int argc, char * argv[]) -{ +int +main(int argc, char* argv[]) { using namespace stir; - if (argc!=4 && argc!=5 ) + if (argc != 4 && argc != 5) print_usage_and_exit(); - + const std::string output_filename = argv[1]; - shared_ptr proj_data_sptr = - ProjData::read_from_file(argv[2]); + shared_ptr proj_data_sptr = ProjData::read_from_file(argv[2]); - shared_ptr > - image_density_sptr(read_from_file >(argv[3])); + shared_ptr> image_density_sptr(read_from_file>(argv[3])); image_density_sptr->set_exam_info(proj_data_sptr->get_exam_info()); shared_ptr back_projector_sptr; - if (argc>=5) - { - KeyParser parser; - parser.add_start_key("Back Projector parameters"); - parser.add_parsing_key("type", &back_projector_sptr); - parser.add_stop_key("END"); - parser.parse(argv[4]); - } - else - { - shared_ptr PM(new ProjMatrixByBinUsingRayTracing()); - back_projector_sptr.reset(new BackProjectorByBinUsingProjMatrixByBin(PM)); - } + if (argc >= 5) { + KeyParser parser; + parser.add_start_key("Back Projector parameters"); + parser.add_parsing_key("type", &back_projector_sptr); + parser.add_stop_key("END"); + parser.parse(argv[4]); + } else { + shared_ptr PM(new ProjMatrixByBinUsingRayTracing()); + back_projector_sptr.reset(new BackProjectorByBinUsingProjMatrixByBin(PM)); + } image_density_sptr->fill(0.F); - back_projector_sptr->set_up(proj_data_sptr->get_proj_data_info_sptr()->create_shared_clone(), - image_density_sptr ); + back_projector_sptr->set_up(proj_data_sptr->get_proj_data_info_sptr()->create_shared_clone(), image_density_sptr); #if 0 back_projector_sptr->back_project(*image_density_sptr, *proj_data_sptr); @@ -115,9 +107,7 @@ main (int argc, char * argv[]) back_projector_sptr->back_project(*proj_data_sptr); back_projector_sptr->get_output(*image_density_sptr); #endif - OutputFileFormat >::default_sptr()-> - write_to_file(output_filename, *image_density_sptr); + OutputFileFormat>::default_sptr()->write_to_file(output_filename, *image_density_sptr); return EXIT_SUCCESS; } - diff --git a/src/utilities/calculate_attenuation_coefficients.cxx b/src/utilities/calculate_attenuation_coefficients.cxx index 001a8c986f..9d25d749a6 100644 --- a/src/utilities/calculate_attenuation_coefficients.cxx +++ b/src/utilities/calculate_attenuation_coefficients.cxx @@ -31,7 +31,7 @@ --ACF calculates the attenuation correction factors, --AF calculates the attenuation factor (i.e. the inverse of the ACFs). - The option --PMRT forces forward projection using the Probability Matrix Using Ray Tracing + The option --PMRT forces forward projection using the Probability Matrix Using Ray Tracing (stir::ProjMatrixByBinUsingRayTracing). The attenuation_image has to contain an estimate of the mu-map for the image. It will be used @@ -66,113 +66,98 @@ using std::endl; using std::cerr; #endif - START_NAMESPACE_STIR - -static void print_usage_and_exit() -{ - std::cerr<<"\nUsage: calculate_attenuation_coefficients [--PMRT --NOPMRT] --AF|--ACF \n" - <<"\t--ACF calculates the attenuation correction factors\n" - <<"\t--AF calculates the attenuation factor (i.e. the inverse of the ACFs)\n" - <<"\t--PMRT uses the Ray Tracing Projection Matrix (default)\n" - <<"\t--NOPMRT uses the (old) Ray Tracing forward projector\n" - <<"The input image has to give the attenuation (or mu) values at 511 keV, and be in units of cm^-1.\n"; - exit(EXIT_FAILURE); +static void +print_usage_and_exit() { + std::cerr << "\nUsage: calculate_attenuation_coefficients [--PMRT --NOPMRT] --AF|--ACF \n" + << "\t--ACF calculates the attenuation correction factors\n" + << "\t--AF calculates the attenuation factor (i.e. the inverse of the ACFs)\n" + << "\t--PMRT uses the Ray Tracing Projection Matrix (default)\n" + << "\t--NOPMRT uses the (old) Ray Tracing forward projector\n" + << "The input image has to give the attenuation (or mu) values at 511 keV, and be in units of cm^-1.\n"; + exit(EXIT_FAILURE); } END_NAMESPACE_STIR USING_NAMESPACE_STIR -int -main (int argc, char * argv[]) -{ +int +main(int argc, char* argv[]) { // variable to decide to use the ray-tracing projection matrix or not - bool use_PMRT=true; - - if (argc>1 && strcmp(argv[1],"--PMRT")==0) - { - use_PMRT=true; - --argc; ++argv; - } - if (argc!=5 ) + bool use_PMRT = true; + + if (argc > 1 && strcmp(argv[1], "--PMRT") == 0) { + use_PMRT = true; + --argc; + ++argv; + } + if (argc != 5) print_usage_and_exit(); - bool doACF=true;// initialise to avoid compiler warning - if (strcmp(argv[1],"--ACF")==0) - doACF=true; - else if (strcmp(argv[1],"--AF")==0) - doACF=false; + bool doACF = true; // initialise to avoid compiler warning + if (strcmp(argv[1], "--ACF") == 0) + doACF = true; + else if (strcmp(argv[1], "--AF") == 0) + doACF = false; else print_usage_and_exit(); - ++argv; --argc; - + ++argv; + --argc; + const std::string atten_image_filename(argv[2]); // read it to get ExamInfo - shared_ptr > - atten_image_sptr(read_from_file >(atten_image_filename)); + shared_ptr> atten_image_sptr(read_from_file>(atten_image_filename)); - shared_ptr template_proj_data_ptr = - ProjData::read_from_file(argv[3]); + shared_ptr template_proj_data_ptr = ProjData::read_from_file(argv[3]); shared_ptr forw_projector_ptr; - if (use_PMRT) - { - shared_ptr PM(new ProjMatrixByBinUsingRayTracing()); - forw_projector_ptr.reset(new ForwardProjectorByBinUsingProjMatrixByBin(PM)); - } - else - { + if (use_PMRT) { + shared_ptr PM(new ProjMatrixByBinUsingRayTracing()); + forw_projector_ptr.reset(new ForwardProjectorByBinUsingProjMatrixByBin(PM)); + } else { forw_projector_ptr.reset(new ForwardProjectorByBinUsingRayTracing()); } - cerr << "\n\nForward projector used:\n" << forw_projector_ptr->parameter_info(); + cerr << "\n\nForward projector used:\n" << forw_projector_ptr->parameter_info(); - if (template_proj_data_ptr->get_proj_data_info_sptr()->is_tof_data()) - { - warning("The scanner template provided contains timing information. The calculation of the attenuation coefficients will not take them into consideration.\n"); + if (template_proj_data_ptr->get_proj_data_info_sptr()->is_tof_data()) { + warning("The scanner template provided contains timing information. The calculation of the attenuation coefficients will not " + "take them into consideration.\n"); } const std::string output_file_name = argv[1]; - shared_ptr - out_proj_data_ptr( - new ProjDataInterfile(template_proj_data_ptr->get_exam_info_sptr(),// TODO this should say it's an ACF File - template_proj_data_ptr->get_proj_data_info_sptr()->create_non_tof_clone(), - output_file_name, - std::ios::in|std::ios::out|std::ios::trunc)); + shared_ptr out_proj_data_ptr( + new ProjDataInterfile(template_proj_data_ptr->get_exam_info_sptr(), // TODO this should say it's an ACF File + template_proj_data_ptr->get_proj_data_info_sptr()->create_non_tof_clone(), output_file_name, + std::ios::in | std::ios::out | std::ios::trunc)); // fill with 1s as we will "normalise" this sinogram. out_proj_data_ptr->fill(1.F); // construct a normalisation object that does all the work for us. - shared_ptr normalisation_ptr - (new BinNormalisationFromAttenuationImage(atten_image_filename, - forw_projector_ptr)); - - if ( - normalisation_ptr->set_up(template_proj_data_ptr->get_exam_info_sptr(),template_proj_data_ptr->get_proj_data_info_sptr()->create_non_tof_clone()) - != Succeeded::yes) - { - warning("calculate_attenuation_coefficients: set-up of normalisation failed\n"); - return EXIT_FAILURE; - } + shared_ptr normalisation_ptr( + new BinNormalisationFromAttenuationImage(atten_image_filename, forw_projector_ptr)); + + if (normalisation_ptr->set_up(template_proj_data_ptr->get_exam_info_sptr(), + template_proj_data_ptr->get_proj_data_info_sptr()->create_non_tof_clone()) != Succeeded::yes) { + warning("calculate_attenuation_coefficients: set-up of normalisation failed\n"); + return EXIT_FAILURE; + } // dummy values currently necessary for BinNormalisation, but they will be ignored const double start_frame = 0; const double end_frame = 0; shared_ptr symmetries_sptr(forw_projector_ptr->get_symmetries_used()->clone()); - if (doACF) - { - normalisation_ptr->apply(*out_proj_data_ptr, symmetries_sptr); - } - else - { - normalisation_ptr->undo(*out_proj_data_ptr,start_frame,end_frame, symmetries_sptr); - } + if (doACF) { + normalisation_ptr->apply(*out_proj_data_ptr, symmetries_sptr); + } else { + normalisation_ptr->undo(*out_proj_data_ptr, start_frame, end_frame, symmetries_sptr); + } return EXIT_SUCCESS; } - diff --git a/src/utilities/compare_image.cxx b/src/utilities/compare_image.cxx index b0dd2bd8a5..1ceded01cb 100644 --- a/src/utilities/compare_image.cxx +++ b/src/utilities/compare_image.cxx @@ -3,7 +3,7 @@ /*! \file -\ingroup utilities +\ingroup utilities \brief compare images to see if they are identical, allowing for small differences \author Matthew Jacobson @@ -12,7 +12,7 @@ \author Charalampos Tsoumpas: Add the tolerance as input This utility compares two images. They are deemed identical if -their maximum absolute relative difference is less than a hard-coded tolerance +their maximum absolute relative difference is less than a hard-coded tolerance value. Diagnostic output is written to stdout, and the return value indicates if the files are identical or not. @@ -36,7 +36,7 @@ if the files are identical or not. See STIR/LICENSE.txt for details */ /* Modification History: - + KT 12/09/2001 added rim_truncation option */ @@ -54,115 +54,105 @@ using std::cout; using std::endl; #endif - - //********************** main - - USING_NAMESPACE_STIR - -int main(int argc, char *argv[]) -{ - if(argc<3 || argc>7) - { - cerr << "Usage: \n" << argv[0] << "\n\t" - << "[-r rimsize] \n\t" - << "[-t tolerance] \n\t" - << "old_image new_image \n\t" - << "'rimsize' has to be a nonnegative integer.\n\t" - << "'tolerance' is by default .0005 \n\t" - << "When the -r option is used, the (radial) rim of the\n\t" - << "images will be set to 0, for 'rimsize' pixels.\n"; - return(EXIT_FAILURE); +int +main(int argc, char* argv[]) { + if (argc < 3 || argc > 7) { + cerr << "Usage: \n" + << argv[0] << "\n\t" + << "[-r rimsize] \n\t" + << "[-t tolerance] \n\t" + << "old_image new_image \n\t" + << "'rimsize' has to be a nonnegative integer.\n\t" + << "'tolerance' is by default .0005 \n\t" + << "When the -r option is used, the (radial) rim of the\n\t" + << "images will be set to 0, for 'rimsize' pixels.\n"; + return (EXIT_FAILURE); } // skip program name --argc; ++argv; int rim_truncation_image = -1; - float tolerance = .0005F ; + float tolerance = .0005F; // first process command line options - while (argc>0 && argv[0][0]=='-') - { - if (strcmp(argv[0], "-r")==0) - { - if (argc<2) - { cerr << "Option '-r' expects a nonnegative (integer) argument\n"; exit(EXIT_FAILURE); } - rim_truncation_image = atoi(argv[1]); - argc-=2; argv+=2; - } - if (strcmp(argv[0], "-t")==0) - { - if (argc<2) - { cerr << "Option '-t' expects a (float) argument\n"; exit(EXIT_FAILURE); } - tolerance = static_cast(atof(argv[1])); - argc-=2; argv+=2; - } + while (argc > 0 && argv[0][0] == '-') { + if (strcmp(argv[0], "-r") == 0) { + if (argc < 2) { + cerr << "Option '-r' expects a nonnegative (integer) argument\n"; + exit(EXIT_FAILURE); + } + rim_truncation_image = atoi(argv[1]); + argc -= 2; + argv += 2; + } + if (strcmp(argv[0], "-t") == 0) { + if (argc < 2) { + cerr << "Option '-t' expects a (float) argument\n"; + exit(EXIT_FAILURE); + } + tolerance = static_cast(atof(argv[1])); + argc -= 2; + argv += 2; } + } - shared_ptr< DiscretisedDensity<3,float> > - first_operand(read_from_file >(argv[0])); + shared_ptr> first_operand(read_from_file>(argv[0])); - if (is_null_ptr(first_operand)) - { cerr << "Could not read first file\n"; exit(EXIT_FAILURE); } + if (is_null_ptr(first_operand)) { + cerr << "Could not read first file\n"; + exit(EXIT_FAILURE); + } - shared_ptr< DiscretisedDensity<3,float> > - second_operand(read_from_file >(argv[1])); - if (is_null_ptr(second_operand)) - { cerr << "Could not read 2nd file\n"; exit(EXIT_FAILURE); } + shared_ptr> second_operand(read_from_file>(argv[1])); + if (is_null_ptr(second_operand)) { + cerr << "Could not read 2nd file\n"; + exit(EXIT_FAILURE); + } // check if images are compatible { std::string explanation; - if (!first_operand->has_same_characteristics(*second_operand, - explanation)) - { - warning("input images do not have the same characteristics.\n%s", - explanation.c_str()); - return EXIT_FAILURE; - } + if (!first_operand->has_same_characteristics(*second_operand, explanation)) { + warning("input images do not have the same characteristics.\n%s", explanation.c_str()); + return EXIT_FAILURE; + } } - if (rim_truncation_image>=0) - { + if (rim_truncation_image >= 0) { truncate_rim(*first_operand, rim_truncation_image); truncate_rim(*second_operand, rim_truncation_image); } - float reference_max=first_operand->find_max(); - float reference_min=first_operand->find_min(); + float reference_max = first_operand->find_max(); + float reference_min = first_operand->find_min(); - float amplitude=fabs(reference_max)>fabs(reference_min)? - fabs(reference_max):fabs(reference_min); + float amplitude = fabs(reference_max) > fabs(reference_min) ? fabs(reference_max) : fabs(reference_min); *first_operand -= *second_operand; - const float max_error=first_operand->find_max(); - const float min_error=first_operand->find_min(); + const float max_error = first_operand->find_max(); + const float min_error = first_operand->find_min(); in_place_abs(*first_operand); - const float max_abs_error=first_operand->find_max(); + const float max_abs_error = first_operand->find_max(); - const bool same=(max_abs_error/amplitude<=tolerance); + const bool same = (max_abs_error / amplitude <= tolerance); - cout << "\nMaximum absolute error = "<& input1, const SegmentByView &input2, - float &max_pos_error, float& max_neg_error, float &litude) -{ - const float reference_max=input1.find_max(); - const float reference_min=input1.find_min(); - - const float local_amplitude= - fabs(reference_max)>fabs(reference_min)? - fabs(reference_max):fabs(reference_min); - - amplitude=local_amplitude>amplitude?local_amplitude:amplitude; - - input1-=input2; - const float max_local_pos_error=input1.find_max(); - const float max_local_neg_error=input1.find_min(); - - max_pos_error=max_local_pos_error>max_pos_error? max_local_pos_error:max_pos_error; - max_neg_error=max_local_neg_error& input1, const SegmentByView& input2, float& max_pos_error, float& max_neg_error, + float& amplitude) { + const float reference_max = input1.find_max(); + const float reference_min = input1.find_min(); + + const float local_amplitude = fabs(reference_max) > fabs(reference_min) ? fabs(reference_max) : fabs(reference_min); + amplitude = local_amplitude > amplitude ? local_amplitude : amplitude; + input1 -= input2; + const float max_local_pos_error = input1.find_max(); + const float max_local_neg_error = input1.find_min(); + max_pos_error = max_local_pos_error > max_pos_error ? max_local_pos_error : max_pos_error; + max_neg_error = max_local_neg_error < max_neg_error ? max_local_neg_error : max_neg_error; +} END_NAMESPACE_STIR //********************** main - - - - -int main(int argc, char *argv[]) -{ +int +main(int argc, char* argv[]) { USING_NAMESPACE_STIR; // defaults - float tolerance=0.0001F; - + float tolerance = 0.0001F; // first process command line options - const char * const progname = argv[0]; + const char* const progname = argv[0]; // skip program name --argc; ++argv; - while (argc>0 && argv[0][0]=='-') - { - if (strcmp(argv[0], "-t")==0) - { - if (argc<2) - { cerr << "Option '-t' expects a (float) argument\n"; exit(EXIT_FAILURE); } - tolerance = static_cast(atof(argv[1])); - argc-=2; argv+=2; - } - else - { - std::cerr << "Unknown option '" << argv[0] <<"'\n"; exit(EXIT_FAILURE); - } + while (argc > 0 && argv[0][0] == '-') { + if (strcmp(argv[0], "-t") == 0) { + if (argc < 2) { + cerr << "Option '-t' expects a (float) argument\n"; + exit(EXIT_FAILURE); + } + tolerance = static_cast(atof(argv[1])); + argc -= 2; + argv += 2; + } else { + std::cerr << "Unknown option '" << argv[0] << "'\n"; + exit(EXIT_FAILURE); } + } - if(argc<2) - { - cerr<< "Usage:" << progname << " [-t tolerance] old_projdata new_projdata [max_segment_num]\n"; + if (argc < 2) { + cerr << "Usage:" << progname << " [-t tolerance] old_projdata new_projdata [max_segment_num]\n"; exit(EXIT_FAILURE); } - - - shared_ptr first_operand=ProjData::read_from_file(argv[0]); - shared_ptr second_operand=ProjData::read_from_file(argv[1]); + shared_ptr first_operand = ProjData::read_from_file(argv[0]); + shared_ptr second_operand = ProjData::read_from_file(argv[1]); - int max_segment=first_operand->get_max_segment_num(); - if(argc==3 && atoi(argv[2])>=0 && atoi(argv[2])get_max_segment_num(); + if (argc == 3 && atoi(argv[2]) >= 0 && atoi(argv[2]) < max_segment) + max_segment = atoi(argv[2]); // compare proj_data_info { shared_ptr first_sptr(first_operand->get_proj_data_info_sptr()->clone()); shared_ptr second_sptr(second_operand->get_proj_data_info_sptr()->clone()); - if (argc==4) - { - first_sptr->reduce_segment_range(-max_segment, max_segment); - second_sptr->reduce_segment_range(-max_segment, max_segment); - } - if (*first_sptr != *second_sptr) - { - cout << "\nProjection data sizes or other data characteristics are not identical. \n" - << "Use list_projdata_info to investigate.\n"; - return EXIT_FAILURE; } + if (argc == 4) { + first_sptr->reduce_segment_range(-max_segment, max_segment); + second_sptr->reduce_segment_range(-max_segment, max_segment); + } + if (*first_sptr != *second_sptr) { + cout << "\nProjection data sizes or other data characteristics are not identical. \n" + << "Use list_projdata_info to investigate.\n"; + return EXIT_FAILURE; + } } - float max_pos_error=0.F, max_neg_error=0.F, amplitude=0.F; - for (int segment_num = -max_segment; segment_num <= max_segment ; segment_num++) - { - SegmentByView input1=first_operand->get_segment_by_view(segment_num); - const SegmentByView input2=second_operand->get_segment_by_view(segment_num); + float max_pos_error = 0.F, max_neg_error = 0.F, amplitude = 0.F; + for (int segment_num = -max_segment; segment_num <= max_segment; segment_num++) { + SegmentByView input1 = first_operand->get_segment_by_view(segment_num); + const SegmentByView input2 = second_operand->get_segment_by_view(segment_num); - update_comparison(input1,input2,max_pos_error,max_neg_error, amplitude); - } + update_comparison(input1, input2, max_pos_error, max_neg_error, amplitude); + } - const float max_abs_error=max(max_pos_error, -max_neg_error); - bool same=(max_abs_error/amplitude<=tolerance)?true:false; + const float max_abs_error = max(max_pos_error, -max_neg_error); + bool same = (max_abs_error / amplitude <= tolerance) ? true : false; - cout << "\nMaximum absolute error = "< START_NAMESPACE_STIR -static void print_usage_and_exit() -{ - std::cerr<<"\nThis executable computes the square root of the Hessian row sum of the objective function." - "\n\nUsage: compute_sqrt_Hessian_row_sum compute_sqrt_Hessian_row_sum.par" - "\n\n (The example parameter file can be found in the samples folder.)" << std::endl; +static void +print_usage_and_exit() { + std::cerr << "\nThis executable computes the square root of the Hessian row sum of the objective function." + "\n\nUsage: compute_sqrt_Hessian_row_sum compute_sqrt_Hessian_row_sum.par" + "\n\n (The example parameter file can be found in the samples folder.)" + << std::endl; exit(EXIT_FAILURE); } END_NAMESPACE_STIR USING_NAMESPACE_STIR int -main(int argc, char *argv[]) -{ - if (argc!=2) +main(int argc, char* argv[]) { + if (argc != 2) print_usage_and_exit(); - SqrtHessianRowSum> SqrtHessianRowSumObject(argv[1]); + SqrtHessianRowSum> SqrtHessianRowSumObject(argv[1]); SqrtHessianRowSumObject.set_up(); SqrtHessianRowSumObject.process_data(); return EXIT_SUCCESS; diff --git a/src/utilities/construct_randoms_from_GEsingles.cxx b/src/utilities/construct_randoms_from_GEsingles.cxx old mode 100755 new mode 100644 index f060fd852b..3fafcd382a --- a/src/utilities/construct_randoms_from_GEsingles.cxx +++ b/src/utilities/construct_randoms_from_GEsingles.cxx @@ -64,20 +64,17 @@ using std::ios; USING_NAMESPACE_STIR -int main(int argc, char **argv) -{ - if (argc!=4 && argc!=3) - { - cerr << "Usage: " << argv[0] - << " out_filename GE_RDF_filename [template_projdata]\n" - << "The template is used for size- and time-frame-info, but actual counts are ignored.\n" - << "If no template is specified, we will use the normal GE sizes and the time frame information of the RDF.\n"; - return EXIT_FAILURE; - } +int +main(int argc, char** argv) { + if (argc != 4 && argc != 3) { + cerr << "Usage: " << argv[0] << " out_filename GE_RDF_filename [template_projdata]\n" + << "The template is used for size- and time-frame-info, but actual counts are ignored.\n" + << "If no template is specified, we will use the normal GE sizes and the time frame information of the RDF.\n"; + return EXIT_FAILURE; + } // const int eff_iter_num = atoi(argv[4]); - const int iter_num = 1;//atoi(argv[5]); + const int iter_num = 1; // atoi(argv[5]); - const string input_filename = argv[2]; const string output_file_name = argv[1]; const string program_name = argv[0]; @@ -86,82 +83,66 @@ int main(int argc, char **argv) GE::RDF_HDF5::GEHDF5Wrapper input_file(input_filename); std::string template_filename; - if (argc==4) - { - template_filename = argv[3]; - shared_ptr template_projdata_sptr = ProjData::read_from_file(template_filename); - proj_data_info_sptr = template_projdata_sptr->get_proj_data_info_sptr(); - exam_info_sptr = template_projdata_sptr->get_exam_info_sptr(); - } - else - { - template_filename = input_filename; - proj_data_info_sptr = input_file.get_proj_data_info_sptr(); - exam_info_sptr = input_file.get_exam_info_sptr(); - } - - if (exam_info_sptr->get_time_frame_definitions().get_num_time_frames()==0 || + if (argc == 4) { + template_filename = argv[3]; + shared_ptr template_projdata_sptr = ProjData::read_from_file(template_filename); + proj_data_info_sptr = template_projdata_sptr->get_proj_data_info_sptr(); + exam_info_sptr = template_projdata_sptr->get_exam_info_sptr(); + } else { + template_filename = input_filename; + proj_data_info_sptr = input_file.get_proj_data_info_sptr(); + exam_info_sptr = input_file.get_exam_info_sptr(); + } + + if (exam_info_sptr->get_time_frame_definitions().get_num_time_frames() == 0 || exam_info_sptr->get_time_frame_definitions().get_duration(1) < .0001) - error("Missing time-frame information in \"" + template_filename +'\"'); + error("Missing time-frame information in \"" + template_filename + '\"'); - ProjDataInterfile - proj_data(exam_info_sptr, - proj_data_info_sptr->create_shared_clone(), - output_file_name); + ProjDataInterfile proj_data(exam_info_sptr, proj_data_info_sptr->create_shared_clone(), output_file_name); - const int num_rings = - proj_data_info_sptr->get_scanner_ptr()->get_num_rings(); - const int num_detectors_per_ring = - proj_data_info_sptr->get_scanner_ptr()->get_num_detectors_per_ring(); + const int num_rings = proj_data_info_sptr->get_scanner_ptr()->get_num_rings(); + const int num_detectors_per_ring = proj_data_info_sptr->get_scanner_ptr()->get_num_detectors_per_ring(); // this uses the wrong naming currently. It so happens that the formulas are the same // as when multiplying efficiencies DetectorEfficiencies efficiencies(IndexRange2D(num_rings, num_detectors_per_ring)); { - GE::RDF_HDF5::SinglesRatesFromGEHDF5 singles; + GE::RDF_HDF5::SinglesRatesFromGEHDF5 singles; singles.read_singles_from_listmode_file(input_filename); // efficiencies - if (true) - { - for (int r=0; r pos(c,r,0); - efficiencies[r][c]=singles.get_singles_rate(pos, - exam_info_sptr->get_time_frame_definitions().get_start_time(1), - exam_info_sptr->get_time_frame_definitions().get_end_time(1)); + if (true) { + for (int r = 0; r < num_rings; ++r) + for (int c = 0; c < num_detectors_per_ring; ++c) { + DetectionPosition<> pos(c, r, 0); + efficiencies[r][c] = singles.get_singles_rate(pos, exam_info_sptr->get_time_frame_definitions().get_start_time(1), + exam_info_sptr->get_time_frame_definitions().get_end_time(1)); } } - }// nothing + } // nothing { - const ProjDataInfoCylindricalNoArcCorr * const proj_data_info_ptr = - dynamic_cast - (proj_data.get_proj_data_info_sptr().get()); - if (proj_data_info_ptr == 0) - { + const ProjDataInfoCylindricalNoArcCorr* const proj_data_info_ptr = + dynamic_cast(proj_data.get_proj_data_info_sptr().get()); + if (proj_data_info_ptr == 0) { error("Can only process not arc-corrected data\n"); } const int half_fan_size = - std::min(proj_data_info_ptr->get_max_tangential_pos_num(), - -proj_data_info_ptr->get_min_tangential_pos_num()); - const int fan_size = 2*half_fan_size+1; + std::min(proj_data_info_ptr->get_max_tangential_pos_num(), -proj_data_info_ptr->get_min_tangential_pos_num()); + const int fan_size = 2 * half_fan_size + 1; - const int max_ring_diff = - proj_data_info_ptr->get_max_ring_difference(proj_data_info_ptr->get_max_segment_num()); + const int max_ring_diff = proj_data_info_ptr->get_max_ring_difference(proj_data_info_ptr->get_max_segment_num()); - const int mashing_factor = - proj_data_info_ptr->get_view_mashing_factor(); + const int mashing_factor = proj_data_info_ptr->get_view_mashing_factor(); shared_ptr scanner_sptr(new Scanner(*proj_data_info_ptr->get_scanner_ptr())); - const ProjDataInfoCylindricalNoArcCorr * const uncompressed_proj_data_info_ptr = - dynamic_cast(ProjDataInfo::ProjDataInfoCTI(scanner_sptr, - /*span=*/1, max_ring_diff, - /*num_views=*/num_detectors_per_ring/2, - fan_size, - /*arccorrection=*/false)); + const ProjDataInfoCylindricalNoArcCorr* const uncompressed_proj_data_info_ptr = + dynamic_cast( + ProjDataInfo::ProjDataInfoCTI(scanner_sptr, + /*span=*/1, max_ring_diff, + /*num_views=*/num_detectors_per_ring / 2, fan_size, + /*arccorrection=*/false)); const float coincidence_time_window = input_file.get_coincidence_time_window(); @@ -181,83 +162,68 @@ int main(int argc, char **argv) const double isotope_halflife = 6586.2; const double decay_corr_factor = decay_correction_factor(isotope_halflife, 0., duration); const double total_to_activity = decay_corr_factor / duration; - info(boost::format("decay correction factor: %1%, time frame duration: %2%. total correction factor from activity to counts: %3%") - % decay_corr_factor % duration % (1/total_to_activity), + info(boost::format( + "decay correction factor: %1%, time frame duration: %2%. total correction factor from activity to counts: %3%") % + decay_corr_factor % duration % (1 / total_to_activity), 2); Bin bin; Bin uncompressed_bin; - for (bin.segment_num() = proj_data.get_min_segment_num(); - bin.segment_num() <= proj_data.get_max_segment_num(); - ++ bin.segment_num()) - { + for (bin.segment_num() = proj_data.get_min_segment_num(); bin.segment_num() <= proj_data.get_max_segment_num(); + ++bin.segment_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()) - { - Sinogram sinogram = - proj_data_info_ptr->get_empty_sinogram(bin.axial_pos_num(),bin.segment_num()); + bin.axial_pos_num() <= proj_data.get_max_axial_pos_num(bin.segment_num()); ++bin.axial_pos_num()) { + Sinogram sinogram = proj_data_info_ptr->get_empty_sinogram(bin.axial_pos_num(), bin.segment_num()); const float out_m = proj_data_info_ptr->get_m(bin); - const int in_min_segment_num = - proj_data_info_ptr->get_min_ring_difference(bin.segment_num()); - const int in_max_segment_num = - proj_data_info_ptr->get_max_ring_difference(bin.segment_num()); + const int in_min_segment_num = proj_data_info_ptr->get_min_ring_difference(bin.segment_num()); + const int in_max_segment_num = proj_data_info_ptr->get_max_ring_difference(bin.segment_num()); // now loop over uncompressed detector-pairs { - for (uncompressed_bin.segment_num() = in_min_segment_num; - uncompressed_bin.segment_num() <= in_max_segment_num; - ++uncompressed_bin.segment_num()) - { - for (uncompressed_bin.axial_pos_num() = uncompressed_proj_data_info_ptr->get_min_axial_pos_num(uncompressed_bin.segment_num()); - uncompressed_bin.axial_pos_num() <= uncompressed_proj_data_info_ptr->get_max_axial_pos_num(uncompressed_bin.segment_num()); - ++uncompressed_bin.axial_pos_num() ) - { + for (uncompressed_bin.segment_num() = in_min_segment_num; uncompressed_bin.segment_num() <= in_max_segment_num; + ++uncompressed_bin.segment_num()) { + for (uncompressed_bin.axial_pos_num() = + uncompressed_proj_data_info_ptr->get_min_axial_pos_num(uncompressed_bin.segment_num()); + uncompressed_bin.axial_pos_num() <= + uncompressed_proj_data_info_ptr->get_max_axial_pos_num(uncompressed_bin.segment_num()); + ++uncompressed_bin.axial_pos_num()) { const float in_m = uncompressed_proj_data_info_ptr->get_m(uncompressed_bin); if (fabs(out_m - in_m) > 1E-4) continue; - // views etc - if (proj_data.get_min_view_num()!=0) + if (proj_data.get_min_view_num() != 0) error("Can only handle min_view_num==0\n"); - for (bin.view_num() = proj_data.get_min_view_num(); - bin.view_num() <= proj_data.get_max_view_num(); - ++ bin.view_num()) - { - - for (bin.tangential_pos_num() = -half_fan_size; - bin.tangential_pos_num() <= half_fan_size; - ++bin.tangential_pos_num()) - { + for (bin.view_num() = proj_data.get_min_view_num(); bin.view_num() <= proj_data.get_max_view_num(); + ++bin.view_num()) { + + for (bin.tangential_pos_num() = -half_fan_size; bin.tangential_pos_num() <= half_fan_size; + ++bin.tangential_pos_num()) { uncompressed_bin.tangential_pos_num() = bin.tangential_pos_num(); - for (uncompressed_bin.view_num() = bin.view_num()*mashing_factor; - uncompressed_bin.view_num() < (bin.view_num()+1)*mashing_factor; - ++ uncompressed_bin.view_num()) - { + for (uncompressed_bin.view_num() = bin.view_num() * mashing_factor; + uncompressed_bin.view_num() < (bin.view_num() + 1) * mashing_factor; ++uncompressed_bin.view_num()) { int ra = 0, a = 0; int rb = 0, b = 0; - uncompressed_proj_data_info_ptr->get_det_pair_for_bin(a, ra, b, rb, - uncompressed_bin); - - sinogram[bin.view_num()][bin.tangential_pos_num()] += - coincidence_time_window*efficiencies[ra][a]*efficiencies[rb][(b%num_detectors_per_ring)]*total_to_activity; - }// endfor uncompresed view num - }//endfor tangeial pos num - }// endfor view num - }//endfor uncompresed axial pos - }//endfor uncompresed segment num - }// nothing + uncompressed_proj_data_info_ptr->get_det_pair_for_bin(a, ra, b, rb, uncompressed_bin); + + sinogram[bin.view_num()][bin.tangential_pos_num()] += coincidence_time_window * efficiencies[ra][a] * + efficiencies[rb][(b % num_detectors_per_ring)] * + total_to_activity; + } // endfor uncompresed view num + } // endfor tangeial pos num + } // endfor view num + } // endfor uncompresed axial pos + } // endfor uncompresed segment num + } // nothing proj_data.set_sinogram(sinogram); - }//endfor axial pos num - - }//endfor segment num - }//nothing + } // endfor axial pos num + } // endfor segment num + } // nothing return EXIT_SUCCESS; } diff --git a/src/utilities/construct_randoms_from_singles.cxx b/src/utilities/construct_randoms_from_singles.cxx index dfb34bb3f6..2d2db4c1a9 100644 --- a/src/utilities/construct_randoms_from_singles.cxx +++ b/src/utilities/construct_randoms_from_singles.cxx @@ -29,7 +29,6 @@ #include "stir/ProjDataInterfile.h" - #include "stir/ProjDataInfoCylindricalNoArcCorr.h" #include "stir/Scanner.h" #include "stir/Bin.h" @@ -54,38 +53,33 @@ using std::ios; USING_NAMESPACE_STIR -int main(int argc, char **argv) -{ - if (argc!=5) - { - cerr << "Usage: " << argv[0] - << " out_filename in_norm_filename_prefix template_projdata eff_iter_num\n"; - return EXIT_FAILURE; - } +int +main(int argc, char** argv) { + if (argc != 5) { + cerr << "Usage: " << argv[0] << " out_filename in_norm_filename_prefix template_projdata eff_iter_num\n"; + return EXIT_FAILURE; + } #if 0 bool do_block = argc>=10?atoi(argv[9])!=0: true; bool do_geo = argc>=9?atoi(argv[8])!=0: true; - bool do_eff = argc>=8?atoi(argv[7])!=0: true; + bool do_eff = argc>=8?atoi(argv[7])!=0: true; #else bool do_eff = true; #endif const int eff_iter_num = atoi(argv[4]); - const int iter_num = 1;//atoi(argv[5]); - //const bool apply_or_undo = atoi(argv[4])!=0; + const int iter_num = 1; // atoi(argv[5]); + // const bool apply_or_undo = atoi(argv[4])!=0; shared_ptr template_projdata_ptr = ProjData::read_from_file(argv[3]); const string in_filename_prefix = argv[2]; const string output_file_name = argv[1]; const string program_name = argv[0]; - ProjDataInterfile - proj_data(template_projdata_ptr->get_exam_info_sptr(), - template_projdata_ptr->get_proj_data_info_sptr()->create_shared_clone(), - output_file_name); + ProjDataInterfile proj_data(template_projdata_ptr->get_exam_info_sptr(), + template_projdata_ptr->get_proj_data_info_sptr()->create_shared_clone(), output_file_name); - const int num_rings = - template_projdata_ptr->get_proj_data_info_sptr()->get_scanner_ptr()->get_num_rings(); - const int num_detectors_per_ring = - template_projdata_ptr->get_proj_data_info_sptr()->get_scanner_ptr()->get_num_detectors_per_ring(); + const int num_rings = template_projdata_ptr->get_proj_data_info_sptr()->get_scanner_ptr()->get_num_rings(); + const int num_detectors_per_ring = + template_projdata_ptr->get_proj_data_info_sptr()->get_scanner_ptr()->get_num_detectors_per_ring(); #if 0 const int num_tangential_crystals_per_block = 8; const int num_tangential_blocks = num_detectors_per_ring/num_tangential_crystals_per_block; @@ -101,20 +95,17 @@ int main(int argc, char **argv) { // efficiencies - if (do_eff) - { - char *in_filename = new char[in_filename_prefix.size() + 30]; - sprintf(in_filename, "%s_%s_%d_%d.out", - in_filename_prefix.c_str(), "eff", iter_num, eff_iter_num); - ifstream in(in_filename); - in >> efficiencies; - if (!in) - { - warning("Error reading %s, using all 1s instead\n", in_filename); - do_eff = false; - } - delete[] in_filename; + if (do_eff) { + char* in_filename = new char[in_filename_prefix.size() + 30]; + sprintf(in_filename, "%s_%s_%d_%d.out", in_filename_prefix.c_str(), "eff", iter_num, eff_iter_num); + ifstream in(in_filename); + in >> efficiencies; + if (!in) { + warning("Error reading %s, using all 1s instead\n", in_filename); + do_eff = false; } + delete[] in_filename; + } #if 0 // block norm if (do_block) @@ -138,108 +129,79 @@ int main(int argc, char **argv) { const shared_ptr proj_data_info_sptr = - dynamic_pointer_cast - (proj_data.get_proj_data_info_sptr()); - if (is_null_ptr(proj_data_info_sptr)) - { - error("Can only process not arc-corrected data\n"); - } - const int max_ring_diff = - proj_data_info_sptr->get_max_ring_difference - (proj_data_info_sptr->get_max_segment_num()); + dynamic_pointer_cast(proj_data.get_proj_data_info_sptr()); + if (is_null_ptr(proj_data_info_sptr)) { + error("Can only process not arc-corrected data\n"); + } + const int max_ring_diff = proj_data_info_sptr->get_max_ring_difference(proj_data_info_sptr->get_max_segment_num()); - const int mashing_factor = - proj_data_info_sptr->get_view_mashing_factor(); + const int mashing_factor = proj_data_info_sptr->get_view_mashing_factor(); shared_ptr scanner_sptr(new Scanner(*proj_data_info_sptr->get_scanner_ptr())); - unique_ptr uncompressed_proj_data_info_uptr - (ProjDataInfo::construct_proj_data_info(scanner_sptr, - /*span=*/1, max_ring_diff, - /*num_views=*/num_detectors_per_ring / 2, - scanner_sptr->get_max_num_non_arccorrected_bins(), - /*arccorrection=*/false)); - const ProjDataInfoCylindricalNoArcCorr * const - uncompressed_proj_data_info_sptr = - dynamic_cast - (uncompressed_proj_data_info_uptr.get()); - - + unique_ptr uncompressed_proj_data_info_uptr(ProjDataInfo::construct_proj_data_info( + scanner_sptr, + /*span=*/1, max_ring_diff, + /*num_views=*/num_detectors_per_ring / 2, scanner_sptr->get_max_num_non_arccorrected_bins(), + /*arccorrection=*/false)); + const ProjDataInfoCylindricalNoArcCorr* const uncompressed_proj_data_info_sptr = + dynamic_cast(uncompressed_proj_data_info_uptr.get()); + Bin bin; Bin uncompressed_bin; - for (bin.segment_num() = proj_data.get_min_segment_num(); - bin.segment_num() <= proj_data.get_max_segment_num(); - ++ bin.segment_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()) - { - Sinogram sinogram = - proj_data_info_sptr->get_empty_sinogram(bin.axial_pos_num(),bin.segment_num()); - const float out_m = proj_data_info_sptr->get_m(bin); - const int in_min_segment_num = - proj_data_info_sptr->get_min_ring_difference(bin.segment_num()); - const int in_max_segment_num = - proj_data_info_sptr->get_max_ring_difference(bin.segment_num()); - - // now loop over uncompressed detector-pairs - - { - for (uncompressed_bin.segment_num() = in_min_segment_num; - uncompressed_bin.segment_num() <= in_max_segment_num; - ++uncompressed_bin.segment_num()) - for (uncompressed_bin.axial_pos_num() = uncompressed_proj_data_info_sptr->get_min_axial_pos_num(uncompressed_bin.segment_num()); - uncompressed_bin.axial_pos_num() <= uncompressed_proj_data_info_sptr->get_max_axial_pos_num(uncompressed_bin.segment_num()); - ++uncompressed_bin.axial_pos_num() ) - { - const float in_m = uncompressed_proj_data_info_sptr->get_m(uncompressed_bin); - if (fabs(out_m - in_m) > 1E-4) - continue; - - - // views etc - if (proj_data.get_min_view_num()!=0) - error("Can only handle min_view_num==0\n"); - for (bin.view_num() = proj_data.get_min_view_num(); - bin.view_num() <= proj_data.get_max_view_num(); - ++ bin.view_num()) - { - - for (bin.tangential_pos_num() = proj_data_info_sptr->get_min_tangential_pos_num(); - bin.tangential_pos_num() <= proj_data_info_sptr->get_max_tangential_pos_num(); - ++bin.tangential_pos_num()) - { - uncompressed_bin.tangential_pos_num() = - bin.tangential_pos_num(); - for (uncompressed_bin.view_num() = bin.view_num()*mashing_factor; - uncompressed_bin.view_num() < (bin.view_num()+1)*mashing_factor; - ++ uncompressed_bin.view_num()) - { - - int ra = 0, a = 0; - int rb = 0, b = 0; - - uncompressed_proj_data_info_sptr->get_det_pair_for_bin(a, ra, b, rb, - uncompressed_bin); - - /*(*segment_ptr)[bin.axial_pos_num()]*/ - sinogram[bin.view_num()][bin.tangential_pos_num()] += - efficiencies[ra][a]*efficiencies[rb][b%num_detectors_per_ring]; - } - } - } - - - } - } - proj_data.set_sinogram(sinogram); - } - + for (bin.segment_num() = proj_data.get_min_segment_num(); bin.segment_num() <= proj_data.get_max_segment_num(); + ++bin.segment_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()) { + Sinogram sinogram = proj_data_info_sptr->get_empty_sinogram(bin.axial_pos_num(), bin.segment_num()); + const float out_m = proj_data_info_sptr->get_m(bin); + const int in_min_segment_num = proj_data_info_sptr->get_min_ring_difference(bin.segment_num()); + const int in_max_segment_num = proj_data_info_sptr->get_max_ring_difference(bin.segment_num()); + + // now loop over uncompressed detector-pairs + + { + for (uncompressed_bin.segment_num() = in_min_segment_num; uncompressed_bin.segment_num() <= in_max_segment_num; + ++uncompressed_bin.segment_num()) + for (uncompressed_bin.axial_pos_num() = + uncompressed_proj_data_info_sptr->get_min_axial_pos_num(uncompressed_bin.segment_num()); + uncompressed_bin.axial_pos_num() <= + uncompressed_proj_data_info_sptr->get_max_axial_pos_num(uncompressed_bin.segment_num()); + ++uncompressed_bin.axial_pos_num()) { + const float in_m = uncompressed_proj_data_info_sptr->get_m(uncompressed_bin); + if (fabs(out_m - in_m) > 1E-4) + continue; + + // views etc + if (proj_data.get_min_view_num() != 0) + error("Can only handle min_view_num==0\n"); + for (bin.view_num() = proj_data.get_min_view_num(); bin.view_num() <= proj_data.get_max_view_num(); + ++bin.view_num()) { + + for (bin.tangential_pos_num() = proj_data_info_sptr->get_min_tangential_pos_num(); + bin.tangential_pos_num() <= proj_data_info_sptr->get_max_tangential_pos_num(); ++bin.tangential_pos_num()) { + uncompressed_bin.tangential_pos_num() = bin.tangential_pos_num(); + for (uncompressed_bin.view_num() = bin.view_num() * mashing_factor; + uncompressed_bin.view_num() < (bin.view_num() + 1) * mashing_factor; ++uncompressed_bin.view_num()) { + + int ra = 0, a = 0; + int rb = 0, b = 0; + + uncompressed_proj_data_info_sptr->get_det_pair_for_bin(a, ra, b, rb, uncompressed_bin); + + /*(*segment_ptr)[bin.axial_pos_num()]*/ + sinogram[bin.view_num()][bin.tangential_pos_num()] += + efficiencies[ra][a] * efficiencies[rb][b % num_detectors_per_ring]; + } + } + } + } + } + proj_data.set_sinogram(sinogram); } + } } - return EXIT_SUCCESS; } diff --git a/src/utilities/conv_AVW.cxx b/src/utilities/conv_AVW.cxx index 0c246acde4..f3e12fdf19 100644 --- a/src/utilities/conv_AVW.cxx +++ b/src/utilities/conv_AVW.cxx @@ -20,7 +20,7 @@ \file \ingroup utilities \brief convert image files to different file format. Images are read using the AVW library. -\author Kris Thielemans +\author Kris Thielemans */ @@ -36,25 +36,23 @@ #include #include -void print_usage_and_exit( const char * const program_name) -{ +void +print_usage_and_exit(const char* const program_name) { { std::cerr << "Usage : " << program_name << " [ --flip_z ] imagefile\n"; AVW_List* list = AVW_ListFormats(AVW_SUPPORT_READ); std::cerr << "Supported file formats for reading by AVW:\n "; - for (int i=0; iNumberOfEntries; ++i) - { - std::cerr << list->Entry[i] << '\n'; + for (int i = 0; i < list->NumberOfEntries; ++i) { + std::cerr << list->Entry[i] << '\n'; } exit(EXIT_FAILURE); } } int -main(int argc, char **argv) -{ - const char * const program_name = argv[0]; +main(int argc, char** argv) { + const char* const program_name = argv[0]; // skip program name --argc; ++argv; @@ -62,100 +60,82 @@ main(int argc, char **argv) bool flip_z = false; // first process command line options - while (argc>0 && argv[0][0]=='-') - { - if (strcmp(argv[0], "--flip_z")==0) - { - flip_z = true; - argc-=1; argv+=1; - } - else - { - std::cerr << "Unknown option '" << argv[0] <<"'\n"; - print_usage_and_exit(program_name); + while (argc > 0 && argv[0][0] == '-') { + if (strcmp(argv[0], "--flip_z") == 0) { + flip_z = true; + argc -= 1; + argv += 1; + } else { + std::cerr << "Unknown option '" << argv[0] << "'\n"; + print_usage_and_exit(program_name); } } if (argc != 1) - print_usage_and_exit(program_name); + print_usage_and_exit(program_name); - char *imagefile = argv[0]; - stir::shared_ptr > > - output_file_format_sptr = - stir::OutputFileFormat >::default_sptr(); + char* imagefile = argv[0]; + stir::shared_ptr>> output_file_format_sptr = + stir::OutputFileFormat>::default_sptr(); { // open non-existent file first // this is necessary to get AVW_LoadObjectMap to work - AVW_ImageFile *avw_file= AVW_OpenImageFile("xxx non-existent I hope","r"); + AVW_ImageFile* avw_file = AVW_OpenImageFile("xxx non-existent I hope", "r"); } - + std::cout << "Reading ImageMap " << imagefile << '\n'; - AVW_ImageFile*avw_file = AVW_OpenImageFile(imagefile,"r"); - if(!avw_file) - { - AVW_Error("AVW_OpenImageFile"); - std::cout << std::flush; - exit(EXIT_FAILURE); - } - + AVW_ImageFile* avw_file = AVW_OpenImageFile(imagefile, "r"); + if (!avw_file) { + AVW_Error("AVW_OpenImageFile"); + std::cout << std::flush; + exit(EXIT_FAILURE); + } + std::cout << "Number of volumes: " << avw_file->NumVols << '\n'; unsigned int min_volume_num = 0; - unsigned int max_volume_num = avw_file->NumVols-1; - if (!stir::ask("Attempt all data-sets (Y) or single data-set (N)", true)) - { - min_volume_num = max_volume_num = - stir::ask_num("Volume number ? ", 1U, avw_file->NumVols, 1U) - - 1; // subtract 1 as AVW numbering starts from 0 - } + unsigned int max_volume_num = avw_file->NumVols - 1; + if (!stir::ask("Attempt all data-sets (Y) or single data-set (N)", true)) { + min_volume_num = max_volume_num = + stir::ask_num("Volume number ? ", 1U, avw_file->NumVols, 1U) - 1; // subtract 1 as AVW numbering starts from 0 + } { - AVW_Volume *volume = NULL; - for (unsigned int volume_num=min_volume_num; volume_num<=max_volume_num; ++volume_num) - { + AVW_Volume* volume = NULL; + for (unsigned int volume_num = min_volume_num; volume_num <= max_volume_num; ++volume_num) { { - volume = AVW_ReadVolume(avw_file, volume_num, volume); - if(!volume) - { - AVW_Error("AVW_ReadVolume"); - stir::warning("Error in volume %d. Skipping...", volume_num+1);//, AVW_ErrorMessage); - continue; - } - stir::shared_ptr > stir_volume_sptr = - stir::AVW::AVW_Volume_to_VoxelsOnCartesianGrid(volume, flip_z); - if (stir::is_null_ptr(stir_volume_sptr)) - { - stir::warning("Error converting volume to STIR format. Skipping...", volume_num); - continue; - } - char *header_filename = new char[strlen(imagefile) + 100]; - { - strcpy(header_filename, imagefile); - // keep extension, just in case we would have conflicts otherwise - // but replace the . with a _ - char * dot_ptr = strchr(stir::find_filename(header_filename),'.'); - if (dot_ptr != NULL) - *dot_ptr = '_'; - // now add stuff to say which frame, gate, bed, data this was - sprintf(header_filename+strlen(header_filename), "_f%ug%dd%db%d", - volume_num+1, 1, 0, 0); - } - if (output_file_format_sptr->write_to_file(header_filename, *stir_volume_sptr) - == stir::Succeeded::no) - { - stir::warning("Error writing %s", header_filename); - } - else - { - std::cout << "Wrote " << header_filename << '\n'; - } - delete[] header_filename; + volume = AVW_ReadVolume(avw_file, volume_num, volume); + if (!volume) { + AVW_Error("AVW_ReadVolume"); + stir::warning("Error in volume %d. Skipping...", volume_num + 1); //, AVW_ErrorMessage); + continue; + } + stir::shared_ptr> stir_volume_sptr = + stir::AVW::AVW_Volume_to_VoxelsOnCartesianGrid(volume, flip_z); + if (stir::is_null_ptr(stir_volume_sptr)) { + stir::warning("Error converting volume to STIR format. Skipping...", volume_num); + continue; + } + char* header_filename = new char[strlen(imagefile) + 100]; + { + strcpy(header_filename, imagefile); + // keep extension, just in case we would have conflicts otherwise + // but replace the . with a _ + char* dot_ptr = strchr(stir::find_filename(header_filename), '.'); + if (dot_ptr != NULL) + *dot_ptr = '_'; + // now add stuff to say which frame, gate, bed, data this was + sprintf(header_filename + strlen(header_filename), "_f%ug%dd%db%d", volume_num + 1, 1, 0, 0); + } + if (output_file_format_sptr->write_to_file(header_filename, *stir_volume_sptr) == stir::Succeeded::no) { + stir::warning("Error writing %s", header_filename); + } else { + std::cout << "Wrote " << header_filename << '\n'; + } + delete[] header_filename; } } AVW_DestroyVolume(volume); } AVW_CloseImageFile(avw_file); - - return(EXIT_SUCCESS); + return (EXIT_SUCCESS); } - - diff --git a/src/utilities/conv_GATE_raw_ECAT_projdata_to_interfile.cxx b/src/utilities/conv_GATE_raw_ECAT_projdata_to_interfile.cxx index fd0fc3102b..1c2fabc697 100644 --- a/src/utilities/conv_GATE_raw_ECAT_projdata_to_interfile.cxx +++ b/src/utilities/conv_GATE_raw_ECAT_projdata_to_interfile.cxx @@ -3,27 +3,27 @@ Copyright (C) 2010- 2013, King's College London Copyright (C) 2013, University College London This file is part of STIR. - + This file is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. - + This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - + See STIR/LICENSE.txt for details */ /*! - \file - \ingroup utilities + \file + \ingroup utilities \brief This program converts GATE ECAT output (.ima) into STIR interfile format - + \author Charalampos Tsoumpas \author Pablo Aguiar - \author Kris Thielemans + \author Kris Thielemans */ #include "stir/ProjDataInterfile.h" @@ -37,94 +37,82 @@ #define NUMARG 8 -int main(int argc,char **argv) -{ +int +main(int argc, char** argv) { using namespace stir; - - static const char * const options[]={ - "argv[1] GATE file\n", - "argv[2] Angles in GATE file\n", - "argv[3] Bins in GATE file\n", - "argv[4] Rings in GATE file\n", - "argv[5] STIR scanner name\n", - "argv[6] maximum ring difference to use for writing\n", - "argv[7] STIR file name\n" - }; - if (argc!=NUMARG){ + + static const char* const options[] = {"argv[1] GATE file\n", "argv[2] Angles in GATE file\n", + "argv[3] Bins in GATE file\n", "argv[4] Rings in GATE file\n", + "argv[5] STIR scanner name\n", "argv[6] maximum ring difference to use for writing\n", + "argv[7] STIR file name\n"}; + if (argc != NUMARG) { std::cerr << "\n\nConvert GATE ECAT format to STIR\n\n"; std::cerr << "Not enough arguments !!! ..\n"; - for (int i=1;i=num_rings) - error("Cannot have max ring difference larger than the number of rings"); - if( sizeof(short int)!=2 ) - error("Expected Input Data should be in UINT16 format\nand size of short int is should be 2\n") ; - shared_ptr scanner_sptr( Scanner::get_scanner_from_name(scanner_name)); + const char* const STIR_output_filename = argv[7]; + const long num_bins_per_sino = num_views * num_tangential_poss; + + if (max_ring_difference >= num_rings) + error("Cannot have max ring difference larger than the number of rings"); + if (sizeof(short int) != 2) + error("Expected Input Data should be in UINT16 format\nand size of short int is should be 2\n"); + shared_ptr scanner_sptr(Scanner::get_scanner_from_name(scanner_name)); if (is_null_ptr(scanner_sptr)) error("Scanner '%s' is not a valid name", scanner_name); - - FILE *GATE_file ; - if( (GATE_file=fopen(GATE_filename,"rb"))==NULL) + + FILE* GATE_file; + if ((GATE_file = fopen(GATE_filename, "rb")) == NULL) error("Cannot open GATE file %s", GATE_filename); else { - long GATE_file_size=fseek(GATE_file, 0, SEEK_END); - GATE_file_size=ftell(GATE_file); + long GATE_file_size = fseek(GATE_file, 0, SEEK_END); + GATE_file_size = ftell(GATE_file); rewind(GATE_file); - std::cerr << GATE_file_size << " size of file" << std::endl; - std::cerr << num_bins_per_sino << " bins per sino" << std::endl; - - if( GATE_file_size%num_bins_per_sino!=0 ) - error("Expected Input Data should be multiple of the number of bins per sinogram. Check input for bins and angles or GATE file.\n") ; + std::cerr << GATE_file_size << " size of file" << std::endl; + std::cerr << num_bins_per_sino << " bins per sino" << std::endl; + + if (GATE_file_size % num_bins_per_sino != 0) + error("Expected Input Data should be multiple of the number of bins per sinogram. Check input for bins and angles or GATE " + "file.\n"); } - const float STIR_scanner_length = - scanner_sptr->get_num_rings() * scanner_sptr->get_ring_spacing(); + const float STIR_scanner_length = scanner_sptr->get_num_rings() * scanner_sptr->get_ring_spacing(); scanner_sptr->set_num_rings(num_rings); - scanner_sptr->set_ring_spacing(STIR_scanner_length/num_rings); - scanner_sptr->set_num_detectors_per_ring(num_views*2); - shared_ptr proj_data_info_sptr( - ProjDataInfo::ProjDataInfoCTI( scanner_sptr, - /*span=*/1, - /*max_delta=*/max_ring_difference, - num_views, - num_tangential_poss, - /*arc_corrected =*/ false)); + scanner_sptr->set_ring_spacing(STIR_scanner_length / num_rings); + scanner_sptr->set_num_detectors_per_ring(num_views * 2); + shared_ptr proj_data_info_sptr(ProjDataInfo::ProjDataInfoCTI(scanner_sptr, + /*span=*/1, + /*max_delta=*/max_ring_difference, num_views, + num_tangential_poss, + /*arc_corrected =*/false)); shared_ptr exam_info_sptr(new ExamInfo); - ProjDataInterfile proj_data(exam_info_sptr, proj_data_info_sptr, - STIR_output_filename, std::ios::out); - + ProjDataInterfile proj_data(exam_info_sptr, proj_data_info_sptr, STIR_output_filename, std::ios::out); + // loop over segments in the GATE ECAT output in a fancy way - for (int seg_num=0; seg_num<=max_ring_difference; seg_num = (seg_num<=0) ? 1+seg_num*-1 : -1*seg_num) - { - // correct sign - const int segment_num = -seg_num; - for (int axial_pos_num = proj_data.get_min_axial_pos_num(segment_num); - axial_pos_num <= proj_data.get_max_axial_pos_num(segment_num); - axial_pos_num++) - { - Sinogram sino = proj_data.get_empty_sinogram(axial_pos_num,segment_num); - float scale=1; - if (read_data(GATE_file, sino, NumericType::SHORT, scale) != - Succeeded::yes) - { - warning("error reading from GATE sino"); - fclose(GATE_file); - return EXIT_FAILURE; - } - proj_data.set_sinogram(sino); - } + for (int seg_num = 0; seg_num <= max_ring_difference; seg_num = (seg_num <= 0) ? 1 + seg_num * -1 : -1 * seg_num) { + // correct sign + const int segment_num = -seg_num; + for (int axial_pos_num = proj_data.get_min_axial_pos_num(segment_num); + axial_pos_num <= proj_data.get_max_axial_pos_num(segment_num); axial_pos_num++) { + Sinogram sino = proj_data.get_empty_sinogram(axial_pos_num, segment_num); + float scale = 1; + if (read_data(GATE_file, sino, NumericType::SHORT, scale) != Succeeded::yes) { + warning("error reading from GATE sino"); + fclose(GATE_file); + return EXIT_FAILURE; + } + proj_data.set_sinogram(sino); } + } fclose(GATE_file); - + return EXIT_SUCCESS; } diff --git a/src/utilities/conv_gipl_to_interfile.cxx b/src/utilities/conv_gipl_to_interfile.cxx index a272810779..7feb1c93cb 100644 --- a/src/utilities/conv_gipl_to_interfile.cxx +++ b/src/utilities/conv_gipl_to_interfile.cxx @@ -2,12 +2,12 @@ Copyright (C) 2009 - 2013, King's College London Copyright (C) 2013, University College London This file is part of STIR. - + This file is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. - + This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -16,9 +16,9 @@ See STIR/LICENSE.txt for details */ /*! - \file + \file \ingroup utilities - + \brief This program converts Images from GIPL (Guy's Imaging Processing Lab) format to Interfile Format. \author Charalampos Tsoumpas */ @@ -45,130 +45,129 @@ using std::cerr; using std::endl; #endif - USING_NAMESPACE_STIR // ------------------------------------------------------------------------- // Main function // ------------------------------------------------------------------------- -int main(int argc, char* argv[]) -{ - if(argc>3 || argc<2) { - std::cerr<<"Usage: " << argv[0] << " \n" - << "\nConversion of an image from gipl format to interfile.\n" - << "Orientation flag can be: 1, 2 or 3\n" - << " For Transverse set to: 1 \n" - << " For Coronal set to: 2 \n" - << " For Sagittal set to: 3 \n" - << "\t Orientation defaults to Transverse \n" - << "output file will change only extension\n"; +int +main(int argc, char* argv[]) { + if (argc > 3 || argc < 2) { + std::cerr << "Usage: " << argv[0] << " \n" + << "\nConversion of an image from gipl format to interfile.\n" + << "Orientation flag can be: 1, 2 or 3\n" + << " For Transverse set to: 1 \n" + << " For Coronal set to: 2 \n" + << " For Sagittal set to: 3 \n" + << "\t Orientation defaults to Transverse \n" + << "output file will change only extension\n"; exit(EXIT_FAILURE); - } + } - Image * image= new Image; + Image* image = new Image; image->GiplRead(argv[1]); string filename(argv[1]); string output_filename; string::iterator string_iter; - for(string_iter=filename.begin(); - string_iter!=filename.end() && *string_iter!='.' ; - ++string_iter) - output_filename.push_back(*string_iter); - const int orientation = (argc==2) ? 1 : atoi(argv[2]) ; - - if (orientation==1) { - BasicCoordinate<3,int> min_range; BasicCoordinate<3,int> max_range; - min_range[1]=0; max_range[1]=image->m_dim[2]-1; - min_range[2]=-static_cast(floor(image->m_dim[1]/2.F)); max_range[2]=min_range[2] + image->m_dim[1]-1; - min_range[3]=-static_cast(floor(image->m_dim[0]/2.F)); max_range[3]=min_range[3] + image->m_dim[0]-1; - IndexRange<3> data_range(min_range,max_range); - Array<3,float> v_array(data_range); - - for (int k_out=min_range[1]; k_out<=max_range[1]; ++k_out) - for (int j_out=min_range[2]; j_out<=max_range[2]; ++j_out) - for (int i_out=min_range[3]; i_out<=max_range[3]; ++i_out) - { - int index = i_out-min_range[3] + image->ImageOffset[0]*(j_out-min_range[2]) + image->ImageOffset[1]*(k_out-min_range[1]); - if (image->m_image_type==15) - v_array[k_out][j_out][i_out]=(float)image->vData[index]; - else if (image->m_image_type==64) - v_array[k_out][j_out][i_out]=image->vData_f[index]; - } - const CartesianCoordinate3D - grid_spacing(image->m_pixdim[2],image->m_pixdim[1],image->m_pixdim[0]); - CartesianCoordinate3D origin(0.F,0.F,0.F); - // TODO not sure if this is correct for even/odd-sized data. - origin[1]=static_cast(image->m_origin[2]+image->m_pixdim[2]*image->m_dim[2]/2.F); - origin[2]=static_cast(image->m_origin[1]+image->m_pixdim[1]*image->m_dim[1]/2.F); - origin[3]=static_cast(image->m_origin[0]+image->m_pixdim[0]*image->m_dim[0]/2.F); - const VoxelsOnCartesianGrid new_image(v_array,origin,grid_spacing); - OutputFileFormat >::default_sptr()-> - write_to_file(output_filename, new_image); - } - else if (orientation==2) { - BasicCoordinate<3,int> min_range; BasicCoordinate<3,int> max_range; - min_range[1]=0; max_range[1]=image->m_dim[1]-1; - min_range[2]=-static_cast(floor(image->m_dim[2]/2.F)); max_range[2]=min_range[2] + image->m_dim[2]-1; - min_range[3]=-static_cast(floor(image->m_dim[0]/2.F)); max_range[3]=min_range[3] + image->m_dim[0]-1; - - IndexRange<3> data_range(min_range,max_range); - Array<3,float> v_array(data_range); - - for (int k_out=min_range[1]; k_out<=max_range[1]; ++k_out) - for (int j_out=min_range[2]; j_out<=max_range[2]; ++j_out) - for (int i_out=min_range[3]; i_out<=max_range[3]; ++i_out) - { - int index = i_out-min_range[3] + image->ImageOffset[0]*(k_out-min_range[1]) + image->ImageOffset[1]*(j_out-min_range[2]); - if (image->m_image_type==15) - v_array[k_out][j_out][i_out]=(float)image->vData[index]; - else if (image->m_image_type==64) - v_array[k_out][j_out][i_out]=image->vData_f[index]; - } - const CartesianCoordinate3D - grid_spacing(image->m_pixdim[1],image->m_pixdim[2],image->m_pixdim[0]); - CartesianCoordinate3D origin(0.F,0.F,0.F); - origin[2]=static_cast(image->m_origin[2]+image->m_pixdim[2]*image->m_dim[2]/2.F); - origin[1]=static_cast(image->m_origin[1]+image->m_pixdim[1]*image->m_dim[1]/2.F); - origin[3]=static_cast(image->m_origin[0]+image->m_pixdim[0]*image->m_dim[0]/2.F); - const VoxelsOnCartesianGrid new_image(v_array,origin,grid_spacing); - OutputFileFormat >::default_sptr()-> - write_to_file(output_filename, new_image); - } - else if (orientation==3) { - BasicCoordinate<3,int> min_range; BasicCoordinate<3,int> max_range; - min_range[1]=0; max_range[1]=image->m_dim[1]-1; - min_range[2]=-static_cast(floor(image->m_dim[0]/2.F)); max_range[2]=min_range[2] + image->m_dim[0]-1; - min_range[3]=-static_cast(floor(image->m_dim[2]/2.F)); max_range[3]=max_range[3] + image->m_dim[2]-1; - - IndexRange<3> data_range(min_range,max_range); - Array<3,float> v_array(data_range); - - for (int k_out=min_range[1]; k_out<=max_range[1]; ++k_out) - for (int j_out=min_range[2]; j_out<=max_range[2]; ++j_out) - for (int i_out=min_range[3]; i_out<=max_range[3]; ++i_out) - { - int index = j_out-min_range[2] + image->ImageOffset[0]*(k_out-min_range[1]) + image->ImageOffset[1]*(max_range[3]-i_out); - if (image->m_image_type==15) - v_array[k_out][j_out][i_out]=(float)image->vData[index]; - else if (image->m_image_type==64) - v_array[k_out][j_out][i_out]=image->vData_f[index]; - } - const CartesianCoordinate3D - grid_spacing(image->m_pixdim[1],image->m_pixdim[0],image->m_pixdim[2]); - CartesianCoordinate3D origin(0.F,0.F,0.F); - origin[3]=static_cast(image->m_origin[2]+image->m_pixdim[2]*image->m_dim[2]/2.F); - origin[1]=static_cast(image->m_origin[1]+image->m_pixdim[1]*image->m_dim[1]/2.F); - origin[2]=static_cast(image->m_origin[0]+image->m_pixdim[0]*image->m_dim[0]/2.F); - - const VoxelsOnCartesianGrid new_image(v_array,origin,grid_spacing); - OutputFileFormat >::default_sptr()-> - write_to_file(output_filename, new_image); + for (string_iter = filename.begin(); string_iter != filename.end() && *string_iter != '.'; ++string_iter) + output_filename.push_back(*string_iter); + const int orientation = (argc == 2) ? 1 : atoi(argv[2]); + + if (orientation == 1) { + BasicCoordinate<3, int> min_range; + BasicCoordinate<3, int> max_range; + min_range[1] = 0; + max_range[1] = image->m_dim[2] - 1; + min_range[2] = -static_cast(floor(image->m_dim[1] / 2.F)); + max_range[2] = min_range[2] + image->m_dim[1] - 1; + min_range[3] = -static_cast(floor(image->m_dim[0] / 2.F)); + max_range[3] = min_range[3] + image->m_dim[0] - 1; + IndexRange<3> data_range(min_range, max_range); + Array<3, float> v_array(data_range); + + for (int k_out = min_range[1]; k_out <= max_range[1]; ++k_out) + for (int j_out = min_range[2]; j_out <= max_range[2]; ++j_out) + for (int i_out = min_range[3]; i_out <= max_range[3]; ++i_out) { + int index = i_out - min_range[3] + image->ImageOffset[0] * (j_out - min_range[2]) + + image->ImageOffset[1] * (k_out - min_range[1]); + if (image->m_image_type == 15) + v_array[k_out][j_out][i_out] = (float)image->vData[index]; + else if (image->m_image_type == 64) + v_array[k_out][j_out][i_out] = image->vData_f[index]; + } + const CartesianCoordinate3D grid_spacing(image->m_pixdim[2], image->m_pixdim[1], image->m_pixdim[0]); + CartesianCoordinate3D origin(0.F, 0.F, 0.F); + // TODO not sure if this is correct for even/odd-sized data. + origin[1] = static_cast(image->m_origin[2] + image->m_pixdim[2] * image->m_dim[2] / 2.F); + origin[2] = static_cast(image->m_origin[1] + image->m_pixdim[1] * image->m_dim[1] / 2.F); + origin[3] = static_cast(image->m_origin[0] + image->m_pixdim[0] * image->m_dim[0] / 2.F); + const VoxelsOnCartesianGrid new_image(v_array, origin, grid_spacing); + OutputFileFormat>::default_sptr()->write_to_file(output_filename, new_image); + } else if (orientation == 2) { + BasicCoordinate<3, int> min_range; + BasicCoordinate<3, int> max_range; + min_range[1] = 0; + max_range[1] = image->m_dim[1] - 1; + min_range[2] = -static_cast(floor(image->m_dim[2] / 2.F)); + max_range[2] = min_range[2] + image->m_dim[2] - 1; + min_range[3] = -static_cast(floor(image->m_dim[0] / 2.F)); + max_range[3] = min_range[3] + image->m_dim[0] - 1; + + IndexRange<3> data_range(min_range, max_range); + Array<3, float> v_array(data_range); + + for (int k_out = min_range[1]; k_out <= max_range[1]; ++k_out) + for (int j_out = min_range[2]; j_out <= max_range[2]; ++j_out) + for (int i_out = min_range[3]; i_out <= max_range[3]; ++i_out) { + int index = i_out - min_range[3] + image->ImageOffset[0] * (k_out - min_range[1]) + + image->ImageOffset[1] * (j_out - min_range[2]); + if (image->m_image_type == 15) + v_array[k_out][j_out][i_out] = (float)image->vData[index]; + else if (image->m_image_type == 64) + v_array[k_out][j_out][i_out] = image->vData_f[index]; + } + const CartesianCoordinate3D grid_spacing(image->m_pixdim[1], image->m_pixdim[2], image->m_pixdim[0]); + CartesianCoordinate3D origin(0.F, 0.F, 0.F); + origin[2] = static_cast(image->m_origin[2] + image->m_pixdim[2] * image->m_dim[2] / 2.F); + origin[1] = static_cast(image->m_origin[1] + image->m_pixdim[1] * image->m_dim[1] / 2.F); + origin[3] = static_cast(image->m_origin[0] + image->m_pixdim[0] * image->m_dim[0] / 2.F); + const VoxelsOnCartesianGrid new_image(v_array, origin, grid_spacing); + OutputFileFormat>::default_sptr()->write_to_file(output_filename, new_image); + } else if (orientation == 3) { + BasicCoordinate<3, int> min_range; + BasicCoordinate<3, int> max_range; + min_range[1] = 0; + max_range[1] = image->m_dim[1] - 1; + min_range[2] = -static_cast(floor(image->m_dim[0] / 2.F)); + max_range[2] = min_range[2] + image->m_dim[0] - 1; + min_range[3] = -static_cast(floor(image->m_dim[2] / 2.F)); + max_range[3] = max_range[3] + image->m_dim[2] - 1; + + IndexRange<3> data_range(min_range, max_range); + Array<3, float> v_array(data_range); + + for (int k_out = min_range[1]; k_out <= max_range[1]; ++k_out) + for (int j_out = min_range[2]; j_out <= max_range[2]; ++j_out) + for (int i_out = min_range[3]; i_out <= max_range[3]; ++i_out) { + int index = j_out - min_range[2] + image->ImageOffset[0] * (k_out - min_range[1]) + + image->ImageOffset[1] * (max_range[3] - i_out); + if (image->m_image_type == 15) + v_array[k_out][j_out][i_out] = (float)image->vData[index]; + else if (image->m_image_type == 64) + v_array[k_out][j_out][i_out] = image->vData_f[index]; + } + const CartesianCoordinate3D grid_spacing(image->m_pixdim[1], image->m_pixdim[0], image->m_pixdim[2]); + CartesianCoordinate3D origin(0.F, 0.F, 0.F); + origin[3] = static_cast(image->m_origin[2] + image->m_pixdim[2] * image->m_dim[2] / 2.F); + origin[1] = static_cast(image->m_origin[1] + image->m_pixdim[1] * image->m_dim[1] / 2.F); + origin[2] = static_cast(image->m_origin[0] + image->m_pixdim[0] * image->m_dim[0] / 2.F); + + const VoxelsOnCartesianGrid new_image(v_array, origin, grid_spacing); + OutputFileFormat>::default_sptr()->write_to_file(output_filename, new_image); + } else { + std::cerr << "Orientation flag is not recognised." << std::endl; + exit(EXIT_FAILURE); } - else - { - std::cerr << "Orientation flag is not recognised." << std::endl; - exit(EXIT_FAILURE); - } return EXIT_SUCCESS; } diff --git a/src/utilities/conv_interfile_to_gipl.cxx b/src/utilities/conv_interfile_to_gipl.cxx index 9df04dbbeb..2eb217b53b 100644 --- a/src/utilities/conv_interfile_to_gipl.cxx +++ b/src/utilities/conv_interfile_to_gipl.cxx @@ -1,12 +1,12 @@ /* Copyright (C) 2009 - 2013, King's College London This file is part of STIR. - + This file is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. - + This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -15,10 +15,10 @@ See STIR/LICENSE.txt for details */ /*! - \file + \file \ingroup utilities - \brief This program converts Images from Interfile Format to GIPL (Guy's Imaging Processing Lab) format. + \brief This program converts Images from Interfile Format to GIPL (Guy's Imaging Processing Lab) format. \author Charalampos Tsoumpas */ @@ -45,114 +45,109 @@ using std::cerr; using std::endl; #endif - USING_NAMESPACE_STIR // ------------------------------------------------------------------------- // Main function // ------------------------------------------------------------------------- -int main(int argc, char* argv[]) -{ - if(argc>3 || argc<2) { - std::cerr<<"Usage: " << argv[0] << " \n" - << "\nConversion of an image from image file format to giplfile.\n" - << "Orientation flag can be: 1, 2 or 3\n" - << " For Transverse set to: 1 \n" - << " For Coronal set to: 2 \n" - << " For Sagittal set to: 3 \n" - << "\t Orientation defaults to Coronal \n" - << "output file will change only extension\n"; +int +main(int argc, char* argv[]) { + if (argc > 3 || argc < 2) { + std::cerr << "Usage: " << argv[0] << " \n" + << "\nConversion of an image from image file format to giplfile.\n" + << "Orientation flag can be: 1, 2 or 3\n" + << " For Transverse set to: 1 \n" + << " For Coronal set to: 2 \n" + << " For Sagittal set to: 3 \n" + << "\t Orientation defaults to Coronal \n" + << "output file will change only extension\n"; exit(EXIT_FAILURE); } string filename(argv[1]); - const shared_ptr > image_sptr(DiscretisedDensity<3,float>::read_from_file(filename)); + const shared_ptr> image_sptr(DiscretisedDensity<3, float>::read_from_file(filename)); string output_filename; string::iterator string_iter; - for(string_iter=filename.begin(); - string_iter!=filename.end() && *string_iter!='.' ; - ++string_iter) - output_filename.push_back(*string_iter); - output_filename+=".gipl"; - const int orientation = (argc==2) ? 2 : atoi(argv[2]) ; + for (string_iter = filename.begin(); string_iter != filename.end() && *string_iter != '.'; ++string_iter) + output_filename.push_back(*string_iter); + output_filename += ".gipl"; + const int orientation = (argc == 2) ? 2 : atoi(argv[2]); - const DiscretisedDensity<3,float>& input_image = *image_sptr; - const DiscretisedDensityOnCartesianGrid <3,float>* image_cartesian_ptr = - dynamic_cast< DiscretisedDensityOnCartesianGrid<3,float>* > (image_sptr.get()); - - const IndexRange<3> data_range=image_sptr->get_index_range(); - BasicCoordinate<3,int> min_range; BasicCoordinate<3,int> max_range; - data_range.get_regular_range(min_range,max_range); - - const int num_voxels=(max_range[3]-min_range[3]+1)*(max_range[2]-min_range[2]+1)*(max_range[1]-min_range[1]+1); - Image image(num_voxels,64); - const Coordinate3D origin=input_image.get_origin(); - const Coordinate3D grid_spacing=image_cartesian_ptr->get_grid_spacing(); - if (orientation==1){ - image.m_dim[2]=max_range[1]-min_range[1]+1; - image.m_dim[1]=max_range[2]-min_range[2]+1; - image.m_dim[0]=max_range[3]-min_range[3]+1; - image.m_pixdim[2]=grid_spacing[1]; - image.m_pixdim[1]=grid_spacing[2]; - image.m_pixdim[0]=grid_spacing[3]; - image.m_origin[2]=origin[1]-image.m_pixdim[2]*image.m_dim[2]/2.F; - image.m_origin[1]=origin[2]-image.m_pixdim[1]*image.m_dim[1]/2.F; - image.m_origin[0]=origin[3]-image.m_pixdim[0]*image.m_dim[0]/2.F; - image.ImageOffset[1]=image.m_dim[0]*image.m_dim[1]; - image.ImageOffset[0]=image.m_dim[0]; - for (int k_out=min_range[1]; k_out<=max_range[1]; k_out++) - for (int j_out=min_range[2]; j_out<=max_range[2]; j_out++) - for (int i_out=min_range[3]; i_out<=max_range[3]; i_out++) - { - int index = i_out-min_range[3] + image.ImageOffset[0]*(j_out-min_range[2]) + image.ImageOffset[1]*(k_out-min_range[1]); - image.vData_f[index]=input_image[k_out][j_out][i_out]; - } - } - else if (orientation==2) { - image.m_dim[2]=max_range[2]-min_range[2]+1; - image.m_dim[1]=max_range[1]-min_range[1]+1; - image.m_dim[0]=max_range[3]-min_range[3]+1; - image.m_pixdim[2]=grid_spacing[2]; - image.m_pixdim[1]=grid_spacing[1]; - image.m_pixdim[0]=grid_spacing[3]; - image.m_origin[2]=origin[2]-image.m_pixdim[2]*image.m_dim[2]/2.F; - image.m_origin[1]=origin[1]-image.m_pixdim[1]*image.m_dim[1]/2.F; - image.m_origin[0]=origin[3]-image.m_pixdim[0]*image.m_dim[0]/2.F; - image.ImageOffset[1]=image.m_dim[0]*image.m_dim[1]; - image.ImageOffset[0]=image.m_dim[0]; - for (int k_out=min_range[1]; k_out<=max_range[1]; k_out++) - for (int j_out=min_range[2]; j_out<=max_range[2]; j_out++) - for (int i_out=min_range[3]; i_out<=max_range[3]; i_out++) - { - int index = i_out-min_range[3] + image.ImageOffset[0]*(k_out-min_range[1]) + image.ImageOffset[1]*(j_out-min_range[2]); - image.vData_f[index]=input_image[k_out][j_out][i_out]; - } - } - else if (orientation==3) { - image.m_dim[0]=max_range[2]-min_range[2]+1; - image.m_dim[1]=max_range[1]-min_range[1]+1; - image.m_dim[2]=max_range[3]-min_range[3]+1; - image.m_pixdim[0]=grid_spacing[2]; - image.m_pixdim[1]=grid_spacing[1]; - image.m_pixdim[2]=grid_spacing[3]; - image.m_origin[2]=origin[3]-image.m_pixdim[2]*image.m_dim[2]/2.F; - image.m_origin[1]=origin[1]-image.m_pixdim[1]*image.m_dim[1]/2.F; - image.m_origin[0]=origin[2]-image.m_pixdim[0]*image.m_dim[0]/2.F; - image.ImageOffset[1]=image.m_dim[0]*image.m_dim[1]; - image.ImageOffset[0]=image.m_dim[0]; - for (int k_out=min_range[1]; k_out<=max_range[1]; k_out++) - for (int j_out=min_range[2]; j_out<=max_range[2]; j_out++) - for (int i_out=min_range[3]; i_out<=max_range[3]; i_out++) - { - int index = j_out-min_range[2] + image.ImageOffset[0]*(k_out-min_range[1]) + image.ImageOffset[1]*(max_range[3]-i_out); - image.vData_f[index]=input_image[k_out][j_out][i_out]; - } + const DiscretisedDensity<3, float>& input_image = *image_sptr; + const DiscretisedDensityOnCartesianGrid<3, float>* image_cartesian_ptr = + dynamic_cast*>(image_sptr.get()); + + const IndexRange<3> data_range = image_sptr->get_index_range(); + BasicCoordinate<3, int> min_range; + BasicCoordinate<3, int> max_range; + data_range.get_regular_range(min_range, max_range); + + const int num_voxels = + (max_range[3] - min_range[3] + 1) * (max_range[2] - min_range[2] + 1) * (max_range[1] - min_range[1] + 1); + Image image(num_voxels, 64); + const Coordinate3D origin = input_image.get_origin(); + const Coordinate3D grid_spacing = image_cartesian_ptr->get_grid_spacing(); + if (orientation == 1) { + image.m_dim[2] = max_range[1] - min_range[1] + 1; + image.m_dim[1] = max_range[2] - min_range[2] + 1; + image.m_dim[0] = max_range[3] - min_range[3] + 1; + image.m_pixdim[2] = grid_spacing[1]; + image.m_pixdim[1] = grid_spacing[2]; + image.m_pixdim[0] = grid_spacing[3]; + image.m_origin[2] = origin[1] - image.m_pixdim[2] * image.m_dim[2] / 2.F; + image.m_origin[1] = origin[2] - image.m_pixdim[1] * image.m_dim[1] / 2.F; + image.m_origin[0] = origin[3] - image.m_pixdim[0] * image.m_dim[0] / 2.F; + image.ImageOffset[1] = image.m_dim[0] * image.m_dim[1]; + image.ImageOffset[0] = image.m_dim[0]; + for (int k_out = min_range[1]; k_out <= max_range[1]; k_out++) + for (int j_out = min_range[2]; j_out <= max_range[2]; j_out++) + for (int i_out = min_range[3]; i_out <= max_range[3]; i_out++) { + int index = i_out - min_range[3] + image.ImageOffset[0] * (j_out - min_range[2]) + + image.ImageOffset[1] * (k_out - min_range[1]); + image.vData_f[index] = input_image[k_out][j_out][i_out]; + } + } else if (orientation == 2) { + image.m_dim[2] = max_range[2] - min_range[2] + 1; + image.m_dim[1] = max_range[1] - min_range[1] + 1; + image.m_dim[0] = max_range[3] - min_range[3] + 1; + image.m_pixdim[2] = grid_spacing[2]; + image.m_pixdim[1] = grid_spacing[1]; + image.m_pixdim[0] = grid_spacing[3]; + image.m_origin[2] = origin[2] - image.m_pixdim[2] * image.m_dim[2] / 2.F; + image.m_origin[1] = origin[1] - image.m_pixdim[1] * image.m_dim[1] / 2.F; + image.m_origin[0] = origin[3] - image.m_pixdim[0] * image.m_dim[0] / 2.F; + image.ImageOffset[1] = image.m_dim[0] * image.m_dim[1]; + image.ImageOffset[0] = image.m_dim[0]; + for (int k_out = min_range[1]; k_out <= max_range[1]; k_out++) + for (int j_out = min_range[2]; j_out <= max_range[2]; j_out++) + for (int i_out = min_range[3]; i_out <= max_range[3]; i_out++) { + int index = i_out - min_range[3] + image.ImageOffset[0] * (k_out - min_range[1]) + + image.ImageOffset[1] * (j_out - min_range[2]); + image.vData_f[index] = input_image[k_out][j_out][i_out]; + } + } else if (orientation == 3) { + image.m_dim[0] = max_range[2] - min_range[2] + 1; + image.m_dim[1] = max_range[1] - min_range[1] + 1; + image.m_dim[2] = max_range[3] - min_range[3] + 1; + image.m_pixdim[0] = grid_spacing[2]; + image.m_pixdim[1] = grid_spacing[1]; + image.m_pixdim[2] = grid_spacing[3]; + image.m_origin[2] = origin[3] - image.m_pixdim[2] * image.m_dim[2] / 2.F; + image.m_origin[1] = origin[1] - image.m_pixdim[1] * image.m_dim[1] / 2.F; + image.m_origin[0] = origin[2] - image.m_pixdim[0] * image.m_dim[0] / 2.F; + image.ImageOffset[1] = image.m_dim[0] * image.m_dim[1]; + image.ImageOffset[0] = image.m_dim[0]; + for (int k_out = min_range[1]; k_out <= max_range[1]; k_out++) + for (int j_out = min_range[2]; j_out <= max_range[2]; j_out++) + for (int i_out = min_range[3]; i_out <= max_range[3]; i_out++) { + int index = j_out - min_range[2] + image.ImageOffset[0] * (k_out - min_range[1]) + + image.ImageOffset[1] * (max_range[3] - i_out); + image.vData_f[index] = input_image[k_out][j_out][i_out]; + } + } else { + std::cerr << "Orientation flag is not recognised." << std::endl; + exit(EXIT_FAILURE); } - else - { - std::cerr << "Orientation flag is not recognised." << std::endl; - exit(EXIT_FAILURE); - } std::cerr << "Now writing image in gipl format" << std::endl; image.GiplWrite(output_filename.c_str()); diff --git a/src/utilities/convert_to_binary_image.cxx b/src/utilities/convert_to_binary_image.cxx index 7bb7981876..185ea309e6 100644 --- a/src/utilities/convert_to_binary_image.cxx +++ b/src/utilities/convert_to_binary_image.cxx @@ -17,10 +17,10 @@ See STIR/LICENSE.txt for details */ /*! - \file + \file \ingroup utilities - - \brief This program outputs a binary image (voxel values 0 or 1) where all values + + \brief This program outputs a binary image (voxel values 0 or 1) where all values strictly below a threshold are set to 0, and others to 1. \author Kris Thielemans @@ -33,40 +33,33 @@ USING_NAMESPACE_STIR -int main(int argc, char **argv) -{ - if(argc!=4) { - std::cerr<<"Usage: " << argv[0] << " threshold\n"; +int +main(int argc, char** argv) { + if (argc != 4) { + std::cerr << "Usage: " << argv[0] << " threshold\n"; exit(EXIT_FAILURE); } - + // get parameters from command line std::string output_filename = argv[1]; - char const * const input_filename = argv[2]; + char const* const input_filename = argv[2]; const float threshold = atof(argv[3]); - // read image + // read image - shared_ptr > - density_ptr(read_from_file >(input_filename)); + shared_ptr> density_ptr(read_from_file>(input_filename)); // binarise - for (DiscretisedDensity<3,float>::full_iterator iter = density_ptr->begin_all(); - iter != density_ptr->end_all(); - ++iter) - { - if (*iter < threshold) - *iter = 0.F; - else - *iter = 1.F; - } - shared_ptr > > output_file_format_sptr = - OutputFileFormat >::default_sptr(); + for (DiscretisedDensity<3, float>::full_iterator iter = density_ptr->begin_all(); iter != density_ptr->end_all(); ++iter) { + if (*iter < threshold) + *iter = 0.F; + else + *iter = 1.F; + } + shared_ptr>> output_file_format_sptr = + OutputFileFormat>::default_sptr(); output_file_format_sptr->set_type_of_numbers(NumericType::SCHAR); output_file_format_sptr->set_scale_to_write_data(1); - return - output_file_format_sptr->write_to_file(output_filename, *density_ptr) == Succeeded::yes ? - EXIT_SUCCESS : EXIT_FAILURE; - + return output_file_format_sptr->write_to_file(output_filename, *density_ptr) == Succeeded::yes ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/src/utilities/correct_projdata.cxx b/src/utilities/correct_projdata.cxx index b7422f8c7a..812014fb5d 100644 --- a/src/utilities/correct_projdata.cxx +++ b/src/utilities/correct_projdata.cxx @@ -28,7 +28,7 @@ Here's a sample .par file \verbatim -correct_projdata Parameters := +correct_projdata Parameters := input file := trues.hs ; Current way of specifying time frames, pending modifications to @@ -41,24 +41,24 @@ correct_projdata Parameters := ; the number should be between 1 and num_frames and defaults to 1 ; this is currently only used to pass the relevant time to the normalisation ; time frame number := 1 - + ; output file - ; for future compatibility, do not use an extension in the name of + ; for future compatibility, do not use an extension in the name of ; the output file. It will be added automatically output filename := precorrected ; default value for next is -1, meaning 'all segments' - ; maximum absolute segment number to process := - + ; maximum absolute segment number to process := + ; use data in the input file, or substitute data with all 1's ; (useful to get correction factors only) ; default is '1' - ; use data (1) or set to one (0) := + ; use data (1) or set to one (0) := ; precorrect data, or undo precorrection ; default is '1' - ; apply (1) or undo (0) correction := + ; apply (1) or undo (0) correction := ; parameters specifying correction factors ; if no value is given, the corresponding correction will not be performed @@ -74,7 +74,7 @@ correct_projdata Parameters := ; attenuation image, will be forward projected to get attenuation factors ; OBSOLETE ;attenuation image filename := attenuation_image.hv - + ; forward projector used to estimate attenuation factors, defaults to Ray Tracing ; OBSOLETE ;forward_projector type := Ray Tracing @@ -85,7 +85,7 @@ correct_projdata Parameters := ; to interpolate to uniform sampling in 's', set value to 1 ; arc correction := 1 -END:= +END:= \endverbatim Time frame definition is only necessary when the normalisation type uses @@ -93,11 +93,12 @@ this time info for dead-time correction. \warning arc-correction can currently not be undone. -The following gives a brief explanation of the non-obvious parameters. +The following gives a brief explanation of the non-obvious parameters.
    • use data (1) or set to one (0):
      -Use the data in the input file, or substitute data with all 1's. This is useful to get correction factors only. Its value defaults to 1. +Use the data in the input file, or substitute data with all 1's. This is useful to get correction factors only. Its value defaults +to 1.
    • apply (1) or undo (0) correction:
      @@ -105,21 +106,21 @@ Precorrect data, or undo precorrection. Its value defaults to 1.
    • Bin Normalisation type:
      -Normalisation (or binwise multiplication, so can contain attenuation factors +Normalisation (or binwise multiplication, so can contain attenuation factors as well). \see stir::BinNormalisation
    • attenuation image filename: obsolete
      -Specify the attenuation image, which will be forward projected to get +Specify the attenuation image, which will be forward projected to get attenuation factors. Has to be in units cm^-1. -This parameter will be removed. Use instead a "chained" bin normalisation -with a bin normalisation "from attenuation image" +This parameter will be removed. Use instead a "chained" bin normalisation +with a bin normalisation "from attenuation image" \see stir::ChainedBinNormalisation \see stir::BinNormalisationFromAttenuationImage
    • forward_projector type: obsolete
      -Forward projector used to estimate attenuation factors, defaults to +Forward projector used to estimate attenuation factors, defaults to Ray Tracing. \see stir::ForwardProjectorUsingRayTracing This parameter will be removed.
    • @@ -144,14 +145,14 @@ This parameter will be removed. #include "stir/ArrayFunction.h" #include "stir/TimeFrameDefinitions.h" #ifndef USE_PMRT -#include "stir/recon_buildblock/ForwardProjectorByBinUsingRayTracing.h" +# include "stir/recon_buildblock/ForwardProjectorByBinUsingRayTracing.h" #else -#include "stir/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.h" -#include "stir/recon_buildblock/ProjMatrixByBinUsingRayTracing.h" +# include "stir/recon_buildblock/ForwardProjectorByBinUsingProjMatrixByBin.h" +# include "stir/recon_buildblock/ProjMatrixByBinUsingRayTracing.h" #endif #include "stir/is_null_ptr.h" #include -#include +#include #include #include @@ -167,8 +168,6 @@ using std::vector; START_NAMESPACE_STIR - - // TODO most of this is identical to ReconstructionParameters, so make a common class /*! \ingroup utilities \brief class to do precorrections @@ -176,38 +175,36 @@ START_NAMESPACE_STIR \todo Preliminary class interface. At some point, this class should move to the library, instead of being in correct_projdata.cxx. */ -class CorrectProjDataApplication : public ParsingObject -{ +class CorrectProjDataApplication : public ParsingObject { public: - - CorrectProjDataApplication(const char * const par_filename); + CorrectProjDataApplication(const char* const par_filename); //! set-up variables before processing Succeeded set_up(); //! do precorrection - /*! set_up() has to be run first */ + /*! set_up() has to be run first */ Succeeded run() const; - + // shared_ptrs such that they clean up automatically at exit shared_ptr input_projdata_ptr; shared_ptr scatter_projdata_ptr; shared_ptr randoms_projdata_ptr; shared_ptr output_projdata_ptr; shared_ptr normalisation_ptr; - shared_ptr > attenuation_image_ptr; + shared_ptr> attenuation_image_ptr; shared_ptr forward_projector_ptr; //! apply_or_undo_correction==true means: apply it bool apply_or_undo_correction; //! use input data, or replace it with all 1's /*! use_data_or_set_to_1 == true means: use the data*/ - bool use_data_or_set_to_1; + bool use_data_or_set_to_1; int max_segment_num_to_process; int frame_num; TimeFrameDefinitions frame_defs; bool do_arc_correction; -private: +private: virtual void set_defaults(); virtual void initialise_keymap(); virtual bool post_processing(); @@ -215,80 +212,60 @@ class CorrectProjDataApplication : public ParsingObject string output_filename; string scatter_projdata_filename; string atten_image_filename; - string norm_filename; - string randoms_projdata_filename; + string norm_filename; + string randoms_projdata_filename; string frame_definition_filename; - - shared_ptr arc_correction_sptr; + shared_ptr arc_correction_sptr; }; - Succeeded -CorrectProjDataApplication:: -run() const -{ +CorrectProjDataApplication::run() const { ProjData& output_projdata = *output_projdata_ptr; const ProjData& input_projdata = *input_projdata_ptr; const bool do_scatter = !is_null_ptr(scatter_projdata_ptr); const bool do_randoms = !is_null_ptr(randoms_projdata_ptr); - // TODO - shared_ptr - symmetries_ptr( - is_null_ptr(forward_projector_ptr) ? - new TrivialDataSymmetriesForViewSegmentNumbers - : - forward_projector_ptr->get_symmetries_used()->clone() - ); - for (int timing_pos_num = output_projdata.get_min_tof_pos_num(); timing_pos_num <= output_projdata.get_max_tof_pos_num(); timing_pos_num++) - for (int segment_num = output_projdata.get_min_segment_num(); segment_num <= output_projdata.get_max_segment_num() ; segment_num++) - { - cerr<is_basic(view_seg_nums)) - continue; - - // ** first fill in the data ** - RelatedViewgrams - viewgrams = input_projdata.get_empty_related_viewgrams(view_seg_nums,symmetries_ptr, - false,timing_pos_num); - if (use_data_or_set_to_1) - { - viewgrams += - input_projdata.get_related_viewgrams(view_seg_nums, - symmetries_ptr,false,timing_pos_num); - } - else - { - viewgrams.fill(1.F); - } - - if (do_arc_correction && !apply_or_undo_correction) - { - error("Cannot undo arc-correction yet. Sorry."); - // TODO - //arc_correction_sptr->undo_arc_correction(output_viewgrams, viewgrams); - } + shared_ptr symmetries_ptr(is_null_ptr(forward_projector_ptr) + ? new TrivialDataSymmetriesForViewSegmentNumbers + : forward_projector_ptr->get_symmetries_used()->clone()); + for (int timing_pos_num = output_projdata.get_min_tof_pos_num(); timing_pos_num <= output_projdata.get_max_tof_pos_num(); + timing_pos_num++) + for (int segment_num = output_projdata.get_min_segment_num(); segment_num <= output_projdata.get_max_segment_num(); + segment_num++) { + cerr << endl + << "Processing segment # " << segment_num << "(and any related segments) of timing position index # " << timing_pos_num + << endl; + for (int view_num = input_projdata.get_min_view_num(); view_num <= input_projdata.get_max_view_num(); ++view_num) { + const ViewSegmentNumbers view_seg_nums(view_num, segment_num); + if (!symmetries_ptr->is_basic(view_seg_nums)) + continue; + + // ** first fill in the data ** + RelatedViewgrams viewgrams = + input_projdata.get_empty_related_viewgrams(view_seg_nums, symmetries_ptr, false, timing_pos_num); + if (use_data_or_set_to_1) { + viewgrams += input_projdata.get_related_viewgrams(view_seg_nums, symmetries_ptr, false, timing_pos_num); + } else { + viewgrams.fill(1.F); + } - if (do_scatter && !apply_or_undo_correction) - { - viewgrams += - scatter_projdata_ptr->get_related_viewgrams(view_seg_nums, - symmetries_ptr,false,timing_pos_num); - } + if (do_arc_correction && !apply_or_undo_correction) { + error("Cannot undo arc-correction yet. Sorry."); + // TODO + // arc_correction_sptr->undo_arc_correction(output_viewgrams, viewgrams); + } - if (do_randoms && apply_or_undo_correction) - { - viewgrams -= - randoms_projdata_ptr->get_related_viewgrams(view_seg_nums, - symmetries_ptr,false,timing_pos_num); - } - #if 0 + if (do_scatter && !apply_or_undo_correction) { + viewgrams += scatter_projdata_ptr->get_related_viewgrams(view_seg_nums, symmetries_ptr, false, timing_pos_num); + } + + if (do_randoms && apply_or_undo_correction) { + viewgrams -= randoms_projdata_ptr->get_related_viewgrams(view_seg_nums, symmetries_ptr, false, timing_pos_num); + } +#if 0 if (frame_num==-1) { int num_frames = frame_def.get_num_frames(); @@ -314,73 +291,56 @@ run() const else - #endif - { - const double start_frame = frame_defs.get_start_time(frame_num); - const double end_frame = frame_defs.get_end_time(frame_num); - if (apply_or_undo_correction) - { - normalisation_ptr->apply(viewgrams,start_frame,end_frame); - } - else - { - normalisation_ptr->undo(viewgrams,start_frame,end_frame); - } - } - if (do_scatter && apply_or_undo_correction) - { - viewgrams -= - scatter_projdata_ptr->get_related_viewgrams(view_seg_nums, - symmetries_ptr,false,timing_pos_num); - } +#endif + { + const double start_frame = frame_defs.get_start_time(frame_num); + const double end_frame = frame_defs.get_end_time(frame_num); + if (apply_or_undo_correction) { + normalisation_ptr->apply(viewgrams, start_frame, end_frame); + } else { + normalisation_ptr->undo(viewgrams, start_frame, end_frame); + } + } + if (do_scatter && apply_or_undo_correction) { + viewgrams -= scatter_projdata_ptr->get_related_viewgrams(view_seg_nums, symmetries_ptr, false, timing_pos_num); + } - if (do_randoms && !apply_or_undo_correction) - { - viewgrams += - randoms_projdata_ptr->get_related_viewgrams(view_seg_nums, - symmetries_ptr,false,timing_pos_num); - } + if (do_randoms && !apply_or_undo_correction) { + viewgrams += randoms_projdata_ptr->get_related_viewgrams(view_seg_nums, symmetries_ptr, false, timing_pos_num); + } - if (do_arc_correction && apply_or_undo_correction) - { - viewgrams = arc_correction_sptr->do_arc_correction(viewgrams); - } + if (do_arc_correction && apply_or_undo_correction) { + viewgrams = arc_correction_sptr->do_arc_correction(viewgrams); + } - // output - { - // Unfortunately, segment range in output_projdata and input_projdata can be - // different. - // Hence, output_projdata.set_related_viewgrams(viewgrams) would not work. - // So, we need an extra viewgrams object to take this into account. - // The trick relies on calling Array::operator+= instead of - // RelatedViewgrams::operator= - RelatedViewgrams - output_viewgrams = - output_projdata.get_empty_related_viewgrams(view_seg_nums, - symmetries_ptr,false,timing_pos_num); - output_viewgrams += viewgrams; - - if (!(output_projdata.set_related_viewgrams(viewgrams) == Succeeded::yes)) - { - warning("CorrectProjData: Error set_related_viewgrams\n"); - return Succeeded::no; - } - } - } - - } + // output + { + // Unfortunately, segment range in output_projdata and input_projdata can be + // different. + // Hence, output_projdata.set_related_viewgrams(viewgrams) would not work. + // So, we need an extra viewgrams object to take this into account. + // The trick relies on calling Array::operator+= instead of + // RelatedViewgrams::operator= + RelatedViewgrams output_viewgrams = + output_projdata.get_empty_related_viewgrams(view_seg_nums, symmetries_ptr, false, timing_pos_num); + output_viewgrams += viewgrams; + + if (!(output_projdata.set_related_viewgrams(viewgrams) == Succeeded::yes)) { + warning("CorrectProjData: Error set_related_viewgrams\n"); + return Succeeded::no; + } + } + } + } return Succeeded::yes; -} - +} -void -CorrectProjDataApplication:: -set_defaults() -{ +void +CorrectProjDataApplication::set_defaults() { input_projdata_ptr.reset(); max_segment_num_to_process = -1; normalisation_ptr.reset(); - use_data_or_set_to_1= true; + use_data_or_set_to_1 = true; apply_or_undo_correction = true; scatter_projdata_filename = ""; atten_image_filename = ""; @@ -394,25 +354,23 @@ set_defaults() #ifndef USE_PMRT forward_projector_ptr.reset(new ForwardProjectorByBinUsingRayTracing); #else - shared_ptr PM(new ProjMatrixByBinUsingRayTracing); - forward_projector_ptr.reset(new ForwardProjectorByBinUsingProjMatrixByBin(PM)); + shared_ptr PM(new ProjMatrixByBinUsingRayTracing); + forward_projector_ptr.reset(new ForwardProjectorByBinUsingProjMatrixByBin(PM)); #endif - do_arc_correction= false; + do_arc_correction = false; } -void -CorrectProjDataApplication:: -initialise_keymap() -{ +void +CorrectProjDataApplication::initialise_keymap() { parser.add_start_key("correct_projdata Parameters"); - parser.add_key("input file",&input_filename); - parser.add_key("time frame definition filename", &frame_definition_filename); + parser.add_key("input file", &input_filename); + parser.add_key("time frame definition filename", &frame_definition_filename); parser.add_key("time frame number", &frame_num); - parser.add_key("output filename",&output_filename); + parser.add_key("output filename", &output_filename); parser.add_key("maximum absolute segment number to process", &max_segment_num_to_process); - + parser.add_key("use data (1) or set to one (0)", &use_data_or_set_to_1); parser.add_key("apply (1) or undo (0) correction", &apply_or_undo_correction); parser.add_parsing_key("Bin Normalisation type", &normalisation_ptr); @@ -424,76 +382,59 @@ initialise_keymap() parser.add_stop_key("END"); } - bool -CorrectProjDataApplication:: -post_processing() -{ - if (is_null_ptr(normalisation_ptr)) - { +CorrectProjDataApplication::post_processing() { + if (is_null_ptr(normalisation_ptr)) { warning("Invalid normalisation object\n"); return true; } - // read time frame def - if (frame_definition_filename.size()!=0) + // read time frame def + if (frame_definition_filename.size() != 0) frame_defs = TimeFrameDefinitions(frame_definition_filename); - else - { - // make a single frame starting from 0 to 1. - std::vector > frame_times(1, std::pair(0,1)); - frame_defs = TimeFrameDefinitions(frame_times); - } + else { + // make a single frame starting from 0 to 1. + std::vector> frame_times(1, std::pair(0, 1)); + frame_defs = TimeFrameDefinitions(frame_times); + } - if (frame_num<=0) - { - warning("frame_num should be >= 1 \n"); - return true; - } + if (frame_num <= 0) { + warning("frame_num should be >= 1 \n"); + return true; + } - if (static_cast(frame_num)> frame_defs.get_num_frames()) - { - warning("frame_num is %d, but should be less than the number of frames %d.\n", - frame_num, frame_defs.get_num_frames()); - return true; - } + if (static_cast(frame_num) > frame_defs.get_num_frames()) { + warning("frame_num is %d, but should be less than the number of frames %d.\n", frame_num, frame_defs.get_num_frames()); + return true; + } input_projdata_ptr = ProjData::read_from_file(input_filename); - if (scatter_projdata_filename!="" && scatter_projdata_filename != "0") + if (scatter_projdata_filename != "" && scatter_projdata_filename != "0") scatter_projdata_ptr = ProjData::read_from_file(scatter_projdata_filename); - if (randoms_projdata_filename!="" && randoms_projdata_filename != "0") + if (randoms_projdata_filename != "" && randoms_projdata_filename != "0") randoms_projdata_ptr = ProjData::read_from_file(randoms_projdata_filename); return false; } Succeeded -CorrectProjDataApplication:: -set_up() -{ - const int max_segment_num_available = - input_projdata_ptr->get_max_segment_num(); +CorrectProjDataApplication::set_up() { + const int max_segment_num_available = input_projdata_ptr->get_max_segment_num(); // Set default or upper bound of data to process (if out of bounds) - if (max_segment_num_to_process<0 || - max_segment_num_to_process > max_segment_num_available) + if (max_segment_num_to_process < 0 || max_segment_num_to_process > max_segment_num_available) max_segment_num_to_process = max_segment_num_available; - shared_ptr - input_proj_data_info_sptr(input_projdata_ptr->get_proj_data_info_sptr()->clone()); + shared_ptr input_proj_data_info_sptr(input_projdata_ptr->get_proj_data_info_sptr()->clone()); shared_ptr output_proj_data_info_sptr; if (!do_arc_correction) output_proj_data_info_sptr = input_proj_data_info_sptr; - else - { - arc_correction_sptr = - shared_ptr(new ArcCorrection); - arc_correction_sptr->set_up(input_proj_data_info_sptr); - output_proj_data_info_sptr = - arc_correction_sptr->get_arc_corrected_proj_data_info_sptr()->create_shared_clone(); - } - output_proj_data_info_sptr->reduce_segment_range(-max_segment_num_to_process, - max_segment_num_to_process); + else { + arc_correction_sptr = shared_ptr(new ArcCorrection); + arc_correction_sptr->set_up(input_proj_data_info_sptr); + output_proj_data_info_sptr = arc_correction_sptr->get_arc_corrected_proj_data_info_sptr()->create_shared_clone(); + } + output_proj_data_info_sptr->reduce_segment_range(-max_segment_num_to_process, max_segment_num_to_process); // construct output_projdata { @@ -514,7 +455,7 @@ set_up() else #endif { - string output_filename_with_ext = output_filename; + string output_filename_with_ext = output_filename; #if 0 if (frame_definition_filename.size()!=0) { @@ -523,26 +464,20 @@ set_up() output_filename_with_ext += ext; } #endif - output_projdata_ptr.reset(new ProjDataInterfile(input_projdata_ptr->get_exam_info_sptr(), - output_proj_data_info_sptr,output_filename_with_ext)); - }// output_projdata block + output_projdata_ptr.reset( + new ProjDataInterfile(input_projdata_ptr->get_exam_info_sptr(), output_proj_data_info_sptr, output_filename_with_ext)); + } // output_projdata block + + } // output_projdata block - }// output_projdata block - // read attenuation image and add it to the normalisation object - if(atten_image_filename!="0" && atten_image_filename!="") - { - - shared_ptr atten_sptr - (new BinNormalisationFromAttenuationImage(atten_image_filename, - forward_projector_ptr)); - - normalisation_ptr = - shared_ptr - ( new ChainedBinNormalisation(normalisation_ptr, atten_sptr)); - } - else - { + if (atten_image_filename != "0" && atten_image_filename != "") { + + shared_ptr atten_sptr( + new BinNormalisationFromAttenuationImage(atten_image_filename, forward_projector_ptr)); + + normalisation_ptr = shared_ptr(new ChainedBinNormalisation(normalisation_ptr, atten_sptr)); + } else { // get rid of this object for now // this is currently checked to find the symmetries: bad // TODO @@ -550,49 +485,37 @@ set_up() } // set up normalisation object - if ( - normalisation_ptr->set_up(input_projdata_ptr->get_exam_info_sptr(),input_proj_data_info_sptr) - != Succeeded::yes) - { - warning("correct_projdata: set-up of normalisation failed\n"); - return Succeeded::no; - } + if (normalisation_ptr->set_up(input_projdata_ptr->get_exam_info_sptr(), input_proj_data_info_sptr) != Succeeded::yes) { + warning("correct_projdata: set-up of normalisation failed\n"); + return Succeeded::no; + } return Succeeded::yes; - } -CorrectProjDataApplication:: -CorrectProjDataApplication(const char * const par_filename) -{ +CorrectProjDataApplication::CorrectProjDataApplication(const char* const par_filename) { set_defaults(); - if (par_filename!=0) - parse(par_filename) ; + if (par_filename != 0) + parse(par_filename); else ask_parameters(); - } END_NAMESPACE_STIR USING_NAMESPACE_STIR -int main(int argc, char *argv[]) -{ - - if(argc!=2) - { - cerr<<"Usage: " << argv[0] << " par_file\n" - << endl; +int +main(int argc, char* argv[]) { + + if (argc != 2) { + cerr << "Usage: " << argv[0] << " par_file\n" << endl; + } + CorrectProjDataApplication correct_proj_data_application(argc == 2 ? argv[1] : 0); + + if (argc != 2) { + cerr << "Corresponding .par file input \n" << correct_proj_data_application.parameter_info() << endl; } - CorrectProjDataApplication correct_proj_data_application( argc==2 ? argv[1] : 0); - - if (argc!=2) - { - cerr << "Corresponding .par file input \n" - << correct_proj_data_application.parameter_info() << endl; - } - CPUTimer timer; timer.start(); @@ -600,11 +523,8 @@ int main(int argc, char *argv[]) if (correct_proj_data_application.set_up() == Succeeded::no) return EXIT_FAILURE; - Succeeded success = - correct_proj_data_application.run(); + Succeeded success = correct_proj_data_application.run(); timer.stop(); cerr << "CPU time : " << timer.value() << "secs" << endl; - return success==Succeeded::yes ? - EXIT_SUCCESS : EXIT_FAILURE; - + return success == Succeeded::yes ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/src/utilities/create_multi_header.cxx b/src/utilities/create_multi_header.cxx index e397d29589..5dff3f74cb 100644 --- a/src/utilities/create_multi_header.cxx +++ b/src/utilities/create_multi_header.cxx @@ -33,44 +33,46 @@ \warning There is no check that the data sizes and other info are compatible. Hence, lots of funny effects can happen if data are not compatible. - \author Kris Thielemans + \author Kris Thielemans */ #include "stir/MultipleDataSetHeader.h" #include -#include -#include +#include +#include USING_NAMESPACE_STIR -int -main(int argc, char **argv) -{ - if(argc<3) - { - std::cerr<< "Usage: " << argv[0] << "\n\t" - << "output_filename_with_extension in_data1 [in_data2 [in_data3...]]\n\n"; - exit(EXIT_FAILURE); - } +int +main(int argc, char** argv) { + if (argc < 3) { + std::cerr << "Usage: " << argv[0] << "\n\t" + << "output_filename_with_extension in_data1 [in_data2 [in_data3...]]\n\n"; + exit(EXIT_FAILURE); + } // skip program name --argc; ++argv; - - if (argc==0) - { std::cerr << "No output file (nor input files) on command line\n"; exit(EXIT_FAILURE); } + if (argc == 0) { + std::cerr << "No output file (nor input files) on command line\n"; + exit(EXIT_FAILURE); + } // find output filename const std::string output_file_name = *argv; - { - --argc; ++argv; - } + { + --argc; + ++argv; + } const int num_files = argc; - if (num_files==0) - { std::cerr << "No input files on command line\n"; exit(EXIT_FAILURE); } + if (num_files == 0) { + std::cerr << "No input files on command line\n"; + exit(EXIT_FAILURE); + } - MultipleDataSetHeader::write_header(output_file_name, std::vector(argv, argv+argc)); + MultipleDataSetHeader::write_header(output_file_name, std::vector(argv, argv + argc)); return EXIT_SUCCESS; } diff --git a/src/utilities/create_projdata_template.cxx b/src/utilities/create_projdata_template.cxx index 877995452c..220266843f 100644 --- a/src/utilities/create_projdata_template.cxx +++ b/src/utilities/create_projdata_template.cxx @@ -18,7 +18,7 @@ */ /* Copyright (C) 2004, Hammersmith Imanet Ltd - Copyright (C) 2014, University College London + Copyright (C) 2014, University College London This file is part of STIR. This file is free software; you can redistribute it and/or modify @@ -43,31 +43,29 @@ using std::cerr; #endif - USING_NAMESPACE_STIR -int main(int argc, char *argv[]) -{ - - if(argc!=2) - { - cerr<<"Usage: " << argv[0] << " output_filename\n"; +int +main(int argc, char* argv[]) { + + if (argc != 2) { + cerr << "Usage: " << argv[0] << " output_filename\n"; return EXIT_FAILURE; } - shared_ptr proj_data_info_sptr(ProjDataInfo::ask_parameters()); - + const std::string output_file_name = argv[1]; shared_ptr exam_info_sptr(new ExamInfo); // TODO, Currently all stir::Scanner types are PET. exam_info_sptr->imaging_modality = ImagingModality::PT; // If TOF activated -- No mashing factor will produce surrealistic sinograms - //if ( proj_data_info_sptr->get_num_tof_poss() >1) - // shared_ptr proj_data_sptr(new ProjDataInterfile(exam_info_sptr, proj_data_info_sptr, output_file_name, std::ios::out, - // ProjDataFromStream::Timing_Segment_View_AxialPos_TangPos)); - //else - shared_ptr proj_data_sptr(new ProjDataInterfile(exam_info_sptr, proj_data_info_sptr, output_file_name)); + // if ( proj_data_info_sptr->get_num_tof_poss() >1) + // shared_ptr proj_data_sptr(new ProjDataInterfile(exam_info_sptr, proj_data_info_sptr, output_file_name, + // std::ios::out, + // ProjDataFromStream::Timing_Segment_View_AxialPos_TangPos)); + // else + shared_ptr proj_data_sptr(new ProjDataInterfile(exam_info_sptr, proj_data_info_sptr, output_file_name)); return EXIT_SUCCESS; } diff --git a/src/utilities/ctac_to_mu_values.cxx b/src/utilities/ctac_to_mu_values.cxx index 86128c5754..0d73ced55f 100644 --- a/src/utilities/ctac_to_mu_values.cxx +++ b/src/utilities/ctac_to_mu_values.cxx @@ -25,10 +25,8 @@ \par Usage: \code - ctac_to_mu_values -o output_filename -i input_volume -j slope_filename -m manufacturer_name [-v kilovoltage_peak] -k target_photon_energy - \endcode - Default value for tube voltage is 120 kV. For PET, the \c target_photon_energy - has to be 511. + ctac_to_mu_values -o output_filename -i input_volume -j slope_filename -m manufacturer_name [-v kilovoltage_peak] -k + target_photon_energy \endcode Default value for tube voltage is 120 kV. For PET, the \c target_photon_energy has to be 511. This convert HU to mu-values using a piece-wise linear curve. \see HUToMuImageProcessor for details on file format etc @@ -36,7 +34,6 @@ */ - #include "stir/info.h" #include "stir/Succeeded.h" #include "stir/HUToMuImageProcessor.h" @@ -49,107 +46,95 @@ USING_NAMESPACE_STIR -typedef DiscretisedDensity<3,float> FloatImageType; - +typedef DiscretisedDensity<3, float> FloatImageType; -int main(int argc, char * argv[]) -{ +int +main(int argc, char* argv[]) { USING_NAMESPACE_STIR; - const char * output_filename = 0; - const char * input_filename = 0; - const char * slope_filename = 0; - const char * manufacturer_name = 0; - const char * kVp_str = 0; - const char * keV_str = 0; - - const char * const usage = "ctac_to_mu_values -o output_filename -i input_volume -j slope_filename -m manufacturer_name [-v kilovoltage_peak] -k target_photon_energy\n" - "Default value for tube voltage is 120 kV.\n"; + const char* output_filename = 0; + const char* input_filename = 0; + const char* slope_filename = 0; + const char* manufacturer_name = 0; + const char* kVp_str = 0; + const char* keV_str = 0; + + const char* const usage = "ctac_to_mu_values -o output_filename -i input_volume -j slope_filename -m manufacturer_name [-v " + "kilovoltage_peak] -k target_photon_energy\n" + "Default value for tube voltage is 120 kV.\n"; opterr = 0; { char c; - while ((c = getopt (argc, argv, "i:o:j:m:v:k:?")) != -1) - switch (c) - { - case 'i': - input_filename = optarg; - break; - case 'o': - output_filename = optarg; - break; - case 'j': - slope_filename = optarg; - break; - case 'm': - manufacturer_name = optarg; - break; - case 'v': - kVp_str = optarg; - break; - case 'k': - keV_str = optarg; - break; - case '?': - std::cerr << usage; - return EXIT_FAILURE; - default: - if (isprint (optopt)) - fprintf (stderr, "Unknown option `-%c'.\n", optopt); - else - fprintf (stderr, - "Unknown option character `\\x%x'.\n", - optopt); - std::cerr << usage; - return EXIT_FAILURE; - } + while ((c = getopt(argc, argv, "i:o:j:m:v:k:?")) != -1) + switch (c) { + case 'i': + input_filename = optarg; + break; + case 'o': + output_filename = optarg; + break; + case 'j': + slope_filename = optarg; + break; + case 'm': + manufacturer_name = optarg; + break; + case 'v': + kVp_str = optarg; + break; + case 'k': + keV_str = optarg; + break; + case '?': + std::cerr << usage; + return EXIT_FAILURE; + default: + if (isprint(optopt)) + fprintf(stderr, "Unknown option `-%c'.\n", optopt); + else + fprintf(stderr, "Unknown option character `\\x%x'.\n", optopt); + std::cerr << usage; + return EXIT_FAILURE; + } } - if (output_filename==0 || input_filename==0 || slope_filename==0 || manufacturer_name==0 || keV_str==0 ) - { - std::cerr << usage; - return EXIT_FAILURE; - } + if (output_filename == 0 || input_filename == 0 || slope_filename == 0 || manufacturer_name == 0 || keV_str == 0) { + std::cerr << usage; + return EXIT_FAILURE; + } - if (kVp_str == 0){ - //If the user does not specify a value for kVP, assume 120 kVp. + if (kVp_str == 0) { + // If the user does not specify a value for kVP, assume 120 kVp. kVp_str = "120"; stir::info(boost::format("No value for kVp given, assuming %s") % kVp_str); } - try - { - HUToMuImageProcessor > hu_to_mu; - hu_to_mu.set_manufacturer_name(manufacturer_name); - hu_to_mu.set_slope_filename(slope_filename); - hu_to_mu.set_target_photon_energy(std::stof(keV_str)); - hu_to_mu.set_kilovoltage_peak(std::stof(kVp_str)); - //Read DICOM data - stir::info(boost::format("ctac_to_mu_values: opening file %1%") % input_filename); - unique_ptr< FloatImageType > input_image_sptr(stir::read_from_file( input_filename )); - hu_to_mu.set_up(*input_image_sptr); - - //Create output image from input image. - shared_ptr< FloatImageType > output_image_sptr(input_image_sptr->clone()); - //Apply scaling. - hu_to_mu.apply(*output_image_sptr, *input_image_sptr); - //Write output file. - write_to_file(output_filename, *output_image_sptr); - } - catch (std::string& error_string) - { - // don't print yet, as error() already does that at the moment - // std::cerr << error_string << std::endl; - return EXIT_FAILURE; - } - catch (std::exception& e) - { - stir::warning(e.what()); - return EXIT_FAILURE; - } - catch (...) - { - return EXIT_FAILURE; - } + try { + HUToMuImageProcessor> hu_to_mu; + hu_to_mu.set_manufacturer_name(manufacturer_name); + hu_to_mu.set_slope_filename(slope_filename); + hu_to_mu.set_target_photon_energy(std::stof(keV_str)); + hu_to_mu.set_kilovoltage_peak(std::stof(kVp_str)); + // Read DICOM data + stir::info(boost::format("ctac_to_mu_values: opening file %1%") % input_filename); + unique_ptr input_image_sptr(stir::read_from_file(input_filename)); + hu_to_mu.set_up(*input_image_sptr); + + // Create output image from input image. + shared_ptr output_image_sptr(input_image_sptr->clone()); + // Apply scaling. + hu_to_mu.apply(*output_image_sptr, *input_image_sptr); + // Write output file. + write_to_file(output_filename, *output_image_sptr); + } catch (std::string& error_string) { + // don't print yet, as error() already does that at the moment + // std::cerr << error_string << std::endl; + return EXIT_FAILURE; + } catch (std::exception& e) { + stir::warning(e.what()); + return EXIT_FAILURE; + } catch (...) { + return EXIT_FAILURE; + } return EXIT_SUCCESS; - } diff --git a/src/utilities/display_projdata.cxx b/src/utilities/display_projdata.cxx index 09002a6b80..fdad4485b5 100644 --- a/src/utilities/display_projdata.cxx +++ b/src/utilities/display_projdata.cxx @@ -1,9 +1,9 @@ // // /*! - \file + \file \ingroup utilities - + \brief This program displays projection data by segment \author Kris Thielemans @@ -35,48 +35,40 @@ #include "stir/display.h" #include "stir/utilities.h" - #ifndef STIR_NO_NAMESPACES using std::cout; #endif USING_NAMESPACE_STIR - -int main(int argc, char *argv[]) -{ - if(argc<2) { - cout<<"Usage: display_projdata
      (*.hs)\n"; - exit(EXIT_FAILURE); - } - - - - shared_ptr s3d = ProjData::read_from_file(argv[1]); - - do { - int segment_num = ask_num("Which segment number do you want to display", - s3d->get_min_segment_num(), s3d->get_max_segment_num(), 0); - - int tof_num = ask_num("Which timing pos number do you want to display", - s3d->get_min_tof_pos_num(), s3d->get_max_tof_pos_num(), 0); - - if(ask_num("Display as SegmentByView (0) or BySinogram (1)?", 0,1,0)==0) - { - SegmentByView segment= s3d->get_segment_by_view(segment_num, tof_num); - const float maxi = - ask_num("Maximum in color scale (default is actual max)",0.F,2*segment.find_max(),segment.find_max()); - display(segment,maxi); - } - else - { - SegmentBySinogram segment = s3d->get_segment_by_sinogram(segment_num, tof_num); - const float maxi = - ask_num("Maximum in color scale (default is actual max)",0.F,2*segment.find_max(),segment.find_max()); - display(segment,maxi); - } +int +main(int argc, char* argv[]) { + if (argc < 2) { + cout << "Usage: display_projdata
      (*.hs)\n"; + exit(EXIT_FAILURE); + } + + shared_ptr s3d = ProjData::read_from_file(argv[1]); + + do { + int segment_num = + ask_num("Which segment number do you want to display", s3d->get_min_segment_num(), s3d->get_max_segment_num(), 0); + + int tof_num = + ask_num("Which timing pos number do you want to display", s3d->get_min_tof_pos_num(), s3d->get_max_tof_pos_num(), 0); + + if (ask_num("Display as SegmentByView (0) or BySinogram (1)?", 0, 1, 0) == 0) { + SegmentByView segment = s3d->get_segment_by_view(segment_num, tof_num); + const float maxi = + ask_num("Maximum in color scale (default is actual max)", 0.F, 2 * segment.find_max(), segment.find_max()); + display(segment, maxi); + } else { + SegmentBySinogram segment = s3d->get_segment_by_sinogram(segment_num, tof_num); + const float maxi = + ask_num("Maximum in color scale (default is actual max)", 0.F, 2 * segment.find_max(), segment.find_max()); + display(segment, maxi); } - while (ask("Display another segment ?",true)); + } while (ask("Display another segment ?", true)); - return EXIT_SUCCESS; + return EXIT_SUCCESS; } diff --git a/src/utilities/do_linear_regression.cxx b/src/utilities/do_linear_regression.cxx index d21f9f523c..27e5d47bfe 100644 --- a/src/utilities/do_linear_regression.cxx +++ b/src/utilities/do_linear_regression.cxx @@ -1,10 +1,10 @@ // // /*! - \file + \file \ingroup utilities - - \brief + + \brief A simple programme to perform weighted least squares. \author Kris Thielemans @@ -12,7 +12,7 @@ This performs a weighted least squares fit.
      - stdev and covariance are computed using the estimated + stdev and covariance are computed using the estimated variance chi_square/(n-2). The file should contain data in the following format:
      @@ -54,32 +54,26 @@ using std::ifstream; USING_NAMESPACE_STIR +int +main(int argc, char** argv) { -int main(int argc, char **argv) -{ - - if (argc != 2) - { + if (argc != 2) { cerr << "Usage : " << argv[0] << " filename\n" << "This performs a weighted least squares fit.\n" - << "stdev and covariance are computed using the estimated " - << "variance chi_square/(n-2).\n\n" - << "The file should contain data in the following format:\n\n" - << "number_of_points\n" - << "coordinates\n" - << "data\n" - << "weights\n" << endl; + << "stdev and covariance are computed using the estimated " + << "variance chi_square/(n-2).\n\n" + << "The file should contain data in the following format:\n\n" + << "number_of_points\n" + << "coordinates\n" + << "data\n" + << "weights\n" + << endl; return EXIT_FAILURE; } - - ifstream in(argv[1]); - if (!in) - { - cerr << argv[0] - << ": Error opening input file " << argv[1] << "\nExiting." - << endl; + if (!in) { + cerr << argv[0] << ": Error opening input file " << argv[1] << "\nExiting." << endl; return EXIT_FAILURE; } @@ -89,50 +83,34 @@ int main(int argc, char **argv) VectorWithOffset coordinates(size); VectorWithOffset measured_data(size); VectorWithOffset weights(size); - for (int i=0; i> coordinates[i]; - if (!in) - error("%s: error reading input file %s after the %d-th coordinate\n", - argv[0], argv[1], i); - } - for (int i=0; i> measured_data[i]; - if (!in) - error("%s: error reading input file %s after the %d-th measured_data\n", - argv[0], argv[1], i); - } - for (int i=0; i> weights[i]; - if (!in) - error("%s: error reading input file %s after the %d-th weight\n", - argv[0], argv[1], i); - } - - double scale=0; - double constant=0; - double variance_of_scale=0; - double variance_of_constant=0; - double covariance_of_constant_with_scale=0; + for (int i = 0; i < size; i++) { + in >> coordinates[i]; + if (!in) + error("%s: error reading input file %s after the %d-th coordinate\n", argv[0], argv[1], i); + } + for (int i = 0; i < size; i++) { + in >> measured_data[i]; + if (!in) + error("%s: error reading input file %s after the %d-th measured_data\n", argv[0], argv[1], i); + } + for (int i = 0; i < size; i++) { + in >> weights[i]; + if (!in) + error("%s: error reading input file %s after the %d-th weight\n", argv[0], argv[1], i); + } + + double scale = 0; + double constant = 0; + double variance_of_scale = 0; + double variance_of_constant = 0; + double covariance_of_constant_with_scale = 0; double chi_square = 0; - linear_regression( - constant, scale, - chi_square, - variance_of_constant, - variance_of_scale, - covariance_of_constant_with_scale, - measured_data, - coordinates, - weights); - - cout << "scale = " << scale << " +- " << sqrt(variance_of_scale) - << ", cst = " << constant << " +- " << sqrt(variance_of_constant) - << "\nchi_square = " << chi_square - << "\ncovariance = " << covariance_of_constant_with_scale + linear_regression(constant, scale, chi_square, variance_of_constant, variance_of_scale, covariance_of_constant_with_scale, + measured_data, coordinates, weights); + + cout << "scale = " << scale << " +- " << sqrt(variance_of_scale) << ", cst = " << constant << " +- " + << sqrt(variance_of_constant) << "\nchi_square = " << chi_square << "\ncovariance = " << covariance_of_constant_with_scale << endl; return EXIT_SUCCESS; } - diff --git a/src/utilities/ecat/conv_to_ecat6.cxx b/src/utilities/ecat/conv_to_ecat6.cxx index 5b88bbe5a8..5bc2b0dc8e 100644 --- a/src/utilities/ecat/conv_to_ecat6.cxx +++ b/src/utilities/ecat/conv_to_ecat6.cxx @@ -17,16 +17,16 @@ See STIR/LICENSE.txt for details */ -/*! +/*! \file \ingroup ECAT_utilities -\brief Conversion from interfile (or any format that we can read) +\brief Conversion from interfile (or any format that we can read) to ECAT 6 cti (image and sinogram data) \author Kris Thielemans \author PARAPET project -This programme is used to convert image or projection data into CTI ECAT 6 data (input -can be any format currently supported by the library). It normally should be run as +This programme is used to convert image or projection data into CTI ECAT 6 data (input +can be any format currently supported by the library). It normally should be run as follows
      conv_to_ecat6 [-k] [-i]  outputfilename.img input_filename1 [input_filename2 ...] scanner_name
       
      @@ -34,27 +34,26 @@ follows
      conv_to_ecat6 -s[2] [-k] [-i] outputfilename.scn input_filename1 [input_filename2 ...]
       
      (for projection data)
      -If there are no command line parameters, the user is asked for the filenames and options -instead. Unless the -i option is used, the data will be assigned a frame number in the +If there are no command line parameters, the user is asked for the filenames and options +instead. Unless the -i option is used, the data will be assigned a frame number in the order that they occur on the command line.
      -See buildblock/Scanner.cxx for supported scanner names, but examples are ECAT 953, -ART, Advance. ECAT HR+, etc. If the scanner_name contains a space, the scanner name has to +See buildblock/Scanner.cxx for supported scanner names, but examples are ECAT 953, +ART, Advance. ECAT HR+, etc. If the scanner_name contains a space, the scanner name has to be surrounded by double quotes (") when used as a command line argument. \par Command line options:
      • -s2: This option forces output to 2D sinograms (ignoring higher segments).
      • -
      • -k: the existing ECAT6 file will NOT be overwritten, but added to. Any existing -data in the ECAT6 file with the same <frame,gate,data,bed> specification will be +
      • -k: the existing ECAT6 file will NOT be overwritten, but added to. Any existing +data in the ECAT6 file with the same <frame,gate,data,bed> specification will be overwritten.
      • -i: ask for <frame,gate,data,bed> for each dataset
      -Note that to store projection data in ECAT6, a 3D sinogram cannot be axially compressed +Note that to store projection data in ECAT6, a 3D sinogram cannot be axially compressed (CTI span=1). */ - #include "stir/DiscretisedDensity.h" #include "stir/ProjData.h" #include "stir/shared_ptr.h" @@ -80,240 +79,191 @@ USING_NAMESPACE_STIR USING_NAMESPACE_ECAT USING_NAMESPACE_ECAT6 - -int main(int argc, char *argv[]) -{ +int +main(int argc, char* argv[]) { char cti_name[1000], scanner_name[1000] = ""; vector filenames; bool its_an_image = true; bool its_a_2D_sinogram = false; bool add_to_existing = false; bool interactive = false; - - if(argc>=4) - { - if (strncmp(argv[1],"-s",2)==0) - { - its_an_image = false; - its_a_2D_sinogram = strlen(argv[1])==3 && argv[1][2]=='2'; - if (its_a_2D_sinogram) - cout << "I will write 2D sinograms\n"; - while (argv[2][0] == '-') - { - if (strcmp(argv[2],"-k")==0) - add_to_existing = true; - else if (strcmp(argv[2],"-i")==0) - interactive = true; - else - warning("Ignored unrecognised option: %s.", argv[2]); - - argv++; argc--; - } - strcpy(cti_name,argv[2]); - int num_files = argc-3; - argv+=3; - filenames.reserve(num_files); - for (; num_files>0; --num_files, ++argv) - filenames.push_back(*argv); - } - else - { - its_an_image = true; - strcpy(cti_name,argv[1]); - int num_files = argc-3; - argv+=2; - filenames.reserve(num_files); - for (; num_files>0; --num_files, ++argv) - filenames.push_back(*argv); - strcpy(scanner_name,*argv); + if (argc >= 4) { + if (strncmp(argv[1], "-s", 2) == 0) { + its_an_image = false; + its_a_2D_sinogram = strlen(argv[1]) == 3 && argv[1][2] == '2'; + if (its_a_2D_sinogram) + cout << "I will write 2D sinograms\n"; + + while (argv[2][0] == '-') { + if (strcmp(argv[2], "-k") == 0) + add_to_existing = true; + else if (strcmp(argv[2], "-i") == 0) + interactive = true; + else + warning("Ignored unrecognised option: %s.", argv[2]); + + argv++; + argc--; } - } - else - { - cerr<< "\nConversion from data to ECAT6 CTI.\n" - << "Multiples files can be written to a single ECAT 6 file.\n" - << "Unless the -i option is used, the data will be assigned a frame number in \n" - << "the order that they occur on the command line.\n\n" - << "Usage: 2 possible forms depending on data type\n" - << "For sinogram data:\n" - << "\tconv_to_ecat6 -s[2] [-k] [-i] output_ECAT6_name orig_filename1 [orig_filename2 ...]\n" - << "\tThe -s2 option forces output to 2D sinograms (ignoring higher segments).\n" - << "For image data:\n" - << "\tconv_to_ecat6 [-k] [-i] output_ECAT6_name orig_filename1 [orig_filename2 ...] scanner_name\n" - << "scanner_name has to be recognised by the Scanner class\n" - << "Examples are : \"ECAT 953\", \"RPT\" etc.\n" - << "(the quotes are required when used as a command line argument)\n\n" - << "Options:\n" - << " -k: the existing ECAT6 file will NOT be overwritten,\n" - << "\tbut added to. Any existing data in the ECAT6 file with the same \n" - << "\tspecification will be overwritten.\n" - << " -i: ask for for each dataset\n\n" - << "I will now ask you the same info interactively...\n\n"; - - its_an_image = ask("Converting images?",true); + strcpy(cti_name, argv[2]); + int num_files = argc - 3; + argv += 3; + filenames.reserve(num_files); + for (; num_files > 0; --num_files, ++argv) + filenames.push_back(*argv); + } else { + its_an_image = true; + strcpy(cti_name, argv[1]); + int num_files = argc - 3; + argv += 2; + filenames.reserve(num_files); + for (; num_files > 0; --num_files, ++argv) + filenames.push_back(*argv); + strcpy(scanner_name, *argv); + } + } else { + cerr << "\nConversion from data to ECAT6 CTI.\n" + << "Multiples files can be written to a single ECAT 6 file.\n" + << "Unless the -i option is used, the data will be assigned a frame number in \n" + << "the order that they occur on the command line.\n\n" + << "Usage: 2 possible forms depending on data type\n" + << "For sinogram data:\n" + << "\tconv_to_ecat6 -s[2] [-k] [-i] output_ECAT6_name orig_filename1 [orig_filename2 ...]\n" + << "\tThe -s2 option forces output to 2D sinograms (ignoring higher segments).\n" + << "For image data:\n" + << "\tconv_to_ecat6 [-k] [-i] output_ECAT6_name orig_filename1 [orig_filename2 ...] scanner_name\n" + << "scanner_name has to be recognised by the Scanner class\n" + << "Examples are : \"ECAT 953\", \"RPT\" etc.\n" + << "(the quotes are required when used as a command line argument)\n\n" + << "Options:\n" + << " -k: the existing ECAT6 file will NOT be overwritten,\n" + << "\tbut added to. Any existing data in the ECAT6 file with the same \n" + << "\tspecification will be overwritten.\n" + << " -i: ask for for each dataset\n\n" + << "I will now ask you the same info interactively...\n\n"; + + its_an_image = ask("Converting images?", true); if (!its_an_image) - its_a_2D_sinogram = ask("Write as 2D sinogram?",false); + its_a_2D_sinogram = ask("Write as 2D sinogram?", false); - int num_files = ask_num("Number of files",1,10000,1); + int num_files = ask_num("Number of files", 1, 10000, 1); filenames.reserve(num_files); char cur_name[max_filename_length]; - for (; num_files>0; --num_files) - { - ask_filename_with_extension(cur_name,"Name of the input file? ",its_an_image?".hv":".hs"); + for (; num_files > 0; --num_files) { + ask_filename_with_extension(cur_name, "Name of the input file? ", its_an_image ? ".hv" : ".hs"); filenames.push_back(cur_name); } - - ask_filename_with_extension(cti_name,"Name of the ECAT6 file? ", - its_an_image ? ".img" : ".scn"); + + ask_filename_with_extension(cti_name, "Name of the ECAT6 file? ", its_an_image ? ".img" : ".scn"); } size_t num_frames, num_gates, num_bed_poss, num_data; - if (interactive) - { - num_frames = ask_num("Num frames?",static_cast(1),filenames.size(), filenames.size()); - num_gates = ask_num("Num gates?",static_cast(1),filenames.size()/num_frames, filenames.size()/num_frames); - num_bed_poss = ask_num("Num bed positions?",static_cast(1),filenames.size(), filenames.size()); - num_data = ask_num("Num data?",static_cast(1),filenames.size()/num_frames, filenames.size()/num_frames); - } - else - { - num_frames = filenames.size(); - num_gates=1; - num_bed_poss=1; - num_data=1; - } + if (interactive) { + num_frames = ask_num("Num frames?", static_cast(1), filenames.size(), filenames.size()); + num_gates = ask_num("Num gates?", static_cast(1), filenames.size() / num_frames, filenames.size() / num_frames); + num_bed_poss = ask_num("Num bed positions?", static_cast(1), filenames.size(), filenames.size()); + num_data = ask_num("Num data?", static_cast(1), filenames.size() / num_frames, filenames.size() / num_frames); + } else { + num_frames = filenames.size(); + num_gates = 1; + num_bed_poss = 1; + num_data = 1; + } size_t min_frame_num = 1; size_t max_frame_num = num_frames; size_t min_bed_num = 0; - size_t max_bed_num = num_bed_poss-1; + size_t max_bed_num = num_bed_poss - 1; size_t min_gate_num = 1; size_t max_gate_num = num_gates; size_t min_data_num = 0; - if (its_an_image) - { + if (its_an_image) { - shared_ptr scanner_ptr( - strlen(scanner_name)==0 ? - Scanner::ask_parameters() : - Scanner::get_scanner_from_name(scanner_name)); + shared_ptr scanner_ptr(strlen(scanner_name) == 0 ? Scanner::ask_parameters() + : Scanner::get_scanner_from_name(scanner_name)); // read first image cerr << "Reading " << filenames[0] << endl; - shared_ptr > - density_ptr(read_from_file >(filenames[0])); - + shared_ptr> density_ptr(read_from_file>(filenames[0])); + ECAT6_Main_header mhead; make_ECAT6_Main_header(mhead, *scanner_ptr, filenames[0], *density_ptr); mhead.num_frames = filenames.size(); - FILE *fptr= cti_create (cti_name, &mhead); - if (fptr == NULL) - { + FILE* fptr = cti_create(cti_name, &mhead); + if (fptr == NULL) { warning("conv_to_ecat6: error opening output file %s\n", cti_name); return EXIT_FAILURE; } size_t frame_num = 1; - while (1) - { - if (DiscretisedDensity_to_ECAT6(fptr, - *density_ptr, - mhead, - frame_num) - == Succeeded::no) - { + while (1) { + if (DiscretisedDensity_to_ECAT6(fptr, *density_ptr, mhead, frame_num) == Succeeded::no) { fclose(fptr); return EXIT_FAILURE; } - if (++frame_num > filenames.size()) - { + if (++frame_num > filenames.size()) { fclose(fptr); return EXIT_SUCCESS; } - cerr << "Reading " << filenames[frame_num-1] << endl; - density_ptr = - read_from_file >(filenames[frame_num-1]); + cerr << "Reading " << filenames[frame_num - 1] << endl; + density_ptr = read_from_file>(filenames[frame_num - 1]); } - } - else - { - + } else { + // read first data set cerr << "Reading " << filenames[0] << endl; - shared_ptr proj_data_ptr = - ProjData::read_from_file(filenames[0]); - + shared_ptr proj_data_ptr = ProjData::read_from_file(filenames[0]); + ECAT6_Main_header mhead; - FILE *fptr; + FILE* fptr; - if (add_to_existing) - { - fptr = fopen(cti_name,"wb+"); - if (!fptr) - error("Error opening cti file %s\n", cti_name); - if (cti_read_ECAT6_Main_header (fptr, &mhead) == EXIT_FAILURE) - error("Error reading main header from cti file %s\n", cti_name); - } - else - { - make_ECAT6_Main_header(mhead, filenames[0], *proj_data_ptr->get_proj_data_info_sptr()); - mhead.num_frames = num_frames; - mhead.num_gates = num_gates; - mhead.num_bed_pos = num_bed_poss-1; - //mhead.num_data = num_data; - - fptr = cti_create (cti_name, &mhead); - if (fptr == NULL) - { - warning("conv_to_ecat6: error opening output file %s\n", cti_name); - return EXIT_FAILURE; - } + if (add_to_existing) { + fptr = fopen(cti_name, "wb+"); + if (!fptr) + error("Error opening cti file %s\n", cti_name); + if (cti_read_ECAT6_Main_header(fptr, &mhead) == EXIT_FAILURE) + error("Error reading main header from cti file %s\n", cti_name); + } else { + make_ECAT6_Main_header(mhead, filenames[0], *proj_data_ptr->get_proj_data_info_sptr()); + mhead.num_frames = num_frames; + mhead.num_gates = num_gates; + mhead.num_bed_pos = num_bed_poss - 1; + // mhead.num_data = num_data; + + fptr = cti_create(cti_name, &mhead); + if (fptr == NULL) { + warning("conv_to_ecat6: error opening output file %s\n", cti_name); + return EXIT_FAILURE; } + } size_t frame_num = 1; - while (1) - { + while (1) { size_t current_frame_num, current_bed_num, current_gate_num, current_data_num; - if (interactive) - { - current_frame_num= - ask_num("Frame number ? ", min_frame_num, max_frame_num, min_frame_num); - current_bed_num= - ask_num("Bed number ? ", min_bed_num, max_bed_num, min_bed_num); - current_gate_num= - ask_num("Gate number ? ", min_gate_num, max_gate_num, min_gate_num); - current_data_num= - ask_num("Data number ? ",0,7, 0); - } - else - { - current_frame_num = frame_num; - current_bed_num = min_bed_num; - current_gate_num = min_gate_num; - current_data_num = min_data_num; - } - if (ProjData_to_ECAT6(fptr, - *proj_data_ptr, - mhead, - current_frame_num, current_gate_num, current_data_num, current_bed_num, - its_a_2D_sinogram) - == Succeeded::no) - { + if (interactive) { + current_frame_num = ask_num("Frame number ? ", min_frame_num, max_frame_num, min_frame_num); + current_bed_num = ask_num("Bed number ? ", min_bed_num, max_bed_num, min_bed_num); + current_gate_num = ask_num("Gate number ? ", min_gate_num, max_gate_num, min_gate_num); + current_data_num = ask_num("Data number ? ", 0, 7, 0); + } else { + current_frame_num = frame_num; + current_bed_num = min_bed_num; + current_gate_num = min_gate_num; + current_data_num = min_data_num; + } + if (ProjData_to_ECAT6(fptr, *proj_data_ptr, mhead, current_frame_num, current_gate_num, current_data_num, current_bed_num, + its_a_2D_sinogram) == Succeeded::no) { fclose(fptr); return EXIT_FAILURE; } - if (++frame_num > filenames.size()) - { + if (++frame_num > filenames.size()) { fclose(fptr); return EXIT_SUCCESS; } - cerr << "Reading " << filenames[frame_num-1] << endl; - proj_data_ptr = - ProjData::read_from_file(filenames[frame_num-1]); + cerr << "Reading " << filenames[frame_num - 1] << endl; + proj_data_ptr = ProjData::read_from_file(filenames[frame_num - 1]); } - } + } } - - diff --git a/src/utilities/ecat/conv_to_ecat7.cxx b/src/utilities/ecat/conv_to_ecat7.cxx index 24ae587a4a..2b0e950d48 100644 --- a/src/utilities/ecat/conv_to_ecat7.cxx +++ b/src/utilities/ecat/conv_to_ecat7.cxx @@ -1,14 +1,14 @@ -/*! +/*! \file \ingroup ECAT_utilities -\brief Conversion from interfile (or any format that we can read) +\brief Conversion from interfile (or any format that we can read) to ECAT 7 cti (image and sinogram data) \author Kris Thielemans \author PARAPET project -This programme is used to convert image or projection data into CTI ECAT 7 data (input -can be any format currently supported by the library). It normally should be run as +This programme is used to convert image or projection data into CTI ECAT 7 data (input +can be any format currently supported by the library). It normally should be run as follows
      conv_to_ecat7 output_ECAT7_name input_filename1 [input_filename2 ...] scanner_name
       
      @@ -19,11 +19,11 @@ follows
      conv_to_ecat7 -a output_ECAT7_name  input_filename1 [input_filename2 ...]
       
      (for sinogram-attenuation data)
      -If there are no command line parameters, the user is asked for the filenames and options -instead. The data will be assigned a frame number in the +If there are no command line parameters, the user is asked for the filenames and options +instead. The data will be assigned a frame number in the order that they occur on the command line.
      -See buildblock/Scanner.cxx for supported scanner names, but examples are ECAT 953, -ART, Advance. ECAT HR+, etc. If the scanner_name contains a space, the scanner name has to +See buildblock/Scanner.cxx for supported scanner names, but examples are ECAT 953, +ART, Advance. ECAT HR+, etc. If the scanner_name contains a space, the scanner name has to be surrounded by double quotes (") when used as a command line argument. */ /* @@ -68,242 +68,195 @@ USING_NAMESPACE_STIR USING_NAMESPACE_ECAT USING_NAMESPACE_ECAT7 - - - -void usage() { - - cerr << "\nConversion from data to ECAT7 CTI.\n" - << "Multiples files can be written to a single ECAT 7 file.\n" - << "The data will be assigned a frame number in the " - << "order that they occur on the command line.\n\n" - << "Usage: 3 possible forms depending on data type\n" - << "For sinogram data:\n" - << "\tconv_to_ecat7 -s [-n] output_ECAT7_name orig_filename1 [orig_filename2 ...]\n" - << "For sinogram-attenuation data:\n" - << "\tconv_to_ecat7 -a [-n] output_ECAT7_name orig_filename1 [orig_filename2 ...]\n" - << "For image data:\n" - << "\tconv_to_ecat7 output_ECAT7_name orig_filename1 [orig_filename2 ...] scanner_name\n" - << "scanner_name has to be recognised by the Scanner class\n" - << "Examples are : \"ECAT 953\", \"RPT\" etc.\n" - << "(the quotes are required when used as a command line argument)\n\n"; +void +usage() { + + cerr << "\nConversion from data to ECAT7 CTI.\n" + << "Multiples files can be written to a single ECAT 7 file.\n" + << "The data will be assigned a frame number in the " + << "order that they occur on the command line.\n\n" + << "Usage: 3 possible forms depending on data type\n" + << "For sinogram data:\n" + << "\tconv_to_ecat7 -s [-n] output_ECAT7_name orig_filename1 [orig_filename2 ...]\n" + << "For sinogram-attenuation data:\n" + << "\tconv_to_ecat7 -a [-n] output_ECAT7_name orig_filename1 [orig_filename2 ...]\n" + << "For image data:\n" + << "\tconv_to_ecat7 output_ECAT7_name orig_filename1 [orig_filename2 ...] scanner_name\n" + << "scanner_name has to be recognised by the Scanner class\n" + << "Examples are : \"ECAT 953\", \"RPT\" etc.\n" + << "(the quotes are required when used as a command line argument)\n\n"; } - - - - - -int main(int argc, char *argv[]) -{ +int +main(int argc, char* argv[]) { char cti_name[1000], scanner_name[1000] = ""; vector filenames; bool its_an_image = true; bool write_as_attenuation = false; float scale_factor = 0.0; - - + int arg_index = 1; - + /* Check options - single letters only */ - while ( arg_index < argc && argv[arg_index][0] == '-' ) { - + while (arg_index < argc && argv[arg_index][0] == '-') { + int i = 1; char c; - - while ( (c = argv[arg_index][i]) != '\0' ) { - - switch ( c ) { - + + while ((c = argv[arg_index][i]) != '\0') { + + switch (c) { + case 's': its_an_image = false; break; - + case 'a': its_an_image = false; write_as_attenuation = true; break; - + case 'n': scale_factor = 1.0F; break; - + default: cerr << "Error: Unknown option " << c << " \n\n"; usage(); exit(0); break; - } - + i++; } - + arg_index++; } - - - /* Check number of remaining arguments */ - if ( (its_an_image == false && argc - arg_index >= 1) || argc - arg_index >= 2) { - + if ((its_an_image == false && argc - arg_index >= 1) || argc - arg_index >= 2) { + // Warn about scaling option on it's own. - if ( its_an_image == true && scale_factor != 0.0F ) { + if (its_an_image == true && scale_factor != 0.0F) { cerr << "Warning: option -n has no effect when converting images.\n\n"; } - - + /* Parse remaining arguments */ strcpy(cti_name, argv[arg_index]); arg_index++; - int num_files; - if ( its_an_image ) { - + if (its_an_image) { + for (num_files = argc - arg_index - 1; num_files > 0; --num_files, arg_index++) { filenames.push_back(argv[arg_index]); } - + strcpy(scanner_name, argv[arg_index]); - + } else { - - for (num_files = argc - arg_index; num_files>0; --num_files, arg_index++) { + + for (num_files = argc - arg_index; num_files > 0; --num_files, arg_index++) { filenames.push_back(argv[arg_index]); } } } else { - + usage(); cerr << "I will now ask you the same info interactively...\n\n"; - - its_an_image = ask("Converting images?",true); - + + its_an_image = ask("Converting images?", true); + if (!its_an_image) { - write_as_attenuation = ask("Write as attenuation data?",false); + write_as_attenuation = ask("Write as attenuation data?", false); } else { - if ( ask("Fix scale factor to 1.0?", false) ) { + if (ask("Fix scale factor to 1.0?", false)) { scale_factor = 1.0F; } } - int num_files = ask_num("Number of files",1,10000,1); + int num_files = ask_num("Number of files", 1, 10000, 1); filenames.reserve(num_files); char cur_name[max_filename_length]; - for (; num_files>0; --num_files) - { - ask_filename_with_extension(cur_name,"Name of the input file? ",its_an_image?".hv":".hs"); + for (; num_files > 0; --num_files) { + ask_filename_with_extension(cur_name, "Name of the input file? ", its_an_image ? ".hv" : ".hs"); filenames.push_back(cur_name); } - - ask_filename_with_extension(cti_name,"Name of the ECAT7 file? ", - its_an_image ? ".img" : ".scn"); - - } - + ask_filename_with_extension(cti_name, "Name of the ECAT7 file? ", its_an_image ? ".img" : ".scn"); + } - if (its_an_image) - { + if (its_an_image) { - shared_ptr scanner_ptr( - strlen(scanner_name)==0 ? - Scanner::ask_parameters() : - Scanner::get_scanner_from_name(scanner_name)); + shared_ptr scanner_ptr(strlen(scanner_name) == 0 ? Scanner::ask_parameters() + : Scanner::get_scanner_from_name(scanner_name)); // read first image cerr << "Reading " << filenames[0] << endl; - shared_ptr > density_ptr( - read_from_file >(filenames[0])); - + shared_ptr> density_ptr(read_from_file>(filenames[0])); + Main_header mhead; make_ECAT7_main_header(mhead, *scanner_ptr, filenames[0], *density_ptr); mhead.num_frames = filenames.size(); - mhead.acquisition_type = - mhead.num_frames>1 ? DynamicEmission : StaticEmission; + mhead.acquisition_type = mhead.num_frames > 1 ? DynamicEmission : StaticEmission; - MatrixFile* mptr= matrix_create (cti_name, MAT_CREATE, &mhead); - if (mptr == 0) - { + MatrixFile* mptr = matrix_create(cti_name, MAT_CREATE, &mhead); + if (mptr == 0) { warning(boost::format("conv_to_ecat7: error opening output file %s. Remove first if it exists already") % cti_name); return EXIT_FAILURE; } unsigned int frame_num = 1; - while (1) - { - if (DiscretisedDensity_to_ECAT7(mptr, - *density_ptr, - frame_num) - == Succeeded::no) - { + while (1) { + if (DiscretisedDensity_to_ECAT7(mptr, *density_ptr, frame_num) == Succeeded::no) { matrix_close(mptr); return EXIT_FAILURE; } - if (++frame_num > filenames.size()) - { + if (++frame_num > filenames.size()) { matrix_close(mptr); return EXIT_SUCCESS; } - cerr << "Reading " << filenames[frame_num-1] << endl; - density_ptr = - read_from_file >(filenames[frame_num-1]); + cerr << "Reading " << filenames[frame_num - 1] << endl; + density_ptr = read_from_file>(filenames[frame_num - 1]); } - } - else - { - + } else { + // read first data set cerr << "Reading " << filenames[0] << endl; - shared_ptr proj_data_ptr = - ProjData::read_from_file(filenames[0]); - + shared_ptr proj_data_ptr = ProjData::read_from_file(filenames[0]); + Main_header mhead; // TODO exam_info currently used from the first frame, which means that time frame info is incorrect // better to use DynamicProjData etc. - make_ECAT7_main_header(mhead, filenames[0], - proj_data_ptr->get_exam_info(), - *proj_data_ptr->get_proj_data_info_sptr(), - write_as_attenuation, - NumericType::SHORT); + make_ECAT7_main_header(mhead, filenames[0], proj_data_ptr->get_exam_info(), *proj_data_ptr->get_proj_data_info_sptr(), + write_as_attenuation, NumericType::SHORT); // fix time frame info mhead.num_frames = filenames.size(); - if (!write_as_attenuation) - { - mhead.acquisition_type = - mhead.num_frames>1 ? DynamicEmission : StaticEmission; - } - MatrixFile* mptr= matrix_create (cti_name, MAT_CREATE, &mhead); - if (mptr == 0) - { + if (!write_as_attenuation) { + mhead.acquisition_type = mhead.num_frames > 1 ? DynamicEmission : StaticEmission; + } + MatrixFile* mptr = matrix_create(cti_name, MAT_CREATE, &mhead); + if (mptr == 0) { warning(boost::format("conv_to_ecat7: error opening output file %s. Remove first if it exists already") % cti_name); return EXIT_FAILURE; } unsigned int frame_num = 1; - while (1) - { - if (ProjData_to_ECAT7(mptr, *proj_data_ptr, - frame_num, 1, 0, 0, scale_factor) == Succeeded::no) - { + while (1) { + if (ProjData_to_ECAT7(mptr, *proj_data_ptr, frame_num, 1, 0, 0, scale_factor) == Succeeded::no) { matrix_close(mptr); return EXIT_FAILURE; } - if (++frame_num > filenames.size()) - { + if (++frame_num > filenames.size()) { matrix_close(mptr); return EXIT_SUCCESS; } - cerr << "Reading " << filenames[frame_num-1] << endl; - proj_data_ptr = - ProjData::read_from_file(filenames[frame_num-1]); + cerr << "Reading " << filenames[frame_num - 1] << endl; + proj_data_ptr = ProjData::read_from_file(filenames[frame_num - 1]); } - } + } } - diff --git a/src/utilities/ecat/convecat6_if.cxx b/src/utilities/ecat/convecat6_if.cxx index 417bc4b464..902db1baa2 100644 --- a/src/utilities/ecat/convecat6_if.cxx +++ b/src/utilities/ecat/convecat6_if.cxx @@ -19,7 +19,7 @@ See STIR/LICENSE.txt for details */ -/*! +/*! \file \ingroup ECAT_utilities \brief Conversion from ECAT 6 cti to interfile (image and sinogram data) @@ -28,22 +28,22 @@ \author Sanida Mustafovic \author PARAPET project - \par Usage: + \par Usage:
      convecat6_if [output_file_name_without_extension cti_data_file_name [scanner_name]]
         
      The optional \a scanner_name can be used to force to a particular scanner (ignoring the system_type in the main header). \a scanner_name has to be recognised by the Scanner class. - Examples are : ECAT 953, RPT, ECAT HR+, Advance etc. If the \a scanner_name - contains a space, the scanner name has to be surrounded by double quotes + Examples are : ECAT 953, RPT, ECAT HR+, Advance etc. If the \a scanner_name + contains a space, the scanner name has to be surrounded by double quotes (") when used as a command line argument.
      - The program asks if all frames should be written or not. If so, all + The program asks if all frames should be written or not. If so, all sinograms/images are converted for a fixed 'data' number. For each data set, - a suffix is added to the output_filename of the form "_f#g#b#d#" where the # + a suffix is added to the output_filename of the form "_f#g#b#d#" where the # are replaced by the corresponding number of the frame, gate, bed, data. - \warning CTI ECAT files seem to have a peculiarity that frames and gates are + \warning CTI ECAT files seem to have a peculiarity that frames and gates are numbered from 1, while bed positions are numbered from 0. Similarly, the number of bed positions in the main header seems to be 1 less than the actual number present. This is at least the case for single bed studies. If this is not true @@ -59,7 +59,6 @@ we would have to extend OutputFileFormat to handle projection data. */ - #include "stir/utilities.h" #include "stir/IO/InterfileOutputFileFormat.h" #include "stir/shared_ptr.h" @@ -81,160 +80,127 @@ using std::min; using std::max; #endif - - USING_NAMESPACE_STIR USING_NAMESPACE_ECAT USING_NAMESPACE_ECAT6 int -main(int argc, char *argv[]) -{ +main(int argc, char* argv[]) { std::string cti_name; std::string out_name; - char * scanner_name_ptr = 0; - FILE *cti_fptr; - - if(argc==3 || argc==4) - { - cti_name = argv[2]; - out_name = argv[1]; - if (argc>3) - scanner_name_ptr = argv[3]; - } - else - { - cerr<<"\nConversion from ECAT6 CTI data to interfile.\n"; - cerr<<"Usage: convecat6_if [output_file_name_without_extension cti_data_file_name [scanner_name]]\n" - <<"The optional scanner_name can be used to force to a particular scanner" - <<" (ignoring the system_type in the main header).\n" - << "scanner_name has to be recognised by the Scanner class\n" - << "Examples are : \"ECAT 953\", \"RPT\" etc.\n" - << "(the quotes are required when used as a command line argument)\n" - << endl; - - if (argc!=1) - exit(EXIT_FAILURE); - - out_name = ask_filename_with_extension("Name of the output file? (.hv/.hs and .v/.s will be added)",""); - cti_name = ask_filename_with_extension("Name of the input data file? ",".scn"); - - } - + char* scanner_name_ptr = 0; + FILE* cti_fptr; + + if (argc == 3 || argc == 4) { + cti_name = argv[2]; + out_name = argv[1]; + if (argc > 3) + scanner_name_ptr = argv[3]; + } else { + cerr << "\nConversion from ECAT6 CTI data to interfile.\n"; + cerr << "Usage: convecat6_if [output_file_name_without_extension cti_data_file_name [scanner_name]]\n" + << "The optional scanner_name can be used to force to a particular scanner" + << " (ignoring the system_type in the main header).\n" + << "scanner_name has to be recognised by the Scanner class\n" + << "Examples are : \"ECAT 953\", \"RPT\" etc.\n" + << "(the quotes are required when used as a command line argument)\n" + << endl; + + if (argc != 1) + exit(EXIT_FAILURE); + + out_name = ask_filename_with_extension("Name of the output file? (.hv/.hs and .v/.s will be added)", ""); + cti_name = ask_filename_with_extension("Name of the input data file? ", ".scn"); + } // open input file, read main header - cti_fptr=fopen(cti_name.c_str(), "rb"); - if(!cti_fptr) { - error("Error opening input file: %s",cti_name.c_str()); + cti_fptr = fopen(cti_name.c_str(), "rb"); + if (!cti_fptr) { + error("Error opening input file: %s", cti_name.c_str()); } ECAT6_Main_header mhead; - if(cti_read_ECAT6_Main_header(cti_fptr, &mhead)!=EXIT_SUCCESS) { - error("Unable to read main header in file: %s",cti_name.c_str()); + if (cti_read_ECAT6_Main_header(cti_fptr, &mhead) != EXIT_SUCCESS) { + error("Unable to read main header in file: %s", cti_name.c_str()); } - if (scanner_name_ptr != 0) - { + if (scanner_name_ptr != 0) { // force scanner - shared_ptr scanner_ptr( - Scanner::get_scanner_from_name(scanner_name_ptr)); + shared_ptr scanner_ptr(Scanner::get_scanner_from_name(scanner_name_ptr)); mhead.system_type = find_ECAT_system_type(*scanner_ptr); } // funnily enough, num_bed_pos seems to be offset with 1 - // (That's to say, in a singled bed study, num_bed_pos==0) + // (That's to say, in a singled bed study, num_bed_pos==0) // TODO maybe not true for multi-bed studies - const int num_frames = max(static_cast( mhead.num_frames),1); - const int num_bed_poss = max(static_cast( mhead.num_bed_pos) + 1,1); - const int num_gates = max(static_cast( mhead.num_gates),1); - + const int num_frames = max(static_cast(mhead.num_frames), 1); + const int num_bed_poss = max(static_cast(mhead.num_bed_pos) + 1, 1); + const int num_gates = max(static_cast(mhead.num_gates), 1); int min_frame_num = 1; int max_frame_num = num_frames; int min_bed_num = 0; - int max_bed_num = num_bed_poss-1; + int max_bed_num = num_bed_poss - 1; int min_gate_num = 1; int max_gate_num = num_gates; int data_num = 0; bool do_all = true; - - if (ask("Attempt all data-sets (Y) or single data-set (N)", true)) - { - data_num=ask_num("Data number ? ",0,8, 0); - - cout << "Processing frames " << min_frame_num << '-' << max_frame_num - << ", gates " << min_gate_num << '-' << max_gate_num - << ", bed positions " << min_bed_num << '-' << max_bed_num - << endl; - } - else - { - do_all = false; - min_frame_num= max_frame_num= - ask_num("Frame number ? ", min_frame_num, max_frame_num, min_frame_num); - min_bed_num= max_bed_num= - ask_num("Bed number ? ", min_bed_num, max_bed_num, min_bed_num); - min_gate_num= max_gate_num= - ask_num("Gate number ? ", min_gate_num, max_gate_num, min_gate_num); - data_num= - ask_num("Data number ? ",0,7, 0); - } - - switch(mhead.file_type) - { - case matImageFile: - { - char *new_out_filename = new char[out_name.size()+100]; - for (int frame_num=min_frame_num; frame_num<=max_frame_num;++frame_num) - for (int bed_num=min_bed_num; bed_num<=max_bed_num;++bed_num) - for (int gate_num=min_gate_num; gate_num<=max_gate_num;++gate_num) - { - strcpy(new_out_filename, out_name.c_str()); - if (do_all) - sprintf(new_out_filename+strlen(new_out_filename), "_f%dg%db%dd%d", - frame_num, gate_num, bed_num, data_num); - cout << "Writing " << new_out_filename << endl; - shared_ptr > image_ptr( - ECAT6_to_VoxelsOnCartesianGrid(frame_num, gate_num, data_num, bed_num, - cti_fptr, mhead)); - InterfileOutputFileFormat output_file_format; - output_file_format.write_to_file(new_out_filename,*image_ptr); - } - delete[] new_out_filename; - break; - } - case matScanFile: - case matAttenFile: - case matNormFile: - { - const int max_ring_diff= - ask_num("Max ring diff to store (-1 == num_rings-1)",-1,100,-1); - - const bool arccorrected = - ask("Consider the data to be arc-corrected?",false); - - char *new_out_filename = new char[out_name.size()+100]; - for (int frame_num=min_frame_num; frame_num<=max_frame_num;++frame_num) - for (int bed_num=min_bed_num; bed_num<=max_bed_num;++bed_num) - for (int gate_num=min_gate_num; gate_num<=max_gate_num;++gate_num) - { - strcpy(new_out_filename, out_name.c_str()); - if (do_all) - sprintf(new_out_filename+strlen(new_out_filename), "_f%dg%db%dd%d", - frame_num, gate_num, bed_num, data_num); - cout << "Writing " << new_out_filename << endl; - ECAT6_to_PDFS(frame_num, gate_num, data_num, bed_num, - max_ring_diff, arccorrected, - new_out_filename, cti_fptr, mhead); - } - delete[] new_out_filename; - break; - } - default: - { - error("\nSupporting only image, scan, atten or norm file type at the moment. Sorry.\n"); - } - } + + if (ask("Attempt all data-sets (Y) or single data-set (N)", true)) { + data_num = ask_num("Data number ? ", 0, 8, 0); + + cout << "Processing frames " << min_frame_num << '-' << max_frame_num << ", gates " << min_gate_num << '-' << max_gate_num + << ", bed positions " << min_bed_num << '-' << max_bed_num << endl; + } else { + do_all = false; + min_frame_num = max_frame_num = ask_num("Frame number ? ", min_frame_num, max_frame_num, min_frame_num); + min_bed_num = max_bed_num = ask_num("Bed number ? ", min_bed_num, max_bed_num, min_bed_num); + min_gate_num = max_gate_num = ask_num("Gate number ? ", min_gate_num, max_gate_num, min_gate_num); + data_num = ask_num("Data number ? ", 0, 7, 0); + } + + switch (mhead.file_type) { + case matImageFile: { + char* new_out_filename = new char[out_name.size() + 100]; + for (int frame_num = min_frame_num; frame_num <= max_frame_num; ++frame_num) + for (int bed_num = min_bed_num; bed_num <= max_bed_num; ++bed_num) + for (int gate_num = min_gate_num; gate_num <= max_gate_num; ++gate_num) { + strcpy(new_out_filename, out_name.c_str()); + if (do_all) + sprintf(new_out_filename + strlen(new_out_filename), "_f%dg%db%dd%d", frame_num, gate_num, bed_num, data_num); + cout << "Writing " << new_out_filename << endl; + shared_ptr> image_ptr( + ECAT6_to_VoxelsOnCartesianGrid(frame_num, gate_num, data_num, bed_num, cti_fptr, mhead)); + InterfileOutputFileFormat output_file_format; + output_file_format.write_to_file(new_out_filename, *image_ptr); + } + delete[] new_out_filename; + break; + } + case matScanFile: + case matAttenFile: + case matNormFile: { + const int max_ring_diff = ask_num("Max ring diff to store (-1 == num_rings-1)", -1, 100, -1); + + const bool arccorrected = ask("Consider the data to be arc-corrected?", false); + + char* new_out_filename = new char[out_name.size() + 100]; + for (int frame_num = min_frame_num; frame_num <= max_frame_num; ++frame_num) + for (int bed_num = min_bed_num; bed_num <= max_bed_num; ++bed_num) + for (int gate_num = min_gate_num; gate_num <= max_gate_num; ++gate_num) { + strcpy(new_out_filename, out_name.c_str()); + if (do_all) + sprintf(new_out_filename + strlen(new_out_filename), "_f%dg%db%dd%d", frame_num, gate_num, bed_num, data_num); + cout << "Writing " << new_out_filename << endl; + ECAT6_to_PDFS(frame_num, gate_num, data_num, bed_num, max_ring_diff, arccorrected, new_out_filename, cti_fptr, mhead); + } + delete[] new_out_filename; + break; + } + default: { + error("\nSupporting only image, scan, atten or norm file type at the moment. Sorry.\n"); + } + } fclose(cti_fptr); - + return EXIT_SUCCESS; } diff --git a/src/utilities/ecat/copy_ecat7_header.cxx b/src/utilities/ecat/copy_ecat7_header.cxx index b1099b79d9..132400a773 100644 --- a/src/utilities/ecat/copy_ecat7_header.cxx +++ b/src/utilities/ecat/copy_ecat7_header.cxx @@ -18,7 +18,7 @@ See STIR/LICENSE.txt for details */ -/*! +/*! \file \ingroup utilities \ingroup ECAT @@ -40,7 +40,6 @@ To copy a subheader (but keeping essential info) \author Kris Thielemans */ - #include "stir/Succeeded.h" #include "stir/utilities.h" #include "stir/IO/stir_ecat7.h" @@ -61,42 +60,39 @@ using std::ostream; USING_NAMESPACE_STIR USING_NAMESPACE_ECAT USING_NAMESPACE_ECAT7 -#define STIR_DO_IT(x) out_sh.x =in_sh.x; +#define STIR_DO_IT(x) out_sh.x = in_sh.x; -void copy_subheader(Image_subheader& out_sh, const Image_subheader& in_sh) -{ +void +copy_subheader(Image_subheader& out_sh, const Image_subheader& in_sh) { /* - short data_type; - short num_dimensions; - short x_dimension; - short y_dimension; - short z_dimension; - short align_0; - float z_offset; - float x_offset; - float y_offset; - float scale_factor; - short image_min; - short image_max; - float x_pixel_size; - float y_pixel_size; - float z_pixel_size; - short align_1; - short align_2; - short align_3; + short data_type; + short num_dimensions; + short x_dimension; + short y_dimension; + short z_dimension; + short align_0; + float z_offset; + float x_offset; + float y_offset; + float scale_factor; + short image_min; + short image_max; + float x_pixel_size; + float y_pixel_size; + float z_pixel_size; + short align_1; + short align_2; + short align_3; */ - if (out_sh.recon_zoom==0) - { - warning("Copying recon_zoom from template as original zoom is 0. I didn't check the pixel sizes though"); - STIR_DO_IT(recon_zoom); - } - else - { - if (fabs(in_sh.recon_zoom - out_sh.recon_zoom)<.05) - warning("recon_zoom field in template (%g) and output (%g) is different.\n" - "Keeping original value for zoom (%g) (also keeping the original voxel sizes)", - out_sh.recon_zoom, in_sh.recon_zoom, in_sh.recon_zoom); - } + if (out_sh.recon_zoom == 0) { + warning("Copying recon_zoom from template as original zoom is 0. I didn't check the pixel sizes though"); + STIR_DO_IT(recon_zoom); + } else { + if (fabs(in_sh.recon_zoom - out_sh.recon_zoom) < .05) + warning("recon_zoom field in template (%g) and output (%g) is different.\n" + "Keeping original value for zoom (%g) (also keeping the original voxel sizes)", + out_sh.recon_zoom, in_sh.recon_zoom, in_sh.recon_zoom); + } STIR_DO_IT(frame_duration); STIR_DO_IT(frame_start_time); @@ -115,10 +111,10 @@ void copy_subheader(Image_subheader& out_sh, const Image_subheader& in_sh) STIR_DO_IT(filter_order); STIR_DO_IT(filter_scatter_fraction); STIR_DO_IT(filter_scatter_slope); - //STIR_DO_IT(x_resolution); - //STIR_DO_IT(y_resolution); - //STIR_DO_IT(z_resolution); - for (int i=0; i<40; ++i) + // STIR_DO_IT(x_resolution); + // STIR_DO_IT(y_resolution); + // STIR_DO_IT(z_resolution); + for (int i = 0; i < 40; ++i) STIR_DO_IT(annotation[i]); STIR_DO_IT(mt_1_1); STIR_DO_IT(mt_1_2); @@ -145,32 +141,31 @@ void copy_subheader(Image_subheader& out_sh, const Image_subheader& in_sh) STIR_DO_IT(recon_views); } - -void copy_subheader(Image_subheader& out_sh, const Scan3D_subheader& in_sh) -{ +void +copy_subheader(Image_subheader& out_sh, const Scan3D_subheader& in_sh) { warning("Copying only timing and gating info from scan to image subheader\n"); /* - short data_type; - short num_dimensions; - short x_dimension; - short y_dimension; - short z_dimension; - short align_0; - float z_offset; - float x_offset; - float y_offset; - float scale_factor; - short image_min; - short image_max; - float x_pixel_size; - float y_pixel_size; - float z_pixel_size; - short align_1; - float x_resolution; - float y_resolution; - float z_resolution; - short align_2; - short align_3; + short data_type; + short num_dimensions; + short x_dimension; + short y_dimension; + short z_dimension; + short align_0; + float z_offset; + float x_offset; + float y_offset; + float scale_factor; + short image_min; + short image_max; + float x_pixel_size; + float y_pixel_size; + float z_pixel_size; + short align_1; + float x_resolution; + float y_resolution; + float z_resolution; + short align_2; + short align_3; // if (out_sh.recon_zoom==0) { @@ -228,24 +223,24 @@ void copy_subheader(Image_subheader& out_sh, const Scan3D_subheader& in_sh) STIR_DO_IT(num_accepted_beats); } -void copy_subheader(Scan3D_subheader& out_sh, const Scan3D_subheader& in_sh) -{ +void +copy_subheader(Scan3D_subheader& out_sh, const Scan3D_subheader& in_sh) { /* - short data_type; - short num_dimensions; - short num_r_elements; - short num_angles; - short num_z_elements[64]; - short ring_difference; - short storage_order; - short axial_compression; - float x_resolution; - float v_resolution; - float z_resolution; - float w_resolution; + short data_type; + short num_dimensions; + short num_r_elements; + short num_angles; + short num_z_elements[64]; + short ring_difference; + short storage_order; + short axial_compression; + float x_resolution; + float v_resolution; + float z_resolution; + float w_resolution; float scale_factor; - short scan_min; - short scan_max; + short scan_min; + short scan_max; */ STIR_DO_IT(frame_start_time); @@ -262,26 +257,26 @@ void copy_subheader(Scan3D_subheader& out_sh, const Scan3D_subheader& in_sh) STIR_DO_IT(r_wave_offset); STIR_DO_IT(num_accepted_beats); STIR_DO_IT(total_coin_rate); - for (int i=0; i<128; ++i) + for (int i = 0; i < 128; ++i) STIR_DO_IT(uncor_singles[i]); } -void copy_subheader(Attn_subheader& out_sh, const Attn_subheader& in_sh) -{ +void +copy_subheader(Attn_subheader& out_sh, const Attn_subheader& in_sh) { /* - short data_type; - short num_dimensions; - short num_r_elements; - short num_angles; - short num_z_elements; - short ring_difference; - float scale_factor; - short z_elements[64]; + short data_type; + short num_dimensions; + short num_r_elements; + short num_angles; + short num_z_elements; + short ring_difference; + float scale_factor; + short z_elements[64]; */ - //STIR_DO_IT(x_resolution); - //STIR_DO_IT(y_resolution); - //STIR_DO_IT(z_resolution); - //STIR_DO_IT(w_resolution); + // STIR_DO_IT(x_resolution); + // STIR_DO_IT(y_resolution); + // STIR_DO_IT(z_resolution); + // STIR_DO_IT(w_resolution); STIR_DO_IT(x_offset); STIR_DO_IT(y_offset); STIR_DO_IT(x_radius); @@ -294,16 +289,14 @@ void copy_subheader(Attn_subheader& out_sh, const Attn_subheader& in_sh) STIR_DO_IT(skull_thickness); STIR_DO_IT(num_additional_atten_coeff); STIR_DO_IT(edge_finding_threshold); - for (int i=0; i<8; ++i) + for (int i = 0; i < 8; ++i) STIR_DO_IT(additional_atten_coeff[i]); } -void copy_subheader(MatrixData * data_out, - const MatrixData * data_in) -{ - MatrixFile *mptr = data_out->matfile; - switch (mptr->mhptr->file_type) - { +void +copy_subheader(MatrixData* data_out, const MatrixData* data_in) { + MatrixFile* mptr = data_out->matfile; + switch (mptr->mhptr->file_type) { #if 0 case CTISinogram: copy_subheader( @@ -316,334 +309,269 @@ void copy_subheader(MatrixData * data_out, *reinterpret_cast(data_in->shptr)); break; #endif + case PetImage: + case ByteVolume: + case PetVolume: { + switch (data_in->matfile->mhptr->file_type) { + case Byte3dSinogram: + case Short3dSinogram: + case Float3dSinogram: + copy_subheader(*reinterpret_cast(data_out->shptr), *reinterpret_cast(data_in->shptr)); + break; case PetImage: case ByteVolume: case PetVolume: - { - switch(data_in->matfile->mhptr->file_type) - { - case Byte3dSinogram: - case Short3dSinogram: - case Float3dSinogram : - copy_subheader( - *reinterpret_cast(data_out->shptr), - *reinterpret_cast(data_in->shptr)); - break; - case PetImage: - case ByteVolume: - case PetVolume: - copy_subheader( - *reinterpret_cast(data_out->shptr), - *reinterpret_cast(data_in->shptr)); - break; - default: - error("\ncopy_subheader: cannot copy input subheader to subheader of type image\n"); - } - break; - } - case AttenCor: - copy_subheader( - *reinterpret_cast(data_out->shptr), - *reinterpret_cast(data_in->shptr)); - break; - case Byte3dSinogram: - case Short3dSinogram: - case Float3dSinogram : - copy_subheader( - *reinterpret_cast(data_out->shptr), - *reinterpret_cast(data_in->shptr)); + copy_subheader(*reinterpret_cast(data_out->shptr), *reinterpret_cast(data_in->shptr)); break; default: - case ByteProjection: - case PetProjection: - error("copy_subheader: file_type not supported yet\n"); + error("\ncopy_subheader: cannot copy input subheader to subheader of type image\n"); } + break; + } + case AttenCor: + copy_subheader(*reinterpret_cast(data_out->shptr), *reinterpret_cast(data_in->shptr)); + break; + case Byte3dSinogram: + case Short3dSinogram: + case Float3dSinogram: + copy_subheader(*reinterpret_cast(data_out->shptr), *reinterpret_cast(data_in->shptr)); + break; + default: + case ByteProjection: + case PetProjection: + error("copy_subheader: file_type not supported yet\n"); + } } -void update_main_header(Main_header& mh_out, const Main_header& mh_in) - { - Main_header mh = mh_in; - mh.num_planes = mh_out.num_planes; - mh.num_frames = mh_out.num_frames; - mh.num_gates = mh_out.num_gates; - mh.num_bed_pos = mh_out.num_bed_pos; - mh.file_type = mh_out.file_type; - - mh_out = mh; - } +void +update_main_header(Main_header& mh_out, const Main_header& mh_in) { + Main_header mh = mh_in; + mh.num_planes = mh_out.num_planes; + mh.num_frames = mh_out.num_frames; + mh.num_gates = mh_out.num_gates; + mh.num_bed_pos = mh_out.num_bed_pos; + mh.file_type = mh_out.file_type; + + mh_out = mh; +} Succeeded -copy_main_header(MatrixFile * mout_ptr, MatrixFile *min_ptr) - { - update_main_header(*mout_ptr->mhptr, *min_ptr->mhptr); - - if (mat_write_main_header(mout_ptr->fptr, mout_ptr->mhptr)) - return Succeeded::no; - else - return Succeeded::yes; - } +copy_main_header(MatrixFile* mout_ptr, MatrixFile* min_ptr) { + update_main_header(*mout_ptr->mhptr, *min_ptr->mhptr); + + if (mat_write_main_header(mout_ptr->fptr, mout_ptr->mhptr)) + return Succeeded::no; + else + return Succeeded::yes; +} -class ECAT_dataset_spec -{ +class ECAT_dataset_spec { public: -ECAT_dataset_spec(); -ECAT_dataset_spec(const char *const spec); -ECAT_dataset_spec(const string&); -int matnum() const; -int frame_num; -int plane_num; -int gate_num; -int data_num; -int bed_pos_num; -private: + ECAT_dataset_spec(); + ECAT_dataset_spec(const char* const spec); + ECAT_dataset_spec(const string&); + int matnum() const; + int frame_num; + int plane_num; + int gate_num; + int data_num; + int bed_pos_num; -void decode_spec(const char * const spec); -void set_defaults(); +private: + void decode_spec(const char* const spec); + void set_defaults(); }; void -ECAT_dataset_spec:: -set_defaults() -{ - frame_num=1; - gate_num=1; - data_num=0; - bed_pos_num=0; - plane_num=0; +ECAT_dataset_spec::set_defaults() { + frame_num = 1; + gate_num = 1; + data_num = 0; + bed_pos_num = 0; + plane_num = 0; } void -ECAT_dataset_spec:: -decode_spec(const char * const spec) -{ +ECAT_dataset_spec::decode_spec(const char* const spec) { set_defaults(); - sscanf(spec, "%d,%d,%d,%d", - &frame_num, &gate_num, &data_num, &bed_pos_num); + sscanf(spec, "%d,%d,%d,%d", &frame_num, &gate_num, &data_num, &bed_pos_num); } -ECAT_dataset_spec:: -ECAT_dataset_spec() -{ - set_defaults(); -} +ECAT_dataset_spec::ECAT_dataset_spec() { set_defaults(); } -ECAT_dataset_spec:: -ECAT_dataset_spec(const char * const spec) -{ - decode_spec(spec); -} +ECAT_dataset_spec::ECAT_dataset_spec(const char* const spec) { decode_spec(spec); } -ECAT_dataset_spec:: -ECAT_dataset_spec(const string& spec) -{ - decode_spec(spec.c_str()); -} +ECAT_dataset_spec::ECAT_dataset_spec(const string& spec) { decode_spec(spec.c_str()); } -int -ECAT_dataset_spec:: -matnum() const -{ - return mat_numcod (frame_num, 1, gate_num, data_num, bed_pos_num); +int +ECAT_dataset_spec::matnum() const { + return mat_numcod(frame_num, 1, gate_num, data_num, bed_pos_num); } -ostream& operator<<(ostream& s, const ECAT_dataset_spec& spec) -{ - s << spec.frame_num << ',' - << spec.gate_num << ',' - << spec.data_num << ',' - << spec.bed_pos_num; +ostream& +operator<<(ostream& s, const ECAT_dataset_spec& spec) { + s << spec.frame_num << ',' << spec.gate_num << ',' << spec.data_num << ',' << spec.bed_pos_num; return s; } - + Succeeded -mat_write_any_subheader( - MatrixData * data) -{ +mat_write_any_subheader(MatrixData* data) { struct MatDir matdir; - + matrix_errno = MAT_OK; matrix_errtxt[0] = '\0'; - if (data==NULL) - { - matrix_errno = MAT_READ_FROM_NILFPTR; - return Succeeded::no; - } + if (data == NULL) { + matrix_errno = MAT_READ_FROM_NILFPTR; + return Succeeded::no; + } - MatrixFile *mptr = data->matfile; - if (mptr == NULL) - { - matrix_errno = MAT_READ_FROM_NILFPTR ; - return Succeeded::no; - } - else if (mptr->mhptr == NULL) matrix_errno = MAT_NOMHD_FILE_OBJECT ; - else if (data->shptr == NULL) matrix_errno = MAT_NIL_SHPTR ; - if (matrix_errno != MAT_OK) return Succeeded::no ; - - if (matrix_find (mptr, data->matnum, &matdir) != 0) + MatrixFile* mptr = data->matfile; + if (mptr == NULL) { + matrix_errno = MAT_READ_FROM_NILFPTR; + return Succeeded::no; + } else if (mptr->mhptr == NULL) + matrix_errno = MAT_NOMHD_FILE_OBJECT; + else if (data->shptr == NULL) + matrix_errno = MAT_NIL_SHPTR; + if (matrix_errno != MAT_OK) return Succeeded::no; - - const int strtblk = matdir.strtblk; - int return_value=0; - switch (mptr->mhptr->file_type) - { - case CTISinogram: - return_value = mat_write_scan_subheader (mptr->fptr, mptr->mhptr, strtblk, - reinterpret_cast(data->shptr)); - break; - case PetImage: - case ByteVolume: - case PetVolume: - return_value = mat_write_image_subheader (mptr->fptr, mptr->mhptr, strtblk, - reinterpret_cast(data->shptr)); - break; - case AttenCor: - return_value = mat_write_attn_subheader (mptr->fptr, mptr->mhptr, strtblk, - reinterpret_cast(data->shptr)); - break; - case Normalization: - return_value = mat_write_norm_subheader (mptr->fptr, mptr->mhptr, strtblk, - reinterpret_cast(data->shptr)); - break; - case Byte3dSinogram: - case Short3dSinogram: - case Float3dSinogram : - return_value = mat_write_Scan3D_subheader (mptr->fptr, mptr->mhptr, strtblk, - reinterpret_cast(data->shptr)); - - break; - default: - case ByteProjection: - case PetProjection: - case PolarMap: - case Norm3d: - fprintf (stderr, "Not implemented yet\n"); - matrix_errno = MAT_WRITE_ERROR; - } - if (return_value) - { - matrix_perror("error in writing subheader"); - return Succeeded::no; - } - else - return Succeeded::yes; + if (matrix_find(mptr, data->matnum, &matdir) != 0) + return Succeeded::no; + const int strtblk = matdir.strtblk; + + int return_value = 0; + switch (mptr->mhptr->file_type) { + case CTISinogram: + return_value = mat_write_scan_subheader(mptr->fptr, mptr->mhptr, strtblk, reinterpret_cast(data->shptr)); + break; + case PetImage: + case ByteVolume: + case PetVolume: + return_value = mat_write_image_subheader(mptr->fptr, mptr->mhptr, strtblk, reinterpret_cast(data->shptr)); + break; + case AttenCor: + return_value = mat_write_attn_subheader(mptr->fptr, mptr->mhptr, strtblk, reinterpret_cast(data->shptr)); + break; + case Normalization: + return_value = mat_write_norm_subheader(mptr->fptr, mptr->mhptr, strtblk, reinterpret_cast(data->shptr)); + break; + case Byte3dSinogram: + case Short3dSinogram: + case Float3dSinogram: + return_value = mat_write_Scan3D_subheader(mptr->fptr, mptr->mhptr, strtblk, reinterpret_cast(data->shptr)); + + break; + default: + case ByteProjection: + case PetProjection: + case PolarMap: + case Norm3d: + fprintf(stderr, "Not implemented yet\n"); + matrix_errno = MAT_WRITE_ERROR; + } + if (return_value) { + matrix_perror("error in writing subheader"); + return Succeeded::no; + } else + return Succeeded::yes; } +int +main(int argc, char* argv[]) { + bool update = true; // switch between update and straight copy + if (argc > 1 && strcmp(argv[1], "--copy") == 0) { + update = false; + --argc; + ++argv; + } - -int main(int argc, char *argv[]) -{ - - bool update=true; // switch between update and straight copy - if (argc>1 && strcmp(argv[1], "--copy")==0) - { - update=false; - --argc; ++ argv; - } - - if(argc!=3 && argc!=5) - { - cerr<< "\nCopy contents of ECAT7 headers.\n" - << "Usage: \n" - << "To copy the main header:\n" - << "\t" << argv[0] << " [--copy] output_ECAT7_name input_ECAT7_name\n" - << " without --copy, num_planes etc are preserved.\n" - << "or to copy a subheader (but keeping essential info)\n" - << "\t" << argv[0] << " output_ECAT7_name f,g,d,b input_ECAT7_name f,g,d,b\n\n"; - return EXIT_FAILURE; + if (argc != 3 && argc != 5) { + cerr << "\nCopy contents of ECAT7 headers.\n" + << "Usage: \n" + << "To copy the main header:\n" + << "\t" << argv[0] << " [--copy] output_ECAT7_name input_ECAT7_name\n" + << " without --copy, num_planes etc are preserved.\n" + << "or to copy a subheader (but keeping essential info)\n" + << "\t" << argv[0] << " output_ECAT7_name f,g,d,b input_ECAT7_name f,g,d,b\n\n"; + return EXIT_FAILURE; } const string output_name = argv[1]; - const string input_name = argc==3? argv[2] :argv[3]; - - const bool write_main_header = argc==3; + const string input_name = argc == 3 ? argv[2] : argv[3]; + + const bool write_main_header = argc == 3; #ifndef USE_MATRIX_LIB_FOR_MAINHEADER - if (write_main_header) - { - FILE * in_fptr = fopen(input_name.c_str(), "rb"); - if (!in_fptr) - { - error("Error opening '%s' for reading: %s", - input_name.c_str(), strerror(errno)); - } - FILE * out_fptr = fopen(output_name.c_str(), "rb+"); - if (!out_fptr) - { - error("Error opening '%s' for reading and writing: %s", - output_name.c_str(), strerror(errno)); - } - Main_header mh_in; - if (mat_read_main_header(in_fptr, &mh_in)!=0) - error("Error reading main header from %s", input_name.c_str()); - if (update) - { - Main_header mh_out; - if (mat_read_main_header(out_fptr, &mh_out)!=0) - error("Error reading main header from %s", output_name.c_str()); - update_main_header(mh_out, mh_in); - if (mat_write_main_header(out_fptr, &mh_out)) - error("Error writing main header to %s", output_name.c_str()); - } - else - { - if (mat_write_main_header(out_fptr, &mh_in)) - error("Error writing main header to %s", output_name.c_str()); - } - fclose(in_fptr); - fclose(out_fptr); - return EXIT_SUCCESS; + if (write_main_header) { + FILE* in_fptr = fopen(input_name.c_str(), "rb"); + if (!in_fptr) { + error("Error opening '%s' for reading: %s", input_name.c_str(), strerror(errno)); + } + FILE* out_fptr = fopen(output_name.c_str(), "rb+"); + if (!out_fptr) { + error("Error opening '%s' for reading and writing: %s", output_name.c_str(), strerror(errno)); } + Main_header mh_in; + if (mat_read_main_header(in_fptr, &mh_in) != 0) + error("Error reading main header from %s", input_name.c_str()); + if (update) { + Main_header mh_out; + if (mat_read_main_header(out_fptr, &mh_out) != 0) + error("Error reading main header from %s", output_name.c_str()); + update_main_header(mh_out, mh_in); + if (mat_write_main_header(out_fptr, &mh_out)) + error("Error writing main header to %s", output_name.c_str()); + } else { + if (mat_write_main_header(out_fptr, &mh_in)) + error("Error writing main header to %s", output_name.c_str()); + } + fclose(in_fptr); + fclose(out_fptr); + return EXIT_SUCCESS; + } #endif - MatrixFile *min_ptr= - matrix_open( input_name.c_str(), MAT_READ_ONLY, MAT_UNKNOWN_FTYPE); + MatrixFile* min_ptr = matrix_open(input_name.c_str(), MAT_READ_ONLY, MAT_UNKNOWN_FTYPE); if (!min_ptr) { matrix_perror(input_name.c_str()); exit(EXIT_FAILURE); } - MatrixFile *mout_ptr= - matrix_open( output_name.c_str(), MAT_OPEN_EXISTING, MAT_UNKNOWN_FTYPE); + MatrixFile* mout_ptr = matrix_open(output_name.c_str(), MAT_OPEN_EXISTING, MAT_UNKNOWN_FTYPE); if (!mout_ptr) { matrix_perror(output_name.c_str()); exit(EXIT_FAILURE); } #ifdef USE_MATRIX_LIB_FOR_MAINHEADER - if (write_main_header) - { - if (copy_main_header(mout_ptr, min_ptr) == Succeeded::no) - return EXIT_FAILURE; - else - return EXIT_SUCCESS; - } + if (write_main_header) { + if (copy_main_header(mout_ptr, min_ptr) == Succeeded::no) + return EXIT_FAILURE; + else + return EXIT_SUCCESS; + } #endif assert(!write_main_header); const ECAT_dataset_spec out_spec(argv[2]); const ECAT_dataset_spec in_spec(argv[4]); - cerr << "Attempting to read in '" << in_spec <<"' and out '" - << out_spec << "'" << endl; - MatrixData * mindata_ptr = - matrix_read(min_ptr, in_spec.matnum(), MAT_SUB_HEADER); - if (mindata_ptr == NULL) - { + cerr << "Attempting to read in '" << in_spec << "' and out '" << out_spec << "'" << endl; + MatrixData* mindata_ptr = matrix_read(min_ptr, in_spec.matnum(), MAT_SUB_HEADER); + if (mindata_ptr == NULL) { matrix_perror("Error reading input subheader"); return EXIT_FAILURE; } - MatrixData * moutdata_ptr = - matrix_read(mout_ptr, out_spec.matnum(), MAT_SUB_HEADER); - if (moutdata_ptr == NULL) - { + MatrixData* moutdata_ptr = matrix_read(mout_ptr, out_spec.matnum(), MAT_SUB_HEADER); + if (moutdata_ptr == NULL) { matrix_perror("Error reading output subheader"); return EXIT_FAILURE; } copy_subheader(moutdata_ptr, mindata_ptr); if (mat_write_any_subheader(moutdata_ptr) == Succeeded::no) - return EXIT_FAILURE; + return EXIT_FAILURE; free_matrix_data(moutdata_ptr); free_matrix_data(mindata_ptr); diff --git a/src/utilities/ecat/ecat_swap_corners.cxx b/src/utilities/ecat/ecat_swap_corners.cxx index 6ad3d1b5a2..14ad701410 100644 --- a/src/utilities/ecat/ecat_swap_corners.cxx +++ b/src/utilities/ecat/ecat_swap_corners.cxx @@ -16,7 +16,7 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - + See STIR/LICENSE.txt for details */ /*! @@ -32,12 +32,12 @@ \par Usage \code - ecat_swap_corners out_name in_name + ecat_swap_corners out_name in_name \endcode \par What does it do? - For some historical reason, CTI scanners store 3D sinograms + For some historical reason, CTI scanners store 3D sinograms sometimes in a 'corner-swapped' mode. What happens is that some corners of the positive and negative segments are interchanged. (As a consequence, segment 0 is never affected).

      @@ -53,15 +53,15 @@ that allows you to find out which mode it is in. For ECAT7 data, the situation is even more confusing. Data acquired - directly in projection data have to be corner-swapped when the + directly in projection data have to be corner-swapped when the acquisition was in 'volume-mode' (i.e. stored by sinograms), but NOT when acquired in 'view-mode' (i.e. stored by view). It seems that bkproj_3D_sun follows this convention by assuming that any ECAT7 projection data stored in 'volume-mode' has to be corner swapped, and when it writes projection data in 'view-mode', it does the corner swapping - for you. - So, although there is strictly speaking no field in the ECAT7 header - concerning corner swapping, it seems that the storage mode field + for you. + So, although there is strictly speaking no field in the ECAT7 header + concerning corner swapping, it seems that the storage mode field determines the corner swapping as well.
      When the data is acquired in listmode, this changes somewhat. @@ -71,26 +71,26 @@ corner-swapping or without. After the acquisition, the listmode data has then to be binned into projection data. It is then up to the binning program to take this corner-swapping into account. This is - easiest to do by generating 'volume-mode' projection data when a - 'volume-mode' when the listmode setup was in 'volume-mode', and + easiest to do by generating 'volume-mode' projection data when a + 'volume-mode' when the listmode setup was in 'volume-mode', and similar for 'view-mode'.
      - If this sounds confusing to you, KT would agree. + If this sounds confusing to you, KT would agree. Here seems to be the best thing to do:

      Do all acquisitions in 'view-mode', set-up your listmode scan - in 'view-mode', bin the data in 'view-mode'. Forget about + in 'view-mode', bin the data in 'view-mode'. Forget about corner-swapping.

      - If you cannot do this, then this utility will corner-swap the - projection data for you. + If you cannot do this, then this utility will corner-swap the + projection data for you. \par Who implemented this and how was it tested? The actual corner swapping code was supplied by Christian Michel, based on code by Larry Byars.
      - KT has tested it by performing a very long cylinder scan in + KT has tested it by performing a very long cylinder scan in 'volume-mode' on the ECAT 966, and looking at the delayeds. The oblique segments had obvious discontinuities in the efficiency patterns. After applyying this utility, these discontinuities appeared. - + \warning This utility does not (and cannot) check for you if the data has to be corner-swapped or not. So, it can do the wrong thing. @@ -111,143 +111,128 @@ using std::swap; USING_NAMESPACE_STIR -static void dets_to_ve( int da, int db, int *v, int *e, int ndets) -{ - int h,x,y,a,b,te; - - h=ndets/2; - x=max(da,db); - y=min(da,db); - a=((x+y+h+1)%ndets)/2; - b=a+h; - te=abs(x-y-h); - if ((ynviews*nmash/2) continue; - nodup = 1; - for (j=0; j0) && (e nviews * nmash / 2) + continue; + nodup = 1; + for (j = 0; j < n; j++) + if (off == list[j]) + nodup = 0; + if (nodup && (e + 1 > 0) && (e < nprojs)) + list[n++] = off; + } + } + *nptr = n; + return (list); } -int main(int argc, char **argv) -{ - - if (argc!=3) - { +int +main(int argc, char** argv) { + + if (argc != 3) { cerr << "Usage: " << argv[0] << " out_name in_name \n"; return EXIT_FAILURE; } - - shared_ptr org_proj_data_ptr = - ProjData::read_from_file(argv[2]); - ProjDataInterfile - new_proj_data(org_proj_data_ptr->get_exam_info_sptr(), - org_proj_data_ptr->get_proj_data_info_sptr()->create_shared_clone(), - argv[1]); - - + + shared_ptr org_proj_data_ptr = ProjData::read_from_file(argv[2]); + ProjDataInterfile new_proj_data(org_proj_data_ptr->get_exam_info_sptr(), + org_proj_data_ptr->get_proj_data_info_sptr()->create_shared_clone(), argv[1]); + const int num_tang_poss = org_proj_data_ptr->get_num_tangential_poss(); const int min_tang_pos_num = org_proj_data_ptr->get_min_tangential_pos_num(); const int min_view_num = org_proj_data_ptr->get_min_view_num(); - const int mash = - org_proj_data_ptr->get_proj_data_info_sptr()->get_scanner_ptr()->get_num_detectors_per_ring() / - org_proj_data_ptr->get_num_views() / 2; + const int mash = org_proj_data_ptr->get_proj_data_info_sptr()->get_scanner_ptr()->get_num_detectors_per_ring() / + org_proj_data_ptr->get_num_views() / 2; cerr << "Mash factor determined from data is " << mash << endl; - int num_swapped = 0; - int *swap_lors = - compute_swap_lors_mashed(org_proj_data_ptr->get_num_tangential_poss(), - org_proj_data_ptr->get_num_views(), - mash, - &num_swapped); + int* swap_lors = compute_swap_lors_mashed(org_proj_data_ptr->get_num_tangential_poss(), org_proj_data_ptr->get_num_views(), + mash, &num_swapped); // first do segment 0 (no swapping) new_proj_data.set_segment(org_proj_data_ptr->get_segment_by_sinogram(0)); // now the rest - for (int segment_num=1; segment_num<=org_proj_data_ptr->get_max_segment_num(); ++segment_num) - { - for (int axial_pos_num=org_proj_data_ptr->get_min_axial_pos_num(segment_num); - axial_pos_num<=org_proj_data_ptr->get_max_axial_pos_num(segment_num); - ++axial_pos_num) - { - Sinogram sino1= - org_proj_data_ptr->get_sinogram(axial_pos_num, segment_num, false); - Sinogram sino2= - org_proj_data_ptr->get_sinogram(axial_pos_num, -segment_num, false); - - for (int i=0; iget_max_segment_num(); ++segment_num) { + for (int axial_pos_num = org_proj_data_ptr->get_min_axial_pos_num(segment_num); + axial_pos_num <= org_proj_data_ptr->get_max_axial_pos_num(segment_num); ++axial_pos_num) { + Sinogram sino1 = org_proj_data_ptr->get_sinogram(axial_pos_num, segment_num, false); + Sinogram sino2 = org_proj_data_ptr->get_sinogram(axial_pos_num, -segment_num, false); + + for (int i = 0; i < num_swapped; i++) { + int offset = swap_lors[i]; + const int tang_pos_num = offset % num_tang_poss + min_tang_pos_num; + const int view_num = offset / num_tang_poss + min_view_num; swap(sino1[view_num][tang_pos_num], sino2[view_num][tang_pos_num]); } - + new_proj_data.set_sinogram(sino1); new_proj_data.set_sinogram(sino2); } } - free (swap_lors); + free(swap_lors); return EXIT_SUCCESS; } diff --git a/src/utilities/ecat/ifheaders_for_ecat7.cxx b/src/utilities/ecat/ifheaders_for_ecat7.cxx index bcebee668f..f4c83be138 100644 --- a/src/utilities/ecat/ifheaders_for_ecat7.cxx +++ b/src/utilities/ecat/ifheaders_for_ecat7.cxx @@ -1,7 +1,7 @@ // // -/*! +/*! \file \ingroup ECAT_utilities \brief Utility to make Interfile headers for ECAT7 data @@ -11,8 +11,8 @@ Usage: $File$ ecat7_filename. - This will attempt to write interfile headers 'pointing into' the ECAT7 file. - + This will attempt to write interfile headers 'pointing into' the ECAT7 file. + A question will be asked if all data sets should be processed, or only a single one. @@ -22,7 +22,7 @@ \see write_basic_interfile_header_for_ecat7() \warning This only works with some CTI file_types. In particular, it does NOT -work with the ECAT6-like files_types, as then there are subheaders 'in' the +work with the ECAT6-like files_types, as then there are subheaders 'in' the datasets. \warning Implementation uses the Louvain la Neuve Ecat library. So, it will @@ -46,8 +46,6 @@ only work on systems where this library works properly. See STIR/LICENSE.txt for details */ - - #include "stir/ProjDataInfo.h" #include "stir/ProjDataFromStream.h" #include "stir/IO/interfile.h" @@ -72,60 +70,50 @@ using std::cerr; using std::endl; #endif - USING_NAMESPACE_STIR USING_NAMESPACE_ECAT USING_NAMESPACE_ECAT7 -int -main( int argc, char **argv) -{ - MatrixFile *mptr; - - if (argc<2) - { - cerr << "usage : "<< argv[0] << " filename\n"; +int +main(int argc, char** argv) { + MatrixFile* mptr; + + if (argc < 2) { + cerr << "usage : " << argv[0] << " filename\n"; exit(EXIT_FAILURE); } - mptr = matrix_open( argv[1], MAT_READ_ONLY, MAT_UNKNOWN_FTYPE); + mptr = matrix_open(argv[1], MAT_READ_ONLY, MAT_UNKNOWN_FTYPE); if (!mptr) { matrix_perror(argv[1]); exit(EXIT_FAILURE); } - - const int num_frames = std::max(static_cast( mptr->mhptr->num_frames),1); + + const int num_frames = std::max(static_cast(mptr->mhptr->num_frames), 1); // funnily enough, num_bed_pos seems to be offset with 1 - // (That's to say, in a singled bed study, num_bed_pos==0) + // (That's to say, in a singled bed study, num_bed_pos==0) // TODO maybe not true for multi-bed studies - const int num_bed_poss = static_cast( mptr->mhptr->num_bed_pos) + 1; - const int num_gates = std::max(static_cast( mptr->mhptr->num_gates),1); + const int num_bed_poss = static_cast(mptr->mhptr->num_bed_pos) + 1; + const int num_gates = std::max(static_cast(mptr->mhptr->num_gates), 1); fclose(mptr->fptr); delete mptr; string interfile_header_filename; - if (ask("Attempt all data-sets (Y) or single data-set (N)", true)) - { - const int data_num=ask_num("Data number ? ",0,8, 0); - - for (int frame_num=1; frame_num<=num_frames;++frame_num) - for (int bed_num=0; bed_num1 && argv[1][0] == '-') - { - if (strcmp(argv[1], "--image") == 0) - { - option = opt_image; --argc; ++argv; break; - } - if (strcmp(argv[1], "--emission") == 0) - { - option = opt_emission; --argc; ++argv; break; - } - if (strcmp(argv[1], "--ACF") == 0) - { - option = opt_ACF; --argc; ++argv; break; - } - else - print_usage_and_exit(prog_name); + while (argc > 1 && argv[1][0] == '-') { + if (strcmp(argv[1], "--image") == 0) { + option = opt_image; + --argc; + ++argv; + break; + } + if (strcmp(argv[1], "--emission") == 0) { + option = opt_emission; + --argc; + ++argv; + break; } - if (argc!=2) + if (strcmp(argv[1], "--ACF") == 0) { + option = opt_ACF; + --argc; + ++argv; + break; + } else + print_usage_and_exit(prog_name); + } + if (argc != 2) print_usage_and_exit(prog_name); - const char * filename = argv[1]; + const char* filename = argv[1]; bool value; - switch (option) - { - case opt_none: value = stir::ecat::ecat7::is_ECAT7_file(filename); break; - case opt_image: value = stir::ecat::ecat7::is_ECAT7_image_file(filename); break; - case opt_emission: value = stir::ecat::ecat7::is_ECAT7_emission_file(filename); break; - case opt_ACF: value = stir::ecat::ecat7::is_ECAT7_attenuation_file(filename); break; - default: value=false; - } + switch (option) { + case opt_none: + value = stir::ecat::ecat7::is_ECAT7_file(filename); + break; + case opt_image: + value = stir::ecat::ecat7::is_ECAT7_image_file(filename); + break; + case opt_emission: + value = stir::ecat::ecat7::is_ECAT7_emission_file(filename); + break; + case opt_ACF: + value = stir::ecat::ecat7::is_ECAT7_attenuation_file(filename); + break; + default: + value = false; + } if (value) std::cout << "yes\n"; diff --git a/src/utilities/ecat/print_ecat_singles_values.cxx b/src/utilities/ecat/print_ecat_singles_values.cxx index e0594fec04..d652df806d 100644 --- a/src/utilities/ecat/print_ecat_singles_values.cxx +++ b/src/utilities/ecat/print_ecat_singles_values.cxx @@ -23,16 +23,13 @@ \author Tim Borgeaud */ - #include "stir/data/SinglesRatesFromECAT7.h" - #include #include #include #include - #ifndef STIR_NO_NAMESPACES using std::cout; using std::cerr; @@ -44,16 +41,12 @@ using std::vector; USING_NAMESPACE_STIR - - - -int -main (int argc, char **argv) -{ +int +main(int argc, char** argv) { vector columns; - // Check arguments. + // Check arguments. // Singles filename + optional bin indices. if (argc < 2) { cerr << "Program to print out values from a singles file.\n\n"; @@ -64,66 +57,56 @@ main (int argc, char **argv) const string ecat7_filename = argv[1]; - for (int arg = 2 ; arg < argc ; ++arg) { + for (int arg = 2; arg < argc; ++arg) { columns.push_back(atoi(argv[arg])); - } - - + } + // Singles file object. ecat::ecat7::SinglesRatesFromECAT7 singles_from_ecat7; - // Read the singles file. singles_from_ecat7.read_singles_from_file(ecat7_filename); - // Get total number of frames int num_frames = singles_from_ecat7.get_num_frames(); - + // Get scanner details and, from these, the number of singles units. - const Scanner *scanner = singles_from_ecat7.get_scanner_ptr(); + const Scanner* scanner = singles_from_ecat7.get_scanner_ptr(); int total_singles_units = scanner->get_num_singles_units(); - - + // If no columns are set. Create a vector with all columns. - if ( columns.size() == 0 ) { - for (int singles_bin = 0 ; singles_bin < total_singles_units ; ++singles_bin) { - columns.push_back(singles_bin); + if (columns.size() == 0) { + for (int singles_bin = 0; singles_bin < total_singles_units; ++singles_bin) { + columns.push_back(singles_bin); } } - // Print columns cout << "# Frame Frame time "; - for (vector::iterator col = columns.begin() ; col < columns.end() ; ++col) { + for (vector::iterator col = columns.begin(); col < columns.end(); ++col) { cout << setw(9) << *col << " "; } cout << "\n"; - // Loop over all frames. - for (int frame = 1 ; frame <= num_frames ; ++frame) { - + for (int frame = 1; frame <= num_frames; ++frame) { + // Ouput frame number, start and end times. - cout << setw(2) << frame << " " - << setw(8) << singles_from_ecat7.get_time_frame_definitions().get_start_time(frame) << " to " - << setw(8) << singles_from_ecat7.get_time_frame_definitions().get_end_time(frame) << " "; + cout << setw(2) << frame << " " << setw(8) << singles_from_ecat7.get_time_frame_definitions().get_start_time(frame) + << " to " << setw(8) << singles_from_ecat7.get_time_frame_definitions().get_end_time(frame) << " "; - for (vector::iterator col = columns.begin() ; col < columns.end() ; ++col) { - - if ( *col >= 0 && *col < total_singles_units ) { + for (vector::iterator col = columns.begin(); col < columns.end(); ++col) { + + if (*col >= 0 && *col < total_singles_units) { float val = singles_from_ecat7.get_singles_rate(*col, frame); - + cout << setw(9) << val << " "; } } - + // Output the end of line. cout << endl; - } - - return EXIT_SUCCESS; } diff --git a/src/utilities/estimate_triple_energy_window_scatter_sinogram.cxx b/src/utilities/estimate_triple_energy_window_scatter_sinogram.cxx index 41efeb5584..2b5bc38266 100644 --- a/src/utilities/estimate_triple_energy_window_scatter_sinogram.cxx +++ b/src/utilities/estimate_triple_energy_window_scatter_sinogram.cxx @@ -24,7 +24,6 @@ \author Daniel Deidda */ - #include "stir/SegmentByView.h" #include "stir/IO/OutputFileFormat.h" #include "stir/IO/read_from_file.h" @@ -41,9 +40,8 @@ #include "stir/IndexRange3D.h" #include "stir/ArrayFilter3DUsingConvolution.h" - -#include -#include +#include +#include #include #include #include @@ -60,165 +58,139 @@ using std::string; using std::vector; #endif - USING_NAMESPACE_STIR -class estimate_TEW_scatter : public KeyParser, ArrayFilter3DUsingConvolution -{ +class estimate_TEW_scatter : public KeyParser, ArrayFilter3DUsingConvolution { public: - estimate_TEW_scatter(); std::string scatter_filename; - std::string lower_filename,upper_filename; + std::string lower_filename, upper_filename; double lower_width, peak_width, upper_width; bool do_smooth; Succeeded compute(); -private: - -// virtual void set_defaults(); +private: + // virtual void set_defaults(); virtual void initialise_keymap(); -// virtual bool post_processing(); + // virtual bool post_processing(); }; void - estimate_TEW_scatter:: -initialise_keymap() -{ +estimate_TEW_scatter::initialise_keymap() { add_start_key(" Estimate_TEW_scatter Parameters"); - add_key("output scatter filename",&scatter_filename); - add_key("lower filename",&lower_filename); - add_key("upper filename",&upper_filename); - add_key("lower width",&lower_width); - add_key("peak width",&peak_width); - add_key("upper width",&upper_width); - add_key("do smooth",&do_smooth); + add_key("output scatter filename", &scatter_filename); + add_key("lower filename", &lower_filename); + add_key("upper filename", &upper_filename); + add_key("lower width", &lower_width); + add_key("peak width", &peak_width); + add_key("upper width", &upper_width); + add_key("do smooth", &do_smooth); add_stop_key("END"); - -} - -estimate_TEW_scatter::estimate_TEW_scatter() -{ - initialise_keymap(); } -int -main(int argc, char **argv) -{ - if(argc<2) - { - cerr<< "Usage: " << argv[0] << "\n\t" - << "[par_file]\n\t" - << " Should contain the following:\n" - << "Estimate_TEW_scatter Parameters:= \n" - << "output scatter filename :=\n" - << "lower filename :=\n" - << "upper filename :=\n" - << "lower width :=\n" - << "peak width :=\n" - << "upper width :=\n" - << "do smooth :=\n" - << "END :=\n"; - exit(EXIT_FAILURE); - } +estimate_TEW_scatter::estimate_TEW_scatter() { initialise_keymap(); } + +int +main(int argc, char** argv) { + if (argc < 2) { + cerr << "Usage: " << argv[0] << "\n\t" + << "[par_file]\n\t" + << " Should contain the following:\n" + << "Estimate_TEW_scatter Parameters:= \n" + << "output scatter filename :=\n" + << "lower filename :=\n" + << "upper filename :=\n" + << "lower width :=\n" + << "peak width :=\n" + << "upper width :=\n" + << "do smooth :=\n" + << "END :=\n"; + exit(EXIT_FAILURE); + } // first process command line options - estimate_TEW_scatter estimate; - - if (argc==0) - { cerr << "No par file on command line\n"; exit(EXIT_FAILURE); } - else{ - if (argv[1]!=0) - { - if (estimate.parse(argv[1]) == false) - exit(EXIT_FAILURE); - } - else - estimate.ask_parameters();} + estimate_TEW_scatter estimate; + + if (argc == 0) { + cerr << "No par file on command line\n"; + exit(EXIT_FAILURE); + } else { + if (argv[1] != 0) { + if (estimate.parse(argv[1]) == false) + exit(EXIT_FAILURE); + } else + estimate.ask_parameters(); + } // find output filename const string output_file_name = estimate.scatter_filename; -// start the main processing -// read windows data - shared_ptr lower_sptr; - shared_ptr upper_sptr; - shared_ptr out_scatter_proj_data_ptr; - - lower_sptr = ProjData::read_from_file(estimate.lower_filename); - upper_sptr = ProjData::read_from_file(estimate.upper_filename); - - - shared_ptr - output_proj_data_info_sptr((*lower_sptr).get_proj_data_info_sptr()->clone()); - - out_scatter_proj_data_ptr.reset(new ProjDataInterfile((*lower_sptr).get_exam_info_sptr(), - output_proj_data_info_sptr, - output_file_name)); - -// if (num_files>1) -// { -// // reset time-frames as we don't really know what's happening with all this -// ExamInfo new_exam_info(out_scatter_proj_data_ptr->get_exam_info()); -// new_exam_info.set_time_frame_definitions(TimeFrameDefinitions()); -// out_scatter_proj_data_ptr->set_exam_info(new_exam_info); -// } - - - // do reading/writing in a loop over segments - for (int segment_num = out_scatter_proj_data_ptr->get_min_segment_num(); - segment_num <= out_scatter_proj_data_ptr->get_max_segment_num(); - ++segment_num) - { - - SegmentByView lower_segment_by_view = - (*lower_sptr).get_segment_by_view(segment_num); - SegmentByView upper_segment_by_view = - (*upper_sptr).get_segment_by_view(segment_num); - SegmentByView scatter_segment_by_view= - (*lower_sptr).get_segment_by_view(segment_num); - SegmentByView filter_lower_segment_by_view = - (*lower_sptr).get_segment_by_view(segment_num); - SegmentByView filter_upper_segment_by_view = - (*upper_sptr).get_segment_by_view(segment_num); - - if(estimate.do_smooth){ - IndexRange3D kernel_size(0,0,-2,2,-2,2); - Array<3,float> kernel(kernel_size); - kernel.fill(1/25.F); - ArrayFilter3DUsingConvolution filter3d(kernel); - filter3d(filter_lower_segment_by_view,lower_segment_by_view); - filter3d(filter_upper_segment_by_view,upper_segment_by_view); - } - - - // construct function object that does the manipulations on each data - float power=1 ; - float add_scalar=0; - float min_threshold = NumericInfo().min_value(); - float max_threshold = NumericInfo().max_value(); - -// The following apply the the TEW method C_{scatter}=(C_{lower}/W_{lower}+C_{upper}/W_{upper})*W_{peak}/2 + // start the main processing + // read windows data + shared_ptr lower_sptr; + shared_ptr upper_sptr; + shared_ptr out_scatter_proj_data_ptr; + + lower_sptr = ProjData::read_from_file(estimate.lower_filename); + upper_sptr = ProjData::read_from_file(estimate.upper_filename); + + shared_ptr output_proj_data_info_sptr((*lower_sptr).get_proj_data_info_sptr()->clone()); + + out_scatter_proj_data_ptr.reset( + new ProjDataInterfile((*lower_sptr).get_exam_info_sptr(), output_proj_data_info_sptr, output_file_name)); + + // if (num_files>1) + // { + // // reset time-frames as we don't really know what's happening with all this + // ExamInfo new_exam_info(out_scatter_proj_data_ptr->get_exam_info()); + // new_exam_info.set_time_frame_definitions(TimeFrameDefinitions()); + // out_scatter_proj_data_ptr->set_exam_info(new_exam_info); + // } + + // do reading/writing in a loop over segments + for (int segment_num = out_scatter_proj_data_ptr->get_min_segment_num(); + segment_num <= out_scatter_proj_data_ptr->get_max_segment_num(); ++segment_num) { + + SegmentByView lower_segment_by_view = (*lower_sptr).get_segment_by_view(segment_num); + SegmentByView upper_segment_by_view = (*upper_sptr).get_segment_by_view(segment_num); + SegmentByView scatter_segment_by_view = (*lower_sptr).get_segment_by_view(segment_num); + SegmentByView filter_lower_segment_by_view = (*lower_sptr).get_segment_by_view(segment_num); + SegmentByView filter_upper_segment_by_view = (*upper_sptr).get_segment_by_view(segment_num); + + if (estimate.do_smooth) { + IndexRange3D kernel_size(0, 0, -2, 2, -2, 2); + Array<3, float> kernel(kernel_size); + kernel.fill(1 / 25.F); + ArrayFilter3DUsingConvolution filter3d(kernel); + filter3d(filter_lower_segment_by_view, lower_segment_by_view); + filter3d(filter_upper_segment_by_view, upper_segment_by_view); + } - pow_times_add divide_by_lower_width(add_scalar, 1/estimate.lower_width,power ,min_threshold ,max_threshold ); - pow_times_add divide_by_upper_width(add_scalar, 1/estimate.upper_width,power ,min_threshold ,max_threshold ); - pow_times_add mult_by_half_peak_width(add_scalar, estimate.peak_width/2,power ,min_threshold ,max_threshold ); + // construct function object that does the manipulations on each data + float power = 1; + float add_scalar = 0; + float min_threshold = NumericInfo().min_value(); + float max_threshold = NumericInfo().max_value(); - in_place_apply_function(filter_lower_segment_by_view, divide_by_lower_width); - in_place_apply_function(filter_upper_segment_by_view, divide_by_upper_width); + // The following apply the the TEW method C_{scatter}=(C_{lower}/W_{lower}+C_{upper}/W_{upper})*W_{peak}/2 - scatter_segment_by_view=filter_lower_segment_by_view; - scatter_segment_by_view+=filter_upper_segment_by_view; + pow_times_add divide_by_lower_width(add_scalar, 1 / estimate.lower_width, power, min_threshold, max_threshold); + pow_times_add divide_by_upper_width(add_scalar, 1 / estimate.upper_width, power, min_threshold, max_threshold); + pow_times_add mult_by_half_peak_width(add_scalar, estimate.peak_width / 2, power, min_threshold, max_threshold); + in_place_apply_function(filter_lower_segment_by_view, divide_by_lower_width); + in_place_apply_function(filter_upper_segment_by_view, divide_by_upper_width); - in_place_apply_function(scatter_segment_by_view, mult_by_half_peak_width); + scatter_segment_by_view = filter_lower_segment_by_view; + scatter_segment_by_view += filter_upper_segment_by_view; + in_place_apply_function(scatter_segment_by_view, mult_by_half_peak_width); - if (!(out_scatter_proj_data_ptr->set_segment(scatter_segment_by_view) == Succeeded::yes)) - warning("Error set_segment %d\n", segment_num); - } + if (!(out_scatter_proj_data_ptr->set_segment(scatter_segment_by_view) == Succeeded::yes)) + warning("Error set_segment %d\n", segment_num); + } - std::cout<<"TEW scatter estimated "< (*.hs)\n"; - exit(EXIT_FAILURE); - } - - char const * const filename = argv[1]; - - shared_ptr s3d = ProjData::read_from_file(filename); - - const bool extract_by_view = - ask_num("Extract as SegmentByView (0) or BySinogram (1)?", 0,1,0)==0; - - for (int segment_num = s3d->get_min_segment_num(); - segment_num <= s3d->get_max_segment_num(); - ++segment_num) - { - std::string output_filename=filename; - replace_extension(output_filename, ""); - output_filename+="seg"; - output_filename+=boost::str(boost::format("%d") % segment_num); - - Bin central_bin(segment_num,0,0,0); - const float m_spacing = s3d->get_proj_data_info_sptr()->get_sampling_in_m(central_bin); - const float s_spacing = s3d->get_proj_data_info_sptr()->get_sampling_in_s(central_bin); - const float m = s3d->get_proj_data_info_sptr()->get_m(central_bin); - const float s = s3d->get_proj_data_info_sptr()->get_s(central_bin); - - if (extract_by_view) - { - SegmentByView segment= s3d->get_segment_by_view(segment_num); - write_basic_interfile(output_filename + "_by_view.hv", - segment, - CartesianCoordinate3D(1.F, m_spacing, s_spacing), - CartesianCoordinate3D(0.F, m, s)); - } - else { - SegmentBySinogram segment = s3d->get_segment_by_sinogram(segment_num); - write_basic_interfile(output_filename + "_by_sino.hv", - segment, - CartesianCoordinate3D(m_spacing, 1.F, s_spacing), - CartesianCoordinate3D(m, 0.F, s)); - } +int +main(int argc, char* argv[]) { + if (argc < 2) { + cerr << "Usage: " << argv[0] << " (*.hs)\n"; + exit(EXIT_FAILURE); + } + + char const* const filename = argv[1]; + + shared_ptr s3d = ProjData::read_from_file(filename); + + const bool extract_by_view = ask_num("Extract as SegmentByView (0) or BySinogram (1)?", 0, 1, 0) == 0; + + for (int segment_num = s3d->get_min_segment_num(); segment_num <= s3d->get_max_segment_num(); ++segment_num) { + std::string output_filename = filename; + replace_extension(output_filename, ""); + output_filename += "seg"; + output_filename += boost::str(boost::format("%d") % segment_num); + + Bin central_bin(segment_num, 0, 0, 0); + const float m_spacing = s3d->get_proj_data_info_sptr()->get_sampling_in_m(central_bin); + const float s_spacing = s3d->get_proj_data_info_sptr()->get_sampling_in_s(central_bin); + const float m = s3d->get_proj_data_info_sptr()->get_m(central_bin); + const float s = s3d->get_proj_data_info_sptr()->get_s(central_bin); + + if (extract_by_view) { + SegmentByView segment = s3d->get_segment_by_view(segment_num); + write_basic_interfile(output_filename + "_by_view.hv", segment, CartesianCoordinate3D(1.F, m_spacing, s_spacing), + CartesianCoordinate3D(0.F, m, s)); + } else { + SegmentBySinogram segment = s3d->get_segment_by_sinogram(segment_num); + write_basic_interfile(output_filename + "_by_sino.hv", segment, CartesianCoordinate3D(m_spacing, 1.F, s_spacing), + CartesianCoordinate3D(m, 0.F, s)); } + } - return EXIT_SUCCESS; + return EXIT_SUCCESS; } diff --git a/src/utilities/extract_single_images_from_dynamic_image.cxx b/src/utilities/extract_single_images_from_dynamic_image.cxx index 531688f8fe..147841b10c 100644 --- a/src/utilities/extract_single_images_from_dynamic_image.cxx +++ b/src/utilities/extract_single_images_from_dynamic_image.cxx @@ -24,7 +24,7 @@ \author Richard Brown \par Usage: - \code + \code extract_single_images_from_dynamic_image output_filename_pattern input_header_filename output_format_parameter_file The output filename should look something like this: dyn_im_%d_output.file_extension, @@ -51,68 +51,68 @@ #include "stir/IO/OutputFileFormat.h" #include "stir/Succeeded.h" -int main(int argc, char *argv[]) -{ - USING_NAMESPACE_STIR - - if (argc != 3 && argc != 4) { - std::cerr << "\nUsage: extract_single_images_from_dynamic_image output_filename_pattern input_header_filename [output_format_parameter_file]\n\n"; - return EXIT_FAILURE; +int +main(int argc, char* argv[]) { + USING_NAMESPACE_STIR + + if (argc != 3 && argc != 4) { + std::cerr << "\nUsage: extract_single_images_from_dynamic_image output_filename_pattern input_header_filename " + "[output_format_parameter_file]\n\n"; + return EXIT_FAILURE; + } + + try { + + // Read images + shared_ptr dyn_im_sptr(read_from_file(argv[2])); + + // Check + if (is_null_ptr(dyn_im_sptr)) + throw std::runtime_error("Failed to read dynamic image (" + std::string(argv[2]) + ")."); + + // Set up the output type + shared_ptr>> output_file_format_sptr; + if (argc == 3) + output_file_format_sptr = OutputFileFormat>::default_sptr(); + else { + KeyParser parser; + parser.add_start_key("OutputFileFormat Parameters"); + parser.add_parsing_key("output file format type", &output_file_format_sptr); + parser.add_stop_key("END"); + std::ifstream in(argv[3]); + if (!parser.parse(in) || is_null_ptr(output_file_format_sptr)) + throw std::runtime_error("Failed to parse output format file (" + std::string(argv[3]) + ")."); } - try { - - // Read images - shared_ptr dyn_im_sptr(read_from_file(argv[2])); - - // Check - if (is_null_ptr(dyn_im_sptr)) - throw std::runtime_error("Failed to read dynamic image (" + std::string(argv[2]) + ")."); - - // Set up the output type - shared_ptr > > output_file_format_sptr; - if (argc == 3) - output_file_format_sptr = OutputFileFormat >::default_sptr(); - else { - KeyParser parser; - parser.add_start_key("OutputFileFormat Parameters"); - parser.add_parsing_key("output file format type", &output_file_format_sptr); - parser.add_stop_key("END"); - std::ifstream in(argv[3]); - if (!parser.parse(in) || is_null_ptr(output_file_format_sptr)) - throw std::runtime_error("Failed to parse output format file (" + std::string(argv[3]) + ")."); - } - - // Loop over each image - for (unsigned i=1; i<=dyn_im_sptr->get_num_time_frames(); ++i) { - - DiscretisedDensity<3,float> &disc = dyn_im_sptr->get_density(i); - - std::string current_filename; - try { - current_filename = boost::str(boost::format(argv[1]) % i); - } catch (std::exception& e) { - error(boost::format("Error using 'output_filename' pattern (which is set to '%1%'). " - "Check syntax for boost::format. Error is:\n%2%") % argv[1] % e.what()); - return EXIT_FAILURE; - } - - // Write to file - const Succeeded success = output_file_format_sptr->write_to_file(current_filename,disc); - if (success == Succeeded::no) - throw std::runtime_error("Failed writing."); - } - - // If all is good, exit - return EXIT_SUCCESS; + // Loop over each image + for (unsigned i = 1; i <= dyn_im_sptr->get_num_time_frames(); ++i) { - // If there was an error - } catch(const std::exception &error) { - std::cerr << "\nHere's the error:\n\t" << error.what() << "\n\n"; - return EXIT_FAILURE; - } catch(...) { + DiscretisedDensity<3, float>& disc = dyn_im_sptr->get_density(i); + + std::string current_filename; + try { + current_filename = boost::str(boost::format(argv[1]) % i); + } catch (std::exception& e) { + error(boost::format("Error using 'output_filename' pattern (which is set to '%1%'). " + "Check syntax for boost::format. Error is:\n%2%") % + argv[1] % e.what()); return EXIT_FAILURE; + } + + // Write to file + const Succeeded success = output_file_format_sptr->write_to_file(current_filename, disc); + if (success == Succeeded::no) + throw std::runtime_error("Failed writing."); } -} + // If all is good, exit + return EXIT_SUCCESS; + // If there was an error + } catch (const std::exception& error) { + std::cerr << "\nHere's the error:\n\t" << error.what() << "\n\n"; + return EXIT_FAILURE; + } catch (...) { + return EXIT_FAILURE; + } +} diff --git a/src/utilities/find_ML_normfactors.cxx b/src/utilities/find_ML_normfactors.cxx index bd15dcfcad..4db836ab36 100644 --- a/src/utilities/find_ML_normfactors.cxx +++ b/src/utilities/find_ML_normfactors.cxx @@ -42,7 +42,6 @@ START_NAMESPACE_STIR - #if 0 // this is a test routine for the code // should really be in a test class @@ -104,77 +103,66 @@ void check_geo_data() #endif - - END_NAMESPACE_STIR -static void print_usage_and_exit(const std::string& program_name) -{ - std::cerr<<"Usage: " << program_name << " [--display | --print-KL | --include-block-timing-model] \\\n" - << " out_filename_prefix measured_data model num_iterations num_eff_iterations\n" - << " set num_iterations to 0 to do only efficiencies\n"; +static void +print_usage_and_exit(const std::string& program_name) { + std::cerr << "Usage: " << program_name << " [--display | --print-KL | --include-block-timing-model] \\\n" + << " out_filename_prefix measured_data model num_iterations num_eff_iterations\n" + << " set num_iterations to 0 to do only efficiencies\n"; exit(EXIT_FAILURE); } - USING_NAMESPACE_STIR -int main(int argc, char **argv) -{ - const char * const program_name = argv[0]; +int +main(int argc, char** argv) { + const char* const program_name = argv[0]; // skip program name --argc; ++argv; - //check_geo_data(); + // check_geo_data(); bool do_display = false; bool do_KL = false; bool do_block = false; // first process command line options - while (argc>0 && argv[0][0]=='-' && argc>=1) - { - if (strcmp(argv[0], "--display")==0) - { - do_display = true; - --argc; ++argv; - } - else if (strcmp(argv[0], "--print-KL")==0) - { - do_KL = true; - --argc; ++argv; - } - else if (strcmp(argv[0], "--include-block-timing-model")==0) - { - do_block = true; - --argc; ++argv; - } - else - print_usage_and_exit(program_name); - } - // go back to previous counts such that we don't have to change code below - ++argc; --argv; - - if (argc!=6) - { + while (argc > 0 && argv[0][0] == '-' && argc >= 1) { + if (strcmp(argv[0], "--display") == 0) { + do_display = true; + --argc; + ++argv; + } else if (strcmp(argv[0], "--print-KL") == 0) { + do_KL = true; + --argc; + ++argv; + } else if (strcmp(argv[0], "--include-block-timing-model") == 0) { + do_block = true; + --argc; + ++argv; + } else print_usage_and_exit(program_name); - } + } + // go back to previous counts such that we don't have to change code below + ++argc; + --argv; + + if (argc != 6) { + print_usage_and_exit(program_name); + } const int num_eff_iterations = atoi(argv[5]); const int num_iterations = atoi(argv[4]); shared_ptr model_data = ProjData::read_from_file(argv[3]); shared_ptr measured_data = ProjData::read_from_file(argv[2]); const std::string out_filename_prefix = argv[1]; - /* const int num_rings = + /* const int num_rings = measured_data->get_proj_data_info_sptr()->get_scanner_ptr()->get_num_rings(); */ - const int num_detectors = - measured_data->get_proj_data_info_sptr()->get_scanner_ptr()->get_num_detectors_per_ring(); - const int num_crystals_per_block = - measured_data->get_proj_data_info_sptr()->get_scanner_ptr()-> - get_num_transaxial_crystals_per_block(); - const int num_blocks = - measured_data->get_proj_data_info_sptr()->get_scanner_ptr()-> - get_num_transaxial_blocks(); + const int num_detectors = measured_data->get_proj_data_info_sptr()->get_scanner_ptr()->get_num_detectors_per_ring(); + const int num_crystals_per_block = + measured_data->get_proj_data_info_sptr()->get_scanner_ptr()->get_num_transaxial_crystals_per_block(); + const int num_blocks = measured_data->get_proj_data_info_sptr()->get_scanner_ptr()->get_num_transaxial_blocks(); CPUTimer timer; timer.start(); @@ -182,174 +170,156 @@ int main(int argc, char **argv) const int segment_num = 0; DetPairData det_pair_data; DetPairData model_det_pair_data; - Array<1,float> data_fan_sums(num_detectors); - Array<1,float> efficiencies(num_detectors); - assert(num_crystals_per_block%2 == 0); - GeoData measured_geo_data(IndexRange2D(num_crystals_per_block/2, num_detectors)); - GeoData norm_geo_data(IndexRange2D(num_crystals_per_block/2, num_detectors)); + Array<1, float> data_fan_sums(num_detectors); + Array<1, float> efficiencies(num_detectors); + assert(num_crystals_per_block % 2 == 0); + GeoData measured_geo_data(IndexRange2D(num_crystals_per_block / 2, num_detectors)); + GeoData norm_geo_data(IndexRange2D(num_crystals_per_block / 2, num_detectors)); BlockData measured_block_data(IndexRange2D(num_blocks, num_blocks)); BlockData norm_block_data(IndexRange2D(num_blocks, num_blocks)); for (int ax_pos_num = measured_data->get_min_axial_pos_num(segment_num); - ax_pos_num <= measured_data->get_max_axial_pos_num(segment_num); - ++ax_pos_num) + ax_pos_num <= measured_data->get_max_axial_pos_num(segment_num); ++ax_pos_num) { + // next could be local if KL is not computed below + DetPairData measured_det_pair_data; + float threshold_for_KL; + // compute factors dependent on the data { - // next could be local if KL is not computed below - DetPairData measured_det_pair_data; - float threshold_for_KL; - // compute factors dependent on the data - { - make_det_pair_data(measured_det_pair_data, *measured_data, segment_num, ax_pos_num); - threshold_for_KL = measured_det_pair_data.find_max()/100000.F; - std::cerr << "ax_pos " << ax_pos_num << std::endl; - //display(measured_det_pair_data, "measured data"); - - make_fan_sum_data(data_fan_sums, measured_det_pair_data); - make_geo_data(measured_geo_data, measured_det_pair_data); - make_block_data(measured_block_data, measured_det_pair_data); - if (do_display) - display(measured_block_data, "raw block data from measurements"); - /*{ - char *out_filename = new char[20]; - sprintf(out_filename, "%s_%d.out", - "fan", ax_pos_num); - std::ofstream out(out_filename); - out << data_fan_sums; - delete[] out_filename; - } - */ - } + make_det_pair_data(measured_det_pair_data, *measured_data, segment_num, ax_pos_num); + threshold_for_KL = measured_det_pair_data.find_max() / 100000.F; + std::cerr << "ax_pos " << ax_pos_num << std::endl; + // display(measured_det_pair_data, "measured data"); - make_det_pair_data(model_det_pair_data, *model_data, segment_num, ax_pos_num); - //display(model_det_pair_data, "model"); + make_fan_sum_data(data_fan_sums, measured_det_pair_data); + make_geo_data(measured_geo_data, measured_det_pair_data); + make_block_data(measured_block_data, measured_det_pair_data); + if (do_display) + display(measured_block_data, "raw block data from measurements"); + /*{ + char *out_filename = new char[20]; + sprintf(out_filename, "%s_%d.out", + "fan", ax_pos_num); + std::ofstream out(out_filename); + out << data_fan_sums; + delete[] out_filename; + } + */ + } - for (int iter_num = 1; iter_num<=std::max(num_iterations, 1); ++iter_num) - { - if (iter_num== 1) - { - efficiencies.fill(sqrt(data_fan_sums.sum()/model_det_pair_data.sum())); - norm_geo_data.fill(1); - norm_block_data.fill(1); - } - // efficiencies - { - det_pair_data = model_det_pair_data; - apply_geo_norm(det_pair_data, norm_geo_data); - apply_block_norm(det_pair_data, norm_block_data); - if (do_display) - display(det_pair_data, "model*geo*block"); - for (int eff_iter_num = 1; eff_iter_num<=num_eff_iterations; ++eff_iter_num) - { - iterate_efficiencies(efficiencies, data_fan_sums, det_pair_data); - { - char *out_filename = new char[out_filename_prefix.size() + 30]; - sprintf(out_filename, "%s_%s_%d_%d_%d.out", - out_filename_prefix.c_str(), "eff", ax_pos_num, iter_num, eff_iter_num); - std::ofstream out(out_filename); - out << efficiencies; - delete[] out_filename; - } - if (do_KL) - { - DetPairData model_times_norm = det_pair_data; - apply_efficiencies(model_times_norm, efficiencies); - if (do_display) - display( model_times_norm, "model_times_norm"); - //std::cerr << "model_times_norm min max: " << model_times_norm.find_min() << ',' << model_times_norm.find_max() << std::endl; + make_det_pair_data(model_det_pair_data, *model_data, segment_num, ax_pos_num); + // display(model_det_pair_data, "model"); - std::cerr << "KL " << KL(measured_det_pair_data, model_times_norm, threshold_for_KL) << std::endl; - } - if (do_display) - { - DetPairData norm = det_pair_data; - norm.fill(1); - apply_efficiencies(norm, efficiencies); - display(norm, "eff norm"); - } - - } - } - if (num_iterations==0) - break; - // geo norm - { - det_pair_data = model_det_pair_data; - apply_efficiencies(det_pair_data, efficiencies); - apply_block_norm(det_pair_data, norm_block_data); - iterate_geo_norm(norm_geo_data, measured_geo_data, det_pair_data); - { // check - for (int a=0; a0 && argv[0][0]=='-' && argc>=1) - { - if (strcmp(argv[0], "--display")==0) - { - do_display = true; - --argc; ++argv; - } - else if (strcmp(argv[0], "--print-KL")==0) - { - do_KL = true; - --argc; ++argv; - } - else if (strcmp(argv[0], "--include-geometric-model")==0) - { - do_geo = true; - --argc; ++argv; - } - else if (strcmp(argv[0], "--include-block-timing-model")==0) - { - do_block = true; - --argc; ++argv; - } - else - print_usage_and_exit(program_name); - } - // go back to previous counts such that we don't have to change code below - ++argc; --argv; - - //check_geo_data(); - if (argc!=6) - { + while (argc > 0 && argv[0][0] == '-' && argc >= 1) { + if (strcmp(argv[0], "--display") == 0) { + do_display = true; + --argc; + ++argv; + } else if (strcmp(argv[0], "--print-KL") == 0) { + do_KL = true; + --argc; + ++argv; + } else if (strcmp(argv[0], "--include-geometric-model") == 0) { + do_geo = true; + --argc; + ++argv; + } else if (strcmp(argv[0], "--include-block-timing-model") == 0) { + do_block = true; + --argc; + ++argv; + } else print_usage_and_exit(program_name); - } + } + // go back to previous counts such that we don't have to change code below + ++argc; + --argv; + + // check_geo_data(); + if (argc != 6) { + print_usage_and_exit(program_name); + } const int num_eff_iterations = atoi(argv[5]); const int num_iterations = atoi(argv[4]); shared_ptr model_data = ProjData::read_from_file(argv[3]); shared_ptr measured_data = ProjData::read_from_file(argv[2]); const std::string out_filename_prefix = argv[1]; - const int num_rings = - measured_data->get_proj_data_info_sptr()->get_scanner_sptr()-> - get_num_rings(); - const int num_detectors_per_ring = - measured_data->get_proj_data_info_sptr()->get_scanner_sptr()-> - get_num_detectors_per_ring(); - const int num_transaxial_blocks = - measured_data->get_proj_data_info_sptr()->get_scanner_sptr()-> - get_num_transaxial_blocks(); - const int num_axial_blocks = - measured_data->get_proj_data_info_sptr()->get_scanner_sptr()-> - get_num_axial_blocks(); - const int num_transaxial_crystals_per_block = - measured_data->get_proj_data_info_sptr()->get_scanner_sptr()-> - get_num_transaxial_crystals_per_block(); - const int num_axial_crystals_per_block = - measured_data->get_proj_data_info_sptr()->get_scanner_sptr()-> - get_num_axial_crystals_per_block(); - - - - CPUTimer timer; - timer.start(); - - FanProjData model_fan_data; - FanProjData fan_data; - Array<2,float> data_fan_sums(IndexRange2D(num_rings, num_detectors_per_ring)); - DetectorEfficiencies efficiencies(IndexRange2D(num_rings, num_detectors_per_ring)); - - GeoData3D measured_geo_data(num_axial_crystals_per_block, num_transaxial_crystals_per_block/2, num_rings, num_detectors_per_ring ); //inputes have to be modified - GeoData3D norm_geo_data(num_axial_crystals_per_block, num_transaxial_crystals_per_block/2, num_rings, num_detectors_per_ring ); //inputes have to be modified - - BlockData3D measured_block_data(num_axial_blocks, num_transaxial_blocks, num_axial_blocks-1, num_transaxial_blocks-1); - BlockData3D norm_block_data(num_axial_blocks, num_transaxial_blocks, num_axial_blocks-1, num_transaxial_blocks-1); - - - make_fan_data(model_fan_data, *model_data); - { - // next could be local if KL is not computed below - FanProjData measured_fan_data; - float threshold_for_KL; - // compute factors dependent on the data - { - make_fan_data(measured_fan_data, *measured_data); - -/* TEMP FIX */ - for (int ra = model_fan_data.get_min_ra(); ra <= model_fan_data.get_max_ra(); ++ra) + const int num_rings = measured_data->get_proj_data_info_sptr()->get_scanner_sptr()->get_num_rings(); + const int num_detectors_per_ring = measured_data->get_proj_data_info_sptr()->get_scanner_sptr()->get_num_detectors_per_ring(); + const int num_transaxial_blocks = measured_data->get_proj_data_info_sptr()->get_scanner_sptr()->get_num_transaxial_blocks(); + const int num_axial_blocks = measured_data->get_proj_data_info_sptr()->get_scanner_sptr()->get_num_axial_blocks(); + const int num_transaxial_crystals_per_block = + measured_data->get_proj_data_info_sptr()->get_scanner_sptr()->get_num_transaxial_crystals_per_block(); + const int num_axial_crystals_per_block = + measured_data->get_proj_data_info_sptr()->get_scanner_sptr()->get_num_axial_crystals_per_block(); + + CPUTimer timer; + timer.start(); + + FanProjData model_fan_data; + FanProjData fan_data; + Array<2, float> data_fan_sums(IndexRange2D(num_rings, num_detectors_per_ring)); + DetectorEfficiencies efficiencies(IndexRange2D(num_rings, num_detectors_per_ring)); + + GeoData3D measured_geo_data(num_axial_crystals_per_block, num_transaxial_crystals_per_block / 2, num_rings, + num_detectors_per_ring); // inputes have to be modified + GeoData3D norm_geo_data(num_axial_crystals_per_block, num_transaxial_crystals_per_block / 2, num_rings, + num_detectors_per_ring); // inputes have to be modified + + BlockData3D measured_block_data(num_axial_blocks, num_transaxial_blocks, num_axial_blocks - 1, num_transaxial_blocks - 1); + BlockData3D norm_block_data(num_axial_blocks, num_transaxial_blocks, num_axial_blocks - 1, num_transaxial_blocks - 1); + + make_fan_data(model_fan_data, *model_data); + { + // next could be local if KL is not computed below + FanProjData measured_fan_data; + float threshold_for_KL; + // compute factors dependent on the data { - for (int a = model_fan_data.get_min_a(); a <= model_fan_data.get_max_a(); ++a) - { - for (int rb = std::max(ra,model_fan_data.get_min_rb(ra)); rb <= model_fan_data.get_max_rb(ra); ++rb) - { - for (int b = model_fan_data.get_min_b(a); b <= model_fan_data.get_max_b(a); ++b) - if (model_fan_data(ra,a,rb,b) == 0) - measured_fan_data(ra,a,rb,b) = 0; - } + make_fan_data(measured_fan_data, *measured_data); + + /* TEMP FIX */ + for (int ra = model_fan_data.get_min_ra(); ra <= model_fan_data.get_max_ra(); ++ra) { + for (int a = model_fan_data.get_min_a(); a <= model_fan_data.get_max_a(); ++a) { + for (int rb = std::max(ra, model_fan_data.get_min_rb(ra)); rb <= model_fan_data.get_max_rb(ra); ++rb) { + for (int b = model_fan_data.get_min_b(a); b <= model_fan_data.get_max_b(a); ++b) + if (model_fan_data(ra, a, rb, b) == 0) + measured_fan_data(ra, a, rb, b) = 0; + } } - } + } - threshold_for_KL = measured_fan_data.find_max()/100000.F; - //display(measured_fan_data, "measured data"); - - make_fan_sum_data(data_fan_sums, measured_fan_data); - make_geo_data(measured_geo_data, measured_fan_data); - make_block_data(measured_block_data, measured_fan_data); - if (do_display) - display(measured_block_data, "raw block data from measurements"); - - /* { - char *out_filename = new char[20]; - sprintf(out_filename, "%s_%d.out", - "fan", ax_pos_num); - std::ofstream out(out_filename); - out << data_fan_sums; - delete[] out_filename; - } - */ + threshold_for_KL = measured_fan_data.find_max() / 100000.F; + // display(measured_fan_data, "measured data"); + + make_fan_sum_data(data_fan_sums, measured_fan_data); + make_geo_data(measured_geo_data, measured_fan_data); + make_block_data(measured_block_data, measured_fan_data); + if (do_display) + display(measured_block_data, "raw block data from measurements"); + + /* { + char *out_filename = new char[20]; + sprintf(out_filename, "%s_%d.out", + "fan", ax_pos_num); + std::ofstream out(out_filename); + out << data_fan_sums; + delete[] out_filename; } - - //std::cerr << "model min " << model_fan_data.find_min() << " ,max " << model_fan_data.find_max() << std::endl; - if (do_display) - display(model_fan_data, "model"); + */ + } + + // std::cerr << "model min " << model_fan_data.find_min() << " ,max " << model_fan_data.find_max() << std::endl; + if (do_display) + display(model_fan_data, "model"); #if 0 { shared_ptr out_proj_data_ptr = @@ -196,71 +176,64 @@ int main(int argc, char **argv) set_fan_data(*out_proj_data_ptr, model_fan_data); } #endif - - for (int iter_num = 1; iter_num<=std::max(num_iterations, 1); ++iter_num) - { - if (iter_num== 1) - { - efficiencies.fill(sqrt(data_fan_sums.sum()/model_fan_data.sum())); - norm_geo_data.fill(1); - norm_block_data.fill(1); - } - // efficiencies - { - fan_data = model_fan_data; - apply_geo_norm(fan_data, norm_geo_data); - apply_block_norm(fan_data, norm_block_data); - if (do_display) - display(fan_data, "model*geo*block"); - for (int eff_iter_num = 1; eff_iter_num<=num_eff_iterations; ++eff_iter_num) - { - iterate_efficiencies(efficiencies, data_fan_sums, fan_data); - { - char *out_filename = new char[out_filename_prefix.size() + 30]; - sprintf(out_filename, "%s_%s_%d_%d.out", - out_filename_prefix.c_str(), "eff", iter_num, eff_iter_num); - std::ofstream out(out_filename); - out << efficiencies; - delete[] out_filename; - } - if (do_KL) - { - apply_efficiencies(fan_data, efficiencies); - std::cerr << "measured*norm min " << measured_fan_data.find_min() << " ,max " << measured_fan_data.find_max() << std::endl; - std::cerr << "model*norm min " << fan_data.find_min() << " ,max " << fan_data.find_max() << std::endl; - if (do_display) - display( fan_data, "model_times_norm"); - info(boost::format("KL %1%") % KL(measured_fan_data, fan_data, threshold_for_KL)); - // now restore for further iterations - fan_data = model_fan_data; - apply_geo_norm(fan_data, norm_geo_data); - apply_block_norm(fan_data, norm_block_data); - } - if (do_display) - { - fan_data.fill(1); - apply_efficiencies(fan_data, efficiencies); - display(fan_data, "eff norm"); - // now restore for further iterations - fan_data = model_fan_data; - apply_geo_norm(fan_data, norm_geo_data); - apply_block_norm(fan_data, norm_block_data); - } - - } - } // end efficiencies - - - // geo norm - + + for (int iter_num = 1; iter_num <= std::max(num_iterations, 1); ++iter_num) { + if (iter_num == 1) { + efficiencies.fill(sqrt(data_fan_sums.sum() / model_fan_data.sum())); + norm_geo_data.fill(1); + norm_block_data.fill(1); + } + // efficiencies + { + fan_data = model_fan_data; + apply_geo_norm(fan_data, norm_geo_data); + apply_block_norm(fan_data, norm_block_data); + if (do_display) + display(fan_data, "model*geo*block"); + for (int eff_iter_num = 1; eff_iter_num <= num_eff_iterations; ++eff_iter_num) { + iterate_efficiencies(efficiencies, data_fan_sums, fan_data); + { + char* out_filename = new char[out_filename_prefix.size() + 30]; + sprintf(out_filename, "%s_%s_%d_%d.out", out_filename_prefix.c_str(), "eff", iter_num, eff_iter_num); + std::ofstream out(out_filename); + out << efficiencies; + delete[] out_filename; + } + if (do_KL) { + apply_efficiencies(fan_data, efficiencies); + std::cerr << "measured*norm min " << measured_fan_data.find_min() << " ,max " << measured_fan_data.find_max() + << std::endl; + std::cerr << "model*norm min " << fan_data.find_min() << " ,max " << fan_data.find_max() << std::endl; + if (do_display) + display(fan_data, "model_times_norm"); + info(boost::format("KL %1%") % KL(measured_fan_data, fan_data, threshold_for_KL)); + // now restore for further iterations fan_data = model_fan_data; + apply_geo_norm(fan_data, norm_geo_data); + apply_block_norm(fan_data, norm_block_data); + } + if (do_display) { + fan_data.fill(1); apply_efficiencies(fan_data, efficiencies); + display(fan_data, "eff norm"); + // now restore for further iterations + fan_data = model_fan_data; + apply_geo_norm(fan_data, norm_geo_data); apply_block_norm(fan_data, norm_block_data); - - if (do_geo) - iterate_geo_norm(norm_geo_data, measured_geo_data, fan_data); - - #if 0 + } + } + } // end efficiencies + + // geo norm + + fan_data = model_fan_data; + apply_efficiencies(fan_data, efficiencies); + apply_block_norm(fan_data, norm_block_data); + + if (do_geo) + iterate_geo_norm(norm_geo_data, measured_geo_data, fan_data); + +#if 0 { // check for (int a=0; a fan_sums(IndexRange2D(num_rings, num_detectors_per_ring)); - GeoData3D geo_data(num_axial_crystals_per_block, num_transaxial_crystals_per_block/2, num_rings, num_detectors_per_ring ); //inputes have to be modified - BlockData3D block_data(num_axial_blocks, num_transaxial_blocks, num_axial_blocks-1, num_transaxial_blocks-1); - - make_fan_sum_data(fan_sums, fan_data); - make_geo_data(geo_data, fan_data); - make_block_data(block_data, measured_fan_data); - -std::cerr << "KL on fans: " << KL(measured_fan_data, fan_data,0) << ", " << KL(measured_geo_data,geo_data,0) << std::endl; -} +#endif + { + char* out_filename = new char[out_filename_prefix.size() + 30]; + sprintf(out_filename, "%s_%s_%d.out", out_filename_prefix.c_str(), "block", iter_num); + std::ofstream out(out_filename); + out << norm_block_data; + delete[] out_filename; + } + if (do_KL) { + apply_block_norm(fan_data, norm_block_data); + info(boost::format("KL %1%") % KL(measured_fan_data, fan_data, threshold_for_KL)); } + if (do_display) { + fan_data.fill(1); + apply_block_norm(fan_data, norm_block_data); + display(norm_block_data, "raw block norm"); + display(fan_data, "block norm"); + } + } // end block + + //// print KL for fansums + if (do_KL) { + Array<2, float> fan_sums(IndexRange2D(num_rings, num_detectors_per_ring)); + GeoData3D geo_data(num_axial_crystals_per_block, num_transaxial_crystals_per_block / 2, num_rings, + num_detectors_per_ring); // inputes have to be modified + BlockData3D block_data(num_axial_blocks, num_transaxial_blocks, num_axial_blocks - 1, num_transaxial_blocks - 1); + + make_fan_sum_data(fan_sums, fan_data); + make_geo_data(geo_data, fan_data); + make_block_data(block_data, measured_fan_data); + + std::cerr << "KL on fans: " << KL(measured_fan_data, fan_data, 0) << ", " << KL(measured_geo_data, geo_data, 0) + << std::endl; + } } - timer.stop(); - info(boost::format("CPU time %1% secs") % timer.value()); - return EXIT_SUCCESS; + } + timer.stop(); + info(boost::format("CPU time %1% secs") % timer.value()); + return EXIT_SUCCESS; } diff --git a/src/utilities/find_ML_singles_from_delayed.cxx b/src/utilities/find_ML_singles_from_delayed.cxx index d6498d0528..0977717eb5 100644 --- a/src/utilities/find_ML_singles_from_delayed.cxx +++ b/src/utilities/find_ML_singles_from_delayed.cxx @@ -37,191 +37,160 @@ START_NAMESPACE_STIR -static unsigned long compute_num_bins(const int num_rings, const int num_detectors_per_ring, - const int max_ring_diff, const int half_fan_size) -{ +static unsigned long +compute_num_bins(const int num_rings, const int num_detectors_per_ring, const int max_ring_diff, const int half_fan_size) { unsigned long num = 0; for (int ra = 0; ra < num_rings; ++ra) - for (int a =0; a < num_detectors_per_ring; ++a) - { - for (int rb = std::max(ra-max_ring_diff, 0); rb <= std::min(ra+max_ring_diff, num_rings-1); ++rb) - for (int b = a+num_detectors_per_ring/2-half_fan_size; b <= a+num_detectors_per_ring/2+half_fan_size; ++b) - ++num; + for (int a = 0; a < num_detectors_per_ring; ++a) { + for (int rb = std::max(ra - max_ring_diff, 0); rb <= std::min(ra + max_ring_diff, num_rings - 1); ++rb) + for (int b = a + num_detectors_per_ring / 2 - half_fan_size; b <= a + num_detectors_per_ring / 2 + half_fan_size; ++b) + ++num; } return num; } - END_NAMESPACE_STIR USING_NAMESPACE_STIR -int main(int argc, char **argv) -{ - if (!(argc==4 || (argc==7 && strcmp(argv[1],"-f")==0))) - { - std::cerr << "Usage: \n" - << '\t' << argv[0] << " -f out_filename_prefix measured_fan_sum_data num_iterations max_ring_diff fan_size\n" - << "or\n" - << '\t' << argv[0] << " out_filename_prefix measured_projdata num_iterations\n" - << "If the -f option is used, the 2nd arg should be a file with fan_sums. " - << "Otherwise, it has to be projection data.\n"; - - return EXIT_FAILURE; - } - const int num_eff_iterations = atoi(argv[argc==4?3:4]); - const std::string out_filename_prefix = argv[argc==4?1:2]; - - const int do_display_interval = - ask_num("Display iterations which are a multiple of ",0,num_eff_iterations,0); - const int do_KL_interval = - ask_num("Compute KL distance between fan-sums at iterations which are a multiple of ",0,num_eff_iterations,0); - const int do_save_interval = - ask_num("Write output at iterations which are a multiple of ",0,num_eff_iterations,num_eff_iterations); +int +main(int argc, char** argv) { + if (!(argc == 4 || (argc == 7 && strcmp(argv[1], "-f") == 0))) { + std::cerr << "Usage: \n" + << '\t' << argv[0] << " -f out_filename_prefix measured_fan_sum_data num_iterations max_ring_diff fan_size\n" + << "or\n" + << '\t' << argv[0] << " out_filename_prefix measured_projdata num_iterations\n" + << "If the -f option is used, the 2nd arg should be a file with fan_sums. " + << "Otherwise, it has to be projection data.\n"; + + return EXIT_FAILURE; + } + const int num_eff_iterations = atoi(argv[argc == 4 ? 3 : 4]); + const std::string out_filename_prefix = argv[argc == 4 ? 1 : 2]; + + const int do_display_interval = ask_num("Display iterations which are a multiple of ", 0, num_eff_iterations, 0); + const int do_KL_interval = + ask_num("Compute KL distance between fan-sums at iterations which are a multiple of ", 0, num_eff_iterations, 0); + const int do_save_interval = + ask_num("Write output at iterations which are a multiple of ", 0, num_eff_iterations, num_eff_iterations); - - int num_rings; int num_detectors_per_ring; int fan_size; int max_ring_diff; - Array<2,float> data_fan_sums; + Array<2, float> data_fan_sums; - if (argc==4) - { - shared_ptr measured_data = ProjData::read_from_file(argv[2]); - get_fan_info(num_rings, num_detectors_per_ring, max_ring_diff, fan_size, - *measured_data->get_proj_data_info_sptr()); - data_fan_sums.grow(IndexRange2D(num_rings, num_detectors_per_ring)); + if (argc == 4) { + shared_ptr measured_data = ProjData::read_from_file(argv[2]); + get_fan_info(num_rings, num_detectors_per_ring, max_ring_diff, fan_size, *measured_data->get_proj_data_info_sptr()); + data_fan_sums.grow(IndexRange2D(num_rings, num_detectors_per_ring)); #if 0 FanProjData measured_fan_data; make_fan_data(measured_fan_data, *measured_data); make_fan_sum_data(data_fan_sums, measured_fan_data); #else - make_fan_sum_data(data_fan_sums, *measured_data); + make_fan_sum_data(data_fan_sums, *measured_data); #endif - // write fan sums to file - { - std::string fan_sum_name = "fansums_for_"; - fan_sum_name += argv[2]; - fan_sum_name.erase(fan_sum_name.begin() + fan_sum_name.rfind('.'), - fan_sum_name.end()); - fan_sum_name += ".dat"; - std::ofstream out(fan_sum_name.c_str()); - if (!out) - { - warning("Error opening output file %s\n", fan_sum_name.c_str()); - exit(EXIT_FAILURE); - } - out << data_fan_sums; - if (!out) - { - warning("Error writing data to output file %s\n", fan_sum_name.c_str()); - exit(EXIT_FAILURE); - } + // write fan sums to file + { + std::string fan_sum_name = "fansums_for_"; + fan_sum_name += argv[2]; + fan_sum_name.erase(fan_sum_name.begin() + fan_sum_name.rfind('.'), fan_sum_name.end()); + fan_sum_name += ".dat"; + std::ofstream out(fan_sum_name.c_str()); + if (!out) { + warning("Error opening output file %s\n", fan_sum_name.c_str()); + exit(EXIT_FAILURE); + } + out << data_fan_sums; + if (!out) { + warning("Error writing data to output file %s\n", fan_sum_name.c_str()); + exit(EXIT_FAILURE); } } - else - { - max_ring_diff = atoi(argv[5]); - fan_size = atoi(argv[6]); - std::ifstream in(argv[3]); - if (!in) - { - warning("Error opening input file %s\n", argv[3]); - exit(EXIT_FAILURE); - } - in >> data_fan_sums; - num_rings = data_fan_sums.get_length(); - if (num_rings==0) - { - warning("input file %s should be a 2d list of numbers but I found " - "a list of length 0 (or no list at all).\n", argv[3]); - exit(EXIT_FAILURE); - } - assert(data_fan_sums.get_min_index()==0); - num_detectors_per_ring = data_fan_sums[0].get_length(); - if (!data_fan_sums.is_regular()) - { - warning("input file %s should be a (rectangular) matrix of numbers\n", argv[3]); - exit(EXIT_FAILURE); - } - - if (num_detectors_per_ring==0) - { - warning("input file %s should be a 2d list of numbers but I found something else (zero number of columns?)\n", argv[3]); - exit(EXIT_FAILURE); - } - if (num_rings> data_fan_sums; + num_rings = data_fan_sums.get_length(); + if (num_rings == 0) { + warning("input file %s should be a 2d list of numbers but I found " + "a list of length 0 (or no list at all).\n", + argv[3]); + exit(EXIT_FAILURE); + } + assert(data_fan_sums.get_min_index() == 0); + num_detectors_per_ring = data_fan_sums[0].get_length(); + if (!data_fan_sums.is_regular()) { + warning("input file %s should be a (rectangular) matrix of numbers\n", argv[3]); + exit(EXIT_FAILURE); } - const int half_fan_size = fan_size/2; + if (num_detectors_per_ring == 0) { + warning("input file %s should be a 2d list of numbers but I found something else (zero number of columns?)\n", argv[3]); + exit(EXIT_FAILURE); + } + if (num_rings < max_ring_diff || num_detectors_per_ring < fan_size) { + warning("input file %s is a matrix with sizes %dx%d, but this is " + "too small compared to max_ring_diff (%d) and/or fan_size (%d)\n", + argv[3], num_rings, num_detectors_per_ring, max_ring_diff, fan_size); + exit(EXIT_FAILURE); + } + } + const int half_fan_size = fan_size / 2; CPUTimer timer; timer.start(); - + DetectorEfficiencies efficiencies(IndexRange2D(num_rings, num_detectors_per_ring)); { - float threshold_for_KL = data_fan_sums.find_max()/100000.F; + float threshold_for_KL = data_fan_sums.find_max() / 100000.F; const int iter_num = 1; { - if (iter_num== 1) - { - efficiencies.fill(sqrt(data_fan_sums.sum()/ - compute_num_bins(num_rings, num_detectors_per_ring, max_ring_diff, half_fan_size))); + if (iter_num == 1) { + efficiencies.fill( + sqrt(data_fan_sums.sum() / compute_num_bins(num_rings, num_detectors_per_ring, max_ring_diff, half_fan_size))); } // efficiencies { - for (int eff_iter_num = 1; eff_iter_num<=num_eff_iterations; ++eff_iter_num) - { + for (int eff_iter_num = 1; eff_iter_num <= num_eff_iterations; ++eff_iter_num) { std::cout << "Starting iteration " << eff_iter_num; iterate_efficiencies(efficiencies, data_fan_sums, max_ring_diff, half_fan_size); - if (eff_iter_num==num_eff_iterations || (do_save_interval>0 && eff_iter_num%do_save_interval==0)) - { - char *out_filename = new char[out_filename_prefix.size() + 30]; - sprintf(out_filename, "%s_%s_%d_%d.out", - out_filename_prefix.c_str(), "eff", iter_num, eff_iter_num); + if (eff_iter_num == num_eff_iterations || (do_save_interval > 0 && eff_iter_num % do_save_interval == 0)) { + char* out_filename = new char[out_filename_prefix.size() + 30]; + sprintf(out_filename, "%s_%s_%d_%d.out", out_filename_prefix.c_str(), "eff", iter_num, eff_iter_num); std::ofstream out(out_filename); - if (!out) - { - warning("Error opening output file %s\n", out_filename); - exit(EXIT_FAILURE); - } + if (!out) { + warning("Error opening output file %s\n", out_filename); + exit(EXIT_FAILURE); + } out << efficiencies; - if (!out) - { - warning("Error writing data to output file %s\n", out_filename); - exit(EXIT_FAILURE); - } + if (!out) { + warning("Error writing data to output file %s\n", out_filename); + exit(EXIT_FAILURE); + } delete[] out_filename; } - if (eff_iter_num==num_eff_iterations || (do_KL_interval>0 && eff_iter_num%do_KL_interval==0)) - { - Array<2,float> estimated_fan_sums(data_fan_sums.get_index_range()); - make_fan_sum_data(estimated_fan_sums, efficiencies, max_ring_diff, half_fan_size); - std::cout << "\tKL " << KL(data_fan_sums, estimated_fan_sums, threshold_for_KL); - + if (eff_iter_num == num_eff_iterations || (do_KL_interval > 0 && eff_iter_num % do_KL_interval == 0)) { + Array<2, float> estimated_fan_sums(data_fan_sums.get_index_range()); + make_fan_sum_data(estimated_fan_sums, efficiencies, max_ring_diff, half_fan_size); + std::cout << "\tKL " << KL(data_fan_sums, estimated_fan_sums, threshold_for_KL); } std::cout << std::endl; - if (do_display_interval>0 && eff_iter_num%do_display_interval==0) - { + if (do_display_interval > 0 && eff_iter_num % do_display_interval == 0) { display(efficiencies, "efficiencies"); } - } } // end efficiencies - } - } + } timer.stop(); std::cout << "CPU time " << timer.value() << " secs" << std::endl; return EXIT_SUCCESS; diff --git a/src/utilities/find_fwhm_in_image.cxx b/src/utilities/find_fwhm_in_image.cxx index 3d889071d7..d3fe2e7a0f 100644 --- a/src/utilities/find_fwhm_in_image.cxx +++ b/src/utilities/find_fwhm_in_image.cxx @@ -18,7 +18,7 @@ \file \ingroup utilities \brief List of FWHM and location of maximum in the image - + \author Charalampos Tsoumpas \author Kris Thielemans \author Sanida Mustafovic @@ -28,23 +28,23 @@ find_fwhm_in_image filename [num_maxima] [level] [dimension] [nema] \endcode \param num_maxima defaults to 1 - \param level defaults to 2 (half maximum) + \param level defaults to 2 (half maximum) \param dimension: for point sources (default) set to 0 for line source along z, y, x -axis, set to: 1, 2, 3 respectively - \param NEMA defaults to 1 - - - If you have point sources, it prints the value of the [num_maxima] maximum point source with its location - and the resolution at the three dimensions, sorting from the maximum to the minimum. If you have a line + \param NEMA defaults to 1 + + + If you have point sources, it prints the value of the [num_maxima] maximum point source with its location + and the resolution at the three dimensions, sorting from the maximum to the minimum. If you have a line source, a text file is returned that it contains the maximum value at its one slice, which is sorted from - the mimimum to maximum slice index, at the wanted direction, with its location and the resolution at the - three dimensions. The resolution at the axis of the line is set to be 0. If given as [num_maxima] less - than the total slices it returns the results for some slices by sampling with the same step the total - slices of the wanted dimension. The [nema] parameter enables the oportunity of using the NEMA Standards - Publication NU 2-2001. If it is set to 0 the function estimates the FWHM using 3D interpolation, for - closer approximation. + the mimimum to maximum slice index, at the wanted direction, with its location and the resolution at the + three dimensions. The resolution at the axis of the line is set to be 0. If given as [num_maxima] less + than the total slices it returns the results for some slices by sampling with the same step the total + slices of the wanted dimension. The [nema] parameter enables the oportunity of using the NEMA Standards + Publication NU 2-2001. If it is set to 0 the function estimates the FWHM using 3D interpolation, for + closer approximation. */ #include "stir/shared_ptr.h" #include "stir/DiscretisedDensity.h" @@ -55,93 +55,73 @@ #include #include #include -#include +#include #include #ifndef STIR_NO_NAMESPACES using std::setw; #endif -/***********************************************************/ -int main(int argc, char *argv[]) -{ +/***********************************************************/ +int +main(int argc, char* argv[]) { USING_NAMESPACE_STIR - if (argc< 2 || argc>6) - { - std::cerr << "Usage:" << argv[0] << " input_image [num_maxima] [level] [dimension] [nema]\n" - << "\tnum_maxima defaults to 1\n" - << "\tlevel of maximum defaults at half maximum: 2\n" - << "\tfor point sources dimension set to 0 (default)\n" - << "\tfor line source along z, y, x -axis dimension set to 1, 2, 3 respectively:\n" - << "\tnema defaults to 1, NEMA Standards NU 2-2001 is enabled\n" - << "returns a file containing the resolutions\n\n"; + if (argc < 2 || argc > 6) { + std::cerr << "Usage:" << argv[0] << " input_image [num_maxima] [level] [dimension] [nema]\n" + << "\tnum_maxima defaults to 1\n" + << "\tlevel of maximum defaults at half maximum: 2\n" + << "\tfor point sources dimension set to 0 (default)\n" + << "\tfor line source along z, y, x -axis dimension set to 1, 2, 3 respectively:\n" + << "\tnema defaults to 1, NEMA Standards NU 2-2001 is enabled\n" + << "returns a file containing the resolutions\n\n"; + + return EXIT_FAILURE; + } + const unsigned short num_maxima = argc >= 3 ? atoi(argv[2]) : 1; + const float level = argc >= 4 ? static_cast(atof(argv[3])) : 2; + const int dimension = argc >= 5 ? atoi(argv[4]) : 0; + const bool nema = argc >= 6 ? (atoi(argv[5]) != 0) : true; + std::cerr << "Finding " << num_maxima << " maxima\n"; + shared_ptr> input_image_sptr(read_from_file>(argv[1])); + DiscretisedDensity<3, float>& input_image = *input_image_sptr; + std::list> list_res_index = find_fwhm_in_image(input_image, num_maxima, level, dimension, nema); + std::list>::iterator current_iter = list_res_index.begin(); + if (dimension != 0) { + std::string output_string; + std::string input_string(argv[1]); + std::string slices_string(argv[2]); + std::string::iterator string_iter; + for (string_iter = input_string.begin(); string_iter != input_string.end() && *string_iter != '.'; ++string_iter) + output_string.push_back(*string_iter); + if (argc >= 4) { + std::string level_string(argv[3]); + output_string += '_' + slices_string + "_slices_FW" + level_string + 'M'; + } else + output_string += '_' + slices_string + "_slices_FWHM"; - return EXIT_FAILURE; - } - const unsigned short num_maxima = argc>=3 ? atoi(argv[2]) : 1 ; - const float level = argc>=4 ? static_cast(atof(argv[3])) : 2 ; - const int dimension = argc>=5 ? atoi(argv[4]) : 0 ; - const bool nema = argc>=6 ? (atoi(argv[5])!=0) : true ; - std::cerr << "Finding " << num_maxima << " maxima\n" ; - shared_ptr< DiscretisedDensity<3,float> > - input_image_sptr(read_from_file >(argv[1])); - DiscretisedDensity<3,float>& input_image = *input_image_sptr; - std::list > list_res_index = - find_fwhm_in_image(input_image,num_maxima,level,dimension,nema); - std::list >:: iterator current_iter=list_res_index.begin(); - if (dimension!=0) - { - std::string output_string; - std::string input_string(argv[1]); - std::string slices_string(argv[2]); - std::string:: iterator string_iter; - for(string_iter=input_string.begin(); - string_iter!=input_string.end() && *string_iter!='.' ; - ++string_iter) - output_string.push_back(*string_iter); - if (argc>=4) - { - std::string level_string(argv[3]); - output_string += '_' + slices_string + "_slices_FW" + level_string + 'M' ; - } - else - output_string += '_' + slices_string + "_slices_FWHM" ; - - std::ofstream out(output_string.c_str()); //output file // - if(!out) - { - std::cerr << "Cannot open text file.\n" ; - return EXIT_FAILURE; - } - out << "Slice\t Z\tY\t X\tResZ(mm) ResY(mm) ResX(mm) Value\n"; - for (short counter=0 ; counter!=num_maxima ; ++counter) - { - out << setw(3) << counter+1 << "\t" - << setw(3) << current_iter->voxel_location[1] << "\t" - << setw(3) << current_iter->voxel_location[2] << "\t" - << setw(3) << current_iter->voxel_location[3] << "\t" - << setw(6) << current_iter->resolution[1]<< "\t" - << setw(6) << current_iter->resolution[2]<< "\t" - << setw(6) << current_iter->resolution[3]<< "\t" - << setw(9) << current_iter->voxel_value << "\n" ; - ++current_iter; - } - out.close(); - } - else - for (short counter=0 ; counter!=num_maxima ; ++counter) - { - std::cout << counter+1 << ". max: " << setw(6) << current_iter->voxel_value - << " at: " << setw(3) << current_iter->voxel_location[1] - << " (Z) ," << setw(3) << current_iter->voxel_location[2] - << " (Y) ," << setw(3) << current_iter->voxel_location[3] << " (X) \n" ; - std::cout << " \n The resolution in z axis is " - << setw(6) << (current_iter->resolution[1]) - << ", \n The resolution in y axis is " - << setw(6) << (current_iter->resolution[2]) - << ", \n The resolution in x axis is " - << setw(6) << (current_iter->resolution[3]) - << ", in mm relative to origin. \n \n"; - ++current_iter; - } + std::ofstream out(output_string.c_str()); // output file // + if (!out) { + std::cerr << "Cannot open text file.\n"; + return EXIT_FAILURE; + } + out << "Slice\t Z\tY\t X\tResZ(mm) ResY(mm) ResX(mm) Value\n"; + for (short counter = 0; counter != num_maxima; ++counter) { + out << setw(3) << counter + 1 << "\t" << setw(3) << current_iter->voxel_location[1] << "\t" << setw(3) + << current_iter->voxel_location[2] << "\t" << setw(3) << current_iter->voxel_location[3] << "\t" << setw(6) + << current_iter->resolution[1] << "\t" << setw(6) << current_iter->resolution[2] << "\t" << setw(6) + << current_iter->resolution[3] << "\t" << setw(9) << current_iter->voxel_value << "\n"; + ++current_iter; + } + out.close(); + } else + for (short counter = 0; counter != num_maxima; ++counter) { + std::cout << counter + 1 << ". max: " << setw(6) << current_iter->voxel_value << " at: " << setw(3) + << current_iter->voxel_location[1] << " (Z) ," << setw(3) << current_iter->voxel_location[2] << " (Y) ," + << setw(3) << current_iter->voxel_location[3] << " (X) \n"; + std::cout << " \n The resolution in z axis is " << setw(6) << (current_iter->resolution[1]) + << ", \n The resolution in y axis is " << setw(6) << (current_iter->resolution[2]) + << ", \n The resolution in x axis is " << setw(6) << (current_iter->resolution[3]) + << ", in mm relative to origin. \n \n"; + ++current_iter; + } return EXIT_SUCCESS; -} +} diff --git a/src/utilities/find_maxima_in_image.cxx b/src/utilities/find_maxima_in_image.cxx index 55cf6083c3..c9b9721c4a 100644 --- a/src/utilities/find_maxima_in_image.cxx +++ b/src/utilities/find_maxima_in_image.cxx @@ -48,7 +48,6 @@ #include #include - #ifndef STIR_NO_NAMESPACES using std::endl; using std::cout; @@ -57,100 +56,80 @@ using std::setw; #endif USING_NAMESPACE_STIR - -int main(int argc, char *argv[]) -{ - - if (argc< 2 || argc>5) - { + +int +main(int argc, char* argv[]) { + + if (argc < 2 || argc > 5) { cerr << "Usage:" << argv[0] << " input_image [num_maxima [ half_mask_size_xy [half_mask_size z]] ]\n" - << "\tnum_maxima defaults to 1\n" - << "\thalf_mask_size_xy defaults to 1\n" - << "\thalf_mask_size_z defaults to half_mask_size_xy" <=3 ? atoi(argv[2]) : 1; - const int mask_size_xy = argc>=4 ? atoi(argv[3]) : 1; - const int mask_size_z = argc>=5 ? atoi(argv[4]) : mask_size_xy; + const unsigned num_maxima = argc >= 3 ? atoi(argv[2]) : 1; + const int mask_size_xy = argc >= 4 ? atoi(argv[3]) : 1; + const int mask_size_z = argc >= 5 ? atoi(argv[4]) : mask_size_xy; - cerr << "Finding " << num_maxima << " maxima, each at least \n\t" - << mask_size_xy << " pixels from each other in x,y direction, and\n\t" - << mask_size_z << " pixels from each other in z direction.\n"; + cerr << "Finding " << num_maxima << " maxima, each at least \n\t" << mask_size_xy + << " pixels from each other in x,y direction, and\n\t" << mask_size_z << " pixels from each other in z direction.\n"; - shared_ptr< DiscretisedDensity<3,float> > - input_image_sptr(read_from_file >(argv[1])); - DiscretisedDensity<3,float>& input_image = *input_image_sptr; + shared_ptr> input_image_sptr(read_from_file>(argv[1])); + DiscretisedDensity<3, float>& input_image = *input_image_sptr; - const float mask_value = std::min(input_image.find_min()-100, -1.E20F); + const float mask_value = std::min(input_image.find_min() - 100, -1.E20F); - for (unsigned int maximum_num=0; maximum_num!=num_maxima; ++ maximum_num) + for (unsigned int maximum_num = 0; maximum_num != num_maxima; ++maximum_num) { + const float current_maximum = input_image.find_max(); + int max_k = 0, max_j = 0, max_i = 0; // initialise to avoid compiler warnings + bool found = false; { - const float current_maximum = input_image.find_max(); - int max_k=0, max_j=0,max_i=0; // initialise to avoid compiler warnings - bool found=false; - { - const int min_k_index = input_image.get_min_index(); - const int max_k_index = input_image.get_max_index(); - for ( int k = min_k_index; k<= max_k_index && !found; ++k) - { - const int min_j_index = input_image[k].get_min_index(); - const int max_j_index = input_image[k].get_max_index(); - for ( int j = min_j_index; j<= max_j_index && !found; ++j) - { - const int min_i_index = input_image[k][j].get_min_index(); - const int max_i_index = input_image[k][j].get_max_index(); - for ( int i = min_i_index; i<= max_i_index && !found; ++i) - { - if ( input_image[k][j][i] == current_maximum) - { - max_k = k; - max_j = j; - max_i = i; - cout << "max: " << setw(6) << current_maximum - << " at: " - << setw(3) << max_k - << ',' << setw(3) << max_j - << ',' << setw(3) << max_i; - { - BasicCoordinate<3,float> phys_coord = - input_image. - get_physical_coordinates_for_indices(make_coordinate(max_k, max_j, max_i)); - cout << " which is " - << setw(6) << phys_coord[1] - << ',' << setw(6) << phys_coord[2] - << ',' << setw(6) < phys_coord = + input_image.get_physical_coordinates_for_indices(make_coordinate(max_k, max_j, max_i)); + cout << " which is " << setw(6) << phys_coord[1] << ',' << setw(6) << phys_coord[2] << ',' << setw(6) + << phys_coord[3] << " in mm in physical coordinates"; + } + cout << '\n'; + found = true; + } + } + } + } + if (!found) { + warning("Something strange going on: can't find maximum %g\n", current_maximum); + return EXIT_FAILURE; + } + if (maximum_num + 1 != num_maxima) { + // now mask it out for next run + for (int k = std::max(max_k - mask_size_z, min_k_index); k <= std::min(max_k + mask_size_z, max_k_index); ++k) { + const int min_j_index = input_image[k].get_min_index(); + const int max_j_index = input_image[k].get_max_index(); + for (int j = std::max(max_j - mask_size_xy, min_j_index); j <= std::min(max_j + mask_size_xy, max_j_index); ++j) { + const int min_i_index = input_image[k][j].get_min_index(); + const int max_i_index = input_image[k][j].get_max_index(); + for (int i = std::max(max_i - mask_size_xy, min_i_index); i <= std::min(max_i + mask_size_xy, max_i_index); ++i) + input_image[k][j][i] = mask_value; + } + } } } + } return EXIT_SUCCESS; } - diff --git a/src/utilities/forward_project.cxx b/src/utilities/forward_project.cxx index 55be8f17cf..822bb3a61f 100644 --- a/src/utilities/forward_project.cxx +++ b/src/utilities/forward_project.cxx @@ -55,85 +55,72 @@ #include #include -static void print_usage_and_exit() -{ - std::cerr<<"\nUsage:\nforward_project output-filename image_to_forward_project template_proj_data_file [forwardprojector-parfile ]\n"; - std::cerr<<"The default projector uses the ray-tracing matrix.\n\n"; - std::cerr<<"Example parameter file:\n\n" - <<"Forward Projector parameters:=\n" - <<" type := Matrix\n" - <<" Forward projector Using Matrix Parameters :=\n" - <<" Matrix type := Ray Tracing\n" - <<" Ray tracing matrix parameters :=\n" - <<" End Ray tracing matrix parameters :=\n" - <<" End Forward Projector Using Matrix Parameters :=\n" - <<"End:=\n"; +static void +print_usage_and_exit() { + std::cerr << "\nUsage:\nforward_project output-filename image_to_forward_project template_proj_data_file " + "[forwardprojector-parfile ]\n"; + std::cerr << "The default projector uses the ray-tracing matrix.\n\n"; + std::cerr << "Example parameter file:\n\n" + << "Forward Projector parameters:=\n" + << " type := Matrix\n" + << " Forward projector Using Matrix Parameters :=\n" + << " Matrix type := Ray Tracing\n" + << " Ray tracing matrix parameters :=\n" + << " End Ray tracing matrix parameters :=\n" + << " End Forward Projector Using Matrix Parameters :=\n" + << "End:=\n"; exit(EXIT_FAILURE); } - -int -main (int argc, char * argv[]) -{ +int +main(int argc, char* argv[]) { using namespace stir; - if (argc!=4 && argc!=5 ) + if (argc != 4 && argc != 5) print_usage_and_exit(); - + const std::string output_filename = argv[1]; - shared_ptr > - image_density_sptr(read_from_file >(argv[2])); + shared_ptr> image_density_sptr(read_from_file>(argv[2])); - shared_ptr template_proj_data_sptr = - ProjData::read_from_file(argv[3]); + shared_ptr template_proj_data_sptr = ProjData::read_from_file(argv[3]); // create exam_info. Use most things from the image, as often people will // just have a standard template shared_ptr exam_info_sptr(image_density_sptr->get_exam_info().create_shared_clone()); if (image_density_sptr->get_exam_info().imaging_modality.is_unknown() && - template_proj_data_sptr->get_exam_info().imaging_modality.is_known()) - { - exam_info_sptr->imaging_modality = template_proj_data_sptr->get_exam_info().imaging_modality; - } - else if (image_density_sptr->get_exam_info().imaging_modality != - template_proj_data_sptr->get_exam_info().imaging_modality) + template_proj_data_sptr->get_exam_info().imaging_modality.is_known()) { + exam_info_sptr->imaging_modality = template_proj_data_sptr->get_exam_info().imaging_modality; + } else if (image_density_sptr->get_exam_info().imaging_modality != template_proj_data_sptr->get_exam_info().imaging_modality) error("forward_project: Imaging modality should be the same for the image and the projection data"); - if (template_proj_data_sptr->get_exam_info().has_energy_information()) - { - if (image_density_sptr->get_exam_info().has_energy_information()) - warning("Both image and template have energy information. Using the latter."); + if (template_proj_data_sptr->get_exam_info().has_energy_information()) { + if (image_density_sptr->get_exam_info().has_energy_information()) + warning("Both image and template have energy information. Using the latter."); - exam_info_sptr->set_energy_information_from(template_proj_data_sptr->get_exam_info()); - } + exam_info_sptr->set_energy_information_from(template_proj_data_sptr->get_exam_info()); + } shared_ptr forw_projector_sptr; - if (argc>=5) - { - KeyParser parser; - parser.add_start_key("Forward Projector parameters"); - parser.add_parsing_key("type", &forw_projector_sptr); - parser.add_stop_key("END"); - parser.parse(argv[4]); - } - else - { - shared_ptr PM(new ProjMatrixByBinUsingRayTracing()); - forw_projector_sptr.reset(new ForwardProjectorByBinUsingProjMatrixByBin(PM)); - } - - forw_projector_sptr->set_up(template_proj_data_sptr->get_proj_data_info_sptr()->create_shared_clone(), - image_density_sptr ); - - ProjDataInterfile output_projdata(exam_info_sptr, - template_proj_data_sptr->get_proj_data_info_sptr()->create_shared_clone(), + if (argc >= 5) { + KeyParser parser; + parser.add_start_key("Forward Projector parameters"); + parser.add_parsing_key("type", &forw_projector_sptr); + parser.add_stop_key("END"); + parser.parse(argv[4]); + } else { + shared_ptr PM(new ProjMatrixByBinUsingRayTracing()); + forw_projector_sptr.reset(new ForwardProjectorByBinUsingProjMatrixByBin(PM)); + } + + forw_projector_sptr->set_up(template_proj_data_sptr->get_proj_data_info_sptr()->create_shared_clone(), image_density_sptr); + + ProjDataInterfile output_projdata(exam_info_sptr, template_proj_data_sptr->get_proj_data_info_sptr()->create_shared_clone(), output_filename); forw_projector_sptr->forward_project(output_projdata, *image_density_sptr); - + return EXIT_SUCCESS; } - diff --git a/src/utilities/generate_image.cxx b/src/utilities/generate_image.cxx index 4b16eaa444..a6e6272630 100644 --- a/src/utilities/generate_image.cxx +++ b/src/utilities/generate_image.cxx @@ -51,7 +51,7 @@ number format := unsigned integer number_of_bytes_per_pixel:=2 ; fix the scale factor to 1 - ; comment out next line to let STIR use the full dynamic + ; comment out next line to let STIR use the full dynamic ; range of the output type scale_to_write_data:= 1 End Interfile Output File Format Parameters:= @@ -75,7 +75,7 @@ ; Shape3D hierarchy for possible shapes and their parameters shape type:= ellipsoidal cylinder Ellipsoidal Cylinder Parameters:= - radius-x (in mm):= 1 + radius-x (in mm):= 1 radius-y (in mm):= 2 length-z (in mm):= 3 origin (in mm):= {z,y,x} @@ -95,7 +95,7 @@ is at the edge of the image, the current mechanism of generating the image might miss the shape entirely. - \warning Does not currently support interactive parsing, so a par file + \warning Does not currently support interactive parsing, so a par file must be given on the command line. \todo Code duplicates things from stir::InterfileHeader. This is bad as it might @@ -116,17 +116,13 @@ START_NAMESPACE_STIR - -class GenerateImage : public KeyParser -{ +class GenerateImage : public KeyParser { public: - - GenerateImage(const char * const par_filename); - + GenerateImage(const char* const par_filename); Succeeded compute(); -private: +private: virtual void set_defaults(); virtual void initialise_keymap(); virtual bool post_processing(); @@ -138,13 +134,12 @@ class GenerateImage : public KeyParser int patient_orientation_index; int patient_rotation_index; - std::vector > shape_ptrs; + std::vector> shape_ptrs; shared_ptr current_shape_ptr; std::vector values; float current_value; std::string output_filename; - shared_ptr > > - output_file_format_sptr; + shared_ptr>> output_file_format_sptr; void increment_current_shape_num(); @@ -161,86 +156,73 @@ class GenerateImage : public KeyParser double rel_start_time; }; - -void GenerateImage:: -increment_current_shape_num() -{ - if (!is_null_ptr( current_shape_ptr)) - { - shape_ptrs.push_back(current_shape_ptr); - values.push_back(current_value); - current_shape_ptr.reset(); - } +void +GenerateImage::increment_current_shape_num() { + if (!is_null_ptr(current_shape_ptr)) { + shape_ptrs.push_back(current_shape_ptr); + values.push_back(current_value); + current_shape_ptr.reset(); + } } -void -GenerateImage:: -set_defaults() -{ +void +GenerateImage::set_defaults() { exam_info_sptr.reset(new ExamInfo); // need to default to PET for backwards compatibility exam_info_sptr->imaging_modality = ImagingModality::PT; - patient_orientation_index = 3; //unknown - patient_rotation_index = 5; //unknown - output_image_size_x=128; - output_image_size_y=128; - output_image_size_z=1; - output_voxel_size_x=1; - output_voxel_size_y=1; - output_voxel_size_z=1; - num_samples = CartesianCoordinate3D(5,5,5); + patient_orientation_index = 3; // unknown + patient_rotation_index = 5; // unknown + output_image_size_x = 128; + output_image_size_y = 128; + output_image_size_z = 1; + output_voxel_size_x = 1; + output_voxel_size_y = 1; + output_voxel_size_z = 1; + num_samples = CartesianCoordinate3D(5, 5, 5); shape_ptrs.resize(0); values.resize(0); image_duration = -1.0; rel_start_time = 0; output_filename.resize(0); - output_file_format_sptr = - OutputFileFormat >::default_sptr(); + output_file_format_sptr = OutputFileFormat>::default_sptr(); } -void GenerateImage::set_imaging_modality() -{ +void +GenerateImage::set_imaging_modality() { set_variable(); this->exam_info_sptr->imaging_modality = ImagingModality(imaging_modality_as_string); } -void -GenerateImage:: -initialise_keymap() -{ +void +GenerateImage::initialise_keymap() { add_start_key("generate_image Parameters"); // copy of InterfileHeader (TODO) - add_key("imaging modality", - KeyArgument::ASCII, (KeywordProcessor)&GenerateImage::set_imaging_modality, - &imaging_modality_as_string); + add_key("imaging modality", KeyArgument::ASCII, (KeywordProcessor)&GenerateImage::set_imaging_modality, + &imaging_modality_as_string); add_key("originating system", &exam_info_sptr->originating_system); - add_key("patient orientation", - &patient_orientation_index, - &patient_orientation_values); - add_key("patient rotation", - &patient_rotation_index, - &patient_rotation_values); + add_key("patient orientation", &patient_orientation_index, &patient_orientation_values); + add_key("patient rotation", &patient_rotation_index, &patient_rotation_values); patient_orientation_values.push_back("head_in"); patient_orientation_values.push_back("feet_in"); patient_orientation_values.push_back("other"); - patient_orientation_values.push_back("unknown"); //default + patient_orientation_values.push_back("unknown"); // default patient_rotation_values.push_back("supine"); patient_rotation_values.push_back("prone"); patient_rotation_values.push_back("right"); patient_rotation_values.push_back("left"); patient_rotation_values.push_back("other"); - patient_rotation_values.push_back("unknown"); //default - - add_key("output filename",&output_filename); - add_parsing_key("output file format type",&output_file_format_sptr); - add_key("X output image size (in pixels)",&output_image_size_x); - add_key("Y output image size (in pixels)",&output_image_size_y); - add_key("Z output image size (in pixels)",&output_image_size_z); - add_key("X voxel size (in mm)",&output_voxel_size_x); - add_key("Y voxel size (in mm)",&output_voxel_size_y); - add_key("Z voxel size (in mm)",&output_voxel_size_z); - + patient_rotation_values.push_back("unknown"); // default + + add_key("output filename", &output_filename); + add_parsing_key("output file format type", &output_file_format_sptr); + add_key("X output image size (in pixels)", &output_image_size_x); + add_key("Y output image size (in pixels)", &output_image_size_y); + add_key("Z output image size (in pixels)", &output_image_size_z); + add_key("X voxel size (in mm)", &output_voxel_size_x); + add_key("Y voxel size (in mm)", &output_voxel_size_y); + add_key("Z voxel size (in mm)", &output_voxel_size_z); + add_key("Z number of samples to take per voxel", &num_samples.z()); add_key("Y number of samples to take per voxel", &num_samples.y()); add_key("X number of samples to take per voxel", &num_samples.x()); @@ -250,103 +232,82 @@ initialise_keymap() add_parsing_key("shape type", ¤t_shape_ptr); add_key("value", ¤t_value); - add_key("next shape", KeyArgument::NONE, - (KeywordProcessor)&GenerateImage::increment_current_shape_num); + add_key("next shape", KeyArgument::NONE, (KeywordProcessor)&GenerateImage::increment_current_shape_num); add_stop_key("END"); - } - bool -GenerateImage:: -post_processing() -{ +GenerateImage::post_processing() { assert(values.size() == shape_ptrs.size()); - if (patient_orientation_index<0 || patient_rotation_index<0) + if (patient_orientation_index < 0 || patient_rotation_index < 0) return true; // warning: relies on index taking same values as enums in PatientPosition exam_info_sptr->patient_position.set_rotation(static_cast(patient_rotation_index)); exam_info_sptr->patient_position.set_orientation(static_cast(patient_orientation_index)); - if (!is_null_ptr( current_shape_ptr)) - { - shape_ptrs.push_back(current_shape_ptr); - values.push_back(current_value); - } - if (output_filename.size()==0) - { - warning("You have to specify an output_filename\n"); - return true; - } - if (is_null_ptr(output_file_format_sptr)) - { - warning("You have specified an invalid output file format\n"); - return true; - } - if (output_image_size_x<=0) - { - warning("X output_image_size should be strictly positive\n"); - return true; - } - if (output_image_size_y<=0) - { - warning("Y output_image_size should be strictly positive\n"); - return true; - } - if (output_image_size_z<=0) - { - warning("Z output_image_size should be strictly positive\n"); - return true; - } - if (output_voxel_size_x<=0) - { - warning("X output_voxel_size should be strictly positive\n"); - return true; - } - if (output_voxel_size_y<=0) - { - warning("Y output_voxel_size should be strictly positive\n"); - return true; - } - if (output_voxel_size_z<=0) - { - warning("Z output_voxel_size should be strictly positive\n"); - return true; - } - if (num_samples.z()<=0) - { - warning("number of samples to take in z-direction should be strictly positive\n"); - return true; - } - if (num_samples.y()<=0) - { - warning("number of samples to take in y-direction should be strictly positive\n"); - return true; - } - if (num_samples.x()<=0) - { - warning("number of samples to take in x-direction should be strictly positive\n"); - return true; - } + if (!is_null_ptr(current_shape_ptr)) { + shape_ptrs.push_back(current_shape_ptr); + values.push_back(current_value); + } + if (output_filename.size() == 0) { + warning("You have to specify an output_filename\n"); + return true; + } + if (is_null_ptr(output_file_format_sptr)) { + warning("You have specified an invalid output file format\n"); + return true; + } + if (output_image_size_x <= 0) { + warning("X output_image_size should be strictly positive\n"); + return true; + } + if (output_image_size_y <= 0) { + warning("Y output_image_size should be strictly positive\n"); + return true; + } + if (output_image_size_z <= 0) { + warning("Z output_image_size should be strictly positive\n"); + return true; + } + if (output_voxel_size_x <= 0) { + warning("X output_voxel_size should be strictly positive\n"); + return true; + } + if (output_voxel_size_y <= 0) { + warning("Y output_voxel_size should be strictly positive\n"); + return true; + } + if (output_voxel_size_z <= 0) { + warning("Z output_voxel_size should be strictly positive\n"); + return true; + } + if (num_samples.z() <= 0) { + warning("number of samples to take in z-direction should be strictly positive\n"); + return true; + } + if (num_samples.y() <= 0) { + warning("number of samples to take in y-direction should be strictly positive\n"); + return true; + } + if (num_samples.x() <= 0) { + warning("number of samples to take in x-direction should be strictly positive\n"); + return true; + } return false; } //! \brief parses parameters -/*! \warning Currently does not support interactive input, due to +/*! \warning Currently does not support interactive input, due to the use of the 'next shape' keyword. */ -GenerateImage:: -GenerateImage(const char * const par_filename) -{ +GenerateImage::GenerateImage(const char* const par_filename) { set_defaults(); initialise_keymap(); - if (par_filename!=0) - { - if (parse(par_filename) == false) - exit(EXIT_FAILURE); - } - else + if (par_filename != 0) { + if (parse(par_filename) == false) + exit(EXIT_FAILURE); + } else ask_parameters(); #if 0 @@ -358,14 +319,10 @@ GenerateImage(const char * const par_filename) std::cerr << (**iter).parameter_info() << '\n'; } #endif - } - Succeeded -GenerateImage:: -compute() -{ +GenerateImage::compute() { #if 0 shared_ptr > density_ptr( read_from_file >(template_filename)); @@ -377,44 +334,32 @@ compute() #else - if (image_duration>0.0) - { - std::vector start_times(1, rel_start_time); - std::vector durations(1, image_duration); - TimeFrameDefinitions frame_defs(start_times, durations); - exam_info_sptr->set_time_frame_definitions(frame_defs); - } - else - { - warning("image duration not set, so time frame definitions will not be initialised"); - } - VoxelsOnCartesianGrid - current_image(exam_info_sptr, - IndexRange3D(0,output_image_size_z-1, - -(output_image_size_y/2), - -(output_image_size_y/2)+output_image_size_y-1, - -(output_image_size_x/2), - -(output_image_size_x/2)+output_image_size_x-1), - CartesianCoordinate3D(0,0,0), - CartesianCoordinate3D(output_voxel_size_z, - output_voxel_size_y, - output_voxel_size_x)); - shared_ptr > out_density_ptr(current_image.clone()); + if (image_duration > 0.0) { + std::vector start_times(1, rel_start_time); + std::vector durations(1, image_duration); + TimeFrameDefinitions frame_defs(start_times, durations); + exam_info_sptr->set_time_frame_definitions(frame_defs); + } else { + warning("image duration not set, so time frame definitions will not be initialised"); + } + VoxelsOnCartesianGrid current_image( + exam_info_sptr, + IndexRange3D(0, output_image_size_z - 1, -(output_image_size_y / 2), -(output_image_size_y / 2) + output_image_size_y - 1, + -(output_image_size_x / 2), -(output_image_size_x / 2) + output_image_size_x - 1), + CartesianCoordinate3D(0, 0, 0), + CartesianCoordinate3D(output_voxel_size_z, output_voxel_size_y, output_voxel_size_x)); + shared_ptr> out_density_ptr(current_image.clone()); #endif - std::vector::const_iterator value_iter = values.begin(); - for (std::vector >::const_iterator iter = shape_ptrs.begin(); - iter != shape_ptrs.end(); - ++iter, ++value_iter) - { - std::cerr << "Next shape\n"; //(**iter).parameter_info() << '\n'; - current_image.fill(0); - (**iter).construct_volume(current_image, num_samples); - current_image *= *value_iter; - *out_density_ptr += current_image; - } - return - output_file_format_sptr->write_to_file(output_filename, *out_density_ptr); - + std::vector::const_iterator value_iter = values.begin(); + for (std::vector>::const_iterator iter = shape_ptrs.begin(); iter != shape_ptrs.end(); + ++iter, ++value_iter) { + std::cerr << "Next shape\n"; //(**iter).parameter_info() << '\n'; + current_image.fill(0); + (**iter).construct_volume(current_image, num_samples); + current_image *= *value_iter; + *out_density_ptr += current_image; + } + return output_file_format_sptr->write_to_file(output_filename, *out_density_ptr); } END_NAMESPACE_STIR @@ -422,15 +367,15 @@ END_NAMESPACE_STIR /************************ main ************************/ USING_NAMESPACE_STIR -int main(int argc, char * argv[]) -{ - - if ( argc!=2) { +int +main(int argc, char* argv[]) { + + if (argc != 2) { std::cerr << "Usage: " << argv[0] << " par_file\n"; exit(EXIT_FAILURE); } - GenerateImage application(argc==2 ? argv[1] : 0); + GenerateImage application(argc == 2 ? argv[1] : 0); Succeeded success = application.compute(); - return success==Succeeded::yes ? EXIT_SUCCESS : EXIT_FAILURE; + return success == Succeeded::yes ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/src/utilities/get_time_frame_info.cxx b/src/utilities/get_time_frame_info.cxx index bb6fd46037..f64f6fd830 100644 --- a/src/utilities/get_time_frame_info.cxx +++ b/src/utilities/get_time_frame_info.cxx @@ -37,9 +37,10 @@ using std::cout; USING_NAMESPACE_STIR -void print_usage_and_exit(char const * const prog_name) -{ - cerr << "Usage:\n" << prog_name << " PARAMETERS\n" +void +print_usage_and_exit(char const* const prog_name) { + cerr << "Usage:\n" + << prog_name << " PARAMETERS\n" << "where PARAMETERS has two possibilities:\n\n" << "1) print number of time frames\n" << "\t--num-time-frames Frame_def_filename\n\n" @@ -51,14 +52,13 @@ void print_usage_and_exit(char const * const prog_name) << "and similarly for --start-time, --mid-time and --end-time.\n" << "Without these options, both will be printed to stdout, together with some text.\n\n" << "Times are reported in seconds unless the --msecs option is used.\n\n" - << "end_frame_number defaults to start_frame_number to print just a single frame."<< endl; - exit(EXIT_FAILURE); + << "end_frame_number defaults to start_frame_number to print just a single frame." << endl; + exit(EXIT_FAILURE); } int -main(int argc, char* argv[]) -{ - const char * const prog_name = argv[0]; +main(int argc, char* argv[]) { + const char* const prog_name = argv[0]; bool units_secs = true; bool only_duration = false; @@ -67,108 +67,91 @@ main(int argc, char* argv[]) bool only_mid_time = false; bool only_num_time_frames = false; - while (argc>=2 && argv[1][1]=='-') - { - if (strcmp(argv[1], "--num-time-frames")==0) - { - only_num_time_frames = true; - --argc; ++argv; - } - else - { - if (strcmp(argv[1], "--msecs")==0) - { - units_secs=false; - --argc; ++argv; - } - else if (strcmp(argv[1], "--duration")==0 && !only_start_time && !only_end_time && !only_mid_time) - { - only_duration = true; - --argc; ++argv; - } - else if (strcmp(argv[1], "--start-time")==0 && !only_duration && !only_end_time && !only_mid_time) - { - only_start_time = true; - --argc; ++argv; - } - else if (strcmp(argv[1], "--end-time")==0 && !only_duration && !only_start_time && !only_mid_time) - { - only_end_time = true; - --argc; ++argv; - } - else if (strcmp(argv[1], "--mid-time")==0 && !only_duration && !only_start_time && !only_end_time) - { - only_mid_time = true; - --argc; ++argv; - } - else - print_usage_and_exit(prog_name); - } + while (argc >= 2 && argv[1][1] == '-') { + if (strcmp(argv[1], "--num-time-frames") == 0) { + only_num_time_frames = true; + --argc; + ++argv; + } else { + if (strcmp(argv[1], "--msecs") == 0) { + units_secs = false; + --argc; + ++argv; + } else if (strcmp(argv[1], "--duration") == 0 && !only_start_time && !only_end_time && !only_mid_time) { + only_duration = true; + --argc; + ++argv; + } else if (strcmp(argv[1], "--start-time") == 0 && !only_duration && !only_end_time && !only_mid_time) { + only_start_time = true; + --argc; + ++argv; + } else if (strcmp(argv[1], "--end-time") == 0 && !only_duration && !only_start_time && !only_mid_time) { + only_end_time = true; + --argc; + ++argv; + } else if (strcmp(argv[1], "--mid-time") == 0 && !only_duration && !only_start_time && !only_end_time) { + only_mid_time = true; + --argc; + ++argv; + } else + print_usage_and_exit(prog_name); } + } // we need at least one argument: the filename - if(argc <2) + if (argc < 2) print_usage_and_exit(prog_name); const TimeFrameDefinitions time_def(argv[1]); - if (only_num_time_frames) - { - if(argc !=2) - print_usage_and_exit(prog_name); - cout << time_def.get_num_frames() << std::endl; - exit(EXIT_SUCCESS); - } + if (only_num_time_frames) { + if (argc != 2) + print_usage_and_exit(prog_name); + cout << time_def.get_num_frames() << std::endl; + exit(EXIT_SUCCESS); + } // normal case of info for one or more frames - if(argc !=3 && argc!=4) + if (argc != 3 && argc != 4) print_usage_and_exit(prog_name); const unsigned int start_frame_num = atoi(argv[2]); - const unsigned int end_frame_num = - argc>3 ? atoi(argv[3]) : start_frame_num; - - - for (unsigned frame_num = start_frame_num; frame_num<=end_frame_num; ++frame_num) - { - if (frame_num > time_def.get_num_frames() || frame_num<1) - { - /* Note: we intentionally check this in the loop. - This way, we do get output for valid frames. - */ - warning("frame_num should be between 1 and %d\n", - time_def.get_num_frames()); - exit(EXIT_FAILURE); - } - const double start_frame = time_def.get_start_time(frame_num); - const double end_frame = time_def.get_end_time(frame_num); - const double frame_duration = end_frame-start_frame; - const double mid_frame = (start_frame+end_frame)/2; - - // make sure results never get printed as scientific - // because we pass it on to header_doc/edit_ecat_header - // which expects an int - cout.setf(std::ios::fixed, std::ios::floatfield); - - const int units = units_secs ? 1 : 1000; - const std::string units_string = units_secs ? " secs" : " millisecs"; - if (only_duration) - cout << frame_duration*units << endl; - else if (only_start_time) - cout << start_frame*units << endl; - else if (only_end_time) - cout << end_frame*units << endl; - else if (only_end_time) - cout << end_frame*units << endl; - else if (only_mid_time) - cout << mid_frame*units << endl; - else - cout << "Start of frame " << frame_num << " : " << start_frame*units << units_string - << "\nMiddle of frame " << frame_num << " : " << mid_frame*units << units_string - << "\nEnd of frame " << frame_num << " : " << end_frame*units << units_string - << "\nFrame duration " << frame_num << " : " << frame_duration*units << units_string << endl; - - + const unsigned int end_frame_num = argc > 3 ? atoi(argv[3]) : start_frame_num; + + for (unsigned frame_num = start_frame_num; frame_num <= end_frame_num; ++frame_num) { + if (frame_num > time_def.get_num_frames() || frame_num < 1) { + /* Note: we intentionally check this in the loop. + This way, we do get output for valid frames. + */ + warning("frame_num should be between 1 and %d\n", time_def.get_num_frames()); + exit(EXIT_FAILURE); } + const double start_frame = time_def.get_start_time(frame_num); + const double end_frame = time_def.get_end_time(frame_num); + const double frame_duration = end_frame - start_frame; + const double mid_frame = (start_frame + end_frame) / 2; + + // make sure results never get printed as scientific + // because we pass it on to header_doc/edit_ecat_header + // which expects an int + cout.setf(std::ios::fixed, std::ios::floatfield); + + const int units = units_secs ? 1 : 1000; + const std::string units_string = units_secs ? " secs" : " millisecs"; + if (only_duration) + cout << frame_duration * units << endl; + else if (only_start_time) + cout << start_frame * units << endl; + else if (only_end_time) + cout << end_frame * units << endl; + else if (only_end_time) + cout << end_frame * units << endl; + else if (only_mid_time) + cout << mid_frame * units << endl; + else + cout << "Start of frame " << frame_num << " : " << start_frame * units << units_string << "\nMiddle of frame " + << frame_num << " : " << mid_frame * units << units_string << "\nEnd of frame " << frame_num << " : " + << end_frame * units << units_string << "\nFrame duration " << frame_num << " : " << frame_duration * units + << units_string << endl; + } return EXIT_SUCCESS; } - diff --git a/src/utilities/invert_axis.cxx b/src/utilities/invert_axis.cxx old mode 100755 new mode 100644 index 67f50038f0..cb070da953 --- a/src/utilities/invert_axis.cxx +++ b/src/utilities/invert_axis.cxx @@ -28,34 +28,31 @@ USING_NAMESPACE_STIR -int main(int argc, char **argv) -{ - if(argc!=4) { - std::cerr << "Usage: " << argv[0] - << " \n" +int +main(int argc, char** argv) { + if (argc != 4) { + std::cerr << "Usage: " << argv[0] << " \n" << " has to be x, y, z\n" << "\nWARNING: this will reorder the voxel values without adjusting the geometric information\n"; exit(EXIT_FAILURE); } InvertAxis invert; - char const * const axis_name = argv[1]; - char const * const output_filename_prefix = argv[2]; - char const * const input_filename= argv[3]; + char const* const axis_name = argv[1]; + char const* const output_filename_prefix = argv[2]; + char const* const input_filename = argv[3]; std::string name(axis_name); - // const int num_planes = atoi(argv[3]); -std::cout<<" the axis to invert is "<< name< > image_aptr(DiscretisedDensity<3,float>::read_from_file(input_filename)); -const std::auto_ptr > out_image_aptr(image_aptr->clone()); + // const int num_planes = atoi(argv[3]); + std::cout << " the axis to invert is " << name << std::endl; + const std::auto_ptr> image_aptr(DiscretisedDensity<3, float>::read_from_file(input_filename)); + const std::auto_ptr> out_image_aptr(image_aptr->clone()); -DiscretisedDensity<3,float>& image = *image_aptr; -DiscretisedDensity<3,float>& out_image = *out_image_aptr; + DiscretisedDensity<3, float>& image = *image_aptr; + DiscretisedDensity<3, float>& out_image = *out_image_aptr; - invert.invert_axis(out_image, - image, - name); + invert.invert_axis(out_image, image, name); - const Succeeded res = OutputFileFormat >::default_sptr()-> - write_to_file(output_filename_prefix, out_image); + const Succeeded res = + OutputFileFormat>::default_sptr()->write_to_file(output_filename_prefix, out_image); - return res==Succeeded::yes ? EXIT_SUCCESS : EXIT_FAILURE; + return res == Succeeded::yes ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/src/utilities/list_ROI_values.cxx b/src/utilities/list_ROI_values.cxx index d9c9e62134..b9453e7e96 100644 --- a/src/utilities/list_ROI_values.cxx +++ b/src/utilities/list_ROI_values.cxx @@ -67,66 +67,57 @@ using std::endl; using std::ofstream; #endif - START_NAMESPACE_STIR -//TODO repetition of postfilter.cxx to be able to use its .par file -class ROIValuesParameters : public KeyParser -{ +// TODO repetition of postfilter.cxx to be able to use its .par file +class ROIValuesParameters : public KeyParser { public: ROIValuesParameters(); virtual void set_defaults(); virtual void initialise_keymap(); virtual bool post_processing(); - std::vector > shape_ptrs; + std::vector> shape_ptrs; std::vector shape_names; CartesianCoordinate3D num_samples; - shared_ptr > > filter_ptr; + shared_ptr>> filter_ptr; + private: shared_ptr current_shape_sptr; std::string current_shape_name; void increment_current_shape_num(); }; -ROIValuesParameters::ROIValuesParameters() -{ +ROIValuesParameters::ROIValuesParameters() { set_defaults(); initialise_keymap(); } -void ROIValuesParameters:: -increment_current_shape_num() -{ - if (!is_null_ptr( current_shape_sptr)) - { - shape_ptrs.push_back(current_shape_sptr); - shape_names.push_back(current_shape_name); - current_shape_sptr.reset(); - current_shape_name = ""; - } +void +ROIValuesParameters::increment_current_shape_num() { + if (!is_null_ptr(current_shape_sptr)) { + shape_ptrs.push_back(current_shape_sptr); + shape_names.push_back(current_shape_name); + current_shape_sptr.reset(); + current_shape_name = ""; + } } void -ROIValuesParameters:: -set_defaults() -{ +ROIValuesParameters::set_defaults() { shape_ptrs.resize(0); shape_names.resize(0); filter_ptr.reset(); current_shape_sptr.reset(); current_shape_name = ""; - num_samples = CartesianCoordinate3D(1,1,1); + num_samples = CartesianCoordinate3D(1, 1, 1); } void -ROIValuesParameters:: -initialise_keymap() -{ +ROIValuesParameters::initialise_keymap() { add_start_key("ROIValues Parameters"); add_key("ROI name", ¤t_shape_name); add_parsing_key("ROI Shape type", ¤t_shape_sptr); - add_key("next shape", KeyArgument::NONE, - (KeywordProcessor)&ROIValuesParameters::increment_current_shape_num); + add_key("next shape", KeyArgument::NONE, (KeywordProcessor)&ROIValuesParameters::increment_current_shape_num); add_key("number of samples to take for ROI template-z", &num_samples.z()); add_key("number of samples to take for ROI template-y", &num_samples.y()); add_key("number of samples to take for ROI template-x", &num_samples.x()); @@ -135,30 +126,24 @@ initialise_keymap() } bool -ROIValuesParameters:: -post_processing() -{ +ROIValuesParameters::post_processing() { assert(shape_names.size() == shape_ptrs.size()); - if (!is_null_ptr( current_shape_sptr)) - { - increment_current_shape_num(); - } - if (num_samples.z()<=0) - { - warning("number of samples to take in z-direction should be strictly positive\n"); - return true; - } - if (num_samples.y()<=0) - { - warning("number of samples to take in y-direction should be strictly positive\n"); - return true; - } - if (num_samples.x()<=0) - { - warning("number of samples to take in x-direction should be strictly positive\n"); - return true; - } + if (!is_null_ptr(current_shape_sptr)) { + increment_current_shape_num(); + } + if (num_samples.z() <= 0) { + warning("number of samples to take in z-direction should be strictly positive\n"); + return true; + } + if (num_samples.y() <= 0) { + warning("number of samples to take in y-direction should be strictly positive\n"); + return true; + } + if (num_samples.x() <= 0) { + warning("number of samples to take in x-direction should be strictly positive\n"); + return true; + } return false; } @@ -167,143 +152,123 @@ END_NAMESPACE_STIR USING_NAMESPACE_STIR int -main(int argc, char *argv[]) -{ - bool do_CV=false; - bool do_V=false; - bool do_filename=false; - bool do_max=false; - bool do_min=false; - - const char * const progname = argv[0]; - -while (argc>1 && strncmp(argv[1],"--",2)==0) -{ - if(strcmp(argv[1],"--min")==0) - do_min=true; - else if(strcmp(argv[1],"--max")==0) - do_max=true; - else if(strcmp(argv[1],"--list-filename")==0) - do_filename=true; - else if(strcmp(argv[1],"--CV")==0) - do_CV=true; - else if(strcmp(argv[1],"--V")==0) - do_V=true; - else - error(boost::format("Unknown option %s") % argv[1]); - --argc; ++argv; -} - +main(int argc, char* argv[]) { + bool do_CV = false; + bool do_V = false; + bool do_filename = false; + bool do_max = false; + bool do_min = false; + + const char* const progname = argv[0]; + + while (argc > 1 && strncmp(argv[1], "--", 2) == 0) { + if (strcmp(argv[1], "--min") == 0) + do_min = true; + else if (strcmp(argv[1], "--max") == 0) + do_max = true; + else if (strcmp(argv[1], "--list-filename") == 0) + do_filename = true; + else if (strcmp(argv[1], "--CV") == 0) + do_CV = true; + else if (strcmp(argv[1], "--V") == 0) + do_V = true; + else + error(boost::format("Unknown option %s") % argv[1]); + --argc; + ++argv; + } - if(argc!=6 && argc!=5 && argc!=4 && argc!=3) - { - cerr<<"\nUsage: " << progname << " \\\n" - << "\t[--CV] [--V] [--list-filename] [--max] [--min] output_filename data_filename [ ROI_filename.par [min_plane_num max_plane_num]]\n"; + if (argc != 6 && argc != 5 && argc != 4 && argc != 3) { + cerr << "\nUsage: " << progname << " \\\n" + << "\t[--CV] [--V] [--list-filename] [--max] [--min] output_filename data_filename [ ROI_filename.par [min_plane_num " + "max_plane_num]]\n"; cerr << "Normally, only mean and stddev are listed.\n" - << "Use the option --CV to output the Coefficient of Variation as well.\n" - << "Use the option --V to output the Total Volume, as well.\n" - << "Use the option --list-filename to output the filename as well.\n" - << "Use the option --max to output the max value as well.\n" - << "Use the option --min to output the min as well.\n";; + << "Use the option --CV to output the Coefficient of Variation as well.\n" + << "Use the option --V to output the Total Volume, as well.\n" + << "Use the option --list-filename to output the filename as well.\n" + << "Use the option --max to output the max value as well.\n" + << "Use the option --min to output the min as well.\n"; + ; cerr << "If [min_plane_num] is set to 0 and no [max_plane_num given] then sum of the plane values will be listed.\n"; cerr << "When ROI_filename.par is not given, the user will be asked for the parameters.\n" - "Use this to see what a .par file should look like.\n."< > - image_ptr(read_from_file >(input_file)); + shared_ptr> image_ptr(read_from_file>(input_file)); ROIValuesParameters parameters; - if (argc<4) + if (argc < 4) parameters.ask_parameters(); - else - { - if (parameters.parse(argv[3]) == false) - exit(EXIT_FAILURE); - } + else { + if (parameters.parse(argv[3]) == false) + exit(EXIT_FAILURE); + } cerr << "Parameters used (aside from names and ROIs):\n\n" << parameters.parameter_info() << endl; + const int min_plane_number = argc == 6 ? atoi(argv[4]) - 1 : image_ptr->get_min_index(); + const int max_plane_number = argc == 6 ? atoi(argv[5]) - 1 : image_ptr->get_max_index(); - const int min_plane_number = - argc==6 ? atoi(argv[4])-1 : image_ptr->get_min_index(); - const int max_plane_number = - argc==6 ? atoi(argv[5])-1 : image_ptr->get_max_index(); - - const bool by_plane=argc==5 ? (atoi(argv[4])!=0):true ; + const bool by_plane = argc == 5 ? (atoi(argv[4]) != 0) : true; if (!is_null_ptr(parameters.filter_ptr)) parameters.filter_ptr->apply(*image_ptr); -if (do_filename) - out << std::setw(15) << "ImageName"; -else - out << input_file << '\n'; + if (do_filename) + out << std::setw(15) << "ImageName"; + else + out << input_file << '\n'; out << std::setw(15) << "ROI"; - if(by_plane) - out << std::setw(10) << "Plane_num" ; - out << std::setw(15) << "Mean " - << std::setw(15) << "Stddev"; - if(do_max) - out << std::setw(15) << "Max "; - if(do_min) - out << std::setw(15) << "Min "; - if (do_CV) - out << std::setw(15) << "CV"; - if (do_V) - out << std::setw(15) << "Volume"; - out <<'\n'; + if (by_plane) + out << std::setw(10) << "Plane_num"; + out << std::setw(15) << "Mean " << std::setw(15) << "Stddev"; + if (do_max) + out << std::setw(15) << "Max "; + if (do_min) + out << std::setw(15) << "Min "; + if (do_CV) + out << std::setw(15) << "CV"; + if (do_V) + out << std::setw(15) << "Volume"; + out << '\n'; { - std::vector >::const_iterator current_shape_iter = - parameters.shape_ptrs.begin(); - std::vector::const_iterator current_name_iter = - parameters.shape_names.begin(); - for (; - current_shape_iter != parameters.shape_ptrs.end(); - ++current_shape_iter, ++current_name_iter) - { - if(by_plane) - { - VectorWithOffset values; - compute_ROI_values_per_plane(values, *image_ptr, **current_shape_iter, parameters.num_samples); - - for (int i=min_plane_number;i<=max_plane_number;i++) - { - if (do_filename) - out << std::setw(15) <>::const_iterator current_shape_iter = parameters.shape_ptrs.begin(); + std::vector::const_iterator current_name_iter = parameters.shape_names.begin(); + for (; current_shape_iter != parameters.shape_ptrs.end(); ++current_shape_iter, ++current_name_iter) { + if (by_plane) { + VectorWithOffset values; + compute_ROI_values_per_plane(values, *image_ptr, **current_shape_iter, parameters.num_samples); + + for (int i = min_plane_number; i <= max_plane_number; i++) { + if (do_filename) + out << std::setw(15) << input_file; + out << std::setw(15) << *current_name_iter << std::setw(10) << i + 1 << std::setw(15) << values[i].get_mean() + << std::setw(15) << values[i].get_stddev(); + if (do_max) + out << std::setw(15) << values[i].get_max(); + if (do_min) + out << std::setw(15) << values[i].get_min(); + if (do_CV) + out << std::setw(15) << values[i].get_CV(); + if (do_V) + out << std::setw(15) << values[i].get_roi_volume(); + out << '\n'; + } } - } - if(!by_plane) - { + if (!by_plane) { ROIValues values; - values=compute_total_ROI_values(*image_ptr, **current_shape_iter, parameters.num_samples); + values = compute_total_ROI_values(*image_ptr, **current_shape_iter, parameters.num_samples); if (do_filename) - out << std::setw(15) <::const_iterator iter = values.begin(); @@ -322,7 +287,7 @@ else std::cout << iter->report(); } #endif - } + } } return EXIT_SUCCESS; diff --git a/src/utilities/list_detector_and_bin_info.cxx b/src/utilities/list_detector_and_bin_info.cxx index a79ab3e032..c95c0fef6b 100644 --- a/src/utilities/list_detector_and_bin_info.cxx +++ b/src/utilities/list_detector_and_bin_info.cxx @@ -32,7 +32,7 @@ This is really only useful for developers who need to get their head round the STIR conventions. - Currently bin info is listed for non-arccorrected projection data without any + Currently bin info is listed for non-arccorrected projection data without any compression. \author Kris Thielemans @@ -44,38 +44,28 @@ #include "stir/Bin.h" #include "stir/Scanner.h" #include "stir/stream.h" -#include +#include USING_NAMESPACE_STIR -int main(int argc, char *argv[]) -{ - - if(argc!=6) - { - std::cerr<<"Usage: " << argv[0] << " scanner_name crystal1 crystal2 ring1 ring2\n"; - std::cerr<<"\nLists detection and bin info for non-arccorrected projection data without any compression.\n"; - return EXIT_FAILURE; +int +main(int argc, char* argv[]) { + if (argc != 6) { + std::cerr << "Usage: " << argv[0] << " scanner_name crystal1 crystal2 ring1 ring2\n"; + std::cerr << "\nLists detection and bin info for non-arccorrected projection data without any compression.\n"; + return EXIT_FAILURE; } shared_ptr scanner_sptr(Scanner::get_scanner_from_name(argv[1])); - if (scanner_sptr->get_type() == Scanner::Unknown_scanner) - { - std::cerr << "I did not recognise the scanner\n"; - return (EXIT_FAILURE); - } - - shared_ptr proj_data_info_sptr - (dynamic_cast - ( - ProjDataInfo::ProjDataInfoCTI(scanner_sptr, - 1, scanner_sptr->get_num_rings()-1, - scanner_sptr->get_num_detectors_per_ring()/2, - scanner_sptr->get_default_num_arccorrected_bins(), - false) - )); - + if (scanner_sptr->get_type() == Scanner::Unknown_scanner) { + std::cerr << "I did not recognise the scanner\n"; + return (EXIT_FAILURE); + } + shared_ptr proj_data_info_sptr(dynamic_cast( + ProjDataInfo::ProjDataInfoCTI(scanner_sptr, 1, scanner_sptr->get_num_rings() - 1, + scanner_sptr->get_num_detectors_per_ring() / 2, + scanner_sptr->get_default_num_arccorrected_bins(), false))); { using std::cout; @@ -89,35 +79,26 @@ int main(int argc, char *argv[]) DetectionPositionPair<> det_pos; LORInAxialAndNoArcCorrSinogramCoordinates lor; - proj_data_info_sptr->get_bin_for_det_pair (bin, - det_num_a, ring_a, det_num_b, ring_b); - cout << "bin: (segment " << bin.segment_num() << ", axial pos " << bin.axial_pos_num() - << ", view = " << bin.view_num() + proj_data_info_sptr->get_bin_for_det_pair(bin, det_num_a, ring_a, det_num_b, ring_b); + cout << "bin: (segment " << bin.segment_num() << ", axial pos " << bin.axial_pos_num() << ", view = " << bin.view_num() << ", tangential_pos_num = " << bin.tangential_pos_num() << ")\n"; - cout << "bin coordinates: (tantheta: " << proj_data_info_sptr->get_tantheta(bin) - << ", m: " << proj_data_info_sptr->get_m(bin) - << ", phi: " << proj_data_info_sptr->get_phi(bin) - << ", s: " << proj_data_info_sptr->get_s(bin) - << ")\n"; + cout << "bin coordinates: (tantheta: " << proj_data_info_sptr->get_tantheta(bin) << ", m: " << proj_data_info_sptr->get_m(bin) + << ", phi: " << proj_data_info_sptr->get_phi(bin) << ", s: " << proj_data_info_sptr->get_s(bin) << ")\n"; proj_data_info_sptr->get_LOR(lor, bin); - cout << "LOR cylindrical: (z1: " << lor.z1() << ", z2: " << lor.z2() - << ", phi: " << lor.phi() << ", beta: " << lor.beta() << " (= s: " << lor.s() << ")" - << ")\n"; + cout << "LOR cylindrical: (z1: " << lor.z1() << ", z2: " << lor.z2() << ", phi: " << lor.phi() << ", beta: " << lor.beta() + << " (= s: " << lor.s() << ")" + << ")\n"; LORAs2Points lor_points; lor.get_intersections_with_cylinder(lor_points, scanner_sptr->get_effective_ring_radius()); - cout << "Detection position Cartesian: " << lor_points.p1() << lor_points.p2() <<'\n'; + cout << "Detection position Cartesian: " << lor_points.p1() << lor_points.p2() << '\n'; proj_data_info_sptr->get_det_pos_pair_for_bin(det_pos, bin); cout << "Detection position index " - <<"(c:" << det_pos.pos1().tangential_coord() - << ",r:" << det_pos.pos1().axial_coord() - << ",l:" << det_pos.pos1().radial_coord() - << ")-" - << "(c:" << det_pos.pos2().tangential_coord() - << ",r:" << det_pos.pos2().axial_coord() - << ",l:" << det_pos.pos2().radial_coord() - << ")"; + << "(c:" << det_pos.pos1().tangential_coord() << ",r:" << det_pos.pos1().axial_coord() + << ",l:" << det_pos.pos1().radial_coord() << ")-" + << "(c:" << det_pos.pos2().tangential_coord() << ",r:" << det_pos.pos2().axial_coord() + << ",l:" << det_pos.pos2().radial_coord() << ")"; #if 0 { diff --git a/src/utilities/list_image_info.cxx b/src/utilities/list_image_info.cxx index bf9d3fce25..e9fac87107 100644 --- a/src/utilities/list_image_info.cxx +++ b/src/utilities/list_image_info.cxx @@ -19,9 +19,9 @@ See STIR/LICENSE.txt for details */ /*! - \file + \file \ingroup utilities - + \brief This program lists basic image info. It works for dynamic images. Exam info and numerical info can be listed, depending on command line options. Run the utility to get a usage message. @@ -42,18 +42,18 @@ USING_NAMESPACE_STIR -static void print_usage_and_exit(const std::string& program_name) -{ - std::cerr<<"Usage: " << program_name << " [--all | --min | --max | --sum | --exam | --per-volume] image_file\n" - <<"\nAdd one or more options to print the exam/geometric/min/max/sum information.\n" - <<"\nIf no option is specified, geometric/min/max/sum info is printed." - <<"For dynamic images, overall min/max/sum are printed, unless the --per-volume option is specified.\n"; +static void +print_usage_and_exit(const std::string& program_name) { + std::cerr << "Usage: " << program_name << " [--all | --min | --max | --sum | --exam | --per-volume] image_file\n" + << "\nAdd one or more options to print the exam/geometric/min/max/sum information.\n" + << "\nIf no option is specified, geometric/min/max/sum info is printed." + << "For dynamic images, overall min/max/sum are printed, unless the --per-volume option is specified.\n"; exit(EXIT_FAILURE); } -int main(int argc, char *argv[]) -{ - const char * const program_name = argv[0]; +int +main(int argc, char* argv[]) { + const char* const program_name = argv[0]; // skip program name --argc; ++argv; @@ -68,57 +68,47 @@ int main(int argc, char *argv[]) bool no_options = true; // need this for default behaviour // first process command line options - while (argc>0 && argv[0][0]=='-' && argc>=2) - { - no_options=false; - if (strcmp(argv[0], "--all")==0) - { - print_min = print_max = print_sum = print_geom = print_exam = true; - --argc; ++argv; - } - else if (strcmp(argv[0], "--per-volume")==0) - { - print_per_volume = true; - --argc; ++argv; - } - else if (strcmp(argv[0], "--max")==0) - { - print_max = true; - --argc; ++argv; - } - else if (strcmp(argv[0], "--min")==0) - { - print_min = true; - --argc; ++argv; - } - else if (strcmp(argv[0], "--sum")==0) - { - print_sum = true; - --argc; ++argv; - } - else if (strcmp(argv[0], "--geom")==0) - { - print_geom = true; - --argc; ++argv; - } - else if (strcmp(argv[0], "--exam")==0) - { - print_exam = true; - --argc; ++argv; - } - else - print_usage_and_exit(program_name); - } - if (no_options) - { - print_geom = true; - print_min = true; + while (argc > 0 && argv[0][0] == '-' && argc >= 2) { + no_options = false; + if (strcmp(argv[0], "--all") == 0) { + print_min = print_max = print_sum = print_geom = print_exam = true; + --argc; + ++argv; + } else if (strcmp(argv[0], "--per-volume") == 0) { + print_per_volume = true; + --argc; + ++argv; + } else if (strcmp(argv[0], "--max") == 0) { print_max = true; + --argc; + ++argv; + } else if (strcmp(argv[0], "--min") == 0) { + print_min = true; + --argc; + ++argv; + } else if (strcmp(argv[0], "--sum") == 0) { print_sum = true; - } + --argc; + ++argv; + } else if (strcmp(argv[0], "--geom") == 0) { + print_geom = true; + --argc; + ++argv; + } else if (strcmp(argv[0], "--exam") == 0) { + print_exam = true; + --argc; + ++argv; + } else + print_usage_and_exit(program_name); + } + if (no_options) { + print_geom = true; + print_min = true; + print_max = true; + print_sum = true; + } - if(argc!=1) - { + if (argc != 1) { print_usage_and_exit(program_name); } @@ -129,55 +119,46 @@ int main(int argc, char *argv[]) if (print_exam) std::cout << image_aptr->get_exam_info_sptr()->parameter_info(); - if (print_geom) - { - BasicCoordinate<3,int> min_indices, max_indices; - auto vox = dynamic_cast &>( image_aptr->get_density(1)); - if (!vox.get_regular_range(min_indices, max_indices)) - error("Non-regular range of coordinates. That's strange."); - - BasicCoordinate<3,float> edge_min_indices(min_indices), edge_max_indices(max_indices); - edge_min_indices-= 0.5F; - edge_max_indices+= 0.5F; - - std::cout << "\nOrigin in mm {z,y,x} :" << vox.get_origin() - << "\nVoxel-size in mm {z,y,x}:" << vox.get_voxel_size() - << "\nMin_indices {z,y,x} :" << min_indices - << "\nMax_indices {z,y,x} :" << max_indices - << "\nNumber of voxels {z,y,x}:" << max_indices - min_indices + 1 - << "\nPhysical coordinate of first index in mm {z,y,x} :" - << vox.get_physical_coordinates_for_indices(min_indices) - << "\nPhysical coordinate of last index in mm {z,y,x} :" - << vox.get_physical_coordinates_for_indices(max_indices) - << "\nPhysical coordinate of first edge in mm {z,y,x} :" - << vox.get_physical_coordinates_for_indices(edge_min_indices) - << "\nPhysical coordinate of last edge in mm {z,y,x} :" - << vox.get_physical_coordinates_for_indices(edge_max_indices); - } + if (print_geom) { + BasicCoordinate<3, int> min_indices, max_indices; + auto vox = dynamic_cast&>(image_aptr->get_density(1)); + if (!vox.get_regular_range(min_indices, max_indices)) + error("Non-regular range of coordinates. That's strange."); + + BasicCoordinate<3, float> edge_min_indices(min_indices), edge_max_indices(max_indices); + edge_min_indices -= 0.5F; + edge_max_indices += 0.5F; + + std::cout << "\nOrigin in mm {z,y,x} :" << vox.get_origin() << "\nVoxel-size in mm {z,y,x}:" << vox.get_voxel_size() + << "\nMin_indices {z,y,x} :" << min_indices << "\nMax_indices {z,y,x} :" << max_indices + << "\nNumber of voxels {z,y,x}:" << max_indices - min_indices + 1 + << "\nPhysical coordinate of first index in mm {z,y,x} :" << vox.get_physical_coordinates_for_indices(min_indices) + << "\nPhysical coordinate of last index in mm {z,y,x} :" << vox.get_physical_coordinates_for_indices(max_indices) + << "\nPhysical coordinate of first edge in mm {z,y,x} :" + << vox.get_physical_coordinates_for_indices(edge_min_indices) + << "\nPhysical coordinate of last edge in mm {z,y,x} :" + << vox.get_physical_coordinates_for_indices(edge_max_indices); + } - if (print_per_volume) - { - for (unsigned vol = 1U; vol <= image_aptr->get_num_time_frames(); ++vol) - { - auto& volume = image_aptr->get_density(vol); - if (print_min) - std::cout << "\nImage " << vol << " min: " << *std::min_element(volume.begin_all_const(), volume.end_all_const()); - if (print_max) - std::cout << "\nImage " << vol << " max: " << *std::max_element(volume.begin_all_const(), volume.end_all_const()); - if (print_sum) - std::cout<< "\nImage " << vol << " sum: " << std::accumulate(volume.begin_all_const(), volume.end_all_const(), 0.F); - std::cout << std::endl; - } - } - else - { + if (print_per_volume) { + for (unsigned vol = 1U; vol <= image_aptr->get_num_time_frames(); ++vol) { + auto& volume = image_aptr->get_density(vol); if (print_min) - std::cout << "\nImage min: " << *std::min_element(image_aptr->begin_all_const(), image_aptr->end_all_const()); + std::cout << "\nImage " << vol << " min: " << *std::min_element(volume.begin_all_const(), volume.end_all_const()); if (print_max) - std::cout << "\nImage max: " << *std::max_element(image_aptr->begin_all_const(), image_aptr->end_all_const()); + std::cout << "\nImage " << vol << " max: " << *std::max_element(volume.begin_all_const(), volume.end_all_const()); if (print_sum) - std::cout<< "\nImage sum: " << std::accumulate(image_aptr->begin_all_const(), image_aptr->end_all_const(), 0.F); + std::cout << "\nImage " << vol << " sum: " << std::accumulate(volume.begin_all_const(), volume.end_all_const(), 0.F); std::cout << std::endl; } + } else { + if (print_min) + std::cout << "\nImage min: " << *std::min_element(image_aptr->begin_all_const(), image_aptr->end_all_const()); + if (print_max) + std::cout << "\nImage max: " << *std::max_element(image_aptr->begin_all_const(), image_aptr->end_all_const()); + if (print_sum) + std::cout << "\nImage sum: " << std::accumulate(image_aptr->begin_all_const(), image_aptr->end_all_const(), 0.F); + std::cout << std::endl; + } return EXIT_SUCCESS; } diff --git a/src/utilities/list_image_values.cxx b/src/utilities/list_image_values.cxx index 22f089e6b1..5eb029c5df 100644 --- a/src/utilities/list_image_values.cxx +++ b/src/utilities/list_image_values.cxx @@ -40,100 +40,86 @@ #include #include - - USING_NAMESPACE_STIR -const char * prog_name; +const char* prog_name; -void print_usage_and_exit() -{ - std::cerr << "Usage:\n" << prog_name << " \\\n" +void +print_usage_and_exit() { + std::cerr << "Usage:\n" + << prog_name << " \\\n" << " [ --LPS-output] [--csv] [--no-title-row] \\\n" << " output_profile_name input_image min_plane max_plane min_col max_col min_row max_row\n" << "Indices need to be in the STIR convention (plane starts from 0, col,row are centred around 0)\n" << "Writes 4 columns to file, normally \"plane row column value\", unless --LPS-output is on,\n" << " in which case it writes \"L P S value\"\n" << "Output is separated by spaces, unless --csv is on, in which case commas are used.\n"; - + exit(EXIT_FAILURE); } -int main(int argc, const char *argv[]) -{ +int +main(int argc, const char* argv[]) { bool print_LPS = false; bool print_csv = false; bool print_first_line = true; prog_name = argv[0]; - while (argc>1 && (strncmp(argv[1],"--",2)==0)) - { - if (strcmp(argv[1],"--LPS-output")==0) - print_LPS=true; - else if ((strcmp(argv[1],"--csv")==0) || (strcmp(argv[1],"--CSV")==0)) - print_csv=true; - else if (strcmp(argv[1],"--no-title-row")==0) - print_first_line = false; - else - print_usage_and_exit(); - ++argv; --argc; - } - - if (argc!= 9) + while (argc > 1 && (strncmp(argv[1], "--", 2) == 0)) { + if (strcmp(argv[1], "--LPS-output") == 0) + print_LPS = true; + else if ((strcmp(argv[1], "--csv") == 0) || (strcmp(argv[1], "--CSV") == 0)) + print_csv = true; + else if (strcmp(argv[1], "--no-title-row") == 0) + print_first_line = false; + else + print_usage_and_exit(); + ++argv; + --argc; + } + + if (argc != 9) print_usage_and_exit(); - const char * const output_filename = argv[1]; - const char * const input_filename = argv[2]; + const char* const output_filename = argv[1]; + const char* const input_filename = argv[2]; + + shared_ptr> input_image_sptr(read_from_file>(input_filename)); + const DiscretisedDensity<3, float>& input_image = *input_image_sptr; - - shared_ptr > - input_image_sptr(read_from_file >(input_filename)); - const DiscretisedDensity<3,float>& input_image = - *input_image_sptr; - const int min_plane_num = atoi(argv[3]); - const int max_plane_num = atoi(argv[4]); + const int max_plane_num = atoi(argv[4]); const int min_column_num = atoi(argv[5]); - const int max_column_num = atoi(argv[6]); - const int min_row_num = atoi(argv[7]); - const int max_row_num = atoi(argv[8]); - - std::ofstream profile_file(output_filename); + const int max_column_num = atoi(argv[6]); + const int min_row_num = atoi(argv[7]); + const int max_row_num = atoi(argv[8]); + + std::ofstream profile_file(output_filename); using std::setw; - const char separator = print_csv ? ',' : ' ' ; - - - if (print_first_line) - { - if (print_LPS) - profile_file << setw(8) << "L" << separator << setw(8) << "P" - << separator << setw(8) << "S" << separator << setw(10) << "value" <<'\n'; - else - profile_file << setw(8) << "plane" << separator << setw(8) << "row" - << separator << setw(8) << "column" << separator << setw(10) << "value" <<'\n'; - } - - for (int plane = min_plane_num;plane <=max_plane_num;plane++) - for (int row = min_row_num;row <=max_row_num;row++) - for (int column = min_column_num;column<=max_column_num;column++) - { - const BasicCoordinate<3,int> index = make_coordinate(plane, row, column); - if (print_LPS) - { - const CartesianCoordinate3D LPS = - input_image.get_LPS_coordinates_for_indices(index); - profile_file << setw(8) << LPS[3] << separator << setw(8) << LPS[2] - << separator << setw(8) << LPS[1]; - } - else - { - profile_file << setw(8) << plane << separator << setw(8) << row - << separator << setw(8) << column; - } + const char separator = print_csv ? ',' : ' '; + + if (print_first_line) { + if (print_LPS) + profile_file << setw(8) << "L" << separator << setw(8) << "P" << separator << setw(8) << "S" << separator << setw(10) + << "value" << '\n'; + else + profile_file << setw(8) << "plane" << separator << setw(8) << "row" << separator << setw(8) << "column" << separator + << setw(10) << "value" << '\n'; + } + + for (int plane = min_plane_num; plane <= max_plane_num; plane++) + for (int row = min_row_num; row <= max_row_num; row++) + for (int column = min_column_num; column <= max_column_num; column++) { + const BasicCoordinate<3, int> index = make_coordinate(plane, row, column); + if (print_LPS) { + const CartesianCoordinate3D LPS = input_image.get_LPS_coordinates_for_indices(index); + profile_file << setw(8) << LPS[3] << separator << setw(8) << LPS[2] << separator << setw(8) << LPS[1]; + } else { + profile_file << setw(8) << plane << separator << setw(8) << row << separator << setw(8) << column; + } profile_file << separator << setw(10) << input_image.at(index) << '\n'; } - + return EXIT_SUCCESS; } - diff --git a/src/utilities/list_projdata_info.cxx b/src/utilities/list_projdata_info.cxx index c64f50bc31..9dd5b4c199 100644 --- a/src/utilities/list_projdata_info.cxx +++ b/src/utilities/list_projdata_info.cxx @@ -41,23 +41,23 @@ #include "stir/ProjDataInfo.h" #include "stir/SegmentByView.h" #include "stir/is_null_ptr.h" -#include +#include #include #include USING_NAMESPACE_STIR -void print_usage_and_exit(const std::string& program_name) -{ - std::cerr<<"Usage: " << program_name << " [--all | --min | --max | --sum | --geom | --exam] projdata_file\n" - <<"\nAdd one or more options to print the exam/geometric/min/max/sum information.\n" - <<"\nIf no option is specified, geometric info is printed.\n"; +void +print_usage_and_exit(const std::string& program_name) { + std::cerr << "Usage: " << program_name << " [--all | --min | --max | --sum | --geom | --exam] projdata_file\n" + << "\nAdd one or more options to print the exam/geometric/min/max/sum information.\n" + << "\nIf no option is specified, geometric info is printed.\n"; exit(EXIT_FAILURE); } -int main(int argc, char *argv[]) -{ - const char * const program_name = argv[0]; +int +main(int argc, char* argv[]) { + const char* const program_name = argv[0]; // skip program name --argc; ++argv; @@ -71,47 +71,39 @@ int main(int argc, char *argv[]) bool no_options = true; // need this for default behaviour // first process command line options - while (argc>0 && argv[0][0]=='-' && argc>=2) - { - no_options=false; - if (strcmp(argv[0], "--all")==0) - { - print_min = print_max = print_sum = print_geom = print_exam = true; - --argc; ++argv; - } - else if (strcmp(argv[0], "--max")==0) - { - print_max = true; - --argc; ++argv; - } - else if (strcmp(argv[0], "--min")==0) - { - print_min = true; - --argc; ++argv; - } - else if (strcmp(argv[0], "--sum")==0) - { - print_sum = true; - --argc; ++argv; - } - else if (strcmp(argv[0], "--geom")==0) - { - print_geom = true; - --argc; ++argv; - } - else if (strcmp(argv[0], "--exam")==0) - { - print_exam = true; - --argc; ++argv; - } - else - print_usage_and_exit(program_name); - } + while (argc > 0 && argv[0][0] == '-' && argc >= 2) { + no_options = false; + if (strcmp(argv[0], "--all") == 0) { + print_min = print_max = print_sum = print_geom = print_exam = true; + --argc; + ++argv; + } else if (strcmp(argv[0], "--max") == 0) { + print_max = true; + --argc; + ++argv; + } else if (strcmp(argv[0], "--min") == 0) { + print_min = true; + --argc; + ++argv; + } else if (strcmp(argv[0], "--sum") == 0) { + print_sum = true; + --argc; + ++argv; + } else if (strcmp(argv[0], "--geom") == 0) { + print_geom = true; + --argc; + ++argv; + } else if (strcmp(argv[0], "--exam") == 0) { + print_exam = true; + --argc; + ++argv; + } else + print_usage_and_exit(program_name); + } if (no_options) print_geom = true; - if(argc!=1) - { + if (argc != 1) { print_usage_and_exit(program_name); } @@ -120,57 +112,53 @@ int main(int argc, char *argv[]) shared_ptr proj_data_sptr(ProjData::read_from_file(filename)); - if (is_null_ptr(proj_data_sptr)) - { - warning("Could not read %s", filename.c_str()); - return EXIT_FAILURE; - } + if (is_null_ptr(proj_data_sptr)) { + warning("Could not read %s", filename.c_str()); + return EXIT_FAILURE; + } if (print_exam) std::cout << proj_data_sptr->get_exam_info_sptr()->parameter_info(); if (print_geom) std::cout << proj_data_sptr->get_proj_data_info_sptr()->parameter_info() << std::endl; - if (print_min || print_max || print_sum) - { - const int min_segment_num = proj_data_sptr->get_min_segment_num(); - const int max_segment_num = proj_data_sptr->get_max_segment_num(); - const int min_timing_num = proj_data_sptr->get_min_tof_pos_num(); - const int max_timing_num = proj_data_sptr->get_max_tof_pos_num(); - - for (int timing_num = min_timing_num; timing_num <= max_timing_num; ++timing_num) - { - std::cout << "\nTOF bin: " << timing_num; - bool accumulators_initialized = false; - float accum_min=std::numeric_limits::max(); // initialize to very large in case projdata is empty (although that's unlikely) - float accum_max=std::numeric_limits::min(); - double sum=0.; - for (int segment_num = min_segment_num; segment_num<= max_segment_num; ++segment_num) - { - const SegmentByView seg(proj_data_sptr->get_segment_by_view(segment_num, timing_num)); - const float this_max=seg.find_max(); - const float this_min=seg.find_min(); - sum+=static_cast(seg.sum()); - if(!accumulators_initialized) - { - accum_max=this_max; - accum_min=this_min; - accumulators_initialized=true; - } - else - { - if (accum_maxthis_min) accum_min=this_min; - } - } - if (print_min) - std::cout << "\nData min: " << accum_min; - if (print_max) - std::cout << "\nData max: " << accum_max; - if (print_sum) - std::cout << "\nData sum: " << sum; - std::cout << "\n"; + if (print_min || print_max || print_sum) { + const int min_segment_num = proj_data_sptr->get_min_segment_num(); + const int max_segment_num = proj_data_sptr->get_max_segment_num(); + const int min_timing_num = proj_data_sptr->get_min_tof_pos_num(); + const int max_timing_num = proj_data_sptr->get_max_tof_pos_num(); + + for (int timing_num = min_timing_num; timing_num <= max_timing_num; ++timing_num) { + std::cout << "\nTOF bin: " << timing_num; + bool accumulators_initialized = false; + float accum_min = + std::numeric_limits::max(); // initialize to very large in case projdata is empty (although that's unlikely) + float accum_max = std::numeric_limits::min(); + double sum = 0.; + for (int segment_num = min_segment_num; segment_num <= max_segment_num; ++segment_num) { + const SegmentByView seg(proj_data_sptr->get_segment_by_view(segment_num, timing_num)); + const float this_max = seg.find_max(); + const float this_min = seg.find_min(); + sum += static_cast(seg.sum()); + if (!accumulators_initialized) { + accum_max = this_max; + accum_min = this_min; + accumulators_initialized = true; + } else { + if (accum_max < this_max) + accum_max = this_max; + if (accum_min > this_min) + accum_min = this_min; + } } + if (print_min) + std::cout << "\nData min: " << accum_min; + if (print_max) + std::cout << "\nData max: " << accum_max; + if (print_sum) + std::cout << "\nData sum: " << sum; + std::cout << "\n"; + } } return EXIT_SUCCESS; } diff --git a/src/utilities/manip_image.cxx b/src/utilities/manip_image.cxx index 89a7504b47..5ccddb1635 100644 --- a/src/utilities/manip_image.cxx +++ b/src/utilities/manip_image.cxx @@ -18,9 +18,9 @@ See STIR/LICENSE.txt for details */ /*! - \file + \file \ingroup utilities - + \brief This program performs operations on image data \author Matthew Jacobson @@ -41,8 +41,7 @@ #include "stir/ArrayFunction.h" #include "stir/zoom.h" - -#include +#include #include #include #include @@ -62,353 +61,339 @@ static void trim_edges(VoxelsOnCartesianGrid& main_buffer); static void get_plane(VoxelsOnCartesianGrid& main_buffer); static void get_plane_row(VoxelsOnCartesianGrid& main_buffer); -static VoxelsOnCartesianGrid ask_interfile_image(const char *const input_query); +static VoxelsOnCartesianGrid ask_interfile_image(const char* const input_query); static void show_menu(); static void show_math_menu(); -static void math_mode(VoxelsOnCartesianGrid &main_buffer, int &quit_from_math); - -static void remove_nan(VoxelsOnCartesianGrid & output_image, - const VoxelsOnCartesianGrid & input_image){ -//change all the pointers - const int min_z = input_image.get_min_index(); - const int max_z = input_image.get_max_index(); +static void math_mode(VoxelsOnCartesianGrid& main_buffer, int& quit_from_math); +static void +remove_nan(VoxelsOnCartesianGrid& output_image, const VoxelsOnCartesianGrid& input_image) { + // change all the pointers + const int min_z = input_image.get_min_index(); + const int max_z = input_image.get_max_index(); - for (int z=min_z; z<=max_z; z++){ + for (int z = min_z; z <= max_z; z++) { - const int min_y = input_image[z].get_min_index(); - const int max_y = input_image[z].get_max_index(); + const int min_y = input_image[z].get_min_index(); + const int max_y = input_image[z].get_max_index(); - for (int y=min_y;y<= max_y;y++){ + for (int y = min_y; y <= max_y; y++) { - const int min_x = input_image[z][y].get_min_index(); - const int max_x = input_image[z][y].get_max_index(); + const int min_x = input_image[z][y].get_min_index(); + const int max_x = input_image[z][y].get_max_index(); - for (int x=min_x;x<= max_x;x++){ - - if(input_image[z][y][x]>=0 && input_image[z][y][x]<=1000000) - output_image[z][y][x]=input_image[z][y][x]; - else - output_image[z][y][x]=0; - } - } - } - } + for (int x = min_x; x <= max_x; x++) { + if (input_image[z][y][x] >= 0 && input_image[z][y][x] <= 1000000) + output_image[z][y][x] = input_image[z][y][x]; + else + output_image[z][y][x] = 0; + } + } + } +} -static VoxelsOnCartesianGrid -transpose_13(const VoxelsOnCartesianGrid & image) -{ +static VoxelsOnCartesianGrid +transpose_13(const VoxelsOnCartesianGrid& image) { CartesianCoordinate3D origin = image.get_origin(); swap(origin.x(), origin.z()); CartesianCoordinate3D voxel_size = image.get_voxel_size(); swap(voxel_size.x(), voxel_size.z()); - VoxelsOnCartesianGrid - out(IndexRange3D(image.get_min_x(),image.get_max_x(), - image.get_min_y(),image.get_max_y(), - image.get_min_z(),image.get_max_z()), - origin, - voxel_size); - for (int x=image.get_min_x(); x<=image.get_max_x(); ++x) - for (int y=image.get_min_y(); y<=image.get_max_y(); ++y) - for (int z=image.get_min_z(); z<=image.get_max_z(); ++z) + VoxelsOnCartesianGrid out(IndexRange3D(image.get_min_x(), image.get_max_x(), image.get_min_y(), image.get_max_y(), + image.get_min_z(), image.get_max_z()), + origin, voxel_size); + for (int x = image.get_min_x(); x <= image.get_max_x(); ++x) + for (int y = image.get_min_y(); y <= image.get_max_y(); ++y) + for (int z = image.get_min_z(); z <= image.get_max_z(); ++z) out[x][y][z] = image[z][y][x]; return out; } -static VoxelsOnCartesianGrid -transpose_12(const VoxelsOnCartesianGrid & image) -{ +static VoxelsOnCartesianGrid +transpose_12(const VoxelsOnCartesianGrid& image) { CartesianCoordinate3D origin = image.get_origin(); swap(origin.y(), origin.z()); CartesianCoordinate3D voxel_size = image.get_voxel_size(); swap(voxel_size.y(), voxel_size.z()); - VoxelsOnCartesianGrid - out(IndexRange3D(image.get_min_y(),image.get_max_y(), - image.get_min_z(),image.get_max_z(), - image.get_min_x(),image.get_max_x()), - origin, - voxel_size); - for (int y=image.get_min_y(); y<=image.get_max_y(); ++y) - for (int z=image.get_min_z(); z<=image.get_max_z(); ++z) - for (int x=image.get_min_x(); x<=image.get_max_x(); ++x) + VoxelsOnCartesianGrid out(IndexRange3D(image.get_min_y(), image.get_max_y(), image.get_min_z(), image.get_max_z(), + image.get_min_x(), image.get_max_x()), + origin, voxel_size); + for (int y = image.get_min_y(); y <= image.get_max_y(); ++y) + for (int z = image.get_min_z(); z <= image.get_max_z(); ++z) + for (int x = image.get_min_x(); x <= image.get_max_x(); ++x) out[y][z][x] = image[z][y][x]; return out; } -int main(int argc, char *argv[]) -{ - // file input - VoxelsOnCartesianGrid main_buffer; - if(argc>1) +int +main(int argc, char* argv[]) { + // file input + VoxelsOnCartesianGrid main_buffer; + if (argc > 1) { + main_buffer = *dynamic_cast*>(DiscretisedDensity<3, float>::read_from_file(argv[1])); + } else { + cerr << endl << "Usage: manip_image

      (*.hv)" << endl << endl; + main_buffer = ask_interfile_image("File to load in main buffer? "); + } + + int zs, ys, xs, ze, ye, xe, choice; + + zs = main_buffer.get_min_z(); + ys = main_buffer.get_min_y(); + xs = main_buffer.get_min_x(); + + ze = main_buffer.get_max_z(); + ye = main_buffer.get_max_y(); + xe = main_buffer.get_max_x(); + + cerr << "resx: " << main_buffer.get_x_size() << endl; + cerr << "resy: " << main_buffer.get_y_size() << endl; + cerr << "resz: " << main_buffer.get_z_size() << endl; + cerr << "Min and Max in image " << main_buffer.find_min() << " " << main_buffer.find_max() << endl; + + int plane = 1, quit_from_math = 0; + + show_menu(); + + do { // start main mode + choice = ask_num("Selection: ", 0, 13, 13); + + switch (choice) { + case 0: // quit + break; + + case 1: // display { - main_buffer= - * dynamic_cast *>( - DiscretisedDensity<3,float>::read_from_file(argv[1])); + const float maxi = ask_num("Maximum in color scale", 0.F, main_buffer.find_max(), main_buffer.find_max()); + + switch (ask_num("transaxial (0), coronal (1), sagital (2)", 0, 2, 0)) { + case 0: + display(main_buffer, maxi); + break; + case 1: + display(transpose_12(main_buffer), maxi); + break; + case 2: + display(transpose_13(main_buffer), maxi); + break; + } + break; } - else { - cerr< (*.hv)"<0 && plane <=ze-zs+1 ) { - plane = ask_num("Input plane # (0 to exit)", 0, ze-zs+1, zs); - - if(plane==0) break; - - for (int y=ys; y <= ye; y++) - for (int x=xs; x<= xe; x++) - cerr<0 && plane <=ze-zs+1 ) { - plane = ask_num("Input plane # (0 to exit)", 0, ze-zs+1, zs); - - if(plane==0) break; - - cerr << "Min and Max in plane " - << main_buffer[zs+plane-1].find_min() - << " " << main_buffer[zs+plane-1].find_max() << endl; - - cerr << "Number of counts = " - << main_buffer[zs+plane-1].sum() - << endl; - } - break; - } - - case 5: // min-max image - { - cerr << "Min and Max in image " << main_buffer.find_min() - << " " << main_buffer.find_max() << endl; - break; - } - - case 6: // trim - { - trim_edges(main_buffer); - break; - } - - case 7: // get plane - { - get_plane(main_buffer); - break; - } - - case 8: // get row - { - get_plane_row(main_buffer); - break; - } - - case 9: // counts - { - cerr<& output_buffer(*main_buffer.get_empty_copy()); - ask_filename_with_extension(outfile, "Output filename (without extension) ", ""); - remove_nan(output_buffer, main_buffer); - write_to_file(outfile, output_buffer); - break; - } - - case 14: show_menu(); - - } // end switch main mode - } while(choice>0 && choice<=13 && (!quit_from_math)); - return EXIT_SUCCESS; -} + case 3: // data plane-wise + { + plane = 1; -void trim_edges(VoxelsOnCartesianGrid& input_image) -{ - const int xe=input_image.get_max_x(); - const int xs=input_image.get_min_x(); - const int xm=(xs+xe)/2; - const int rim_trunc=ask_num("By how many voxels to trim the FOV? ",0,(int)(xe-xm),2); + while (plane > 0 && plane <= ze - zs + 1) { + plane = ask_num("Input plane # (0 to exit)", 0, ze - zs + 1, zs); - truncate_rim(input_image,rim_trunc); + if (plane == 0) + break; - if(ask("Zero end planes?",false)) truncate_end_planes(input_image); -} + for (int y = ys; y <= ye; y++) + for (int x = xs; x <= xe; x++) + cerr << main_buffer[zs + plane - 1][y][x] << " "; // pause + } + break; + } -void get_plane(VoxelsOnCartesianGrid& input_image) -{ - int zs,ys,xs, ze,ye,xe; + case 4: // min-max, counts plane-wise + { + plane = 1; - zs=input_image.get_min_z(); - ys=input_image.get_min_y(); - xs=input_image.get_min_x(); - - ze=input_image.get_max_z(); - ye=input_image.get_max_y(); - xe=input_image.get_max_x(); + while (plane > 0 && plane <= ze - zs + 1) { + plane = ask_num("Input plane # (0 to exit)", 0, ze - zs + 1, zs); - const int zm=(ze+zs)/2; + if (plane == 0) + break; - int plane=ask_num("Which plane?",1,ze-zs+1,(int)zm-zs+1); + cerr << "Min and Max in plane " << main_buffer[zs + plane - 1].find_min() << " " << main_buffer[zs + plane - 1].find_max() + << endl; - ofstream profile; - ask_filename_and_open(profile, "Output filename ", ".prof", ios::out); + cerr << "Number of counts = " << main_buffer[zs + plane - 1].sum() << endl; + } + break; + } - for (int y=ys; y<= ye; y++) - for (int x=xs; x<= xe; x++) - profile<& input_image) -{ - int zs,ys,xs, ze,ye,xe; - - zs=input_image.get_min_z(); - ys=input_image.get_min_y(); - xs=input_image.get_min_x(); - - ze=input_image.get_max_z(); - ye=input_image.get_max_y(); - xe=input_image.get_max_x(); - - float zm=(zs+ze)/2.F; - float ym=(ys+ye)/2.F; - float xm=(xs+xe)/2.F; - - int axdir=ask_num("Which axis direction (z=0,y=1,x=2)?",0,2,2); - - if (axdir==0) { - int xcoord=ask_num("X COORDINATE: ",1,xe-xs+1,(int)xm-xs+1); - int ycoord=ask_num("Y COORDINATE: ",1,ye-ys+1,(int)ym-ys+1); - - ofstream profile; - ask_filename_and_open(profile, "Output filename ", ".prof", ios::out); - - for (int z=zs; z<= ze; z++) - profile<& output_buffer(*main_buffer.get_empty_copy()); + ask_filename_with_extension(outfile, "Output filename (without extension) ", ""); + remove_nan(output_buffer, main_buffer); + write_to_file(outfile, output_buffer); + break; } + + case 14: + show_menu(); + + } // end switch main mode + } while (choice > 0 && choice <= 13 && (!quit_from_math)); + return EXIT_SUCCESS; +} + +void +trim_edges(VoxelsOnCartesianGrid& input_image) { + const int xe = input_image.get_max_x(); + const int xs = input_image.get_min_x(); + const int xm = (xs + xe) / 2; + const int rim_trunc = ask_num("By how many voxels to trim the FOV? ", 0, (int)(xe - xm), 2); + + truncate_rim(input_image, rim_trunc); + + if (ask("Zero end planes?", false)) + truncate_end_planes(input_image); +} + +void +get_plane(VoxelsOnCartesianGrid& input_image) { + int zs, ys, xs, ze, ye, xe; + + zs = input_image.get_min_z(); + ys = input_image.get_min_y(); + xs = input_image.get_min_x(); + + ze = input_image.get_max_z(); + ye = input_image.get_max_y(); + xe = input_image.get_max_x(); + + const int zm = (ze + zs) / 2; + + int plane = ask_num("Which plane?", 1, ze - zs + 1, (int)zm - zs + 1); + + ofstream profile; + ask_filename_and_open(profile, "Output filename ", ".prof", ios::out); + + for (int y = ys; y <= ye; y++) + for (int x = xs; x <= xe; x++) + profile << input_image[zs + plane - 1][y][x] << " "; } -VoxelsOnCartesianGrid ask_interfile_image(const char *const input_query) -{ - char filename[max_filename_length]; +void +get_plane_row(VoxelsOnCartesianGrid& input_image) { + int zs, ys, xs, ze, ye, xe; + + zs = input_image.get_min_z(); + ys = input_image.get_min_y(); + xs = input_image.get_min_x(); + + ze = input_image.get_max_z(); + ye = input_image.get_max_y(); + xe = input_image.get_max_x(); - ask_filename_with_extension(filename, input_query, ".hv"); + float zm = (zs + ze) / 2.F; + float ym = (ys + ye) / 2.F; + float xm = (xs + xe) / 2.F; + + int axdir = ask_num("Which axis direction (z=0,y=1,x=2)?", 0, 2, 2); + + if (axdir == 0) { + int xcoord = ask_num("X COORDINATE: ", 1, xe - xs + 1, (int)xm - xs + 1); + int ycoord = ask_num("Y COORDINATE: ", 1, ye - ys + 1, (int)ym - ys + 1); + + ofstream profile; + ask_filename_and_open(profile, "Output filename ", ".prof", ios::out); - shared_ptr > - image_ptr(read_from_file >(filename)); - return - * dynamic_cast*>(image_ptr.get()); + for (int z = zs; z <= ze; z++) + profile << input_image[z][ycoord + ys - 1][xcoord + xs - 1] << " "; + } + else if (axdir == 1) { + int zcoord = ask_num("Z COORDINATE: ", 1, ze - zs + 1, (int)zm - zs + 1); + int xcoord = ask_num("X COORDINATE: ", 1, xe - xs + 1, (int)xm - xs + 1); + + ofstream profile; + ask_filename_and_open(profile, "Output filename ", ".prof", ios::out); + + for (int y = ys; y <= ye; y++) + profile << input_image[zcoord + zs - 1][y][xcoord + xs - 1] << " "; + } + + else { // axdir=2 + int zcoord = ask_num("Z COORDINATE: ", 1, ze - zs + 1, (int)zm - zs + 1); + int ycoord = ask_num("Y COORDINATE: ", 1, ye - ys + 1, (int)ym - ys + 1); + + ofstream profile; + ask_filename_and_open(profile, "Output filename ", ".prof", ios::out); + + for (int x = xs; x <= xe; x++) + profile << input_image[zcoord + zs - 1][ycoord + ys - 1][x] << " "; + } } +VoxelsOnCartesianGrid +ask_interfile_image(const char* const input_query) { + char filename[max_filename_length]; + + ask_filename_with_extension(filename, input_query, ".hv"); -void show_menu() -{ - cerr<<"\n\ + shared_ptr> image_ptr(read_from_file>(filename)); + return *dynamic_cast*>(image_ptr.get()); +} + +void +show_menu() { + cerr << "\n\ MAIN MODE:\n\ 0. Quit\n\ 1. Display image\n\ @@ -424,11 +409,12 @@ MAIN MODE:\n\ 11. Reload main buffer\n\ 12. Write buffer to file\n\ 13. Remove nan values, substitute nan with 0\n\ -14. Redisplay menu"<& main_buffer, int& quit_from_math) { + VoxelsOnCartesianGrid math_buffer = main_buffer; // initialize math buffer + int operation; + + show_math_menu(); + + do { + operation = ask_num("Choose Operation: ", 0, 16, 15); + + switch (operation) { // math mode + + case 0: // quit + { + quit_from_math = 1; + break; + } + + case 1: // display math buffer + { + display(math_buffer, math_buffer.find_max()); + break; + } + + case 2: // absolute difference + { + + VoxelsOnCartesianGrid aux_image = ask_interfile_image("What image to compare with?"); + + math_buffer -= aux_image; + in_place_abs(math_buffer); + + // MJ 07/10/2000 removed trimming + // trim_edges(math_buffer); + + cerr << endl << "Min and Max absolute difference " << math_buffer.find_min() << " " << math_buffer.find_max() << endl; + cerr << endl << "Difference (L1 norm): " << math_buffer.sum() << endl; -void math_mode(VoxelsOnCartesianGrid &main_buffer, int &quit_from_math) -{ - VoxelsOnCartesianGrid math_buffer=main_buffer; //initialize math buffer - int operation; + break; + } - show_math_menu(); - - do { - operation=ask_num("Choose Operation: ",0,16,15); + case 3: // image addition + { + VoxelsOnCartesianGrid aux_image = ask_interfile_image("What image to add?"); - switch(operation) { // math mode + math_buffer += aux_image; + break; + } + + case 4: // image subtraction + { + VoxelsOnCartesianGrid aux_image = ask_interfile_image("What image to subtract?"); + + math_buffer -= aux_image; + break; + } + + case 5: // image multiplication + { + VoxelsOnCartesianGrid aux_image = ask_interfile_image("What image to multiply?"); + + math_buffer *= aux_image; + break; + } + + case 6: // image division + { + VoxelsOnCartesianGrid aux_image = ask_interfile_image("What image to divide?"); + + divide_array(math_buffer, aux_image); + break; + } + + case 7: // scalar addition + { + float scalar = ask_num("What scalar to add?", -100000.F, +100000.F, 0.F); + + math_buffer += scalar; + break; + } + + case 8: // scalar mulltiplication + { + float scalar = ask_num("What scalar to multiply?", -100000.F, +100000.F, 1.F); + + math_buffer *= scalar; + break; + } + + case 9: // scalar division + { + float scalar = 0.0; + + do { + scalar = ask_num("What scalar to divide?", -100000.F, +100000.F, 1.F); + + if (scalar == 0.0) + cerr << endl << "Illegal -- division by 0" << endl; + + } while (scalar == 0.0); + + math_buffer /= scalar; + break; + } + + case 10: // zoom + { + const float zoom_x = ask_num("Zoom factor x", 0.1F, 5.F, 1.F); + const float zoom_y = ask_num("Zoom factor y", 0.1F, 5.F, zoom_x); + const float zoom_z = ask_num("Zoom factor z", 0.1F, 5.F, 1.F); + const float offset_x = ask_num("Offset x (in mm)", -math_buffer.get_x_size() * math_buffer.get_voxel_size().x(), + math_buffer.get_x_size() * math_buffer.get_voxel_size().x(), 0.F); + const float offset_y = ask_num("Offset y (in mm)", -math_buffer.get_y_size() * math_buffer.get_voxel_size().y(), + math_buffer.get_y_size() * math_buffer.get_voxel_size().y(), 0.F); + const float offset_z = ask_num("Offset z (in mm)", -math_buffer.get_z_size() * math_buffer.get_voxel_size().z(), + math_buffer.get_z_size() * math_buffer.get_voxel_size().z(), 0.F); + const int new_size_x = ask_num("New x size (pixels)", 1, static_cast(math_buffer.get_x_size() * zoom_x * 2), + static_cast(math_buffer.get_x_size() * zoom_x)); + const int new_size_y = + ask_num("New y size (pixels)", 1, static_cast(math_buffer.get_y_size() * zoom_y * 2), new_size_x); + const int new_size_z = ask_num("New z size (pixels)", 1, static_cast(math_buffer.get_z_size() * zoom_z * 2), + static_cast(math_buffer.get_z_size() * zoom_z)); + zoom_image_in_place(math_buffer, CartesianCoordinate3D(zoom_z, zoom_y, zoom_x), + CartesianCoordinate3D(offset_z, offset_y, offset_x), + CartesianCoordinate3D(new_size_z, new_size_y, new_size_x)); + break; + } + + case 11: { + cerr << "Min and Max in image " << math_buffer.find_min() << " " << math_buffer.find_max() << endl; + break; + } + + case 12: // reinitialize math buffer + { + math_buffer = main_buffer; + break; + } + + case 13: // dump math buffer to main buffer + { + main_buffer = math_buffer; + break; + } + + case 14: // reload main buffer in math mode + { + main_buffer = ask_interfile_image("File to load in buffer? "); + break; + } + + case 15: // redisplay menu + { + show_math_menu(); + break; + } + + case 16: // go back to main mode + { + show_menu(); + } - case 0: // quit - { - quit_from_math=1; - break; - } - - case 1: // display math buffer - { - display(math_buffer, math_buffer.find_max()); - break; - } - - case 2: // absolute difference - { - - VoxelsOnCartesianGrid aux_image= - ask_interfile_image("What image to compare with?"); - - math_buffer-=aux_image; - in_place_abs(math_buffer); - - //MJ 07/10/2000 removed trimming - //trim_edges(math_buffer); - - cerr < aux_image= - ask_interfile_image("What image to add?"); - - math_buffer+=aux_image; - break; - } - - - case 4: // image subtraction - { - VoxelsOnCartesianGrid aux_image= - ask_interfile_image("What image to subtract?"); - - math_buffer-=aux_image; - break; - } - - case 5: // image multiplication - { - VoxelsOnCartesianGrid aux_image= - ask_interfile_image("What image to multiply?"); - - math_buffer*=aux_image; - break; - } - - case 6: // image division - { - VoxelsOnCartesianGrid aux_image= - ask_interfile_image("What image to divide?"); - - divide_array(math_buffer, aux_image); - break; - } - - case 7: //scalar addition - { - float scalar=ask_num("What scalar to add?", -100000.F,+100000.F,0.F); - - math_buffer+=scalar; - break; - } - - case 8: //scalar mulltiplication - { - float scalar=ask_num("What scalar to multiply?", -100000.F,+100000.F,1.F); - - math_buffer*=scalar; - break; - } - - case 9: //scalar division - { - float scalar=0.0; - - do { - scalar=ask_num("What scalar to divide?", -100000.F,+100000.F,1.F); - - if(scalar==0.0) cerr<(math_buffer.get_x_size()*zoom_x * 2), - static_cast(math_buffer.get_x_size()*zoom_x)); - const int new_size_y = - ask_num("New y size (pixels)", 1, - static_cast(math_buffer.get_y_size()*zoom_y * 2), - new_size_x); - const int new_size_z = - ask_num("New z size (pixels)", 1, - static_cast(math_buffer.get_z_size()*zoom_z * 2), - static_cast(math_buffer.get_z_size()*zoom_z)); - zoom_image_in_place(math_buffer, - CartesianCoordinate3D(zoom_z, zoom_y, zoom_x), - CartesianCoordinate3D(offset_z, offset_y, offset_x), - CartesianCoordinate3D(new_size_z, new_size_y, new_size_x)); - break; - } - - case 11: - { - cerr << "Min and Max in image " << math_buffer.find_min() - << " " << math_buffer.find_max() << endl; - break; - } - - case 12: //reinitialize math buffer - { - math_buffer=main_buffer; - break; - } - - case 13: //dump math buffer to main buffer - { - main_buffer=math_buffer; - break; - } - - case 14: //reload main buffer in math mode - { - main_buffer = ask_interfile_image("File to load in buffer? "); - break; - } - - case 15: //redisplay menu - { - show_math_menu(); - break; - } - - - case 16: //go back to main mode - { - show_menu(); - } - - } // end switch math mode - } while(operation>0 && operation<16); + } // end switch math mode + } while (operation > 0 && operation < 16); } diff --git a/src/utilities/manip_projdata.cxx b/src/utilities/manip_projdata.cxx index 92b52701b4..239d7e3598 100644 --- a/src/utilities/manip_projdata.cxx +++ b/src/utilities/manip_projdata.cxx @@ -7,12 +7,12 @@ \author Sanida Mustafovic and Kris Thielemans (conversion to new design) \author PARAPET project -This utility programme processes (interfile) sinogram data +This utility programme processes (interfile) sinogram data (maximum number of segments as input). It can
      • display by View - by Segment
      • do operations between two data -
      • do operations with a scalar +
      • do operations with a scalar
      */ /* @@ -37,8 +37,6 @@ This utility programme processes (interfile) sinogram data // TODO get rid of pos, neg segments (can now do each one separately) // MJ doesn't think doing each one separately is a good idea (for display) - - #include "stir/ProjDataFromStream.h" #include "stir/SegmentByView.h" #include "stir/SegmentBySinogram.h" @@ -46,7 +44,7 @@ This utility programme processes (interfile) sinogram data #include "stir/Viewgram.h" //#include "stir/Scanner.h" -#include "stir/ArrayFunction.h" +#include "stir/ArrayFunction.h" #include "stir/recon_array_functions.h" #include "stir/display.h" #include "stir/IO/interfile.h" @@ -55,8 +53,8 @@ This utility programme processes (interfile) sinogram data #include "stir/is_null_ptr.h" #include -#include -#include +#include +#include #ifndef STIR_NO_NAMESPACES using std::cerr; @@ -64,197 +62,200 @@ using std::endl; using std::fstream; #endif - - START_NAMESPACE_STIR // in relation with show_math_menu() // _menu HAS to be the last option -enum options { _quit, _display_view, _display_sino, _absdiff, _add_sino, _subtract_sino, - _mult_sino, _div_sino, _add_scalar, _mult_scalar, _div_scalar, _stats, - _pos_ind, _trunc_neg, _trim, _zero_ends, /*_pad_ends,*/ _restart, _menu}; +enum options { + _quit, + _display_view, + _display_sino, + _absdiff, + _add_sino, + _subtract_sino, + _mult_sino, + _div_sino, + _add_scalar, + _mult_scalar, + _div_scalar, + _stats, + _pos_ind, + _trunc_neg, + _trim, + _zero_ends, + /*_pad_ends,*/ _restart, + _menu +}; //*********************** prototypes // operations between two datas -void do_math(enum options operation, SegmentByView& sino1,SegmentByView &sino2, - float &accum_max, float &accum_min, float &accum_sum, bool accumulators_initialized); +void do_math(enum options operation, SegmentByView& sino1, SegmentByView& sino2, float& accum_max, float& accum_min, + float& accum_sum, bool accumulators_initialized); // display, operations with a scalar, others -void do_math(enum options operation, SegmentByView& sino1, SegmentBySinogram& seg_sinogram, float &accum_max, float &accum_min, float &accum_sum, bool accumulators_initialized,float scalar=0.0); +void do_math(enum options operation, SegmentByView& sino1, SegmentBySinogram& seg_sinogram, float& accum_max, + float& accum_min, float& accum_sum, bool accumulators_initialized, float scalar = 0.0); -void make_buffer_header(const char *data_filename,const char *header_filename, - ProjData& input_sino, int limit_segments, - NumericType::Type output_type=NumericType::FLOAT); +void make_buffer_header(const char* data_filename, const char* header_filename, ProjData& input_sino, int limit_segments, + NumericType::Type output_type = NumericType::FLOAT); void show_math_menu(); float pos_indicate(float x); -shared_ptr ask_proj_data(const char *const input_query); +shared_ptr ask_proj_data(const char* const input_query); //*********************** functions -void do_math(enum options operation, SegmentByView& sino1,SegmentByView &sino2, - float &accum_max, float &accum_min, float &accum_sum, bool accumulators_initialized) -{ - switch(operation) - { - - case _absdiff: { //absolute difference - sino1-=sino2; - in_place_abs(sino1); - - if(!accumulators_initialized) { - accum_max=sino1.find_max(); - accum_min=sino1.find_min(); - accum_sum=sino1.sum(); - accumulators_initialized=true; - } - else { - if (accum_maxsino1.find_min()) accum_min= sino1.find_min(); - accum_sum+=sino1.sum(); - } - break; - } - - case _add_sino: { // sinogram addition - sino1+=sino2; - break; - } - - - case _subtract_sino: { // sinogram subtraction - sino1-=sino2; - break; +void +do_math(enum options operation, SegmentByView& sino1, SegmentByView& sino2, float& accum_max, float& accum_min, + float& accum_sum, bool accumulators_initialized) { + switch (operation) { + + case _absdiff: { // absolute difference + sino1 -= sino2; + in_place_abs(sino1); + + if (!accumulators_initialized) { + accum_max = sino1.find_max(); + accum_min = sino1.find_min(); + accum_sum = sino1.sum(); + accumulators_initialized = true; + } else { + if (accum_max < sino1.find_max()) + accum_max = sino1.find_max(); + if (accum_min > sino1.find_min()) + accum_min = sino1.find_min(); + accum_sum += sino1.sum(); } + break; + } + + case _add_sino: { // sinogram addition + sino1 += sino2; + break; + } + + case _subtract_sino: { // sinogram subtraction + sino1 -= sino2; + break; + } + + case _mult_sino: { // image multiplication + sino1 *= sino2; + break; + } + + case _div_sino: { // sinogram division + divide_array(sino1, sino2); + break; + } + + // MJ 07/14/2000 empty default to suppress warning in gcc 2.95.2 + default: { + // empty statement + } + + } // end switch +} - case _mult_sino: { // image multiplication - sino1*=sino2; - break; +void +do_math(enum options operation, SegmentByView& sino1, SegmentBySinogram& seg_sinogram, float& accum_max, + float& accum_min, float& accum_sum, bool accumulators_initialized, float scalar) { + switch (operation) { + + case _display_view: { // display math buffer by View + char title[100]; + sprintf(title, "Segment %d", sino1.get_segment_num()); + display(sino1, sino1.find_max(), title); + if (ask("Display single viewgram?", false)) { + int vs = sino1.get_min_view_num(); + int ve = sino1.get_max_view_num(); + int view_num = ask_num("Which viewgram?", vs, ve, vs); + + Viewgram viewgram = sino1.get_viewgram(view_num); + display(viewgram); } - - case _div_sino: { // sinogram division - divide_array(sino1,sino2); - break; + break; + } + + case _display_sino: { // display math buffer by sinogram + char title[100]; + sprintf(title, "Segment %d", sino1.get_segment_num()); + display(seg_sinogram, seg_sinogram.find_max()); + break; + } + + case _add_scalar: { // scalar addition + sino1 += scalar; + break; + } + + case _mult_scalar: { // scalar multiplication + sino1 *= scalar; + break; + } + + case _div_scalar: { // scalar division + sino1 /= scalar; + break; + } + + case _stats: { // global min&max + number of counts + if (!accumulators_initialized) { + accum_max = sino1.find_max(); + accum_min = sino1.find_min(); + accum_sum = sino1.sum(); + accumulators_initialized = true; + } else { + if (accum_max < sino1.find_max()) + accum_max = sino1.find_max(); + if (accum_min > sino1.find_min()) + accum_min = sino1.find_min(); + accum_sum += sino1.sum(); } - - - //MJ 07/14/2000 empty default to suppress warning in gcc 2.95.2 - default: - { - //empty statement - } - - - - } // end switch + break; + } + + case _pos_ind: { + in_place_apply_function(sino1, pos_indicate); // positive indicator + break; + } + + case _trim: { + truncate_rim(sino1, (int)scalar); // trim rim + break; + } + + case _trunc_neg: { + in_place_apply_function(sino1, neg_trunc); + break; + } + + // MJ 07/14/2000 empty default to suppress warning in gcc 2.95.2 + default: { + // empty statement + } + + } // end switch } +shared_ptr +ask_proj_data(const char* const input_query) { + char filename[max_filename_length]; -void do_math(enum options operation, SegmentByView& sino1, SegmentBySinogram& seg_sinogram, float &accum_max, float &accum_min, float &accum_sum, bool accumulators_initialized,float scalar) -{ - switch(operation) - { + // system("ls *hs"); + ask_filename_with_extension(filename, input_query, ".hs"); - case _display_view: { //display math buffer by View - char title[100]; - sprintf(title, "Segment %d", sino1.get_segment_num()); - display(sino1,sino1.find_max(), title); - if(ask("Display single viewgram?",false)) { - int vs=sino1.get_min_view_num(); - int ve=sino1.get_max_view_num(); - int view_num=ask_num("Which viewgram?",vs,ve,vs); - - Viewgram viewgram=sino1.get_viewgram(view_num); - display(viewgram); - } - break; - } - - case _display_sino: { //display math buffer by sinogram - char title[100]; - sprintf(title, "Segment %d", sino1.get_segment_num()); - display(seg_sinogram, seg_sinogram.find_max()); - break; - } - - case _add_scalar: { //scalar addition - sino1+=scalar; - break; - } - - case _mult_scalar: { //scalar multiplication - sino1*=scalar; - break; - } - - case _div_scalar: { //scalar division - sino1/=scalar; - break; - } - - case _stats: { //global min&max + number of counts - if(!accumulators_initialized) { - accum_max=sino1.find_max(); - accum_min=sino1.find_min(); - accum_sum=sino1.sum(); - accumulators_initialized=true; - } - else { - if (accum_maxsino1.find_min()) accum_min= sino1.find_min(); - accum_sum+=sino1.sum(); - } - break; - } - - case _pos_ind: - { - in_place_apply_function(sino1,pos_indicate); //positive indicator - break; - } - - case _trim: - { - truncate_rim(sino1, (int) scalar); //trim rim - break; - } - - case _trunc_neg: - { - in_place_apply_function(sino1,neg_trunc); - break; - } - - - //MJ 07/14/2000 empty default to suppress warning in gcc 2.95.2 - default: - { - //empty statement - } - - - } //end switch + return ProjData::read_from_file(filename); } -shared_ptr ask_proj_data(const char *const input_query) -{ - char filename[max_filename_length]; - - //system("ls *hs"); - ask_filename_with_extension(filename, input_query, ".hs"); - - return - ProjData::read_from_file(filename); -} - -void show_math_menu() -{ +void +show_math_menu() { assert(_menu == 17); // KT disabled Pad end planes: 16. Pad end planes of segment 0 \n -cerr<<"\n\ + cerr << "\n\ MENU:\n\ 0. Quit\n\ 1. Display viewgrams\n\ @@ -276,145 +277,132 @@ MENU:\n\ 15. Apply axial truncating window to segment 0 \n\ (scalar operand = No. of planes to truncate)\n\ 16. Restart\n\ -17. Redisplay menu"<0.0)?1.0F:0.0F; +float +pos_indicate(float x) { + return (x > 0.0) ? 1.0F : 0.0F; } END_NAMESPACE_STIR //********************** main - - USING_NAMESPACE_STIR +int +main(int argc, char* argv[]) { + bool quit = false, reload = false; -int main(int argc, char *argv[]) -{ - bool quit=false,reload=false; - - shared_ptr first_operand; - ProjDataFromStream *output_proj_data= NULL; - // Start - do { //(re)start from here - bool buffer_opened=false; - char output_buffer_header[max_filename_length]; - - if (is_null_ptr(first_operand)) - { + shared_ptr first_operand; + ProjDataFromStream* output_proj_data = NULL; + // Start + do { //(re)start from here + bool buffer_opened = false; + char output_buffer_header[max_filename_length]; - if (reload) - // changed the ask... returns ponter - first_operand=ask_proj_data("Input sinogram"); //new - //ProjDataFromStream(ask_proj_data("Input sinogram")); - - else // just starting - { - if(argc<2) - { - cerr< (*.hs)"< (*.hs)" << endl << endl; + first_operand = ask_proj_data("Input sinogram"); + } else + first_operand = ProjData::read_from_file(argv[1]); - int limit_segments=ask_num("Maximum absolute segment number to process: ", 0, first_operand->get_max_segment_num(), first_operand->get_max_segment_num() ); + reload = false; + } + } + int limit_segments = ask_num("Maximum absolute segment number to process: ", 0, first_operand->get_max_segment_num(), + first_operand->get_max_segment_num()); - do { //math operations loop - float accum_max, accum_min, accum_sum; - show_math_menu(); - enum options operation; + do { // math operations loop + float accum_max, accum_min, accum_sum; + show_math_menu(); + enum options operation; - operation= - static_cast( - ask_num("Choose Operation: ", - 0,static_cast(_menu), static_cast(_menu)) - ); - if (operation==_menu) continue; //redisplay menu - if (operation==_restart || operation==_quit) { //restart or quit + operation = static_cast(ask_num("Choose Operation: ", 0, static_cast(_menu), static_cast(_menu))); + if (operation == _menu) + continue; // redisplay menu + if (operation == _restart || operation == _quit) { // restart or quit #if 1 - assert(output_proj_data == NULL); + assert(output_proj_data == NULL); #else - // enable this when using the output buffer for reading/writing at the same time - if (output_proj_data != NULL) - { - delete output_proj_data; - output_proj_data = NULL; - } + // enable this when using the output buffer for reading/writing at the same time + if (output_proj_data != NULL) { + delete output_proj_data; + output_proj_data = NULL; + } #endif - first_operand.reset(); - if(operation==_restart) reload=true; - if(operation==_quit) quit=true; - break; - } - - if (operation!= _display_view && operation!= _display_sino - && operation!= _stats &&!buffer_opened) - { - //operation result is a sinogram - - char output_buffer_root[max_filename_length]; - char output_buffer_filename[max_filename_length]; - - ask_filename_with_extension(output_buffer_root, "Output to which file (without extension)?", ""); - sprintf(output_buffer_filename, "%s.%s",output_buffer_root , "s"); - // TODO relies on write_basic_interfile_PDFS_header using .hs extension - sprintf(output_buffer_header, "%s.%s",output_buffer_root , "hs"); - shared_ptr new_sino_ptr(new fstream); - open_write_binary(*new_sino_ptr, output_buffer_filename); - shared_ptr pdi_ptr = - first_operand->get_proj_data_info_sptr()->create_shared_clone(); - pdi_ptr->reduce_segment_range(-limit_segments, limit_segments); - output_proj_data = new ProjDataFromStream(first_operand->get_exam_info_sptr(), - pdi_ptr, new_sino_ptr); - write_basic_interfile_PDFS_header(output_buffer_filename, *output_proj_data); - buffer_opened=true; - } - - shared_ptr second_operand; - float *scalar=NULL; - - if(operation==_absdiff || operation==_add_sino || operation==_subtract_sino || - operation==_mult_sino || operation==_div_sino) //requiring 2nd sinogram operand - second_operand= ask_proj_data("Second sinogram operand" ); - - if(operation==_add_scalar || operation==_mult_scalar || operation==_div_scalar || - operation==_trim || operation==_zero_ends ) { //requiring scalar operand - bool need_int=false; - float upper_bound=100000.F,lower_bound=-100000.F,deflt=0.F; - - if(operation==_trim) { - need_int=true; - upper_bound=(float) (first_operand->get_proj_data_info_sptr()->get_num_tangential_poss()/2 +1); - lower_bound=deflt=0.0; - } - - if(operation==_zero_ends) { - need_int=true; - upper_bound=(float) (first_operand->get_proj_data_info_sptr()->get_num_axial_poss(0)/2+1); - lower_bound=deflt=1.0; - } - - do scalar= new float (ask_num("Scalar Operand: ",(need_int)?(int)lower_bound:lower_bound , - (need_int)?(int)upper_bound: upper_bound,(need_int)?(int)deflt:deflt)); - while(*scalar==0.0 && operation==_div_scalar ); - - } -// first do segment 0 - { - SegmentByView seg1=first_operand->get_segment_by_view(0); - SegmentBySinogram seg_sinogram=first_operand->get_segment_by_sinogram(0); + first_operand.reset(); + if (operation == _restart) + reload = true; + if (operation == _quit) + quit = true; + break; + } + + if (operation != _display_view && operation != _display_sino && operation != _stats && !buffer_opened) { + // operation result is a sinogram + + char output_buffer_root[max_filename_length]; + char output_buffer_filename[max_filename_length]; + + ask_filename_with_extension(output_buffer_root, "Output to which file (without extension)?", ""); + sprintf(output_buffer_filename, "%s.%s", output_buffer_root, "s"); + // TODO relies on write_basic_interfile_PDFS_header using .hs extension + sprintf(output_buffer_header, "%s.%s", output_buffer_root, "hs"); + shared_ptr new_sino_ptr(new fstream); + open_write_binary(*new_sino_ptr, output_buffer_filename); + shared_ptr pdi_ptr = first_operand->get_proj_data_info_sptr()->create_shared_clone(); + pdi_ptr->reduce_segment_range(-limit_segments, limit_segments); + output_proj_data = new ProjDataFromStream(first_operand->get_exam_info_sptr(), pdi_ptr, new_sino_ptr); + write_basic_interfile_PDFS_header(output_buffer_filename, *output_proj_data); + buffer_opened = true; + } + + shared_ptr second_operand; + float* scalar = NULL; + + if (operation == _absdiff || operation == _add_sino || operation == _subtract_sino || operation == _mult_sino || + operation == _div_sino) // requiring 2nd sinogram operand + second_operand = ask_proj_data("Second sinogram operand"); + + if (operation == _add_scalar || operation == _mult_scalar || operation == _div_scalar || operation == _trim || + operation == _zero_ends) { // requiring scalar operand + bool need_int = false; + float upper_bound = 100000.F, lower_bound = -100000.F, deflt = 0.F; + + if (operation == _trim) { + need_int = true; + upper_bound = (float)(first_operand->get_proj_data_info_sptr()->get_num_tangential_poss() / 2 + 1); + lower_bound = deflt = 0.0; + } + + if (operation == _zero_ends) { + need_int = true; + upper_bound = (float)(first_operand->get_proj_data_info_sptr()->get_num_axial_poss(0) / 2 + 1); + lower_bound = deflt = 1.0; + } + + do + scalar = new float(ask_num("Scalar Operand: ", (need_int) ? (int)lower_bound : lower_bound, + (need_int) ? (int)upper_bound : upper_bound, (need_int) ? (int)deflt : deflt)); + while (*scalar == 0.0 && operation == _div_scalar); + } + // first do segment 0 + { + SegmentByView seg1 = first_operand->get_segment_by_view(0); + SegmentBySinogram seg_sinogram = first_operand->get_segment_by_sinogram(0); #if 0 // TODO grow statement is wrong // also this can't work anymore, as set_segment would complain about incompatible sizes @@ -446,100 +434,94 @@ int main(int argc, char *argv[]) } } #endif - if(!is_null_ptr(second_operand)) { - SegmentByView seg2=second_operand->get_segment_by_view(0); - do_math(operation,seg1,seg2,accum_max,accum_min,accum_sum,false); - } - - else if(scalar != NULL) { - if(operation==_zero_ends ) - for(int i=seg1.get_min_view_num();i<=seg1.get_max_view_num();i++) - for(int j=0;j<*scalar;j++ ) { - seg1[i][seg1.get_min_axial_pos_num()+j].fill(0); - seg1[i][seg1.get_max_axial_pos_num()-j].fill(0); - - - } - else do_math(operation,seg1,seg_sinogram,accum_max,accum_min,accum_sum,false,*scalar); - - - } - - else do_math(operation,seg1,seg_sinogram,accum_max,accum_min,accum_sum,false); - - - - - //Write sinogram result to file - if(operation!= _display_view && operation!= _display_sino && operation!= _stats && buffer_opened) - output_proj_data->set_segment(seg1); - } -//Now do other segments - - - - - if(limit_segments>0) - for (int segment_num = 1; segment_num <= limit_segments ; segment_num++) { - if((operation==_display_view || operation==_display_sino) && ask("Abort display",false)) break; - SegmentByView seg1_pos=first_operand->get_segment_by_view(segment_num); - SegmentByView seg1_neg=first_operand->get_segment_by_view(-segment_num); - SegmentBySinogram seg_sinogram_pos=first_operand->get_segment_by_sinogram(segment_num); - SegmentBySinogram seg_sinogram_neg=first_operand->get_segment_by_sinogram(-segment_num); - - if(!is_null_ptr(second_operand)) { - SegmentByView seg2_pos=second_operand->get_segment_by_view(segment_num); - SegmentByView seg2_neg=second_operand->get_segment_by_view(-segment_num); - do_math(operation,seg1_pos,seg2_pos,accum_max,accum_min,accum_sum,true); - do_math(operation,seg1_neg,seg2_neg,accum_max,accum_min,accum_sum,true); - } - else if(scalar != NULL) { - do_math(operation,seg1_pos,seg_sinogram_pos,accum_max,accum_min,accum_sum,true,*scalar); - do_math(operation,seg1_neg,seg_sinogram_neg,accum_max,accum_min,accum_sum,true,*scalar); - } - else { - do_math(operation,seg1_pos,seg_sinogram_pos,accum_max,accum_min,accum_sum,true); - if((operation==_display_view || operation==_display_sino) - && ask("Abort display",false)) break; - do_math(operation,seg1_neg,seg_sinogram_neg,accum_max,accum_min,accum_sum,true); - if((operation==_display_view || operation==_display_sino) - && segment_numset_segment(seg1_neg); - output_proj_data->set_segment(seg1_pos); - } - } - - -//if buffer changed, reinitialize first operand to output of previous math operation - if(operation!= _display_view && operation!= _display_sino && operation!= _stats && buffer_opened) - { - // at the moment, we close the output buffer, and will reopen it later on - // this is to avoid conflicts with reading and writing from/to the same file - // alternatively, the output_proj_data would use a read/write file, and - // we would do first_operand = output_proj_data - - if (output_proj_data != NULL) - { - delete output_proj_data; - output_proj_data = NULL; - } - buffer_opened = false; - - first_operand=ProjData::read_from_file(output_buffer_header); - } - -//Get accumulator results and de-allocate - if (operation ==_absdiff || operation ==_stats) cerr< seg1_pos = first_operand->get_segment_by_view(segment_num); + SegmentByView seg1_neg = first_operand->get_segment_by_view(-segment_num); + SegmentBySinogram seg_sinogram_pos = first_operand->get_segment_by_sinogram(segment_num); + SegmentBySinogram seg_sinogram_neg = first_operand->get_segment_by_sinogram(-segment_num); + + if (!is_null_ptr(second_operand)) { + SegmentByView seg2_pos = second_operand->get_segment_by_view(segment_num); + SegmentByView seg2_neg = second_operand->get_segment_by_view(-segment_num); + do_math(operation, seg1_pos, seg2_pos, accum_max, accum_min, accum_sum, true); + do_math(operation, seg1_neg, seg2_neg, accum_max, accum_min, accum_sum, true); + } else if (scalar != NULL) { + do_math(operation, seg1_pos, seg_sinogram_pos, accum_max, accum_min, accum_sum, true, *scalar); + do_math(operation, seg1_neg, seg_sinogram_neg, accum_max, accum_min, accum_sum, true, *scalar); + } else { + do_math(operation, seg1_pos, seg_sinogram_pos, accum_max, accum_min, accum_sum, true); + if ((operation == _display_view || operation == _display_sino) && ask("Abort display", false)) + break; + do_math(operation, seg1_neg, seg_sinogram_neg, accum_max, accum_min, accum_sum, true); + if ((operation == _display_view || operation == _display_sino) && segment_num < limit_segments && + ask("Abort display", false)) + break; + } + + // Write sinogram result to file + if (operation != _display_view && operation != _display_sino && operation != _stats && buffer_opened) { + output_proj_data->set_segment(seg1_neg); + output_proj_data->set_segment(seg1_pos); + } + } + + // if buffer changed, reinitialize first operand to output of previous math operation + if (operation != _display_view && operation != _display_sino && operation != _stats && buffer_opened) { + // at the moment, we close the output buffer, and will reopen it later on + // this is to avoid conflicts with reading and writing from/to the same file + // alternatively, the output_proj_data would use a read/write file, and + // we would do first_operand = output_proj_data + + if (output_proj_data != NULL) { + delete output_proj_data; + output_proj_data = NULL; + } + buffer_opened = false; + + first_operand = ProjData::read_from_file(output_buffer_header); + } + + // Get accumulator results and de-allocate + if (operation == _absdiff || operation == _stats) + cerr << endl << "Maximum= " << accum_max << endl; + if (operation == _absdiff || operation == _stats) + cerr << endl << "Minimum= " << accum_min << endl; + if (operation == _absdiff || operation == _stats) + cerr << endl << "Total counts= " << accum_sum << endl; + if (scalar != NULL) + delete scalar; + + } while (!quit); // end math operations do-while loop + } while (!quit); // restart do-while loop + + return EXIT_SUCCESS; +} // end main diff --git a/src/utilities/poisson_noise.cxx b/src/utilities/poisson_noise.cxx index f1195677cb..604791a23d 100644 --- a/src/utilities/poisson_noise.cxx +++ b/src/utilities/poisson_noise.cxx @@ -1,7 +1,7 @@ /*! \file - \ingroup utilities - \brief Generates a noise realisation according to Poisson statistics for + \ingroup utilities + \brief Generates a noise realisation according to Poisson statistics for some projection data \author Kris Thielemans @@ -45,58 +45,50 @@ USING_NAMESPACE_STIR -void usage() -{ - using std::cerr; - cerr <<"Usage: poisson_noise [-p | --preserve-mean] scaling_factor seed-unsigned-int\n" - <<"The seed value for the random number generator has to be strictly positive.\n" - << "Without the -p option, the mean of the output data will" - << " be equal to\nscaling_factor*mean_of_input, otherwise it" - << "will be equal to mean_of_input.\n" - << "The options -p and --preserve-mean are identical.\n"; +void +usage() { + using std::cerr; + cerr << "Usage: poisson_noise [-p | --preserve-mean] scaling_factor " + "seed-unsigned-int\n" + << "The seed value for the random number generator has to be strictly positive.\n" + << "Without the -p option, the mean of the output data will" + << " be equal to\nscaling_factor*mean_of_input, otherwise it" + << "will be equal to mean_of_input.\n" + << "The options -p and --preserve-mean are identical.\n"; } int -main (int argc,char *argv[]) -{ - if(argc<5) - { +main(int argc, char* argv[]) { + if (argc < 5) { usage(); - return(EXIT_FAILURE); - } - + return (EXIT_FAILURE); + } + bool preserve_mean = false; // option processing - if (argv[1][0] == '-') - { - if (strcmp(argv[1],"-p")==0 || - strcmp(argv[1],"--preserve-mean")==0) - preserve_mean = true; - else - { - usage(); - return(EXIT_FAILURE); - } - ++argv; + if (argv[1][0] == '-') { + if (strcmp(argv[1], "-p") == 0 || strcmp(argv[1], "--preserve-mean") == 0) + preserve_mean = true; + else { + usage(); + return (EXIT_FAILURE); } - - const char *const filename = argv[1]; + ++argv; + } + + const char* const filename = argv[1]; const float scaling_factor = static_cast(atof(argv[3])); - shared_ptr in_data = ProjData::read_from_file(argv[2]); + shared_ptr in_data = ProjData::read_from_file(argv[2]); unsigned int seed = atoi(argv[4]); GeneralisedPoissonNoiseGenerator generator(scaling_factor, preserve_mean); generator.seed(seed); - ProjDataInterfile new_data(in_data->get_exam_info_sptr(),in_data->get_proj_data_info_sptr()->create_shared_clone(), filename); + ProjDataInterfile new_data(in_data->get_exam_info_sptr(), in_data->get_proj_data_info_sptr()->create_shared_clone(), filename); + + generator.generate_random(new_data, *in_data); - - generator.generate_random(new_data,*in_data); - return EXIT_SUCCESS; } - - - diff --git a/src/utilities/postfilter.cxx b/src/utilities/postfilter.cxx index 123ab11461..46bd6f40c3 100644 --- a/src/utilities/postfilter.cxx +++ b/src/utilities/postfilter.cxx @@ -20,26 +20,24 @@ */ /*! - \file + \file \ingroup utilities \brief This program performs filtering on image data - + \author Sanida Mustafovic \author Kris Thielemans \author Matthew Jacobson \author PARAPET project \author Richard Brown - - This program enables calling any stir::DataProcessor object on input data, + + This program enables calling any stir::DataProcessor object on input data, and writing it to file. It can take the following command line: \verbatim - postfilter [--verbose] [--dynamic|--parametric] [output_format_par_file] - \endverbatim - This is done to make it easy to process a lot of files with the same - ImageProcessor. However, if the number of command line arguments is not - correct, appropriate questions will be asked interactively. + postfilter [--verbose] [--dynamic|--parametric] + [output_format_par_file] \endverbatim This is done to make it easy to process a lot of files with the same ImageProcessor. + However, if the number of command line arguments is not correct, appropriate questions will be asked interactively. If the --verbose option is used, the filter-parameters that are going to be used will be written to stdout. This is useful for checking/debugging. @@ -48,9 +46,9 @@ (but see stir::MedianImageFilter3D to see if the following example is still correct) \verbatim PostFilteringParameters := - Postfilter type :=Median + Postfilter type :=Median Median Filter Parameters := - mask radius x := 1 + mask radius x := 1 mask radius y := 2 mask radius z := 3 End Median Filter Parameters:= @@ -76,208 +74,177 @@ #include "stir/IO/OutputFileFormat.h" #include "stir/IO/read_from_file.h" #include "stir/Succeeded.h" -#include +#include #ifndef STIR_NO_NAMESPACES using std::cerr; using std::endl; #endif - START_NAMESPACE_STIR -template -STIRImageType * ask_image(const char *const input_query) -{ +template +STIRImageType* +ask_image(const char* const input_query) { char filename[max_filename_length]; - ask_filename_with_extension(filename, - input_query, - ""); - + ask_filename_with_extension(filename, input_query, ""); + return STIRImageType::read_from_file(filename); } - -template -shared_ptr > set_up_output_format(const std::string &filename) -{ - shared_ptr > output = - OutputFileFormat::default_sptr(); - if (filename.size() != 0) { - KeyParser parser; - parser.add_start_key("output file format parameters"); - parser.add_parsing_key("output file format type", &output); - parser.add_stop_key("END"); - if (parser.parse(filename.c_str()) == false || is_null_ptr(output)) { - warning("Error parsing output file format. Using default format."); - output = OutputFileFormat::default_sptr(); - } + +template +shared_ptr> +set_up_output_format(const std::string& filename) { + shared_ptr> output = OutputFileFormat::default_sptr(); + if (filename.size() != 0) { + KeyParser parser; + parser.add_start_key("output file format parameters"); + parser.add_parsing_key("output file format type", &output); + parser.add_stop_key("END"); + if (parser.parse(filename.c_str()) == false || is_null_ptr(output)) { + warning("Error parsing output file format. Using default format."); + output = OutputFileFormat::default_sptr(); } - return output; + } + return output; } -template -static -shared_ptr read_image(const std::string &filename) -{ - shared_ptr output; - if (!filename.empty()) - output = read_from_file(filename); - else - output.reset(ask_image("Image to process?")); - if (is_null_ptr(output)) - error("postfilter: No input image. Not writing any output."); - - return output; +template +static shared_ptr +read_image(const std::string& filename) { + shared_ptr output; + if (!filename.empty()) + output = read_from_file(filename); + else + output.reset(ask_image("Image to process?")); + if (is_null_ptr(output)) + error("postfilter: No input image. Not writing any output."); + + return output; } -template -static -void save_image(shared_ptr image, const std::string &filename, const std::string &par) -{ - shared_ptr > output_file_format = - set_up_output_format(par); +template +static void +save_image(shared_ptr image, const std::string& filename, const std::string& par) { + shared_ptr> output_file_format = set_up_output_format(par); - if (output_file_format->write_to_file(filename,*image) == Succeeded::no) - error("postfilter: Saving image failed."); + if (output_file_format->write_to_file(filename, *image) == Succeeded::no) + error("postfilter: Saving image failed."); } - END_NAMESPACE_STIR USING_NAMESPACE_STIR static void -print_usage() -{ - cerr<<"\nUsage: postfilter [--verbose] [--dynamic|--parametric] [output_format_par_file]\n"< [output_format_par_file]\n" + << endl; } int -main(int argc, char *argv[]) -{ +main(int argc, char* argv[]) { enum ImageType { normal, dynamic, parametric }; ImageType image_type = normal; - shared_ptr > input_image_single_ptr; - shared_ptr input_image_dynamic_ptr; + shared_ptr> input_image_single_ptr; + shared_ptr input_image_dynamic_ptr; shared_ptr input_image_parametric_ptr; - PostFiltering > post_filtering; + PostFiltering> post_filtering; std::string out_filename, output_file_format_par = "", input_filename = ""; bool verbose = false; // option processing - while (argc>1 && argv[1][0]=='-') - { - if (strcmp(argv[1], "--verbose") == 0) - { - verbose = true; - --argc; ++argv; - } - else if (strcmp(argv[1], "--dynamic") == 0) - { - image_type = dynamic; - --argc; ++argv; - } - else if (strcmp(argv[1], "--parametric") == 0) - { - image_type = parametric; - --argc; ++argv; - } - else - { - print_usage(); - return EXIT_FAILURE; - } - } - if (argc<4 || argc>5) - { + while (argc > 1 && argv[1][0] == '-') { + if (strcmp(argv[1], "--verbose") == 0) { + verbose = true; + --argc; + ++argv; + } else if (strcmp(argv[1], "--dynamic") == 0) { + image_type = dynamic; + --argc; + ++argv; + } else if (strcmp(argv[1], "--parametric") == 0) { + image_type = parametric; + --argc; + ++argv; + } else { print_usage(); return EXIT_FAILURE; } - if (argc>1) - { - out_filename = argv[1]; - } - else - { - char outfile[max_filename_length]; - ask_filename_with_extension(outfile, - "Output to which file: ", ""); - out_filename = outfile; - } - if (argc>2) - { - input_filename = argv[2]; - } - - if (argc>3) - { - if (post_filtering.parse(argv[3]) == false) - { - warning("postfilter aborting because error in parsing. Not writing any output"); - return EXIT_FAILURE; - } - } - else - { - cerr << "\nI'm going to ask you for the type of filter (or image processor)\n" - "Possible values:\n"; - DataProcessor >::list_registered_names(cerr); - - post_filtering.ask_parameters(); - } - if (argc>4) - { - output_file_format_par = argv[4]; - } + } + if (argc < 4 || argc > 5) { + print_usage(); + return EXIT_FAILURE; + } + if (argc > 1) { + out_filename = argv[1]; + } else { + char outfile[max_filename_length]; + ask_filename_with_extension(outfile, "Output to which file: ", ""); + out_filename = outfile; + } + if (argc > 2) { + input_filename = argv[2]; + } - if (post_filtering.is_filter_null()) - { - warning("postfilter: No filter set. Not writing any output.\n"); + if (argc > 3) { + if (post_filtering.parse(argv[3]) == false) { + warning("postfilter aborting because error in parsing. Not writing any output"); return EXIT_FAILURE; } + } else { + cerr << "\nI'm going to ask you for the type of filter (or image processor)\n" + "Possible values:\n"; + DataProcessor>::list_registered_names(cerr); + + post_filtering.ask_parameters(); + } + if (argc > 4) { + output_file_format_par = argv[4]; + } + + if (post_filtering.is_filter_null()) { + warning("postfilter: No filter set. Not writing any output.\n"); + return EXIT_FAILURE; + } // Read image if (image_type == normal) - input_image_single_ptr = - read_image >(input_filename); + input_image_single_ptr = read_image>(input_filename); else if (image_type == dynamic) - input_image_dynamic_ptr = - read_image(input_filename); + input_image_dynamic_ptr = read_image(input_filename); else /*if (image_type == parametric)*/ - input_image_parametric_ptr = - read_image(input_filename); + input_image_parametric_ptr = read_image(input_filename); - if (verbose) - { - cerr << "PostFilteringParameters:\n" << post_filtering.parameter_info(); - } + if (verbose) { + cerr << "PostFilteringParameters:\n" << post_filtering.parameter_info(); + } // Post filter! stir::Succeeded success(Succeeded::yes); if (image_type == normal) - success = post_filtering.process_data(*input_image_single_ptr); + success = post_filtering.process_data(*input_image_single_ptr); else if (image_type == dynamic) { - for (unsigned i=1; i<=input_image_dynamic_ptr->get_num_time_frames(); i++) { - if (post_filtering.process_data(input_image_dynamic_ptr->get_density(i)) == Succeeded::no) { - success = Succeeded::no; - break; - } + for (unsigned i = 1; i <= input_image_dynamic_ptr->get_num_time_frames(); i++) { + if (post_filtering.process_data(input_image_dynamic_ptr->get_density(i)) == Succeeded::no) { + success = Succeeded::no; + break; } - } - else /*if (image_type == parametric)*/ { - for (unsigned i=1; i<=input_image_parametric_ptr->get_num_params(); i++) { - VoxelsOnCartesianGrid single_parametric_param = - input_image_parametric_ptr->construct_single_density(int(i)); - if (post_filtering.process_data(single_parametric_param) == Succeeded::no) { - success = Succeeded::no; - break; - } - input_image_parametric_ptr->update_parametric_image(single_parametric_param,i); + } + } else /*if (image_type == parametric)*/ { + for (unsigned i = 1; i <= input_image_parametric_ptr->get_num_params(); i++) { + VoxelsOnCartesianGrid single_parametric_param = input_image_parametric_ptr->construct_single_density(int(i)); + if (post_filtering.process_data(single_parametric_param) == Succeeded::no) { + success = Succeeded::no; + break; } + input_image_parametric_ptr->update_parametric_image(single_parametric_param, i); + } } if (success == Succeeded::no) - error("Postfiltering failed."); + error("Postfiltering failed."); // Save to file if (image_type == normal) @@ -289,5 +256,3 @@ main(int argc, char *argv[]) return EXIT_SUCCESS; } - - diff --git a/src/utilities/rebin_projdata.cxx b/src/utilities/rebin_projdata.cxx index 350b9b340f..291da413e4 100644 --- a/src/utilities/rebin_projdata.cxx +++ b/src/utilities/rebin_projdata.cxx @@ -10,12 +10,12 @@ Here's a sample .par file \verbatim -rebin_projdata Parameters := +rebin_projdata Parameters := rebinning type := FORE FORE Parameters := ... End FORE Parameters:= -END:= +END:= \endverbatim \author Kris Thielemans @@ -38,12 +38,11 @@ END:= See STIR/LICENSE.txt for details */ - #include "stir/recon_buildblock/ProjDataRebinning.h" #include "stir/Succeeded.h" #include "stir/shared_ptr.h" #include "stir/is_null_ptr.h" -#include +#include #ifndef STIR_NO_NAMESPACES using std::cerr; @@ -53,43 +52,32 @@ using std::endl; START_NAMESPACE_STIR // TODO most of this is identical to others, so make a common class -class RebinProjDataParameters : public ParsingObject -{ +class RebinProjDataParameters : public ParsingObject { public: - - RebinProjDataParameters(const char * const par_filename); + RebinProjDataParameters(const char* const par_filename); shared_ptr proj_data_rebinning_sptr; -private: +private: virtual void set_defaults(); virtual void initialise_keymap(); virtual bool post_processing(); - }; -void -RebinProjDataParameters:: -set_defaults() -{ +void +RebinProjDataParameters::set_defaults() { proj_data_rebinning_sptr.reset(); } -void -RebinProjDataParameters:: -initialise_keymap() -{ +void +RebinProjDataParameters::initialise_keymap() { parser.add_start_key("Rebin_projdata Parameters"); parser.add_parsing_key("rebinning type", &proj_data_rebinning_sptr); parser.add_stop_key("END"); } - bool -RebinProjDataParameters:: -post_processing() -{ - if (is_null_ptr(proj_data_rebinning_sptr)) - { +RebinProjDataParameters::post_processing() { + if (is_null_ptr(proj_data_rebinning_sptr)) { warning("Invalid rebinning object\n"); return true; } @@ -97,44 +85,32 @@ post_processing() return false; } -RebinProjDataParameters:: -RebinProjDataParameters(const char * const par_filename) -{ +RebinProjDataParameters::RebinProjDataParameters(const char* const par_filename) { set_defaults(); Succeeded success = Succeeded::yes; - if (par_filename!=0) - success = parse(par_filename)==true? Succeeded::yes : Succeeded::no; + if (par_filename != 0) + success = parse(par_filename) == true ? Succeeded::yes : Succeeded::no; else ask_parameters(); - - if (success== Succeeded::no || - proj_data_rebinning_sptr->set_up()!= Succeeded::yes) - error("Rebin_projdata: set-up failed\n"); - + if (success == Succeeded::no || proj_data_rebinning_sptr->set_up() != Succeeded::yes) + error("Rebin_projdata: set-up failed\n"); } END_NAMESPACE_STIR - -int main(int argc, char *argv[]) -{ +int +main(int argc, char* argv[]) { USING_NAMESPACE_STIR - if(argc!=2) - { - cerr<<"Usage: " << argv[0] << " par_file\n" - << endl; + if (argc != 2) { + cerr << "Usage: " << argv[0] << " par_file\n" << endl; } - RebinProjDataParameters parameters( argc==2 ? argv[1] : 0); - - if (argc!=2) - { - cerr << "Corresponding .par file input \n" - << parameters.parameter_info() << endl; - } - - return - parameters.proj_data_rebinning_sptr->rebin() == Succeeded::yes? - EXIT_SUCCESS: EXIT_FAILURE; + RebinProjDataParameters parameters(argc == 2 ? argv[1] : 0); + + if (argc != 2) { + cerr << "Corresponding .par file input \n" << parameters.parameter_info() << endl; + } + + return parameters.proj_data_rebinning_sptr->rebin() == Succeeded::yes ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/src/utilities/shift_image.cxx b/src/utilities/shift_image.cxx index 5db773bc1b..b0d9867834 100644 --- a/src/utilities/shift_image.cxx +++ b/src/utilities/shift_image.cxx @@ -1,21 +1,21 @@ /* Copyright (C) 2010 - 2013, King's College London This file is part of STIR. - + This file is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.3 of the License, or (at your option) any later version. - + This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - + See STIR/LICENSE.txt for details */ /*! - \file + \file \ingroup utilities \brief This program shifts the origin of an image. \author Charalampos Tsoumpas @@ -33,76 +33,74 @@ using std::cerr; USING_NAMESPACE_STIR - -int main(int argc, char **argv) -{ - if(argc<3 || argc>7) { - cerr<<"Usage: " << argv[0] << " [x-shift] [y-shift] [z-shift] [extend_borders]\n" - << "all shifts are in mm and defaults are set to 0mm\n" - << "extend borders is either 1 or 0, defaults to 0\n"; +int +main(int argc, char** argv) { + if (argc < 3 || argc > 7) { + cerr << "Usage: " << argv[0] + << " [x-shift] [y-shift] [z-shift] [extend_borders]\n" + << "all shifts are in mm and defaults are set to 0mm\n" + << "extend borders is either 1 or 0, defaults to 0\n"; exit(EXIT_FAILURE); } - + // get parameters from command line - char const * const output_filename = argv[1]; - char const * const input_filename = argv[2]; - const float x_shift_in_mm = (argc>3) ? static_cast(atof(argv[3])) : 0; - const float y_shift_in_mm = (argc>4) ? static_cast(atof(argv[4])) : 0; - const float z_shift_in_mm = (argc>5) ? static_cast(atof(argv[5])) : 0; - const int extend_borders = (argc>6) ? atoi(argv[6]) : 0; - + char const* const output_filename = argv[1]; + char const* const input_filename = argv[2]; + const float x_shift_in_mm = (argc > 3) ? static_cast(atof(argv[3])) : 0; + const float y_shift_in_mm = (argc > 4) ? static_cast(atof(argv[4])) : 0; + const float z_shift_in_mm = (argc > 5) ? static_cast(atof(argv[5])) : 0; + const int extend_borders = (argc > 6) ? atoi(argv[6]) : 0; + // read image - const shared_ptr > density_sptr( - DiscretisedDensity<3,float>::read_from_file(input_filename)); - const DiscretisedDensityOnCartesianGrid <3,float>* density_cartesian_sptr = - dynamic_cast< DiscretisedDensityOnCartesianGrid<3,float>* > (density_sptr.get()); - const BasicCoordinate<3,float> grid_spacing=density_cartesian_sptr->get_grid_spacing(); - const CartesianCoordinate3D origin=density_cartesian_sptr->get_origin(); + const shared_ptr> density_sptr(DiscretisedDensity<3, float>::read_from_file(input_filename)); + const DiscretisedDensityOnCartesianGrid<3, float>* density_cartesian_sptr = + dynamic_cast*>(density_sptr.get()); + const BasicCoordinate<3, float> grid_spacing = density_cartesian_sptr->get_grid_spacing(); + const CartesianCoordinate3D origin = density_cartesian_sptr->get_origin(); + + const Coordinate3D image_shift(z_shift_in_mm, y_shift_in_mm, x_shift_in_mm); + const int voxel_shift_z = stir::round(image_shift[1] / grid_spacing[1]); + const int voxel_shift_y = stir::round(image_shift[2] / grid_spacing[2]); + const int voxel_shift_x = stir::round(image_shift[3] / grid_spacing[3]); + const Coordinate3D num_voxels_to_shift(voxel_shift_z, voxel_shift_y, voxel_shift_x); + + const float actual_z_shift_in_mm = static_cast(voxel_shift_z) * grid_spacing[1]; + const float actual_y_shift_in_mm = static_cast(voxel_shift_y) * grid_spacing[2]; + const float actual_x_shift_in_mm = static_cast(voxel_shift_x) * grid_spacing[3]; - const Coordinate3D image_shift(z_shift_in_mm,y_shift_in_mm,x_shift_in_mm); - const int voxel_shift_z=stir::round(image_shift[1]/grid_spacing[1]); - const int voxel_shift_y=stir::round(image_shift[2]/grid_spacing[2]); - const int voxel_shift_x=stir::round(image_shift[3]/grid_spacing[3]); - const Coordinate3D num_voxels_to_shift(voxel_shift_z,voxel_shift_y,voxel_shift_x); - - const float actual_z_shift_in_mm = static_cast (voxel_shift_z)*grid_spacing[1]; - const float actual_y_shift_in_mm = static_cast (voxel_shift_y)*grid_spacing[2]; - const float actual_x_shift_in_mm = static_cast (voxel_shift_x)*grid_spacing[3]; - std::cerr << "Actual z shift: " << actual_z_shift_in_mm << "mm\n"; std::cerr << "Actual y shift: " << actual_y_shift_in_mm << "mm\n"; std::cerr << "Actual x shift: " << actual_x_shift_in_mm << "mm\n"; - BasicCoordinate<3,int> min; BasicCoordinate<3,int> max; - const IndexRange<3> range=density_sptr->get_index_range(); - if (!range.get_regular_range(min,max)) + BasicCoordinate<3, int> min; + BasicCoordinate<3, int> max; + const IndexRange<3> range = density_sptr->get_index_range(); + if (!range.get_regular_range(min, max)) error("image is not in regular grid.\n"); - BasicCoordinate<3,int> out_min=min; BasicCoordinate<3,int> out_max=max; - if (extend_borders==1) { - out_min[1]-=abs(voxel_shift_z) ; - out_min[2]-=abs(voxel_shift_y) ; - out_min[3]-=abs(voxel_shift_x) ; - out_max[1]+=abs(voxel_shift_z) ; - out_max[2]+=abs(voxel_shift_y) ; - out_max[3]+=abs(voxel_shift_x) ; + BasicCoordinate<3, int> out_min = min; + BasicCoordinate<3, int> out_max = max; + if (extend_borders == 1) { + out_min[1] -= abs(voxel_shift_z); + out_min[2] -= abs(voxel_shift_y); + out_min[3] -= abs(voxel_shift_x); + out_max[1] += abs(voxel_shift_z); + out_max[2] += abs(voxel_shift_y); + out_max[3] += abs(voxel_shift_x); } - const IndexRange<3> out_range(out_min,out_max); - VoxelsOnCartesianGrid out_density(out_range,origin,grid_spacing); - - BasicCoordinate<3,int> c, d; - for (c[1]=min[1]; c[1]<=max[1]; ++c[1]) - for (c[2]=min[2]; c[2]<=max[2]; ++c[2]) - for (c[3]=min[3]; c[3]<=max[3]; ++c[3]) - { - d=c+num_voxels_to_shift; - if (d[1]>=out_min[1] && d[2]>=out_min[2] && d[3]>=out_min[3] && d[1]<=out_max[1] && d[2]<=out_max[2] && d[3]<=out_max[3]) - out_density[d] = (*density_sptr)[c]; - } - + const IndexRange<3> out_range(out_min, out_max); + VoxelsOnCartesianGrid out_density(out_range, origin, grid_spacing); + + BasicCoordinate<3, int> c, d; + for (c[1] = min[1]; c[1] <= max[1]; ++c[1]) + for (c[2] = min[2]; c[2] <= max[2]; ++c[2]) + for (c[3] = min[3]; c[3] <= max[3]; ++c[3]) { + d = c + num_voxels_to_shift; + if (d[1] >= out_min[1] && d[2] >= out_min[2] && d[3] >= out_min[3] && d[1] <= out_max[1] && d[2] <= out_max[2] && + d[3] <= out_max[3]) + out_density[d] = (*density_sptr)[c]; + } + // write image - Succeeded res = - OutputFileFormat >::default_sptr()-> - write_to_file(output_filename, out_density); - return res==Succeeded::yes ? EXIT_SUCCESS : EXIT_FAILURE; + Succeeded res = OutputFileFormat>::default_sptr()->write_to_file(output_filename, out_density); + return res == Succeeded::yes ? EXIT_SUCCESS : EXIT_FAILURE; } - diff --git a/src/utilities/shift_image_origin.cxx b/src/utilities/shift_image_origin.cxx index 8387d35f10..e502254aa3 100644 --- a/src/utilities/shift_image_origin.cxx +++ b/src/utilities/shift_image_origin.cxx @@ -1,21 +1,21 @@ /* Copyright (C) 2009 - 2013, King's College London This file is part of STIR. - + This file is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.3 of the License, or (at your option) any later version. - + This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - + See STIR/LICENSE.txt for details */ /*! - \file + \file \ingroup utilities \brief This program shifts the origin of an image. \author Charalampos Tsoumpas @@ -30,37 +30,30 @@ using std::cerr; USING_NAMESPACE_STIR - -int main(int argc, char **argv) -{ - if(argc<3 || argc>6) { - cerr<<"Usage: " << argv[0] << " [x-shift] [y-shift] [z-shift] ] ] ]\n" - << "all shifts are in mm and defaults are set to 0mm\n"; +int +main(int argc, char** argv) { + if (argc < 3 || argc > 6) { + cerr << "Usage: " << argv[0] << " [x-shift] [y-shift] [z-shift] ] ] ]\n" + << "all shifts are in mm and defaults are set to 0mm\n"; exit(EXIT_FAILURE); } - + // get parameters from command line - char const * const output_filename = argv[1]; - char const * const input_filename = argv[2]; - const float x_shift_in_mm = (argc>3) ? static_cast(atof(argv[3])) : 0; - const float y_shift_in_mm = (argc>4) ? static_cast(atof(argv[4])) : 0; - const float z_shift_in_mm = (argc>5) ? static_cast(atof(argv[5])) : 0; - - // read image - shared_ptr > density_sptr( - DiscretisedDensity<3,float>::read_from_file(input_filename)); - shared_ptr > out_density_sptr(density_sptr->clone()); - const Coordinate3D origin=density_sptr->get_origin(); - const Coordinate3D origin_shift(z_shift_in_mm,y_shift_in_mm,x_shift_in_mm); - out_density_sptr->set_origin(origin+origin_shift); + char const* const output_filename = argv[1]; + char const* const input_filename = argv[2]; + const float x_shift_in_mm = (argc > 3) ? static_cast(atof(argv[3])) : 0; + const float y_shift_in_mm = (argc > 4) ? static_cast(atof(argv[4])) : 0; + const float z_shift_in_mm = (argc > 5) ? static_cast(atof(argv[5])) : 0; + + // read image + shared_ptr> density_sptr(DiscretisedDensity<3, float>::read_from_file(input_filename)); + shared_ptr> out_density_sptr(density_sptr->clone()); + const Coordinate3D origin = density_sptr->get_origin(); + const Coordinate3D origin_shift(z_shift_in_mm, y_shift_in_mm, x_shift_in_mm); + out_density_sptr->set_origin(origin + origin_shift); // write image - Succeeded res = - OutputFileFormat >::default_sptr()-> - write_to_file(output_filename, *out_density_sptr); - return res==Succeeded::yes ? EXIT_SUCCESS : EXIT_FAILURE; + Succeeded res = + OutputFileFormat>::default_sptr()->write_to_file(output_filename, *out_density_sptr); + return res == Succeeded::yes ? EXIT_SUCCESS : EXIT_FAILURE; } - - - - diff --git a/src/utilities/stir_math.cxx b/src/utilities/stir_math.cxx index c7e17b6181..442fe81d6f 100644 --- a/src/utilities/stir_math.cxx +++ b/src/utilities/stir_math.cxx @@ -19,23 +19,23 @@ /*! \file \ingroup utilities - \brief add or multiply data, with some other basic math manipulations + \brief add or multiply data, with some other basic math manipulations - This is a command line utility for adding, multiplying, thresholding ... data, - with a somewhat awkward syntax. + This is a command line utility for adding, multiplying, thresholding ... data, + with a somewhat awkward syntax. The command line arguments are as follows (but everything has to fit on 1 line): \code [--output-format parameter-filename ] [--parametric || --dynamic] - [-s [--max_segment_num_to_process number] ] - [--add | --mult] - [--power power_float] - [--times-scalar mult_scalar_float] - [--divide-scalar divide_scalar_float] - [--add-scalar add_scalar_float] + [-s [--max_segment_num_to_process number] ] + [--add | --mult] + [--power power_float] + [--times-scalar mult_scalar_float] + [--divide-scalar divide_scalar_float] + [--add-scalar add_scalar_float] [--min-threshold min_threshold] [--max-threshold max_threshold] - [--including-first] + [--including-first] [--verbose] output_filename_with_extension in_data1 [in_data2 [in_data3...]] \endcode @@ -43,27 +43,27 @@ \code [--output-format parameter-filename ] --accumulate - [-s] + [-s] [--parametric || --dynamic] - [--add | --mult] - [--power power_float] - [--times-scalar mult_scalar_float] - [--divide-scalar divide_scalar_float] - [--add-scalar add_scalar_float] + [--add | --mult] + [--power power_float] + [--times-scalar mult_scalar_float] + [--divide-scalar divide_scalar_float] + [--add-scalar add_scalar_float] [--min-threshold min_threshold] [--max-threshold max_threshold] - [--including-first] + [--including-first] [--verbose] out_and_input_filename in_data2 [in_data3 [in_data4...]] \endcode '--add' is default, and outputs the sum of the result of processed data. '--mult' outputs the multiplication of the result of processed data.
      - The '--include-first' option can be used such that power and scalar - multiplication are done on the first input argument as well. + The '--include-first' option can be used such that power and scalar + multiplication are done on the first input argument as well. Otherwise these manipulations are done only on the 2nd, 3rd,.. argument.
      The '--accumulate' option can be used to say that the first filename given will be - used for input AND output. Note that when using this option together with + used for input AND output. Note that when using this option together with '--including-first', the data in the first filename will first be manipulated according to '--power' and '--times-scalar'.
      The '-s' option is necessary if the arguments are projection data. @@ -78,7 +78,7 @@ The order of the manipulations is as follows:
      (1) thresholding (2) power (3) scalar multiplication (4) scalar addition. - The '--output-format' option can be used to write the output in + The '--output-format' option can be used to write the output in a different file format then the default (although this currently only works for images). The parameter file should have the following format: \verbatim @@ -100,19 +100,19 @@ \code stir_math --accumulate --mult --power -1 in1 in2 \endcode
    - \warning There is no check that the data sizes and other info are compatible - and the output will have the largest data size in the input, - and the characteristics (like voxel-size or so) are taken from the first input data. + \warning There is no check that the data sizes and other info are compatible + and the output will have the largest data size in the input, + and the characteristics (like voxel-size or so) are taken from the first input data. Hence, lots of funny effects can happen if data are not compatible. \warning When '--accumulate' is not used, the output file HAS to be different from all the input files. - \warning The result of using non-integral powers on negative numbers is probably + \warning The result of using non-integral powers on negative numbers is probably system-dependent. \warning For future compatibility, it is recommended to put the command line arguments - in the order that they will be executed (i.e. as listed above). It might be + in the order that they will be executed (i.e. as listed above). It might be that we take the order into account in a future release. - \author Kris Thielemans + \author Kris Thielemans */ #include "stir/ArrayFunction.h" @@ -132,8 +132,8 @@ #include "stir/DynamicDiscretisedDensity.h" #include "stir/stir_math.h" -#include -#include +#include +#include #include #include #include @@ -150,109 +150,77 @@ using std::string; using std::vector; #endif - USING_NAMESPACE_STIR template -void process_data(const string& output_file_name, - const int num_files, char **argv, - const bool no_math_on_data, - const bool except_first, - const bool verbose, - const bool do_add, - const FunctionObjectT& pow_times_add_object, - const OutputFileFormat& output_format) -{ - unique_ptr< DataT > image_ptr = - read_from_file(*argv); - if (!no_math_on_data && !except_first ) +void +process_data(const string& output_file_name, const int num_files, char** argv, const bool no_math_on_data, + const bool except_first, const bool verbose, const bool do_add, const FunctionObjectT& pow_times_add_object, + const OutputFileFormat& output_format) { + unique_ptr image_ptr = read_from_file(*argv); + if (!no_math_on_data && !except_first) in_place_apply_function(*image_ptr, pow_times_add_object); - shared_ptr< DataT > current_image_ptr; - - for (int i=1; ibegin_all(), image_ptr->end_all(), - current_image_ptr->begin_all(), - image_ptr->begin_all(), - std::plus()); - } - else - { - // *image_ptr *= *current_image_ptr; - std::transform(image_ptr->begin_all(), image_ptr->end_all(), - current_image_ptr->begin_all(), - image_ptr->begin_all(), - std::multiplies()); - } + shared_ptr current_image_ptr; + + for (int i = 1; i < num_files; ++i) { + if (verbose) + cout << "Reading image " << argv[i] << endl; + current_image_ptr.reset(DataT::read_from_file(argv[i])); + if (!no_math_on_data) + in_place_apply_function(*current_image_ptr, pow_times_add_object); + if (do_add) { + // TODO the next line doesn't work with some DataT, but its replacement is ugly! + // also, it would be better to be able to call += on each element + //*image_ptr += *current_image_ptr; + std::transform(image_ptr->begin_all(), image_ptr->end_all(), current_image_ptr->begin_all(), image_ptr->begin_all(), + std::plus()); + } else { + // *image_ptr *= *current_image_ptr; + std::transform(image_ptr->begin_all(), image_ptr->end_all(), current_image_ptr->begin_all(), image_ptr->begin_all(), + std::multiplies()); } + } if (verbose) cout << "Writing output image " << output_file_name << endl; output_format.write_to_file(output_file_name, *image_ptr); } -template //class DataT, for DynProjectionData ? -void process_data(const string& output_file_name, - const int num_files, char **argv, - const bool no_math_on_data, - const bool except_first, - const bool verbose, - const bool do_add, - const FunctionObjectT& pow_times_add_object, - const OutputFileFormat& output_format) -{ - unique_ptr - dyn_image_sptr = read_from_file(*argv); - DynamicDiscretisedDensity & dyn_image = *dyn_image_sptr; - for(unsigned int frame_num=1;frame_num<=(dyn_image_sptr->get_time_frame_definitions()).get_num_frames();++frame_num) - { - if (!no_math_on_data && !except_first ) - in_place_apply_function(dyn_image[frame_num], pow_times_add_object); - } +template // class DataT, for DynProjectionData ? +void +process_data(const string& output_file_name, const int num_files, char** argv, const bool no_math_on_data, + const bool except_first, const bool verbose, const bool do_add, const FunctionObjectT& pow_times_add_object, + const OutputFileFormat& output_format) { + unique_ptr dyn_image_sptr = read_from_file(*argv); + DynamicDiscretisedDensity& dyn_image = *dyn_image_sptr; + for (unsigned int frame_num = 1; frame_num <= (dyn_image_sptr->get_time_frame_definitions()).get_num_frames(); ++frame_num) { + if (!no_math_on_data && !except_first) + in_place_apply_function(dyn_image[frame_num], pow_times_add_object); + } shared_ptr dyn_current_image_sptr; - for (int i=1; i(argv[i]); - DynamicDiscretisedDensity & dyn_current_image = *dyn_current_image_sptr; - for(unsigned int frame_num=1;frame_num<=(dyn_image_sptr->get_time_frame_definitions()).get_num_frames();++frame_num) - { - if (!no_math_on_data) - in_place_apply_function(dyn_current_image[frame_num], pow_times_add_object); - if (do_add) - { - // TODO the next line doesn't work with some DataT, but its replacement is ugly! - // also, it would be better to be able to call += on each element - //*image_ptr += *current_image_ptr; - std::transform(dyn_image[frame_num].begin_all(), dyn_image[frame_num].end_all(), - dyn_current_image[frame_num].begin_all(), - dyn_image[frame_num].begin_all(), - std::plus()); - } - else - { - // *image_ptr *= *current_image_ptr; - std::transform(dyn_image[frame_num].begin_all(), dyn_image[frame_num].end_all(), - dyn_current_image[frame_num].begin_all(), - dyn_image[frame_num].begin_all(), - std::multiplies()); - } - } + for (int i = 1; i < num_files; ++i) { + if (verbose) + cout << "Reading image " << argv[i] << endl; + dyn_current_image_sptr = read_from_file(argv[i]); + DynamicDiscretisedDensity& dyn_current_image = *dyn_current_image_sptr; + for (unsigned int frame_num = 1; frame_num <= (dyn_image_sptr->get_time_frame_definitions()).get_num_frames(); ++frame_num) { + if (!no_math_on_data) + in_place_apply_function(dyn_current_image[frame_num], pow_times_add_object); + if (do_add) { + // TODO the next line doesn't work with some DataT, but its replacement is ugly! + // also, it would be better to be able to call += on each element + //*image_ptr += *current_image_ptr; + std::transform(dyn_image[frame_num].begin_all(), dyn_image[frame_num].end_all(), dyn_current_image[frame_num].begin_all(), + dyn_image[frame_num].begin_all(), std::plus()); + } else { + // *image_ptr *= *current_image_ptr; + std::transform(dyn_image[frame_num].begin_all(), dyn_image[frame_num].end_all(), dyn_current_image[frame_num].begin_all(), + dyn_image[frame_num].begin_all(), std::multiplies()); + } } + } if (verbose) cout << "Writing output image " << output_file_name << endl; @@ -260,81 +228,77 @@ void process_data(const string& output_file_name, } template -shared_ptr > -find_output_format(const std::string& filename) -{ +shared_ptr> +find_output_format(const std::string& filename) { if (filename.empty()) return OutputFileFormat::default_sptr(); - shared_ptr > output_format_sptr; + shared_ptr> output_format_sptr; KeyParser parser; parser.add_start_key("output file format parameters"); parser.add_parsing_key("output file format type", &output_format_sptr); parser.add_stop_key("END"); - if (parser.parse(filename.c_str()) == false || is_null_ptr(output_format_sptr)) - { - cerr << "Error parsing output file format from " << filename <().min_value(); @@ -350,264 +314,216 @@ main(int argc, char **argv) // first process command line options - while (argc>0 && argv[0][0]=='-') - { - if (strcmp(argv[0], "--max_segment_num_to_process")==0) - { - if (argc<2) - { cerr << "Option '--max_segment_num_to_process' expects a (int) argument\n"; exit(EXIT_FAILURE); } - max_segment_num_to_process = atoi(argv[1]); - argc-=2; argv+=2; - } - else if (strcmp(argv[0], "--output-format")==0) - { - if (argc<2) - { - cerr << "Option '--output-format' expects a (filename) argument\n"; - exit(EXIT_FAILURE); - } - output_format_filename = argv[1]; - argc-=2; argv+=2; - } - - else if (strcmp(argv[0], "--add-scalar")==0) - { - if (argc<2) - { cerr << "Option '--add-scalar' expects a (float) argument\n"; exit(EXIT_FAILURE); } - add_scalar += static_cast(atof(argv[1])); - argc-=2; argv+=2; - } - else if (strcmp(argv[0], "--times-scalar")==0) - { - if (argc<2) - { cerr << "Option '--times-scalar' expects a (float) argument\n"; exit(EXIT_FAILURE); } - mult_scalar *= static_cast(atof(argv[1])); - argc-=2; argv+=2; - } - else if (strcmp(argv[0], "--divide-scalar")==0) - { - if (argc<2) - { cerr << "Option '--divide-scalar' expects a (float) argument\n"; exit(EXIT_FAILURE); } - mult_scalar /= static_cast(atof(argv[1])); - argc-=2; argv+=2; - } - else if (strcmp(argv[0], "--max-threshold")==0) - { - if (argc<2) - { cerr << "Option '--max-threshold' expects a (float) argument\n"; exit(EXIT_FAILURE); } - max_threshold = static_cast(atof(argv[1])); - argc-=2; argv+=2; - } - else if (strcmp(argv[0], "--min-threshold")==0) - { - if (argc<2) - { cerr << "Option '--min-threshold' expects a (float) argument\n"; exit(EXIT_FAILURE); } - min_threshold = static_cast(atof(argv[1])); - argc-=2; argv+=2; - } - else if (strcmp(argv[0], "--power")==0) - { - if (argc<2) - { cerr << "Option '--power' expects an argument\n"; exit(EXIT_FAILURE); } - power = static_cast(atof(argv[1])); - argc-=2; argv+=2; - } - else if (strcmp(argv[0], "--including-first")==0) - { - except_first = false; - argc-=1; argv+=1; - } - else if (strcmp(argv[0], "--verbose")==0) - { - verbose = true; - argc-=1; argv+=1; - } - else if (strcmp(argv[0], "-s")==0) - { - do_projdata = true; - argc-=1; argv+=1; - } - else if (strcmp(argv[0], "--parametric")==0) - { - parametric = true; - argc-=1; argv+=1; - } - else if (strcmp(argv[0], "--dynamic")==0) - { - dynamic = true; - argc-=1; argv+=1; - } - else if (strcmp(argv[0], "--add")==0) - { - do_add = true; - argc-=1; argv+=1; - } - else if (strcmp(argv[0], "--mult")==0) - { - do_add = false; - argc-=1; argv+=1; - } - else if (strcmp(argv[0], "--accumulate")==0) - { - accumulate = true; - argc-=1; argv+=1; - } - else - { cerr << "Unknown option '" << argv[0] <<"'\n"; exit(EXIT_FAILURE); } + while (argc > 0 && argv[0][0] == '-') { + if (strcmp(argv[0], "--max_segment_num_to_process") == 0) { + if (argc < 2) { + cerr << "Option '--max_segment_num_to_process' expects a (int) argument\n"; + exit(EXIT_FAILURE); + } + max_segment_num_to_process = atoi(argv[1]); + argc -= 2; + argv += 2; + } else if (strcmp(argv[0], "--output-format") == 0) { + if (argc < 2) { + cerr << "Option '--output-format' expects a (filename) argument\n"; + exit(EXIT_FAILURE); + } + output_format_filename = argv[1]; + argc -= 2; + argv += 2; + } + + else if (strcmp(argv[0], "--add-scalar") == 0) { + if (argc < 2) { + cerr << "Option '--add-scalar' expects a (float) argument\n"; + exit(EXIT_FAILURE); + } + add_scalar += static_cast(atof(argv[1])); + argc -= 2; + argv += 2; + } else if (strcmp(argv[0], "--times-scalar") == 0) { + if (argc < 2) { + cerr << "Option '--times-scalar' expects a (float) argument\n"; + exit(EXIT_FAILURE); + } + mult_scalar *= static_cast(atof(argv[1])); + argc -= 2; + argv += 2; + } else if (strcmp(argv[0], "--divide-scalar") == 0) { + if (argc < 2) { + cerr << "Option '--divide-scalar' expects a (float) argument\n"; + exit(EXIT_FAILURE); + } + mult_scalar /= static_cast(atof(argv[1])); + argc -= 2; + argv += 2; + } else if (strcmp(argv[0], "--max-threshold") == 0) { + if (argc < 2) { + cerr << "Option '--max-threshold' expects a (float) argument\n"; + exit(EXIT_FAILURE); + } + max_threshold = static_cast(atof(argv[1])); + argc -= 2; + argv += 2; + } else if (strcmp(argv[0], "--min-threshold") == 0) { + if (argc < 2) { + cerr << "Option '--min-threshold' expects a (float) argument\n"; + exit(EXIT_FAILURE); + } + min_threshold = static_cast(atof(argv[1])); + argc -= 2; + argv += 2; + } else if (strcmp(argv[0], "--power") == 0) { + if (argc < 2) { + cerr << "Option '--power' expects an argument\n"; + exit(EXIT_FAILURE); + } + power = static_cast(atof(argv[1])); + argc -= 2; + argv += 2; + } else if (strcmp(argv[0], "--including-first") == 0) { + except_first = false; + argc -= 1; + argv += 1; + } else if (strcmp(argv[0], "--verbose") == 0) { + verbose = true; + argc -= 1; + argv += 1; + } else if (strcmp(argv[0], "-s") == 0) { + do_projdata = true; + argc -= 1; + argv += 1; + } else if (strcmp(argv[0], "--parametric") == 0) { + parametric = true; + argc -= 1; + argv += 1; + } else if (strcmp(argv[0], "--dynamic") == 0) { + dynamic = true; + argc -= 1; + argv += 1; + } else if (strcmp(argv[0], "--add") == 0) { + do_add = true; + argc -= 1; + argv += 1; + } else if (strcmp(argv[0], "--mult") == 0) { + do_add = false; + argc -= 1; + argv += 1; + } else if (strcmp(argv[0], "--accumulate") == 0) { + accumulate = true; + argc -= 1; + argv += 1; + } else { + cerr << "Unknown option '" << argv[0] << "'\n"; + exit(EXIT_FAILURE); } + } - if (argc==0) - { cerr << "No output file (nor input files) on command line\n"; exit(EXIT_FAILURE); } + if (argc == 0) { + cerr << "No output file (nor input files) on command line\n"; + exit(EXIT_FAILURE); + } // find output filename const string output_file_name = *argv; - if (!accumulate) - { - --argc; ++argv; - } + if (!accumulate) { + --argc; + ++argv; + } // some basic error checking and output const int num_files = argc; - if (num_files==0) - { cerr << "No input files on command line\n"; exit(EXIT_FAILURE); } + if (num_files == 0) { + cerr << "No input files on command line\n"; + exit(EXIT_FAILURE); + } - const bool no_math_on_data = power==1 && mult_scalar==1 && add_scalar==0 && - min_threshold == NumericInfo().min_value() && - max_threshold == NumericInfo().max_value(); + const bool no_math_on_data = power == 1 && mult_scalar == 1 && add_scalar == 0 && + min_threshold == NumericInfo().min_value() && + max_threshold == NumericInfo().max_value(); - - if (verbose) - { - cout << program_name << ": " - << (do_add ? "adding " : "multiplying ") - << num_files; - if (!no_math_on_data) - cout <<" files after thresholding to a min value of " - << min_threshold << "\n and a max value of " - << max_threshold << "\n and then taking a power of " - << power << "\n and then multiplying with " - << mult_scalar << "\n and then adding " - << add_scalar - << (except_first?"\n except for" : " including") - <<" the first file"; - cout << endl; - } + if (verbose) { + cout << program_name << ": " << (do_add ? "adding " : "multiplying ") << num_files; + if (!no_math_on_data) + cout << " files after thresholding to a min value of " << min_threshold << "\n and a max value of " << max_threshold + << "\n and then taking a power of " << power << "\n and then multiplying with " << mult_scalar + << "\n and then adding " << add_scalar << (except_first ? "\n except for" : " including") << " the first file"; + cout << endl; + } // construct function object that does the manipulations on each data pow_times_add pow_times_add_object(add_scalar, mult_scalar, power, min_threshold, max_threshold); // start the main processing - if (!do_projdata) - { - - if (!parametric && !dynamic) - { - process_data(output_file_name, - num_files, argv, - no_math_on_data, - except_first, - verbose, - do_add, - pow_times_add_object, - *find_output_format >(output_format_filename)); - } - else if (parametric) - { - process_data(output_file_name, - num_files, argv, - no_math_on_data, - except_first, - verbose, - do_add, - pow_times_add_object, - *find_output_format(output_format_filename)); - } - else if (dynamic) - { - process_data(output_file_name, - num_files, argv, - no_math_on_data, - except_first, - verbose, - do_add, - pow_times_add_object, - *find_output_format(output_format_filename)); - } + if (!do_projdata) { + + if (!parametric && !dynamic) { + process_data(output_file_name, num_files, argv, no_math_on_data, except_first, verbose, do_add, pow_times_add_object, + *find_output_format>(output_format_filename)); + } else if (parametric) { + process_data(output_file_name, num_files, argv, no_math_on_data, except_first, verbose, do_add, pow_times_add_object, + *find_output_format(output_format_filename)); + } else if (dynamic) { + process_data(output_file_name, num_files, argv, no_math_on_data, except_first, verbose, do_add, pow_times_add_object, + *find_output_format(output_format_filename)); + } + } else // do_projdata + { + if (!output_format_filename.empty()) + error("We do not support specifying the output format yet for projection data"); + + vector> all_proj_data(num_files); + shared_ptr out_proj_data_ptr; + if (accumulate) { + all_proj_data[0] = ProjData::read_from_file(argv[0], std::ios::in | std::ios::out); + out_proj_data_ptr = all_proj_data[0]; + + if (max_segment_num_to_process >= 0) + warning("Parameter max_segment_num_to_process will be ignored."); + } else { + all_proj_data[0] = ProjData::read_from_file(argv[0]); + shared_ptr output_proj_data_info_sptr((*all_proj_data[0]).get_proj_data_info_sptr()->clone()); + if (max_segment_num_to_process >= 0) { + output_proj_data_info_sptr->reduce_segment_range(-max_segment_num_to_process, max_segment_num_to_process); + } + out_proj_data_ptr.reset( + new ProjDataInterfile((*all_proj_data[0]).get_exam_info_sptr(), output_proj_data_info_sptr, output_file_name)); + } + if (num_files > 1) { + // reset time-frames as we don't really know what's happening with all this + ExamInfo new_exam_info(out_proj_data_ptr->get_exam_info()); + new_exam_info.set_time_frame_definitions(TimeFrameDefinitions()); + out_proj_data_ptr->set_exam_info(new_exam_info); + } + // read rest of projection data headers + for (int i = 1; i < num_files; ++i) + all_proj_data[i] = ProjData::read_from_file(argv[i]); + + // do reading/writing in a loop over segments + for (int segment_num = out_proj_data_ptr->get_min_segment_num(); segment_num <= out_proj_data_ptr->get_max_segment_num(); + ++segment_num) { + if (verbose) + cout << "Processing segment num " << segment_num << " for all files" << endl; + + for (int k = out_proj_data_ptr->get_min_tof_pos_num(); k <= out_proj_data_ptr->get_max_tof_pos_num(); ++k) { + SegmentByView segment_by_view = (*all_proj_data[0]).get_segment_by_view(segment_num, k); + if (!no_math_on_data && !except_first) + in_place_apply_function(segment_by_view, pow_times_add_object); + for (int i = 1; i < num_files; ++i) { + SegmentByView current_segment_by_view = (*all_proj_data[i]).get_segment_by_view(segment_num, k); + if (!no_math_on_data) + in_place_apply_function(current_segment_by_view, pow_times_add_object); + if (do_add) + segment_by_view += current_segment_by_view; + else + segment_by_view *= current_segment_by_view; + } + + if (!(out_proj_data_ptr->set_segment(segment_by_view) == Succeeded::yes)) + warning("Error set_segment %d\n", segment_num); + } } - else // do_projdata - { - if (!output_format_filename.empty()) - error("We do not support specifying the output format yet for projection data"); - - vector< shared_ptr > all_proj_data(num_files); - shared_ptr out_proj_data_ptr; - if (accumulate) - { - all_proj_data[0] = ProjData::read_from_file(argv[0], std::ios::in | std::ios::out); - out_proj_data_ptr = all_proj_data[0]; - - if (max_segment_num_to_process>=0) - warning("Parameter max_segment_num_to_process will be ignored."); - } - else - { - all_proj_data[0] = ProjData::read_from_file(argv[0]); - shared_ptr - output_proj_data_info_sptr((*all_proj_data[0]).get_proj_data_info_sptr()->clone()); - if (max_segment_num_to_process>=0) - { - output_proj_data_info_sptr-> - reduce_segment_range(-max_segment_num_to_process, - max_segment_num_to_process); - } - out_proj_data_ptr.reset(new ProjDataInterfile((*all_proj_data[0]).get_exam_info_sptr(), - output_proj_data_info_sptr, - output_file_name)); - } - if (num_files>1) - { - // reset time-frames as we don't really know what's happening with all this - ExamInfo new_exam_info(out_proj_data_ptr->get_exam_info()); - new_exam_info.set_time_frame_definitions(TimeFrameDefinitions()); - out_proj_data_ptr->set_exam_info(new_exam_info); - } - // read rest of projection data headers - for (int i=1; iget_min_segment_num(); - segment_num <= out_proj_data_ptr->get_max_segment_num(); - ++segment_num) - { - if (verbose) - cout << "Processing segment num " << segment_num << " for all files" << endl; - - for (int k=out_proj_data_ptr->get_min_tof_pos_num(); - k<=out_proj_data_ptr->get_max_tof_pos_num(); - ++k) - { - SegmentByView segment_by_view = - (*all_proj_data[0]).get_segment_by_view(segment_num,k); - if (!no_math_on_data && !except_first ) - in_place_apply_function(segment_by_view, pow_times_add_object); - for (int i=1; i current_segment_by_view = - (*all_proj_data[i]).get_segment_by_view(segment_num,k); - if (!no_math_on_data) - in_place_apply_function(current_segment_by_view, pow_times_add_object); - if(do_add) - segment_by_view += current_segment_by_view; - else - segment_by_view *= current_segment_by_view; - } - - if (!(out_proj_data_ptr->set_segment(segment_by_view) == Succeeded::yes)) - warning("Error set_segment %d\n", segment_num); - } - } - } + } return EXIT_SUCCESS; } diff --git a/src/utilities/stir_write_pgm.cxx b/src/utilities/stir_write_pgm.cxx index 2bf3eda394..f07630053b 100644 --- a/src/utilities/stir_write_pgm.cxx +++ b/src/utilities/stir_write_pgm.cxx @@ -18,9 +18,9 @@ See STIR/LICENSE.txt for details */ /*! - \file + \file \ingroup utilities - + \brief This program writes a PGM bitmap for an image (preliminary) Run to get a usage message. @@ -47,188 +47,160 @@ START_NAMESPACE_STIR /* helper functions. Currently copied from manip_image. */ -static VoxelsOnCartesianGrid -transpose_13(const VoxelsOnCartesianGrid & image) -{ +static VoxelsOnCartesianGrid +transpose_13(const VoxelsOnCartesianGrid& image) { CartesianCoordinate3D origin = image.get_origin(); std::swap(origin.x(), origin.z()); CartesianCoordinate3D voxel_size = image.get_voxel_size(); std::swap(voxel_size.x(), voxel_size.z()); - VoxelsOnCartesianGrid - out(IndexRange3D(image.get_min_x(),image.get_max_x(), - image.get_min_y(),image.get_max_y(), - image.get_min_z(),image.get_max_z()), - origin, - voxel_size); - for (int x=image.get_min_x(); x<=image.get_max_x(); ++x) - for (int y=image.get_min_y(); y<=image.get_max_y(); ++y) - for (int z=image.get_min_z(); z<=image.get_max_z(); ++z) + VoxelsOnCartesianGrid out(IndexRange3D(image.get_min_x(), image.get_max_x(), image.get_min_y(), image.get_max_y(), + image.get_min_z(), image.get_max_z()), + origin, voxel_size); + for (int x = image.get_min_x(); x <= image.get_max_x(); ++x) + for (int y = image.get_min_y(); y <= image.get_max_y(); ++y) + for (int z = image.get_min_z(); z <= image.get_max_z(); ++z) out[x][y][z] = image[z][y][x]; return out; } -static VoxelsOnCartesianGrid -transpose_12(const VoxelsOnCartesianGrid & image) -{ +static VoxelsOnCartesianGrid +transpose_12(const VoxelsOnCartesianGrid& image) { CartesianCoordinate3D origin = image.get_origin(); std::swap(origin.y(), origin.z()); CartesianCoordinate3D voxel_size = image.get_voxel_size(); std::swap(voxel_size.y(), voxel_size.z()); - VoxelsOnCartesianGrid - out(IndexRange3D(image.get_min_y(),image.get_max_y(), - image.get_min_z(),image.get_max_z(), - image.get_min_x(),image.get_max_x()), - origin, - voxel_size); - for (int y=image.get_min_y(); y<=image.get_max_y(); ++y) - for (int z=image.get_min_z(); z<=image.get_max_z(); ++z) - for (int x=image.get_min_x(); x<=image.get_max_x(); ++x) + VoxelsOnCartesianGrid out(IndexRange3D(image.get_min_y(), image.get_max_y(), image.get_min_z(), image.get_max_z(), + image.get_min_x(), image.get_max_x()), + origin, voxel_size); + for (int y = image.get_min_y(); y <= image.get_max_y(); ++y) + for (int z = image.get_min_z(); z <= image.get_max_z(); ++z) + for (int x = image.get_min_x(); x <= image.get_max_x(); ++x) out[y][z][x] = image[z][y][x]; return out; } template -static -void -write_pgm (const std::string& filename, - const Array<2,elemT>& plane, - const double min_threshold, const double max_threshold) -{ +static void +write_pgm(const std::string& filename, const Array<2, elemT>& plane, const double min_threshold, const double max_threshold) { if (plane.get_length() == 0) return; - + Coordinate2D min_indices; Coordinate2D max_indices; - if (!plane.get_regular_range(min_indices, max_indices)) - { + if (!plane.get_regular_range(min_indices, max_indices)) { warning("write_pgm: can only display 'regular' arrays. Returning.\n"); return; } - FILE *pgm = fopen ( filename.c_str() , "wb"); - if (pgm == NULL) - { - error("Error opening file %s for output to PGM.",filename.c_str()); + FILE* pgm = fopen(filename.c_str(), "wb"); + if (pgm == NULL) { + error("Error opening file %s for output to PGM.", filename.c_str()); } const int pgm_max = 255; { - const int X = max_indices[2] - min_indices[2] + 1; + const int X = max_indices[2] - min_indices[2] + 1; const int Y = (max_indices[1] - min_indices[1] + 1); - fprintf ( pgm, "P5\n#created by STIR\n%d %d\n%d\n", X , Y, pgm_max); + fprintf(pgm, "P5\n#created by STIR\n%d %d\n%d\n", X, Y, pgm_max); } - - for ( int y = min_indices[1]; y <= max_indices[1]; y++) - { - for ( int x = min_indices[2]; x <= max_indices[2]; x++) - { - double val = static_cast(plane[y][x]); - if (val>max_threshold) - val=max_threshold; - else if (val(stir::round(val)) ); - } + + for (int y = min_indices[1]; y <= max_indices[1]; y++) { + for (int x = min_indices[2]; x <= max_indices[2]; x++) { + double val = static_cast(plane[y][x]); + if (val > max_threshold) + val = max_threshold; + else if (val < min_threshold) + val = min_threshold; + // now to pgm range + val = (val - min_threshold) / (max_threshold - min_threshold) * pgm_max; + fprintf(pgm, "%c", static_cast(stir::round(val))); } - fclose ( pgm); + } + fclose(pgm); } END_NAMESPACE_STIR -void print_usage_and_exit(const std::string& program_name) -{ - std::cerr<< "Usage: " << program_name << "\n\t" - << "[--min min_value] [--max max_value] \\\n\t" - << "[--orientation t|c|s] [--slice_index idx] \\\n\t" - << "output_filename.pgm input_filename \n" - << "min_value default to 0, max_value to max in image\n" - <<"oritentation defaults to transverse\n" - << "slice index is zero-based and defaults to the middle of the image (using rounding)\n"; +void +print_usage_and_exit(const std::string& program_name) { + std::cerr << "Usage: " << program_name << "\n\t" + << "[--min min_value] [--max max_value] \\\n\t" + << "[--orientation t|c|s] [--slice_index idx] \\\n\t" + << "output_filename.pgm input_filename \n" + << "min_value default to 0, max_value to max in image\n" + << "oritentation defaults to transverse\n" + << "slice index is zero-based and defaults to the middle of the image (using rounding)\n"; exit(EXIT_FAILURE); } -int -main(int argc, char **argv) -{ - const char * const program_name = argv[0]; +int +main(int argc, char** argv) { + const char* const program_name = argv[0]; // skip program name --argc; ++argv; - double min_threshold=0.; - double max_threshold=-1.; + double min_threshold = 0.; + double max_threshold = -1.; char orientation = 't'; int slice_index = -1; - // first process command line options - while (argc>0 && argv[0][0]=='-' && argc>=2) - { - if (strcmp(argv[0], "--max")==0) - { - max_threshold = atof(argv[1]); - } - else if (strcmp(argv[0], "--min")==0) - { - min_threshold = atof(argv[1]); - } - else if (strcmp(argv[0], "--orientation")==0) - { - orientation = argv[1][0]; - } - else if (strcmp(argv[0], "--slice_index")==0) - { - slice_index = atoi(argv[1]); - } - else - { - std::cerr << "Unknown option: " < 0 && argv[0][0] == '-' && argc >= 2) { + if (strcmp(argv[0], "--max") == 0) { + max_threshold = atof(argv[1]); + } else if (strcmp(argv[0], "--min") == 0) { + min_threshold = atof(argv[1]); + } else if (strcmp(argv[0], "--orientation") == 0) { + orientation = argv[1][0]; + } else if (strcmp(argv[0], "--slice_index") == 0) { + slice_index = atoi(argv[1]); + } else { + std::cerr << "Unknown option: " << argv[0] << '\n'; + print_usage_and_exit(argv[0]); } + argc -= 2; + argv += 2; + } - if(argc!=2) - { - print_usage_and_exit(program_name); - } + if (argc != 2) { + print_usage_and_exit(program_name); + } const std::string filename = argv[0]; const std::string input_filename = argv[1]; - stir::VoxelsOnCartesianGrid image( - dynamic_cast &> - (* stir::read_from_file >(input_filename))); + stir::VoxelsOnCartesianGrid image(dynamic_cast&>( + *stir::read_from_file>(input_filename))); if (max_threshold < min_threshold) max_threshold = image.find_max(); - switch (orientation) - { - case 't': case 'T': - // transverse, nothing to do at the moment - break; - case 's': case 'S': - // sagital - image=stir::transpose_13(image); - break; - case 'c': case 'C': - // coronal - image=stir::transpose_12(image); - break; - default: - stir::error("Unsupported orientation %d, has to be t,s, or c", orientation); - } + switch (orientation) { + case 't': + case 'T': + // transverse, nothing to do at the moment + break; + case 's': + case 'S': + // sagital + image = stir::transpose_13(image); + break; + case 'c': + case 'C': + // coronal + image = stir::transpose_12(image); + break; + default: + stir::error("Unsupported orientation %d, has to be t,s, or c", orientation); + } - if (slice_index<0) - slice_index = stir::round(image.get_length()/2.); - else if (slice_index >= image.get_length()) + if (slice_index < 0) + slice_index = stir::round(image.get_length() / 2.); + else if (slice_index >= image.get_length()) stir::error("Requested slice index too large"); - stir::write_pgm (filename, - image[slice_index + image.get_min_index()], - min_threshold, max_threshold); + stir::write_pgm(filename, image[slice_index + image.get_min_index()], min_threshold, max_threshold); return EXIT_SUCCESS; } diff --git a/src/utilities/warp_and_accumulate_gated_images.cxx b/src/utilities/warp_and_accumulate_gated_images.cxx index 0f98586f8a..ee76b8b174 100644 --- a/src/utilities/warp_and_accumulate_gated_images.cxx +++ b/src/utilities/warp_and_accumulate_gated_images.cxx @@ -2,24 +2,24 @@ /* Copyright (C) 2009 - 2013, King's College London This file is part of STIR. - + This file is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.3 of the License, or (at your option) any later version. - + This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - + See STIR/LICENSE.txt for details - */ + */ /*! - \file + \file \ingroup utilities \ingroup spatial_transformation - + \brief This program corrects the motion from an image. \author Charalampos Tsoumpas */ @@ -40,24 +40,23 @@ USING_NAMESPACE_STIR using namespace BSpline; - -int main(int argc, char **argv) -{ - if(argc<3 || argc>4) { - cerr<<"Usage: " << argv[0] << " \n"; +int +main(int argc, char** argv) { + if (argc < 3 || argc > 4) { + cerr << "Usage: " << argv[0] << " \n"; exit(EXIT_FAILURE); } // GatedDiscretisedDensity tmp; const GatedDiscretisedDensity gated_density(argv[2]); GatedSpatialTransformation transformation; - if(argc==3) + if (argc == 3) transformation.read_from_files(argv[2]); - else if(argc==4) + else if (argc == 4) transformation.read_from_files(argv[3]); - shared_ptr > corrected_image_sptr((gated_density[1]).get_empty_copy()); - transformation.warp_image(*corrected_image_sptr,gated_density); - - const Succeeded res = OutputFileFormat >::default_sptr()-> - write_to_file(argv[1], *corrected_image_sptr); - return res==Succeeded::yes ? EXIT_SUCCESS : EXIT_FAILURE; + shared_ptr> corrected_image_sptr((gated_density[1]).get_empty_copy()); + transformation.warp_image(*corrected_image_sptr, gated_density); + + const Succeeded res = + OutputFileFormat>::default_sptr()->write_to_file(argv[1], *corrected_image_sptr); + return res == Succeeded::yes ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/src/utilities/warp_image.cxx b/src/utilities/warp_image.cxx index 1affc716d7..4bae0c880a 100644 --- a/src/utilities/warp_image.cxx +++ b/src/utilities/warp_image.cxx @@ -1,24 +1,24 @@ /* Copyright (C) 2010 - 2013, King's College London This file is part of STIR. - + This file is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.3 of the License, or (at your option) any later version. - + This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - + See STIR/LICENSE.txt for details */ /*! - \file + \file \ingroup utilities \ingroup spatial_transformation - + \brief This program warps an image. \author Charalampos Tsoumpas */ @@ -29,43 +29,40 @@ USING_NAMESPACE_STIR - -int main(int argc, char **argv) -{ - if(argc<6 || argc>8) { - std::cerr<<"Usage: " << argv[0] << " [x-motion-field] [y-motion-field] [z-motion-field] [spline_type] [extend_borders]\n" - << "all shifts are in mm\n" - << "x, y, z are in STIR conventions\n" - << "extend borders is either 1 or 0, defaults to 0\n"; +int +main(int argc, char** argv) { + if (argc < 6 || argc > 8) { + std::cerr << "Usage: " << argv[0] + << " [x-motion-field] [y-motion-field] [z-motion-field] " + "[spline_type] [extend_borders]\n" + << "all shifts are in mm\n" + << "x, y, z are in STIR conventions\n" + << "extend borders is either 1 or 0, defaults to 0\n"; exit(EXIT_FAILURE); } - + // get parameters from command line - char const * const output_filename = argv[1]; - char const * const input_filename = argv[2]; - char const * const motion_x_filename = argv[3]; - char const * const motion_y_filename = argv[4]; - char const * const motion_z_filename = argv[5]; - const int interpolation_type = (argc==6) ? 3 : atoi(argv[6]); - const bool extend_borders = (argc<=7) ? false : (atoi(argv[7])!=0); + char const* const output_filename = argv[1]; + char const* const input_filename = argv[2]; + char const* const motion_x_filename = argv[3]; + char const* const motion_y_filename = argv[4]; + char const* const motion_z_filename = argv[5]; + const int interpolation_type = (argc == 6) ? 3 : atoi(argv[6]); + const bool extend_borders = (argc <= 7) ? false : (atoi(argv[7]) != 0); - const BSpline::BSplineType spline_type = static_cast (interpolation_type); + const BSpline::BSplineType spline_type = static_cast(interpolation_type); - std::cerr << "Interpolating using with splines level: " << spline_type << "\n"; + std::cerr << "Interpolating using with splines level: " << spline_type << "\n"; // read image - const shared_ptr > density_sptr(read_from_file >(input_filename)); - const shared_ptr > motion_x_sptr( - read_from_file >(motion_x_filename)); - const shared_ptr > motion_y_sptr( - read_from_file >(motion_y_filename)); - const shared_ptr > motion_z_sptr( - read_from_file >(motion_z_filename)); - - const VoxelsOnCartesianGrid out_density=warp_image(density_sptr, motion_x_sptr, motion_y_sptr, motion_z_sptr, spline_type, extend_borders); - + const shared_ptr> density_sptr(read_from_file>(input_filename)); + const shared_ptr> motion_x_sptr(read_from_file>(motion_x_filename)); + const shared_ptr> motion_y_sptr(read_from_file>(motion_y_filename)); + const shared_ptr> motion_z_sptr(read_from_file>(motion_z_filename)); + + const VoxelsOnCartesianGrid out_density = + warp_image(density_sptr, motion_x_sptr, motion_y_sptr, motion_z_sptr, spline_type, extend_borders); + // write image - Succeeded res = - OutputFileFormat >::default_sptr()-> - write_to_file(output_filename, out_density); - return res==Succeeded::yes ? EXIT_SUCCESS : EXIT_FAILURE; + Succeeded res = OutputFileFormat>::default_sptr()->write_to_file(output_filename, out_density); + return res == Succeeded::yes ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/src/utilities/write_proj_matrix_by_bin.cxx b/src/utilities/write_proj_matrix_by_bin.cxx index 97cb3ca832..ae4087f9f8 100644 --- a/src/utilities/write_proj_matrix_by_bin.cxx +++ b/src/utilities/write_proj_matrix_by_bin.cxx @@ -8,7 +8,7 @@ \brief Program that writes a projection matrix by bin to file \author Kris Thielemans - + */ /* Copyright (C) 2004- 2011, Hammersmith Imanet Ltd @@ -45,85 +45,63 @@ using std::cerr; using std::endl; #endif - int -main(int argc, char **argv) -{ +main(int argc, char** argv) { USING_NAMESPACE_STIR - if (argc==1 || argc>5) - { - cerr <<"Usage: " << argv[0] << " \\\n" - << "\toutput-filename [proj_data_file [projmatrixbybin-parfile [template-image]]]\n"; + if (argc == 1 || argc > 5) { + cerr << "Usage: " << argv[0] << " \\\n" + << "\toutput-filename [proj_data_file [projmatrixbybin-parfile [template-image]]]\n"; exit(EXIT_FAILURE); } - const std::string output_filename_prefix= - argc>1? argv[1] : ask_string("Output filename prefix"); - - shared_ptr proj_data_info_sptr; - if (argc>2) - { - shared_ptr proj_data_sptr = ProjData::read_from_file(argv[2]); - proj_data_info_sptr=proj_data_sptr->get_proj_data_info_sptr()->create_shared_clone(); - } - else - { - proj_data_info_sptr.reset(ProjDataInfo::ask_parameters()); - } + const std::string output_filename_prefix = argc > 1 ? argv[1] : ask_string("Output filename prefix"); + + shared_ptr proj_data_info_sptr; + if (argc > 2) { + shared_ptr proj_data_sptr = ProjData::read_from_file(argv[2]); + proj_data_info_sptr = proj_data_sptr->get_proj_data_info_sptr()->create_shared_clone(); + } else { + proj_data_info_sptr.reset(ProjDataInfo::ask_parameters()); + } shared_ptr proj_matrix_sptr; - if (argc>3) - { - KeyParser parser; - parser.add_start_key("ProjMatrixByBin parameters"); - parser.add_parsing_key("type", &proj_matrix_sptr); - parser.add_stop_key("END"); - parser.parse(argv[3]); - } - - shared_ptr > image_sptr; - - if (argc>4) - { - image_sptr = read_from_file >(argv[4]); - } - else - { - const float zoom = ask_num("Zoom factor (>1 means smaller voxels)",0.F,100.F,1.F); - int xy_size = static_cast(proj_data_info_sptr->get_num_tangential_poss()*zoom); - xy_size = ask_num("Number of x,y pixels",3,xy_size*2,xy_size); - int z_size = 2*proj_data_info_sptr->get_scanner_ptr()->get_num_rings()-1; - z_size = ask_num("Number of z pixels",1,1000,z_size); - VoxelsOnCartesianGrid * vox_image_ptr = - new VoxelsOnCartesianGrid(*proj_data_info_sptr, - zoom, - Coordinate3D(0,0,0), - Coordinate3D(z_size,xy_size,xy_size)); - const float z_origin = - ask_num("Shift z-origin (in pixels)", - -vox_image_ptr->get_length()/2, - vox_image_ptr->get_length()/2, - 0) - *vox_image_ptr->get_voxel_size().z(); - vox_image_ptr->set_origin(Coordinate3D(z_origin,0,0)); - - image_sptr.reset(vox_image_ptr); - } - - while (is_null_ptr(proj_matrix_sptr)) - { - proj_matrix_sptr.reset(ProjMatrixByBin::ask_type_and_parameters()); - } - - proj_matrix_sptr->set_up(proj_data_info_sptr, - image_sptr); - - return - ProjMatrixByBinFromFile:: - write_to_file(output_filename_prefix, - *proj_matrix_sptr, - proj_data_info_sptr, - *image_sptr) == Succeeded::yes ? - EXIT_SUCCESS : EXIT_FAILURE; + if (argc > 3) { + KeyParser parser; + parser.add_start_key("ProjMatrixByBin parameters"); + parser.add_parsing_key("type", &proj_matrix_sptr); + parser.add_stop_key("END"); + parser.parse(argv[3]); + } + + shared_ptr> image_sptr; + + if (argc > 4) { + image_sptr = read_from_file>(argv[4]); + } else { + const float zoom = ask_num("Zoom factor (>1 means smaller voxels)", 0.F, 100.F, 1.F); + int xy_size = static_cast(proj_data_info_sptr->get_num_tangential_poss() * zoom); + xy_size = ask_num("Number of x,y pixels", 3, xy_size * 2, xy_size); + int z_size = 2 * proj_data_info_sptr->get_scanner_ptr()->get_num_rings() - 1; + z_size = ask_num("Number of z pixels", 1, 1000, z_size); + VoxelsOnCartesianGrid* vox_image_ptr = new VoxelsOnCartesianGrid( + *proj_data_info_sptr, zoom, Coordinate3D(0, 0, 0), Coordinate3D(z_size, xy_size, xy_size)); + const float z_origin = + ask_num("Shift z-origin (in pixels)", -vox_image_ptr->get_length() / 2, vox_image_ptr->get_length() / 2, 0) * + vox_image_ptr->get_voxel_size().z(); + vox_image_ptr->set_origin(Coordinate3D(z_origin, 0, 0)); + + image_sptr.reset(vox_image_ptr); + } + + while (is_null_ptr(proj_matrix_sptr)) { + proj_matrix_sptr.reset(ProjMatrixByBin::ask_type_and_parameters()); + } + + proj_matrix_sptr->set_up(proj_data_info_sptr, image_sptr); + + return ProjMatrixByBinFromFile::write_to_file(output_filename_prefix, *proj_matrix_sptr, proj_data_info_sptr, *image_sptr) == + Succeeded::yes + ? EXIT_SUCCESS + : EXIT_FAILURE; } -//cache_proj_matrix_elems_for_one_bin +// cache_proj_matrix_elems_for_one_bin diff --git a/src/utilities/zeropad_planes.cxx b/src/utilities/zeropad_planes.cxx index 45ebcec4ef..63035ca92f 100644 --- a/src/utilities/zeropad_planes.cxx +++ b/src/utilities/zeropad_planes.cxx @@ -1,12 +1,12 @@ /* Copyright (C) 2011 - 2013, King's College London This file is part of STIR. - + This file is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.3 of the License, or (at your option) any later version. - + This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -15,9 +15,9 @@ See STIR/LICENSE.txt for details */ /*! - \file + \file \ingroup utilities - + \brief This program zero pads the start & end planes of an image. \author Charalampos Tsoumpas */ @@ -27,50 +27,45 @@ USING_NAMESPACE_STIR -int main(int argc, char **argv) -{ - if(argc!=4) { - std::cerr << "Usage: " << argv[0] - << " [number of axial planes] \n"; +int +main(int argc, char** argv) { + if (argc != 4) { + std::cerr << "Usage: " << argv[0] << " [number of axial planes] \n"; exit(EXIT_FAILURE); } - char const * const output_filename_prefix = argv[1]; - char const * const input_filename = argv[2]; + char const* const output_filename_prefix = argv[1]; + char const* const input_filename = argv[2]; const int num_planes = atoi(argv[3]); - const shared_ptr > image_sptr(DiscretisedDensity<3,float>::read_from_file(input_filename)); - const shared_ptr > out_image_sptr(image_sptr->clone()); - - BasicCoordinate<3,int> c, min, max; - min[1]=image_sptr->get_min_index(); - max[1]=image_sptr->get_max_index(); - - for (c[1]=min[1]; c[1]<=min[1]+num_planes-1; ++c[1]) - { - min[2]=(*image_sptr)[c[1]].get_min_index(); - max[2]=(*image_sptr)[c[1]].get_max_index(); - for (c[2]=min[2]; c[2]<=max[2]; ++c[2]) - { - min[3]=(*image_sptr)[c[1]][c[2]].get_min_index(); - max[3]=(*image_sptr)[c[1]][c[2]].get_max_index(); - for (c[3]=min[3]; c[3]<=max[3]; ++c[3]) - (*out_image_sptr)[c[1]][c[2]][c[3]]=0.F; - } + const shared_ptr> image_sptr(DiscretisedDensity<3, float>::read_from_file(input_filename)); + const shared_ptr> out_image_sptr(image_sptr->clone()); + + BasicCoordinate<3, int> c, min, max; + min[1] = image_sptr->get_min_index(); + max[1] = image_sptr->get_max_index(); + + for (c[1] = min[1]; c[1] <= min[1] + num_planes - 1; ++c[1]) { + min[2] = (*image_sptr)[c[1]].get_min_index(); + max[2] = (*image_sptr)[c[1]].get_max_index(); + for (c[2] = min[2]; c[2] <= max[2]; ++c[2]) { + min[3] = (*image_sptr)[c[1]][c[2]].get_min_index(); + max[3] = (*image_sptr)[c[1]][c[2]].get_max_index(); + for (c[3] = min[3]; c[3] <= max[3]; ++c[3]) + (*out_image_sptr)[c[1]][c[2]][c[3]] = 0.F; } - for (c[1]=max[1]; c[1]>=max[1]-num_planes+1; --c[1]) - { - min[2]=(*image_sptr)[c[1]].get_min_index(); - max[2]=(*image_sptr)[c[1]].get_max_index(); - for (c[2]=min[2]; c[2]<=max[2]; ++c[2]) - { - min[3]=(*image_sptr)[c[1]][c[2]].get_min_index(); - max[3]=(*image_sptr)[c[1]][c[2]].get_max_index(); - for (c[3]=min[3]; c[3]<=max[3]; ++c[3]) - (*out_image_sptr)[c[1]][c[2]][c[3]]=0.F; - } - } - const Succeeded res = OutputFileFormat >::default_sptr()-> - write_to_file(output_filename_prefix, *out_image_sptr); - - return res==Succeeded::yes ? EXIT_SUCCESS : EXIT_FAILURE; + } + for (c[1] = max[1]; c[1] >= max[1] - num_planes + 1; --c[1]) { + min[2] = (*image_sptr)[c[1]].get_min_index(); + max[2] = (*image_sptr)[c[1]].get_max_index(); + for (c[2] = min[2]; c[2] <= max[2]; ++c[2]) { + min[3] = (*image_sptr)[c[1]][c[2]].get_min_index(); + max[3] = (*image_sptr)[c[1]][c[2]].get_max_index(); + for (c[3] = min[3]; c[3] <= max[3]; ++c[3]) + (*out_image_sptr)[c[1]][c[2]][c[3]] = 0.F; + } + } + const Succeeded res = + OutputFileFormat>::default_sptr()->write_to_file(output_filename_prefix, *out_image_sptr); + + return res == Succeeded::yes ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/src/utilities/zoom_image.cxx b/src/utilities/zoom_image.cxx index a77318ef12..0e14781ef9 100644 --- a/src/utilities/zoom_image.cxx +++ b/src/utilities/zoom_image.cxx @@ -18,9 +18,9 @@ See STIR/LICENSE.txt for details */ /*! - \file + \file \ingroup utilities - + \brief This program serves as a stand-alone zoom/trim utility for image files Run without arguments to get a usage message. @@ -35,142 +35,125 @@ //#include "stir/utilities.h" #include "stir/Succeeded.h" - #ifndef STIR_NO_NAMESPACES using std::cerr; #endif USING_NAMESPACE_STIR -static void print_usage_and_exit(const std::string& prog_name) -{ - cerr<<"Usage: \n" - << '\t' << prog_name << " [--scaling
  • @fn|Y{%@=e%JcfC3`(X4{rJtZ#rVT$e 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